Bug Description
The opencode adapter's message.updated handler (src/adapters/opencode/plugin.ts:544-579) fires on every step-finish within a turn. The event's .cost is the CUMULATIVE turn cost (the comment at plugin.ts:558-564 states "cost is cumulative for the turn"). Each firing calls db.insertEvent(sessionId, usageEvent), which INSERTs a new agent_usage row (src/session/db.ts:1185). The dedup check (checkDuplicate, keyed on type + data_hash over the last 5 events, db.ts:1175-1178) does not hit because the data string includes the per-step token snapshot, which changes each step.
Result: a 3-step turn writes 3 agent_usage rows, each carrying the cumulative cost so far (e.g. $0.02, $0.04, $0.05). The events are forwarded to the platform with cost_usd in the top-level envelope (src/session/extract.ts:1898, 1941), where the natural additive reading sums them to $0.11 instead of the true $0.05.
The code comment claims "re-emitting is idempotent at the cost column", but there is no cost-column update: each firing appends a row. This is the same cumulative-cost class that the task-usage path explicitly guards against by stripping cost_usd and tagging usage_scope="task_cumulative" (src/session/extract.ts:56-59); the opencode adapter reintroduces it.
Steps To Reproduce
- Configure context-mode with the opencode adapter.
- Run a turn that triggers multiple steps (e.g. a tool-calling turn with 3 model calls).
- Observe:
agent_usage events in session_events : one row per step, each with the cumulative cost_usd.
- Sum
cost_usd across the rows for the turn (the natural additive reading, and what the platform receives): the total exceeds the turn's actual cost.
Expected Behavior
The turn's cost should be emitted exactly once (or as deltas), so additive aggregation yields the true cost.
Actual Behavior
Multi-step turns emit one cumulative-cost row per step; additive aggregation over-counts by the sum of the intermediate cumulative values.
Environment
- context-mode main (e27c143)
- opencode adapter
- macOS 15
Suggested fix
Emit the usage event once per turn (e.g. only on the final step-finish / message completion), or upsert/replace the previous agent_usage row for the same assistant message, or store per-step deltas instead of cumulative values on multi-fire rows. Update the misleading "idempotent at the cost column" comment either way.
Bug Description
The opencode adapter's
message.updatedhandler (src/adapters/opencode/plugin.ts:544-579) fires on every step-finish within a turn. The event's.costis the CUMULATIVE turn cost (the comment at plugin.ts:558-564 states "cost is cumulative for the turn"). Each firing callsdb.insertEvent(sessionId, usageEvent), which INSERTs a newagent_usagerow (src/session/db.ts:1185). The dedup check (checkDuplicate, keyed on type + data_hash over the last 5 events, db.ts:1175-1178) does not hit because the data string includes the per-step token snapshot, which changes each step.Result: a 3-step turn writes 3
agent_usagerows, each carrying the cumulative cost so far (e.g. $0.02, $0.04, $0.05). The events are forwarded to the platform withcost_usdin the top-level envelope (src/session/extract.ts:1898, 1941), where the natural additive reading sums them to $0.11 instead of the true $0.05.The code comment claims "re-emitting is idempotent at the cost column", but there is no cost-column update: each firing appends a row. This is the same cumulative-cost class that the task-usage path explicitly guards against by stripping
cost_usdand taggingusage_scope="task_cumulative"(src/session/extract.ts:56-59); the opencode adapter reintroduces it.Steps To Reproduce
agent_usageevents insession_events: one row per step, each with the cumulativecost_usd.cost_usdacross the rows for the turn (the natural additive reading, and what the platform receives): the total exceeds the turn's actual cost.Expected Behavior
The turn's cost should be emitted exactly once (or as deltas), so additive aggregation yields the true cost.
Actual Behavior
Multi-step turns emit one cumulative-cost row per step; additive aggregation over-counts by the sum of the intermediate cumulative values.
Environment
Suggested fix
Emit the usage event once per turn (e.g. only on the final step-finish / message completion), or upsert/replace the previous
agent_usagerow for the same assistant message, or store per-step deltas instead of cumulative values on multi-fire rows. Update the misleading "idempotent at the cost column" comment either way.