Repository navigation
Description
Feature hasn't been suggested before.
- I have verified this feature I'm about to request hasn't been suggested before.
Describe the enhancement you want to request
Related: #56115 (plugin inbox.cancel and a busy snapshot), #58776 (conditional admission with an expected-turn guard, closed by its author), #60461 / #54487 (experimental.stopping), #61884 (execution continuity). References are to dev at 893e8dc43f05e872d4cac603486ce8a7181170fa, from reading the source; the repro below has not been executed.
Problem
A plugin can use experimental.idle as a completion reviewer: after the model's last step it decides, often with a model call, whether to continue, and admits ctx.session.synthetic(...) to keep the same execution going. The built-in /goal plugin works this way.
If the user stops the Session after the continuation is admitted but before it is promoted, the continuation survives the stop:
SessionRunCoordinator.interrupt(packages/core/src/session/run-coordinator.ts) interrupts the execution fiber and claims its recorded wakes.- Settlement of a non-shutdown interrupt runs
jobs.cancel(packages/core/src/session/execution.ts), but pending inbox rows stay pending. - With
interrupt({ resume: true })the continuation is promoted on the resume wake; without it, it is promoted with the user's next prompt.
inbox.cancel from #56115 would let a plugin retract the row, but a plugin reacting to the interruption races the resume wake. Promise-API session hooks also get no AbortSignal (packages/plugin/src/promise/adapter.ts, host.session.hook wraps the callback in Effect.promise(() => ...)), so a promise handler can still call synthetic after the stop.
Proposal
An opt-in synthetic option, scope: "execution". The runner records the inbox ids admitted with it during an execution. When that execution ends by a non-shutdown interrupt, settlement cancels those rows that are still pending, through the existing SessionInbox.cancel and its InboxCancelled event, before any resume wake reads the inbox. The default, "session", keeps today's behaviour, and a shutdown interrupt is unchanged, since restart continuity is intended there.
This mirrors how interrupt already claims the interrupted intent's wakes.
Smaller, related:
- Promise-API session hooks could receive an
AbortSignalthat is aborted when the execution is interrupted. - A user prompt sent while the reviewer runs is promoted beside its continuation, because pending steers are published together (
SessionInbox.promote,packages/core/src/session/inbox.ts). The conditional admission asked for in [FEATURE]: conditional steering admission with an expected-turn guard #58776 would close that race too. Note thatSessionInbox.admitdoes not take the per-Session lock (SessionInbox.serialized) thatpromoteholds, so such a check would need admission to take it.
Repro sketch (not executed)
In the style of packages/core/test/session-runner.test.ts:
- Register an
experimental.idlehandler that waits on a deferred, then callsctx.session.synthetic({ sessionID, text: "continue" }). - Prompt the Session and let the model finish one step, so the handler blocks.
- Resolve the deferred, then call
interrupt({ sessionID, resume: true })before the runner promotes the row. - Observe that "continue" is promoted and a model step runs after the stop.
Expected with scope: "execution": the row is cancelled and no step runs.
Activity
opencodejr commentedon Nov 5, 2026
Thanks for the detailed write-up and the code references!
To restate the request: you'd like an opt-in
scope: "execution"option onctx.session.synthetic. A continuation admitted by anexperimental.idlehook (for example a/goal-style reviewer) would then be cancelled if the user stops the Session before it's promoted, including withresume: true. Shutdown interrupts and the default"session"scope would keep today's behaviour. You also suggest anAbortSignalfor promise-API session hooks, so a handler can tell that the execution was stopped.This can't really be done in a plugin today. Reacting to the interrupt races the resume wake, and promise hooks don't see the stop. So it would need support in core or in the plugin API. Maintainers will take a look and consider it.
Related issues:
inbox.canceland a busy query, the closest existing requestexperimental.stoppingcontinuation hook