fix: resolve the session agent so the plan guard cannot fail open - #67
Merged
Conversation
A live canary against OpenCode 1.18.25 showed the planning-only
restriction never firing in the real TUI: a goal set while the Plan
agent was selected was created live, auto-continued, and edited files.
Root cause: the guard read only the cached execution context, which the
host populates from `chat.message` / `chat.params` / `session.updated`.
OpenCode runs `command.execute.before` *before* any of those fire, so the
context is empty for the first command in a session — the exact case a
freshly opened Plan-mode session hits. `isRestrictedAgent(undefined)` is
false, so the restriction failed open precisely where it mattered most.
The unit tests passed because they drove a `chat.message` or
`session.updated` first, encoding my assumption rather than the host's
behavior.
Fall back to the session record, which carries the selected agent from
the moment the user picks it. The cached context is still preferred, so a
known agent costs no extra call, and a host that cannot report the agent
still fails open rather than holding every goal.
Verified live on the same canary that found the bug. With the documented
install config, a goal set under Plan now records:
stopped: true, stopReason: "plan agent active", turnCount: 0
history: [set, paused]
against the previous `stopped: false` with auto-continues. Zero
auto-continues means no unattended work.
README now states precisely what the restriction does and does not
prevent: it stops the goal loop, but cannot stop a model acting on the
single routed command turn, which is the already-documented
`command.execute.before` interception limitation.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The bug this fixes
A live canary against a real OpenCode 1.18.25 TUI showed the planning-only restriction merged in #64 never firing in practice. With the Plan agent selected,
/goal make index.js print goodbyeproduced:The goal was created live, ran the auto-continue loop, and edited the file. Reproduced twice, with and without an
agentpin on the command config.Root cause
The guard read only the cached execution context, populated from
chat.message/chat.params/session.updated. OpenCode runscommand.execute.beforebefore any of those fire, so the context is empty for the first command in a session — exactly the case a freshly opened Plan-mode session hits.isRestrictedAgent(undefined)returns false, so the restriction failed open precisely where it mattered most.The unit tests in #64 passed because they drove a
chat.messageorsession.updatedfirst. They encoded my assumption about the host rather than the host's actual behavior.Fix
Fall back to the session record (
Session.agent), which carries the selected agent from the moment the user picks it. Confirmed present in 1.18 and verified to readplanafter switching.Verification
Re-ran the same live canary that found the bug, using the documented install config. With the fix:
turnCount(auto-continues)stoppedfalsetruestopReason''plan agent activeset, auto-continue, warning, auto-continueset, pausedZero auto-continues means no unattended work.
Also verified live in the same run:
sessionTitleStatusrenders correctly in the real TUI —⏸ make index.js print goodbye instead of hi · 0/10 · 0s · 25k/200k, with▶for a running goal.npm run release:check→ exit 0: 414 tests (411 before; +3 new), mutation 68/68, benchmark 100, 0 vulnerabilities.The new regression test reproduces the live failure exactly — agent visible only on the session record, no execution context — and was confirmed to fail against the pre-fix code.
Honest scope
The README now states precisely what the restriction does and does not prevent. It stops the goal loop (zero auto-continues). It cannot stop a model acting on the single routed command turn, because
command.execute.beforedoes not fully intercept command text — the already-documented limitation. In the canary the free test model did edit the file on that one turn despite being told not to; the plugin correctly held the goal and never continued it.🤖 Generated with Claude Code