What the gap is
@tangle-network/agent-eval/rollout owns the opencode session store. openOpencodeDb is a good citizen — read-only, returns null rather than throwing on a corrupt or half-written file — and discovery-lab uses it for every open.
readOpencodeSessionMessages is not used, and the reason is a capability gap rather than a preference. It projects the store into canonical chat messages, and that projection drops three fields that are present in the tables it reads:
| Dropped |
Where it lives |
What it answers |
per-message tokens {input, output, reasoning, cache} |
the message row |
what a single turn cost — the only per-turn cost signal that exists |
per-part time {start, end} |
the part row |
where a turn spent its wall time; which tool call was the slow one |
tool state.status |
the part row |
whether a call is pending, errored, or completed — and whether its output was ever persisted |
The supervisor's journal records that a worker existed, what it was called, and how it died. It records none of the above. The store is the only place these three exist, and the canonical projection is where they are lost.
What the consumer does today
discovery-lab/dashboard/lib/transcript.mjs opens the store with your openOpencodeDb and uses your session parser's column set, then reads the message and part rows directly for those three fields. That is a strictly additive read of the same tables through the same handle — it does not fork the schema — but it means the store has two readers in the stack instead of one, and the second one is in a viewer.
The file says so in its own header. Closing this gap deletes that function, not that file.
Suggested shape
An options flag on the existing reader rather than a second entry point, so the canonical projection stays the default:
readOpencodeSessionMessages(db, sessionId, { withUsage: true })
carrying, per message, the token object as recorded (absent rather than zero when the row has none — an unmeasured turn must not read as a free one), and per part, time and state.status as recorded.
Not asking for
No change to the default projection's shape, and no new dependency. The rows are already being read; this is about what survives the projection.
What the gap is
@tangle-network/agent-eval/rolloutowns the opencode session store.openOpencodeDbis a good citizen — read-only, returns null rather than throwing on a corrupt or half-written file — anddiscovery-labuses it for every open.readOpencodeSessionMessagesis not used, and the reason is a capability gap rather than a preference. It projects the store into canonical chat messages, and that projection drops three fields that are present in the tables it reads:tokens {input, output, reasoning, cache}time {start, end}state.statusThe supervisor's journal records that a worker existed, what it was called, and how it died. It records none of the above. The store is the only place these three exist, and the canonical projection is where they are lost.
What the consumer does today
discovery-lab/dashboard/lib/transcript.mjsopens the store with youropenOpencodeDband uses your session parser's column set, then reads the message and part rows directly for those three fields. That is a strictly additive read of the same tables through the same handle — it does not fork the schema — but it means the store has two readers in the stack instead of one, and the second one is in a viewer.The file says so in its own header. Closing this gap deletes that function, not that file.
Suggested shape
An options flag on the existing reader rather than a second entry point, so the canonical projection stays the default:
carrying, per message, the token object as recorded (absent rather than zero when the row has none — an unmeasured turn must not read as a free one), and per part,
timeandstate.statusas recorded.Not asking for
No change to the default projection's shape, and no new dependency. The rows are already being read; this is about what survives the projection.