Files
orca/src/shared/agent-interrupt-intent.ts
T
fe4237cd41 fix(agent-hooks): let the provider, not a keystroke, end these turns (#20149)
Escape is ambiguous at the source for Claude, OMP, Pi and Prime Agent: the same
key closes an overlay and cancels a turn, and which one it meant is focus state
only the TUI holds. Nothing downstream can recover it, so for these agents a
plain Escape is never evidence a turn ended — the provider's own hook decides.
Ctrl+C is untouched, and no other agent type changes.

The renderer skips the round-trip and main re-checks the same rule, so a stale
or direct inference request cannot route around it. A navigation Escape does not
clear a Ctrl+C already waiting to settle: Escape is not a retraction.

Fixes #13547
Fixes #9208

Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
Co-authored-by: Rod Boev <rod.boev@gmail.com>
2026-09-13 00:01:33 -07:00

43 lines
1.4 KiB
TypeScript

import type { AgentType } from './agent-status-types'
export type AgentInterruptInputIntent = 'plain-escape' | 'ctrl-c'
export const AGENT_INTERRUPT_SETTLE_MS = 500
export type AgentInterruptInferenceRequest = {
paneKey: string
baselineUpdatedAt: number
baselineStateStartedAt: number
baselinePrompt: string
baselineAgentType: AgentType | undefined
intent: AgentInterruptInputIntent
inputCount?: number
}
export function isAgentInterruptInputIntent(intent: unknown): intent is AgentInterruptInputIntent {
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
// 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([
'claude',
'omp',
'pi',
'prime-agent'
])
/** True when this keypress is one of those TUIs' navigation Escape, and so proves nothing. */
export function isNavigationEscapeIntent(
agentType: AgentType | undefined,
intent: AgentInterruptInputIntent
): boolean {
return (
intent === 'plain-escape' &&
agentType !== undefined &&
ESCAPE_ALSO_NAVIGATES_AGENT_TYPES.has(agentType)
)
}