Skip to content

Preserve unknown usage on failed headless turns #93

Description

@byapparov

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

  • A completed, failed, or aborted assistant turn produces one terminal message event.
  • Terminal events include a stable messageID.
  • Missing provider usage is distinguishable from a reported numeric zero.
  • Provider-reported usage received before a later failure is preserved.
  • No failed turn is reported as zero-token usage solely because fields were absent.
  • tokens.total is finite when components are sufficient to compute it, otherwise its absence is explicit.
  • Primary-session terminal events exclude child-session assistant messages.
  • Integration tests cover reported zero, missing usage, provider error, abort, and usage received before a later failure.
  • Deduplication behavior is coordinated with bug: duplicate message_complete events in NDJSON output (--format json) #45.

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#25899open; cancellation is reported as clean completion with zero usage even though usage is unknown. This is the closest upstream match.
  • anomalyco/opencode#30118open; ACP usage updates are absent until end-of-turn and missing at session start.
  • anomalyco/opencode#21913auto-closed after inactivity, not resolved; proposes selective multi-step aggregation: preserve latest input/cache-read while accumulating output/reasoning/cache-write.
  • anomalyco/opencode#26170auto-closed after inactivity, not resolved; truncated provider streams can be accepted as successful zero-usage turns.
  • anomalyco/opencode#30706completed; 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    Fields

    No fields configured for Bug.

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions