fix(antigravity): restore workspaces by exact conversation identity (#21638)

This commit is contained in:
Neil
2026-09-20 21:03:08 -07:00
committed by GitHub
parent ae35a1324d
commit 909c3b3f45
2 changed files with 95 additions and 9 deletions
@@ -28,7 +28,10 @@ type AntigravityHistoryEntry = {
workspace: string
}
type AntigravityHistoryIndex = Map<string, AntigravityHistoryEntry[]>
type AntigravityHistoryIndex = {
byConversationId: Map<string, AntigravityHistoryEntry>
byDisplay: Map<string, AntigravityHistoryEntry[]>
}
export type AntigravityWorkspaceResolver = {
enrich(session: AiVaultSession, historyPath: string): Promise<AiVaultSession>
@@ -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
}
@@ -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: `<USER_REQUEST>${firstPrompt}</USER_REQUEST>`
},
{
source: 'USER_EXPLICIT',
type: 'USER_INPUT',
created_at: '2026-07-15T11:40:00.000Z',
content: `<USER_REQUEST>${'long dispatched worker prompt '.repeat(8)}</USER_REQUEST>`
}
])
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)