Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,22 @@
- `codeburn sync push --attribution` (opt-in): sends git attribution spans — the session→commit correlation from `codeburn yield` (`codeburn.session.attribution` and `codeburn.commit` span types with normalized repo remote, commit SHAs, merged/reverted state, and PR links). Nothing new is sent without the flag; local-only repos and Windows filesystem paths are never emitted as repo identities, and sessions whose project path no longer resolves never inherit the push-time working directory's repo. See docs/sync/README.md "Git attribution".

### Changed (@codeburn/core — breaking; version bump deferred to the next release, which must take at least a minor under 0.x)
- **The `model` field is bounded at the observation boundary.** `model` (and the optional `pricingModel`) on a `CallObservation` must now match the identifier charset `[A-Za-z0-9._:/@-]`, max 128 chars, in both the zod schema and the published `schemas/observation-0.2.0.json`. Every provider's `toObservations` normalizes through `normalizeModelIdentifier`, so a provider display name — Antigravity's `"Gemini 3.5 Flash (High)"`, Warp's and Devin's display strings — surfaces as `unknown` in an emitted observation rather than rejecting the whole envelope. **CLI output is unaffected:** normalization happens only at the observation boundary, so terminal, dashboard, menubar, and desktop numbers and model labels are byte-identical to before. The bound is a tightening of `observation-0.2.0` in place; an ARCHIVED pre-hardening 0.2.0 envelope whose `model` held a display name now fails validation against that same version string and must be re-normalized before re-validating.
- **The `model` field is bounded at the observation boundary.** `model` (and the optional `pricingModel`) on a `CallObservation` must now match the identifier charset `[A-Za-z0-9._:/@-]`, max 128 chars, in both the zod schema and the new `schemas/observation-0.3.0.json`. Every provider's `toObservations` normalizes through `normalizeModelIdentifier`, so a provider display name — Antigravity's `"Gemini 3.5 Flash (High)"`, Warp's and Devin's display strings — surfaces as `unknown` in an emitted observation rather than rejecting the whole envelope. **CLI output is unaffected:** normalization happens only at the observation boundary, so terminal, dashboard, menubar, and desktop numbers and model labels are byte-identical to before. The published `observation-0.2.0` artifact remains byte-for-byte compatible with `@codeburn/core@0.9.20`, so archived envelopes continue validating against their declared version.
- **The diagnostics API changed shape.** `DIAGNOSTIC_DETAIL_MAX` is deleted, `sanitizeDetail(value)` becomes `sanitizeDetail(value, key)` and returns a 16-hex keyed fingerprint instead of a truncated message, `DiagnosticDetail` now accepts only that fingerprint, `RecordOutcome.diagnostics` may no longer carry a `detail`, and `isolateRecords` takes an optional `privacyKey`. A diagnostic now carries a record index, a controlled code, and a keyed fingerprint — never content. The previous rule (strip path separators, cap at 200 chars) let any slash-free path, command fragment, prompt line, or API key through verbatim. Callers with no key omit the field entirely; core never degrades to an unkeyed digest.
- **`decodeCopilot` throws on an empty `privacyKey`** when the record is a JetBrains store. That arm derives its per-turn dedup key with `createHmac(privacyKey, replyText)`, and `createHmac` accepts `''` silently — returning a digest anyone can recompute from the reply text, which is the exact weakness the HMAC replaced. It now refuses, matching every other keyed primitive in the package (`fingerprint.ts`, `sanitizeDetail`). A host calling `decodeCopilot` with `context.privacyKey: ''` used to get a (weak) result and now gets an error; supply a real per-install key.
- **`decodeCopilot` throws on an empty `privacyKey`** when the record is a JetBrains store. That arm derives its public per-turn dedup key with an HMAC over a stable local content identity, and `createHmac` accepts `''` silently — returning a digest anyone can recompute, which is the exact weakness the HMAC replaced. It now refuses, matching every other keyed primitive in the package (`fingerprint.ts`, `sanitizeDetail`). A host calling `decodeCopilot` with `context.privacyKey: ''` used to get a (weak) result and now gets an error; supply a real per-install key.
- **`SOURCE_REF_KEYED_PARSE_VERSION` is no longer exported** from the CLI's session-cache module. It was a marker token that `computeEnvFingerprint` substring-matched to decide which providers re-parse when the host privacy key changes, and that selector silently excluded copilot — whose dedup keys are equally key-derived but whose parse version names no source ref. It is replaced by the explicit `KEY_DERIVED_PROVIDERS` set.

