Commit graph

1634 commits

Author SHA1 Message Date
iamtoruk
287791e2a9 feat: live quota for Gemini, Copilot, and Antigravity in the desktop app
Extends the existing borrowed-credential quota module (claude/codex) with
three providers, per-provider settings toggles, and a provider display map:

- gemini: Code Assist loadCodeAssist/retrieveUserQuota via the Gemini CLI's
  on-disk OAuth creds; optional env-gated token refresh; retired-tier
  responses degrade to terminalFailure with migration guidance
- copilot: copilot_internal/user with editor-plugin headers; token read-only
  from hosts.json/apps.json with one re-read on 401; marked as an internal
  API that may drift, failures degrade honestly
- antigravity: loopback-only Connect-RPC probe of the local language server
  (ps/lsof discovery via execFile, csrf token for the app variant, agy CLI
  accepted tokenless); TLS relaxation scoped to 127.0.0.1; no OAuth fallback
- settings: per-provider quota switches (default on) persisted renderer-side,
  honored in the main-process service so disabled fetchers never run; IPC
  disabled-list filtered against the provider allowlist
- sanitizeError extended for Google (ya29.) and GitHub (gh*_) token shapes

Kimi audited, not duplicated: the macOS menubar already fetches Kimi quota
live (KimiSubscriptionService.swift); the electron app not surfacing it is a
pre-existing gap, unchanged here.
2026-08-23 03:13:47 -07:00
Resham Joshi
554d32eba6
Merge pull request #1098 from avs-io/fix/pr-tab-empty-period
Some checks failed
CI / semgrep (push) Waiting to run
Tests / test (22) (push) Waiting to run
Tests / test (22.13.0) (push) Waiting to run
macOS Menubar CI / test (push) Has been cancelled
fix(desktop): say why the PR tab is empty
2026-08-22 13:08:51 -07:00
iamtoruk
21382bb8e6 copy: drop the em dash from the wider-period hint (repo copy rule) 2026-08-22 12:46:42 -07:00
Resham Joshi
ed8fa3af3d
Merge pull request #1100 from getagentseal/fix/menubar-freshness-marker
payload: add-only stale marker for read-only stale menubar serves
2026-08-22 12:43:06 -07:00
Resham Joshi
3599b2d40a
Merge pull request #1101 from getagentseal/fix/copilot-haiku-pricing
fix(models): price claude-haiku-4.5 copilot session-store rows
2026-08-22 12:43:01 -07:00
iamtoruk
e19c099b05 fix: capture hydration flag right after the primary parse, not at return
isSessionHydrationComplete() (parser.ts) reads a module-level global that is
only safe immediately after the parse it describes, with no intervening
awaits. The stale-marker wiring landed in the prior commit read it at the
bottom of buildMenubarPayloadForRange, after several awaits -- including,
on the claude-config-scoped branch, a second parseAllSessions call for the
365-day history block that runs after the parse producing the headline
data. That let the history backfill's hydration outcome silently overwrite
the headline's.

Capture the flag into a local right after each branch's primary parse
resolves (the claude-config-scoped parse, or buildDurablePeriod) and thread
that captured value to buildMenubarPayload instead of re-reading the global
at the end. This also closes a cross-request race: once captured
synchronously, no other in-flight request's parse can flip it under us.

Regression test simulates a complete primary parse followed by an
incomplete bystander parse and asserts the payload reflects the primary
outcome, not the bystander's.

