Commit graph

13 commits

Author SHA1 Message Date
iamtoruk
4fa16a2293 fix(dsh): correct usage attribution against the real session format
Reviewed src/providers/dsh.ts against deepseek-harness @ 99f6f02f and fixed
what the format says but the parser did not:

- A forked session's log replays its parent's events verbatim, and codeburn
  parses the parent's own log as its own session, so every inherited call was
  billed twice. The header's parentSession + seedLength mark that prefix;
  events with seq < seedLength are now skipped.
- The model now comes from the reporting assistant/message's own
  message.source, which is what actually served the step. request/header only
  describes the request DSH was about to make, and is the fallback.
- user/message also carries agent-injected context (runtime snapshots, skill
  bodies) under source.kind 'plugin'; only a typed prompt becomes the preview,
  and it is bounded to 500 chars like every other provider rather than holding
  a whole injected system prompt per turn.
- A log stamped with a session format version other than 0 is skipped with a
  notice. The format is pinned at 0 upstream with no compatibility implied, so
  reading a bumped format under today's assumptions would report confident
  wrong numbers.
- Timestamps go through the seconds-vs-milliseconds guard and fall back to the
  header createdAt, so a call can no longer carry an empty timestamp and land
  in the undated cache shard.
- The compressed read buffers the whole log to scan its frames, so it now takes
  the same oversize guard readSessionFile applies to the uncompressed variant.
- The zstd-unavailable notice fired once per session log; each distinct notice
  is now emitted once.
- Emit workingDirectory beside projectPath, as codex does.

Tests add the upstream examples/acp-agent snapshot as a fixture, covering the
real record shapes: packed reasoning-chunks/tool-call-chunks storage rows, a
plugin-injected user/message beside the typed one, and both the streamed usage
chunk and the final assistant/message usage for the same step. Plus the same
snapshot re-encoded as multi-frame zstd with a torn tail (identical output), a
forked session, an unsupported format version, and unparsable lines.
2026-08-17 10:59:52 -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
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
Aditya Vikram Singh
d5144593f3 fix(cache): recover a corrupt session-refresh lock instead of freezing ingestion
observe() classified a stable unparseable session-refresh.lock body as the
terminal 'unavailable'. parseAllSessions routes that to a read-only parse, so a
zero-byte or truncated lock froze warm-cache ingestion permanently across every
later run while each command still exited successfully.

A corrupt body is now a recoverable observation carrying a real mtime, and is
recovered only through the UNMODIFIED staleness gate — tryTakeover and the age
check are byte-identical to main. sameObservation gains an explicit null/non-null
boundary and a sha1 of the raw bytes, because two corrupt bodies have no tokens
to compare and mtime granularity is coarse on some filesystems.

