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
Hidden LLM calls (session title / message summaries) are billed by the provider but absent from session cost #46371
Reviving #7175 (auto-closed as not planned after 90 days of inactivity) with billing-level evidence, since the gap is still live and measurable.
What happens: title generation, summary titles, and body summaries make real LLM calls through LLM.stream() but never persist token usage or cost on any message (confirmed empirically in #7175 by @fedealliani and still the case on current versions).
Billing-level confirmation. Reconciling one day of usage against the provider's invoice export (SQLite walk over root + subagent sessions vs billed tokens):
Billed requests: 204. Sessions account for 203 (root + 3 subagents, cache_read matched to the exact token).
The 204th request is the session-title generation call: 752 input / 498 output tokens, billed, present in no session — it ran at session creation (agent=title, small=true in the logs), before the first user message.
Individually tiny (~$0.003 on a deepseek-v4-pro session), but it's a systematic understatement: every session pays it, and it compounds with the subagent rollup gap (#45417) — for cost analytics on top of opencode data (our use case, and fedealliani's multi-tenant one) any unrecorded call breaks reconciliation.
Suggested direction (happy to send a PR): persist usage/cost for hidden LLM calls — either as a synthetic session-scoped record or accumulated into session.cost with the message count untouched. The usage numbers are already available at the call site since LLM.stream() receives the provider's usage payload.
Related: #45417 (subagent cost rollup — same reconciliation turned up both gaps).
I traced the gap in the code. Title generation (packages/core/src/session/title.ts) does publish a SessionEvent.UsageRecorded with source: "title" on step finish, so the token/cost are recorded as events. The problem is on the aggregation side: packages/core/src/session/stats.ts only folds UsageRecorded events into session cost when json_extract(data, '$.source') = 'compaction' (line ~296). The source: "title" records are therefore excluded from the session/stats cost totals, which is exactly the understatement described here.
The fix is small and scoped to the stats aggregation: include the title source alongside compaction so these hidden title-generation calls show up in session cost/usage.
I'll implement this against v2 with a regression test and open a PR linked to this issue.
@argszero Thanks for tracing the aggregation side — the "title" filter exclusion is
exactly the gap, and it's in #46544 with credit.
One heads-up on where #46544 went wider: /generate never publishing usage is actually
documented in the session.generate contract (#38558 — "Durability: no … usage", non-goals
"unless we explicitly promote them"). #46544 proposes that promotion with a billing argument
(the call is billed, so cost reconciliation can't see it), adding the event emission next
to your filter fix plus regression tests for both sides.
If you'd rather keep the stats change as yours, I'll drop that piece — and if maintainers
prefer the contract as-is, the title-only part stands on its own.
@greedyivan Thank you for picking this up in #46544 and for the credit. I've reviewed the diff — the stats.ts filter change exactly matches the gap I traced (the source: "title" exclusion), and the generate usage recording plus the regression tests on both sides are the right way to handle the #38558 contract promotion.
I'm deferring my implementation to #46544 — this is the cleaner home for the fix, and it saves a duplicate PR. No need to drop the stats piece; the combined PR is stronger.
I'll keep the issue linked to #46544. Happy to review/test if maintainers want a second pair of eyes.
Thanks for the careful reconciliation against the invoice, it makes the gap very clear.
From your numbers in #45417 (opencode 1.18.25) and the code paths you cite, this report is about OpenCode v1 (the opencode-ai package). v1 now only gets fixes for critical bugs: data loss, security problems, or crashes with no workaround. A small accounting gap like this is unlikely to be fixed there.
OpenCode v2 (the @opencode/cli package, versions 2.x) already works the way you suggest. Title generation records its token usage and cost as a session-level usage event (source: "title"). That cost is added to session.cost without creating a message, and background compaction calls are recorded the same way. If you upgrade to v2, your reconciliation should cover the title call.
Closing this as not planned since there's been no reply. This report is about OpenCode v1 (1.18.25), which only gets fixes for critical bugs. Missing cost for the title call doesn't qualify, since it only affects accounting. OpenCode v2 (@opencode/cli) already records the cost of title generation and background compaction as session-level usage and adds it to session.cost, so upgrading should close this gap in your reconciliation. For the subagent cost gap, see #45417. If you see the same problem on v2, please open a new issue with the version and your reconciliation details. Thanks again for the thorough analysis.
Activity
argszero commentedon Aug 31, 2026
I'd like to work on this.
I traced the gap in the code. Title generation (
packages/core/src/session/title.ts) does publish aSessionEvent.UsageRecordedwithsource: "title"on step finish, so the token/cost are recorded as events. The problem is on the aggregation side:packages/core/src/session/stats.tsonly foldsUsageRecordedevents into session cost whenjson_extract(data, '$.source') = 'compaction'(line ~296). Thesource: "title"records are therefore excluded from the session/stats cost totals, which is exactly the understatement described here.The fix is small and scoped to the stats aggregation: include the
titlesource alongsidecompactionso these hidden title-generation calls show up in session cost/usage.I'll implement this against
v2with a regression test and open a PR linked to this issue.greedyivan commentedon Sep 1, 2026
@argszero Thanks for tracing the aggregation side — the
"title"filter exclusion isexactly the gap, and it's in #46544 with credit.
One heads-up on where #46544 went wider:
/generatenever publishing usage is actuallydocumented in the session.generate contract (#38558 — "Durability: no … usage", non-goals
"unless we explicitly promote them"). #46544 proposes that promotion with a billing argument
(the call is billed, so cost reconciliation can't see it), adding the event emission next
to your filter fix plus regression tests for both sides.
If you'd rather keep the stats change as yours, I'll drop that piece — and if maintainers
prefer the contract as-is, the title-only part stands on its own.
argszero commentedon Sep 1, 2026
@greedyivan Thank you for picking this up in #46544 and for the credit. I've reviewed the diff — the
stats.tsfilter change exactly matches the gap I traced (thesource: "title"exclusion), and thegenerateusage recording plus the regression tests on both sides are the right way to handle the#38558contract promotion.I'm deferring my implementation to #46544 — this is the cleaner home for the fix, and it saves a duplicate PR. No need to drop the stats piece; the combined PR is stronger.
I'll keep the issue linked to #46544. Happy to review/test if maintainers want a second pair of eyes.
opencodejr commentedon Oct 28, 2026
Thanks for the careful reconciliation against the invoice, it makes the gap very clear.
From your numbers in #45417 (opencode 1.18.25) and the code paths you cite, this report is about OpenCode v1 (the
opencode-aipackage). v1 now only gets fixes for critical bugs: data loss, security problems, or crashes with no workaround. A small accounting gap like this is unlikely to be fixed there.OpenCode v2 (the
@opencode/clipackage, versions 2.x) already works the way you suggest. Title generation records its token usage and cost as a session-level usage event (source: "title"). That cost is added tosession.costwithout creating a message, and background compaction calls are recorded the same way. If you upgrade to v2, your reconciliation should cover the title call.Related issues:
If you see the same problem on v2, or think this is critical for v1, please reply here with the version and details.
opencodejr commentedon Oct 29, 2026
Closing this as not planned since there's been no reply. This report is about OpenCode v1 (1.18.25), which only gets fixes for critical bugs. Missing cost for the title call doesn't qualify, since it only affects accounting. OpenCode v2 (
@opencode/cli) already records the cost of title generation and background compaction as session-level usage and adds it tosession.cost, so upgrading should close this gap in your reconciliation. For the subagent cost gap, see #45417. If you see the same problem on v2, please open a new issue with the version and your reconciliation details. Thanks again for the thorough analysis.