Skip to content

fix: add persistent cost to prevent under-reporting spent value - #7763

Closed
IdrisGit wants to merge 18 commits into
anomalyco:devfrom
IdrisGit:fix-session-cost-calculation
Closed

fix: add persistent cost to prevent under-reporting spent value#7763
IdrisGit wants to merge 18 commits into
anomalyco:devfrom
IdrisGit:fix-session-cost-calculation

Conversation

@IdrisGit

@IdrisGitIdrisGit commented Jan 11, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

This PR adds a persistent cost field to sessions to fix incorrect cost reporting in the sidebar when sessions exceed 100 messages. Previously, cost was calculated from the last 100 messages only, causing under-reporting for long sessions.

Key changes:

  • Added cost field to Session schema
  • Sessions now accumulate cost incrementally when assistant messages complete
  • Sidebar reads cost directly from session metadata instead of summing messages
  • Forked sessions inherit parent's accumulated cost
  • Added startup migration to backfill existing sessions

How did you verify your code works?

Verified with before/after testing on the same long session:

  • v1.1.12: Sidebar showed $0.12 (incorrect - last 100 messages only)
  • Local build: Sidebar shows $1.61 (correct - accumulated total cost)
  • Used local build to work on an issue in the same session, cost is being accumulated correctly.
OpenCode v1.1.12
image
OpenCode local
image

Fixes#7767#6989

@github-actions

Copy link
Copy Markdown
Contributor

Thanks for your contribution!

This PR doesn't have a linked issue. All PRs must reference an existing issue.

