Track OpenCode child sessions under root sessions#343
Conversation
|
Nice fix for the child session attribution. One related issue this PR doesn't address: discoverFromDb still emits one SessionSource per root session with path set to "${dbPath}:${sessionId}". That composite string isn't a real filesystem path, so when parser.ts calls fingerprintFile(source.path) → stat(filePath), stat always fails and returns null. The cache reconciler then treats every OpenCode session as changed on every run, bypassing the session cache entirely. The fix is to emit one SessionSource per DB file (path = dbPath) and move the root-session iteration inside createParser, which already has the open DB connection. That way fingerprintFile can stat the actual .db file and the cache works as intended. |
|
@vmvarela thanks for calling this out. #347 added a generic fingerprint fallback, and I opened #348 as the OpenCode provider-side follow-up using the shape you suggested: discovery now emits one source per real I also added a synthetic cache proof in the PR showing the cache key moves from |
Summary
Addresses the OpenCode portion of #336 by tracking agent/subagent work that OpenCode stores in child sessions.
OpenCode records sessions in SQLite with a
session.parent_idhierarchy. Before this PR, CodeBurn discovered only root sessions and then parsed only messages whosemessage.session_idmatched that root. That meant work done by OpenCode child or grandchild sessions could be invisible in CodeBurn, including token usage, tool calls, bash commands, and costs.This PR keeps discovery root-only to avoid double counting, but changes parsing so a root session includes its unarchived child-session subtree.
Root cause
The existing parser used root-session queries like this:
That works for a flat session, but not for OpenCode's parent-child session shape:
Because the query only matched the root session id, CodeBurn ignored the child and grandchild rows even though they belong to the same user-visible OpenCode task.
What changed
messageandpartreads now use a recursivesession_treeCTE starting from the discovered root session.parent_id IS NULLdiscovery query. This prevents double counting.sessionId, so downstream session summaries stay grouped under the root task.opencode:<message-session-id>:<message-id>. This avoids collisions between root and child messages.Example behavior
Given this SQLite session tree:
Before:
After:
Tests added
sessionId.taskand grandchild bash commands are surfaced.Validation
npx vitest run tests/providers/opencode.test.ts tests/provider-registry.test.ts- 49/49 tests passed.npx tsc --noEmit --pretty false- passed.npm run build- passed.git diff --check- passed.Scope and privacy notes
I did not use real local OpenCode history or reporter data. The validation uses synthetic SQLite fixtures only, so no local project names, prompts, paths, session IDs, usage values, or private product details are included.
This PR intentionally does not change OpenCode zero-usage router-call handling from #342. If #342 merges first, this branch may need a small rebase because both PRs touch
src/providers/opencode.ts, but the behaviors are separate.