fabro/lib/foundation
Bryan Helmkamp 1978257aa5
Interrupt a live agent stage's model turn over Petri
Petri 639ce3e added `ControlService::interrupt_firing` and the `LiveTurns`
capability a host installs beside the pause hooks. Fabro now drives it:
`RunControls::interrupt(stage, text)` resolves its stage the way a steer
does (a label, a node name, or the run's one live agent stage) and stops
that stage's current model turn, keeping the session; the text, when
given, is the stage's next input. `engine::run` installs the live-turn set
as a runtime capability, so without it no interrupt could ever land.

The worker maps `run.interrupt` and `run.interrupt_then_steer`, both of
which now carry an optional `stage`, to that call. The control bus is
one-way, so a refusal is recorded the way a refused steer is: a
`run.notice` on the run's stream whose code says why (`no_live_turn` when
Petri refuses a stage with no turn in flight, `no_such_stage`,
`interrupt_refused`).

The server's `POST /runs/{id}/interrupt` and `POST /runs/{id}/steer` with
`interrupt=true` forward the control and answer 202, replacing the 501
`interrupt_unsupported` stub. The interrupt endpoint takes an optional
body (`stage`, `text`), refuses a finished run with 409
`run_not_interruptible`, and forwards an interrupt of a blocked run, since
an agent stage may be running a turn beside the question and the worker
judges each stage itself. `fabro events --pretty` prints the delivered
interrupt and the stage's `attractor.turn.interrupted` report.

Verified on the twin: `fabro steer --interrupt` during a long tool call
ends the turn, the text is the agent's next request, and the stream
carries the `$interrupt` record and the interrupted-turn report; an
interrupt of a gate stage is refused with `no_live_turn` and the gate's
question is untouched.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-18 22:38:17 -04:00
..
build-support refactor: organize crates into three layers 2026-07-23 17:59:34 -04:00
fabro-api Show the sandbox's ready duration and retention outcome on the run 2026-09-18 19:34:56 -04:00
fabro-auth Name built-in providers through lithos catalog::builtin 2026-09-10 09:48:37 -06:00
fabro-client Interrupt a live agent stage's model turn over Petri 2026-09-18 22:38:17 -04:00
fabro-config Settle clippy and formatting after the sandbox merge 2026-09-18 18:39:58 -04:00
fabro-db Drop the run_events table and narrow the runs row 2026-09-18 14:37:18 -04:00
fabro-dev Delete fabro-hooks and the engine freeze check 2026-09-18 10:47:56 -04:00
fabro-http Depend on lithos-llm and the published twin-openai crate 2026-09-09 17:26:57 -06:00
fabro-macros refactor: organize crates into three layers 2026-07-23 17:59:34 -04:00
fabro-oauth refactor: organize crates into three layers 2026-07-23 17:59:34 -04:00
fabro-options-metadata refactor: organize crates into three layers 2026-07-23 17:59:34 -04:00
fabro-proc Consolidate server Git subprocess ownership 2026-09-11 17:36:40 -06:00
fabro-redact Give the checkout its GitHub credentials through the driver's store 2026-09-11 10:02:01 -06:00
fabro-static Remove the engine flag: every run is a Petri run 2026-09-18 09:58:21 -04:00
fabro-telemetry refactor: organize crates into three layers 2026-07-23 17:59:34 -04:00
fabro-template Simplify model stylesheet template plumbing 2026-08-25 18:54:06 -04:00
fabro-test Render the branch, identity, diff and artifact records and re-record the run snapshots 2026-09-18 18:39:58 -04:00
fabro-types Show the sandbox's ready duration and retention outcome on the run 2026-09-18 19:34:56 -04:00
fabro-util Fix the rustdoc link warnings and gate rustdoc in CI 2026-09-18 19:24:34 -04:00
fabro-vault refactor: organize crates into three layers 2026-07-23 17:59:34 -04:00