Drafted with minimax/MiniMax-M3 via local gateway.
2026-08-22 11:56:03 -07:00
iamtoruk
fa89015947 fix(models): price claude-haiku-4.5 copilot session-store rows (#1093)
The Copilot session-store.db writes the model as claude-haiku-4.5
(tier-first, dot), but no pricing alias existed for that raw id, so
calculateCost returned $0 while GitHub billed the real Anthropic rate
(kelchm's #946 validation: $0.0063536 for a sampled request). Aliases
it to the existing, correctly-priced claude-haiku-4-5 snapshot row --
no new rate invented. Unlike #1090's gpt-5.6-codex case, this id has
no prefix-fallback self-heal, so the daily cache bumps v26 -> v27 to
force re-derivation of already-finalized days.

Drafted with cline-pass/deepseek-v4-pro via local gateway.
2026-08-22 11:41:08 -07:00
iamtoruk
affd40e230 payload: add-only stale marker for read-only stale menubar serves (#771)
isSessionHydrationComplete() (parser.ts, PR #937) already reports when a
read-only serve skipped or staled real on-disk changes; thread it through
buildMenubarPayload as an optional stale field, present and true only on a
stale serve, always absent otherwise, so older/newer CLI-app pairs stay
compatible. Mirrors the field into the desktop renderer types and the macOS
menubar's Codable payload model (data layer only, no view change).

Drafted with minimax/MiniMax-M3 via local gateway.
2026-08-22 11:38:46 -07:00
Aditya Vikram Singh
0a43427924 fix(desktop): name a custom range on the empty PR tab
The empty note still used the dormant 30D label after a calendar
selection. Use the same rangeLabel as the TopBar caption.
2026-08-22 23:58:50 +05:30
Aditya Vikram Singh
b095091aad fix(desktop): thread PR-tab period through App
Empty copy was defaulting to Today because App never passed the selected
period. Labels now share PERIOD_LABELS; the wider hint queries Lifetime.
2026-08-22 23:46:46 +05:30
Aditya Vikram Singh
7b802f4dd0 fix(desktop): say why the PR tab is empty
Today/week/month often have no session-linked PRs, so the tab
looked broken. Name the selected period and, when All has rows,
point at that count. Do not invent gh links. Do not change
attribution or the default period.
2026-08-22 23:24:59 +05:30
Resham Joshi
9cfa957e3e
Merge pull request #1096 from getagentseal/fix/app-timeout-watchdog
fix(app): no-output watchdog replaces fixed timeouts; graceful kills; cold floor everywhere; orphan serve reaping
2026-08-22 10:47:15 -07:00
iamtoruk
4eabda17d7 fix: bound the serve drain, reap orphans on Windows, and de-flake the grace test
G1a - the stdin-EOF drain is now bounded (Promise.race against 45s, overridable
via CODEBURN_SERVE_DRAIN_MS for the test). Generous enough that no legitimate
request answered at EOF comes near it, and an async-wedged one releases the
child instead of turning it into the orphan the drain was added to prevent. The
comment names the ceiling: a SYNCHRONOUSLY wedged parse never yields to this
timer or to any other JS path, and only a signal can end that process.

G1b - Windows is the majority of the desktop fleet and had no orphan escape at
all: no ps, and no stdin-close recourse once the app has crashed. The reap now
works there. tasklist cannot report argv (image name and window title only) and
wmic is gone from current Windows, so the command line comes from
Get-CimInstance Win32_Process. Match discipline is unchanged - exact argv - with
quoting normalized on both sides, because we record a plain space-joined argv
while Windows reports the real quoted command line; a path with spaces still
round-trips. serveCommandMatches is extracted and tested directly.

G2 - the grace test raced node's cold boot (a 300ms window). The child now emits
one byte the moment it is ready, so the watchdog window is measured from
READINESS, not from spawn: boot cost cannot eat it at any machine speed. Timeout
raised on top of that. 6/6 green under 8 busy loops.

G3 - a served request heartbeats for its whole duration now, not just its parse:
the wrap moved to the one seam (runCaptured) that also covers aggregation and
payload serialization, whose ~8s tail lands back-to-back with the parse's own
quiet stretches. The one-shot path keeps the parse-only wrap, where an 8s tail
is comfortably inside the window on its own, and the CHANGELOG now says exactly
that instead of claiming every phase.

G4 - stillCold() anchors to coldStartBegan ?? bootedAt.

Text: the cold-timeout bound no longer claims to restore the "locate the CLI"
recovery (that is a not-found state, never cold-flagged), and the CHANGELOG
states the >15min case honestly - the ceiling still ends it, but partial cache
saves mean successive polls converge rather than each rescanning from nothing.
2026-08-22 10:36:58 -07:00
Resham Joshi
b4a53b5443
Merge pull request #1097 from getagentseal/fix/provider-phase-partial-saves
fix: thread partial-progress saves into non-Claude provider phases
2026-08-22 10:18:20 -07:00
iamtoruk
3317189dd6 fix: address adversarial review of the timeout watchdog (F1-F7)
F1 - `codeburn serve` lost an in-flight request's response when stdin closed
mid-flight: runStdioServe returned before its queue drained, and the explicit
process.exit(0) then hit runCaptured's monkeypatched exit, throwing ExitSignal
and exiting 1 with the frame never written. The finally now awaits the queue.

F2 - the SIGTERM rationale was false. armSignalCleanup unlinks the refresh lock
and re-raises; it publishes no partial cache, and a lock left by SIGKILL already
self-heals through the stale-pid takeover. SIGTERM-first is kept for the real
(smaller) benefit - a clean lock release instead of a takeover - and every
comment plus the CHANGELOG now says only that.

F3 - the cold gate had no exit. overviewWarmed only flips on success, so an
install that can never hydrate sat behind an indexing splash forever with no
error and no route to the CLI recovery. The cold claim now expires with the cold
window itself.

F4 - the real CLI does not heartbeat the way the demo did: a cold parse's
inter-provider cache save measured 31.6s of total silence, which a 45s window
survives only until the machine is 1.5x slower. Under CODEBURN_PROGRESS a
running parse now emits a keepalive every 10s regardless of phase, so silence
genuinely means stopped. Consumers that do not know the event ignore it.

F5 - the orphan-reap identity check matched any `cli.js` running `serve`. The
pidfile now records the exact argv and `ps -ww` must match it exactly.

F6 - bump() re-armed the watchdog after settle, leaving a timer finish() never
clears when a killed child's buffered output landed.

F7 - kill paths dropped the child from activeChildren before SIGTERM and the
SIGKILL backstop was unref'd, so a quit inside the 5s grace orphaned a child
that ignores SIGTERM. It now stays registered until it actually dies.

N8 - the silence test wrote its only byte at t~0, so it passed without the
re-arm. The byte now lands mid-window and the kill is asserted from it.
N9 - documented why mutations keep a plain total cap.
2026-08-22 09:58:59 -07:00
iamtoruk
9d2235ff0e docs: correct the stale per-file save comment 2026-08-22 09:48:09 -07:00
Resham Joshi
5042139903
Merge pull request #946 from kelchm/feat/copilot-session-store
feat(copilot): read per-request input/cache from session-store.db
2026-08-22 09:41:20 -07:00
iamtoruk
c5f03cd373 fix: thread partial-progress saves into non-Claude provider phases
scanProjectDirs (Claude) has long taken a throttled saveProgress
callback so a killed cold parse resumes from a warm cache;
parseProviderSources (every other provider) only persisted at the
whole-provider boundary. A run killed mid-way through a large single-
provider phase (a multi-GB codex corpus is the common case) discarded
everything parsed since the last provider finished, restarting that
whole phase from zero.

parseProviderSources now accepts the same onFileParsed callback,
invoked once per source right after that source's cache entry lands,
outside the per-file try/catch, mirroring scanProjectDirs' placement
and throttle. A file's fingerprint is only ever set after it fully
parses, so a mid-file kill can never leave a half-parsed file looking
complete on resume.

Verified against a real ~2GB Claude + ~4GB codex corpus (frozen
snapshot): kill -9 mid-codex-phase leaves a genuine partial cache
(no codex-results flush, complete: false), a resume only reparses the
unfinished remainder, and the final report is byte-identical to an
uninterrupted cold parse.
2026-08-22 09:39:26 -07:00
iamtoruk
c30ebecff4 Merge origin/main into feat/copilot-session-store
Resolves the conflicts #946 accumulated while it was in validation. Eight
files conflicted; the session-store accounting is unchanged.

src/daily-cache.ts — version collision. This PR minted 25 when main was at
24; #1056 (`codex-auto-review` pricing) then spent 25 on main. The bump moves
to 26/MIN 26 and daily-cache.v26.json, with main's full comment ladder kept as
the foundation and this PR's paragraph rewritten to name 26 and record the
collision. PENDING_REDERIVE_PROVIDERS and the B1 migration semantics from
b6481c19 carry over intact, retargeted at 26.

src/models.ts, src/parser.ts, src/audit-report.ts, src/models-report.ts —
#1075/#1078 replaced the per-site "reasoning is already inside output" tests
with billableOutputTokens() and REASONING_INCLUDED_IN_OUTPUT. This PR had
added copilot to that case at three sites independently. Union: all three
sites take main's helper call verbatim, and copilot joins claude and codex in
the set — same accounting this PR shipped, now through main's single source of
truth. It also reaches parser.ts activeGeneratedTokens (a fourth site, from
#1079), which is the same correction: a copilot supplementary call carries
reasoning with output 0, so counting it as generated repeats the per-turn
output. The audit legend already said so on this side.

src/providers/copilot.ts — comment-only. #1054's lastEventTimestamp-first
shutdown fallback was derived from this branch, so the code was already
identical on both sides: the `shutdownTimestamp` expression and the
`copilot:<sid>:shutdown:<model>:<n>` key are byte-for-byte main's. Both
rationales are kept (leg-collapse on date, and residual anchoring).

src/session-cache.ts — PROVIDER_PARSE_VERSIONS.copilot takes this PR's
`-session-store-v3` suffix; main's #1051 note about why a fingerprint change
is expensive is kept above it. Codex keeps main's #1092 suffix chain untouched.

src/main.ts — #1067 deleted the unreachable live dailyMap fallback that this
PR had taught behavioral weight. Main's deletion wins; the now-unused
isBehavioralTurn import goes with it.

tests/parser.test.ts — import union.

Also: scripts/upgrade-path/run.mjs NEW_DAILY_CACHE -> daily-cache.v26.json,
CODEBURN_COPILOT_SESSION_STORE_DB added to the #1064 env-isolation CLEARED
list, and the CHANGELOG entry's stale "v21" corrected to v26.

Verified: tsc clean; 3132 tests pass across 223 files; test:locks 26/26;
verify:upgrade PASSED, re-deriving daily-cache.v26.json and holding durable
copilot history across the bump. #1054's regression ("keeps three stampless
shutdown legs as :n keys with lastEventTimestamp") passes on the merged tree.
Real-corpus A/B against origin/main over 2026-07-01..2026-08-22: codex, grok,
kimicode and opencode byte-identical in export, audit and models; claude drifts
only monotonically with run order (a live session writing transcripts, confirmed
by interleaving four runs). This machine has no copilot data, so the copilot
recovery semantics rest on the suites and the upgrade-path corpus.
2026-08-22 09:36:51 -07:00
iamtoruk
d8bd428054 fix(app): give the mutation-restart and destroy paths the same SIGTERM grace
A settings mutation replaces the resident serve child, which may be mid-write:
the same lock hazard a timed-out read has, so it gets the same treatment.
restartAfterMutation and destroy now go through killGracefully instead of
SIGKILL. reapAll's one-shot loop stays a hard kill and now says why - quit has a
1.5s flush budget, shorter than the grace, so waiting one out would wedge the
quit, and a one-shot read holds no lock worth releasing.
2026-08-22 09:26:29 -07:00
iamtoruk
d4b7b48dc8 fix(app): no-output watchdog replaces fixed timeouts; graceful kills; cold floor everywhere; orphan serve reaping
The desktop app capped every read at 45s of TOTAL runtime, so a slow machine's
warm `optimize` (52.5s on 0.9.20, 39.6s on main) was SIGKILLed mid-parse and the
panel painted a red "timed out" that a 60s poll then reproduced forever.

- cli.ts: the read timeout is now a no-output watchdog. The window restarts on
  every byte the child writes (serve: every frame carrying that request's id),
  so only a genuinely silent child times out. A 15-minute absolute ceiling still
  reaps a livelocked child. Every read spawn sets CODEBURN_PROGRESS=1 so a long
  parse heartbeats through it; progress lines are stripped from any surfaced
  stderr so they can never become the error message.
- cli.ts: a timed-out child gets SIGTERM first and SIGKILL only after a 5s
  grace, on both the spawn and the serve branch. That lets the CLI's signal
  cleanup publish its partial parse and release the cross-process refresh lock
  instead of leaving both stale.
- main.ts: the 10-minute cold floor now covers EVERY read while the first
  hydration is still running, not just the overview. Sections start polling the
  moment `ready` flips (which an overview error also does), and a 45s section
  spawn queued behind the cold parse was killed on arrival.
- main.ts/renderer: a timeout raised while the hydration is still running is
  flagged `cold`, and the renderer keeps its indexing splash (and keeps the
  sections gated) instead of latching ready and painting CliErrorPanel. Only
  timeouts are flagged; a permission or nonzero failure still surfaces.
- cli.ts/main.ts: the resident serve child's pid is recorded under userData and
  a serve orphaned by a previous crash is reaped at launch, after `ps` confirms
  the pid still belongs to a codeburn serve.
- src/main.ts: `codeburn serve` exits explicitly once its stdin closes, so a
  handle outliving the transport cannot keep the child alive as an orphan.
2026-08-22 09:22:38 -07:00
Resham Joshi
68f3ed89e1
Merge pull request #1050 from avs-io/fix/968-subscription-flat-rate
Some checks are pending
CI / semgrep (push) Waiting to run
Tests / test (22) (push) Waiting to run
Tests / test (22.13.0) (push) Waiting to run
fix(models): treat subscription SKUs as honestly $0
2026-08-22 04:31:04 -07:00
iamtoruk
99c72f7ccc Update stale flat-rate test expectation for codex-auto-review
The merge with main pulled in #1056's codex-auto-review -> gpt-5.5
alias, which findUnpricedModels' pre-existing hasBillableRate check
now resolves for the raw id, so a synthetic $0 row for it is no
longer reported as unpriced (it correctly has a billable rate).

Removed it from the expected findUnpricedModels() output in the
"skips subscription / flat-rate product SKUs" test; the display-name
variant ('Codex Auto Review') is unaffected since getModelCosts does
not resolve display names, so it stays in the expected list. The
codex-auto-review / #1056 pricing interaction itself is already
covered by the "Codex activity ids (#1047)" describe block.
2026-08-22 04:25:47 -07:00
iamtoruk
f9c99a0651 Merge origin/main into fix/968-subscription-flat-rate
Resolves conflicts against main after #1078/#1084/#1088/#1090/#1092/
#1053/#1056 landed since this branch's last upstream merge.

- CHANGELOG.md: kept both entries (this PR's #968/#1050 note plus
  main's #1079/#1088, #1082, #1075 notes that had moved into the same
  "### Fixed" slot).
- Everything else (src/models.ts, main.ts, usage-aggregator.ts,
  daily-cache.ts, tests/*) merged cleanly with no conflict markers;
  git's recursive merge combined #1050's flat-rate classifier changes
  with #1056's codex-auto-review -> gpt-5.5 alias without overlap.

Verified the codex-auto-review / #1056 interaction post-merge:
isBuiltInFlatRateModel no longer matches codex-auto-review (dropped
per #1050), while MODEL_ALIASES still aliases it to gpt-5.5 (#1056),
so it prices at GPT-5.5 rates rather than $0. Covered by the existing
tests/models.test.ts "Codex activity ids (#1047)" describe block and
the "does not treat a priced sibling as expected-free" case.

getFlatRateModelsConfigHash's output is folded into
getDailyCacheConfigHash's template literal unconditionally
(flatRateModels=<hash>), so the flat-rate section always participates
in the daily-cache invalidation hash even when empty -- no
DAILY_CACHE_VERSION bump needed for this change.
2026-08-22 04:17:13 -07:00
Resham Joshi
13c1785df7
Merge pull request #1056 from avs-io/fix/1047-codex-activity-ids
fix(models): price Codex activity ids via the official underlying model
2026-08-22 04:14:32 -07:00
Resham Joshi
a7b9041bdd
Merge pull request #1053 from avs-io/fix/967-models-display-merge
fix(models): resolve and merge raw ids in models report
2026-08-22 03:50:25 -07:00
iamtoruk
00377968a0 Merge origin/main into fix/967-models-display-merge
Resolves conflicts from main's #1078 (billableOutputTokens for the
output bucket) and #1084 (models-report test exemplar swap) against
this branch's canonical-id row merging.

- src/audit-report.ts: import-line collision only. Union both sides'
  imports (billableOutputTokens + fallbackRawModelDisplayName/
  getShortModelName); both are used elsewhere in the file and neither
  side's logic needed further changes.
- src/models-report.ts: import-line collision resolved the same way.
  The row-construction conflict was structural, not a data conflict:
  main's side pushed one row per raw bucket (a simpler variant this
  branch's canonical-id folding already made obsolete downstream --
  rowsByKey/foldedCategoryCost/foldKey are used unconditionally past
  this point). Kept this branch's rowsByKey merge-by-canonical-id
  structure, which already computes credits from bucket.outputTokens
  (already billable-output-summed at the accumulation stage, main's
  change, untouched by this conflict) using the identical formula
  main used; folded main's explanatory comment about billable output
  into the kept credits block.
2026-08-22 03:43:24 -07:00
iamtoruk
b6481c1933 fix(copilot): let a parse-version bump re-derive the durable calls it re-reads
@vidoluco's round-6 re-validation: a cache generated by 0.9.20, copied and then
migrated by this branch, dropped exactly ONE call a virgin cache serves -
mai-code-1-flash-picker, 324 input / 91,136 cache read / $0.01, two minutes
after the labelled compaction row in its session. Every other day matched.

Root cause is the durable union merge, not the reconciliation. `compactedAt` is
a capture-only field this branch adds to the shutdown-ROLLUP call, and 0.9.20
already cached that call under the same (deliberately stable) dedup key. The
union appended only UNSEEN keys, so the bump could never reach it. The migrated
cache then ran the compaction-anchored residual math with no anchor - the
pre-anchor behaviour exactly: the interval opens at -Infinity, the
PRE-compaction rows are subtracted from a rollup that never counted them, and
the residual clamps to zero and disappears.

The union now REPLACES a cached call with the freshly-derived one wherever the
key matches. Nothing is deleted: keys the re-read did not produce are exactly
the rows the source has pruned, and those still carry forward untouched with
the old parser's fields. Keys it did produce were just derived from the live
source in this very pass, so the fresh call is by construction the better one.

This is also what makes the round-5 compaction-output fix reach an existing
cache - that row's key is stable too, so append-only would have pinned it at
output 0 forever. Both directions are pinned by (c6).
2026-08-22 03:30:53 -07:00
iamtoruk
12c8cb7790 fix(copilot): re-derive migrated copilot day slices, count the compaction row's output
Two defects from @kelchm's two-machine validation of #946.

1. The daily-cache migration carried the copilot slice of every existing day
   entry verbatim instead of re-deriving it, so `overview` on a migrated cache
   kept serving pre-store numbers (2,980,804 tokens for a day whose fresh
   derivation is 74,811,412; 21,608 calls vs 16,326) while export/models/audit
   served the corrected ones off the same cache.

   Root cause is the partial-survival guard (e9d922ca), not the migration: it
   keeps a settled baseline slice whenever the fresh derivation reports FEWER
   calls, reading that as aged-out sources. This change's whole point is that
   copilot's supplementary accounting calls stop counting as api calls, so
   every store-era day shrinks in calls and got pinned to its pre-store value.
   The guard's own TRADE-OFF note predicted exactly this case.

   A migration from an older cache version now grants copilot ONE
   guarded-shrink-exempt re-derivation (`pendingRederive`), spent by the first
   COMPLETE parse. It fires only where that parse actually produced a slice for
   the (day, provider), so a day whose sources are gone still carries forward
   whole - never-lose is unchanged in both directions - and no other provider
   is exempt.

   The daily cache moves 21 -> 25: 21-24 landed on main during validation, and
   only a number the validators' own daily-cache.v21.json cannot claim gets
   their stale carried slices re-derived.

2. The `initiator='compaction'` store row was ingested with outputTokens 0. Its
   output has no assistant.message anywhere in events.jsonl, so that row is the
   only place those tokens exist - dropping them was the sole token discrepancy
   (-3,085) across a 30-session store-matched comparison. It is now read (on the
   same optional-select rung as `initiator`, which is what identifies the row),
   counted and priced; every other row keeps output 0 because a per-turn call
   owns it. The copilot parse version moves to session-store-v3 so cached v2
   rows re-parse; the dedup key is deliberately unchanged.

Settle window stays at 24 h - the three-machine evidence for tightening it is
recorded next to the constant, but shortening it is a product call.
2026-08-22 02:26:35 -07:00
Aditya Vikram Singh
9e24d093af merge: rebase #1056 onto main with Codex v15 / daily v25
Main already spent Codex results v14 (#1092). Daily stays 25
(#1090 still the last daily bump). Keep all four parse-version
tokens (codex-pricing-v1 + codex-tps-v1 + codex-mcp-skills-v1 +
activity-price-v1) so a take-ours merge cannot drop the pricing,
throughput, or MCP/skills invalidation. Do not Extra High again.
2026-08-22 14:53:59 +05:30
Resham Joshi
e4b05471b4
Merge pull request #1092 from getagentseal/fix/codex-mcp-skills-attribution
fix(codex): attribute MCP + skills from the exec custom tool and the item model (#478)
2026-08-22 02:10:22 -07:00
iamtoruk
1c2ac6e8a4 fix(codex): attribute MCP + skills from the exec custom tool and item model
The `mcp-cli call <server> <tool>` matcher from #656 only ran on
`function_call` arguments. Codex records the same exec two other ways:
the custom-tool transport carries the shell tool as a `custom_tool_call`
whose payload is an `input` program (no `arguments` at all), and the item
model repeats a finished command as `event_msg`/`item_completed` with a
`CommandExecution` item holding an argv `command`. Both reach the Bash
counter and neither reached the matcher, so a CLI-wrapped MCP call stayed
missing from the MCP breakdown (#478).

All three shapes now feed one classification pipeline. The same pipeline
learns skills: Codex has no skill tool, so a skill load is a shell read of
the skill's SKILL.md, which landed entirely under Bash. A read counts only
when the command segment starts with a file-reading binary and the path
ends in `<name>/SKILL.md`; the skill is `<name>`, matching pi's key (#588)
and the Claude parser's vocabulary. A grep near a SKILL.md stays Bash.

Attribution only: the item-model path adds no tool of its own, and a
command carried by both shapes is attributed once (per-name claim ledger).
Cost, tokens, calls and daily rollups cannot move.

CODEX_CACHE_VERSION 13 -> 14 and the codex parse version both move: both
layers store tools/toolSequence/skills verbatim, and on a warm cache the
fix is otherwise invisible (verified by reverting the bumps: skills stay
empty). DAILY_CACHE_VERSION is deliberately unchanged - the daily cache
holds no tool/mcp/skill breakdown, and turn category cannot change because
every added attribution rides on a call that already has Bash.
2026-08-22 01:51:33 -07:00
Aditya Vikram Singh
090499ef7e merge: rebase #1056 onto main with Codex v14 / daily v25
Main already spent Codex results v13 (#1079) and daily v24 (#1090).
Keep all three parse-version tokens (codex-pricing-v1 +
codex-tps-v1 + activity-price-v1) so a take-ours merge cannot
drop the pricing or throughput invalidation. Do not Extra High again.
2026-08-22 05:26:00 +05:30
Resham Joshi
a9b7670f2f
Merge pull request #1090 from getagentseal/fix/gpt56-codex-pricing
Some checks are pending
CI / semgrep (push) Waiting to run
Tests / test (22) (push) Waiting to run
Tests / test (22.13.0) (push) Waiting to run
fix(models): price gpt-5.6-codex and gpt-5.6-codex-max
2026-08-21 16:32:15 -07:00
Resham Joshi
d3b971c21f
Merge pull request #1088 from getagentseal/fix/codex-tps-reasoning
fix(codex): exclude harness startup and fix partial-dedup timing in Tok/s
2026-08-21 16:31:45 -07:00
iamtoruk
fa8c008f71 revert(codex): retract the BUG-2 partial-dedup timing fix
Re-instrumenting this exact head over the full corpus traced the
original 12.5%/46% figures to a replication gap: the earlier
prevCumulativeTotal guard (codex.ts) already discards any token_count
event whose running total exactly repeats the previous kept one, so a
drop at the seenKeys dedup site is always a byte-identical replay of
already-counted tokens -- never a real loss. The condition BUG-2's fix
guarded against does not occur in real Codex output.

Removes taskDedupedTokens, its increment at the dedup site, the
active-time scaling at task_complete, and the now-unexercisable unit
test (its fixture forces a dedup collision that is not also a
cumulative-total repeat, a state the real writer never produces). The
dedup site keeps a short comment recording why no rescaling is needed,
so the next investigator doesn't retrace this.

Keeps: the reasoning double-count fix, the harness-startup exclusion
(BUG-1, confirmed to the decimal), the shared mergeToolIntervals
helper and permissive duration parsing (BUG-8), and the legend rename.
The real-corpus table is unchanged -- BUG-2 measured zero effect on it
before this revert too.
2026-08-21 15:37:57 -07:00
iamtoruk
c7e754d3c0 fix(codex): exclude harness startup and fix partial-dedup timing in Tok/s
Extends #1079 (reasoning double-count) with three more findings from an
exactness pass over the same throughput path:

- BUG-1: task_started fires before Codex assembles the request, so the
  gap to the first request-context event (turn_context, world_state,
  event_msg/user_message, or response_item/message) was counted as
  active model time. The active window now starts at that event instead.

- BUG-2: a token_count event dropped by fork-replay dedup lost its
  tokens from the numerator while the task's real duration still spanned
  it in the denominator, understating Tok/s for a partially (not fully)
  deduped task. The active window is now scaled down by the dropped
  tokens' proportional share.

- BUG-8: the tool-interval clip/merge/cap logic was duplicated between
  providers/codex.ts and codex-throughput.ts and had already drifted
  (task_complete only read a plain-number duration, unlike
  mcp_tool_call_end). Now one shared mergeToolIntervals, and
  task_complete's duration parses the same permissive forms.

Cost and every token count remain byte-identical. activeGeneratedTokens/
activeDurationMs/toolWaitMs are stored verbatim in both Codex caches, so
none of this self-heals -- but the CODEX_CACHE_VERSION 13 bump already
shipped for #1079 covers the same fields, so no further bump is needed.

Dashboard's per-model column keeps the "Tok/s" header (zero width slack
at the standard layout, verified against a real test); the legend now
spells out "Effective Tok/s" with a decode-speed disclaimer.
2026-08-21 15:29:14 -07:00
iamtoruk
98348d973b fix(models): price gpt-5.6-codex and gpt-5.6-codex-max
Neither id is in LiteLLM yet, so litellm-snapshot.json had no row for
them (285 sessions / 5,446 calls priced at $0 on one real corpus,
flagged during #1075 verification). Add explicit snapshot rows sourced
from the pattern every prior Codex-suffixed id LiteLLM does carry
follows: it bills identically to its bare-model sibling of the same
generation (gpt-5-codex == gpt-5, gpt-5.1-codex == gpt-5.1-codex-max ==
gpt-5.1, gpt-5.2-codex == gpt-5.2, gpt-5.3-codex == gpt-5.3, verified
against the live model_prices_and_context_window.json) - both new ids
get the exact gpt-5.6 tuple rather than an invented rate.

getModelCosts already resolved both ids to the correct rate through
the `gpt-5.6` prefix fallback before this, so a fresh parse or a warm
session-cache read (codeburn models included) was already pricing
these calls correctly. The daily cache is the one place that does not
self-heal: it has no per-provider invalidation, so a day finalized
before a `gpt-5.6` snapshot row existed at all keeps that $0 forever.
Raising MIN_SUPPORTED_VERSION (v23 -> v24) forces the one-time
re-derivation, a lossless no-op for days already correct - the
mechanism itself is generic and already covered by
daily-cache-version-rederivation.test.ts. #1056 also claims 24 on its
own branch; whichever lands second takes the next number.

Closes #1077
2026-08-21 14:53:00 -07:00
iamtoruk
bfcf63ce3a fix(codex): stop double-counting reasoning tokens in Tok/s throughput
The same reasoning-inclusion bug #1075/#1078 fixed for cost also affected
the throughput display: activeGeneratedTokens/taskGeneratedTokens in the
Codex parser and generatedTokens in the live codex-tps reader summed
outputTokens + reasoningTokens, but reasoning is already inside output.
All three sites now route through the billableOutputTokens('codex', ...)
helper #1078 introduced, so the throughput numerator can never drift from
the billed one.

Cost and every token count are unchanged (verified byte-identical on a
real 51,753-call corpus); Tok/s drops 20-42% depending on how
reasoning-heavy the model is.

activeGeneratedTokens/activeDurationMs/toolWaitMs are stored verbatim in
both the Codex result cache and the session cache rather than re-derived
on read, so neither self-heals: CODEX_CACHE_VERSION moves 11 -> 13 (12 is
claimed by feat/core-extraction's own port of this feature) and
PROVIDER_PARSE_VERSIONS.codex gains a codex-tps-v1 suffix, forcing Codex
sessions to re-parse once.

Closes #1079
2026-08-21 14:42:04 -07:00
Aditya Vikram Singh
33d344abc3 merge: rebase #1056 onto main with Codex v12 / daily v24
Main already spent Codex results v11 and daily v23 on #1075, and
result caches are now version-suffixed. Keep both parse-version
tokens (codex-pricing-v1 + activity-price-v1) so a take-ours merge
cannot drop the pricing invalidation. Do not Extra High again.
2026-08-22 02:35:51 +05:30
ozymandiashh
aaf9a4b780
Merge pull request #1085 from avs-io/fix/result-cache-version-suffix
Some checks are pending
CI / semgrep (push) Waiting to run
Tests / test (22) (push) Waiting to run
Tests / test (22.13.0) (push) Waiting to run
fix(cache): version-suffix provider result caches
2026-08-21 23:34:53 +03:00
ozymandiashh
67f085a3c2
Merge pull request #1054 from avs-io/fix/1051-copilot-rollup-dedup
fix(copilot): prefer lastEventTimestamp for stampless shutdown calls
2026-08-21 23:34:19 +03:00
ozymandiashh
d1d2ecda27
Merge pull request #1073 from avs-io/fix/sync-drop-cleartext-session-id
fix(sync): drop cleartext session id from attribution spans
2026-08-21 23:33:34 +03:00
ozymandiashh
06b297c885
Merge pull request #1072 from avs-io/fix/json-report-require-durable
fix(report): require durable period; drop unreachable dailyMap
2026-08-21 23:32:53 +03:00
ozymandiashh
3ec96d3a1f
Merge pull request #1071 from avs-io/fix/cache-lock-loser-outcome
fix(cache): accept completed-by-other as the stale-lock loser
2026-08-21 23:32:03 +03:00
ozymandiashh
f697cce53b
Merge pull request #1069 from avs-io/fix/chart-session-title-legend
fix(web): lead session chart legends with title when unique
2026-08-21 23:29:15 +03:00
Aditya Vikram Singh
add21524da fix(cache): treat a present versioned result file as exclusive 2026-08-22 01:49:39 +05:30
Aditya Vikram Singh
5ec82ea18c fix(cache): version-suffix provider result caches
Mixed-version binaries were clobbering the unsuffixed Codex, Cursor,
and Antigravity result files and each re-parsed the whole corpus.
Write *-results.v<n>.json like the daily cache. Leave the unsuffixed
file for older binaries; adopt a matching-version copy once.
2026-08-22 01:36:15 +05:30
Resham Joshi
cfba136e91
Merge pull request #1084 from getagentseal/fix/codex-pricing-polish
fix(pricing): harden the #1078 follow-ups — cache-flag survival, credits trap, harness cost bound
2026-08-21 12:56:28 -07:00
iamtoruk
f92949c081 docs(pricing): bound the codex repricing drift in verify:upgrade, fix stale comments
The COST_CHANGED_BY_DESIGN carve-out in compare.mjs left codex cost entirely
unasserted after #1075/#1078. It now requires the upgraded cost to be strictly
lower than baseline and within 25% of it, and the row verdict says "repriced"
instead of the misleading "identical (cost N% drift)".

grok.ts's comment on the reasoning/output split still claimed provider-side
splitting was the repo's only mechanism; it now also names
billableOutputTokens/REASONING_INCLUDED_IN_OUTPUT (models.ts), which is the
other half since #1078. usage-aggregator.ts's "folds reasoning into output"
comment was true pre-#1078 but is backwards for codex now (reasoning is
already inside output, not added to it).

Test exemplars for the "reasoning is additive" case used hermes, whose
upstream is OpenAI-shaped and may not stay a safe example; swapped to gemini,
which documents "thoughts" as genuinely separate output.

CHANGELOG's #1075 entry gets one line noting days whose codex transcripts
have aged out keep their pre-fix totals via the daily-cache never-lose guard,
matching the disclosure already given for #1040.
2026-08-21 12:49:05 -07:00