Commit graph

163 commits

Author SHA1 Message Date
iamtoruk
1acdecdebf test(codex): give the every-boundary differential an explicit timeout
110 resume splits take ~1.3s locally but exceeded vitest's 5s default on the
CI runner once the parallel suite also hosts the parse-worker tests.
2026-08-17 01:48:04 -07:00
iamtoruk
0e11b51516 fix(cache): make the shard layout safe against a second live writer
Two live processes share one cache directory routinely (a one-shot CLI
beside the resident serve child, two menubar polls), and the shard layout
had two ways to lose data there.

- The atomic write used a FIXED temp name, so two writers publishing the
  envelope — every save does — shared one `envelope.json.tmp` and
  interleaved into a torn payload, or one deleted the shards the other's
  envelope named. 39 of 40 rounds ended in a total cache loss. The temp
  name carries a nonce again, as it did before the shard layout.
- A save reused a shard filename from its own load snapshot without
  checking the file was still there. Another process republishing that
  provider unlinks the old shard, so the stale writer published an
  envelope naming a deleted file — read back as a corrupt provider and
  dropped whole, including PR-linked orphans no re-parse can recover. A
  reused shard is now existence-checked, and re-verified once more
  immediately before the envelope is published; a vanished one is
  rewritten from memory.

Also:
- Progress saves take a 30s floor beside the file counter. Only the
  claude scan reports per file; every other provider calls saveProgress
  once at its own boundary, so the counter alone never fired there.
- The unreferenced-shard sweep waits an hour (temps still 5 minutes): an
  unreferenced shard may belong to a concurrent save whose envelope has
  not landed yet.
- The sweep also retires the pre-v8 single-file temps in the parent
  directory, which nothing writes anymore.
- The shard directory is created 0o700.
- The claude and provider paths mark the cache dirty where they DELETE a
  stale entry, not only where they replace it: an unreadable file skips
  the replace, and the deletion would otherwise live only in memory.
- Codex only treats a grown file as an append when the recorded boundary
  still lands just after a newline, so a same-inode rewrite that happens
  to end up larger re-parses instead of resuming mid-line.
2026-08-16 19:23:39 -07:00
iamtoruk
72ed163db0 perf(codex): resume an appended rollout from its last task boundary
Codex rollout files are append-only and the active ones run to hundreds
of MB, but any growth re-read the file from byte 0 because the cache
keyed only on mtime+size. The parser now records a restart point at every
task_started boundary — the byte offset plus the state the single-pass
decode carries across it — and a grown file with the same dev/ino picks
up from there.

The boundary sits at the task_started line itself, so the task it opens
is re-decoded from the tail; the entry stores how many calls were decoded
before that point so the resumed run starts from exactly those and cannot
double-count the open task. An unusable or absent snapshot falls back to
a full re-parse.

CODEX_CACHE_VERSION is deliberately not bumped: the new fields are
additive and absence-safe both ways, so a bump would discard a warm
cache for nothing.
2026-08-16 19:03:12 -07:00
iamtoruk
0bfdfa372a perf(cache): shard the session cache per provider
A warm launch rewrote the entire session cache whenever any provider
appended a few KB: on a 6 GB corpus that is a 155 MB stringify + fsync
every run. The on-disk cache is now a version-suffixed directory holding
one shard per provider plus a small envelope, and a save rewrites only
the providers marked dirty.

- Dirtiness is tracked per provider (markCacheDirty) instead of one
  global flag, so an appended Claude session no longer republishes
  Codex, Copilot and the rest.
- Shards carry a nonce in their filename and the envelope is renamed
  last, so a save is published at a single point: readers never see a
  half-updated set, and a writer that loses the refresh ownership fence
  leaves the canonical shards untouched.
- A shard that fails validation is treated as an absent provider rather
  than rejecting the whole cache, so one malformed turn costs one
  provider's re-parse instead of every provider's history.
- v7 migrates losslessly: the blob is re-laid-out into shards and
  removed only once that save publishes. Nothing re-parses.
- Cold-parse progress saves now trigger every N files parsed rather than
  every 5s, so a slow cold parse no longer rewrites the growing cache on
  a wall clock.
