Fix calendar reconnect, recording rename, updater, and analytics#19
Conversation
When an OAuth tab was closed before completion, CalendarManager stayed in a connecting state with the loopback server still bound, so every later connect attempt (onboarding, homepage banner, settings) failed. Main process: connect() now supersedes any in-flight attempt instead of rejecting it, and cancelConnect()/loopback server teardown are awaitable to avoid EADDRINUSE on the next attempt. Renderer: extract a shared useCalendarConnect hook with an app-level focus listener that cancels abandoned flows, replacing the Settings-only cleanup so onboarding and the homepage banner recover too. Also make the calendar account store and token store lazy so they resolve userData on first use (after dev/e2e repath), matching auto-record-store and fixing test/dev profile isolation. Co-authored-by: Cursor <cursoragent@cursor.com>
The AD-87 all-day guard lived as a local helper in recording-ipc.ts, so chat-retrieval's Ask AI/search inventory still derived titles from all-day calendar matches and the Windows background refresh persisted them. Extract getRecordingDisplayCalendarTitle into the shared recording-title module and route the recording list/detail, the inventory builder, and the Windows title refresh through it so every surface excludes all-day events consistently. Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
Actionable comments posted: 27
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
src/renderer/src/hooks/useRecording.ts (1)
308-320:⚠️ Potential issue | 🟠 Major | ⚡ Quick winRollback in
useRecordingActionscan fail before local reset and mask the original start error.On Line 318,
recording:stopis awaited without a catch inside the error handler. If that IPC fails, Line 319 (reset()) is skipped and the thrown error no longer reflects the original capture-start failure.🔧 Proposed fix
await stopCapture() - await window.electronAPI.invoke('recording:stop') + await window.electronAPI.invoke('recording:stop').catch(() => {}) reset() throw err🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/renderer/src/hooks/useRecording.ts` around lines 308 - 320, The error handler in useRecordingActions has a critical issue where if the window.electronAPI.invoke('recording:stop') call on line 317 fails, it throws an error before reset() on line 318 is executed, leaving local state unreset and masking the original capture-start error. Wrap both the stopCapture() and window.electronAPI.invoke('recording:stop') calls in a try-catch block (or finally block) to ensure reset() is always called regardless of whether those cleanup operations succeed, then rethrow the original err variable so the correct capture-start error propagates instead of any IPC failure.src/main/services/calendar-manager.ts (1)
89-99:⚠️ Potential issue | 🔴 Critical | 🏗️ Heavy liftConcurrent supersede callers can still start overlapping OAuth connects.
At Line 93, multiple callers can enter the
this.connectingbranch at the same time, await the same cancellation, then each continue and runprovider.connect()concurrently. That can reintroduce loopback port contention and orphaned pending attempts.Suggested hardening (serialize supersede handoff)
export class CalendarManager { + private connectHandoffGate: Promise<void> = Promise.resolve() + private currentConnectSettled: Promise<void> = Promise.resolve() private providers: Map<string, CalendarProvider> private accounts: CalendarAccount[] = [] @@ async connect(providerType: 'google' | 'microsoft'): Promise<CalendarAccount> { - if (this.connecting) { - await this.cancelConnect() - } - - this.connecting = true + const previousGate = this.connectHandoffGate + let releaseGate!: () => void + this.connectHandoffGate = new Promise<void>((resolve) => { + releaseGate = resolve + }) + await previousGate + try { + if (this.connecting) { + await this.cancelConnect() + await this.currentConnectSettled + } + this.connecting = true + } finally { + releaseGate() + } + const attemptId = ++this.connectAttemptId + let markSettled!: () => void + this.currentConnectSettled = new Promise<void>((resolve) => { + markSettled = resolve + }) try { @@ } finally { if (this.connectAttemptId === attemptId) { this.connecting = false } + markSettled() } }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/main/services/calendar-manager.ts` around lines 89 - 99, Multiple callers can race past the cancellation checkpoint in the connect method because they all see this.connecting is true, await the same cancelConnect() call, and then all proceed to call provider.connect() concurrently. The fix is to leverage the attemptId tracking mechanism that already exists to serialize the handoff: after the await this.cancelConnect() line, add a check to verify that this.connectAttemptId still equals the local attemptId variable before continuing with the rest of the connection logic, so that any superseded callers exit early instead of proceeding to the actual provider connection attempt.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/download-counts.yml:
- Around line 33-37: The GitHub Actions in this workflow use mutable version
tags that create supply-chain security risks. Replace the
`actions/checkout@v6.0.2` action with the pinned commit SHA `44c2b74` and add
the `persist-credentials: false` option beneath it to prevent credential
persistence. Replace the `actions/setup-node@v6.4.0` action with the pinned
commit SHA `48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e`. Pinning to full commit
SHAs ensures immutability and prevents malicious retargeting of action versions.
In `@e2e/calendar-reconnect-stuck.spec.ts`:
- Line 80: In the test function definition for 'AD-79 a new connect supersedes
an abandoned attempt instead of getting stuck' (and the other test at line 143),
replace the empty object pattern `{}` with a named parameter like `_fixtures` in
the async callback signature. This indicates that the fixture object parameter
is intentionally unused and satisfies the no-empty-pattern ESLint rule, changing
`async ({}, testInfo)` to `async (_fixtures, testInfo)`.
In `@scripts/ingest-github-download-counts.js`:
- Around line 88-93: The fetch calls in the GitHub and PostHog request handlers
(at lines 88-93 and also at lines 218-227) lack timeout protection and can hang
indefinitely. Create an AbortController with a timeout (e.g., 10-30 seconds)
before each fetch call, pass its signal to the fetch options, and ensure the
controller aborts if the request exceeds the timeout. Apply this pattern to both
fetch locations to ensure ingestion fails fast instead of stalling the workflow.
In `@scripts/macos-install-policy-smoke.sh`:
- Around line 417-442: The launch_app_bundle function logs a failure message
when the app fails to start within the timeout period, but does not exit with an
error code, allowing the script to continue with invalid state. After the final
while loop that checks for the binary launch attempt (after the vlog call on
line 441), add a die call to terminate the script with an error when neither the
open command nor the direct binary execution successfully starts the AutoDoc
process within the wait_sec timeout.
In `@scripts/macos-updater-smoke.sh`:
- Around line 240-242: The smoke test assertions contain hard-coded version
numbers and zip file names (specifically "0.1.24" and
"AutoDoc-0.1.24-arm64-mac.zip") that will cause the script to fail whenever the
release version changes. Replace the hard-coded version string "0.1.24" in the
feed_head_ok function call and the zip file name reference with a variable that
captures the current or expected version. Apply this same fix to all three
locations mentioned (around lines 240, 278, and 300) by extracting the version
into a parameter or variable at the beginning of the script and reusing it
throughout, rather than embedding specific version numbers directly in the
assertion URLs and file names.
In `@src/main/ipc/__tests__/recording-ipc.test.ts`:
- Around line 275-287: Remove the `as any` casts from the mock dependency
objects passed to registerRecordingIpc across all three test blocks (lines
275-287, 348-360, and 422-434). Instead, properly type each mock object by
extracting the actual parameter types from the registerRecordingIpc function
signature using Parameters<typeof registerRecordingIpc>, or create a local typed
factory function to generate correctly typed mock objects. This will maintain
TypeScript lint compliance without suppressing the no-explicit-any rule.
In `@src/main/services/__tests__/analytics-state-store.test.ts`:
- Around line 78-87: The test "marks daily active once per local day" only
validates daily active behavior using UTC-aligned times. Add a test case that
crosses UTC midnight while remaining within the same local day by setting the
system time to a timezone-offset scenario (such as UTC+5 or UTC-5) and advancing
time across UTC midnight, then verifying that markDailyActive still correctly
returns tracked as false since the local day has not changed. This will prevent
regressions in the day bucketing logic for timezone boundaries.
In `@src/main/services/__tests__/calendar-validity.test.ts`:
- Around line 112-120: The cancelConnect mock function is currently synchronous
and cannot detect if the code stops awaiting async provider cancellation. Modify
the cancelConnect mock to return a Promise instead of executing synchronously,
such as returning Promise.resolve() or a deferred promise, to ensure the test
will properly fail if CalendarManager.connect() no longer awaits the async
cancellation from the provider. Apply this change to the cancelConnect mock
definition and ensure any related assertions in the supersede path (around lines
133-147) are similarly hardened to test async cancellation behavior.
In `@src/main/services/analytics-state-store.ts`:
- Around line 14-23: The todayIsoDate function currently uses toISOString()
which returns UTC time, causing users to transition to a "new day" before local
midnight. Fix this by modifying the todayIsoDate function to derive the date
from local time instead of UTC time. Use local date methods such as
toLocaleDateString with 'en-CA' locale (which returns YYYY-MM-DD format) or
manually construct the date string using getFullYear(), getMonth(), and
getDate() methods to ensure the function returns the local calendar date as an
ISO-formatted string.
In `@src/main/services/auto-updater.ts`:
- Around line 168-175: The onUpdateDownloaded callback on line 171 is invoked
without error handling, which means if the callback throws an exception, the
handler will exit early and skip critical follow-up logic like the smoke install
scheduling. Wrap the onUpdateDownloaded?.(status) call in a try-catch block to
isolate it from the rest of the update-downloaded event handler flow, ensuring
that any exceptions thrown by the app-provided callback don't prevent the
shouldQuitAndInstallAfterDownloadForSmoke() check and subsequent installUpdate()
scheduling from executing.
In `@src/main/services/calendar-manager.ts`:
- Around line 126-129: The cancelConnect method uses Promise.all which will
immediately reject if any single provider's cancelConnect call fails, preventing
remaining providers from completing their cleanup. Replace Promise.all with
Promise.allSettled in the cancelConnect method to ensure all providers have an
opportunity to complete their cancellation/teardown operations regardless of
whether some fail, preventing incomplete loopback teardown.
In `@src/main/services/chat-retrieval.ts`:
- Around line 2847-2848: The calendar materialization cache signature built at
lines 2892-2899 is missing the isAllDay property from the calendar event, even
though the getRecordingDisplayCalendarTitle function at line 2847 depends on
isAllDay to derive the calendarTitle. Add the isAllDay field from
matchedCalendarEvent to the cache signature so that when an event toggles
between timed and all-day status, it generates a different cache key and returns
the correct materialized entry instead of stale data.
In `@src/renderer/index.html`:
- Line 9: The CSP meta tag content attribute allows script execution from an
external third-party origin (https://us-assets.i.posthog.com) in the script-src
directive, which creates a security vulnerability in the Electron renderer.
Remove the https://us-assets.i.posthog.com domain from script-src so it contains
only 'self', keeping the external domain in connect-src where it already exists
for API connections only.
In `@src/renderer/src/App.tsx`:
- Line 59: The functions UpdateReadyPrompt (line 59), and the functions at lines
149 and 223 are missing explicit return type annotations, causing ESLint errors.
Add explicit return types to each of these three functions by specifying the
return type after the closing parenthesis of the function signature (e.g., `:
ReturnType` before the opening brace). Determine the appropriate return type for
each function based on what it returns, such as JSX.Element, void, or other
relevant types, then add these annotations to satisfy the linter requirements.
- Around line 104-112: The handleRestart function sets the update status to
'installing' and invokes the updater:install command, but lacks error handling
for when the invoke promise rejects. Add a catch or error handler to the
window.electronAPI.invoke('updater:install') call within handleRestart to
gracefully handle installation failures. When the promise rejects, update the
setUpdateStatus back to a recoverable state (such as the previous update status
before 'installing' was set) so the button becomes re-enabled and the user can
retry the installation instead of being stuck in an unrecoverable installing
state.
In `@src/renderer/src/components/onboarding/CalendarStep.tsx`:
- Line 4: The CalendarStep function is missing an explicit return type
annotation required by the TypeScript linter rule. Add the return type
annotation JSX.Element to the CalendarStep function signature by adding `:
JSX.Element` after the closing parenthesis of the parameters and before the
opening curly brace.
In `@src/renderer/src/components/onboarding/OllamaStep.tsx`:
- Around line 25-43: The recordStatus function sets the error state only when
status.phase is 'error' but never clears it for non-error updates, causing the
UI to remain in a failed state even as setup progresses. Additionally,
lastFailureKey.current is never reset when setup recovers, so repeated failures
remain incorrectly deduped. To fix this, add logic to clear the error state (by
calling setError with an empty string or appropriate default) whenever
status.phase is not 'error', and reset lastFailureKey.current to an empty string
or null when transitioning out of the error state to ensure proper deduplication
for future failures.
- Line 12: The setupStartedAt useRef is being initialized with performance.now()
at render time, which violates React's purity rules. Move the initialization
from the render-time useRef declaration to inside the useEffect hook where
trackEvent('setup_component_started', ...) is called. Initialize
setupStartedAt.current with performance.now() at the beginning of that useEffect
to ensure timing starts when setup actually begins, not when the component
renders.
In `@src/renderer/src/components/onboarding/TranscriptionStep.tsx`:
- Around line 85-96: The lastFailureKey.current reference variable is not being
reset when a user manually retries the transcription setup, which causes
duplicate failures with the same failureKey to be skipped from analytics
tracking after a retry. Locate where manual retry is triggered in the
TranscriptionStep component and clear the lastFailureKey.current value at that
point to ensure the failure deduplication logic properly resets and the next
failure event is tracked correctly in analytics.
- Line 30: The setupStartedAt useRef is calling performance.now() during
component render, which violates React's purity rules since it produces
different results on each render. Change the initialization of setupStartedAt to
useRef<number | null>(null) at line 30, then locate the existing useEffect hook
around line 122 (the one containing the trackEvent call) and add code at the
beginning of that useEffect to set setupStartedAt.current = performance.now()
before the trackEvent is called. This ensures the timestamp is captured only
once when the effect runs, not on every render.
In `@src/renderer/src/hooks/useCalendarConnect.ts`:
- Around line 53-55: The direct assignment to optionsRef.current during render
is violating the react-hooks/refs rule by causing a side effect during the
render phase. Move the optionsRef.current = options assignment out of the render
scope and into a useEffect hook with [options] as the dependency array. This
ensures the ref is synchronized with the options prop safely during the effect
phase rather than during render.
In `@src/renderer/src/hooks/useRecording.ts`:
- Around line 211-213: The duration is being captured after the stop and
finalize IPC calls in the handleStop function, which causes the store state to
be reset before elapsedSeconds is read, resulting in duration_bucket always
being calculated as 0-9s. Move the code that reads elapsedSeconds from
useRecordingStore.getState() and calculates durationBucket using
toDurationBucket before the IPC calls are made, so the correct elapsed time is
captured for the trackEvent call with recording_stopped.
In `@src/renderer/src/pages/AskAI.tsx`:
- Around line 175-181: The analytics event tracking for canceled chat responses
is only triggered inside the chat:canceled listener, but when handleStop is
called it immediately invokes finishActiveRequest() which tears down all
listeners, causing the canceled event to be missed and the analytics to not be
recorded. Move the trackEvent call with failure_code: 'canceled' to the
handleStop function or ensure it is called before finishActiveRequest() tears
down the listeners, so the canceled analytics event is always tracked when the
user manually stops the chat regardless of whether the chat:canceled event is
received.
In `@src/renderer/src/pages/Onboarding.tsx`:
- Line 38: The `startedAt` ref is calling the impure function
`performance.now()` during render, which violates React's purity requirements.
Remove the `performance.now()` call from the `useRef` initialization in the
render phase and instead move the timestamp initialization into a useEffect
hook. Initialize `startedAt` as an empty ref during render, then set its value
in a useEffect hook that runs once on component mount to capture the actual
timestamp.
In `@src/renderer/src/pages/Settings.tsx`:
- Around line 119-151: The update analytics events are being triggered on state
value rather than state transition, causing duplicate emissions on re-renders.
Add state transition guards similar to the existing check for the 'downloading'
state (previousState !== 'downloading') to prevent duplicate event emissions.
Specifically, add previousState checks to the conditions for the 'available',
'downloaded', and 'error' state checks in the trackEvent calls to ensure events
only fire when the updateStatus.state actually transitions to those states
rather than on every render while in those states.
- Around line 105-107: Remove the setState call `setIsInstallingUpdate(false)`
from within the effect body in the updateStatus.state check. Instead, derive the
`isInstallingUpdate` state directly from `updateStatus.state` without using an
effect—for example, compute it as a derived value where `isInstallingUpdate`
equals true only when `updateStatus.state === 'installing'`, or move the state
reset to an event callback that fires when the installation process completes
(such as an onInstallationComplete handler) rather than inside the effect.
In `@src/renderer/src/services/analytics.ts`:
- Around line 45-105: The ALLOWED_PROPERTIES set in the
sanitizeAnalyticsProperties function is missing five analytics properties that
are being emitted from src/renderer/src/App.tsx: meeting_window_visible,
window_missing_polls, provider_missing_polls, mic_silent_polls, and
recovered_signals. These properties are currently being silently stripped from
analytics payloads. Add these five missing property names to the
ALLOWED_PROPERTIES set to prevent them from being discarded during sanitization.
---
Outside diff comments:
In `@src/main/services/calendar-manager.ts`:
- Around line 89-99: Multiple callers can race past the cancellation checkpoint
in the connect method because they all see this.connecting is true, await the
same cancelConnect() call, and then all proceed to call provider.connect()
concurrently. The fix is to leverage the attemptId tracking mechanism that
already exists to serialize the handoff: after the await this.cancelConnect()
line, add a check to verify that this.connectAttemptId still equals the local
attemptId variable before continuing with the rest of the connection logic, so
that any superseded callers exit early instead of proceeding to the actual
provider connection attempt.
In `@src/renderer/src/hooks/useRecording.ts`:
- Around line 308-320: The error handler in useRecordingActions has a critical
issue where if the window.electronAPI.invoke('recording:stop') call on line 317
fails, it throws an error before reset() on line 318 is executed, leaving local
state unreset and masking the original capture-start error. Wrap both the
stopCapture() and window.electronAPI.invoke('recording:stop') calls in a
try-catch block (or finally block) to ensure reset() is always called regardless
of whether those cleanup operations succeed, then rethrow the original err
variable so the correct capture-start error propagates instead of any IPC
failure.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: e0f46262-a2f0-4c5a-b369-72345f6f5d7b
📒 Files selected for processing (58)
.github/workflows/build.yml.github/workflows/download-counts.yml.gitignorePRIVACY.mdPRODUCT.mdREADME.mdbuild/entitlements.mac.pliste2e/calendar-reconnect-stuck.spec.tselectron-builder.ymlpackage.jsonscripts/after-pack-mac-privacy.cjsscripts/ingest-github-download-counts.jsscripts/macos-install-policy-smoke.shscripts/macos-updater-smoke.shscripts/verify-macos-update-artifact.shsrc/main/index.tssrc/main/ipc/__tests__/recording-ipc.test.tssrc/main/ipc/analytics-ipc.tssrc/main/ipc/calendar-ipc.tssrc/main/ipc/recording-ipc.tssrc/main/services/__tests__/analytics-state-store.test.tssrc/main/services/__tests__/auto-updater.test.tssrc/main/services/__tests__/calendar-validity.test.tssrc/main/services/analytics-state-store.tssrc/main/services/auto-updater.tssrc/main/services/calendar-manager.tssrc/main/services/calendar-types.tssrc/main/services/calendar.tssrc/main/services/chat-retrieval.tssrc/main/services/microsoft-calendar.tssrc/main/services/recording-title.tssrc/main/services/token-store.tssrc/preload/ipc.d.tssrc/renderer/index.htmlsrc/renderer/src/App.test.tsxsrc/renderer/src/App.tsxsrc/renderer/src/components/RecordingControls.tsxsrc/renderer/src/components/__tests__/RecordingControls.test.tsxsrc/renderer/src/components/onboarding/AnalyticsStep.tsxsrc/renderer/src/components/onboarding/CalendarStep.tsxsrc/renderer/src/components/onboarding/MicPermissionStep.tsxsrc/renderer/src/components/onboarding/OllamaStep.tsxsrc/renderer/src/components/onboarding/ScreenPermissionStep.tsxsrc/renderer/src/components/onboarding/TranscriptionStep.tsxsrc/renderer/src/hooks/useCalendarConnect.tssrc/renderer/src/hooks/useRecording.tssrc/renderer/src/pages/AskAI.tsxsrc/renderer/src/pages/MeetingDetail.tsxsrc/renderer/src/pages/Onboarding.tsxsrc/renderer/src/pages/Search.tsxsrc/renderer/src/pages/Settings.test.tsxsrc/renderer/src/pages/Settings.tsxsrc/renderer/src/pages/Upcoming.tsxsrc/renderer/src/services/__tests__/analytics.test.tssrc/renderer/src/services/analytics.tssrc/renderer/src/test/fixtures.tssrc/shared/external-modules.d.tssrc/shared/types.ts
💤 Files with no reviewable changes (1)
- build/entitlements.mac.plist
PR #19 (eef1aeb) dropped com.apple.security.device.audio-input from the macOS entitlements on the mistaken belief that it invalidates Developer ID signatures — a reaction to a misdiagnosed "invalid signature" report. That entitlement is the Hardened Runtime gate for microphone access, so without it macOS auto-denies askForMediaAccess with no prompt, AutoDoc never appears in Privacy > Microphone, and recordings capture no audio. Screen recording uses a separate TCC path and kept working, which masked the cause. v0.1.21–v0.1.46 shipped this entitlement, signed/notarized fine, with working mics. Restore the entitlement and flip both CI guards in verify-macos-update-artifact.sh so they now require it (on the source plist and the signed app) instead of forbidding it, preventing a silent regression. Co-authored-by: Cursor <cursoragent@cursor.com>
* Fix macOS transcription runtime SIGKILL from missing JIT entitlements
The `mac.signIgnore` rule for Contents/Resources stops electron-builder
from applying the inherited Hardened Runtime entitlements to the bundled
MLX/Python transcription runtime. The afterPack signer then re-signed those
binaries with `--options runtime` but no entitlements, so the bundled
python lacked allow-jit / allow-unsigned-executable-memory. macOS SIGKILLed
the runtime ("Code Signature Invalid" on a JIT page) the moment MLX Whisper
was imported, which surfaced to QA as the recurring "transcription setup"
failure (and blocked the onboarding flow that registers microphone TCC).
- afterPack now signs bundled resource Mach-O binaries with the app
entitlements file (allow-jit etc.), restoring the entitlements that
signing inheritance used to provide before signIgnore was added.
- verify-macos-update-artifact.sh now probes the bundled runtime
(asserts allow-jit on python3 and imports mlx_whisper) so this class of
regression fails CI instead of shipping. codesign --verify cannot catch
it because the signature is valid; only an actual import does.
- The verify script also mounts and verifies the DMG (the artifact users
install), not just the auto-update zip.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Fix onboarding calendar Continue button not appearing after OAuth
The focus listener added for abandoned-OAuth recovery also fires when OAuth
completes successfully (returning to the app refocuses the window). It cleared
the in-flight attempt before calendar:connect resolved, so onConnected never
ran and the UI never advanced — the onboarding "Continue" button only appeared
after a relaunch re-read the persisted account. The focus handler now waits a
short grace period and only abandons the attempt if it has not resolved on its
own, so a successful connection is no longer dropped.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Restore microphone entitlement removed by PR #19
PR #19 (eef1aeb) dropped com.apple.security.device.audio-input from the
macOS entitlements on the mistaken belief that it invalidates Developer ID
signatures — a reaction to a misdiagnosed "invalid signature" report. That
entitlement is the Hardened Runtime gate for microphone access, so without it
macOS auto-denies askForMediaAccess with no prompt, AutoDoc never appears in
Privacy > Microphone, and recordings capture no audio. Screen recording uses a
separate TCC path and kept working, which masked the cause. v0.1.21–v0.1.46
shipped this entitlement, signed/notarized fine, with working mics.
Restore the entitlement and flip both CI guards in
verify-macos-update-artifact.sh so they now require it (on the source plist and
the signed app) instead of forbidding it, preventing a silent regression.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Fix calendar connect latency and connecting-state flash
The connect promise resolved only after the loopback server fully closed,
but the browser holds a keep-alive socket to 127.0.0.1, so server.close()
waited on its idle timeout (~2-3s) even though the OAuth tokens were already
in hand. Resolve/reject the moment tokens arrive and tear down the server in
the background, and drop lingering sockets via closeAllConnections() so the
cancel/retry port-rebind path stays fast.
Also stop clearing the "Connecting" state on window blur: that flipped the
button back the instant it was pressed (a click-time visual hitch). Keep it
disabled until the (now near-instant) result arrives, and clear it on window
focus instead so an abandoned OAuth tab can be retried. Focus only clears the
display, not the attempt token, so a success that lands just after the user
returns still surfaces.
Return the calendar account and broadcast connection-changed before the
best-effort upcoming-events fetch so the renderer isn't gated on a second
network round-trip.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Cursor <cursoragent@cursor.com>

Summary
useCalendarConnecthook across onboarding, settings, and the homepage banner with focus-based cancellation.getRecordingDisplayCalendarTitleused by the recording list/detail, Windows background refresh, and Ask AI/search inventory.Test plan
npm run test:main:run -- src/main/ipc/__tests__/recording-ipc.test.ts src/main/services/__tests__/calendar-validity.test.tsnpm run test:run -- src/renderer/src/pages/Settings.test.tsxnpm run test:e2e -- e2e/calendar-reconnect-stuck.spec.ts(real-setup profile)