From 909c3b3f45db35bf428bd021d591f1fa8d7b4bef Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Sun, 20 Sep 2026 21:03:08 -0700 Subject: [PATCH] fix(antigravity): restore workspaces by exact conversation identity (#21638) --- .../session-scanner-antigravity-history.ts | 39 ++++++++--- ...session-scanner-antigravity-source.test.ts | 65 +++++++++++++++++++ 2 files changed, 95 insertions(+), 9 deletions(-) diff --git a/src/main/ai-vault/session-scanner-antigravity-history.ts b/src/main/ai-vault/session-scanner-antigravity-history.ts index ccfb506b7f7..3463e6b7d07 100644 --- a/src/main/ai-vault/session-scanner-antigravity-history.ts +++ b/src/main/ai-vault/session-scanner-antigravity-history.ts @@ -28,7 +28,10 @@ type AntigravityHistoryEntry = { workspace: string } -type AntigravityHistoryIndex = Map +type AntigravityHistoryIndex = { + byConversationId: Map + byDisplay: Map +} export type AntigravityWorkspaceResolver = { enrich(session: AiVaultSession, historyPath: string): Promise @@ -67,18 +70,32 @@ export function createAntigravityWorkspaceResolver( } function indexAntigravityHistory(content: string | null): AntigravityHistoryIndex { - const index: AntigravityHistoryIndex = new Map() + const index: AntigravityHistoryIndex = { + byConversationId: new Map(), + byDisplay: new Map() + } for (const line of content?.split(/\r?\n/) ?? []) { const record = parseJsonObject(line) const display = typeof record?.display === 'string' ? normalizeTitleText(record.display) : null const workspace = typeof record?.workspace === 'string' ? record.workspace.trim() : '' + const conversationId = + typeof record?.conversationId === 'string' ? record.conversationId.trim() : '' const entryTimestampMs = timestampMs(record?.timestamp) - if (!display || !workspace || !Number.isFinite(entryTimestampMs)) { + if (!workspace || !Number.isFinite(entryTimestampMs)) { continue } - const entries = index.get(display) ?? [] - entries.push({ timestampMs: entryTimestampMs, workspace }) - index.set(display, entries) + const entry = { timestampMs: entryTimestampMs, workspace } + const directEntry = index.byConversationId.get(conversationId) + if (conversationId && (!directEntry || entryTimestampMs < directEntry.timestampMs)) { + index.byConversationId.set(conversationId, entry) + } + // An explicit conversation ID must never become another session's prompt fallback. + if (conversationId || !display) { + continue + } + const entries = index.byDisplay.get(display) ?? [] + entries.push(entry) + index.byDisplay.set(display, entries) } return index } @@ -87,6 +104,10 @@ function findAntigravityWorkspace( session: AiVaultSession, index: AntigravityHistoryIndex ): string | null { + const directMatch = index.byConversationId.get(session.sessionId) + if (directMatch) { + return directMatch.workspace + } // Why: truncated titles are not prompt identities; long worker prompts often // share the same 96-character prefix across unrelated workspaces. if (session.title.endsWith('...')) { @@ -99,10 +120,10 @@ function findAntigravityWorkspace( if (!Number.isFinite(promptTimestampMs)) { return null } - const matches = (index.get(session.title) ?? []).filter( + const matches = (index.byDisplay.get(session.title) ?? []).filter( (entry) => Math.abs(entry.timestampMs - promptTimestampMs) <= HISTORY_MATCH_WINDOW_MS ) - // Why: history rows have no conversation id. A unique prompt/time match is - // evidence for cwd; ambiguity must stay unknown instead of crossing projects. + // Why: legacy history rows have no conversation id. A unique prompt/time + // match is evidence for cwd; ambiguity must stay unknown instead of crossing projects. return matches.length === 1 ? (matches[0]?.workspace ?? null) : null } diff --git a/src/main/ai-vault/session-scanner-antigravity-source.test.ts b/src/main/ai-vault/session-scanner-antigravity-source.test.ts index 454b3cf92f6..afb4f835bba 100644 --- a/src/main/ai-vault/session-scanner-antigravity-source.test.ts +++ b/src/main/ai-vault/session-scanner-antigravity-source.test.ts @@ -105,6 +105,71 @@ describe('Antigravity AI Vault discovery', () => { ).toEqual([sessionId]) }) + it.each(['Connectivity setup only', 'Long setup prompt '.repeat(12)])( + 'uses the conversation id for a mismatched or truncated title: %s', + async (firstPrompt) => { + const root = await mkdtemp(join(tmpdir(), 'orca-antigravity-conversation-id-')) + tempRoots.push(root) + const roots = isolatedScanRoots(root) + const sessionId = 'dddddddd-eeee-4fff-8aaa-bbbbbbbbbbbb' + const workspace = join(root, 'active-workspace') + await writeAntigravityTranscript(roots.antigravityBrainDir, sessionId, [ + { + source: 'USER_EXPLICIT', + type: 'USER_INPUT', + created_at: '2026-07-15T11:39:10.000Z', + content: `${firstPrompt}` + }, + { + source: 'USER_EXPLICIT', + type: 'USER_INPUT', + created_at: '2026-07-15T11:40:00.000Z', + content: `${'long dispatched worker prompt '.repeat(8)}` + } + ]) + await writeAntigravityHistory(roots.antigravityBrainDir, [ + { + conversationId: sessionId, + display: 'A later prompt with a different title', + timestamp: Date.parse('2026-07-15T11:40:00.100Z'), + workspace + } + ]) + + const result = await scanAiVaultSessions({ ...roots, platform: 'darwin' }) + + expect(result.sessions[0]).toMatchObject({ + sessionId, + cwd: workspace + }) + } + ) + + it('does not borrow a matching prompt from an explicitly different conversation', async () => { + const root = await mkdtemp(join(tmpdir(), 'orca-antigravity-foreign-id-')) + tempRoots.push(root) + const roots = isolatedScanRoots(root) + const sessionId = '11111111-2222-4333-8444-555555555555' + await writeAntigravityTranscript(roots.antigravityBrainDir, sessionId, [ + { + source: 'USER_EXPLICIT', + type: 'USER_INPUT', + created_at: '2026-07-15T11:39:10.000Z', + content: 'Same prompt' + } + ]) + await writeAntigravityHistory(roots.antigravityBrainDir, [ + { + conversationId: 'aaaaaaaa-bbbb-4ccc-8ddd-eeeeeeeeeeee', + display: 'Same prompt', + timestamp: Date.parse('2026-07-15T11:39:10.000Z'), + workspace: join(root, 'other-workspace') + } + ]) + const result = await scanAiVaultSessions({ ...roots, platform: 'darwin' }) + expect(result.sessions[0]?.cwd).toBeNull() + }) + it('keeps workspace unknown when matching history rows are ambiguous', async () => { const root = await mkdtemp(join(tmpdir(), 'orca-antigravity-ambiguous-')) tempRoots.push(root)