Skip to content

fix(opencode): agent goes idle after a v2 question is dismissed - #6177

Merged
paseo-bot[bot] merged 2 commits into
mainfrom
opencode-v2-dismissed-question-idle
Oct 5, 2026
Merged

paseo-bot[bot] merged 2 commits into
mainfrom
opencode-v2-dismissed-question-idle

Conversation

@paseo-bot

@paseo-bot paseo-bot Bot commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor

Linked issue

Closes #6136

Refs #3749 (the same Stop error on other providers, with a different cause)

Type of change

  • Bug fix
  • New feature
  • Enhancement
  • Refactor
  • Docs

Reasoning

Dismissing an OpenCode v2 question from Paseo, from the question card or with paseo permit deny, left the agent at running for good. Stop, reload, and sending a message were then refused with active run cancellation was not acknowledged, and only a daemon restart freed it. With this change the turn ends as canceled when the question is dismissed, and the agent is idle and accepts the next message.

OpenCode ends the execution when its question is dismissed, but reports the interruption with the reason shutdown. Paseo deliberately keeps a turn open on a shutdown interruption, because OpenCode uses the same reason for an execution it will resume (#5674 and its tests). Nothing resumes after a dismissal, so the turn never closed. Paseo knows when it dismissed the question itself, so that interruption now ends the turn, and every other shutdown interruption keeps the existing wait.

Goals

  • Dismissing an OpenCode v2 question in the agent's own session ends the turn as canceled, and the agent goes idle.
  • The next message after a dismissal runs normally.
  • A shutdown interruption that Paseo did not cause still keeps the turn open for a resume, as before.

Non-goals

QA

Real OpenCode v2.0.20 through the daemon (OpenCodeRuntimeClient selecting v2), model through OpenRouter, in a temporary real-provider test that is not part of this PR:

  1. Created an OpenCode agent in build mode and asked it to use its question tool for one single-select question.
  2. When the question arrived as a pending permission, denied it the way paseo permit deny does (agent_permission_response with behavior: "deny").
  3. Before (current main): the permission resolved, but the agent stayed running for 30s. Instrumented logs showed OpenCode emitting form.cancelled, then session.execution.interrupted with reason: "shutdown", and session.active returning {} on every later reconcile.
  4. After: the agent went to idle, and a follow-up message ran to completion with the agent back at idle.

Regression test: packages/server/src/server/agent/providers/opencode/v2/agent.test.ts, "ends the turn when the user dismisses a question". It checks the turn is canceled and that the next turn completes. It fails on main (no terminal turn event) and passes with this change.

$ npx vitest run src/server/agent/providers/opencode/
      Tests  160 passed (160)

Risk surface: the OpenCode v2 adapter only (turns.ts, session.ts, permissions.ts). The existing shutdown-and-resume tests pass unchanged.

Checklist

  • Plugin changes follow the SDK import boundaries (if applicable)
  • One focused change
  • npm run typecheck passes
  • npm run lint passes
  • npm run format passes
  • QA evidence
  • Tests added or updated where it made sense

OpenCode ends the execution when a question is dismissed but reports the
interruption with the reason "shutdown", which the turn tracker keeps open
waiting for a resume that never comes. The agent stayed running, and Stop,
reload and send were refused. A question dismissed from Paseo now lets that
interruption end the turn as canceled.
@greptile-apps

greptile-apps Bot commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerConfidence Score: 5/5

[Medium risk] Fixes agent state handling when a question is dismissed.

The PR appears safe to merge.

Summary

Denying a question in an OpenCode v2 session now cancels the turn so the agent can take another message. Shutdown interruptions without a Paseo question dismissal still wait for a resume.

  • Paseo marks denied questions from its own session.
  • Turn reconciliation uses that mark to tell a dismissal from a resumable shutdown.
  • A regression test covers cancellation and the next message completing.

Reviews (2) · Last reviewed commit: "test(opencode): cover the next message a..."

@paseo-bot paseo-bot Bot added diagnosed The diagnose bot investigated this; on a PR, the bot authored it and removed diagnosing The diagnose bot is working on this issue labels Oct 5, 2026
@paseo-bot
paseo-bot Bot merged commit 7a30305 into main Oct 5, 2026
20 checks passed
@paseo-bot
paseo-bot Bot deleted the opencode-v2-dismissed-question-idle branch October 5, 2026 23:06

@paseo-bot paseo-bot Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs from you: nothing, merged
Product delta: Before, dismissing an OpenCode v2 question from Paseo (question card or paseo permit deny) left the agent running for good, and Stop, reload, and send were refused until the daemon restarted. After, the agent goes idle and the next message runs.
Protocol delta: none

This pull request ends the OpenCode v2 turn as canceled when Paseo itself dismisses a question in the agent's own session. Every other shutdown interruption still keeps the turn open for a resume.

Review

  1. Does it fix what was reported? Yes. The diagnosis observed OpenCode sending form.cancelled and then session.execution.interrupted with reason: "shutdown". SessionTurns.reconcileTurn (turns.ts) returns early on that reason, so no terminal turn event is emitted and interrupt() waits on a completion that never settles. That is the reporter's stuck running state and the Stop error. The regression test drives respondToPermission(deny) through the session and checks for turn_canceled followed by a completed next turn. It does not depend on how the fix is implemented.
  2. Is it the right fix? Yes, within its scope. The early return for shutdown is intended (#5674, whose tests still pass). OpenCode gives a dismissed question the same reason, so the adapter has to use what Paseo itself knows: it dismissed the question. The flag lives on the turn in SessionTurns, the layer that owns the turn outcome, and only changes what a shutdown means for that turn. Possible reshaping for later: denied tool permissions get stuck the same way (see #6184). If that turns out to be the same shutdown interruption, questionDismissed could be generalised to "Paseo rejected a request in this turn". This PR does not need that change.
  3. Does it do anything else? No. The change touches only the OpenCode v2 adapter (turns.ts, session.ts, permissions.ts) and a test. There is no protocol, schema, setting, dependency, or design change. The diagnose report's notes (dismissals made outside Paseo, child-session questions, and steer-cleared questions keep the old wait; the broader Stop recovery in #3749) are follow-ups and none blocks this merge.
  4. Is it worth shipping? Yes. Any OpenCode v2 user who dismisses a question hits it every time, and the only recovery is restarting the daemon, which interrupts every other agent. The fix is 18 lines in one provider adapter.

QA checklist (real OpenCode 2.0.20 through an isolated daemon built from this branch, with the web app where noted)

  1. Reported scenario: pass. On the PR head, paseo permit deny on a real question left the agent idle. Stop returned without error, reload succeeded, and a follow-up paseo send completed. On the merge base, the same deny left the agent running after 11 s, and Stop printed active run cancellation was not acknowledged.
  2. Regression test: pass. "ends the turn when the user dismisses a question" failed with the three source files at the merge base (no terminal event) and passed on the head. The full OpenCode provider suite passed (15 files, 160 tests), and typecheck and lint were clean.
  3. Answering a question: pass. Selecting an option in the app and submitting it continued the turn with the answer, and the turn ended idle.
  4. Stop on a normal turn: pass. Stopping a running turn with no question made the agent idle, and the next message completed.
  5. Dismiss from the web app's question card: pass. The card closed, and a message sent from the composer ran to completion (before and after screenshots taken).
  6. Denying a non-question tool permission: same behavior on head and merge base, so this PR does not change it. In both, the agent stayed running and Stop was refused. That is a separate existing bug, filed as #6184.

Outside the checklist

  • Denied tool permission leaves the agent stuck: real, shown on main, and not caused by this PR. Filed as #6184.
  • One OpenRouter model called a tool name OpenCode does not have, so the QA run switched models to get a real question. This is model behavior, not counted.
  • Lint first picked up the QA run's temporary daemon files and was clean once they were removed. This came from the test environment, not counted.

Review threads: the one thread (follow-up turn untested) was answered in 3be3cbd and is resolved. The test now covers the next message.

Decision: merged as a squash. Closes #6136.
Reviewed commit: 3be3cbd

@paseo-bot paseo-bot Bot added the community-notified Merged work has been communicated to the people who reported or discussed it label Oct 5, 2026
wz-gsa pushed a commit to GSA-TTS/agentic-coding-patterns that referenced this pull request Oct 19, 2026
## Summary

Bumps the `paseo` acq kit from Paseo 0.10.2 to 0.12.0:
`PASEO_CLI_VERSION` in `spec.yaml` and the fallback default in
`paseo-start.sh`.

0.11.0 and 0.12.0 fix OpenCode v2 under Paseo, which matters now that
the `opencode` kit installs v2 in every OpenCode sandbox: agents stuck
after a dismissed question or denied permission (getpaseo/paseo#6177,
#6188), multi-select answers (#6149), daemon OOM on long v2 streams
(#6262), and HTTP 500s instead of Paseo tool errors (#6519, #7183).
0.11.0 also fixes `paseo daemon status` reporting a running daemon as
stopped after the host wakes from sleep (#6279).

The daemon launch surface the kit uses (`paseo daemon run`, `paseo
daemon restart`, the `config.json` settings) is unchanged. The release
notes' breaking changes don't touch the kit: `paseo plugin add
owner/repo` installs from the registry (0.11), `paseo run --background`
is deprecated in favor of `--no-wait`, and `paseo permit allow|deny`
exit non-zero when the daemon doesn't confirm (0.12).

## Verification

- Paseo node tests (16/16) and `validate-kits.py --strict` pass.
- Live `RUN_ACQ=1` kit verify (msb 0.7.8, acq 4.0.2): all checks pass.
- Downstream, with a team kit and the `opencode` kit at 2.0.30 on
`devenv-shell:1.0.0`: Paseo 0.12.0 ran an OpenCode 2.0.30 agent with the
team config, and the session survived a backup, a wiped `opencode.db`,
and a restore, then answered a follow-up.
- Seen once: on first `acq run`, the wrapper's `paseo daemon restart
--timeout 30` reported failure and the kit fell back to the supervisor
respawn, which worked; the daemon log shows the restart went through. A
manual `paseo daemon restart` afterwards succeeded in under 4 s.

## Eligibility & Accountability

- [x] I am a **federal employee or contractor** (PRs merge with
`@GSA-TTS/agentic-coding-team` code-owner approval; eligibility is
attested here, not authenticated).
- [x] **I understand this change and can explain it in my own words** —
I am the author of record and accountable for it.
- [x] By opening this PR, I **attest I have the right to release this
work** under the repository's CC0-1.0 dedication.

## AI Assistance Disclosure

- [x] AI tools were used in this contribution.
- [x] I verified all AI-generated content.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

community-notified Merged work has been communicated to the people who reported or discussed it diagnosed The diagnose bot investigated this; on a PR, the bot authored it

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug: OpenCode v2 agent stays running after a question is dismissed; Stop, reload and send all refused

0 participants