mirror of
https://github.com/stablyai/orca.git
synced 2026-09-21 16:02:20 +00:00
fix(antigravity): do not infer interruption from navigation Escape (#21636)
This commit is contained in:
@@ -157,6 +157,7 @@ describe('navigation Escape during an open tool call', () => {
|
||||
})
|
||||
|
||||
it.each([
|
||||
['antigravity', 'PreToolUse'],
|
||||
['pi', 'tool_call'],
|
||||
['prime-agent', 'tool_execution_start']
|
||||
])('leaves %s work running when Escape closes an overlay mid-%s', (agentType, hookEventName) => {
|
||||
@@ -175,6 +176,37 @@ describe('navigation Escape during an open tool call', () => {
|
||||
expect(server.getStatusSnapshotForPane(PANE)[0]).toMatchObject({ state: 'working' })
|
||||
})
|
||||
|
||||
it('keeps Antigravity working through repeated navigation Escape until its Stop event', () => {
|
||||
const server = new AgentHookServer()
|
||||
const turn = { source: 'antigravity', prompt: 'inspect files', agentType: 'antigravity' }
|
||||
ingest(server, { ...turn, hookEventName: 'PreInvocation', state: 'working' })
|
||||
const published = collectPublishedStates(server)
|
||||
vi.setSystemTime(1_200)
|
||||
expect(pressInterruptKey(server, 'plain-escape', 2)).toBe(false)
|
||||
expect(server.getStatusSnapshotForPane(PANE)[0]).toMatchObject({ state: 'working' })
|
||||
expect(published).toEqual([])
|
||||
vi.setSystemTime(1_500)
|
||||
ingest(server, { ...turn, hookEventName: 'Stop', state: 'done' })
|
||||
expect(server.getStatusSnapshotForPane(PANE)[0]).toMatchObject({ state: 'done' })
|
||||
})
|
||||
|
||||
it('still accepts Ctrl+C for Antigravity work', () => {
|
||||
const server = new AgentHookServer()
|
||||
ingest(server, {
|
||||
source: 'antigravity',
|
||||
hookEventName: 'PreInvocation',
|
||||
state: 'working',
|
||||
prompt: 'inspect files',
|
||||
agentType: 'antigravity'
|
||||
})
|
||||
vi.setSystemTime(1_200)
|
||||
expect(pressInterruptKey(server, 'ctrl-c')).toBe(true)
|
||||
expect(server.getStatusSnapshotForPane(PANE)[0]).toMatchObject({
|
||||
state: 'done',
|
||||
interrupted: true
|
||||
})
|
||||
})
|
||||
|
||||
it('leaves OMP work running when Escape lands between approval and execution (#9208)', () => {
|
||||
const server = new AgentHookServer()
|
||||
ingest(server, {
|
||||
|
||||
@@ -348,7 +348,7 @@ describe('agent interrupt inference', () => {
|
||||
entry = undefined
|
||||
})
|
||||
|
||||
it.each([['claude'], ['omp'], ['pi'], ['prime-agent']] as const)(
|
||||
it.each([['claude'], ['omp'], ['pi'], ['prime-agent'], ['antigravity']] as const)(
|
||||
'never asks main to interrupt %s on a single Escape while working',
|
||||
(agentType) => {
|
||||
// Why: Escape is ambiguous at the source for these TUIs, so the renderer does not spend a
|
||||
@@ -372,33 +372,36 @@ describe('agent interrupt inference', () => {
|
||||
}
|
||||
)
|
||||
|
||||
it.each([['claude'], ['omp']] as const)('still forwards Ctrl+C for %s', (agentType) => {
|
||||
vi.useFakeTimers()
|
||||
let entry: AgentStatusEntry | undefined = makeEntry({ agentType, toolName: 'Bash' })
|
||||
const inferInterrupt = vi.fn().mockReturnValue(true)
|
||||
const tracker = createAgentInterruptInference({
|
||||
paneKey: PANE_KEY,
|
||||
getStatusEntry: () => entry,
|
||||
inferInterrupt,
|
||||
now: () => 1_100
|
||||
})
|
||||
it.each([['claude'], ['omp'], ['antigravity']] as const)(
|
||||
'still forwards Ctrl+C for %s',
|
||||
(agentType) => {
|
||||
vi.useFakeTimers()
|
||||
let entry: AgentStatusEntry | undefined = makeEntry({ agentType, toolName: 'Bash' })
|
||||
const inferInterrupt = vi.fn().mockReturnValue(true)
|
||||
const tracker = createAgentInterruptInference({
|
||||
paneKey: PANE_KEY,
|
||||
getStatusEntry: () => entry,
|
||||
inferInterrupt,
|
||||
now: () => 1_100
|
||||
})
|
||||
|
||||
tracker.observeInputIntent('ctrl-c')
|
||||
vi.advanceTimersByTime(500)
|
||||
tracker.observeInputIntent('ctrl-c')
|
||||
vi.advanceTimersByTime(500)
|
||||
|
||||
expect(inferInterrupt).toHaveBeenCalledWith({
|
||||
paneKey: PANE_KEY,
|
||||
baselineUpdatedAt: 1_000,
|
||||
baselineStateStartedAt: 900,
|
||||
baselinePrompt: 'write tests',
|
||||
baselineAgentType: agentType,
|
||||
intent: 'ctrl-c'
|
||||
})
|
||||
tracker.dispose()
|
||||
entry = undefined
|
||||
})
|
||||
expect(inferInterrupt).toHaveBeenCalledWith({
|
||||
paneKey: PANE_KEY,
|
||||
baselineUpdatedAt: 1_000,
|
||||
baselineStateStartedAt: 900,
|
||||
baselinePrompt: 'write tests',
|
||||
baselineAgentType: agentType,
|
||||
intent: 'ctrl-c'
|
||||
})
|
||||
tracker.dispose()
|
||||
entry = undefined
|
||||
}
|
||||
)
|
||||
|
||||
it.each([['claude'], ['omp'], ['pi'], ['prime-agent']] as const)(
|
||||
it.each([['claude'], ['omp'], ['pi'], ['prime-agent'], ['antigravity']] as const)(
|
||||
'keeps a pending Ctrl+C for %s when a navigation Escape lands before it settles',
|
||||
(agentType) => {
|
||||
// Why: Escape is not a retraction. The user asked to interrupt; dismissing an overlay
|
||||
|
||||
@@ -18,11 +18,12 @@ export function isAgentInterruptInputIntent(intent: unknown): intent is AgentInt
|
||||
return intent === 'plain-escape' || intent === 'ctrl-c'
|
||||
}
|
||||
|
||||
// Why: these TUIs also close an overlay on a bare Escape (Claude's /btw composer, OMP/Pi's
|
||||
// focused-child and settings views). The keypress is ambiguous at the source and nothing outside
|
||||
// Why: these TUIs also close an overlay on a bare Escape (Antigravity's /usage, Claude's /btw,
|
||||
// OMP/Pi's focused-child and settings views). The keypress is ambiguous at the source and nothing outside
|
||||
// the TUI can disambiguate it, so it is never evidence a turn ended — only the provider's own
|
||||
// hook may retire the row (#13547, #9208). Ctrl+C is unaffected; it has no navigation meaning.
|
||||
const ESCAPE_ALSO_NAVIGATES_AGENT_TYPES: ReadonlySet<AgentType> = new Set([
|
||||
'antigravity',
|
||||
'claude',
|
||||
'omp',
|
||||
'pi',
|
||||
|
||||
Reference in New Issue
Block a user