### Fixed (privacy)
- **Dedup keys no longer carry raw host paths.** `dedupKey` ships on the observation envelope, and the codebuff, zerostack, pi, omp, grok and LingTai TUI decoders were folding the absolute source path (a chat directory, a session file) straight into it. They now fold a keyed HMAC fingerprint of that path (`sourceRefFingerprint`, new `source` domain), and the CLI bridge threads the real per-install privacy key into the rich decode instead of an empty one. (#933, thanks @ozymandiashh)
- **Copilot's JetBrains dedup key is keyed.** The per-turn digest was an unkeyed SHA-256 of the assistant's reply text — dictionary-attackable for short replies ("OK", "Done.") — and it crosses into the envelope and the CLI ledger. It is now an HMAC under the host privacy key. (#935, thanks @ozymandiashh)
- Both changes alter dedup-key VALUES, so the affected providers carry a parse-version bump (`source-ref-fingerprint-v1`; copilot `…-dedup-key-hmac-v1`) that forces one re-parse and drops the old-shape keys from the warm session cache instead of re-ingesting the same records under two shapes. The six fingerprint providers additionally fold a digest of the privacy key into their env fingerprint, so a lost or rotated key also forces a clean re-parse. No totals change: no call is added, dropped, or repriced, and the daily cache needs no bump.
- **Dedup keys no longer carry raw host paths.** `dedupKey` ships on the observation envelope, and the codebuff, zerostack, pi, omp, grok and LingTai TUI decoders were folding the absolute source path (a chat directory, a session file) straight into it. They now fold a keyed HMAC fingerprint of that path (`sourceRefFingerprint`, new `source` domain), and the CLI bridge threads the real per-install privacy key into the rich decode instead of an empty one. The former raw-path keys remain non-enumerable, host-only ledger aliases for seven-day `sync push` continuity; they are deliberately persisted in the local session cache but never enter observations or OTLP payloads. (#933, thanks @ozymandiashh)
- **Copilot's JetBrains dedup key is keyed without sacrificing released durable history.** The public per-turn key is now an HMAC under the host privacy key, while the released SHA-based value survives only as a local cache identity. That split lets the CLI re-key a conversation even after JetBrains has pruned its reply from the still-existing DB. The v8 session cache adopts and unions released durable Copilot history from v5/v6/v7, force-reparses present DBs, replaces matching current records, and retains cached-only records; privacy-key rotation therefore neither drops nor duplicates released calls. Former public keys remain local, non-enumerable ledger aliases so `sync push` does not resend an already-accepted call. A failed or unreadable forced parse keeps the prior entry and a per-file retry obligation. The unreleased #1074 HMAC-v1 development cache has no recoverable link between a DB-pruned keyed row and its released SHA identity: for the exact same-key fingerprint, v8 keeps an overlapping released-cache path authoritative (avoiding deterministic duplicates, but potentially omitting an interim-only pruned row), re-parses the current DB, and preserves v7-only paths. A key-rotated interim cache is intentionally not guessed from an opaque fingerprint. (#935 and follow-up to #1074, thanks @ozymandiashh)
- Both privacy changes alter dedup-key values, so the affected providers carry a parse-version bump (`source-ref-fingerprint-v1-ledger-alias-v1`; Copilot `…-dedup-key-hmac-v2`). The alias suffix also invalidates caches already written by #1074, whose HMAC keys were correct but lacked sent-ledger continuity. The six source-fingerprint providers cleanly re-parse. Copilot migrates in place because discarding a durable DB entry would erase rows already pruned from that DB. Released-cache local totals do not change: no call is added, dropped, or repriced, including cached-only Copilot history.

### Changed (CLI)
- **`codeburn sync` re-keys device, span, and trace identity.** Device, span, and trace ids are now domain-separated HMAC digests under the per-install privacy key rather than bare hashes. This is a one-time identity re-key: a sync backend sees a new device identity after upgrade, and spans pushed before the upgrade won't correlate with spans pushed after it. `sync push` also now hard-aborts — instead of degrading to an ephemeral, unstable key — when the config dir is unwritable or the on-disk privacy-key file is corrupt.

### Fixed
- Claude Desktop and Cowork sessions are discovered for Windows Microsoft Store (MSIX) installs. (#611)
- **Nine providers served silently stale numbers after you pointed their env override at a different profile or root.** Kiro, Grok, Kimi, Mux, Mistral Vibe, Zerostack, Codebuff, Goose and Crush each honor an env var that relocates where discovery looks, but the var was never declared in the provider env fingerprint, so the cache section survived the change and kept reporting sessions parsed from the old root — with no diagnostic anywhere. The fix declares those vars, the adjacent OS-set path variables that resolve a discovery root for Claude, IBM Bob, Open Design and Kilo Code on Windows and Linux, Cursor's parse-budget override, and the Vercel AI Gateway credential — which must invalidate the fingerprint because a read-only refresh serves the cached report and would otherwise keep reporting the previous account's usage after a swap. Your next run re-parses the fourteen file-backed providers whose declarations changed — the nine above plus Claude, Cursor, Open Design, IBM Bob and Kilo Code — once, and only once; the Vercel AI Gateway declaration is a read-only-path correction, not a migration (its report is re-fetched on every writable run anyway); Copilot is deliberately NOT included, because declaring its overrides would force a re-parse that can drop OTel history only the cache still holds; `codeburn doctor` names deliberate overrides including the XDG_* vars, never the Windows ambient APPDATA / LOCALAPPDATA, and redacts credential values. (#920)
- **Nine providers served silently stale numbers after you pointed their env override at a different profile or root.** Kiro, Grok, Kimi, Mux, Mistral Vibe, Zerostack, Codebuff, Goose and Crush each honor an env var that relocates where discovery looks, but the var was never declared in the provider env fingerprint, so the cache section survived the change and kept reporting sessions parsed from the old root — with no diagnostic anywhere. The fix declares those vars, the adjacent OS-set path variables that resolve a discovery root for Claude, IBM Bob, Open Design and Kilo Code on Windows and Linux, Cursor's parse-budget override, and the Vercel AI Gateway credential — which must invalidate the fingerprint because a read-only refresh serves the cached report and would otherwise keep reporting the previous account's usage after a swap. Your next run re-parses the fourteen file-backed providers whose declarations changed — the nine above plus Claude, Cursor, Open Design, IBM Bob and Kilo Code — once, and only once; the Vercel AI Gateway declaration is a read-only-path correction, not a migration (its report is re-fetched on every writable run anyway); Copilot is deliberately NOT included because a discovery-root/account switch needs provider-specific namespace semantics — blindly carrying its durable section would combine old-root and new-root histories; `codeburn doctor` names deliberate overrides including the XDG_* vars, never the Windows ambient APPDATA / LOCALAPPDATA, and redacts credential values. (#920)
- Sessions that were silently invisible now appear: Pi and Oh My Pi transcripts with an OMP title slot, Cline sessions under Code - Insiders or VSCodium roots, and OpenCode/kilo-code usage that silently read as zero now reports. (#930)
- The build-folder-reads finding is no longer dropped for repos whose junk reads live only under `vendor/`, `site-packages/`, `out/` or `target/` (Go, PHP, Python, Rust, and Java/Next). Core's detector already counted those reads — only the host's display and trend derivation used a narrower directory list, so when the window had recent activity the recent count stayed zero, the trend computed as 'resolved', and the finding vanished entirely. In mixed repos it survived but quoted core's total while listing directories from the host's narrower counts, so the numbers disagreed. Junk-read and duplicate-read detection now ask core for the offending segment, so the count, the directory list and the trend share one vocabulary; the count itself is unchanged.
- **Kiro chat sessions no longer under-report input.** The token estimate for chat-file sessions (legacy `.chat` and chat-shaped IDE files) now counts every human turn's full text instead of only the last 500 characters, so multi-turn sessions and long prompts stop under-reporting input (and cost) severalfold. The estimate still counts human turns only — tool and system content, and resent context, are not included — and the summary still shows the last prompt, capped at 500 chars. Already-cached sessions re-parse once so the corrected numbers replace the old ones, and the daily cache bumps to v26 with this fix (the next free number after #930's v25), so finalized day totals re-derive too — for days whose kiro chat files still exist: since the v14 NEVER-LOSE carry-forward, a bump re-derives what sources survive and carries every sourceless (day, provider) slice forward with its old values, so a day whose kiro chat files have since been deleted keeps the pre-fix cost after the bump (minor in practice, since kiro IDE sources usually persist, but a carry-forward, not a refresh). (#909)
Expand Down
Loading
Loading