Commit graph

89 commits

Author SHA1 Message Date
iamtoruk
b2d0f29b6c Merge origin/main into pr992 2026-08-18 08:49:13 -07:00
iamtoruk
562bab65ba docs: surface the Windows menubar
README gains a Windows section next to the macOS and GNOME ones, a download
badge on the menubar card, and an honest note that the Tauri tray builds on
Linux but is unreleased there. docs/architecture.md picks up windows/ in the
surfaces diagram and gets a section covering the crate layout, the PATH and
System32 spawn rules, and the Claude quota parity.

`codeburn menubar` on Windows now points at the windows-v release page instead
of failing with "macOS only". The generalized installer from the source branch
is not brought over: it is 1182 commits behind this file and would drop the
proxy support, retry/backoff, checksum and bundle verification, and persistent
CLI path handling that landed since.
2026-08-18 04:14:05 -07:00
iamtoruk
b249ba1ffe Merge remote-tracking branch 'origin/main' into feat/optimize-recurring-context
# Conflicts:
#	CHANGELOG.md
2026-08-18 03:31:16 -07:00
iamtoruk
f8bc9b2594 docs: cover what happens after optimize --apply
docs/optimize.md gains an "After you apply" section (the four verdicts,
--auto-revert, what is never auto-reverted); README and CHANGELOG follow.
2026-08-18 03:15:12 -07:00
iamtoruk
8d8848d805 optimize: detect recurring context pasted at the start of sessions
Groups sessions by their opening block (whitespace/ANSI-normalized, hashed
over the first 2 KB) and flags a block of at least 1.5 KB that opens five or
more sessions. Class nudge: CodeBurn will not move the user's own text into
CLAUDE.md, so the fix asks Claude to give the block a permanent home. Only
the repeats count as savings, sized from the block's bytes because provider
usage is per API call and cannot isolate the paste. The opener comes from
the session scan that already runs, so nothing extra is read.
2026-08-18 03:13:40 -07:00
iamtoruk
6267c49c25 docs: document optimize classes, provenance, and what --apply writes
Adds docs/optimize.md (what optimize scans, the three classes, the exact
files --apply may touch plus undo, measured vs estimated, the health
grade bands, the --yes CLAUDE.md guardrail), links it from the README
waste section, and corrects the detector count in docs/architecture.md
(14 -> 19).
2026-08-18 02:27:01 -07:00
iamtoruk
d9a9486b6d docs(dsh): finish the provider registration checklist
docs/providers/NEW_PROVIDER.md items the PR had not reached yet, plus the two
surfaces that are functional rather than cosmetic:

- docs/providers/dsh.md and its row in the provider index, documenting the
  storage layout, the JSONL-backend-only scope (the opt-in SQLite persistence
  backend is not read), and that DSH is a developer preview whose format
  version 0 implies no compatibility.
- CHANGELOG entry under Unreleased.
- README provider count 40 -> 41 and a data-locations row.
- app/package.json: $HOME/.dsh in the snap personal-files allowlist, without
  which the Linux snap build cannot read DSH sessions at all.
- UsageDataChangeGuard: the DSH sessions root, without which the menubar never
  notices a new session and does not refresh.
- Bumps the dsh parse version, since the parser's attribution changed.
2026-08-17 10:59:58 -07:00
iamtoruk
5bc78b8e9e perf(parse-workers): gate on pending bytes, size the per-worker budget per parse
Three fixes from review, all measured on this box.

The workload gate was files OR bytes. The files arm is wrong: 250 pending files
holding 117 KB between them spawned 5 threads and ran ~5% SLOWER than serial,
and a file count only starts paying for itself around 400. Gate on bytes alone;
the count still takes max(files / 50, bytes / 200 MB), so a few hundred huge
rollouts keep their threads.

The flat 256 MB per-worker memory budget was contradicted by the Codex workload:
a 260 MB rollout peaks near 430 MB in its worker, linearly across the pool. It is
now derived per parse as clamp(256 MB, 2 x average pending file + 128 MB, 1 GB),
which leaves a corpus of small Claude transcripts where it was and stops
over-subscribing on rollouts. The parent's buffer of up to pool.size finished
results is part of that peak and is named in the comment.

The worker/file pairing at both install sites was positional, guarded only by
position (Claude) or a path membership check (Codex). Each worker now echoes its
path and the parent asserts it, outside the per-file try: a misalignment would
install one session's turns under another's path -- a wrong number nobody would
ever notice -- so it fails the run rather than being swallowed as a parse
failure. On the Claude side that meant hoisting the whole worker-result block
above the try, which is safe because an append never consumes a result in either
its shortcut or its straddled-fallthrough case.
2026-08-17 02:53:19 -07:00
iamtoruk
2d873c2290 docs: describe the Codex half of the parallel cold parse 2026-08-17 02:30:20 -07:00
iamtoruk
ef636472f4 review: pin the discard invariant in comment, test and verbose output
The comment at the install site claimed only that an overlapping worker result
'is discarded'. State why the empty-set result is installable at all — an empty
id intersection is proof a serial parse would have dropped nothing — and why the
tempting shortcut is wrong: parsedTurnsToCachedTurns delta-encodes gitBranch
across turns, so dropping one turn changes whether a LATER turn carries a
gitBranch key. Overlap discards the whole file, never individual turns.

Tests: the end-to-end determinism check now runs both parses over the SAME
corpus, so cache shard BODIES are compared byte for byte instead of just their
keys, and a new resumed-session fixture (a transcript restating another file's
message ids, in both filename orders) makes install order decide the answer.
Verified by mutation: removing the discard guard fails it, and yielding worker
results out of order fails it.

CODEBURN_VERBOSE now reports how many worker results were re-parsed in-process
on id overlap, which is what the new test asserts on. The worker bundle's source
map is excluded from the published package (-1.8 MB).
2026-08-17 01:43:51 -07:00
iamtoruk
b99744bf93 fix(parser): gate parse workers on available memory, not free memory
os.freemem() reports free pages on macOS, not available memory: on an idle
128 GB machine it reads a few hundred MB, so the 2 GB gate switched the worker
pool on and off between runs on the platform the desktop app ships to. The gate
and the budget now use process.availableMemory() (cgroup/rlimit-aware in a
container), falling back to os.totalmem(): serial under 4 GB available, budget
min(0.25 * available, 2 GB). An 8 GB box earns 8 threads, a 4 GB box none.

The verbose line now carries every decision input — cores, available GB, pending
files and bytes — on both the gate and the go path, so one support log explains
itself.
2026-08-17 01:23:42 -07:00
iamtoruk
da68055c9e docs: document the parallel cold parse and CODEBURN_PARSE_WORKERS 2026-08-17 01:19:25 -07:00
iamtoruk
c7c3a878d8 fix(parser): flatten already-sliced previews and memoize canonical path walks
flatSlice returned strings within the bound unchanged, but provider adapters
pre-truncate with .slice(0, 500) before the cache site, so those views still
pinned their parent buffers. Always flatten; the round-trip is ~150ns per turn.
Use utf16le so lone surrogates survive the copy.

Cache the canonical-path Promise instead of the resolved value so calls in one
Promise.all batch share a single walk.

Document the one-time kiro re-parse and worktree regrouping.
2026-08-16 01:49:10 -07:00
Aditya Vikram Singh
d841ea59d7 fix: retain discovered durable history 2026-08-13 20:44:11 +05:30
Resham Joshi
7b6757504f
Merge pull request #902 from ozymandiashh/chore/contributor-hygiene
chore: pin node via .nvmrc, add the new-provider checklist
2026-08-10 04:24:53 -07:00
Resham Joshi
3bf80c1e51
Merge pull request #949 from getagentseal/feat/openclaude-surfaces
feat(menubar): surface OpenClaude in the menubar and docs indexes
2026-08-10 04:15:27 -07:00
Resham Joshi
a6446d94d8
Merge pull request #948 from therickfactr/fix/root-test-script-scope
Scope the root test script to tests/ so npm test runs from a clean install
2026-08-10 04:02:42 -07:00
iamtoruk
4e9c3771f0 feat(menubar): OpenClaude provider tab, change-guard watch root, docs index rows 2026-08-10 02:53:05 -07:00
Rick Culpepper (claude)
4ca2d4824b
Scope the root test script to tests/ so npm test runs from a clean install
vitest's default glob reached the Electron app's specs under app/, which carry their
own vitest config and their own jsdom in app/node_modules. From a root install that
fails with ERR_MODULE_NOT_FOUND: jsdom, so the command CONTRIBUTING documents and the
one RELEASING.md names as the pre-release gate both error out.

Move the scoping CI already applies into package.json: test runs tests/ minus the
parallelism-sensitive cache-refresh-lock suites, test:locks runs those three serially,
test:watch keeps watch mode at the same scope. The first two are byte-identical to the
invocations .github/workflows/tests.yml spells out, so the workflow can be pointed at
the scripts to stop the two drifting apart again; that edit is left out of this PR so it
needs no workflow permissions. test plus test:locks together still cover all 192 files
under tests/.

