Skip to content

feat: add noInterruptOnUserMessage and noContinueWhileChildrenActive options - #53

Open
harryzhou2000 wants to merge 2 commits into
willytop8:mainfrom
harryzhou2000:feat/no-interrupt-user-message
Open

feat: add noInterruptOnUserMessage and noContinueWhileChildrenActive options#53
harryzhou2000 wants to merge 2 commits into
willytop8:mainfrom
harryzhou2000:feat/no-interrupt-user-message

Conversation

@harryzhou2000

@harryzhou2000 harryzhou2000 commented Aug 4, 2026

Copy link
Copy Markdown

What changed

Adds two plugin-level options (both default false, so existing behavior is unchanged):

noInterruptOnUserMessage — when true, a new human message no longer pauses an active goal with stopReason: "user intervention":

  • the chat.message hook skips the immediate pause/abort;
  • the auto-continue claim guard no longer pauses on newHumanMessage / userInterventionDetected;
  • the idle continuation driver no longer pauses on userInterventionDetected.

The goal loop keeps running and the human message is included in the next continuation, so the message steers the loop (Codex-style) instead of stopping for /goal resume. Plugin-owned command and continuation messages were already excluded from intervention detection and remain so.

noContinueWhileChildrenActive — when true, auto-continue is deferred while the session has active child sessions (subagents, background tasks): the goal stays running, but the goal loop does not prompt the orchestrator over work a child is already doing. The gate lives in claimContinuationSource, which covers both continuation paths (normal continue and budget wrapup), and checks opencode's children + status endpoints through the existing shape adapter (children/status are replay-safe read-only operations). Hosts that cannot report children/status fail open and continue as before.

Why

For long-running autonomous goal loops, any user-role message currently stops the goal and requires an explicit /goal resume. That includes messages that are not real human steering, e.g. synthetic background-task result messages injected by some hosts, which can pause a goal seconds after it starts (observed in practice: a goal paused 55s in by a background-task completion notification). With the option enabled, such messages leave the loop untouched while real user input steers the next continuation.

In particular, OpenCode's experimental background subagents may interrupt an active goal: when a background-task result is delivered into the session as a synthetic user-role message (type: text, synthetic: true, no plugin metadata), the current chat.message path treats it as human intervention and pauses the goal. With noInterruptOnUserMessage: true those synthetic results no longer pause the loop; real human messages keep steering the next continuation instead of stopping the goal.

Conversely, while a background subagent is still running, the goal loop currently has no way to know it: the plugin's auto-continue fires on the orchestrator's own idle even though a child is doing the work, risking overlapping edits on the same files. noContinueWhileChildrenActive: true defers the continuation until the children are idle (matching the guard omo-slim applies to its own continuation nudge).

Checks run

  • CI job node-compatibility (npm run check) — passed locally on official Node 18.20.8, 20.20.2, 22.23.2, and 24.19.0 at the final commit (356/356 tests on each).
  • CI job quality-contracts:
    • npm run test:coverage — passed (Node 22)
    • npm run type:check — passed (NodeNext + Bundler)
    • npm run smoke — passed
    • Latest commit re-ran npm run check on official Node 22 — 356/356 pass (the four new tests included)
    • npm run test:mutation, npm run benchmark:behavior, npm run smoke:packed-host, npm run smoke:packed-tools, npm audit --omit=dev --audit-level=high, npm run pack:check — not completed locally (mutation alone exceeds 15 minutes on this machine and the run was interrupted); these are exercised by the GitHub pipeline once the workflow run is approved.
  • New tests (in test/goal-plugin.test.js):
    • noInterruptOnUserMessage:true keeps the goal running and steers the loop (chat.message + idle driver)
    • normalizeOptions defaults noInterruptOnUserMessage to false and keeps it boolean
    • noContinueWhileChildrenActive:true defers continuation while a child is active (defers, then continues once the child is idle)
    • active children do not block auto-continue by default
    • normalizeOptions defaults noContinueWhileChildrenActive to false and keeps it boolean
  • New tests (in test/opencode-session-api.test.js): children/status shape-adapter and replay-safety coverage.
  • Note: on the local machine's default (pixi) Node 24.18 build, test/persistence-lease.test.js and test/session-concurrency.test.js fail independently of this change (reproduced identically on pristine main); both pass on the official Node builds used by CI.

Manual OpenCode smoke testing

None against a live OpenCode host in this environment; the hook-level behavior is covered by the tests above.

Config

[
  "opencode-goal-plugin",
  {
    "noInterruptOnUserMessage": true,
    "noContinueWhileChildrenActive": true
  }
]

…ad of pausing)

When enabled, a new human message no longer pauses an active goal with
stopReason 'user intervention'. The goal loop keeps running and the
message steers the next continuation, matching Codex-style steering.
Gates the three pause sites (chat.message, auto-continue claim guard,
and the idle continuation driver); plugin-owned command/continuation
messages are never interventions either way. Default remains false.

Adds option docs to README, index.d.ts, and CHANGELOG, plus tests:
a chat.message + idle steering test and a normalizeOptions boolean test.
@harryzhou2000

harryzhou2000 commented Aug 4, 2026

Copy link
Copy Markdown
Author

Hi @willytop8 — this PR now adds two goal-loop options (defaults unchanged): noInterruptOnUserMessage (human messages steer instead of pausing the goal) and noContinueWhileChildrenActive (defer auto-continue while subagent/background children are active). CI runs are still waiting on workflow approval (action_required) — could you approve them to start the pipeline? The full CI matrix (npm run check on Node 18/20/22/24) passes locally at the final commit (356/356), plus coverage, type contracts, and smoke. Open to review feedback on either option.

…hile subagents run)

When enabled, auto-continue is deferred while the session has active child
sessions (subagents, background tasks): the goal stays running but the goal
loop does not prompt the orchestrator over work a child is already doing.
The gate lives in claimContinuationSource, which covers both continuation
paths (normal continue and budget wrapup). It checks opencode's children
and status endpoints through the existing shape adapter (children/status are
replay-safe read-only operations); hosts that cannot report children/status
fail open and continue as before. Default remains false.

Adds adapter coverage (children/status shape probing) and behavioral tests
(deferral while a child is busy, continuation once children are idle, and
unchanged default behavior).
@harryzhou2000 harryzhou2000 changed the title feat: add noInterruptOnUserMessage option (human messages steer instead of pausing) feat: add noInterruptOnUserMessage and noContinueWhileChildrenActive options Aug 4, 2026
harryzhou2000 added a commit to harryzhou2000/cfd_solver_agentic_benchmark_workspace_template that referenced this pull request Aug 4, 2026
…age patch)

Pin the willytop8 goal-plugin fork at 03c493c (feat/no-interrupt-user-message):
a no-interrupt config option so human messages steer an active goal instead
of pausing it. PR: willytop8/OpenCode-goal-plugin#53.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant