65 lines
2.8 KiB
TypeScript
65 lines
2.8 KiB
TypeScript
/**
|
|
* Headless Event Detection — notification classification and command detection
|
|
*
|
|
* Detects terminal notifications, blocked notifications, milestone-ready signals,
|
|
* and classifies commands as quick (single-turn) vs long-running.
|
|
*/
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Completion Detection
|
|
// ---------------------------------------------------------------------------
|
|
|
|
/**
|
|
* Detect genuine auto-mode termination notifications.
|
|
*
|
|
* Only matches the actual stop signals emitted by stopAuto():
|
|
* "Auto-mode stopped..."
|
|
* "Step-mode stopped..."
|
|
*
|
|
* Does NOT match progress notifications that happen to contain words like
|
|
* "complete" or "stopped" (e.g., "Override resolved — rewrite-docs completed",
|
|
* "All slices are complete — nothing to discuss", "Skipped 5+ completed units").
|
|
*
|
|
* Blocked detection is separate — checked via isBlockedNotification.
|
|
*/
|
|
export const TERMINAL_PREFIXES = ['auto-mode stopped', 'step-mode stopped']
|
|
export const IDLE_TIMEOUT_MS = 15_000
|
|
// new-milestone is a long-running creative task where the LLM may pause
|
|
// between tool calls (e.g. after mkdir, before writing files). Use a
|
|
// longer idle timeout to avoid killing the session prematurely (#808).
|
|
export const NEW_MILESTONE_IDLE_TIMEOUT_MS = 120_000
|
|
|
|
export function isTerminalNotification(event: Record<string, unknown>): boolean {
|
|
if (event.type !== 'extension_ui_request' || event.method !== 'notify') return false
|
|
const message = String(event.message ?? '').toLowerCase()
|
|
return TERMINAL_PREFIXES.some((prefix) => message.startsWith(prefix))
|
|
}
|
|
|
|
export function isBlockedNotification(event: Record<string, unknown>): boolean {
|
|
if (event.type !== 'extension_ui_request' || event.method !== 'notify') return false
|
|
const message = String(event.message ?? '').toLowerCase()
|
|
// Blocked notifications come through stopAuto as "Auto-mode stopped (Blocked: ...)"
|
|
return message.includes('blocked:')
|
|
}
|
|
|
|
export function isMilestoneReadyNotification(event: Record<string, unknown>): boolean {
|
|
if (event.type !== 'extension_ui_request' || event.method !== 'notify') return false
|
|
return /milestone\s+m\d+.*ready/i.test(String(event.message ?? ''))
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Quick Command Detection
|
|
// ---------------------------------------------------------------------------
|
|
|
|
export const FIRE_AND_FORGET_METHODS = new Set(['notify', 'setStatus', 'setWidget', 'setTitle', 'set_editor_text'])
|
|
|
|
export const QUICK_COMMANDS = new Set([
|
|
'status', 'queue', 'history', 'hooks', 'export', 'stop', 'pause',
|
|
'capture', 'skip', 'undo', 'knowledge', 'config', 'prefs',
|
|
'cleanup', 'migrate', 'doctor', 'remote', 'help', 'steer',
|
|
'triage', 'visualize',
|
|
])
|
|
|
|
export function isQuickCommand(command: string): boolean {
|
|
return QUICK_COMMANDS.has(command)
|
|
}
|