Scoping the script changes what a trailing path argument means: vitest ORs positional
filters, so 'npm test -- tests/providers/hermes.test.ts' would no longer narrow to that
file, it would run the whole suite. Rewrite those to 'npx vitest run <path>' everywhere
they appear - four provider guides and the MCP design plan, thirteen lines in all.

Also refresh the stale test docs: 42 files/568 tests (now 192 under tests/), the
per-directory counts, the line claiming vitest does not run in CI which stopped being
true when tests.yml landed, and the provider test-gap list, which still named
antigravity and gemini after both gained test files.

Record the cache-refresh-lock naming convention in CONTRIBUTING, since the split makes
it load-bearing: a lock test that misses the prefix runs under the full worker pool and
flakes, and one that matches it but is absent from test:locks never runs at all.
2026-08-09 10:19:58 -05: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
ozymandiashh
41deb44b06 chore: pin node via .nvmrc, add the new-provider checklist
.nvmrc matches the engines floor and the appx pin (22.13.0), taming the
package-lock churn from contributors on drifting node/npm versions. The
checklist distills the house rules new-provider PRs keep relearning:
product split, cache-key coupling, reported-cost presence semantics,
defensive parsing, probeRoots for doctor, and the real-local-testing bar.
2026-08-04 03:17:41 +03: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
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
Andrew Lee
50c8251719 fix(sync): close credential-leak paths; session retraction; span/key/CLI hardening
Review rounds 2-3 + self-review on --attribution:

Credential egress (round 2):
- normalizeRemoteUrl: scp userinfo expressed as an optional regex group
  let backtracking re-parse a credential prefix as host:path
  (x-access-token:ghp_...@host/repo -> token in git.repo). Userinfo is
  now split off at the first @ BEFORE any host matching.
- Positive validation (allow-list) as the final gate on EVERY branch:
  host must be hostname-shaped, every path segment repo-shaped, total
  identity <= 200 chars. Kills transport-helper remotes (ext:: leaks
  local SSH key paths, codecommit:: leaks AWS profile names), residual
  @, spaces/colons, and unbounded strings.
- sanitizePrLinks: links are rebuilt from origin + pathname — userinfo,
  query strings, and fragments are dropped instead of passed through;
  collapsed duplicates dedupe.

Attribution correctness (round 3 + self-review):
- Double-count fix with precise retraction semantics: when a commit
  migrates to a later-parsed tighter-window session, the loser re-emits
  git.commit_count=0. Empty records are emitted ONLY on a true loss in
  THIS computation (lostCandidacy) — a commit that merely aged out of
  the --since range was lost to nobody, and retracting it would
  permanently zero a still-correct server-side count. The sync layer
  additionally requires a prior ledgered state for the session.
- Session dedup key includes project + both window timestamps, so
  ongoing sessions re-emit with corrected span times.
- Span end times clamped like the usage builder (never 0, never
  earlier than start + 1ms).
- CLI mirrors the usage path on attribution push failures instead of
  claiming success.
- Identity normalization: case-insensitive .git strip, doubled path
  slashes collapse.

AI-Origin: human
2026-08-02 12:56:59 +00:00
Andrew Lee
ccee28ae82 fix(sync): address attribution review — cwd-fallback egress, Windows paths, PR-link validation
Review findings on the --attribution PR:

- Privacy: sessions whose project path no longer resolves inherited the
  cwd-fallback repo identity, egressing whatever (possibly confidential)
  repo the user pushes from and falsely attributing its commits.
  buildRepoGroups now tracks per-session identity provenance; the
  attribution path excludes fallback sessions from commit attribution
  entirely (no repo, no commits, PR links only) — they also can no
  longer steal a commit from a genuine session's window.
- Privacy: Windows drive-letter paths (C:/..., C:\..., drive-relative)
  parsed as scp-like remotes, emitting local filesystem paths as repo
  identities. normalizeRemoteUrl rejects drive letters and
  single-character hosts (dotless intranet hosts still accepted).
- Hardening: PR links are shape-checked before sending (https,
  /org/repo/pull/N path, <=256 chars, max 20 per session) — upstream
  parsers only truthiness-check them.
- Safety valve: MAX_ATTRIBUTION_PER_PUSH (10k) caps a first
  --since all --attribution push; dry-run reports the cap.
- Tests: adversarial normalize corpus, cwd-fallback egress repro,
  commit-stealing prevention, PR-link sanitization, and CLI-level tests
  (mock IdP + collector): dry-run sends nothing to the traces endpoint,
  flag-off emits no attribution span names on the wire.
- Docs: reconciled the 'never sent' wording with reality (PR links ride
  even when repo is null; device_id/methodology/timestamps disclosed).
  CHANGELOG Unreleased entry added.

AI-Origin: human
2026-08-02 12:29:18 +00:00
Andrew Lee
1bf7206842 feat(sync): push git attribution spans with --attribution
Expose the yield session-to-commit correlation through codeburn sync so
backends can join AI usage to git activity without local git hooks.

- yield: export normalizeRemoteUrl (host/org/repo; credentials, ports,
  and .git stripped) and computeAttributionRecords, which reuses the
  exact repo-grouping + tightest-window attribution from computeYield
  (extracted into a shared buildRepoGroups) and joins in the normalized
  origin remote and session prLinks.
- otlp: two new span types sharing the session traceId —
  codeburn.session.attribution (git.repo, git.pr_links, git.commit_count)
  and codeburn.commit (git.sha, git.in_main, git.was_reverted). Resource
  attribute codeburn.attribution_methodology=timestamp-window marks the
  attribution as inferred.
- push: generic send core reused by usage and attribution batches. Dedup
  keys encode mutable state (inMain/wasReverted), so a state transition
  re-sends the updated fact while identical states dedupe via the
  existing sent-ledger.
- cli: opt-in --attribution flag on sync push (dry-run aware); commits
  in repos with no network remote are never sent.

AI-Origin: human
2026-08-02 12:28:13 +00:00
Richard Boisvert
3e400bffa7
feat(codex): show the credit limit on credit-metered ChatGPT workspaces
Signed-off-by: Richard Boisvert <rboisvert@devolutions.net>
2026-07-27 08:19:59 -04: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
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
ozymandiashh
0232595847 docs(kimicode): subagent accounting verified at kimi-code source, no double count 2026-07-19 03:11:29 +03: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
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
Resham Joshi
b7e3bac263
Merge pull request #679 from getagentseal/feat/desktop-app
CodeBurn Desktop: standalone Electron app
2026-07-16 07:54:05 -07:00
iamtoruk
1b600d1c82 Merge origin/main into feat/desktop-app
Resolves menubar-json conflicts: main's granular-history timeline
param unions cleanly with this branch's providerDetails and currency
payload additions; both new tests kept.
2026-07-16 07:52:28 -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
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
7dcd618898 kiro: preserve credit-based cost through the session cache
providerCallToCachedCall drops provider-computed costUSD unless the
provider is allowlisted, and cachedCallToApiCall then re-prices from
token counts. Kiro's cost now comes from metered credits, which token
pricing cannot reproduce — reports were understating real kiro spend
~18x (e.g. $130.72 of metered CLI credits reported as $7.20 of token
estimates). Add kiro to the pass-through allowlist, same as the other
platform-billed providers (mistral-vibe, devin, hermes, ...).

Both persistence layers froze pre-fix dollar values and neither
self-invalidates (historical session files never change), so bump:

- session cache: CACHE_VERSION 4 -> 5 (cached kiro entries carry
  costUSD: undefined and would keep being token re-priced forever)
- daily cache: DAILY_CACHE_VERSION 10 -> 11 with MIN_SUPPORTED_VERSION
  raised (finalized days carry token-estimated kiro costs off by up to
  16x per model), same as the v10 cursor precedent

Test: end-to-end through parseAllSessions — a CLI fixture with 2.5
metered credits must report $0.10; fails with the token re-price
($0.000048) when the allowlist entry is removed.

Docs: document the pricing contract in docs/providers/kiro.md — metered
credit cost is frozen at parse time, so price-override / model-alias
do not affect kiro dollar amounts (shared tradeoff of all allowlisted
providers).

AI-Origin: ai-generated
AI-Tool: kiro
2026-07-14 22:35:51 +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
Andrew Lee
cec61e3b30 docs(sync): user guide, developer/protocol reference, README section
- docs/sync/README.md: setup, push, status, logout, reset; privacy
  guarantees; FAQ
- docs/sync/DEVELOPER.md: architecture, discovery/OTLP protocol,
  sent-ledger design rationale, partial-success and rate-limiting
  semantics, server contract, testing guide
- README.md: sync command group listed under Commands (preview label)

AI-Origin: human
2026-07-12 16:05:34 +00:00
iamtoruk
1f3fc0184b docs(desktop): implementation plan (T0 scaffold, T1 emitters, 6 sections, integration)
Dependency-ordered plan for the standalone Electron app. Corrects spec data
gaps: yield/plan already emit JSON; only spend flow-json (Sankey matrix) and
devices/share --format json are new. Subagent-driven execution: Opus 4.8 +
Codex 5.6-high implement, Fable reviews each task.
2026-07-10 14:49:20 -07:00
iamtoruk
82f716d335 docs(desktop): design spec + wireframes for standalone CodeBurn Desktop app
Standalone Electron app rendering the v6 "indigo instrument" wireframes,
fed by the codeburn CLI via the menubar JSON contract (spawn codeburn --json,
decode, poll). Six sections: Overview, Spend, Optimize, Models, Plans,
Settings/Devices. Data aggregation stays CLI-side; renderer never fakes data.
2026-07-10 14:28:16 -07:00
ZacharyHu0
a485087b5b Add LingTai TUI provider support 2026-07-09 23:47:59 +02:00
Nihal Jain
cd07707363 fix(copilot): parse JetBrains agent sessions from old plugin format (≤1.5.x)
JetBrains Copilot plugin ≤1.5.x (e.g. 1.5.59-243) stores all session turns
inside ONE large binary-framed outer Nitrite document, rather than the
per-turn {"__first__":{"type":"Subgraph",...}} blobs introduced in later
plugins (≥1.12.x, e.g. 1.12.1-251).

In the old format each assistant turn is a UUID-keyed Value entry whose
value field contains a JSON-string-escaped AgentRound record:

  {"<uuid>":{"type":"Value","value":"{\"type\":\"AgentRound\",
    \"data\":\"{...reply...}\"}"}, ...}

The extractResponseText depth-unescape loop already handles this one extra
level of escaping; the only gap was that extractJetBrainsDbTurns never fed
it the outer document — it only scanned for __first__/Subgraph blobs, which
the old plugin never writes.

Add a fallback that activates when the Subgraph scan produces zero turns but
'AgentRound' text is present in the raw file (old-format signal). It locates
the binary-framed outer document (UUID-keyed Value entry, hex matched
case-insensitively so an uppercase UUID does not fall through to $0), extracts
it with matchJsonObject, and passes it to extractResponseText. Because the outer
document holds every turn in one blob, this emits ONE session-level call per
document (all rounds' replies joined): cost/tokens are correct, only the
per-turn call-count granularity is coarser — an accepted tradeoff for legacy
data. MVStore keeps two identical collection copies; seenReplies dedupes them.

The fallback is guarded by turns.length === 0 so new-format sessions (whose
Subgraph scan succeeds) are completely unaffected and never double-counted.

Tests: old-format doc with multiple AgentRound rounds → 1 call whose token
count equals the two non-empty replies joined (the empty tool-call round is
excluded); an uppercase-UUID variant (fails without the case-insensitive
match); and a guard that new-format Subgraph turns are not double-counted.
docs/providers/copilot.md documents the old format and the one-call-per-session
limitation.
2026-07-03 18:21:08 +05:30
Nihal Jain
2916cd988e fix(copilot): read JetBrains agent-mode replies from AgentRound records
JetBrains Copilot has two turn shapes in the Nitrite .db:

- ask mode — the reply is a `Markdown` record's `text`;
- agent / plan mode (e.g. PyCharm agent sessions, `/plan …`) — the reply is the
  `reply` field of an `AgentRound` record, and the `Markdown` record instead
  holds the USER's prompt.

extractResponseText only read Markdown, so agent-mode turns yielded no reply
text: they were discovered (session/turn counts showed up) but priced at $0
because output tokens came out zero. On this machine that silently
under-counted a PyCharm session ($0 → $0.35) and several IntelliJ agent turns.

Determine the mode by the PRESENCE of an `AgentRound` record and read only that
record's `reply` (collecting every non-empty round in a multi-round blob).
Crucially, an agent blob whose reply is empty — a failed turn or a pure
tool-call round — does NOT fall back to the Markdown record, so a user prompt
is never mistaken for the assistant's output; such turns bill $0 as before.
Ask-mode blobs (no AgentRound) keep reading Markdown. Plan mode's sidecar
records — Thinking, PendingChanges (proposed diff, under `content`), AskQuestion,
Notification, SubTurn, and file-read `text` results — are never read as output.
Verified across all local stores: the two reply shapes never coexist in one
blob, so the split is unambiguous.

Tests: agent-mode reply extraction (ignoring the prompt Markdown), pure
tool-call rounds → $0, multi-round collection, and a failed agent turn → $0.
docs/providers/copilot.md documents both turn shapes and the ignored sidecar
records.
2026-07-03 17:06:14 +05:30