Please:

  1. Open an issue describing the bug/feature (if one doesn't exist)
  2. Add Fixes #<number> or Closes #<number> to this PR description

See CONTRIBUTING.md for details.

@github-actions

Copy link
Copy Markdown
Contributor

The following comment was made by an LLM, it may be inaccurate:

No duplicate PRs found


const cost = createMemo(() => {
const total = messages().reduce((sum, x) => sum + (x.role === "assistant" ? x.cost : 0), 0)
const total = session().cost ?? 0

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you would want to fallback to the messages cost right cause otherwise opening old sessions would show 0

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should be handled by the migration script for old sessions as well, but yeah it makes more sense to fallback to the old method instead of defaulting to zero. updated.


export const getCost = fn(Identifier.schema("session"), async (id) => {
const read = await Storage.read<Info>(["session", Instance.project.id, id])
return read.cost ?? 0

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

prefer leaving as undefined so we can properly do checks in frontend right

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

makes sense, updated

this informs the caller function that cost can be undefined so it can
properly handle it, instead of just defaulting to 0.
Comment on lines +141 to +149
async (dir) => {
log.info("migrating session costs")
const startTime = Date.now()
let migratedCount = 0

for await (const sessionPath of new Bun.Glob("session/*/*.json").scan({
cwd: dir,
absolute: true,
})) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we have to do a migration?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's fine if only new things are updated, some people have a LOT of sessions

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would be awesome to have a way to run the migration manually if its not automatic.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rekram1-node with the fallback to deriving cost from messages present in the memory (from commit affc11a), migration is not strictly required in a sense that it will break the functionality but for older sessions the cost reporting will still be bugged.

from my local testing the migration was fine but again I don't know the extent of how many sessions people have, it could be a concern.

as @maharshi365 mentioned I can also add lazy migration, that only runs when a session is loaded, checks if the session has cost field, and run the migration if needed. I am currently not aware if users can manually run migrations in OC or not, also if it's fine from maintainers perspective to only updated new sessions I am fine with that as well, let me know @rekram1-node .

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think one time migration is probably way more important than whatever slowness there is in startup. Its not just stale data, but incorrect cost data that is being shown.

@IdrisGit
IdrisGitforce-pushed the fix-session-cost-calculation branch from b5e01b5 to 736f67eCompareJanuary 31, 2026 14:54
@bluet

bluet commented Feb 5, 2026

Copy link
Copy Markdown
Contributor

Thanks for this fix! One consideration: addCost() only updates the current session, so subagent costs (via Task tool) won't roll up to parent sessions. For multi-agent workflows, the sidebar will still under-report.

Would you consider adding parent propagation via the existing parentID chain? Happy to discuss or contribute a follow-up PR.

Related: #11027 (subagent costs not aggregated)

I've also filed a comprehensive architecture proposal: #12377

@IdrisGit

Copy link
Copy Markdown
ContributorAuthor

@bluet I am happy to discuss further on this, maybe we can have a proper tracking of the main agent cost and subagent cost for a proper breakdown, but first this PR needs to be merged or any improvement suggestions from the core team (cc: @thdxr)

@IdrisGit

Copy link
Copy Markdown
ContributorAuthor

I have updated the PR to use the new SQLite db and have removed the old migration script, now this is forward fix PR (it will only work for new sessions only)

@bluet

Copy link
Copy Markdown
Contributor

Hi @IdrisGit@rekram1-node@thdxr , just would like to know if this is still planned?

@IdrisGit

Copy link
Copy Markdown
ContributorAuthor

@bluet I haven't recieved any feedback from the team, waiting on that.

@wasd171

Copy link
Copy Markdown

Just got hit with x2 charge compared to what was visible in the UI :/
Would be great to get this PR merged!

@Ale-SaP

Copy link
Copy Markdown

Bumping this, shouldn't cost related issues be priority?

@tailwhipstruggle

Copy link
Copy Markdown

Looking forward to the merge resolution here. Much love

@aravindparappil46

Copy link
Copy Markdown

Same here! Looking forward to this fix!

@Ale-SaP

Copy link
Copy Markdown

Looking forward to this! There seem to be merge conflicts, if this is not solved by next week I'll take a stab at it myself ;)

@IdrisGit

Copy link
Copy Markdown
ContributorAuthor

I am going to close this PR now for a few reasons:

  1. The issue associated with this PR was closed as not planned by the team. (Issue session-cost/spent-value under reported for sessions with 100+ messages #7767).
  2. It has been sitting stale for a while now and codebase has changed alot, with the plugin system and refactor to EffectTS.
  3. I am unavailable for the next week and can't work on the conflict resolution.

If someone wants to take up, please do, I might be able to help with testing next week if needed. I had already used sqlite so you can steal/copy that if you want.

Apologies to everyone who have been subscribed to this PR for the late resolution.

@Integralist

Integralist commented Jul 17, 2026

Copy link
Copy Markdown

👋🏻

Just in case someone is interested...

I've made a fork of opencode and added two really important features to it (file cycling + sub-agent costs), and then decided to add a few other things that improve the quality of life in opencode (see my changelog and links to the commits if you want them; or just use my fork):

https://github.com/Integralist/opencode/blob/custom-features/FORK.md

  • File Cycling in Permission Prompt: (e74ec3ad3)
  • Exposed Sub-agent Cost Calculations in TUI Sidebar: (a1f59f85e)
  • Added global /btw command (as a toggleable overlay): (b52d39fb3)
  • Fixed @ Autocomplete Resolution for External Paths: (8fcf878af)
  • Added Session Recap Card: (39a8902e6)
  • Fixed Copy-on-Select in Permission Prompt: (88cb2bb67)
  • Sorted Skills Dialog Alphabetically: (277fd693e)
  • Added /skills: Sub-completion: (e3db58627)
  • Added ctrl+r Prompt History Search: (14c3c6aaa) (/rename is still available for session renaming).

Screenshots

Notice the "1/3", you can use [ and ] to cycle through the edits to be sure you're happy with all of them:

Screenshot 2026-07-17 at 20 13 27

Notice the sub-agent cost followed by the sub-total:

Screenshot 2026-07-17 at 20 08 44

Notice the /btw overlay (you can also do things like /btw --model="gemini-3.1-pro-preview" --effort="high" here's my prompt!" if you need to override the sub-agent with a specific model and effort).

Screenshot 2026-07-17 at 18 55 37

Here is what auto-complete looks like now that you can @../ out of the project directory:

Screenshot 2026-07-18 at 16 31 34

This is the Claude Code "recap" feature I added:

Screenshot 2026-07-18 at 16 30 26

So much nicer when Skills are sorted:

Screenshot 2026-07-18 at 18 35 57

Yay! My /... menu is still clear of skills (like how opencode works currently, so I can focus looking through harness specific slash commands), whilst at the same time if I type a colon /skills: then it'll invoke a skill autocomplete without going to a separate overlay:

Screenshot 2026-07-18 at 18 43 37

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

session-cost/spent-value under reported for sessions with 100+ messages

9 participants

@IdrisGit@bluet@wasd171@Ale-SaP@tailwhipstruggle@aravindparappil46@Integralist@maharshi365@rekram1-node