Skip to content

fix(v2): restore auto-capture by mapping execution end to session.idle - #318

Closed
share121 wants to merge 1 commit into
tickernelz:mainfrom
share121:fix/v2-idle-execution-mapping
Closed

share121 wants to merge 1 commit into
tickernelz:mainfrom
share121:fix/v2-idle-execution-mapping

Conversation

@share121

Copy link
Copy Markdown
Contributor

Problem

On OpenCode v2, auto-capture and user-profile learning silently stop working: every user prompt is still recorded into user-prompts.db (so nothing looks broken), but captured never flips to 1, and no Auto-capture memory persisted entries are written.

Root cause

The V1 idle handler (src/index.ts) drives auto-capture and profile learning from the session.idle event. OpenCode v2 no longer emits session.idle at all, and it does not emit an idle session.status either — both are deprecated in the v2 schema. A finished turn is now signalled by the durable session.execution.succeeded / session.execution.failed / session.execution.interrupted events.

The v2 adapter (src/v2/legacy-client.ts, toLegacyEvent) only translated session.compaction.ended → session.compacted, so the idle handler was never invoked under v2.

Verified by subscribing to the live host event stream: over a full turn there were zero session.idle and zero session.status events, while session.execution.succeeded fired on every completed turn.

Fix

Map a finished session execution back to session.idle so the existing idle pipeline runs again:

const isIdleStatus = rawType === "session.status" && data?.status?.type === "idle";
const isExecutionEnd =
  rawType === "session.execution.succeeded" ||
  rawType === "session.execution.failed" ||
  rawType === "session.execution.interrupted";
const type =
  rawType === "session.compaction.ended"
    ? "session.compacted"
    : isIdleStatus || isExecutionEnd
      ? "session.idle"
      : rawType;

The existing session.status idle mapping is kept for host versions that still emit it.

Testing

  • Added unit tests covering the execution-end → session.idle mapping (and that session.execution.started is not treated as a turn end).
  • bun test tests/v2-legacy-client.test.ts → 8 pass / 0 fail.
  • End-to-end on OpenCode v2.0.16: after restart, Auto-capture memory persisted entries appear again and captured flips to 1 in user-prompts.db.

OpenCode v2 no longer emits the deprecated session.idle event, nor an
idle session.status, so the V1 idle handler that drives auto-capture and
user-profile learning never fired. Map a finished session execution
(succeeded/failed/interrupted) back to session.idle so the existing idle
pipeline runs again.
@EyJunge1

Copy link
Copy Markdown
Collaborator

OpenCode v2 support is already on main (#311). It just isn't in a release yet, so npm still installs v2.26.0, which does not load on OpenCode v2.

I'll cut the release once I'm a maintainer again. Until then a current maintainer has to publish it.

@karaaslanz

Copy link
Copy Markdown
Collaborator

I traced this against current main and the OpenCode v2 event path. The mapping itself looks consistent: opencode-mem's shared consumer only starts auto-capture/profile learning from legacy session.idle, while OpenCode v2 treats session.execution.succeeded, session.execution.failed, and session.execution.interrupted as terminal states that return a session to idle.
One regression-coverage gap I would close: tests/v2-plugin-adapter.test.ts still feeds the adapter a synthetic session.idle event, even though this PR exists because V2 no longer emits that event. The new v2-legacy-client test proves toLegacyEvent() in isolation, but it does not exercise the full ctx.event.subscribe() → eventBelongsToLocation() → toLegacyEvent() → legacy.event() path where the real failure occurs.
I'd change/add the adapter-level fixture to emit session.execution.succeeded and assert that the legacy consumer receives session.idle. That would lock the actual bridge behavior, not only the converter helper.
The Actions on this head are currently action_required with no jobs started, so I haven't treated CI as validation yet.

@stysus

stysus commented Sep 30, 2026

Copy link
Copy Markdown

Independent confirmation on OpenCode v2.0.19 (Linux, plugin built from main).

Symptom reproduced: every prompt was still written to user-prompts.db, but captured stayed 0 and capture_attempts stayed 0 for all of them — i.e. performAutoCapture was never invoked.

Root cause confirmed by instrumenting the v2 event bridge. ctx.event.subscribe() in registerV2Adapter works fine and events pass eventBelongsToLocation, but session.idle is never emitted. Full set of session.* events observed over a turn:

session.inbox.enqueued / session.inbox.delivered
session.instructions.updated
session.execution.started / session.execution.succeeded
session.step.started / .streamed / .ended
session.reasoning.started / .delta / .ended
session.text.started / .delta / .ended
session.usage.updated
session.renamed

No session.idle appeared even 60s after the turn finished with the server still alive, so this is not a timing or threshold issue.

Fix verified. With session.execution.{succeeded,failed,interrupted} mapped to the legacy session.idle hook (same approach as this PR), auto-capture fires end-to-end:

Auto-capture memory persisted: {"promptId":"prompt_...","sessionID":"ses_...","memoryId":"mem_1790741548439_thsbslphi"}

The memory was then read back from the store and its content matched the session. It also fires correctly in a real TUI session, not only in a headless test.

One note for whoever ships the release: this only reaches users once #322 (npm 2.26.0 still exporting the V1-only plugin shape) is resolved too — right now building from main is the only way to run the v2 path at all.

@EyJunge1

EyJunge1 commented Oct 1, 2026

Copy link
Copy Markdown
Collaborator

Closing as superseded by #332 (v2 auto-capture / session.execution → session.idle already merged). Thanks for the investigation and fix!

@EyJunge1 EyJunge1 closed this Oct 1, 2026
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.

4 participants