Commit graph

1012 commits

Author SHA1 Message Date
Brandin
d8006ca76b
fix: keep a saved Capacity Dock provider across quota refreshes (#1768)
Some checks failed
CI / semgrep (push) Waiting to run
Tests / test (22) (push) Waiting to run
Tests / test (22.13.0) (push) Waiting to run
Tests / test (24) (push) Waiting to run
Tests / test (26) (push) Waiting to run
Tests / test-platforms (macos-latest) (push) Waiting to run
Tests / test-platforms (windows-latest) (push) Waiting to run
Tests / desktop (push) Waiting to run
Tests / vscode (push) Waiting to run
macOS Menubar CI / test (push) Has been cancelled
Windows Menubar CI / check (ubuntu-latest) (push) Has been cancelled
Windows Menubar CI / check (windows-latest) (push) Has been cancelled
The Windows tray's Capacity Dock re-seeded its provider rail and resting
provider from whichever providers answered the last quota refresh, so one
missed Codex read made Cursor the resting provider for good. autoSeed is
now additive: providers already in the rail stay, a saved preferred
provider is kept while the CLI still knows it, and every resting pick
(ring click, Resting menu, Electron relay) is latched in the shared dock
prefs. A separate-rings Claude profile row id never enters the rail,
because autoSeed only pins ids the CLI reports.

The mac menu bar keeps its own seeding and is unchanged; it likely has the
same eviction and gets a parity follow-up. The new tests cover the seed
and latch rules; four of them fail on main.
2026-10-31 03:48:01 +01:00
Dan Caldr
ad7332f591
feat(antigravity): extract initial user prompt and attribute userMessage (#1793)
Antigravity calls carried no user message, so every turn classified as
conversation. The provider now reads the cascade's first USER_INPUT from
the brain transcript (first 50 lines, <USER_REQUEST> unwrapped, cut to
500 chars), with the SQLite steps row as the fallback for .db sources,
and sets it as userMessage on every call of the cascade, the way the
cline-cli, goose, codewhale and mistral-vibe providers already do.

Calls, tokens and cost do not change. The activity split does: on real
data the last 30 days go from 128 conversation turns to 2, with 126 now
exploration. ANTIGRAVITY_CACHE_VERSION moves 9 to 10 and the antigravity
parse tag gets user-msg-v1, so only Antigravity re-parses once. Session
titles and optimize are unchanged. Follow-up prompts in a cascade are not
read yet; the first prompt stands for the whole cascade.

Closes #1792
2026-10-31 03:45:34 +01:00
Hasan Abbas
11c65266c6
fix(claude): count the PowerShell tool as a shell tool (#1806)
Claude Code on Windows runs shell commands through a tool named PowerShell,
with the same { command } input as Bash. The shell-tool sets only knew Bash,
BashTool and PowerShellTool, and PowerShellTool never appears in a transcript:
the Claude Code binary names the tool PowerShell. compactEntry dropped the
command, so it never reached the shell breakdown, command-only turns fell
through to coding or conversation, and optimize missed read-shaped commands.

The claude parse tag gets powershell-shell-tool-v1 so cached sessions re-parse
once, as in #1791. DAILY_CACHE_VERSION is unchanged, so settled days keep
their old split until the next bump. Calls, tokens and cost do not change:
identical on a full real day, main vs branch.
2026-10-31 00:50:52 +01:00
Aditya Vikram Singh
9a624cc2e7
fix(cursor): unwrap Cursor 3.27 safe-storage sessions (#1802)
Cursor 3.27 can store its app session as cursor-safe-storage:v1 ciphertext
(Chromium v10, key in the "Cursor Safe Storage" keychain item), behind
Cursor's own cursor.storage.authTokenEncryptionAtRest flag. CodeBurn read it
as an expired login. The CLI and the menu bar now unlock it with that item
and run the usual JWT checks; the newline `security -w` appends is trimmed
before the KDF.

Keychain consent is user-initiated, as for Claude: the first read happens on
`codeburn import cursor --sync`, `codeburn quota`, or Retry/Reconnect on the
menu bar's Cursor quota. Background syncs and dock refreshes read only after
one such read succeeded, reuse the opened token per blob, and the first
refused read switches that off again. Status never reads the keychain. No
service-only keychain lookup, since after a Deny it asks again.

Windows and Linux say CodeBurn cannot unlock the session there yet; the
desktop app, VS Code and the Windows tray show the new `locked` line in all
locales. Docs describe the prompt and what Always Allow grants.

Closes #1801
2026-10-30 22:32:51 +01:00
ozymandiashh
07c0ee601f
fix(tui): keep provider totals through the background refresh (#1784)
Some checks failed
CI / semgrep (push) Has been cancelled
Tests / test (22) (push) Has been cancelled
Tests / test (22.13.0) (push) Has been cancelled
Tests / test (24) (push) Has been cancelled
Tests / test (26) (push) Has been cancelled
Tests / test-platforms (macos-latest) (push) Has been cancelled
Tests / test-platforms (windows-latest) (push) Has been cancelled
Tests / vscode (push) Has been cancelled
Tests / desktop (push) Has been cancelled
A provider-filtered dashboard painted the right total, then changed it
when the background index finished. Dates the shared daily cache did not
hold were dropped, so Copilot fell to $0 in #1769, and a cached day
with the same call count as the live parse kept its cached numbers, so
a cache derived under older accounting still moved the headline after
refresh. Report and the first paint did neither.

The provider-scoped index now overlays its normalized days the way
buildDurablePeriod does for report: they fill the dates and slices the
cache lacks and replace a cached slice unless a settled cached day
explains more calls, so expired history is kept and nothing is counted
twice. Against real Copilot history and a complete cache that predates
it, main refreshed 30 days from $0.355 to $0.00; it now stays at $0.355.

The all-provider path is unchanged, and a provider view still never
writes the shared cache. report, models and the first paint do not
change.

Fixes #1769
2026-10-27 17:52:18 +01:00
Kim
e34143824d
fix(claude): keep every parallel tool call of a streamed message (#1791)
Claude Code writes one JSONL line per content block, so a message that
calls several tools in parallel spans several lines with the same message
id. dedupeStreamingMessageIds kept only the last line, which has the right
usage but only the last tool_use, so the earlier calls were missing from
the tool, MCP and shell breakdowns, turn classification, edit line counts,
tool errors and PR link evidence. On a real 7-day sample main counted
59,765 tool calls where the transcripts hold 69,376.

The last line is still the one kept. The tool_use blocks of the earlier
lines with the same id are put back in front of its content, matched by
block id, so the older shape whose final write restates every block (#110)
is not counted twice. The claude parse tag gets streamed-tool-blocks-v1,
so cached sessions re-parse once.

Calls, tokens and cost do not change; activity turns, edit and one-shot
counts and the by-PR split do. DAILY_CACHE_VERSION is not bumped (v77 is
the last bump the zstd split can carry), so days the daily cache has
already settled keep their old activity split until the next bump
re-derives them.
2026-10-27 16:57:09 +01:00
ozymandiashh
6489d5e4e5
fix: exact Today session counts and the Overview estimate-marker crash (#1794)
Some checks are pending
CI / semgrep (push) Waiting to run
Tests / test (22) (push) Waiting to run
Tests / test (22.13.0) (push) Waiting to run
Tests / test (24) (push) Waiting to run
Tests / test (26) (push) Waiting to run
Tests / test-platforms (macos-latest) (push) Waiting to run
Tests / test-platforms (windows-latest) (push) Waiting to run
Tests / desktop (push) Waiting to run
Tests / vscode (push) Waiting to run
Today's day row comes from the live parse with session identities, but
the session-count provenance treated it like a cache row, so a Today-only
period reported its exact count as partial on the headline, the provider
rows and the project rows. Today is now left out of the cache-provenance
days at every call site (money still uses the complete day set) and the
status snapshot render version moves to 13 so stale labels are rejected.

The Overview hero count-up wrote textContent into a div whose children
React owns; when the ~ estimate marker appeared, React hit a detached text
node (insertBefore NotFoundError) and the error boundary took over. GSAP
now writes only into an empty span and React keeps the marker; a currency
change with the same raw value refreshes the text.

Fixes #1760
Fixes #1785
2026-10-26 22:53:08 +01:00
Resham Joshi
cef7c17621
fix(warp): price conversations from total_billed_cost_in_cents (#1787)
Some checks are pending
CI / semgrep (push) Waiting to run
Tests / test (22) (push) Waiting to run
Tests / test (22.13.0) (push) Waiting to run
Tests / test (24) (push) Waiting to run
Tests / test (26) (push) Waiting to run
Tests / test-platforms (macos-latest) (push) Waiting to run
Tests / test-platforms (windows-latest) (push) Waiting to run
Tests / desktop (push) Waiting to run
Tests / vscode (push) Waiting to run
Warp 0.2026.10 removed total_provider_cost_in_cents and now persists
total_billed_cost_in_cents. Read it after total_charged_usage, as Warp
does, so conversations without streamed charges keep their billed dollars
instead of the credits estimate. Bump the warp parse version and
DAILY_CACHE_VERSION to 77 (76 on a Node without zstd, so it re-derives
too and a zstd Node still adopts its file).
2026-10-26 13:30:21 +01:00
Resham Joshi
de08cee8e1
fix(daily-cache): stop token-only slices inflating day totals on merge (#1788)
Some checks are pending
CI / semgrep (push) Waiting to run
Tests / test (22) (push) Waiting to run
Tests / test (22.13.0) (push) Waiting to run
Tests / test (24) (push) Waiting to run
Tests / test (26) (push) Waiting to run
Tests / test-platforms (macos-latest) (push) Waiting to run
Tests / test-platforms (windows-latest) (push) Waiting to run
Tests / desktop (push) Waiting to run
Tests / vscode (push) Waiting to run
A slice with tokens but no calls, cost or savings (a Hermes or Copilot
session total) read as a placeholder, so every adoption or re-derive merge
replaced it while adding its tokens to the day and model rows again. Each
version bump multiplied them by the number of older cache files holding
the day. Count tokens as slice data, and cap day and model-row tokens at
the slices' sum on load for days whose calls and cost match their
slices, this bug's fingerprint, so caches already inflated heal without
a version bump.
A newer token-only slice now wins over an older one with calls, as the
newer-wins rule intends, instead of the two being added.
2026-10-26 03:59:47 +01:00
Resham Joshi
b4974351dc
fix(codex): read Zstandard-compressed rollouts (#1786)
Codex now moves cold rollouts to rollout-*.jsonl.zst. Discovery, the
first-line probe, the parser, fork parent lookup, codex-tps and the context
view read them through a zstd decode; a session present in both forms
counts once from the plain file. Compressed files are parsed whole and never
resumed by byte offset. Without node:zlib zstd (Node < 22.15) they are
skipped with one notice. DAILY_CACHE_VERSION 76 so settled days pick up the
recovered Codex usage. Without zstd it stays 75, so a later Node upgrade
still adopts that cache and re-derives it.
2026-10-26 02:40:29 +01:00
Resham Joshi
c71ee8e26e
test: cap parse-worker requests at the machine's core count (#1771) 2026-10-20 18:48:59 +02:00
Resham Joshi
0322472db3
test: keep the dashboard exit fixture outside the 6 Months window (#1765)
Some checks failed
CI / semgrep (push) Has been cancelled
Tests / desktop (push) Has been cancelled
Tests / test (22) (push) Has been cancelled
Tests / test (22.13.0) (push) Has been cancelled
Tests / test (24) (push) Has been cancelled
Tests / test (26) (push) Has been cancelled
Tests / test-platforms (macos-latest) (push) Has been cancelled
Tests / test-platforms (windows-latest) (push) Has been cancelled
Tests / vscode (push) Has been cancelled
The cold-hydration exit tests wait for the Lifetime phase to hold the hydration lock. Their sessions were 200 days old, which this week falls inside the 6 Months index window, so they parsed in that phase and Lifetime never held the lock long enough: both tests time out on every platform, also on the commit before #1761. 400 days stays outside 6 Months and inside Lifetime.
2026-10-17 17:32:53 +02:00
Resham Joshi
87444adc8f
optimize: stop counting new-file writes in the read/edit ratio (#1761)
Some checks are pending
CI / semgrep (push) Waiting to run
Tests / test-platforms (windows-latest) (push) Waiting to run
Tests / desktop (push) Waiting to run
Tests / test-platforms (macos-latest) (push) Waiting to run
Tests / test (22) (push) Waiting to run
Tests / test (22.13.0) (push) Waiting to run
Tests / test (24) (push) Waiting to run
Tests / test (26) (push) Waiting to run
Tests / vscode (push) Waiting to run
Writing a new file has nothing to read first, so teams whose subagents
write report and evidence files got a false 'edits more than it reads'
finding. The detector now counts only Edit-type calls. EDIT_TOOL_NAMES
stays as is because act report baselines were recorded with it.
2026-10-17 15:07:41 +02:00
Resham Joshi
354492e500
Copilot: count every agent round and bill exact credits in VS Code chat sessions (#1752)
Some checks are pending
CI / semgrep (push) Waiting to run
Tests / test (22) (push) Waiting to run
Tests / test (22.13.0) (push) Waiting to run
Tests / test (24) (push) Waiting to run
Tests / test (26) (push) Waiting to run
Tests / test-platforms (macos-latest) (push) Waiting to run
Tests / test-platforms (windows-latest) (push) Waiting to run
Tests / desktop (push) Waiting to run
Tests / vscode (push) Waiting to run
* Copilot: count every agent round and exact credits in VS Code chat sessions

Copilot Chat 0.69 keeps only the last model call of an agent request in result.metadata. Read the request's modelTotals (input, cache reads, output over every call) first, then root completionTokens, then metadata. Carry copilotCredits as nanoAiu so plan credits are exact. Bump the copilot parse tag so cached chat sessions re-parse.

modelKeyMatches no longer lets claude-sonnet-4 capture claude-sonnet-4-6.

* Copilot: bill chat-session credits exactly and skip agent-host twins

A chat-session request that records copilotCredits now costs credits x $0.01 through costFromBilling; requests without it stay priced from tokens. A chat session whose id matches a session-state dir is left to the agent-host path so it is counted once.

* Copilot: drop agent-host twins at serve time, rate chat requests one by one, re-derive past days

A chat-session journal request is left out when the same session has billed agent-host usage (session-state calls with tokens or nanoAiu, or session-store rows). Plan credits rate chat-session requests individually, so older requests in a mixed session keep their estimate. DAILY_CACHE_VERSION 75 with copilot pending re-derive so settled days pick up the new counts.
2026-10-13 01:02:04 +02:00
Resham Joshi
4c1d524b85
feat(update): Windows tray updates the CLI through the engine (#1743)
The Windows tray updates the CLI through codeburn update, for Store and non-Store installs, and hands desktop updates to the desktop app.
2026-10-11 04:36:20 +02:00
Resham Joshi
679d2f715b
feat(update): one codeburn update command for CLI, menu bar and desktop (#1740)
Adds codeburn update [--check] [--cli|--menubar|--desktop|--all] [--local] [--json] with a fixed JSON step/result contract that the desktop app, menu bar and Windows tray call, so every surface updates in one click without Terminal. Also documents the npm-first release order and adds always-latest /download links.
2026-10-11 04:33:57 +02:00
Resham Joshi
7431a34782
test(cursor-sync): use a week window so the test passes at any hour (#1744)
The CSV row is stamped two hours before now, so a today window failed between 00:00 and 02:00 UTC.
2026-10-11 04:04:54 +02:00
Resham Joshi
ec24de6e65
fix(pr): charge pushed branches to their PR and show every PR's details (#1737)
* fix(pr): push evidence beats the carried pr-link, show every PR fact

Claude Code's pr-link tags a session with its latest PR and CodeBurn carried
that tag to every later turn and subagent run. A run or turn that pushes the
head branch of a PR opened in the same data (paired gh pr create --head) now
goes to that PR, same repo only; spend left only on the tag is carriedUSD.
Rows also get runList (why each run counts), a locally evidenced state and
follow-up round times, and a call opening several PRs pairs its pr-link with
the last create. The PR card states every unknown and the expanded view shows
timeline, runs, cost by phase and work as compact tables.

* fix(app): full text on hover for cropped PR cells, plainer wording for same-session spend

Truncated PR labels, run descriptions, table labels and summary figures now show their full text in the shared tooltip surface while cut off. The carried-from-session-tag wording becomes 'from other work in the same session' in all six locales.

* fix(pr): explain carried spend without the pr-link jargon
2026-10-10 23:47:23 +02:00
Resham Joshi
6ddd8b6735
fix(quota): say when Cline's saved login expired and how to renew it (#1736)
* fix(clinepass): say when the Cline login expired and how to renew it

An expired saved Cline login only said "Run cline to refresh it". It now
reads "Cline login expired 3h ago" (age from the saved expiry, when known)
and asks to send a message in Cline or run `cline auth cline`, then press
Retry. Still a retryable state, so the last quota is kept. CLI and menubar.

* fix(menubar): title an expired Cline login instead of "Retrying"

A provider failure can now carry a short status title. The expired Cline
login sets "Cline login expired", which the settings pane and the dock
caption show in place of the generic retrying text. It stays a retryable
state and keeps the last quota; the title clears with the error.

* fix(clinepass): match the expired-login hint to the visible control

"Then press Retry" only fits where a Retry button shows: the settings pane
with no last quota. Everywhere else, including the CLI, the hint now ends
with "CodeBurn picks up the new login on its next refresh."
2026-10-10 21:40:52 +02:00
Resham Joshi
4c779ef301
fix(commandcode): show the monthly window (#1734)
Command Code's credits endpoint only reports monthly credits left, so the
monthly window was dropped. For an active subscription on a known plan the
plan price now comes from a small table (the API has no monthly cap field):
pool = max(plan, left), used = pool - left, reset = currentPeriodEnd.
Unknown plans and purchased/free credits stay out. CLI and menubar mirrored.
2026-10-10 21:40:43 +02:00
Resham Joshi
48773459ab
feat(pr): attribute PR work honestly and show one shot, review runs and follow-ups (#1733)
* fix(pr): a subagent whose description names a PR folds into that PR

A subagent run folded into whatever PR its parent's last pr-link carried, so
review runs for #1703, #1712, #1728 and #1729 landed on a stale closed #24.
The Agent description ("Review PR 1729 ...") is now read from the subagent's
.meta.json at report time; a single PR it names outranks the carried PR.

* fix(pr): date each PR by its own turns and count its agent runs

Every PR row carried the whole session's first and last timestamp, so one
long orchestrator session stamped the same span and "1 session" on all of
them. Rows now span their first to last attributed turn or folded run, and
a new `runs` field counts contributing sessions plus folded subagent runs.

* feat(pr): local one-shot, follow-up rounds, review runs, merge time and cost split per PR

Each PR row now carries signals read from cached commands, no GitHub calls:
openedAt (gh pr create paired with the PR's first pr-link), follow-up rounds
(pushes naming its --head branch, or from a run whose description names it,
in 10-minute bursts), oneShot only when the open and its branch were seen,
reviewRuns and reviewCostUSD, mergedAt and timeToMergeMs, a build/review/fixes
cost split that sums to the row, and linkEvidence to flag pr-link-only rows.

* feat(session-why): PRs the session opened, their follow-up rounds and review runs

The why view now carries prWork: each gh pr create whose output printed a PR
URL, the pushes after it on its --head branch (or from a helper whose
description names that PR) in 10-minute rounds, and the helpers that read a
PR without committing or pushing, with their cost.

* perf(pr): only parse commands that can name a push or a PR

* feat(app): local PR facts on PR cards and PR work in the session view

Each PR card gets one line of facts from the CLI's local signals: the review
run and its cost, one shot or follow-up rounds, and merge time after open.
Unknowns are left out; a pr-link-only row says so. The count shows agent runs
instead of sessions. The expanded card splits spend into building, review runs
and after first review, and lists the review runs. The session view lists the
PRs it opened and the review runs it spawned.

* perf(pr): read subagent descriptions only for PR attribution

Every report, status and menubar refresh read each subagent's .meta.json
(about 2,000 files in 30 days) and the cross-provider correlation scanned
every folded run's commands for PR signals it never uses. Sessions now carry
the sidecar path; the description is read on demand, memoized by mtime, only
for folded runs and runs that push, and correlation skips the command scan.
The by-pr footer now counts runs to match the Runs column.
2026-10-10 21:40:39 +02:00
Dan Caldr
26c99ed860
fix(antigravity): fall back to the chat model and map Gemini 3.8 Flash placeholders (#1712)
Some checks are pending
CI / semgrep (push) Waiting to run
macOS Menubar CI / test (push) Waiting to run
Tests / test (22) (push) Waiting to run
Tests / test (22.13.0) (push) Waiting to run
Tests / test (24) (push) Waiting to run
Tests / test (26) (push) Waiting to run
Tests / test-platforms (macos-latest) (push) Waiting to run
Tests / desktop (push) Waiting to run
Tests / vscode (push) Waiting to run
Tests / test-platforms (windows-latest) (push) Waiting to run
Windows Menubar CI / check (ubuntu-latest) (push) Waiting to run
Windows Menubar CI / check (windows-latest) (push) Waiting to run
When an Antigravity response left out the model, the parser crashed on the legacy .pb path and the whole session was skipped. It now falls back to the chat's own model and passes it through the live model map.

Maps the Gemini 3.8 Flash placeholders M318/M319/M320 to the high/medium/low ids from Antigravity's model catalog, and the gemini-3-flash-a/-d family strings to gemini-3-flash-preview (LiteLLM prices, nothing hardcoded). Parse version, results cache v9 and daily cache v74 bumped so cached rows re-parse; calls and cost only rise.

Thanks @dcaldr.
2026-10-10 11:34:13 +02:00
Resham Joshi
247957ff91
perf(cursor): stop JSON and menubar answers waiting on cursor.com (#1724)
* perf(cursor): stop JSON and menubar answers waiting on cursor.com

report/today/month --format json, overview and status --format menubar-json
awaited the hourly Cursor usage download (15 s cap, 45 s on the first
backfill) before parsing. The download now runs beside the command and its
import applies after the command (serve: between two requests, then its
memos are dropped), so the next run shows it. Dashboards still wait.

* test(cursor-sync): keep the in-process sync enabled when the shell disables it

* test(cursor-sync): wait for the serve memo instead of a fixed answer count

The serve test expected the second answer to be memoized, but serve only
memoizes once its root watchers are armed, which on a slow CI runner lands
after that answer. Keep asking until two answers share a generation. The
spawn now imports the fake fetch by file URL so Windows accepts the path,
and the in-process test points USERPROFILE at the temp home too.
2026-10-10 07:24:37 +02:00
Resham Joshi
6f4c3f772b
perf(models): memoize short display names by model id (#1727)
Warm reports re-derived the display name of every cached call, a linear
scan of the short-name table each time (about 0.5 s on a lifetime run).
The name depends only on the aliases and the pricing namespaces, so it is
memoized per model id and cleared when either changes.
2026-10-10 07:06:23 +02:00
Resham Joshi
02dff282a8
perf(codex): resolve fork parents once on the main thread (#1725)
The fork-parent memo lived in module state, so every parse worker indexed
the Codex home and read the same parent rollouts again (58 reads of 10
parents, 8 GB for 1.28 GB on a real corpus). Workers now ask the main
thread over a message port, which keeps one memo for the whole parse.
2026-10-10 07:06:15 +02:00
Resham Joshi
95651189e2
fix(optimize): price deferred MCP tools as name lines, once per call (#1720)
The low-coverage and project-scope findings only see servers whose tools were
deferred, yet charged 400 tokens per tool and counted every call as both a
cache write and a cache read. A deferred tool is one name line (~9 tokens),
and each call now pays it as a read, or as a write only when it read no cache.
2026-10-10 07:05:50 +02:00
Resham Joshi
aa85ed2312
fix(optimize): stop flagging delivered and editing sessions as low-worth (#1719)
The delivered check read bashBreakdown keys, which are base names (git), so
git commit / gh pr create never matched; it now reads the full command from
each call's tool sequence (Kimi Code now records one) and allows git global
options. Devin and cursor-agent edit tools count as edits, and the retry
share divides by edit steps instead of user prompts.
2026-10-10 07:05:47 +02:00
Resham Joshi
89bc4598f8
fix(cursor-agent): leave Cursor IDE chat transcripts to the cursor provider (#1717)
* fix(cursor-agent): leave Cursor IDE chat transcripts to the cursor provider

Cursor IDE agent chats write agent transcripts too, so on days without a
Cursor import they were counted by both cursor (state.vscdb) and
cursor-agent. Discovery now skips a transcript or store whose id is an IDE
composerData key; cursor keeps them with per-bubble times and models.
Daily cache v73 lets cursor-agent days shrink once.

* fix(cache): drop old cursor-agent slices the IDE parse now explains

On an existing cache, settled days kept their old cursor-agent slice after
v73 because the fresh parse no longer has one there and the merge carries
missing slices forward. While cursor-agent is pending its v73 re-derive, a
day the fresh parse explains with a cursor slice and no cursor-agent slice
drops the old cursor-agent slice; days with no fresh cursor slice keep it.
2026-10-10 07:05:35 +02:00
Resham Joshi
ed3c3f466b
fix(cursor): parse the full lookback regardless of the requested range (#1718)
The session cache keys the Cursor parse on the DB fingerprint alone, but the
parse stopped at the requested range start, so a narrow run (7 days) was served
to every wider range afterwards (30 days showed 2 calls instead of 1,680). The
parse now always reads the full 6 month lookback; the cursor parse version is
bumped so cached narrow entries re-parse.
2026-10-10 07:05:31 +02:00
Resham Joshi
4af19b73c7
fix(pricing): apply Anthropic long-context tiers to Claude calls (#1716)
Claude calls never used the LiteLLM above-threshold rates, so Haiku 5.5
requests over 100k prompt tokens (input + cache read + cache write) billed
at the base rate. claude joins TIERED_PRICING_PROVIDERS and the transcript
paths pass the provider. Long-prompt test fixtures move to Sonnet 4.6, which
has no tier, so their round figures hold.
2026-10-10 07:05:27 +02:00
Resham Joshi
514ef7081d
fix(currency): show USD instead of a foreign symbol when no FX rate is available (#1715)
When the Frankfurter fetch failed (or under CODEBURN_FX_NO_FETCH) with no
fresh cache, the rate fell back to 1 but the foreign code and symbol stayed,
so USD amounts showed as EUR/JPY. Now the last cached rate of any age is used,
and with none the display falls back to USD code and symbol.
2026-10-10 07:05:23 +02:00
Resham Joshi
039a4e483e
fix(overview): group thousands in the integer part of costs only (#1714)
The comma-grouping regex in overview and devices also matched the fraction,
so $0.0028 rendered as $0.0,028. A lookbehind now skips digits after the
decimal point.
2026-10-10 07:05:12 +02:00
Resham Joshi
b62e4a30ee
fix(cache): drop stale cursor-agent slices on days a Cursor import covers (#1710)
Some checks are pending
CI / semgrep (push) Waiting to run
Tests / test (22) (push) Waiting to run
Tests / test (22.13.0) (push) Waiting to run
Tests / test (24) (push) Waiting to run
Tests / test (26) (push) Waiting to run
Tests / test-platforms (macos-latest) (push) Waiting to run
Tests / test-platforms (windows-latest) (push) Waiting to run
Tests / desktop (push) Waiting to run
Tests / vscode (push) Waiting to run
* fix(cache): drop stale cursor-agent slices on days a Cursor import covers

On a day the import fully covers, the parse drops local cursor-agent (and
Grok Bot, when the store has its rows) calls, so the fresh day has no slice
for them and the merge carried the cached one on top of the imported rows.
Adoption and the complete re-derive now drop those slices. Bump to v72 so
caches that already carry them get repaired.

* test(cache): keep Grok Bot slices an import holds no Grok Bot rows for
2026-10-10 03:16:02 +02:00
Resham Joshi
ef58a17b81
fix(cache): keep history exact when a cache upgrade meets a timezone change (#1713)
* fix(cache): adopt the top cache file's timezone, not the machine's

Adoption kept only files in the machine's timezone whenever any file had it, so an older file in that zone pushed out the newest complete one and every day only it still held. A first run under TZ=UTC on a cache with one old UTC file dropped 46 days.
Adopt the top-priority file's timezone and let the existing timezone re-derive re-bucket the result.

* fix(cache): re-bucket an adopted older cache once after a timezone change

Adopting only older-version files built the cache with an empty savings hash,
so hydration skipped the tz subtraction and the partial-survival guard kept
old-zone days over the fresh ones, counting the hours between the two
midnights twice. Adoption now keeps the top file's hash. A tz residual over a
fresh slice with data also replaced that slice instead of adding to it.

* fix(cache): never let a timezone change inflate when the savings hash also changed

With a tz change and a savings hash change in the same re-derive (every
0.9.25 upgrade, since builtin price overrides changed the hash), the tz
subtraction was skipped and the partial-survival guard put old-zone slices back
over fresh ones, counting the hours between the two midnights twice. Now the
old-tz re-aggregation still runs: a baseline slice any surviving source explains
is dropped whole and the fresh slice wins, with no guard swap. compare
--with-history now passes the tz aggregator too.

Tradeoff: in this case the source-gone part of a partly surviving (day,
provider) is lost, since old-priced and new-priced cost cannot be split
(subtracting would carry the price drift as spend). With a matching hash the
exact subtraction still keeps it.

* fix(cache): keep a tz residual's breakdown within its own totals

The tz subtraction reduces an old slice's totals but its project, model and
category maps only key by key, so keys a fresh parse no longer produces (pre-v67
label-only projects, a project now split per worktree, renamed models) stayed
whole and the summed residual stacked them on the fresh slice. Scale each map
back to the residual's totals, keeping the old keys and whole counts.
2026-10-10 02:56:14 +02:00
Resham Joshi
5253b89286
fix(cache): keep midnight-straddling turns whole on the settle reseal (#1709)
The seal-time re-derive parsed from today-7, so a turn crossing that midnight was cut and its later part re-anchored to the window start, while the day before kept the whole turn. Category cost, turn and session counts on the window start were counted twice.
Parse one day earlier when re-deriving already sealed days and keep only the window. A stale watermark still parses from its first unsealed day, as in 0.9.25.
2026-10-10 02:56:08 +02:00
Resham Joshi
18c4d4043f
fix(projects): take project row money from the day set only (#1708)
With a day set, buildPayloadProjects also added live cost for any project
identity the days did not hold. Live per-call resolution (#1689) and bare
cursor-agent labels can put sealed calls under a new identity, so they were
counted twice and project rows summed above the period total. Rows now take
cost, savings and calls from the same days as the headline; live still
supplies sessions, details and paths.
2026-10-10 02:56:00 +02:00
Resham Joshi
ae30d677f8
fix(projects): stop the git origin walk at the folder's own repo (#1707)
The walk-up skipped a .git without an origin and kept climbing, so a
local-only repo nested in another repo, or any folder under a home dotfiles
repo, took the ancestor's origin and merged into that project row.
Now it stops at the first .git (its origin or none) and never uses home as
the repo of a folder below it.
2026-10-10 02:55:54 +02:00
Resham Joshi
7390810a61
fix(codex): count cache writes toward the long-context threshold, tier only above 272k (#1706)
The tier threshold summed input and cache read but left out cache-write
tokens, which codex splits out of input_tokens on gpt-5.6+ and gpt-6. A
280k-token prompt with 80k cache writes priced at base rates. OpenAI also
bills 272,000 exactly as short context, so the tier now starts above the
threshold. The audit recompute follows the same rule.
2026-10-10 02:55:47 +02:00
Resham Joshi
f5906a0c68
fix(copilot): price JetBrains claude-opus-4.7 as Opus 4.7 (#1705)
The dot-minor lookahead from #1531 stops claude-opus-4 matching
claude-opus-4.7, and 4.7 was not in the token list, so those turns fell
back to copilot-anthropic-auto (Sonnet 4.5 rates). Add the token and bump
the copilot session-cache parse tag so cached turns re-parse.
2026-10-10 02:55:34 +02:00
Uttam Deb
83a18e1308
fix: make npm run dev work on Windows (#1629)
Some checks are pending
CI / semgrep (push) Waiting to run
macOS Menubar CI / test (push) Waiting to run
Tests / test-platforms (macos-latest) (push) Waiting to run
Tests / test-platforms (windows-latest) (push) Waiting to run
Tests / desktop (push) Waiting to run
Tests / vscode (push) Waiting to run
Windows Menubar CI / check (ubuntu-latest) (push) Waiting to run
Windows Menubar CI / check (windows-latest) (push) Waiting to run
Tests / test (22) (push) Waiting to run
Tests / test (22.13.0) (push) Waiting to run
Tests / test (24) (push) Waiting to run
Tests / test (26) (push) Waiting to run
npm run dev now works on Windows: the NODE_OPTIONS prefix was POSIX-only, so the script runs node --no-deprecation --import tsx directly.
2026-10-09 13:38:06 +02:00
ozymandiashh
a01bf87412
feat(codex): show thread names from session_index.jsonl (#1687)
Codex sessions now show their thread names from session_index.jsonl, applied after the cache, so renames show without a re-parse. Closes #1625.
2026-10-09 13:25:13 +02:00
Resham Joshi
b876a311e2
feat: telemetry for CLI error causes, provider read failures and update results (#1702)
cli_error gains duration, exit, a locally classified reason and the scoped provider. New provider_read_fail (desktop) and update_result (desktop, menubar, tray) events, enums only.
2026-10-09 12:47:49 +02:00
Resham Joshi
1fba972580
fix: Cursor import clears covered days on a new build's first run (#1698)
invalidateProviderDays no longer skips when the build has no daily-cache file yet, so the first Cursor import clears the covered days. cursor joins the pending re-derive list at v71 so stuck caches repair once.
2026-10-08 21:56:22 +02:00
Resham Joshi
b875d9ca1d
fix: one timezone per daily cache, re-derive settling days on seal (#1696)
Cache adoption keeps one timezone and prefers complete parses. Sealing a day re-derives the last seven, and a sealed day only takes a fresh parse with at least as many calls.
2026-10-08 20:18:23 +02:00
Resham Joshi
eb51aba46a
feat: project links put a folder in a repo project (#1695)
Adds codeburn project link <folder> <project>, project unlink <folder> and
project links, so a folder that is not a checkout of a repo can join that
repo's project row. Links live in config.json, apply at report time
wherever rows are grouped or filtered, cover subfolders (longest link
wins), and never touch the day cache, so unlink works on cached days.
2026-10-08 17:41:23 +02:00
Resham Joshi
f63fd960f6
fix: fold subagents into top sessions (#1694)
Some checks are pending
CI / semgrep (push) Waiting to run
Tests / test (22) (push) Waiting to run
Tests / test (22.13.0) (push) Waiting to run
Tests / test (24) (push) Waiting to run
Tests / test (26) (push) Waiting to run
Tests / test-platforms (macos-latest) (push) Waiting to run
Tests / test-platforms (windows-latest) (push) Waiting to run
Tests / desktop (push) Waiting to run
Tests / vscode (push) Waiting to run
Top sessions in the JSON report and the menubar/desktop payload ranked each
session by its own cost, so a parent showed without its subagents and a
subagent could show as its own row. Both producers now go through
foldedSessionRows, the same fold the sessions list uses. The Overview model
label matches a top session by provider and id, since its cost and calls
now include subagents.

Co-authored-by: Resham Joshi <resham@codeburn.app>
2026-10-08 13:08:55 +02:00
Resham Joshi
8fb478904e
feat: group a deleted sibling clone with its repository by folder name (#1692)
* feat(app): transient project selector in the desktop top bar

Scopes every report to one project path for the current session through
the same narrowing the VS Code workspace scope uses. The saved project
filter is never written; a restart is back on all projects.

* fix(app): let the name pickers ellipsize before the top bar overflows

* fix: key the daily project split per real path, not per label

A project label can span several paths: every Claude session started from the
home folder shares -Users-<name>, while its cwd puts it in a different project.
The day rollup keyed by label kept the first path it saw, so a rooted
--project headline took the whole label bucket on some days and missed it on
others. Key per (label, path), read old label-keyed days unchanged, bump the
daily cache to v68.

* fix(app): list only selectable, non-zero projects in the top bar picker

Rows with no session cwd (the Cursor and Grok Bot imports, provider label
fallbacks) report their label as the path, so the picker offered names the
bridge refuses and dash-to-slash guesses like eywa/lab. Keep only absolute
paths, and drop rows whose lifetime cost rounds to $0.00. The old test that
rooted a slashless Codex path is replaced: Codex now records the absolute cwd,
and a slashless path is always the label fallback.

* fix(app): show a toast when a project pick is refused

The pick was dropped silently. Also pin that an encoded -Users-... label
never reaches the picker list.

* feat: group projects by git repository

Clones and worktrees that share an origin remote are one project in every
project list (report JSON, By Project, the top bar picker, the terminal
dashboard), with the checkouts listed under the row. A --project path inside
a checkout scopes to the whole repository; --exact-project keeps the old
single-folder match. The origin is learned while the folder exists, kept in
git-origins.json and on each day's project entry (daily cache v69), so a
deleted scratch clone stays in its repository. The report JSON project list
now comes from the durable day set, so a row costs what selecting it reports.

* fix: a picked project row selects that row exactly

The top bar pick sends =<path>: a checkout in a repository still selects the
whole repository, and a plain folder selects itself without the folders under
it, which are rows of their own. So the picker row and the headline it opens
are the same set. Typed --project paths keep their prefix match. Days adopted
from an older cache learn the origin of every checkout that still exists, and
lookups outside a parse are saved on exit.

* fix: key the status snapshot on --exact-project

* feat: one Temporary folders row for scratch folders outside a repository

Every project path under a temp root (os.tmpdir(), /tmp, /var/folders and
their /private spellings) that is not a checkout of a known repository folds
into one row, in the report JSON, By Project, the terminal dashboard, the top
bar picker and the Settings Projects pane. Its pattern @temp selects or hides
all of them. The Projects pane leaves out rows that round to $0.00 unless a
saved pattern names them, and a saved pattern naming any checkout of a
repository row now names the whole row in the renderer too.

* feat: group a deleted sibling folder with its repository by folder name

A deleted folder with no recorded origin, named <checkout>-<suffix> or
<checkout>_<suffix> next to exactly one repository's checkout, joins that
repository row. The checkout entry carries matchedByFolderName and the Spend
drill-down labels it. Existing folders and names two repositories could claim
are left alone.

* feat: the longest checkout name wins a folder-name match

A deleted folder that fits several sibling checkouts joins the one with the
longest name; it is skipped only when the longest fits belong to two
repositories. The report project type declares matchedByFolderName.

* fix(claude): attribute each call to the project of its own cwd

* test: fold the temp roots the tests set, compare checkout ids as identities (Windows)

* test: compare folder-name checkout ids as identities (Windows)

* fix(claude): keep subfolder cwds in their project, count and show a split session once

* fix: an exclude path hides that checkout only, repository rows count a session once

* feat: mark folder-name matches in the CLI report and the project picker

* fix(claude): a deleted subfolder stays its own project
2026-10-08 06:54:41 +02:00
Resham Joshi
bc62059744
feat: group projects by git repository (#1691)
* feat(app): transient project selector in the desktop top bar

Scopes every report to one project path for the current session through
the same narrowing the VS Code workspace scope uses. The saved project
filter is never written; a restart is back on all projects.

* fix(app): let the name pickers ellipsize before the top bar overflows

* fix: key the daily project split per real path, not per label

A project label can span several paths: every Claude session started from the
home folder shares -Users-<name>, while its cwd puts it in a different project.
The day rollup keyed by label kept the first path it saw, so a rooted
--project headline took the whole label bucket on some days and missed it on
others. Key per (label, path), read old label-keyed days unchanged, bump the
daily cache to v68.

* fix(app): list only selectable, non-zero projects in the top bar picker

Rows with no session cwd (the Cursor and Grok Bot imports, provider label
fallbacks) report their label as the path, so the picker offered names the
bridge refuses and dash-to-slash guesses like eywa/lab. Keep only absolute
paths, and drop rows whose lifetime cost rounds to $0.00. The old test that
rooted a slashless Codex path is replaced: Codex now records the absolute cwd,
and a slashless path is always the label fallback.

* fix(app): show a toast when a project pick is refused

The pick was dropped silently. Also pin that an encoded -Users-... label
never reaches the picker list.

* feat: group projects by git repository

Clones and worktrees that share an origin remote are one project in every
project list (report JSON, By Project, the top bar picker, the terminal
dashboard), with the checkouts listed under the row. A --project path inside
a checkout scopes to the whole repository; --exact-project keeps the old
single-folder match. The origin is learned while the folder exists, kept in
git-origins.json and on each day's project entry (daily cache v69), so a
deleted scratch clone stays in its repository. The report JSON project list
now comes from the durable day set, so a row costs what selecting it reports.

* fix: a picked project row selects that row exactly

The top bar pick sends =<path>: a checkout in a repository still selects the
whole repository, and a plain folder selects itself without the folders under
it, which are rows of their own. So the picker row and the headline it opens
are the same set. Typed --project paths keep their prefix match. Days adopted
from an older cache learn the origin of every checkout that still exists, and
lookups outside a parse are saved on exit.

* fix: key the status snapshot on --exact-project

* feat: one Temporary folders row for scratch folders outside a repository

Every project path under a temp root (os.tmpdir(), /tmp, /var/folders and
their /private spellings) that is not a checkout of a known repository folds
into one row, in the report JSON, By Project, the terminal dashboard, the top
bar picker and the Settings Projects pane. Its pattern @temp selects or hides
all of them. The Projects pane leaves out rows that round to $0.00 unless a
saved pattern names them, and a saved pattern naming any checkout of a
repository row now names the whole row in the renderer too.

* fix(claude): attribute each call to the project of its own cwd

* test: fold the temp roots the tests set, compare checkout ids as identities (Windows)

* fix(claude): keep subfolder cwds in their project, count and show a split session once

* fix: an exclude path hides that checkout only, repository rows count a session once

* fix(claude): a deleted subfolder stays its own project
2026-10-08 06:45:26 +02:00
Resham Joshi
d49a6a4eba
fix(claude): attribute each call to the project of its own cwd (#1689)
* fix(claude): attribute each call to the project of its own cwd

* fix(claude): keep subfolder cwds in their project, count and show a split session once

* fix(claude): a deleted subfolder stays its own project
2026-10-08 06:28:32 +02:00
Resham Joshi
d3fecffa9a
feat(sessions): fold subagent sessions into their parent row (#1690)
* feat(sessions): fold subagent sessions into their parent row

The default sessions list (table, JSON, TUI, contributions, desktop) now shows
one row per parent session with its provider-recorded subagents summed in and
listed under subagents. Grouping reuses the work-unit resolver, so only rows
move; totals are unchanged. --by-work-unit keeps the flat rows.

* test: reconcile the session diagnosis with the folded sessions row

* fix(sessions): find and open a parent row by its subagents, count subagents apart from top-level rows
2026-10-08 06:27:58 +02:00