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
As suggested by the bot, I'm opening a V2 issue — I was in #16626, carrying this over since that closed when the bot treated it as V1-only and ignored the follow-ups — including #54487. This is V2-only (dev branch). I've got that PR open with a working implementation.
description
experimental.idle fires after Complete() would return, with only { sessionID, background } — no vote, no bound. A plugin that wants to keep a session alive has to self-bound its loop.
Want experimental.stopping fired before Complete() in packages/core/src/session/runner/llm.ts (maybeContinueStopping), with { sessionID, step, stepsLimit, continuations, decision }, vote shouldContinue + text, capped at 2 continuations/turn.
Types: packages/plugin/src/effect/session.ts + promise mirror. Example consumer contrast: packages/core/src/plugin/goal.ts (uses experimental.idle today).
What it's for
pre-terminal veto, not idle notification. Not a replacement for packages/core/src/plugin/goal.ts: that owns the multi-round objective loop via experimental.idle. stopping is the bounded single-turn gate idle can't express:
- sql check before success: validate the SQL the turn produced, on failure force one more step with a short fix instruction. Needs a bound so an always-fail validator can't loop, and default-allow-stop when checks pass.
idleis too late becauseExecution.Succeededis already published. - typecheck before end: run check-types and inject errors so the agent fixes them in the same session.
idlere-prompt shows as a visible user message instead of a silent continuation. - verified-claims gate: scan the final message for high-risk claims and re-enter once with a correction. Emulated today via
idle→messages()→prompt(), butidlealso fires on abort, the correction pollutes the transcript, and the user can act on the answer during the round-trip. - opencode-dev-framework (my plugin, downstream waiter): completion gate runs today on
experimental.idle→session.synthetic, bounded bygate.max_blocks, plus a dedupedevent.subscribenet for the boundaries the hook skips. Withstoppingthe gate becomes a vote with runner-owned budget instead of self-accounting + net.
Without this, every gate re-implements its own bound on top of idle, after the runner already decided to stop.
Activity
opencodejr commentedon Oct 30, 2026
Thanks for carrying this over to v2 and for the detailed use cases.
In short, you're asking for an
experimental.stoppingsession hook that fires beforeComplete(). A plugin could vote to keep going (shouldContinueplus steer text), and the runner would enforce a limit on continuations per turn. That would replace gates built onexperimental.idle, which only fires after the runner has already decided to stop and which can't vote or see a budget.I checked
dev.experimental.idle({ sessionID, background }) is currently the only hook of this kind, so a plugin can't do this today. It needs the new hook in the plugin API. I've passed this to the maintainers to consider as a feature request.Related:
Stophook with similar semantics