Context
Session.getUsage() converts absent provider usage fields to zero. Separately, message_complete is emitted only when an assistant message has a clean finish value. Failed or aborted turns can therefore disappear from message telemetry, while missing usage can appear indistinguishable from a genuine reported zero.
A live z.ai probe confirmed that thinking mode still returns standard OpenAI-shaped usage. Thinking parameter shape is not part of this defect.
Problem / Goal
Ensure every terminal assistant turn is represented exactly once and distinguish provider-reported usage, genuinely reported zero usage, missing usage, and any explicit estimate.
Proposed Approach
Extend terminal message telemetry with usage provenance, for example:
usageStatus: "reported" | "missing" | "estimated";
- nullable or explicitly unavailable usage when the provider did not report it;
- terminal status such as
completed, error, or aborted;
- stable
messageID and primary-session filtering.
Keep backward compatibility explicit. Do not fabricate token counts from partial text unless the event is marked estimated.
Acceptance Criteria
Out of Scope
- Blindly accumulating token counts across
finish-step events without first proving multiple events belong to one assistant message.
- Fabricated per-token pricing for flat-rate coding plans.
- Provider stream timeout policy.
- Downstream billing policy.
Roadmap Alignment
- Pillar: EXEC
- Quarter: Q3 2026
- Priority: P0
- Theme fit: Code Review production quality and trustworthy execution telemetry.
- Decision gate impact: Directly protects pilot-facing usage and cost reporting.
References
Upstream OpenCode References
- anomalyco/opencode#25899 — open; cancellation is reported as clean completion with zero usage even though usage is unknown. This is the closest upstream match.
- anomalyco/opencode#30118 — open; ACP usage updates are absent until end-of-turn and missing at session start.
- anomalyco/opencode#21913 — auto-closed after inactivity, not resolved; proposes selective multi-step aggregation: preserve latest input/cache-read while accumulating output/reasoning/cache-write.
- anomalyco/opencode#26170 — auto-closed after inactivity, not resolved; truncated provider streams can be accepted as successful zero-usage turns.
- anomalyco/opencode#30706 — completed; cost-accounting work recommended explicit provenance rather than treating zero as universally meaningful.
The selective aggregation proposal is prior art, not an accepted requirement here. Any aggregation change still requires a reproducer proving multiple finish-step events belong to one assistant message.
Context
Session.getUsage()converts absent provider usage fields to zero. Separately,message_completeis emitted only when an assistant message has a cleanfinishvalue. Failed or aborted turns can therefore disappear from message telemetry, while missing usage can appear indistinguishable from a genuine reported zero.A live z.ai probe confirmed that thinking mode still returns standard OpenAI-shaped usage. Thinking parameter shape is not part of this defect.
Problem / Goal
Ensure every terminal assistant turn is represented exactly once and distinguish provider-reported usage, genuinely reported zero usage, missing usage, and any explicit estimate.
Proposed Approach
Extend terminal message telemetry with usage provenance, for example:
usageStatus: "reported" | "missing" | "estimated";completed,error, oraborted;messageIDand primary-session filtering.Keep backward compatibility explicit. Do not fabricate token counts from partial text unless the event is marked
estimated.Acceptance Criteria
messageID.tokens.totalis finite when components are sufficient to compute it, otherwise its absence is explicit.Out of Scope
finish-stepevents without first proving multiple events belong to one assistant message.Roadmap Alignment
References
packages/cli/src/session/index.tspackages/cli/src/session/processor.tspackages/cli/src/cli/cmd/run.tsUpstream OpenCode References
The selective aggregation proposal is prior art, not an accepted requirement here. Any aggregation change still requires a reproducer proving multiple
finish-stepevents belong to one assistant message.