Fix Antigravity done status regression (#2393)

This commit is contained in:
Neil 2026-05-19 20:48:34 -07:00 committed by GitHub
parent 8afdbea256
commit a424908562
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 139 additions and 9 deletions

View File

@ -176,6 +176,41 @@ describe('shared agent-hook-listener', () => {
})
})
it('reads Antigravity user requests from the transcript', () => {
const tmpDir = mkdtempSync(join(tmpdir(), 'orca-antigravity-prompt-'))
const transcriptPath = join(tmpDir, 'transcript.jsonl')
try {
writeFileSync(
transcriptPath,
`${JSON.stringify({
source: 'USER_EXPLICIT',
type: 'USER_INPUT',
content:
'<USER_REQUEST>\nFix the failing test\n</USER_REQUEST>\n<ADDITIONAL_METADATA>\nignored\n</ADDITIONAL_METADATA>'
})}\n`
)
const started = normalizeHookPayload(
state,
'antigravity',
{
paneKey: PANE_KEY,
hook_event_name: 'PreInvocation',
payload: { transcriptPath }
},
'production'
)
expect(started?.payload).toMatchObject({
state: 'working',
prompt: 'Fix the failing test',
agentType: 'antigravity'
})
} finally {
rmSync(tmpDir, { recursive: true, force: true })
}
})
it('maps Antigravity feedback tools to waiting state', () => {
const question = normalizeHookPayload(
state,
@ -285,6 +320,7 @@ describe('shared agent-hook-listener', () => {
expect(done?.payload).toMatchObject({
state: 'done',
prompt: 'hi',
agentType: 'antigravity',
lastAssistantMessage: 'Antigravity is wired up.'
})
@ -293,7 +329,7 @@ describe('shared agent-hook-listener', () => {
}
})
it('keeps Antigravity working when Stop reports the agent is not fully idle', () => {
it('normalizes Antigravity Stop to done even when fullyIdle is false', () => {
const event = normalizeHookPayload(
state,
'antigravity',
@ -306,11 +342,42 @@ describe('shared agent-hook-listener', () => {
)
expect(event?.payload).toMatchObject({
state: 'working',
state: 'done',
agentType: 'antigravity'
})
})
it('ignores late Antigravity tool hooks after a completed Stop for the same transcript', () => {
const transcriptPath = '/tmp/antigravity-transcript.jsonl'
const done = normalizeHookPayload(
state,
'antigravity',
{
paneKey: PANE_KEY,
hook_event_name: 'Stop',
payload: { transcriptPath, fullyIdle: true }
},
'production'
)
expect(done?.payload.state).toBe('done')
const lateTool = normalizeHookPayload(
state,
'antigravity',
{
paneKey: PANE_KEY,
hook_event_name: 'PostToolUse',
payload: {
transcriptPath,
toolCall: { name: 'run_command', args: { CommandLine: 'pwd' } }
}
},
'production'
)
expect(lateTool).toBeNull()
})
it('treats Antigravity Stop transcripts as pending result text', () => {
expect(
hasPendingAgentResultText('antigravity', {

View File

@ -58,6 +58,7 @@ export type HookListenerState = {
lastPromptByPaneKey: Map<string, string>
lastToolByPaneKey: Map<string, ToolSnapshot>
lastStatusByPaneKey: Map<string, AgentHookEventPayload>
antigravityCompletedTranscriptByPaneKey: Map<string, string>
}
export function createHookListenerState(): HookListenerState {
@ -66,7 +67,8 @@ export function createHookListenerState(): HookListenerState {
warnedEnvs: new Set(),
lastPromptByPaneKey: new Map(),
lastToolByPaneKey: new Map(),
lastStatusByPaneKey: new Map()
lastStatusByPaneKey: new Map(),
antigravityCompletedTranscriptByPaneKey: new Map()
}
}
@ -74,17 +76,20 @@ export function clearPaneCacheState(state: HookListenerState, paneKey: string):
state.lastPromptByPaneKey.delete(paneKey)
state.lastToolByPaneKey.delete(paneKey)
state.lastStatusByPaneKey.delete(paneKey)
state.antigravityCompletedTranscriptByPaneKey.delete(paneKey)
}
function clearPaneTurnCacheState(state: HookListenerState, paneKey: string): void {
state.lastPromptByPaneKey.delete(paneKey)
state.lastToolByPaneKey.delete(paneKey)
state.antigravityCompletedTranscriptByPaneKey.delete(paneKey)
}
export function clearAllListenerCaches(state: HookListenerState): void {
state.lastPromptByPaneKey.clear()
state.lastToolByPaneKey.clear()
state.lastStatusByPaneKey.clear()
state.antigravityCompletedTranscriptByPaneKey.clear()
state.warnedVersions.clear()
state.warnedEnvs.clear()
}
@ -558,6 +563,34 @@ function extractAssistantContentText(content: unknown): string | undefined {
return undefined
}
function extractAntigravityUserRequest(content: string): string | undefined {
const request = content.match(/<USER_REQUEST>\s*([\s\S]*?)\s*<\/USER_REQUEST>/)
const text = request ? request[1] : content
const trimmed = text.trim()
return trimmed.length > 0 ? trimmed : undefined
}
function extractUserPromptTextFromLine(line: string): string | undefined {
let entry: unknown
try {
entry = JSON.parse(line)
} catch {
return undefined
}
if (typeof entry !== 'object' || entry === null) {
return undefined
}
const record = entry as Record<string, unknown>
if (
(record.source === 'USER_EXPLICIT' || record.source === 'USER') &&
(record.type === 'USER_INPUT' || record.type === 'REQUEST') &&
typeof record.content === 'string'
) {
return extractAntigravityUserRequest(record.content)
}
return undefined
}
function readLastAssistantFromTranscript(transcriptPath: unknown): string | undefined {
if (typeof transcriptPath !== 'string' || transcriptPath.length === 0) {
return undefined
@ -565,6 +598,13 @@ function readLastAssistantFromTranscript(transcriptPath: unknown): string | unde
return readLastAssistantFromTranscriptOnce(transcriptPath)
}
function readLastUserPromptFromTranscript(transcriptPath: unknown): string | undefined {
if (typeof transcriptPath !== 'string' || transcriptPath.length === 0) {
return undefined
}
return readLastTextFromTranscriptOnce(transcriptPath, extractUserPromptTextFromLine)
}
function parseHookBodyPayloadRecord(body: unknown): Record<string, unknown> | null {
if (typeof body !== 'object' || body === null) {
return null
@ -667,6 +707,13 @@ export function hasPendingAgentResultText(source: AgentHookSource, body: unknown
}
function readLastAssistantFromTranscriptOnce(transcriptPath: string): string | undefined {
return readLastTextFromTranscriptOnce(transcriptPath, extractAssistantTextFromLine)
}
function readLastTextFromTranscriptOnce(
transcriptPath: string,
extractLineText: (line: string) => string | undefined
): string | undefined {
try {
const stats = statSync(transcriptPath)
const size = stats.size
@ -716,7 +763,7 @@ function readLastAssistantFromTranscriptOnce(transcriptPath: string): string | u
if (line.length === 0) {
continue
}
const extracted = extractAssistantTextFromLine(line)
const extracted = extractLineText(line)
if (extracted !== undefined) {
return extracted
}
@ -1530,14 +1577,25 @@ function normalizeAntigravityEvent(
paneKey: string,
hookPayload: Record<string, unknown>
): ParsedAgentStatusPayload | null {
const transcriptPath = readFirstString(hookPayload, ['transcriptPath', 'transcript_path'])
if (eventName === 'PreInvocation') {
state.antigravityCompletedTranscriptByPaneKey.delete(paneKey)
} else if (
transcriptPath &&
eventName !== 'Stop' &&
state.antigravityCompletedTranscriptByPaneKey.get(paneKey) === transcriptPath
) {
// Why: agy can emit a bookkeeping PostToolUse after Stop; ignore it so a
// finished row does not turn back into a yellow spinner.
return null
}
const toolName = readAntigravityToolCall(hookPayload).toolName
const stateName =
eventName === 'PreToolUse' && isAntigravityFeedbackTool(toolName)
? 'waiting'
: eventName === 'Stop'
? hookPayload.fullyIdle === false
? 'working'
: 'done'
? 'done'
: eventName === 'PreInvocation' ||
eventName === 'PostInvocation' ||
eventName === 'PreToolUse' ||
@ -1549,6 +1607,7 @@ function normalizeAntigravityEvent(
return null
}
const effectivePrompt = promptText || readLastUserPromptFromTranscript(transcriptPath) || ''
const snapshot = resolveToolState(
state,
paneKey,
@ -1556,10 +1615,10 @@ function normalizeAntigravityEvent(
{ resetOnNewTurn: isNewTurnEvent('antigravity', eventName) }
)
return parseAgentStatusPayload(
const payload = parseAgentStatusPayload(
JSON.stringify({
state: stateName,
prompt: resolvePrompt(state, paneKey, promptText, {
prompt: resolvePrompt(state, paneKey, effectivePrompt, {
resetOnNewTurn: isNewTurnEvent('antigravity', eventName)
}),
agentType: 'antigravity',
@ -1568,6 +1627,10 @@ function normalizeAntigravityEvent(
lastAssistantMessage: snapshot.lastAssistantMessage
})
)
if (eventName === 'Stop' && transcriptPath) {
state.antigravityCompletedTranscriptByPaneKey.set(paneKey, transcriptPath)
}
return payload
}
function normalizeCodexEvent(