Add hooks for interrupted turns (#40511)

## What changed

- Add an `Interrupt` hook event that runs for an active top-level turn before its
  interrupted abort event is emitted.
- Flush the turn transcript before invoking the hook and provide the session,
  turn, transcript, working directory, model, and permission mode in its input.
- Support command and MCP handlers, including asynchronous commands, with a
  one-second default timeout and a three-second maximum.
- Expose the event through hook configuration, managed requirements, app-server
  notifications, generated schemas, analytics, and the TUI hook views.

## Testing

- Cover handler discovery, timeout normalization, output parsing, protocol
  compatibility, TUI rendering, and interrupt execution ordering.

GitOrigin-RevId: 163fa7c098d94ac2775f6d137f8e916f8ea9b6eb
This commit is contained in:
Andrei Eternal authored and copyberry committed 2026-08-25 01:27:18 +00:00
1 parent 9b2ef38f54
commit cbfd999db7
49 files changed
+1393 -48

No files matched your search

+8
View File
@@ -26,6 +26,7 @@ use tracing::warn;
use crate::codex_thread::BackgroundTerminalInfo;
use crate::config::Config;
use crate::context::ContextualUserFragment;
use crate::hook_runtime::run_turn_interrupt_hooks;
use crate::session::TurnInput;
use crate::session::session::Session;
use crate::session::turn::run_hooks_and_record_inputs;
@@ -791,6 +792,9 @@ impl Session {
ThreadIdleCause::Completed
};
let event = if let Some(reason) = abort_reason {
if reason == TurnAbortReason::Interrupted {
run_turn_interrupt_hooks(self, &turn_context).await;
}
self.emit_turn_abort_lifecycle(reason.clone(), turn_context.extension_data.as_ref())
.await;
EventMsg::TurnAborted(TurnAbortedEvent {
@@ -941,6 +945,10 @@ impl Session {
}
}
if reason == TurnAbortReason::Interrupted {
run_turn_interrupt_hooks(self, &task.turn_context).await;
}
let started_at = task
.turn_context
.turn_timing_state