You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Unified reports count OpenCode Claude Agent sessions twice #2004
OpenCode's claude-agent provider runs Claude Code under the hood. Each turn is saved twice:
in OpenCode's database (opencode.db, table session_message), with the model stored as {"id":"opus","providerID":"claude-agent"}
in Claude Code's own transcript, ~/.claude/projects/<project>/<session>.jsonl
The OpenCode row links to the transcript: each content part has native.sessionID, which matches the transcript's file name.
ccusage daily adds up both sources, so this usage is counted twice. One real day (2026-10-31):
Command
Cache read
Cost
ccusage claude daily
876.2M
$319.96
ccusage opencode daily
1,061.3M (875.3M of it from claude-agent)
$607.37
ccusage daily
1,937.4M
$927.33
The OpenCode copy is also priced too high. OpenCode stores only the alias opus, and ccusage prices it as an older Opus: $573.68 for tokens that cost $319.37 as claude-opus-5-5 in the Claude transcript.
Steps to reproduce
In OpenCode, use the claude-agent provider for a session.
Run ccusage claude daily and ccusage opencode daily for that day.
Run ccusage daily. Its total is the sum of both.
Expected behavior
Unified reports count each Claude Agent turn once.
I have a fix ready on my fork, but I can't open a PR yet: GitHub rejects createPullRequest for my account, which I guess is the contribution gate. Could you approve me with lgtm?
In unified reports, OpenCode v2 rows whose content[].native.sessionID matches a Claude Code transcript on disk are skipped, so the Claude source counts that usage once. ccusage opencode still shows every row. If the transcript is gone (for example after cleanupPeriodDays), the OpenCode rows are counted instead, so no usage disappears.
For 2026-10-31, ccusage daily goes from $927.33 to $360.61. Cache read goes from 1,937.4M to 1,062.2M, which is exactly Claude plus the OpenCode usage that is not claude-agent.
Why keep the Claude side
I compared 35 real claude-agent sessions with their Claude transcripts:
Input and cache write match, and cache read matches within 0.08%.
Output looks about 38% lower in OpenCode, but OpenCode stores thinking separately in tokens.reasoning. For all 330 messages checked, output + reasoning equals Claude's output_tokens.
The 0.08% gap comes from interrupted steps. OpenCode sometimes saves them with tokens: null, while the Claude transcript has their usage (334,142 cache-read tokens in one case).
OpenCode stores only the alias opus. The resolved version is nowhere in its database or config. The Claude transcript records claude-opus-5-5.
The rejected alternatives are in the PR description. If you'd prefer another approach, I'm happy to switch.
Other problems I found while testing
These are not fixed on this branch. I can open separate issues for them.
ccusage opencode overprices claude-agent/opus. The alias has no pricing entry and resolves to an older Opus: $573.68 vs $319.37 for the same tokens.
Unified ccusage session --until X drops Claude and Qwen sessions whose last activity is on day X.filter_session_summaries (rust/crates/ccusage-adapter-all/src/loader.rs) compares the whole lastActivity timestamp as a string, with dashes removed ("20990102T00:00:00.000Z"), against "20990102", so the session fails <= until. It reproduces on 20.0.28: ccusage claude session lists the session, while ccusage session with the same window returns nothing.
unified_reports_return_antigravity_skip_warnings_once_instead_of_printing fails on Windows. It expects antigravity/conversations/... but gets antigravity\conversations\....
Pullfrog triage: priority:high
Kind: bug
Maintenance fit: maintainable
Confidence: high
Unified daily sums Claude and OpenCode sources so claude-agent turns stored in both places count twice, with clear token and cost evidence. Alias opus pricing also overstates cost versus the Claude transcript model. This is a material usage and cost error in core reporting.
Decision: kept open; Pullfrog will attempt a focused implementation PR.
@ryoppippi thanks for picking this up in #2007! Unified reports now look right for me on v20.0.47. (I tried to reopen this issue, but I don't have permission.)
There's still double counting for tools that read each source separately. For example, tokenmaxxing syncs claude, opencode, codex, and other sources on their own and adds them up. Since #2007 deliberately keeps focused reports unchanged, Claude Agent turns are still counted under both claude and opencode, and the OpenCode copy is still priced with the opus alias.
A tokenmaxxing sync --dry-run --since 2026-10-31 for the last three days shows the duplication:
Source
Cost
opencode
$608.25
claude
$535.31
The claude-agent sessions in the opencode row are the same sessions as in the claude row.
I'm not sure where this should be fixed. I lean toward ccusage, because it already has the session and day matching from #2007. Each consumer would otherwise have to rebuild it from both data sources. Some options:
Opt-in flag on focused reports, for example ccusage opencode --exclude-claude-agent. The default stays as it is.
A JSON field that marks OpenCode rows already counted by Claude, so consumers can filter them out.
Exclude by default in ccusage opencode, so focused reports partition usage and their sum always equals ccusage daily.
Leave ccusage as is, and I'll open an issue in tokenmaxxing instead.
Which would you prefer? I'm happy to implement it.
Thanks for the detailed follow-up and the numbers! Let's go with option 3: exclude Claude Agent turns from ccusage opencode by default, so the per-source totals add up to ccusage daily. Claude Code already logs those turns with accurate pricing, so the Claude side should own them. If you'd like, an opt-in flag to include them again would be nice, but it's not required.
A PR would be very welcome. Please include tests for the exclusion. Thanks for offering to take this on!
GitHub still rejects createPullRequest for my account, so I can't open the PR myself. Could you approve me with lgtm, or open it from the branch? The description is ready, and I'll post it as soon as I can.
Focused reports match by session and day in every report kind, session included, so ccusage claude + ccusage opencode equals ccusage daily. On my data for 2026-10-30 to 2026-11-01: $520.34 + $171.26 = $691.60.
Claude logs are read only when an OpenCode row links to a Claude session.
While testing, I found that the unified ccusage session drops Claude sessions that continue past --until. That's separate from this change, so I opened #2016 for it.
🤖 Claude Opus 5.5 responding on behalf of @ryoppippi
Option 3 landed in #2022, which kept your commits from fix/opencode-focused-claude-agent unchanged and added one review-fix commit on top. ccusage opencode now skips Claude Agent turns by default, and --include-claude-agent brings them back. It ships in the next release (v20.0.49). Sorry the PR sat without a reply after your pings. Thanks for the branch and the tests.
Activity
pullfrog commentedon Oct 31, 2026
igorovh commentedon Oct 31, 2026
I have a fix ready on my fork, but I can't open a PR yet: GitHub rejects
createPullRequestfor my account, which I guess is the contribution gate. Could you approve me withlgtm?Branch: main...igorovh:ccusage:fix/opencode-claude-agent-dedup
What the fix does
In unified reports, OpenCode v2 rows whose
content[].native.sessionIDmatches a Claude Code transcript on disk are skipped, so the Claude source counts that usage once.ccusage opencodestill shows every row. If the transcript is gone (for example aftercleanupPeriodDays), the OpenCode rows are counted instead, so no usage disappears.For 2026-10-31,
ccusage dailygoes from $927.33 to $360.61. Cache read goes from 1,937.4M to 1,062.2M, which is exactly Claude plus the OpenCode usage that is notclaude-agent.Why keep the Claude side
I compared 35 real
claude-agentsessions with their Claude transcripts:tokens.reasoning. For all 330 messages checked,output + reasoningequals Claude'soutput_tokens.tokens: null, while the Claude transcript has their usage (334,142 cache-read tokens in one case).opus. The resolved version is nowhere in its database or config. The Claude transcript recordsclaude-opus-5-5.The rejected alternatives are in the PR description. If you'd prefer another approach, I'm happy to switch.
Other problems I found while testing
These are not fixed on this branch. I can open separate issues for them.
ccusage opencodeoverpricesclaude-agent/opus. The alias has no pricing entry and resolves to an older Opus: $573.68 vs $319.37 for the same tokens.ccusage session --until Xdrops Claude and Qwen sessions whose last activity is on dayX.filter_session_summaries(rust/crates/ccusage-adapter-all/src/loader.rs) compares the wholelastActivitytimestamp as a string, with dashes removed ("20990102T00:00:00.000Z"), against"20990102", so the session fails<= until. It reproduces on 20.0.28:ccusage claude sessionlists the session, whileccusage sessionwith the same window returns nothing.unified_reports_return_antigravity_skip_warnings_once_instead_of_printingfails on Windows. It expectsantigravity/conversations/...but getsantigravity\conversations\....github-actions commentedon Oct 31, 2026
Pullfrog triage: priority:high
Kind: bug
Maintenance fit: maintainable
Confidence: high
Unified daily sums Claude and OpenCode sources so claude-agent turns stored in both places count twice, with clear token and cost evidence. Alias opus pricing also overstates cost versus the Claude transcript model. This is a material usage and cost error in core reporting.
Decision: kept open; Pullfrog will attempt a focused implementation PR.
perf(opencode): read Claude Agent links only for claude-agent rows
fix(all): skip Claude Agent rows of every session Claude counted
docs(opencode): qualify how unified reports match Claude Agent rows
fix(claude): skip Claude Agent rows only for fully counted sessions
fix(opencode): keep Claude Agent links past malformed content parts
docs(opencode): explain Claude Agent matching for resumed sessions
fix(claude): let a session's own counted turns decide its coverage
fix(opencode): skip non-string Claude Agent session IDs without unlin…
docs(opencode): state the resumed-session rule and its residual under…
3 remaining items
docs(opencode): tie session-report skips to counted main-thread usage
docs(opencode): state the session-report rule in terms of transcripts
docs(opencode): state both branches of the session-report coverage rule
docs(opencode): complete the dedup tiebreaks and Claude Agent coverag…
docs(opencode): spell out which turns decide a Claude Agent skip
fix(all): count OpenCode Claude Agent turns once in unified reports (#…
igorovh commentedon Nov 1, 2026
@ryoppippi thanks for picking this up in #2007! Unified reports now look right for me on v20.0.47. (I tried to reopen this issue, but I don't have permission.)
There's still double counting for tools that read each source separately. For example, tokenmaxxing syncs
claude,opencode,codex, and other sources on their own and adds them up. Since #2007 deliberately keeps focused reports unchanged, Claude Agent turns are still counted under bothclaudeandopencode, and the OpenCode copy is still priced with theopusalias.A
tokenmaxxing sync --dry-run --since 2026-10-31for the last three days shows the duplication:opencodeclaudeThe
claude-agentsessions in theopencoderow are the same sessions as in theclauderow.I'm not sure where this should be fixed. I lean toward ccusage, because it already has the session and day matching from #2007. Each consumer would otherwise have to rebuild it from both data sources. Some options:
ccusage opencode --exclude-claude-agent. The default stays as it is.ccusage opencode, so focused reports partition usage and their sum always equalsccusage daily.Which would you prefer? I'm happy to implement it.
ryoppippi commentedon Nov 2, 2026
Thanks for the detailed follow-up and the numbers! Let's go with option 3: exclude Claude Agent turns from
ccusage opencodeby default, so the per-source totals add up toccusage daily. Claude Code already logs those turns with accurate pricing, so the Claude side should own them. If you'd like, an opt-in flag to include them again would be nice, but it's not required.A PR would be very welcome. Please include tests for the exclusion. Thanks for offering to take this on!
igorovh commentedon Nov 2, 2026
@ryoppippi option 3 is ready, with the opt-in
--include-claude-agentflag and tests:main...igorovh:ccusage:fix/opencode-focused-claude-agent
GitHub still rejects
createPullRequestfor my account, so I can't open the PR myself. Could you approve me withlgtm, or open it from the branch? The description is ready, and I'll post it as soon as I can.Short summary:
ccusage opencodeskipsclaude-agentturns that the Claude source counts, using the matching from fix(all): count OpenCode Claude Agent turns once in unified reports #2007.--include-claude-agentkeeps them.sessionincluded, soccusage claude+ccusage opencodeequalsccusage daily. On my data for 2026-10-30 to 2026-11-01: $520.34 + $171.26 = $691.60.While testing, I found that the unified
ccusage sessiondrops Claude sessions that continue past--until. That's separate from this change, so I opened #2016 for it.igorovh commentedon Nov 4, 2026
Hi @ryoppippi, just a gentle ping on this one 🙂
The option 3 branch is ready, with the
--include-claude-agentflag and tests:main...igorovh:ccusage:fix/opencode-focused-claude-agent
GitHub still won't let me open the PR. Whenever you have a moment, could you give me an
lgtm, or open it from the branch?fix(opencode): exclude Claude Agent turns by default in focused repor…
ryoppippi commentedon Nov 10, 2026
Note
🤖 Claude Opus 5.5 responding on behalf of @ryoppippi
Option 3 landed in #2022, which kept your commits from
fix/opencode-focused-claude-agentunchanged and added one review-fix commit on top.ccusage opencodenow skips Claude Agent turns by default, and--include-claude-agentbrings them back. It ships in the next release (v20.0.49). Sorry the PR sat without a reply after your pings. Thanks for the branch and the tests.docs(skills): credit issue reporters and reply after merging (#2056)
fix(claude): show model breakdowns for session --id (#2057)