2026-08-16 19:03:12 -07:00
ozymandiashh
a95a2c5bf8 fix(desktop): close cache and lifecycle review gaps 2026-08-12 20:31:17 +03:00
ozymandiashh
d8d343e83a perf(desktop): share cache state and eliminate duplicate cold hydration 2026-08-12 17:16:41 +03:00
iamtoruk
27eac2cca4 Merge main; declare openclaude in the env-declaration guard file map 2026-08-10 02:49:25 -07:00
Resham Joshi
ad3b12bb4d
Merge pull request #908 from ozymandiashh/fix/swarm-robustness
fix(hardening): guard three malformed-input crashes (URL, timestamp, pricing entry)
2026-08-10 02:40:33 -07:00
Matthew Kelch
3536a1d3ac
fix(copilot): classify CLI sessions by source provenance, not producer (#945)
Some checks failed
CI / semgrep (push) Has been cancelled
Tests / test (push) Has been cancelled
Fixes #944.
2026-08-09 04:51:44 +03:00
ozymandiashh
b7235adb16 fix(parser): fold SQLite -wal siblings into source fingerprints
Hermes, Cursor, OpenCode and copilot OTel sources all live in SQLite
databases that their agents keep open in WAL mode for the life of the
process. Committed writes park in <db>-wal until a checkpoint, so the
main file's stat can sit hours or days behind the newest committed data.

fingerprintFile only statted the main file, which broke two ways:

- The date-range mtime pre-filter in parseProviderSources read the stale
  mtime as "nothing in range" and skipped the source entirely. Every
  Hermes session committed after the last checkpoint vanished from
  reports: the today-parse skipped the db (mtime < local midnight) while
  the backfill only keeps days through yesterday. Exactly the "17
  sessions in the DB, 14 reported, the 3 from today missing" report in
  issue #913.

- reconcileFile saw an unchanged fingerprint between checkpoints and kept
  serving stale cached turns for sessions that had since grown.

Fold the -wal sibling into the fingerprint: newest mtime wins and sizes
add, so both WAL growth and a checkpoint (db grows, wal truncates) move
the fingerprint. -shm is deliberately ignored (it mutates on reads).
Bare SQLite paths get the fold only when the extension says database, so
JSONL transcript fingerprints (offset-based append detection) are
untouched.

Refs #913
2026-08-04 14:38:43 +03:00
ozymandiashh
48fd0daa0c fix(providers): guard two malformed-input crashes in the parse path
- vscode-cline-parser: entry.ts was truthy-checked but not validity-checked,
  so a garbage timestamp made new Date(ts).toISOString() throw RangeError and
  abort the whole session parse. Validate the date, fall back to empty.
- models: parseLiteLLMEntry read fields off its argument with no null/type
  guard, so a null value in the remote LiteLLM pricing JSON threw and aborted
  the entire live pricing load. Return null for a null/non-object entry.

Both mutation-checked: the tests raise RangeError / TypeError before the fix.
2026-08-04 06:28:41 +03:00
ozymandiashh
6c4645a8bc fix(kiro): estimate input tokens from the full prompt, not a 500-char slice
parseChatFile estimated input tokens from pendingUserMessage - the last
human turn sliced to 500 chars - while output summed every bot char, so a
multi-turn session or any prompt over 500 chars undercounted input tokens
and therefore costUSD severalfold. Accumulate every human turn's full
length (inputChars), matching the modern-execution path; keep the 500
slice for the display userMessage only. Mutation-checked: a 2400-char
prompt reports 125 tokens before, 600 after.
2026-08-04 05:58:17 +03:00
ozymandiashh
718a2b3a08 feat(openclaude): OpenClaude CLI provider (#213)
OpenClaude is a Claude Code fork routing to any LLM; transcripts are
Claude-Code-schema JSONL under ~/.openclaude/projects/<slug>/<uuid>.jsonl
with replay.json siblings skipped. Only usage-bearing assistant lines
become calls; sidechain lines are counted as real spend; costs are always
computed (the transcript reports none) through the shared tables.

Real local testing: sessions generated with the actual CLI against
DeepSeek (deepseek-chat), parsed end to end.
2026-08-04 04:05:25 +03:00
AgentSeal
c642787113 fix(cline-cli): decline the rollup when per-message calls were all deduped
The rollup fallback was gated on the post-dedup emitted counter, so a session
whose per-message calls were all suppressed by the shared dedup (a duplicated
session directory reusing a session_id) fell through to the metadata.usage
rollup and double-counted its cost. Gate on a hadMetrics flag set before the
dedup check instead.
2026-08-04 01:57:27 +02:00
Rick Culpepper (claude)
448d470049 feat(providers): add cline-cli provider for Cline CLI sessions
The Cline CLI (npm `cline`, 3.x) stores sessions as
<sessions>/<id>/<id>.json + <id>.messages.json. The existing `cline`
provider only discovers tasks/<id>/ui_messages.json, so every CLI session
was silently reported as $0.00 — no warning, not even under --verbose.

Adds `cline-cli` as its own provider rather than a third root on `cline`,
leaving the shared Cline-family parser (Roo Code, KiloCode, IBM Bob)
untouched. It mirrors the CLI's own root resolution
(CLINE_SESSION_DATA_DIR -> CLINE_DATA_DIR -> CLINE_DIR -> ~/.cline),
implements probeRoots() so `doctor` can tell "not installed" from "wrong
override", emits one call per assistant message's `metrics` block, and
falls back to the session rollup when a session carries none. The
fallback reads `usage`, not `aggregateUsage`, which folds in spawned
subagents that are themselves separate session directories.

Two supporting changes, both required for CLI costs to report correctly:

- parser.ts re-priced cline-cli calls from tokens because the provider
  was not on the reported-cost allowlist, inflating a real 12-session
  local sample from $1.11 to $3.92.
- session-cache.ts gains the matching PROVIDER_ENV_VARS entry (so a
  changed override invalidates) and a `reported-cost-v1` parse version
  (so sessions cached before the allowlist fix re-parse once instead of
  being re-priced forever).

Cost is treated as metered only when actually present and non-negative,
so a metered $0 stays reported while a missing or negative cost falls
back to token pricing — applied identically on the per-message and
rollup paths. Timestamps promote a seconds-resolution value rather than
silently landing in 1970, matching the guard kiro.ts uses.

CLINE_DIR / CLINE_DATA_DIR / CLINE_SESSION_DATA_DIR are added to the test
env-isolation list so a developer's real sessions cannot bleed into
fixtures.

The VS Code variant discovery bug reported alongside this in #874 is
deliberately NOT fixed here — it shipped in #882.

Verified against 18 real local sessions: 142 calls, 4,934,762 input /
224,561 output tokens, and a cost matching the CLI's own metered total to
the cent. `codeburn doctor` reports "Cline CLI  OK".

Refs: #874
2026-08-04 02:45:16 +03:00
Resham Joshi
2c69516b02
Merge pull request #882 from ozymandiashh/fix/874-cline-vscode-variants
fix(cline): scan all VS Code variants for task storage
2026-08-03 15:33:23 -07:00
AgentSeal
4ff3497eb8 fix(codex): guard non-string timestamp and model on the parse path
Follow-up to #881. Structural discovery admits third-party rollouts whose
schema is unverified. Two unchecked JSON.parse fields still reached string ops
on the parse path: an unparseable timestamp threw RangeError out of the
fork-cutoff Date math, and a non-string model threw TypeError from calculateCost
(.replace). Either sank that session's usage to zero. Skip the fork cutoff for
an unparseable timestamp, and only adopt a string model (falling back to a real
model otherwise) so the session is counted instead of silently reading zero.
2026-08-04 00:14:28 +02:00
Resham Joshi
733003df35
Merge pull request #881 from ozymandiashh/fix/873-codex-originator-structural
fix(codex): validate rollouts structurally instead of by originator
2026-08-03 15:08:00 -07:00
ozymandiashh
eece4cf005 fix(codex): validate rollouts structurally instead of by originator
Codex session discovery required `payload.originator` to start with
"codex" (case-insensitive). `originator` is a free-form client identity
string, not a format marker: any tool driving `codex app-server` writes
structurally identical rollouts under ~/.codex/sessions with its own
value ("t3code_desktop", "JetBrains.IntelliJ IDEA", ...). Those sessions
were silently dropped from every report, and each past fix only admitted
one more spelling.

Gate on structure instead: a first line that parses as JSON, has
type === "session_meta", and carries a plain-object payload. Foreign and
malformed files are still rejected. Directory ownership decides the
provider — codex.ts is the only provider that reads ~/.codex, and the
walk only visits rollout-*.jsonl under the strict YYYY/MM/DD path or
archived_sessions/ — so no double counting is possible. `originator` is
still parsed onto the meta entry; nothing downstream reads it.

Bump the daily cache to v16. Historical days are served from that cache
(usage-aggregator only recomputes today) and retention is ten years, so
without a bump an upgrading user with a warm cache keeps the pre-fix
rollups forever: discovery reruns, so the session COUNT moves, while
cost and calls stay frozen — a self-contradicting report that reads as
"fixed". Measured on a fixture with two same-day rollouts, one
codex-cli and one t3code_desktop:

  pristine main, fresh cache      cost 4.55  calls 1  sessions 1
  this branch, main's warm cache  cost 4.55  calls 1  sessions 2  (was)
  this branch, main's warm cache  cost 18.2  calls 2  sessions 2  (now)
  this branch, fresh cache        cost 18.2  calls 2  sessions 2  (truth)

CODEX_CACHE_VERSION and PROVIDER_PARSE_VERSIONS.codex deliberately stay
put: both caches are keyed per file path and are written only after a
successful parse, so a file rejected at discovery has no entry to
invalidate. Verified on the fixture above — main's codex-results.json
and session-cache.v7.json hold only the first-party rollout, and reusing
them unchanged still yields the correct total.

Harden `payload.cwd` while admitting unverified clients. It is declared
`string` but comes straight off JSON.parse, and a number/object/array
threw "cwd.replace is not a function" out of sanitizeProject; the throw
escaped discoverSessions into safeDiscoverSessions, which returns [] for
the WHOLE provider, so one malformed file made every Codex report read
zero. Guarded in discovery (falls back to the `unknown` project) and on
the parse side, where a non-string cwd would otherwise ride into
projectPath/workingDirectory and reach the parser's path helpers.

Closes #873, closes #626.
2026-08-04 00:15:24 +03:00
ozymandiashh
43410e88a7 fix(cline): scan all VS Code variants for task storage
Cline discovery only looked at the stable VS Code globalStorage root, so
tasks created in VS Code Insiders or VSCodium were never found. The
singular getVSCodeGlobalStoragePath helper returns paths[0], and because
the provider always passed a concrete overrideDir, the 3-variant fallback
inside discoverClineTasks was never reached - unlike the Roo Code and
KiloCode siblings, which pass overrideDir straight through.

Build the default roots from getVSCodeGlobalStoragePaths (stable,
Insiders, VSCodium) plus the ~/.cline/data root and hand them to
discoverClineTasks in one call. The existing dedupe by task id still
collapses a task id seen in more than one root, so totals cannot inflate.
The configuredDirs override used by tests and createClineProvider(dirs)
is unchanged.
2026-08-04 00:01:57 +03:00
AgentSeal
80f5ed6cf7 Merge branch 'main' into fix/opencode-session-model 2026-08-03 22:56:49 +02:00
AgentSeal
62f6eb27c7 Merge branch 'main' into fix/omp-title-slot-discovery
# Conflicts:
#	src/providers/pi.ts
2026-08-03 21:59:19 +02:00
AgentSeal
2c65764c0b Merge branch 'main' into fix/omp-session-discovery 2026-08-03 21:44:19 +02:00
Aditya Vikram Singh
a4627a1a5a fix(providers): scan bounded leading lines for Pi/OMP session discovery
The shared Pi/OMP discovery gate only checked the first physical line of a
transcript for a `type: "session"` record. Oh My Pi writes a fixed-width
`type: "title"` metadata line before that header (upstream
can1357/oh-my-pi@0ce330a, 2026-06-27), so valid OMP transcripts were rejected
and omitted from `codeburn sessions --provider omp`.

readFirstEntry now scans up to MAX_HEADER_LINES_SCANNED (20) leading lines via
the existing streaming readSessionLines helper, skipping blank lines and
malformed JSON, until it finds a session record. This keeps discovery bounded
for message-only files (and pathological blank/junk-line runs) instead of
reading the whole file, and Pi and OMP continue to share the exact same
discovery path.

Fixes #845
2026-07-29 22:43:01 +05:30
ihearttokyo
a056d22e78
Merge branch 'main' into agent/fix-daily-history-scan 2026-07-27 19:59:08 -04:00
iamtoruk
7fe432094d refactor(codex): buffer a task's calls until its timing is known
Active/tool-wait timing used to be back-patched onto calls that had
already been appended to the result list. Buffer the calls decoded since
the last task_started instead, stamp them in place on task_complete, and
append them at the next task_started or at end of stream, so a task is
only emitted once its timing is final. Single-pass and split decodes then
agree instead of depending on where a decode boundary falls.

Output is unchanged: parsing 827 real rollouts (4.2 GB, 51224 calls) with
and without this change produces byte-identical results. The buffer holds
one task's calls; the deepest observed over that corpus was 924 against a
result list of 8196.

Attribution still spans everything since the last task_started, which
matters when a mid-file session_meta re-arms the fork-replay cutoff and
swallows a task_started while its task_complete lands past the cutoff.
The added test pins that case.
2026-07-27 11:36:21 -07:00
iamtoruk
fad846622f fix(codex): restore discovery fast path, duration precedence, and dashboard width
Review fixes on top of #805.

Discovery: getCachedCodexProject short-circuits again without opening the
file. The PR read the first line of every rollout to collect session_id,
which cost +129ms on an 827-file session directory with a warm cache.
Archived duplicates are now filtered by basename, which needs no file I/O.

Timing: on oversized mcp_tool_call_end lines a `duration_ms` key inside
invocation.arguments outranked the payload-level duration, inflating tool
wait. The depth-aware payload value wins; the naive scan stays as the
fallback for task_complete.

Layout: MIN_WIDE goes back to 90 so 90-129 column terminals keep the
two-column dashboard. The By Model panel drops the Tok/s column (and its
footnote) when the panel has fewer than 61 inner columns or when no model
recorded active timing, so narrow terminals do not truncate and non-Codex
users get no dead column.
2026-07-27 11:36:07 -07:00
Jan Brennenstuhl
36e5ef0a46
fix(omp): discover title-first session transcripts
Co-authored-by: openai/gpt-5.6-terra <noreply@openai.com>
2026-07-27 19:58:50 +02:00
Resham Joshi
264e8e1b50
Merge pull request #800 from getagentseal/fix/611-desktop-msix-sessions
fix(claude): discover Claude Desktop/Cowork sessions in Windows MSIX installs
2026-07-26 06:47:18 -07:00
ihearttokyo
a6bf81f756 feat(codex): add tool-excluded active throughput metrics 2026-07-24 22:53:43 -04:00
EuanTop
dd6147e529 fix(opencode): read session fallback model from real schema 2026-07-24 13:03:22 +08:00
ozymandiashh
d92b9fea43 fix(claude): discover Claude Desktop/Cowork sessions in Windows MSIX installs
The desktop sessions resolver returned a single per-platform path, so
Microsoft Store (MSIX) installs of Claude Desktop were invisible: their
data lives under %LOCALAPPDATA%\Packages\<Claude package>\LocalCache\Roaming\Claude\local-agent-mode-sessions
and a filesystem junction workaround breaks Cowork's own file access
(reported and verified in #611).

getDesktopSessionsDir() becomes getDesktopSessionsDirs(): an ordered,
deduped candidate list (override, then classic APPDATA, then MSIX packages
matching Claude_* or *.Claude_*, existence-checked, lexicographically
sorted; .config on Linux). Results are memoized per env-input tuple so the
parser's per-file classification never rescans Packages. All call sites
scan every candidate; macOS, Linux and classic Windows behavior unchanged.

Fixes #611
2026-07-24 00:30:30 +03:00
reviewer
be5c0c00aa kimicode: discover desktop-runtime sessions and fix menubar visibility
- Resolve k3/k3-agent/k2d6-agent model aliases to canonical Kimi names
- Discover sessions across all Kimi Code homes (CLI + desktop runtime)
- Accept conv-*/ctitle-* session directory naming, not just session_*
- Add Kimi Code provider tab with brand color to the menubar
- Show short model names (Kimi K3, Kimi K2.6) in the menubar payload
2026-07-23 21:31:25 +02:00
Resham Joshi
b30818138e
prerelease: quota NaN guard, 10-year daily retention, test date bomb (#764)
Three review leftovers plus one inconsistency found while fixing them.

computePace returns undefined for a non-finite usedFraction instead of
letting NaN sail through the clamp into every pace field.

Daily-cache retention goes from 730 days to 3650: the cache is the only
durable record of carried days and lifetime now reads from it, so the
2-year prune would have replayed the lost-history bug in slow motion at
that horizon. Measured cost stays small (~2.3 MB / ~11 ms parse per 730
dense days).

The vercel-gateway dashboard-range test pins a relative day instead of
2026-06-01, which would have started failing once the rolling 6-month
window moved past it around December.
2026-07-20 07:37:56 -07:00
ihearttokyo
92b2b5ef39
fix(dashboard): Bound Daily Activity scans (#727)
Reuse one concrete six-month scan for interactive history and selected-period totals. This restores network-backed providers, removes concurrent refresh scans, and keeps non-interactive reports scoped to their requested period.
2026-07-20 05:21:35 -07:00
ozymandiashh
2b95aef7e5 feat(kimicode): Kimi Code CLI provider, parse ~/.kimi-code wire sessions (#747)
Adds a provider for Kimi Code CLI (MoonshotAI/kimi-code, the successor of
kimi-cli), reading ~/.kimi-code/sessions/wd_*/session_*/: state.json for
session metadata and per-agent agents/<id>/wire.jsonl event streams.

- Usage from usage.record events: inputOther->input, output->output,
  inputCacheRead->cacheRead, inputCacheCreation->cacheWrite. The store has
  no cost fields, so cost is computed from tokens and flagged estimated.
- Real model attribution: usage.record.model carries the config ALIAS; the
  real model id lives on llm.request events. Rows resolve the alias through
  the alias->model map first, with the nearest preceding request as
  fallback, so aliases never leak into reports.
- Subagent wire files parse as separate sources under one session without
  double counting; multi-turn continuations in one wire.jsonl are one
  session with multiple turns; retry-only failed sessions parse to zero
  usage; malformed JSONL lines are skipped.
- Tool calls feed the tool breakdown; tool state resets at turn boundaries
  so a failed turn cannot bleed its tools into the next row.
- KIMI_CODE_HOME override, eager registration, PROVIDER_ENV_VARS and
  PROVIDER_PARSE_VERSIONS entries, probeRoots for doctor, docs page.
2026-07-19 02:52:37 +03:00
Resham Joshi
906cb2d550
Merge pull request #735 from ozymandiashh/feat/quickdesk-provider
feat(quickdesk): Amazon Quick Desktop provider (~/.quickwork)
2026-07-18 12:57:08 -07:00
iamtoruk
b71ec34a21 fix(usage): per-provider snapshot capture, placeholder guard, path-id display fallback 2026-07-18 03:37:42 -07:00
ozymandiashh
bdb1475251 feat(quickdesk): Amazon Quick Desktop provider, parse ~/.quickwork sessions and metrics (#707)
Adds an eager provider for Amazon Quick Desktop (aws.amazon.com/quick/desktop),
which stores local data under ~/.quickwork. Usage source of truth is the
per-day EMF metrics JSONL (Model, InputTokens, OutputTokens, CostUSD,
session_id); real CostUSD passes through the cache like kiro/devin/hermes.
sessions.db (SQLite, read-only) enriches calls with titles, first user
message, and tool names, and provides estimated usage (quickdesk-auto,
costIsEstimated) for sessions that predate metrics coverage. Multi-profile
layout via profiles.json entries plus the migrated legacy root; honors
QUICKWORK_HOME; schema-tolerant (sqlite_master and PRAGMA introspection,
per-line JSONL error isolation). The on-disk schema is reverse-engineered and
documented as such in docs/providers/quickdesk.md.
2026-07-18 00:02:01 +03:00
Aditya Vikram Singh
594bc994d0
copilot: extract skill invocations so Skills & Agents populates (#654) (#697)
Co-authored-by: Aditya Vikram Singh <247195684+avs-io@users.noreply.github.com>
2026-07-16 12:02:34 -07:00
Aditya Vikram Singh
35b8f0519e
Extract shared token estimation helper, migrate all CHARS_PER_TOKEN sites (#644) (#677)
Co-authored-by: Aditya Vikram Singh <247195684+avs-io@users.noreply.github.com>
2026-07-16 11:58:22 -07:00
iamtoruk
300e949951 feat(app): refresh cadence, instant switching, cache coherence, telemetry v1
Performance and coherence:
- Settings > General 'Refresh every' (Manual/30s/1m/3m/5m/10m), live via
  RefreshCadenceContext; Manual polls only on demand
- usePolled memoKey LRU: provider/period switches paint the last-good
  result instantly with a switching hairline while refreshing quietly
- quota TTL 5min + honest rate-limited copy on 429 backoff
- version-suffixed cache files (session-cache.v5.json, daily-cache.v12,
  auto-minted on future bumps); legacy files never written or deleted,
  adopted once when versions match: old and new binaries coexist
  without clobbering (field-observed menubar-vs-desktop ping-pong)
- advisory hydration lock: concurrent cold starts share one scan
  (wait-then-read-warm), stale/dead locks self-heal, never a
  correctness gate

Telemetry v1 + onboarding (desktop only, per owner decisions):
- first-launch onboarding (3 feature screens + consent); region-split
  default (EU/EEA/UK/CH off, elsewhere on, unknown off); nothing sends
  before consent completion or while off; dev builds never send
- anonymous install UUID, rotated on opt-out; day-granularity events,
  cost buckets only; whitelisted names; 200-event queue, 5min flush
- Settings > Privacy live toggle replaces the static claim

Zero computed-number changes. App 316/316, root 1805.

Wire contract targets api.codeburn.app/v1/telemetry (Worker follows).
2026-07-16 11:47:05 -07:00
Resham Joshi
98051b4263
fix(copilot): bill CLI input and cache tokens from session.shutdown (#684)
* fix(copilot): bill CLI input and cache tokens from session.shutdown

Copilot CLI sessions (~/.copilot/session-state/*/events.jsonl) recorded
only outputTokens, because the parser read assistant.message events and
ignored the session.shutdown rollup. That rollup is the sole on-disk
source of a CLI session's input, cache-read and cache-write tokens, so
cost was underreported (in the reported case 35k+ cache-read tokens were
dropped entirely).

Handle session.shutdown in the CLI (non-transcript) JSONL path and emit
one supplementary call per model from modelMetrics.<model>.usage. The
per-turn assistant.message events keep owning output tokens, tools and
turn structure; the shutdown call contributes only the input and cache
tokens they lack, with output excluded, so no dimension double-counts and
the combined cost equals the full CLI-measured cost. usage.inputTokens is
cache-inclusive (input + cache_read + cache_write), so the cache
components are subtracted to recover the uncached input calculateCost
expects. These are real counts written by the CLI, so costIsEstimated is
false. Sessions without session.shutdown (crashed or still running) keep
the exact previous output-only behavior, and the VS Code, JetBrains and
OTel paths are untouched.

Bump the copilot entry in PROVIDER_PARSE_VERSIONS so already-cached CLI
sessions re-parse and pick up the corrected tokens and cost.

* fix(copilot): never emit an empty-timestamp shutdown call

A session.shutdown without its own timestamp and without sessionStartTime
produced a call with an empty timestamp, which the date-range filters in
parser.ts drop silently, erasing exactly the tokens this feature bills.
Fall back to the last stamped event in the session.

* fix(cache): durable orphans survive provider fingerprint changes

Bumping a durable provider's PROVIDER_PARSE_VERSIONS changed its env
fingerprint, and getOrCreateProviderSection replaced the whole section on
mismatch. For copilot OTel orphans the discarded cache entry is the only
remaining record of pruned spans, so the first post-upgrade run silently
and permanently erased month-to-date history. Carry forward exactly the
entries whose source no longer exists; present sources still drop and
re-parse under the new fingerprint, which is the point of a bump.

Regression test forges a pre-upgrade fingerprint on a pruned-source
orphan and asserts it stays counted and cached across the transition.
2026-07-16 10:37:58 -07:00
kronos
68ad0be18b
fix(antigravity): stamp mtime fallback at emission, tighten path classification (#612)
* feat(antigravity): add support for Antigravity IDE storage on Windows

CodeBurn previously only detected Antigravity CLI usage (.pb files under
.gemini/antigravity/). Antigravity IDE on Windows stores session state in
VSCode-style storage at %APPDATA%\Antigravity IDE\User\globalStorage\state.vscdb,
which was not detected.

Add support for reading Antigravity IDE sessions from the VSCode-style storage:
- Extend CONVERSATION_ROOTS to include APPDATA Antigravity IDE path
- Refine path classification to properly identify IDE vs CLI sessions
- Handle missing per-call timestamps by stamping file mtime as fallback
- Bump CACHE_VERSION to 4 for cache invalidation

Fixes: CodeBurn reports zero usage when Antigravity IDE is actively used.

* fix(antigravity): stamp mtime fallback at emission, tighten path classification

- Apply the mtime fallback to a copy at emission instead of mutating and
  persisting it into the cache, so a later file rewrite can't retro-date a
  session's history to the new mtime. SQLite gen_metadata rows have no real
  per-call time; the RPC path still uses chatStartMetadata.createdAt.
- Drop the unreachable APPDATA classifier branch (discovery only walks the
  ~/.gemini roots; APPDATA state.vscdb holds no token usage). This also removes
  the misroute of base-antigravity paths under an "Antigravity IDE" profile dir.
- Bump CACHE_VERSION to invalidate caches that persisted a synthesized mtime.

* fix(antigravity): stabilize untimestamped call times across DB rewrites

Extract ChatStartMetadata.created_at from proto-encoded data and decode multiple timestamp formats (ISO string, Timestamp submessage, unix varint). Implement assignStableTimestamps() to preserve first-seen timestamps across file rewrites, preventing retro-dating of sessions when .db files are modified. Make conversation roots dynamic (computed per call) to honor environment overrides in tests. Add comprehensive timestamp stability tests verifying that timestamps remain fixed across file mtime changes while respecting date-range filters.

* test(antigravity): assert today range excludes first-seen timestamps

Adds a test case to verify that the 'today' date range filter correctly excludes sessions based on their first-seen timestamp, not file modification time. This ensures that even if a database file is rewritten with a later mtime, sessions with earlier first-seen dates are properly filtered out.

Refs #411
2026-07-16 07:49:54 -07:00
ozymandiashh
df78e50052
feat: add CodeWhale provider support (#674)
Co-authored-by: AgentSeal <hello@agentseal.org>
2026-07-16 03:12:28 -07:00
ozymandiashh
1e54c693b6
fix(opencode): support custom data dir and db prefix (#620)
Co-authored-by: AgentSeal <hello@agentseal.org>
2026-07-16 02:18:19 -07:00
ozymandiashh
e2d006be9d
fix(claude): support Windows Desktop sessions via APPDATA (#615)
* fix(claude): use APPDATA for Windows Desktop sessions

* test(claude): harden withPlatform mock against cross-file leak

The process.platform mock omitted configurable:true and never restored
the value when the property had no own descriptor, leaving win32 mocked
for later tests on the same Vitest worker. Add configurable + an else
branch that deletes the override to expose the real inherited value.

---------

Co-authored-by: AgentSeal <hello@agentseal.org>
2026-07-16 02:13:11 -07:00
ozymandiashh
e2cba003a8
Include archived Codex sessions in usage reports (#667)
Co-authored-by: zhangshihao03 <zhangshihao03@baijia.com>
2026-07-16 01:55:25 -07:00
Andrew Lee
bdf4f3fe24 kiro: price v1 executions from credits; unify fallback across parsers
v1 modern execution files carry the same metered credits as v2
(usageSummary[].usage, unit "credit" — the predecessor of v2's
promptTurnSummaries), but the parser only harvested usedTools from
that array and priced executions from estimated tokens. Since v2 only
ships in brand-new IDE builds, v1 is the format nearly all Kiro IDE
users are on today. Sum usage across usageSummary entries and price at
the public overage rate.

Align all three credit parsers (CLI, v1, v2) on one fallback contract —
gate on summed credits > 0 and fall back to token-estimated pricing
with costIsEstimated: true:

- CLI turns without metering_usage were priced at a frozen $0
- CLI turns with an EMPTY metering_usage array (75 of 10,105 real turn
  metadatas — meta written before metering lands) passed the truthy
  presence check and froze $0 marked as real cost
- legacy .chat calls now carry costIsEstimated: true, which was always
  the reality but never stated

Validated against a real machine: 221 of 277 v1/legacy calls now carry
metered cost ($20.11) instead of token estimates that had overstated
them ~5x; verified usageSummary values are per-turn amounts, not
cumulative counters (sequences fluctuate). Call counts unchanged before
and after — nothing gained, lost, or double-counted.

Also document the companion-file fingerprint blind spot at
fingerprintFile: kiro CLI credits and v2 modelId live in companion
files the single-file fingerprint never sees, so a parse racing the
companion write can cache fallback values that only self-heal while
the transcript keeps changing.

AI-Origin: ai-generated
AI-Tool: kiro
2026-07-14 22:36:08 +00:00
Andrew Lee
a1f8f4c4ff kiro: add IDE v2 session store support with credit-based pricing
Kiro's new IDE builds write sessions to ~/.kiro/sessions/<hash>/sess_<id>/
(session.json + messages.jsonl event log) instead of globalStorage. Add a
v2 parser and discovery, plus fixes surfaced while validating against a
real upgraded machine:

- Parse v2 event-sourced turns (user/turn_start/assistant/tool_call/
  tool_result/usage_summary/turn_end) with per-execution dedup keys
  (kiro-v2:<session>:<execId>) and defensive flushes for out-of-order
  events and in-progress sessions
- Price turns from real metered credits at the public overage rate
  (USD_PER_KIRO_CREDIT = $0.04/credit, individual plan: $20/mo for
  1,000 credits), falling back to token estimation only when a turn has
  no usage_summary (aborted/in-flight); costIsEstimated distinguishes
  the two
- Fix pre-existing CLI parser bug: metering_usage values are credits,
  not dollars — costs were overstated 25x at the real rate
- Count v2 tool_result content as input context (matching the CLI
  parser's ToolResults treatment); previously only the user prompt was
  counted, undercounting input ~25x on agentic turns
- Keep reasoningTokens disjoint from outputTokens: downstream
  aggregation (models-report, audit-report, parser) sums the two
  fields, so folding reasoning into output would double-count. Combine
  them only for the token-pricing fallback, matching codex/gemini
- Take the real modelId from session.json, so v2 sessions are not
  mislabeled kiro-auto
- Extract parseWorkspaceSession from the inline createParser block for
  parity with the other four format parsers
- Add v2SessionsRootOverride and stop deriving the v2 scan root from
  the cliDir parent when only agentDirOverride is set (tests were
  scanning the system tmpdir)
- Tests: v2 parsing/discovery/dedup/routing, credit vs fallback
  pricing, tool_result turn scoping, reasoning disjointness, cli-dir
  skip guard, and a mixed-format coexistence suite (legacy .chat + v1
  executions + workspace-sessions + CLI + v2 on one machine) asserting
  exact aggregate counts and no double counting
- Docs: v2 store layout, credit pricing, dedup namespaces, and the
  disjoint-store verification (v1->v2 is a clean cutover, no
  migration/dual-write observed)

AI-Origin: human
2026-07-14 22:35:51 +00:00