Skip to content

fix(opencode): handle session.idle event for turn-end detection#771

Open
dipree wants to merge 2 commits intomainfrom
fix/opencode-session-idle-event
Open

fix(opencode): handle session.idle event for turn-end detection#771
dipree wants to merge 2 commits intomainfrom
fix/opencode-session-idle-event

Conversation

@dipree
Copy link
Contributor

@dipree dipree commented Mar 25, 2026

Summary

  • OpenCode ≥1.3.x changed the session event model: session.status now only carries "busy"/"retry" (never "idle"), and a separate session.idle event fires when the agent finishes a turn
  • The plugin only matched session.status with status.type === "idle", so turn-end hooks stopped firing entirely after upgrading from 1.2.x to 1.3.x
  • Added session.idle as a fall-through case alongside session.status for cross-version compatibility

Root cause

Confirmed via diagnostic plugin logging. After the OpenCode 1.2.x → 1.3.x upgrade, the plugin's event handler receives:

// Many of these during a turn — status is always "busy", never "idle":
{"type":"session.status","props":{"sessionID":"ses_...","status":{"type":"busy"}}}
{"type":"session.status","props":{"sessionID":"ses_...","status":{"type":"busy"}}}

// One of these when the agent finishes — this is what the plugin needs to trigger turn-end:
{"type":"session.idle","props":{"sessionID":"ses_..."}}

The plugin checked props?.status?.type !== "idle" which always evaluated to true (since type is "busy"), so the handler broke out of every session.status event. The session.idle event that actually signals turn completion was unhandled.

Impact chain

  1. turn-end hook never fires → PrepareTranscript never runs
  2. opencode export <sessionID> never called → transcript file (.entire/tmp/<sessionID>.json) never created
  3. Post-commit condensation fails silently: "transcript not found at .entire/tmp/<sessionID>.json: file does not exist"
  4. Entire-Checkpoint trailer is still added to commits, but no session data is written to entire/checkpoints/v1
  5. Checkpoint directories on GitHub are empty

Fix

Handle both event types as a fall-through case. session.idle has properties.sessionID directly, while session.status requires checking properties.status.type for "idle" (for ≤1.2.x backwards compatibility).

Test plan

  • Diagnostic logging confirmed session.idle is emitted by OpenCode 1.3.2
  • Diagnostic logging confirmed session.status only carries "busy" in 1.3.2
  • Start an OpenCode session with the updated plugin
  • Make changes and commit — verify turn-end appears in .entire/logs/entire.log
  • Verify transcript file is created at .entire/tmp/<sessionID>.json
  • Verify session condensed log entry with non-zero transcript_lines
  • Push and verify checkpoint data appears on entire/checkpoints/v1 branch

🤖 Generated with Claude Code

OpenCode stopped reliably emitting `session.status` events with
`status.type === "idle"` after a recent update (~v1.3.x). Instead,
it now emits `session.idle` events. The plugin only listened for
`session.status`, causing turn-end hooks to never fire.

Without turn-end, `PrepareTranscript` never runs, the transcript file
is never created, and condensation silently fails on every commit with:
"transcript not found at .entire/tmp/<sessionID>.json: file does not exist"

This results in empty checkpoint directories on the metadata branch —
the `Entire-Checkpoint` trailer is added to commits but no session data
is written to `entire/checkpoints/v1`.

Handle both `session.idle` and `session.status` (with idle type) as a
fall-through case for cross-version compatibility.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: bd73a3b73f81
@dipree dipree requested a review from a team as a code owner March 25, 2026 13:03
Copilot AI review requested due to automatic review settings March 25, 2026 13:03
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR updates the embedded OpenCode plugin template so Entire can reliably detect turn completion across OpenCode versions by handling the newer session.idle event in addition to session.status idle events.

Changes:

  • Add session.idle as an additional switch case to trigger the turn-end hook.
  • Branch per event type to extract sessionID from the correct event shape while keeping the sync hook execution behavior.

Comment on lines +118 to +122
case "session.idle":
case "session.status": {
// session.status fires in both TUI and non-interactive (run) mode.
// session.idle is deprecated and not reliably emitted in run mode.
const props = (event as any).properties
if (props?.status?.type !== "idle") break
const sessionID = props?.sessionID ?? currentSessionID
// session.status fires with status.type "idle" when the agent finishes.
// session.idle is a separate event that some OpenCode versions emit instead.
// Handle both for compatibility across OpenCode versions.
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

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

Consider adding a regression assertion in the existing Go hook installation tests (cmd/entire/cli/agent/opencode/hooks_test.go) that the generated plugin content contains handling for both "session.status" and the newly-added "session.idle" event. Without a test, this compatibility fix could be accidentally removed or drift during future template edits without being caught by CI.

Copilot uses AI. Check for mistakes.
OpenCode ≥1.3.x changed session.status to only carry "busy"/"retry"
and introduced a separate session.idle event for turn completion.
Confirmed via diagnostic plugin logging.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: 5a1b1939a74a
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants