llm-relay 0.61.0 → 0.63.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (235) hide show
  1. package/dist/accounting-store-io.js +0 -33
  2. package/dist/accounting-store-io.js.map +1 -1
  3. package/dist/accounting-store-schema.js +0 -65
  4. package/dist/accounting-store-schema.js.map +1 -1
  5. package/dist/accounting-store.js +0 -195
  6. package/dist/accounting-store.js.map +1 -1
  7. package/dist/accounting.js +0 -72
  8. package/dist/accounting.js.map +1 -1
  9. package/dist/anthropic.js +0 -18
  10. package/dist/anthropic.js.map +1 -1
  11. package/dist/authEnv.js +0 -78
  12. package/dist/authEnv.js.map +1 -1
  13. package/dist/availability-snapshot.js +0 -58
  14. package/dist/availability-snapshot.js.map +1 -1
  15. package/dist/availability.js +0 -162
  16. package/dist/availability.js.map +1 -1
  17. package/dist/backend.js +1 -416
  18. package/dist/backend.js.map +1 -1
  19. package/dist/benchmarks.js +1 -63
  20. package/dist/benchmarks.js.map +1 -1
  21. package/dist/breaker-persistence.js +0 -59
  22. package/dist/breaker-persistence.js.map +1 -1
  23. package/dist/candidates.js +0 -64
  24. package/dist/candidates.js.map +1 -1
  25. package/dist/catalog.js +1 -140
  26. package/dist/catalog.js.map +1 -1
  27. package/dist/circuit-breaker.js +1 -90
  28. package/dist/circuit-breaker.js.map +1 -1
  29. package/dist/claude-hook.js +0 -81
  30. package/dist/claude-hook.js.map +1 -1
  31. package/dist/cli.d.ts +89 -1
  32. package/dist/cli.js +115 -702
  33. package/dist/cli.js.map +1 -1
  34. package/dist/config-edit.js +0 -11
  35. package/dist/config-edit.js.map +1 -1
  36. package/dist/config.d.ts +21 -0
  37. package/dist/config.js +22 -372
  38. package/dist/config.js.map +1 -1
  39. package/dist/configured-limits.js +0 -58
  40. package/dist/configured-limits.js.map +1 -1
  41. package/dist/context-limits.js +0 -97
  42. package/dist/context-limits.js.map +1 -1
  43. package/dist/control-authorization.js +0 -36
  44. package/dist/control-authorization.js.map +1 -1
  45. package/dist/cooldown-clear.js +0 -6
  46. package/dist/cooldown-clear.js.map +1 -1
  47. package/dist/credential-fleet.js +0 -5
  48. package/dist/credential-fleet.js.map +1 -1
  49. package/dist/credential-select.js +4 -30
  50. package/dist/credential-select.js.map +1 -1
  51. package/dist/dashboard-auth.js +0 -29
  52. package/dist/dashboard-auth.js.map +1 -1
  53. package/dist/dashboard-contract.js +0 -46
  54. package/dist/dashboard-contract.js.map +1 -1
  55. package/dist/dashboard-routes.js +0 -32
  56. package/dist/dashboard-routes.js.map +1 -1
  57. package/dist/dashboard-snapshot.js +0 -154
  58. package/dist/dashboard-snapshot.js.map +1 -1
  59. package/dist/dashboard-static.js +0 -22
  60. package/dist/dashboard-static.js.map +1 -1
  61. package/dist/delegate-gate/cast-necessity.js +1 -22
  62. package/dist/delegate-gate/cast-necessity.js.map +1 -1
  63. package/dist/delegate-gate/cli.js +0 -6
  64. package/dist/delegate-gate/cli.js.map +1 -1
  65. package/dist/delegate-gate/diff-parser.js +0 -32
  66. package/dist/delegate-gate/diff-parser.js.map +1 -1
  67. package/dist/delegate-gate/fix.js +0 -10
  68. package/dist/delegate-gate/fix.js.map +1 -1
  69. package/dist/delegate-gate/gate.js +0 -12
  70. package/dist/delegate-gate/gate.js.map +1 -1
  71. package/dist/delegate-gate/minimality.js +0 -38
  72. package/dist/delegate-gate/minimality.js.map +1 -1
  73. package/dist/delegate-gate/post-image.js +1 -13
  74. package/dist/delegate-gate/post-image.js.map +1 -1
  75. package/dist/delegate-gate/shared-state.js +0 -23
  76. package/dist/delegate-gate/shared-state.js.map +1 -1
  77. package/dist/delegate-gate/test-assertions.js +1 -54
  78. package/dist/delegate-gate/test-assertions.js.map +1 -1
  79. package/dist/delegate-gate/types.js +0 -11
  80. package/dist/delegate-gate/types.js.map +1 -1
  81. package/dist/dialect-stream.js +9 -37
  82. package/dist/dialect-stream.js.map +1 -1
  83. package/dist/dispatch-exhaustion-persistence.js +0 -35
  84. package/dist/dispatch-exhaustion-persistence.js.map +1 -1
  85. package/dist/dispatch.js +0 -269
  86. package/dist/dispatch.js.map +1 -1
  87. package/dist/documents.js +0 -92
  88. package/dist/documents.js.map +1 -1
  89. package/dist/dotenv.js +0 -22
  90. package/dist/dotenv.js.map +1 -1
  91. package/dist/dynamic-pools.js +0 -93
  92. package/dist/dynamic-pools.js.map +1 -1
  93. package/dist/emitSse.js +0 -64
  94. package/dist/emitSse.js.map +1 -1
  95. package/dist/executable-lookup.d.ts +28 -0
  96. package/dist/executable-lookup.js +42 -0
  97. package/dist/executable-lookup.js.map +1 -0
  98. package/dist/hard-cap.js +1 -32
  99. package/dist/hard-cap.js.map +1 -1
  100. package/dist/host-routing.js +0 -40
  101. package/dist/host-routing.js.map +1 -1
  102. package/dist/installed-hosts.d.ts +64 -0
  103. package/dist/installed-hosts.js +75 -0
  104. package/dist/installed-hosts.js.map +1 -0
  105. package/dist/json-shape.js +0 -19
  106. package/dist/json-shape.js.map +1 -1
  107. package/dist/kernel/contracts.js +0 -26
  108. package/dist/kernel/contracts.js.map +1 -1
  109. package/dist/kernel/request-lifecycle.js +0 -4
  110. package/dist/kernel/request-lifecycle.js.map +1 -1
  111. package/dist/key-checker.js +0 -115
  112. package/dist/key-checker.js.map +1 -1
  113. package/dist/key-import.js +0 -20
  114. package/dist/key-import.js.map +1 -1
  115. package/dist/keys-cli.js +0 -15
  116. package/dist/keys-cli.js.map +1 -1
  117. package/dist/keystore.js +2 -51
  118. package/dist/keystore.js.map +1 -1
  119. package/dist/lane-cadence.js +1 -51
  120. package/dist/lane-cadence.js.map +1 -1
  121. package/dist/lane-manifest.js +0 -54
  122. package/dist/lane-manifest.js.map +1 -1
  123. package/dist/lane-probe.js +0 -36
  124. package/dist/lane-probe.js.map +1 -1
  125. package/dist/lane-quota-probe.js +0 -61
  126. package/dist/lane-quota-probe.js.map +1 -1
  127. package/dist/log.js +0 -17
  128. package/dist/log.js.map +1 -1
  129. package/dist/mcp/lane-runner.d.ts +114 -0
  130. package/dist/mcp/lane-runner.js +186 -0
  131. package/dist/mcp/lane-runner.js.map +1 -0
  132. package/dist/mcp/protocol.d.ts +98 -0
  133. package/dist/mcp/protocol.js +67 -0
  134. package/dist/mcp/protocol.js.map +1 -0
  135. package/dist/mcp/server.d.ts +78 -0
  136. package/dist/mcp/server.js +360 -0
  137. package/dist/mcp/server.js.map +1 -0
  138. package/dist/metadata.js +0 -73
  139. package/dist/metadata.js.map +1 -1
  140. package/dist/network-block.js +0 -45
  141. package/dist/network-block.js.map +1 -1
  142. package/dist/offload.js +0 -21
  143. package/dist/offload.js.map +1 -1
  144. package/dist/onboarding.js +0 -36
  145. package/dist/onboarding.js.map +1 -1
  146. package/dist/openai-dialect.js +0 -32
  147. package/dist/openai-dialect.js.map +1 -1
  148. package/dist/openai-request.js +0 -236
  149. package/dist/openai-request.js.map +1 -1
  150. package/dist/os-keyring.d.ts +11 -2
  151. package/dist/os-keyring.js +3 -39
  152. package/dist/os-keyring.js.map +1 -1
  153. package/dist/ping/cadence.js +3 -90
  154. package/dist/ping/cadence.js.map +1 -1
  155. package/dist/ping/metrics.js +0 -91
  156. package/dist/ping/metrics.js.map +1 -1
  157. package/dist/ping/ping.js +0 -17
  158. package/dist/ping/ping.js.map +1 -1
  159. package/dist/ping/probe-cache.js +2 -53
  160. package/dist/ping/probe-cache.js.map +1 -1
  161. package/dist/ping/quota.js +0 -17
  162. package/dist/ping/quota.js.map +1 -1
  163. package/dist/ping/runtime-telemetry.js +1 -18
  164. package/dist/ping/runtime-telemetry.js.map +1 -1
  165. package/dist/pool-health.js +0 -49
  166. package/dist/pool-health.js.map +1 -1
  167. package/dist/presets.js +0 -4
  168. package/dist/presets.js.map +1 -1
  169. package/dist/process-safety-net.js +0 -36
  170. package/dist/process-safety-net.js.map +1 -1
  171. package/dist/quota-demotion.js +1 -35
  172. package/dist/quota-demotion.js.map +1 -1
  173. package/dist/quota-observation.js +0 -21
  174. package/dist/quota-observation.js.map +1 -1
  175. package/dist/rate-limits.js +1 -94
  176. package/dist/rate-limits.js.map +1 -1
  177. package/dist/refusal-interpretation.js +2 -327
  178. package/dist/refusal-interpretation.js.map +1 -1
  179. package/dist/registry.js +0 -11
  180. package/dist/registry.js.map +1 -1
  181. package/dist/repair.js +1 -119
  182. package/dist/repair.js.map +1 -1
  183. package/dist/request-log.js +0 -2
  184. package/dist/request-log.js.map +1 -1
  185. package/dist/reshaper.js +0 -85
  186. package/dist/reshaper.js.map +1 -1
  187. package/dist/resolved-attempt.js +0 -3
  188. package/dist/resolved-attempt.js.map +1 -1
  189. package/dist/responses-request.js +0 -187
  190. package/dist/responses-request.js.map +1 -1
  191. package/dist/routes/admin.js +0 -38
  192. package/dist/routes/admin.js.map +1 -1
  193. package/dist/secret-file-acl.js +0 -26
  194. package/dist/secret-file-acl.js.map +1 -1
  195. package/dist/self-update.js +0 -118
  196. package/dist/self-update.js.map +1 -1
  197. package/dist/server.js +15 -952
  198. package/dist/server.js.map +1 -1
  199. package/dist/session-pin.js +0 -9
  200. package/dist/session-pin.js.map +1 -1
  201. package/dist/setup-claude.js +0 -9
  202. package/dist/setup-claude.js.map +1 -1
  203. package/dist/spend-headroom.js +0 -20
  204. package/dist/spend-headroom.js.map +1 -1
  205. package/dist/sse-frames.js +0 -31
  206. package/dist/sse-frames.js.map +1 -1
  207. package/dist/sse.js +1 -19
  208. package/dist/sse.js.map +1 -1
  209. package/dist/state-paths.js +0 -16
  210. package/dist/state-paths.js.map +1 -1
  211. package/dist/stream-commit.js +1 -27
  212. package/dist/stream-commit.js.map +1 -1
  213. package/dist/target-facts.js +2 -130
  214. package/dist/target-facts.js.map +1 -1
  215. package/dist/telemetry.js +0 -8
  216. package/dist/telemetry.js.map +1 -1
  217. package/dist/think-tags.js +1 -38
  218. package/dist/think-tags.js.map +1 -1
  219. package/dist/tier-data.js +0 -29
  220. package/dist/tier-data.js.map +1 -1
  221. package/dist/tool-dialects.js +1 -103
  222. package/dist/tool-dialects.js.map +1 -1
  223. package/dist/tool-use-ids.js +0 -51
  224. package/dist/tool-use-ids.js.map +1 -1
  225. package/dist/usage-observer.js +0 -52
  226. package/dist/usage-observer.js.map +1 -1
  227. package/dist/validator.js +1 -25
  228. package/dist/validator.js.map +1 -1
  229. package/dist/winenv.js +0 -63
  230. package/dist/winenv.js.map +1 -1
  231. package/dist/write-behind.js +0 -12
  232. package/dist/write-behind.js.map +1 -1
  233. package/package.json +2 -2
  234. package/scripts/install-skill.mjs +62 -9
  235. package/skills/llm-relay/SKILL.md +26 -1
@@ -8,7 +8,6 @@ import { emptyAccountingSpendCell, mergeAccountingSpendCells, ACCOUNTING_DAY_SCH
8
8
  import { DASHBOARD_REQUEST_ID_PATTERN, isDashboardAttemptId, isDashboardFailureKind, isDashboardOutcome, isDashboardSafeId, isDashboardUtcTimestamp, } from "./dashboard-contract.js";
9
9
  import { DEFAULT_FLUSH_DELAY_MS, MAX_FLUSH_DELAY_MS, WriteBehindTimer } from "./write-behind.js";
10
10
  export { ACCOUNTING_DAY_SCHEMA, ACCOUNTING_DEDUP_SCHEMA, ACCOUNTING_LIFETIME_SCHEMA, ACCOUNTING_MAX_DAY_ROWS, ACCOUNTING_MAX_DEDUP_IDS, ACCOUNTING_MAX_DETAIL_ATTEMPTS, ACCOUNTING_MAX_FILE_BYTES, ACCOUNTING_MAX_LOSS_MARKERS, ACCOUNTING_MAX_MONTHS, ACCOUNTING_MAX_PACKET_ATTEMPTS, ACCOUNTING_MAX_RECENT_ROWS, ACCOUNTING_MAX_ROWS_PER_CELL, ACCOUNTING_MAX_SAMPLES, ACCOUNTING_MINUTE_SCHEMA, ACCOUNTING_RECENT_SCHEMA, ACCOUNTING_STORE_VERSION, } from "./accounting-store-schema.js";
11
- /** Sized for a Claude Code session holding many parallel subagent streams in flight. */
12
11
  export const ACCOUNTING_MAX_PENDING_REQUESTS = 256;
13
12
  export const ACCOUNTING_MAX_PENDING_ATTEMPTS_PER_REQUEST = ACCOUNTING_MAX_PACKET_ATTEMPTS;
14
13
  export const ACCOUNTING_MAX_READ_DAYS = 31;
@@ -103,8 +102,6 @@ function emptyMetric() { return { sumMs: null, known: 0, unknown: 0, lost: 0, ov
103
102
  function emptyAggregate() {
104
103
  return { requests: 0, attempts: 0, served: 0, errored: 0, cancelled: 0, tokens: emptyTokens(), requestTokens: emptyTokens(), latency: emptyMetric(), commit: emptyMetric(), spend: emptySpend(), unpricedRequests: 0 };
105
104
  }
106
- // Cell keys are owned by the schema module's SPEND_CELL_KEYS; the adders below
107
- // reach them through emptyAccountingAggregateSpend() rather than a second list.
108
105
  function emptySpend() {
109
106
  return {
110
107
  providerPublishedReported: emptyAccountingSpendCell(),
@@ -113,26 +110,18 @@ function emptySpend() {
113
110
  referenceEstimated: emptyAccountingSpendCell(),
114
111
  };
115
112
  }
116
- /** Route one priced spend to its aggregate cell by (priceSource, tokenBasis). */
117
113
  function spendCell(spend, record) {
118
114
  if (record.priceSource === "provider_published") {
119
115
  return record.tokenBasis === "reported" ? spend.providerPublishedReported : spend.providerPublishedEstimated;
120
116
  }
121
117
  return record.tokenBasis === "reported" ? spend.referenceReported : spend.referenceEstimated;
122
118
  }
123
- /**
124
- * Fold one priced spend into an aggregate's ATTEMPT-side cells. First contributor
125
- * seeds the cell; later ones merge through the schema's checked integer adder so a
126
- * safe-integer overflow degrades the amount to null (announced by the caller's
127
- * exact flag) rather than wrapping.
128
- */
129
119
  function addAttemptSpend(target, record) {
130
120
  if (record === null)
131
121
  return true;
132
122
  target.spend ??= emptySpend();
133
123
  return addSpendIntoCells(target.spend, record);
134
124
  }
135
- /** Cell-level fold shared by both scopes. */
136
125
  function addSpendIntoCells(cells, record) {
137
126
  const cell = spendCell(cells, record);
138
127
  const knownBefore = cell.known;
@@ -156,16 +145,7 @@ function emptyDay(date) {
156
145
  function emptyLifetime() { return { schema: ACCOUNTING_LIFETIME_SCHEMA, version: ACCOUNTING_STORE_VERSION, firstRequestAt: null, lastRequestAt: null, aggregate: emptyAggregate(), months: {}, coverage: emptyCoverage() }; }
157
146
  function emptyRecent() { return { schema: ACCOUNTING_RECENT_SCHEMA, version: ACCOUNTING_STORE_VERSION, rows: [], details: {}, coverage: emptyCoverage() }; }
158
147
  function newUsageSide() { return { seen: false, exact: true, value: 0 }; }
159
- /**
160
- * Fold one token cell into a side. Any uncertainty marker (unknown/lost/overflow, or a null
161
- * value despite evidence) poisons the WHOLE side rather than being dropped: presenting a partial
162
- * sum as a measurement is exactly what the provenance invariant forbids.
163
- * Returns whether THIS cell carried a measured amount (drives the estimated-only detection below).
164
- */
165
148
  function noteUsageCell(side, cell) {
166
- // Only a MEASURED amount counts as usage. An all-unknown cell (the store's way of saying "no
167
- // token facts arrived") is absence of evidence, not evidence of consumption — counting it
168
- // would turn every unreported request into a phantom "mixed" reading.
169
149
  if (!(cell.known > 0))
170
150
  return false;
171
151
  side.seen = true;
@@ -175,32 +155,15 @@ function noteUsageCell(side, cell) {
175
155
  side.value += cell.value;
176
156
  return true;
177
157
  }
178
- /** Input + output only: cache creation/read cells are deliberately excluded from this figure. */
179
158
  function noteUsageTokens(side, input, output) {
180
159
  const inputMeasured = noteUsageCell(side, input);
181
160
  const outputMeasured = noteUsageCell(side, output);
182
161
  return inputMeasured || outputMeasured;
183
162
  }
184
- /** Non-negative safe-integer counter, else 0 (callers convert a 0 total back to null). */
185
163
  function windowCounter(value) {
186
164
  return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : 0;
187
165
  }
188
166
  const USED_WINDOW_EMPTY = { requests: null, tokens: null, basis: null };
189
- /**
190
- * Combine the sides under the contract's reported/estimated/mixed vocabulary.
191
- *
192
- * Decision (review fix 2026-08-22): a window holding BOTH bases never sums them into one
193
- * scalar — the accounting invariant keeps reported and estimated accumulators separate precisely
194
- * so no total can quietly blend them, and a lone figure tagged "mixed" names the mix without
195
- * showing its split. Instead:
196
- * - every measured request carried reported tokens ⇒ the REPORTED figure stands alone, labelled
197
- * `reported` (the estimated cells on those same requests cover unreported halves of the same
198
- * requests, so the reported figure is a true measurement, merely narrower);
199
- * - any ESTIMATED-ONLY request sits beside reported ones ⇒ no number: `tokens: null`,
200
- * `basis: "mixed"` — returning the reported figure alone would understate the window by every
201
- * estimated-only request, and the sum would blend bases. Naming the mix without inventing the
202
- * number is the provenance-safe direction.
203
- */
204
167
  function usageReading(requests, reported, estimated, estimatedOnlyRequest) {
205
168
  if (!reported.seen && !estimated.seen)
206
169
  return { requests, tokens: null, basis: null };
@@ -227,8 +190,6 @@ function sourceCell(value) {
227
190
  return {
228
191
  value: safeCounter(source.value),
229
192
  observedAt: validTimestamp(source.observedAt) ? source.observedAt : null,
230
- // `isLoadableId`, not `isDashboardSafeId` — see `methodSnapshot`: the accept side must not
231
- // admit what the loader will reject, or the whole day shard is quarantined on the next read.
232
193
  method: typeof source.method === "string" && isLoadableId(source.method) && Buffer.byteLength(source.method, "utf8") <= 256 ? source.method : null,
233
194
  };
234
195
  }
@@ -399,12 +360,9 @@ function addRequest(target, event, outcome) {
399
360
  exact &&= addMetric(target.latency, outcome === "cancelled" ? null : safeCounter(event.latencyMs), event.endedAt);
400
361
  exact &&= addMetric(target.commit, outcome === "success" ? safeCounter(event.commitMs) : null, event.endedAt);
401
362
  exact &&= addRawTokens(target.requestTokens, event.tokens, event.endedAt);
402
- // Request spend mirrors request tokens: the WINNING serve only, so a
403
- // retried-elsewhere request never double-counts its failed attempts.
404
363
  if (event.spend !== null) {
405
364
  target.requestSpend ??= emptySpend();
406
365
  exact &&= addSpendIntoCells(target.requestSpend, event.spend);
407
- // A partial figure (unpriced cache kinds etc.) is priced but a lower bound.
408
366
  if (event.spend.coverage !== "full") {
409
367
  exact &&= increase(target, "partiallyPricedRequests");
410
368
  }
@@ -416,9 +374,6 @@ function addRequest(target, event, outcome) {
416
374
  }
417
375
  function addRootAttempt(target, attempt) {
418
376
  let exact = increase(target, "attempts");
419
- // Root aggregates accumulate BOTH attempt-side axes, exactly as the dimension
420
- // attempt rows do: dropping the token fold made every root disagree with its
421
- // own rows while the spend fold looked complete.
422
377
  exact &&= addAggregateTokens(target.tokens, attempt.tokens);
423
378
  exact &&= addAttemptSpend(target, attempt.spend);
424
379
  return exact;
@@ -435,10 +390,6 @@ function addAttempt(target, attempt) {
435
390
  function rowKey(row) {
436
391
  return JSON.stringify([row.kind, row.role, row.provider, row.model, row.client, row.credentialId, row.attribution, row.outcome, row.failureKind]);
437
392
  }
438
- /**
439
- * Dimension fields are fixed when a row is created (only aggregate counters move
440
- * afterwards), so its serialized key is memoized rather than re-derived per scan.
441
- */
442
393
  const rowKeys = new WeakMap();
443
394
  function cachedRowKey(row) {
444
395
  const existing = rowKeys.get(row);
@@ -448,10 +399,6 @@ function cachedRowKey(row) {
448
399
  rowKeys.set(row, key);
449
400
  return key;
450
401
  }
451
- /**
452
- * In-memory membership index for a day's dedup array; the SORTED ARRAY remains
453
- * the persisted form. Keyed weakly so an evicted or pruned day drops its index.
454
- */
455
402
  const dedupMembership = new WeakMap();
456
403
  function dedupSet(day) {
457
404
  let ids = dedupMembership.get(day);
@@ -516,8 +463,6 @@ function makeAttempt(event) {
516
463
  model: safeId(event.model),
517
464
  credentialId: safeId(event.credentialId),
518
465
  tokens: tokenSnapshot(event.tokens, event.endedAt),
519
- // The lifecycle already validated this record's provenance fields; a
520
- // re-validation through the packet guard below keeps that guarantee total.
521
466
  spend: event.spend,
522
467
  };
523
468
  const parsed = parseAccountingAttemptPacketV1(value);
@@ -554,20 +499,11 @@ class AccountingStoreImpl {
554
499
  recentReadState = "missing";
555
500
  dirtyLifetime = false;
556
501
  dirtyRecent = false;
557
- /** Fixed snapshots were not safely loaded yet; terminal facts must queue. */
558
502
  fixedLoadPending = false;
559
- /** Quarantine/lower-bound evidence observed before both fixed reads complete. */
560
503
  pendingFixedLoss = false;
561
504
  pendingJournalLoss = false;
562
- /** Losses observed before fixed snapshots load must not dirty empty state. */
563
505
  deferredGlobalLosses = [];
564
- /**
565
- * Retention is its own post-fact transaction. Keep its intent separately so
566
- * a fact commit followed by a failed tombstone commit cannot turn a retry
567
- * into an apparently clean no-op.
568
- */
569
506
  pendingRetention = null;
570
- /** The exact pending journal shape, retained only to merge a later recovery. */
571
507
  pendingRetentionPlan = null;
572
508
  retryTimer = null;
573
509
  retryDelay = RETRY_MIN_MS;
@@ -597,10 +533,6 @@ class AccountingStoreImpl {
597
533
  maxTargets: 128,
598
534
  ...(options.ioHooks === undefined ? {} : { hooks: options.ioHooks }),
599
535
  });
600
- // A read-only store never takes the writer lease and never replays or quarantines
601
- // anything — every one of those is a write against a directory a live relay may be
602
- // committing to. It observes committed snapshots only; unflushed in-memory deltas of
603
- // the running process stay invisible, which the CLI reports as lag rather than repairing.
604
536
  if (this.readOnly) {
605
537
  this.writer = { status: "released", error: null, retryable: false };
606
538
  this.last = result("none", null, false, false);
@@ -649,8 +581,6 @@ class AccountingStoreImpl {
649
581
  flush() {
650
582
  if (this._closed)
651
583
  return result("invalid", "closed");
652
- // Read-only: flushing is a write. Nothing is ever dirty here anyway because no
653
- // record path runs; keep the answer explicit rather than relying on that accident.
654
584
  if (this.readOnly)
655
585
  return result("none", null, false, false);
656
586
  this.timer.clear();
@@ -668,10 +598,6 @@ class AccountingStoreImpl {
668
598
  if (recovered.lowerBoundLoss || recovered.status === "recovery-loss")
669
599
  this.consumeRecoveryLoss(recovered);
670
600
  if (recovered.status === "recovered" && this.pendingRetentionPlan !== null) {
671
- // Never replace current memory with the journal's saved lifetime: records
672
- // may have arrived after the failed retention write. Reconcile only the
673
- // durable pruning effects, then let the ordinary fact commit persist any
674
- // newer in-memory facts.
675
601
  this.reconcileRecoveredRetention(this.pendingRetentionPlan);
676
602
  }
677
603
  this.drainQueued();
@@ -699,9 +625,6 @@ class AccountingStoreImpl {
699
625
  this.recentReadState = "ok";
700
626
  this.retryDelay = RETRY_MIN_MS;
701
627
  const retention = this.commitRetention(committedDates);
702
- // A write-loaded shard stays outside the clean cache until this commit has
703
- // made it durable. Re-admit it now, or evict it if every cache slot is
704
- // still protected by dirty/in-flight facts.
705
628
  for (const date of [...committedDates].sort())
706
629
  this.rememberKnownDay(date);
707
630
  return retention ?? written;
@@ -712,9 +635,6 @@ class AccountingStoreImpl {
712
635
  this.timer.clear();
713
636
  this.clearRetry();
714
637
  const flushed = this.flush();
715
- // Dirty facts and a recoverable journal still belong to this store. Keep
716
- // it open so a later close can retry instead of irreversibly discarding
717
- // the only in-process copy after one transient failure.
718
638
  if (flushed.retryable)
719
639
  return flushed;
720
640
  const closed = this.io.close();
@@ -731,8 +651,6 @@ class AccountingStoreImpl {
731
651
  const parsed = parsedDay(cached);
732
652
  return parsed === null ? { status: "corrupt", value: null, error: "schema" } : { status: "ok", value: this.withDayLoss(parsed) };
733
653
  }
734
- // Quarantine renames a file; an out-of-process reader reports corruption instead of
735
- // touching a shard a live relay may be about to rewrite.
736
654
  const read = this.io.readJson(targetForDay(date), (value) => parseAccountingDayShardV1(value).ok, { quarantineCorrupt: !this.readOnly });
737
655
  if (read.status === "missing")
738
656
  return { status: "missing", value: null };
@@ -793,8 +711,6 @@ class AccountingStoreImpl {
793
711
  if (parsed === null)
794
712
  return { status: "corrupt", value: null, error: "schema" };
795
713
  const requested = typeof options === "number" ? options : options?.limit;
796
- // Unlike the constructor options, an EXPLICIT read limit is honoured exactly:
797
- // 0 asks for zero rows, and only an omitted limit falls back to the default.
798
714
  const limit = typeof requested === "number" && Number.isSafeInteger(requested)
799
715
  ? Math.min(Math.max(requested, 0), this.recentLimit)
800
716
  : this.recentLimit;
@@ -814,24 +730,6 @@ class AccountingStoreImpl {
814
730
  const packet = parsed.details[requestId];
815
731
  return packet === undefined ? { status: "missing", value: null } : { status: "ok", value: frozenClone(packet) };
816
732
  }
817
- /**
818
- * Local usage for one credential in the CURRENT period — the `localUsed` half of spec §5.1
819
- * rung 2. Reads ONLY in-memory state and never touches disk:
820
- *
821
- * - minute/day → this process's in-memory day shard (`this.days`, which holds today's shard
822
- * once any terminal request has been applied; a shard evicted from the read cache or not yet
823
- * reloaded after restart is invisible ⇒ null, never 0).
824
- * - month → in-memory lifetime months only. Reading 31 day shards would put disk IO on the
825
- * availability path, so a month whose rollup was dropped by ACCOUNTING_MAX_MONTHS reports
826
- * null rather than a partial sum.
827
- *
828
- * Included: every TERMINAL event already applied to memory, including ones still awaiting the
829
- * write-behind flush. NOT included: in-flight requests that have not completed, and anything
830
- * recorded before this store instance loaded. Tokens are reported input+output when every
831
- * measured request carried a provider report, estimated-only otherwise; a window holding both
832
- * bases reports NO number (`basis: "mixed"`, tokens null) — see usageReading. An uncertain cell
833
- * poisons its whole side instead of being silently dropped from the sum.
834
- */
835
733
  usedInWindow(options) {
836
734
  const at = options.now ?? Date.now();
837
735
  if (!Number.isFinite(at))
@@ -843,15 +741,10 @@ class AccountingStoreImpl {
843
741
  let requests = null;
844
742
  const reported = newUsageSide();
845
743
  const estimated = newUsageSide();
846
- // True when at least one counted request carried ONLY estimated tokens — the case that turns
847
- // a reported figure into an understatement and forces basis "mixed" with no value.
848
744
  let estimatedOnlyRequest = false;
849
745
  if (options.period !== "month") {
850
- // Request rows carry kind "request"; their aggregate counts requests exactly once per row.
851
746
  const day = this.days.get(date);
852
747
  if (day !== undefined) {
853
- // Both sides are bare HH:MM strings, so lexicographic order is chronological. The day
854
- // shard only contains THIS date's cells; the bound just drops clock-skewed future ones.
855
748
  const minuteKeys = options.period === "minute" ? [minute] : Object.keys(day.cells).filter((cellMinute) => cellMinute <= minute);
856
749
  let total = 0;
857
750
  for (const cellMinute of minuteKeys) {
@@ -861,8 +754,6 @@ class AccountingStoreImpl {
861
754
  for (const row of cell.rows) {
862
755
  if (row.kind !== "request" || row.credentialId !== options.credentialId)
863
756
  continue;
864
- // Unattributable traffic cannot be claimed by a named credential; a model filter
865
- // narrows further when given.
866
757
  if (row.credentialId === null || (model !== null && row.model !== model))
867
758
  continue;
868
759
  total += windowCounter(row.requests);
@@ -877,18 +768,10 @@ class AccountingStoreImpl {
877
768
  }
878
769
  }
879
770
  else {
880
- // The lifetime month rollup carries ROOT aggregates across ALL credentials — it cannot be
881
- // narrowed to one slot. Feeding it to a per-credential rung-2 subtraction would charge every
882
- // key's traffic to the one credential whose ceiling is being resolved, so BOTH figures
883
- // decline until the rollup is per-credential: requests as well as tokens. A whole-relay
884
- // count on a per-slot row is not "exact with stated scope" — the wire row has no scope
885
- // marker to state it with, and a reader cannot tell. Null is the honest reading here; same
886
- // fail-safe direction as everywhere else in this method.
887
771
  return USED_WINDOW_EMPTY;
888
772
  }
889
773
  return usageReading(requests, reported, estimated, estimatedOnlyRequest);
890
774
  }
891
- /** Read paths may touch arbitrary dates; retain only a deterministic bounded hint. */
892
775
  rememberKnownDay(date) {
893
776
  if (!this.days.has(date) || this.knownDays.has(date) || this.knownDaysCap < 1)
894
777
  return;
@@ -903,10 +786,6 @@ class AccountingStoreImpl {
903
786
  break;
904
787
  }
905
788
  if (!evicted) {
906
- // Cache pressure may never discard a fact that is dirty or still has
907
- // in-flight work. Those states are independently bounded by the
908
- // transaction and pending-work caps; only a clean, unprotected read
909
- // cache entry may be dropped here.
910
789
  if (!this.dirtyDays.has(date) && !this.hasPendingDay(date))
911
790
  this.days.delete(date);
912
791
  return;
@@ -953,8 +832,6 @@ class AccountingStoreImpl {
953
832
  onAttemptCompleted(event) {
954
833
  const attempt = makeAttempt(event);
955
834
  if (attempt === null) {
956
- // A packet that fails validation is a measurement quietly disappearing
957
- // unless the loss is announced like every other drop path.
958
835
  this.markGlobalLoss("unknown", "truncated", "attempt_packet");
959
836
  return;
960
837
  }
@@ -990,10 +867,6 @@ class AccountingStoreImpl {
990
867
  if (existing !== undefined)
991
868
  return existing;
992
869
  if (this.pending.size >= this.pendingRequestLimit) {
993
- // Evict the OLDEST-STARTED in-flight request, not merely the first
994
- // inserted: parallel subagent streams do not start in insertion order.
995
- // An entry whose start event has not arrived carries the least identity,
996
- // so it is treated as older than every dated entry.
997
870
  let victim = null;
998
871
  let victimStartedAt = null;
999
872
  for (const [key, entry] of this.pending) {
@@ -1160,8 +1033,6 @@ class AccountingStoreImpl {
1160
1033
  model: authoritative?.model ?? safeId(event.model),
1161
1034
  credentialId: authoritative?.credentialId ?? safeId(event.credentialId),
1162
1035
  tokens: authoritative?.tokens ?? tokenSnapshot(event.tokens, event.endedAt),
1163
- // The WINNING serve's spend mirrors its tokens. Repair spend stays on the
1164
- // repair attempt rows (C1) so a later roll-up can add it back exactly once.
1165
1036
  spend: authoritative?.spend ?? null,
1166
1037
  attempts: retained,
1167
1038
  attemptMetadata: { total, stored: retained.length, dropped: total - retained.length },
@@ -1241,7 +1112,6 @@ class AccountingStoreImpl {
1241
1112
  ids.add(requestId);
1242
1113
  return true;
1243
1114
  }
1244
- /** Keep a fact transaction within the journal's bounded target set. */
1245
1115
  ensureTransactionRoom(date) {
1246
1116
  if (this.dirtyDays.has(date))
1247
1117
  return true;
@@ -1250,14 +1120,10 @@ class AccountingStoreImpl {
1250
1120
  return false;
1251
1121
  if (this.dirtyDays.size + 3 <= maximum)
1252
1122
  return true;
1253
- // A terminal request owns a day + lifetime + recent atomically. Commit the
1254
- // previous bounded batch before accepting a new day rather than allowing an
1255
- // unrepresentable journal transaction to accumulate.
1256
1123
  const flushed = this.flush();
1257
1124
  return (flushed.status === "committed" || flushed.status === "recovered" || flushed.status === "none")
1258
1125
  && this.dirtyDays.size + 3 <= maximum;
1259
1126
  }
1260
- /** Read one named day through the namespace-restricted journal I/O only. */
1261
1127
  loadDayForWrite(date) {
1262
1128
  const cached = this.days.get(date);
1263
1129
  if (cached !== undefined)
@@ -1269,9 +1135,6 @@ class AccountingStoreImpl {
1269
1135
  return created;
1270
1136
  }
1271
1137
  if (read.status !== "ok" || read.value === null) {
1272
- // A quarantined day is intentionally restarted as a lower bound on the
1273
- // next queued pass. A transient read failure remains queued and cannot
1274
- // overwrite an unreadable existing shard.
1275
1138
  if (read.status === "corrupt" || read.status === "oversize") {
1276
1139
  this.markGlobalLoss("corrupt_recovery", "corrupt", "day");
1277
1140
  }
@@ -1286,7 +1149,6 @@ class AccountingStoreImpl {
1286
1149
  this.days.set(date, mutable);
1287
1150
  return mutable;
1288
1151
  }
1289
- /** Keep unread fixed snapshots immutable until a retry has loaded both safely. */
1290
1152
  deferFixedLoad(value) {
1291
1153
  this.fixedLoadPending = true;
1292
1154
  this.lifetimeReadState = "corrupt";
@@ -1300,13 +1162,6 @@ class AccountingStoreImpl {
1300
1162
  for (const loss of losses)
1301
1163
  this.markGlobalLoss(loss.reason, loss.kind, loss.field, loss.count);
1302
1164
  }
1303
- /**
1304
- * Read-only constructor load: read the two fixed snapshots WITHOUT the writer lease and
1305
- * without any recovery/quarantine write. A corrupt or unreadable snapshot stays corrupt
1306
- * (reported by `readLifetime`/`readRecent`) rather than being renamed aside. Day shards
1307
- * are deliberately NOT preloaded — `readDay` reads them on demand through the same
1308
- * no-write readJson path.
1309
- */
1310
1165
  loadFixedSnapshotsReadOnly() {
1311
1166
  const lifetime = this.io.readJson(LIFETIME_TARGET, (value) => parseAccountingLifetimeV1(value).ok, { quarantineCorrupt: false });
1312
1167
  this.lifetimeReadState = lifetime.status === "ok" && lifetime.value !== null
@@ -1321,7 +1176,6 @@ class AccountingStoreImpl {
1321
1176
  if (this.recentReadState === "ok")
1322
1177
  this.recent = mutableRecent(recent.value);
1323
1178
  }
1324
- /** Recover a committed journal first, then load the two fixed snapshots. */
1325
1179
  recoverAndLoad() {
1326
1180
  const recovered = this.io.recover();
1327
1181
  if (recovered.status === "failed" || recovered.status === "invalid") {
@@ -1397,7 +1251,6 @@ class AccountingStoreImpl {
1397
1251
  this.queueRecoveredRetention();
1398
1252
  this.last = recovered;
1399
1253
  }
1400
- /** Validate every outgoing canonical object before handing it to the journal. */
1401
1254
  factSnapshots() {
1402
1255
  const snapshots = {};
1403
1256
  const encode = (name, value) => {
@@ -1432,15 +1285,9 @@ class AccountingStoreImpl {
1432
1285
  }
1433
1286
  return snapshots;
1434
1287
  }
1435
- /**
1436
- * Retention never shares the fact transaction: a fact must be durable before
1437
- * its day can become eligible for a tombstone transaction.
1438
- */
1439
1288
  commitRetention(committedDates) {
1440
1289
  if (this.retentionDays === null)
1441
1290
  return null;
1442
- // A clock that previously rolled back may have caught the durable frontier
1443
- // since construction. Re-evaluate it on every flush, not only at startup.
1444
1291
  this.queueRecoveredRetention();
1445
1292
  const eligible = this.advanceTrustedDate(committedDates);
1446
1293
  if (eligible !== null)
@@ -1448,8 +1295,6 @@ class AccountingStoreImpl {
1448
1295
  const cutoff = this.pendingRetention;
1449
1296
  if (cutoff === null)
1450
1297
  return null;
1451
- // A recovered or already-completed transaction can leave a stale in-memory
1452
- // intent, but it must not cause an endless no-op retry.
1453
1298
  if (!this.retentionNeedsWork(cutoff)) {
1454
1299
  this.pendingRetention = null;
1455
1300
  this.pendingRetentionPlan = null;
@@ -1465,17 +1310,12 @@ class AccountingStoreImpl {
1465
1310
  const start = progress ?? dayFor(current.firstRequestAt ?? "");
1466
1311
  const retention = this.retentionCandidates(start, cutoff, Math.max(0, maximum - 1));
1467
1312
  const candidates = retention.dates;
1468
- // One target is consumed by lifetime. Do not claim retention can make
1469
- // progress if a caller has configured no room for a tombstone at all.
1470
1313
  if (retention.truncated && candidates.length === 0) {
1471
1314
  return this.fail(result("invalid", "retention-targets", false, this.lowerBoundLoss));
1472
1315
  }
1473
1316
  const next = mutableLifetime(current);
1474
1317
  const lastCandidate = candidates.at(-1) ?? null;
1475
1318
  const advanced = lastCandidate === null ? null : nextDate(lastCandidate);
1476
- // `retentionFrom` is the durable cursor: it may reach the policy cutoff
1477
- // only once every bounded tombstone batch before it has committed. This
1478
- // lets a restarted store continue without discovering directory entries.
1479
1319
  next.coverage.retentionFrom = advanced ?? (retention.truncated
1480
1320
  ? progress
1481
1321
  : progress === null || progress < cutoff ? cutoff : progress);
@@ -1505,8 +1345,6 @@ class AccountingStoreImpl {
1505
1345
  this.applyRetentionPlan(plan);
1506
1346
  this.pendingRetentionPlan = null;
1507
1347
  if (plan.truncated) {
1508
- // A complete transaction can still have more bounded work. This is a
1509
- // continuation, not a dirty fact write, so wake it explicitly.
1510
1348
  this.pendingRetention = plan.cutoff;
1511
1349
  this.retryDelay = RETRY_MIN_MS;
1512
1350
  this.scheduleRetry();
@@ -1516,11 +1354,6 @@ class AccountingStoreImpl {
1516
1354
  }
1517
1355
  return written;
1518
1356
  }
1519
- /**
1520
- * A restart cannot scan a directory to rediscover old shards. The durable
1521
- * lifetime lower date lets us name a bounded, deterministic tombstone range
1522
- * instead; absent days are harmless no-op tombstones.
1523
- */
1524
1357
  retentionCandidates(start, cutoff, limit) {
1525
1358
  const dates = new Set();
1526
1359
  let truncated = false;
@@ -1544,15 +1377,11 @@ class AccountingStoreImpl {
1544
1377
  }
1545
1378
  }
1546
1379
  if (start === null) {
1547
- // Only this bounded fallback needs ordering. Normal retained data has a
1548
- // lifetime first-request date, so it names its whole deterministic range
1549
- // without retaining or sorting arbitrary read-path dates.
1550
1380
  for (const date of [...this.knownDays].sort())
1551
1381
  add(date);
1552
1382
  }
1553
1383
  return { dates: [...dates].sort(), truncated };
1554
1384
  }
1555
- /** Apply a retention transaction known to have committed in this process. */
1556
1385
  applyRetentionPlan(plan) {
1557
1386
  this.lifetime = plan.next;
1558
1387
  this.lifetimeReadState = "ok";
@@ -1561,12 +1390,6 @@ class AccountingStoreImpl {
1561
1390
  this.knownDays.delete(date);
1562
1391
  }
1563
1392
  }
1564
- /**
1565
- * A retry may recover a durable retention journal after subsequent records
1566
- * have already changed memory. Merge only its coverage cursor and exact
1567
- * tombstones; a later fact transaction writes the current aggregate rather
1568
- * than replacing it with the journal's stale lifetime payload.
1569
- */
1570
1393
  reconcileRecoveredRetention(plan) {
1571
1394
  this.pendingRetentionPlan = null;
1572
1395
  const current = parsedLifetime(this.lifetime);
@@ -1584,8 +1407,6 @@ class AccountingStoreImpl {
1584
1407
  markCoverage(next.coverage, "unknown", "truncated", "retention");
1585
1408
  this.lifetime = next;
1586
1409
  this.lifetimeReadState = "ok";
1587
- // It is harmless to rewrite an unchanged lifetime once; doing so is what
1588
- // makes the recovered durable state and any unflushed newer facts converge.
1589
1410
  this.dirtyLifetime = true;
1590
1411
  for (const date of plan.candidates) {
1591
1412
  if (this.dirtyDays.has(date))
@@ -1604,12 +1425,6 @@ class AccountingStoreImpl {
1604
1425
  const progress = validDate(current.coverage.retentionFrom) ? current.coverage.retentionFrom : null;
1605
1426
  return current.coverage.retentionDays !== this.retentionDays || progress === null || progress < cutoff;
1606
1427
  }
1607
- /**
1608
- * Backfilling a shard behind the durable cursor invalidates that cursor,
1609
- * including after a wall-clock rollback. Rewind coverage in the fact
1610
- * transaction, then queue pruning when the shard is already expired under
1611
- * the current clock.
1612
- */
1613
1428
  revisitLateRetentionDate(date) {
1614
1429
  if (this.retentionDays === null || !validDate(date))
1615
1430
  return;
@@ -1630,18 +1445,11 @@ class AccountingStoreImpl {
1630
1445
  return;
1631
1446
  this.pendingRetention = latest(this.pendingRetention, cutoff);
1632
1447
  }
1633
- /**
1634
- * `lastRequestAt` is durable evidence of the previous trusted frontier. Do
1635
- * not derive it from a retention cursor: a partial cursor merely says where a
1636
- * tombstone batch stopped. Keep a future frontier too, so a clock rollback
1637
- * cannot let a late fact move retention backwards before the clock catches up.
1638
- */
1639
1448
  seedTrustedDateFromLifetime() {
1640
1449
  const last = dayFor(this.lifetime.lastRequestAt ?? "");
1641
1450
  if (last !== null)
1642
1451
  this.trustedDate = latest(this.trustedDate, last);
1643
1452
  }
1644
- /** Resume an incomplete durable cursor only when its fact frontier is clock-eligible. */
1645
1453
  queueRecoveredRetention() {
1646
1454
  if (this.retentionDays === null || this.trustedDate === null)
1647
1455
  return;
@@ -1650,7 +1458,6 @@ class AccountingStoreImpl {
1650
1458
  return;
1651
1459
  this.queueRetentionForDate(this.trustedDate);
1652
1460
  }
1653
- /** A clock-gated, monotonic date: future and late facts cannot advance pruning. */
1654
1461
  advanceTrustedDate(committedDates) {
1655
1462
  const nowDate = this.clockDate();
1656
1463
  if (nowDate === null)
@@ -1690,7 +1497,6 @@ class AccountingStoreImpl {
1690
1497
  this.markGlobalLoss("corrupt_recovery", "corrupt", "journal");
1691
1498
  this.scheduleFlush();
1692
1499
  }
1693
- /** Record a loss where it can be surfaced without claiming an exact total. */
1694
1500
  markGlobalLoss(reason, kind, field, count = 1) {
1695
1501
  if (this.fixedLoadPending) {
1696
1502
  const existing = this.deferredGlobalLosses.find((loss) => loss.reason === reason && loss.kind === kind && loss.field === field);
@@ -1774,7 +1580,6 @@ class AccountingStoreImpl {
1774
1580
  return value;
1775
1581
  }
1776
1582
  }
1777
- /** Accounting shares the common write-behind cadence, exported for callers/tests. */
1778
1583
  export const ACCOUNTING_FLUSH_DELAY_MS = DEFAULT_FLUSH_DELAY_MS;
1779
1584
  export const ACCOUNTING_MAX_FLUSH_DELAY_MS = MAX_FLUSH_DELAY_MS;
1780
1585
  export function createAccountingStore(options = {}) {