You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[FEATURE]: [V2] experimental.stopping hook (pre-Complete veto with budget) #60461
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 beforeComplete() in packages/core/src/session/runner/llm.ts (maybeContinueStopping), with { sessionID, step, stepsLimit, continuations, decision }, vote shouldContinue + text, capped at 2 continuations/turn.
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. idle is too late because Execution.Succeeded is already published.
typecheck before end: run check-types and inject errors so the agent fixes them in the same session. idle re-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(), but idle also 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 by gate.max_blocks, plus a deduped event.subscribe net for the boundaries the hook skips. With stopping the 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.
Thanks for carrying this over to v2 and for the detailed use cases.
In short, you're asking for an experimental.stopping session hook that fires before Complete(). A plugin could vote to keep going (shouldContinue plus steer text), and the runner would enforce a limit on continuations per turn. That would replace gates built on experimental.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.
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