+The Claude Agent provider (`claude-agent`) runs Claude Code, which also writes its own transcript. Its v2 rows record that Claude Code session as `content[].native.sessionID`, which `load_entries_with_native_sessions` returns with each entry. Unified daily-family reports drop a row when the Claude source counted main-thread usage of that session on the row's date, in the Claude source's timezone and date range; session reports drop it when the report includes that Claude session, whose row holds all of its usage. A resumed or forked session repeats earlier turns, and Claude's dedup keeps each under one session, often the parent, since files are read in path order. A session with its own surviving main-thread usage counts as included exactly when the report includes it. A session whose usage all survives under other sessions counts as included only when every one of those is included. The residual error is an undercount: when a fork is reported without its parent, the fork's rows for repeated turns are dropped, though Claude counts those turns under the parent. Leaving the fork uncovered instead would double count its own turns, which are usually most of its rows, because OpenCode excludes its own fork copies. The usage is then priced by the resolved model ID rather than the `opus`/`sonnet` alias OpenCode stores. The match is per session or session day, not per turn: `native.messageID` is the transcript line `uuid`, and one OpenCode row sums several API calls, so rows do not map one-to-one to Claude entries. This relies on the transcript holding every main-thread turn OpenCode records for that session; it also holds turns OpenCode drops, such as interrupted steps saved with `tokens: null`. A row is dated by its turn's start while Claude dates each call, so in daily-family reports a turn that crosses midnight in the Claude source's timezone is counted by both sources when Claude has no usage of that session on the earlier day, and with `--until` on that day the calls Claude logs after midnight are left out although OpenCode dates the whole turn on the earlier day. Only v2 `session_message` rows can carry the link: OpenCode added the provider after moving to SQLite v2, so legacy JSON messages and the v1 `message` table hold no `claude-agent` rows. `ccusage opencode` keeps every row.
0 commit comments