The heartbeat deliberately does NOT rewrite a body it cannot prove is its own.
An owner that cannot prove ownership ends its ownership: mtime stops advancing,
the publication fence refuses, and a successor recovers the lock one staleMs
later. Losing that parse is the price of never having two owners.
2026-07-30 13:06:25 +05:30
Aditya Vikram Singh
e27fe36bd5
cache: strict cross-process gate for the warm session-cache refresh transaction (#743)
* cache: strict cross-process gate for the warm session-cache refresh transaction (#645)

* lock: serialize the fence against the owner's own heartbeat

verifyStillOwner and the heartbeat tick both take the takeover guard;
without in-process serialization the fence could observe its own
heartbeat's guard file, read it as displacement, and abort a legitimate
publication. Fail-safe, but it discarded the parse the lock exists to
protect (~6% of verifies at a 1ms heartbeat in the repro). Owner-side
guard operations now run through one serializer; cross-process guard
semantics are unchanged. Regression test at heartbeatMs 1, mutation-
verified against the unserialized code.

---------

Co-authored-by: Aditya Vikram Singh <247195684+avs-io@users.noreply.github.com>
Co-authored-by: reviewer <review@local>
2026-07-20 09:06:35 -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
Andrew Lee
d8a7b2a95f fix(sync): address PR review — https enforcement, keychain test isolation, golden ID pins
Before-merge items:
1. HTTPS enforced on every remote endpoint (RFC 8252 §8.3): baseUrl,
   issuer, authorization/token/revocation endpoints, and the traces
   endpoint all reject non-https, with a loopback (127.0.0.1/::1/
   localhost) exception for offline tests and local dev. Enforcement is
   central (assertHttps) — the browser-open guard is no longer the only
   check whose failure was swallowed.
2. Credential store test isolation: CODEBURN_SYNC_TOKEN_STORE=file
   forces the file store (honors HOME) so the offline suite never
   touches the real macOS login keychain. Set in the e2e suite.
3. Golden pins for deriveSpanId/deriveTraceId/deriveDeviceId with fixed
   inputs and expected hex — the idempotency contract depends on these
   encodings being stable across releases; a green-tests encoding change
   would silently double-count history on span-ID-keyed backends.
   getDeviceId refactored over a pure deriveDeviceId(host, user).

Smaller review items:
- Callback server: ready promise resolves the actually-bound port from
  the listening event (kills the 100ms-sleep race after port fallback);
  Connection: close on all responses + closeAllConnections() on
  shutdown (pooled keep-alive sockets from a closed server could
  swallow requests aimed at a later server on the same port); error
  handler guarded so a post-bind error can never rebind to a different
  port than advertised; optional ports param ([0] = ephemeral) removes
  fixed-port contention between parallel test workers.
- fetchOidcConfig verifies the issuer claim matches the fetch origin
  (OIDC Discovery §4.3 mix-up defense).
- partialSuccess.rejectedSpans wrapped in Number() — proto3 int64 JSON
  mapping sends strings from strict protojson servers; += would
  concatenate.
- Ledger writes are atomic (temp + rename); corrupt-ledger recovery and
  no-tmp-left-behind tests added; XDG_CACHE_HOME honored (ledger is
  reconstructible state, not config).
- Mock IdP now implements /oauth2/authorize (registers PKCE challenge,
  302s to redirect_uri) and verifies S256 code_verifier + single-use
  codes at the token endpoint. The e2e drives the real redirect flow
  and asserts wrong-verifier and code-reuse are rejected — PKCE binding
  is now exercised end to end.
- sync reset calls clearLedger() instead of reimplementing the path.
- push sets exit code 1 on rate-limited/server-error outcomes so cron
  and script callers can detect incomplete pushes.

Deferred (noted for fast-follow): macOS 'security -i' stdin mode
(untestable on this Linux box), ai.cost_estimated as a real
ParsedApiCall flag (touches core parser types).

Sync suite: 81 passing (5x stable), 5 developer-only.

AI-Origin: human
2026-07-13 16:45:39 +00:00
Andrew Lee
163842edac test(sync): unit, mock-IdP e2e, and developer-only integration tests
Offline, CI-safe (70 tests):
- sync.test.ts (26): discovery parsing, PKCE, auth URL, scope
  negotiation, callback server (state mismatch, IdP error, timeout,
  port fallback), config round-trip
- sync-ledger-otlp.test.ts (23): deterministic ID derivation, OTLP
  structure/attributes, batching, ledger append/dedup/prune/clear
- sync-push.test.ts (15): pipeline against a scriptable mock OTLP
  server — success+ledger+cost, partial-success batch NOT ledgered,
  401 stop, 429 wait-and-retry / persistent-429 give-up / Retry-After
  parsing (delta-seconds, HTTP-date, garbage), 5xx deferral,
  idempotent failure recovery
- sync-e2e.test.ts (6) + fixtures/mock-idp.ts: full setup→refresh→
  rotate→logout round-trip against an in-process mock IdP

Developer-only (skip unless env vars set, never CI):
- sync-headless-e2e.test.ts (1): real browser PKCE flow via Playwright
- sync-infra-e2e.test.ts (4): push/auth-reject/batch/idempotent
  re-push against a deployed backend

AI-Origin: human
2026-07-12 16:05:34 +00:00
iamtoruk
b95b62d8fa fix(cli): surface mDNS scan errors, robust spend-flow rollup + date label, harden tests
Addresses T1 review: discovery.ts logs mDNS bind errors to stderr and returns
already-found devices instead of masking failures as empty; spend-flow uses a
collision-proof "other" sentinel and a local-date period label; the spend/date
flags get friendly error handling; devices-json tests are deterministic (mocked
discovery) and assert the read-only mutation guards. 13 tests green, tsc clean.

Implemented by Codex gpt-5.5 (high); committed by Fable (git blocked in Codex sandbox).
2026-07-10 16:20:41 -07:00
ozymandiashh
fd44d995c7
feat(providers): add open-design provider for per-model usage tracking (#559) 2026-06-28 19:17:23 +02:00
ozymandiashh
d54f21d08c
fix(antigravity): read current agy antigravity-cli on-disk layout (#541)
* fix(antigravity): read current agy antigravity-cli on-disk layout

* fix(antigravity): propagate SQLITE_BUSY from the .db read so the run retries

---------

Co-authored-by: AgentSeal <hello@agentseal.org>
2026-06-22 01:08:44 +02:00
ozymandiashh
10d911d962
fix(cursor-agent): ingest workspace-less CLI transcript layout (#542)
* fix(cursor-agent): ingest workspace-less CLI transcript layout

* fix(cursor-agent): bump parse version so cached sessions pick up the new ingestion

---------

Co-authored-by: AgentSeal <hello@agentseal.org>
2026-06-22 00:55:01 +02:00
Ninym
e890d9bfc3 test(security): add failing test for HIGH-1 prototype pollution
Three PoC fixtures (tool name, bash command, model name) reproduce
the audit's HIGH-1 attack. Tests assert Object.prototype.calls stays
undefined after parsing. They fail against current parser.ts -- Task 3
will close the pollution sink with Object.create(null).
2026-04-17 08:32:18 +02:00