@rulvar/core 1.230.0 → 1.232.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 (3) hide show
  1. package/dist/index.d.ts +272 -4
  2. package/dist/index.js +1310 -887
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -8794,11 +8794,25 @@ function readRejectedFinishCandidates(raw) {
8794
8794
  *
8795
8795
  * The twenty-fifth comparison run was killed and resumed, and its two
8796
8796
  * terminals mixed both kinds with nothing marking which was which: the
8797
- * money was cumulative, the wake count and the replay figures were not,
8797
+ * money was cumulative, the live-only counters were not,
8798
8798
  * and reconciling them into one honest account of the logical run was
8799
8799
  * hand work over a joined journal. Keys are field paths as a consumer
8800
- * reads them off `RunOutcome` (`cost.orchestrator.wakes`), and
8801
- * {@link TerminalTelemetryScopes} requires every one of them.
8800
+ * reads them off `RunOutcome` (`cost.orchestrator.wakes`): the type
8801
+ * requires every field of the outcome, and the `satisfies` below
8802
+ * requires every counted leaf under `cost` (RV2801), because an index
8803
+ * signature admits nested paths and demands none, so the five that were
8804
+ * declared were declared by hand and by luck while four
8805
+ * (`cost.usageApprox`, `cost.abandoned.usd`, `cost.abandoned.usageApprox`,
8806
+ * `cost.orchestrator.share`) were simply missing. That is the RV2701
8807
+ * blindness one level down: a gate whose subject is nested figures
8808
+ * cannot stop at the top level.
8809
+ *
8810
+ * What neither can decide is whether a declared scope is TRUE, and a
8811
+ * wrong scope is worse than a missing one: a missing one is noticed, a
8812
+ * wrong one is believed. The doctrine test suspends a real run, resumes
8813
+ * it, and holds every declared figure against its own claim (RV2801),
8814
+ * which is how three `cost.orchestrator.*` paths were found calling
8815
+ * themselves `'segment'` while the terminal folded them cumulatively.
8802
8816
  */
8803
8817
  const TERMINAL_TELEMETRY_SCOPE = Object.freeze({
8804
8818
  status: "terminal",
@@ -8827,10 +8841,14 @@ const TERMINAL_TELEMETRY_SCOPE = Object.freeze({
8827
8841
  "cost.totalUsd": "cumulative",
8828
8842
  "cost.grossUsd": "cumulative",
8829
8843
  "cost.wireRequests": "cumulative",
8844
+ "cost.usageApprox": "cumulative",
8845
+ "cost.abandoned.usd": "cumulative",
8846
+ "cost.abandoned.usageApprox": "cumulative",
8830
8847
  "cost.orchestrator.spentUsd": "cumulative",
8831
- "cost.orchestrator.wakes": "segment",
8832
- "cost.orchestrator.forcedFinish": "segment",
8833
- "cost.orchestrator.reserveUsedUsd": "segment",
8848
+ "cost.orchestrator.share": "cumulative",
8849
+ "cost.orchestrator.wakes": "cumulative",
8850
+ "cost.orchestrator.forcedFinish": "cumulative",
8851
+ "cost.orchestrator.reserveUsedUsd": "cumulative",
8834
8852
  transportRetries: "segment",
8835
8853
  schemaRejectedFinishExchanges: "segment",
8836
8854
  schemaRecoveredFinishExchanges: "segment"
@@ -8905,6 +8923,7 @@ function logicalRunTelemetry(entries) {
8905
8923
  function childRostersFromJournal(entries) {
8906
8924
  const rosters = /* @__PURE__ */ new Map();
8907
8925
  const ordered = [...entries].sort((a, b) => a.seq - b.seq);
8926
+ const abandoned = buildAbandonFold(ordered);
8908
8927
  const dispatchesByScope = /* @__PURE__ */ new Map();
8909
8928
  const terminalsByScopeKey = /* @__PURE__ */ new Map();
8910
8929
  for (const entry of ordered) {
@@ -8950,6 +8969,7 @@ function childRostersFromJournal(entries) {
8950
8969
  const terminal = terminalsByScopeKey.get(JSON.stringify([childScope, dispatch.key]))?.find((candidate) => candidate.seq > dispatch.seq);
8951
8970
  roster.children.push({
8952
8971
  handle: dispatch.seq,
8972
+ ...abandoned.isAbandoned(dispatch.seq) ? { abandoned: true } : {},
8953
8973
  ...terminal?.costAttribution?.agentType === void 0 ? {} : { agentType: terminal.costAttribution.agentType },
8954
8974
  ...terminal === void 0 ? {} : { status: terminal.status },
8955
8975
  ...terminal?.evidence === void 0 ? {} : { evidence: { ...terminal.evidence } }
@@ -9071,455 +9091,1021 @@ async function reconcileRunMeta(store, runId, opts) {
9071
9091
  };
9072
9092
  }
9073
9093
  //#endregion
9074
- //#region src/stores/jsonl.ts
9075
- /**
9076
- * JsonlFileStore (M2-T01): the durable file store. One JSON entry per
9077
- * line per run; the journal doubles as an event log. Meta records live
9078
- * beside the journal and are replaced atomically, so listRuns never
9079
- * parses payloads.
9080
- *
9081
- * Contract (DEF-4 tightening):
9082
- * - A1 atomicity: a torn trailing line (crash mid-append) is never
9083
- * visible in load; the incomplete fragment is dropped and overwritten
9084
- * by the next append. Whole records on that line are data, never
9085
- * fragment (RV701): a crash that persisted every JSON byte but not
9086
- * the '\n' leaves a parseable tail that load serves and append
9087
- * terminates before writing, and repair salvages complete records a
9088
- * glued line carries instead of discarding the line, so an entry a
9089
- * load has served can never be un-served by a later repair.
9090
- * - A2 total per-run order: load returns append order, stable across
9091
- * calls (the kernel's per-run queue serializes appends).
9092
- * - A3 read-your-writes: append resolves after the line is written.
9093
- * - A4 opaque payload: entries round-trip byte-for-byte as JSON; unknown
9094
- * kinds and fields pass through untouched.
9095
- *
9096
- * Leasing is NOT implemented here: LeasableStore ships with
9097
- * @rulvar/store-sqlite (M5); JsonlFileStore is single-writer by
9098
- * convention.
9099
- */
9100
- const JOURNAL_SUFFIX = ".jsonl";
9101
- const META_SUFFIX = ".meta.json";
9102
- function safeName(runId) {
9103
- if (!/^[A-Za-z0-9._-]+$/.test(runId)) throw new JournalOrderViolation(`JsonlFileStore: runId '${runId}' is not filesystem-safe ([A-Za-z0-9._-] only)`);
9104
- return runId;
9105
- }
9094
+ //#region src/l0/telemetry-reduce.ts
9095
+ const ZERO = {
9096
+ inputTokens: 0,
9097
+ outputTokens: 0,
9098
+ cacheReadTokens: 0,
9099
+ cacheWriteTokens: 0
9100
+ };
9106
9101
  /**
9107
- * Whole JSON values glued on one line, split apart without parser
9108
- * ambiguity (RV701): depth is tracked outside string literals only, and
9109
- * every candidate must still round-trip JSON.parse. A line that is not a
9110
- * clean concatenation from its first byte salvages its whole prefix
9111
- * values and returns everything after them as the torn fragment, so the
9112
- * caller keeps accepted records and drops exactly the unacknowledged
9113
- * tail a crash tore.
9102
+ * Reduces one run's event stream (or any slice of it) to the invocation
9103
+ * table. Feed it the events in emission order; both a live stream and a
9104
+ * replayed one produce the same usage and cost columns.
9114
9105
  */
9115
- function splitConcatenatedJson(line) {
9116
- const whole = [];
9117
- let start = 0;
9118
- let depth = 0;
9119
- let inString = false;
9120
- let escaped = false;
9121
- for (let i = 0; i < line.length; i += 1) {
9122
- const ch = line[i];
9123
- if (inString) {
9124
- if (escaped) escaped = false;
9125
- else if (ch === "\\") escaped = true;
9126
- else if (ch === "\"") inString = false;
9127
- continue;
9128
- }
9129
- if (ch === "\"") {
9130
- inString = true;
9131
- continue;
9106
+ function reduceInvocationTable(events) {
9107
+ const rows = /* @__PURE__ */ new Map();
9108
+ const order = [];
9109
+ const openPhases = /* @__PURE__ */ new Map();
9110
+ const byRole = {};
9111
+ let totalCostUsd = 0;
9112
+ const rowFor = (event) => {
9113
+ let row = rows.get(event.spanId);
9114
+ if (row === void 0) {
9115
+ row = {
9116
+ spanId: event.spanId,
9117
+ agentType: event.agentType,
9118
+ ...event.label === void 0 ? {} : { label: event.label },
9119
+ usage: ZERO,
9120
+ costUsd: 0,
9121
+ costBasis: "aggregate-estimate",
9122
+ usageApprox: false,
9123
+ retryCount: 0,
9124
+ replayed: event.replayed === true,
9125
+ open: true,
9126
+ phases: []
9127
+ };
9128
+ rows.set(event.spanId, row);
9129
+ order.push(row);
9132
9130
  }
9133
- if (ch === "{" || ch === "[") {
9134
- depth += 1;
9135
- continue;
9131
+ return row;
9132
+ };
9133
+ for (const event of events) switch (event.type) {
9134
+ case "agent:start": {
9135
+ const row = rowFor(event);
9136
+ row.role = event.role;
9137
+ break;
9136
9138
  }
9137
- if (ch === "}" || ch === "]") {
9138
- depth -= 1;
9139
- if (depth < 0) return {
9140
- whole,
9141
- fragment: line.slice(start)
9139
+ case "agent:phase:start": {
9140
+ const row = rowFor(event);
9141
+ const phase = {
9142
+ invocation: event.invocation,
9143
+ role: event.role,
9144
+ model: event.model,
9145
+ durationMs: 0,
9146
+ usage: ZERO,
9147
+ costUsd: 0,
9148
+ costBasis: "aggregate-estimate",
9149
+ retries: 0,
9150
+ replayed: event.replayed === true,
9151
+ open: true
9142
9152
  };
9143
- if (depth === 0) {
9144
- const candidate = line.slice(start, i + 1);
9145
- try {
9146
- whole.push(JSON.parse(candidate));
9147
- } catch {
9148
- return {
9149
- whole,
9150
- fragment: line.slice(start)
9151
- };
9152
- }
9153
- start = i + 1;
9153
+ row.phases.push(phase);
9154
+ openPhases.set(`${event.spanId}#${event.invocation}`, phase);
9155
+ break;
9156
+ }
9157
+ case "agent:phase:end": {
9158
+ const key = `${event.spanId}#${event.invocation}`;
9159
+ let phase = openPhases.get(key);
9160
+ if (phase === void 0) {
9161
+ phase = {
9162
+ invocation: event.invocation,
9163
+ role: event.role,
9164
+ model: event.model,
9165
+ durationMs: 0,
9166
+ usage: ZERO,
9167
+ costUsd: 0,
9168
+ costBasis: "aggregate-estimate",
9169
+ retries: 0,
9170
+ replayed: event.replayed === true,
9171
+ open: true
9172
+ };
9173
+ rowFor(event).phases.push(phase);
9154
9174
  }
9175
+ openPhases.delete(key);
9176
+ phase.open = false;
9177
+ phase.role = event.role;
9178
+ phase.model = event.model;
9179
+ phase.durationMs = event.durationMs;
9180
+ phase.usage = event.usage;
9181
+ phase.costUsd = event.costUsd;
9182
+ phase.costBasis = event.costBasis ?? "aggregate-estimate";
9183
+ phase.outcome = event.outcome;
9184
+ phase.retries = event.retries ?? 0;
9185
+ const bucket = byRole[event.role] ??= {
9186
+ usage: ZERO,
9187
+ costUsd: 0,
9188
+ costBasis: "per-call"
9189
+ };
9190
+ bucket.usage = sumUsage(bucket.usage, event.usage);
9191
+ bucket.costUsd += event.costUsd;
9192
+ if (phase.costBasis === "aggregate-estimate") bucket.costBasis = "aggregate-estimate";
9193
+ break;
9194
+ }
9195
+ case "agent:end": {
9196
+ const row = rowFor(event);
9197
+ row.open = false;
9198
+ row.status = event.status;
9199
+ row.usage = event.usage;
9200
+ row.costUsd = event.costUsd;
9201
+ row.costBasis = event.costBasis ?? "aggregate-estimate";
9202
+ row.usageApprox = event.usageApprox === true;
9203
+ row.retryCount = event.retryCount ?? 0;
9204
+ if (event.toolBudget !== void 0) row.toolBudget = event.toolBudget;
9205
+ totalCostUsd += event.costUsd;
9206
+ break;
9155
9207
  }
9208
+ default: break;
9156
9209
  }
9157
9210
  return {
9158
- whole,
9159
- fragment: line.slice(start)
9211
+ agents: order,
9212
+ byRole,
9213
+ totalCostUsd
9160
9214
  };
9161
9215
  }
9162
- var JsonlFileStore = class {
9163
- dir;
9164
- /**
9165
- * The stored tail seq per run, lazily initialized from the file on the
9166
- * first append this instance performs (obligation A5). Per instance by
9167
- * design: cross-process writers are the lease seam's job.
9168
- */
9169
- lastSeq = /* @__PURE__ */ new Map();
9170
- /**
9171
- * The verify-only load switch (RV1512): with `repairOnLoad: false`,
9172
- * `load` serves the salvageable records WITHOUT rewriting the file,
9173
- * so an auditor's "verification" read never destroys the evidence
9174
- * of a tear it found. The default keeps the owner semantics byte
9175
- * for byte: a torn tail repairs on load exactly as documented in
9176
- * the A1 model above. Mutations (`append`, `putMeta`, `delete`)
9177
- * are unaffected by the flag; an auditor that must not write simply
9178
- * does not call them.
9179
- */
9180
- repairOnLoad;
9181
- constructor(options) {
9182
- this.dir = options.dir;
9183
- this.repairOnLoad = options.repairOnLoad !== false;
9184
- mkdirSync(this.dir, { recursive: true });
9185
- }
9186
- journalPath(runId) {
9187
- return join(this.dir, `${safeName(runId)}${JOURNAL_SUFFIX}`);
9188
- }
9189
- metaPath(runId) {
9190
- return join(this.dir, `${safeName(runId)}${META_SUFFIX}`);
9191
- }
9192
- async append(runId, e) {
9193
- let tail = this.lastSeq.get(runId);
9194
- if (tail === void 0) {
9195
- const existing = await this.load(runId);
9196
- this.terminateUnterminatedTail(runId);
9197
- const last = existing[existing.length - 1];
9198
- tail = last !== void 0 && Number.isFinite(last.seq) ? last.seq : Number.NEGATIVE_INFINITY;
9199
- this.lastSeq.set(runId, tail);
9200
- }
9201
- if (Number.isFinite(e.seq) && e.seq <= tail) throw new JournalOrderViolation(`JsonlFileStore: append of seq ${e.seq} to run '${runId}' is not after the stored tail seq ${tail}; a concurrent writer raced this journal from a stale tail`);
9202
- appendFileSync(this.journalPath(runId), `${JSON.stringify(e)}\n`, "utf8");
9203
- if (Number.isFinite(e.seq)) this.lastSeq.set(runId, e.seq);
9204
- }
9205
- async load(runId) {
9206
- let raw;
9207
- try {
9208
- raw = readFileSync(this.journalPath(runId), "utf8");
9209
- } catch (thrown) {
9210
- if (thrown.code === "ENOENT") return [];
9211
- throw thrown;
9212
- }
9213
- const lines = raw.split("\n");
9214
- const entries = [];
9215
- for (let i = 0; i < lines.length; i += 1) {
9216
- const line = lines[i] ?? "";
9217
- if (line === "") continue;
9218
- try {
9219
- entries.push(JSON.parse(line));
9220
- } catch (thrown) {
9221
- if (lines.slice(i + 1).every((rest) => rest === "")) {
9222
- for (const value of splitConcatenatedJson(line).whole) entries.push(value);
9223
- if (this.repairOnLoad) this.repairTornTail(runId, entries);
9224
- break;
9225
- }
9226
- throw new JournalOrderViolation(`JsonlFileStore: corrupt journal line ${i + 1} of run '${runId}' (not the trailing line, so this is not a torn append)`, { cause: thrown });
9227
- }
9228
- }
9229
- return entries;
9230
- }
9231
- /**
9232
- * Restores the trailing '\n' of a parseable-but-unterminated tail
9233
- * (RV701). One byte appended in place terminates the record exactly
9234
- * where the crash left it; the file's bytes before it stay untouched.
9235
- * No-op on a missing, empty, or already-terminated journal.
9236
- */
9237
- terminateUnterminatedTail(runId) {
9238
- const path = this.journalPath(runId);
9239
- let fd;
9240
- try {
9241
- fd = openSync(path, "r");
9242
- } catch (thrown) {
9243
- if (thrown.code === "ENOENT") return;
9244
- throw thrown;
9245
- }
9246
- let needsNewline = false;
9247
- try {
9248
- const size = fstatSync(fd).size;
9249
- if (size > 0) {
9250
- const lastByte = /* @__PURE__ */ new Uint8Array(1);
9251
- readSync(fd, lastByte, 0, 1, size - 1);
9252
- needsNewline = lastByte[0] !== 10;
9216
+ /**
9217
+ * The label the claim-consistency judge invocation dispatches under
9218
+ * (RV1502; named here since RV1604 so the critical-path reducer and the
9219
+ * orchestrator share one constant): the judge rides role 'synthesize',
9220
+ * and this label is what tells its wall apart from a real final
9221
+ * composition in {@link reduceCriticalPath}.
9222
+ */
9223
+ const CLAIM_JUDGE_LABEL = "claim-consistency-judge";
9224
+ /**
9225
+ * The label the final synthesis (composition) invocation dispatches
9226
+ * under (RV2901). The engine labelling its OWN dispatches is what lets
9227
+ * `criticalPathFromJournal` split the synthesize bucket offline: the
9228
+ * split demands a label on EVERY synthesize span, and the comparison
9229
+ * run that shipped the journal fold still refused it because this one
9230
+ * dispatch stayed anonymous while the claim judge was labelled.
9231
+ */
9232
+ const FINAL_COMPOSITION_LABEL = "final-composition";
9233
+ /**
9234
+ * The label an incremental synthesis note dispatches under (RV2901).
9235
+ * Notes ride role 'synthesize' and are composition-side work, so both
9236
+ * reducers count them toward the composition half of the split; the
9237
+ * label exists so a journal reader can tell WHICH composition spans
9238
+ * were notes without guessing from their size.
9239
+ */
9240
+ const SYNTHESIS_NOTE_LABEL = "synthesis-note";
9241
+ /** Total length of the union of possibly overlapping intervals. */
9242
+ function unionLength(intervals) {
9243
+ const positive = intervals.filter((interval) => interval.to > interval.from);
9244
+ if (positive.length === 0) return 0;
9245
+ const sorted = [...positive].sort((a, b) => a.from - b.from);
9246
+ let total = 0;
9247
+ let from = sorted[0]?.from ?? 0;
9248
+ let to = sorted[0]?.to ?? 0;
9249
+ for (const interval of sorted.slice(1)) if (interval.from > to) {
9250
+ total += to - from;
9251
+ from = interval.from;
9252
+ to = interval.to;
9253
+ } else if (interval.to > to) to = interval.to;
9254
+ return total + (to - from);
9255
+ }
9256
+ function reduceCriticalPath(events) {
9257
+ let runStart;
9258
+ let runEnd;
9259
+ const startBySpan = /* @__PURE__ */ new Map();
9260
+ let lastWorkerEnd;
9261
+ let workerSpans = 0;
9262
+ let synthesisMs = 0;
9263
+ let finalCompositionMs = 0;
9264
+ let semanticJudgeMs = 0;
9265
+ const coordinationModel = [];
9266
+ const coordinationTools = [];
9267
+ const synthesisSpans = [];
9268
+ const spanOf = (durationMs) => Number.isFinite(durationMs) && durationMs > 0 ? durationMs : 0;
9269
+ for (const event of events) {
9270
+ const at = Date.parse(event.ts);
9271
+ if (!Number.isFinite(at)) continue;
9272
+ switch (event.type) {
9273
+ case "run:start":
9274
+ runStart ??= at;
9275
+ break;
9276
+ case "run:end":
9277
+ runEnd = at;
9278
+ break;
9279
+ case "agent:start":
9280
+ startBySpan.set(event.spanId, {
9281
+ role: event.role,
9282
+ at,
9283
+ ...event.label === void 0 ? {} : { label: event.label }
9284
+ });
9285
+ break;
9286
+ case "agent:phase:end":
9287
+ if (startBySpan.get(event.spanId)?.role === "orchestrate") coordinationModel.push({
9288
+ phase: event.role,
9289
+ from: at - spanOf(event.durationMs),
9290
+ to: at
9291
+ });
9292
+ break;
9293
+ case "tool:end":
9294
+ if (startBySpan.get(event.spanId)?.role === "orchestrate") coordinationTools.push({
9295
+ name: event.toolName,
9296
+ from: at - spanOf(event.durationMs),
9297
+ to: at
9298
+ });
9299
+ break;
9300
+ case "agent:end": {
9301
+ const started = startBySpan.get(event.spanId);
9302
+ if (started === void 0) break;
9303
+ if (started.role === "synthesize") {
9304
+ const wall = Math.max(0, at - started.at);
9305
+ const judge = started.label === CLAIM_JUDGE_LABEL;
9306
+ synthesisMs += wall;
9307
+ if (judge) semanticJudgeMs += wall;
9308
+ else finalCompositionMs += wall;
9309
+ synthesisSpans.push({
9310
+ from: started.at,
9311
+ to: at,
9312
+ judge
9313
+ });
9314
+ } else if (started.role !== "orchestrate") {
9315
+ workerSpans += 1;
9316
+ lastWorkerEnd = lastWorkerEnd === void 0 ? at : Math.max(lastWorkerEnd, at);
9317
+ }
9318
+ break;
9253
9319
  }
9254
- } finally {
9255
- closeSync(fd);
9320
+ default: break;
9256
9321
  }
9257
- if (needsNewline) appendFileSync(path, "\n", "utf8");
9258
- }
9259
- repairTornTail(runId, whole) {
9260
- const path = this.journalPath(runId);
9261
- const temp = `${path}.tmp`;
9262
- writeFileSync(temp, whole.map((entry) => JSON.stringify(entry)).join("\n") + (whole.length > 0 ? "\n" : ""), "utf8");
9263
- renameSync(temp, path);
9264
- }
9265
- async putMeta(m) {
9266
- const path = this.metaPath(m.runId);
9267
- const temp = `${path}.tmp`;
9268
- writeFileSync(temp, JSON.stringify(m, null, 2), "utf8");
9269
- renameSync(temp, path);
9270
9322
  }
9271
- async getMeta(runId) {
9272
- try {
9273
- return JSON.parse(readFileSync(this.metaPath(runId), "utf8"));
9274
- } catch {
9275
- return;
9323
+ const path = {
9324
+ synthesisMs,
9325
+ finalCompositionMs,
9326
+ semanticJudgeMs,
9327
+ workerSpans
9328
+ };
9329
+ if (runStart !== void 0 && runEnd !== void 0) path.runWallMs = Math.max(0, runEnd - runStart);
9330
+ if (runEnd !== void 0 && lastWorkerEnd !== void 0) {
9331
+ path.postFanInMs = Math.max(0, runEnd - lastWorkerEnd);
9332
+ const windowFrom = Math.min(lastWorkerEnd, runEnd);
9333
+ const windowTo = runEnd;
9334
+ const clip = (interval) => {
9335
+ if (interval.to < windowFrom || interval.from > windowTo) return;
9336
+ return {
9337
+ from: Math.max(interval.from, windowFrom),
9338
+ to: Math.min(interval.to, windowTo)
9339
+ };
9340
+ };
9341
+ const byPhase = {};
9342
+ const modelClipped = [];
9343
+ for (const interval of coordinationModel) {
9344
+ const clipped = clip(interval);
9345
+ if (clipped === void 0) continue;
9346
+ byPhase[interval.phase] = (byPhase[interval.phase] ?? 0) + (clipped.to - clipped.from);
9347
+ modelClipped.push(clipped);
9276
9348
  }
9277
- }
9278
- async listRuns(f) {
9279
- const metas = [];
9280
- for (const file of readdirSync(this.dir)) {
9281
- if (!file.endsWith(META_SUFFIX)) continue;
9282
- try {
9283
- metas.push(JSON.parse(readFileSync(join(this.dir, file), "utf8")));
9284
- } catch {}
9349
+ const synthesisClipped = [];
9350
+ let judgeClippedMs = 0;
9351
+ let compositionClippedMs = 0;
9352
+ for (const span of synthesisSpans) {
9353
+ const clipped = clip(span);
9354
+ if (clipped === void 0) continue;
9355
+ synthesisClipped.push(clipped);
9356
+ if (span.judge) judgeClippedMs += clipped.to - clipped.from;
9357
+ else compositionClippedMs += clipped.to - clipped.from;
9285
9358
  }
9286
- return metas.filter((meta) => metaMatchesFilter(meta, f));
9359
+ const byName = {};
9360
+ const callsByName = {};
9361
+ const toolsClipped = [];
9362
+ for (const interval of coordinationTools) {
9363
+ const clipped = clip(interval);
9364
+ if (clipped === void 0) continue;
9365
+ byName[interval.name] = (byName[interval.name] ?? 0) + (clipped.to - clipped.from);
9366
+ callsByName[interval.name] = (callsByName[interval.name] ?? 0) + 1;
9367
+ toolsClipped.push(clipped);
9368
+ }
9369
+ const lengthOf = (intervals) => intervals.reduce((sum, interval) => sum + (interval.to - interval.from), 0);
9370
+ const coveredMs = unionLength([
9371
+ ...modelClipped,
9372
+ ...toolsClipped,
9373
+ ...synthesisClipped
9374
+ ]);
9375
+ const modelOnlyMs = unionLength([...modelClipped, ...toolsClipped]) - unionLength(toolsClipped);
9376
+ const breakdown = {
9377
+ coordinationModelMs: lengthOf(modelClipped),
9378
+ coordinationModelMsByPhase: byPhase,
9379
+ coordinationModelOnlyMs: modelOnlyMs,
9380
+ coordinationToolMs: lengthOf(toolsClipped),
9381
+ coordinationToolMsByName: byName,
9382
+ coordinationToolCallsByName: callsByName,
9383
+ synthesisMs: lengthOf(synthesisClipped),
9384
+ finalCompositionMs: compositionClippedMs,
9385
+ semanticJudgeMs: judgeClippedMs,
9386
+ coveredMs,
9387
+ residueMs: Math.max(0, path.postFanInMs - coveredMs)
9388
+ };
9389
+ if (path.postFanInMs > 0) breakdown.residueShare = breakdown.residueMs / path.postFanInMs;
9390
+ path.postFanIn = breakdown;
9287
9391
  }
9288
- async delete(runId) {
9289
- rmSync(this.journalPath(runId), { force: true });
9290
- rmSync(this.metaPath(runId), { force: true });
9291
- this.lastSeq.delete(runId);
9392
+ if (path.runWallMs !== void 0 && path.runWallMs > 0) {
9393
+ if (path.postFanInMs !== void 0) path.postFanInShare = path.postFanInMs / path.runWallMs;
9394
+ path.synthesisShare = synthesisMs / path.runWallMs;
9292
9395
  }
9396
+ return path;
9397
+ }
9398
+ //#endregion
9399
+ //#region src/stores/critical-path.ts
9400
+ const parse$1 = (at) => {
9401
+ if (at === void 0) return;
9402
+ const ms = Date.parse(at);
9403
+ return Number.isFinite(ms) ? ms : void 0;
9293
9404
  };
9294
- const TRANSCRIPT_SUFFIX = ".bin";
9295
9405
  /**
9296
- * File-backed TranscriptStore (M6-T02): blobs (transcripts, checkpoints,
9297
- * persisted CompiledWorkflow sources) as one file per ref under `dir`,
9298
- * so compiled runs resume across processes. Refs follow the
9299
- * `<runId>/<name>` convention; nested segments become directories.
9406
+ * Fold a run's critical path out of its journal.
9300
9407
  *
9301
- * Every ref is contained under `dir` (v1.36.0 review SEC-P1): each
9302
- * segment must match `[A-Za-z0-9._-]` and be neither empty, '.', nor
9303
- * '..', and the resolved path must stay under the resolved root. A '..'
9304
- * segment used to pass the per-segment alphabet (dots are in it) and, via
9305
- * `join`, escape the root; a caller passing an untrusted ref (or an
9306
- * untrusted runId, which prefixes checkpoint and workflow-source refs)
9307
- * could read, write, or delete `.bin` files outside `dir`.
9408
+ * @param entries the journal of one run, in any order
9308
9409
  */
9309
- var FileTranscriptStore = class {
9310
- dir;
9311
- constructor(options) {
9312
- this.dir = options.dir;
9313
- mkdirSync(this.dir, { recursive: true });
9410
+ function criticalPathFromJournal(entries) {
9411
+ const ordered = [...entries].sort((a, b) => a.seq - b.seq);
9412
+ let runStart;
9413
+ let runEnd;
9414
+ let lastWorkerEnd;
9415
+ let workerSpans = 0;
9416
+ let unclassifiedSpans = 0;
9417
+ let synthesisMs = 0;
9418
+ let finalCompositionMs = 0;
9419
+ let semanticJudgeMs = 0;
9420
+ let labelledSynthesis = false;
9421
+ let unlabelledSynthesis = false;
9422
+ for (const entry of ordered) {
9423
+ const startedAt = parse$1(entry.startedAt);
9424
+ const endedAt = parse$1(entry.endedAt);
9425
+ if (startedAt !== void 0) runStart = runStart === void 0 ? startedAt : Math.min(runStart, startedAt);
9426
+ const last = endedAt ?? startedAt;
9427
+ if (last !== void 0) runEnd = runEnd === void 0 ? last : Math.max(runEnd, last);
9428
+ if (entry.kind !== "agent" || entry.status === "running" || entry.status === "suspended") continue;
9429
+ const role = entry.costAttribution?.role;
9430
+ if (role === void 0) {
9431
+ unclassifiedSpans += 1;
9432
+ continue;
9433
+ }
9434
+ if (role === "orchestrate") continue;
9435
+ if (role !== "synthesize") {
9436
+ workerSpans += 1;
9437
+ if (endedAt !== void 0) lastWorkerEnd = lastWorkerEnd === void 0 ? endedAt : Math.max(lastWorkerEnd, endedAt);
9438
+ continue;
9439
+ }
9440
+ if (startedAt === void 0 || endedAt === void 0) continue;
9441
+ const wall = Math.max(0, endedAt - startedAt);
9442
+ synthesisMs += wall;
9443
+ const label = entry.costAttribution?.label;
9444
+ if (label === void 0) {
9445
+ unlabelledSynthesis = true;
9446
+ continue;
9447
+ }
9448
+ labelledSynthesis = true;
9449
+ if (label === "claim-consistency-judge" || label.startsWith(`claim-consistency-judge-`)) semanticJudgeMs += wall;
9450
+ else finalCompositionMs += wall;
9314
9451
  }
9315
- blobPath(ref) {
9316
- const segments = ref.split("/");
9317
- for (const segment of segments) if (segment === "" || segment === "." || segment === ".." || !/^[A-Za-z0-9._-]+$/.test(segment)) throw new JournalOrderViolation(`FileTranscriptStore: ref segment '${segment}' is not filesystem-safe`);
9318
- const name = segments.pop() ?? "";
9319
- const path = join(this.dir, ...segments, `${name}${TRANSCRIPT_SUFFIX}`);
9320
- const root = resolve(this.dir);
9321
- const resolved = resolve(path);
9322
- if (resolved !== root && !resolved.startsWith(`${root}${sep}`)) throw new JournalOrderViolation(`FileTranscriptStore: ref '${ref}' resolves outside the configured root`);
9323
- return path;
9452
+ const segments = logicalRunTelemetry(ordered).segments;
9453
+ const path = {
9454
+ workerSpans,
9455
+ synthesisMs,
9456
+ unclassifiedSpans,
9457
+ segments
9458
+ };
9459
+ if (labelledSynthesis && !unlabelledSynthesis) {
9460
+ path.finalCompositionMs = finalCompositionMs;
9461
+ path.semanticJudgeMs = semanticJudgeMs;
9462
+ }
9463
+ if (segments > 1 || runStart === void 0 || runEnd === void 0) return path;
9464
+ path.runWallMs = Math.max(0, runEnd - runStart);
9465
+ if (lastWorkerEnd !== void 0) path.postFanInMs = Math.max(0, runEnd - lastWorkerEnd);
9466
+ if (path.runWallMs > 0) {
9467
+ if (path.postFanInMs !== void 0) path.postFanInShare = path.postFanInMs / path.runWallMs;
9468
+ path.synthesisShare = synthesisMs / path.runWallMs;
9324
9469
  }
9325
- async put(ref, blob) {
9326
- const path = this.blobPath(ref);
9327
- mkdirSync(dirname(path), { recursive: true });
9328
- const temp = `${path}.tmp`;
9329
- writeFileSync(temp, blob);
9330
- renameSync(temp, path);
9470
+ return path;
9471
+ }
9472
+ //#endregion
9473
+ //#region src/stores/synthesis-candidates.ts
9474
+ const parse = (at) => {
9475
+ if (at === void 0) return;
9476
+ const ms = Date.parse(at);
9477
+ return Number.isFinite(ms) ? ms : void 0;
9478
+ };
9479
+ const VERDICTS = /* @__PURE__ */ new Set([
9480
+ "accepted",
9481
+ "repair",
9482
+ "rejected"
9483
+ ]);
9484
+ const OPTIONAL_USAGE_KEYS = [
9485
+ "reasoningTokens",
9486
+ "cacheWrite5mTokens",
9487
+ "cacheWrite1hTokens"
9488
+ ];
9489
+ function sumUsage$1(rows) {
9490
+ const total = {
9491
+ inputTokens: 0,
9492
+ outputTokens: 0,
9493
+ cacheReadTokens: 0,
9494
+ cacheWriteTokens: 0
9495
+ };
9496
+ for (const row of rows) {
9497
+ if (row.usage === void 0) continue;
9498
+ total.inputTokens += row.usage.inputTokens;
9499
+ total.outputTokens += row.usage.outputTokens;
9500
+ total.cacheReadTokens += row.usage.cacheReadTokens;
9501
+ total.cacheWriteTokens += row.usage.cacheWriteTokens;
9502
+ for (const key of OPTIONAL_USAGE_KEYS) {
9503
+ const share = row.usage[key];
9504
+ if (share !== void 0) total[key] = (total[key] ?? 0) + share;
9505
+ }
9506
+ }
9507
+ return total;
9508
+ }
9509
+ const usageUnknown = (row) => {
9510
+ if (row.outcome === "ok") return false;
9511
+ const usage = row.usage;
9512
+ if (usage === void 0) return true;
9513
+ return usage.inputTokens === 0 && usage.outputTokens === 0 && usage.cacheReadTokens === 0 && usage.cacheWriteTokens === 0 && (usage.reasoningTokens ?? 0) === 0;
9514
+ };
9515
+ /**
9516
+ * Fold the finish candidates (RV2902) out of a run's journal: each
9517
+ * journaled validation verdict with the window of wall, wires, usage,
9518
+ * and priced cost that produced the candidate it judged.
9519
+ *
9520
+ * @param entries the journal of one run, in any order
9521
+ * @param priceUsd prices one call's usage at its serving model, the
9522
+ * same shape `invoiceFromJournal` takes; omit to fold without money
9523
+ */
9524
+ function synthesisCandidatesFromJournal(entries, priceUsd) {
9525
+ const ordered = [...entries].sort((a, b) => a.seq - b.seq);
9526
+ const spans = [];
9527
+ for (const entry of ordered) {
9528
+ if (entry.kind !== "agent" || entry.status === "running" || entry.status === "suspended" || entry.costAttribution?.role !== "synthesize" || typeof entry.ref !== "number") continue;
9529
+ spans.push({
9530
+ runningSeq: entry.ref,
9531
+ terminalSeq: entry.seq,
9532
+ startedAt: parse(entry.startedAt),
9533
+ ...entry.costAttribution.label === void 0 ? {} : { label: entry.costAttribution.label },
9534
+ records: entry.providerCalls,
9535
+ wires: [],
9536
+ verdictSeqs: []
9537
+ });
9331
9538
  }
9332
- async get(ref) {
9333
- try {
9334
- return new Uint8Array(readFileSync(this.blobPath(ref)));
9335
- } catch (error) {
9336
- if (error.code === "ENOENT") return null;
9337
- throw error;
9539
+ const bySeqOpen = /* @__PURE__ */ new Map();
9540
+ for (const span of spans) bySeqOpen.set(span.runningSeq, span);
9541
+ const verdicts = [];
9542
+ for (const entry of ordered) {
9543
+ if (entry.kind !== "decision") continue;
9544
+ const value = entry.value;
9545
+ if (value === void 0) continue;
9546
+ if (value.decisionType === "provider-call") {
9547
+ const wire = value;
9548
+ if (typeof wire.agentRef !== "number") continue;
9549
+ const span = bySeqOpen.get(wire.agentRef);
9550
+ const record = wire.record;
9551
+ if (span === void 0 || record === void 0 || typeof record.ordinal !== "number") continue;
9552
+ span.wires.push({
9553
+ seq: entry.seq,
9554
+ ordinal: record.ordinal,
9555
+ ...typeof record.servedBy === "string" ? { servedBy: record.servedBy } : {},
9556
+ outcome: typeof record.outcome === "string" ? record.outcome : "ok",
9557
+ ...record.usage === void 0 ? {} : { usage: record.usage },
9558
+ wireRequests: typeof record.wireRequests === "number" ? record.wireRequests : 1
9559
+ });
9560
+ continue;
9561
+ }
9562
+ if (value.decisionType !== "orchestrator_finish_validation") continue;
9563
+ const verdictValue = value;
9564
+ if (typeof verdictValue.verdict !== "string" || !VERDICTS.has(verdictValue.verdict)) continue;
9565
+ let host;
9566
+ for (const span of spans) if (entry.seq > span.runningSeq && entry.seq < span.terminalSeq) {
9567
+ if (host === void 0 || span.runningSeq > host.runningSeq) host = span;
9338
9568
  }
9569
+ if (host !== void 0) host.verdictSeqs.push(entry.seq);
9570
+ verdicts.push({
9571
+ seq: entry.seq,
9572
+ ...entry.startedAt === void 0 ? {} : { at: entry.startedAt },
9573
+ value: verdictValue,
9574
+ ...host === void 0 ? {} : { span: host }
9575
+ });
9339
9576
  }
9340
- async list(runId) {
9341
- if (runId === "." || runId === "..") throw new JournalOrderViolation(`FileTranscriptStore: runId '${runId}' is not filesystem-safe`);
9342
- const root = join(this.dir, safeName(runId));
9343
- const refs = [];
9344
- const walk = (dir, prefix) => {
9345
- let names;
9346
- try {
9347
- names = readdirSync(dir);
9348
- } catch {
9349
- return;
9350
- }
9351
- for (const name of names) {
9352
- const path = join(dir, name);
9353
- if (statSync(path).isDirectory()) walk(path, `${prefix}${name}/`);
9354
- else if (name.endsWith(TRANSCRIPT_SUFFIX)) refs.push(`${prefix}${name.slice(0, -4)}`);
9355
- }
9356
- };
9357
- walk(root, `${runId}/`);
9358
- return refs.sort();
9577
+ const attributable = /* @__PURE__ */ new Set();
9578
+ let unattributedSpans = 0;
9579
+ for (const span of spans) {
9580
+ const recorded = (span.records ?? []).map((record) => record.ordinal).sort((a, b) => a - b);
9581
+ const rows = [...span.wires].map((wire) => wire.ordinal).sort((a, b) => a - b);
9582
+ if (span.records !== void 0 && recorded.length === rows.length && recorded.every((ordinal, index) => ordinal === rows[index])) attributable.add(span);
9583
+ else unattributedSpans += 1;
9584
+ }
9585
+ let tailWires = 0;
9586
+ for (const span of spans) {
9587
+ if (!attributable.has(span)) continue;
9588
+ const lastVerdict = span.verdictSeqs.length === 0 ? void 0 : Math.max(...span.verdictSeqs);
9589
+ if (lastVerdict === void 0) continue;
9590
+ for (const wire of span.wires) if (wire.seq > lastVerdict) tailWires += wire.wireRequests;
9359
9591
  }
9360
- async delete(ref) {
9361
- try {
9362
- rmSync(this.blobPath(ref));
9363
- } catch (error) {
9364
- if (error.code !== "ENOENT") throw error;
9592
+ const candidates = [];
9593
+ let unhostedVerdicts = 0;
9594
+ const previousBoundary = /* @__PURE__ */ new Map();
9595
+ for (const verdict of verdicts) {
9596
+ const value = verdict.value;
9597
+ if (verdict.span === void 0) {
9598
+ unhostedVerdicts += 1;
9599
+ continue;
9365
9600
  }
9601
+ const span = verdict.span;
9602
+ const boundary = previousBoundary.get(span) ?? {
9603
+ seq: span.runningSeq,
9604
+ ...span.startedAt === void 0 ? {} : { at: span.startedAt }
9605
+ };
9606
+ const verdictAtMs = parse(verdict.at);
9607
+ previousBoundary.set(span, {
9608
+ seq: verdict.seq,
9609
+ ...verdictAtMs === void 0 ? {} : { at: verdictAtMs }
9610
+ });
9611
+ const candidate = {
9612
+ verdict: value.verdict,
9613
+ verdictSeq: verdict.seq,
9614
+ ...verdict.at === void 0 ? {} : { verdictAt: verdict.at },
9615
+ ...typeof value.callId === "string" ? { callId: value.callId } : {},
9616
+ ...typeof value.repairsUsed === "number" ? { repairsUsed: value.repairsUsed } : {},
9617
+ ...typeof value.maxRepairs === "number" ? { maxRepairs: value.maxRepairs } : {},
9618
+ ...typeof value.contractHash === "string" ? { contractHash: value.contractHash } : {},
9619
+ ...typeof value.candidateHash === "string" ? { candidateHash: value.candidateHash } : {},
9620
+ ...typeof value.candidateChars === "number" ? { candidateChars: value.candidateChars } : {},
9621
+ ...typeof value.candidateRef === "string" ? { candidateRef: value.candidateRef } : {},
9622
+ failed: Array.isArray(value.failed) ? value.failed.filter((failure) => typeof failure.name === "string").map((failure) => ({
9623
+ name: failure.name,
9624
+ reasons: Array.isArray(failure.reasons) ? failure.reasons.filter((reason) => typeof reason === "string") : []
9625
+ })) : [],
9626
+ ...span.label === void 0 ? {} : { spanLabel: span.label }
9627
+ };
9628
+ if (boundary.at !== void 0 && verdictAtMs !== void 0) candidate.windowMs = Math.max(0, verdictAtMs - boundary.at);
9629
+ if (attributable.has(span)) {
9630
+ const window = span.wires.filter((wire) => wire.seq > boundary.seq && wire.seq < verdict.seq);
9631
+ candidate.wires = window.reduce((sum, wire) => sum + wire.wireRequests, 0);
9632
+ candidate.usage = sumUsage$1(window);
9633
+ const unknown = window.filter((wire) => usageUnknown(wire)).length;
9634
+ if (unknown > 0) candidate.usageUnknownWires = unknown;
9635
+ if (priceUsd !== void 0) {
9636
+ let priced = 0;
9637
+ let complete = true;
9638
+ for (const wire of window) {
9639
+ const usd = wire.servedBy === void 0 || wire.usage === void 0 ? void 0 : priceUsd(wire.servedBy, wire.usage);
9640
+ if (usd === void 0) {
9641
+ complete = false;
9642
+ break;
9643
+ }
9644
+ priced += usd;
9645
+ }
9646
+ if (complete) candidate.costUsd = priced;
9647
+ }
9648
+ }
9649
+ candidates.push(candidate);
9366
9650
  }
9367
- };
9651
+ return {
9652
+ candidates,
9653
+ synthesisSpans: spans.length,
9654
+ unhostedVerdicts,
9655
+ unattributedSpans,
9656
+ tailWires
9657
+ };
9658
+ }
9368
9659
  //#endregion
9369
- //#region src/model/pricing.ts
9660
+ //#region src/stores/jsonl.ts
9370
9661
  /**
9371
- * Resolves the pricing for a model: the versioned table wins; the
9372
- * adapter-reported caps.pricing is the fallback; undefined means
9373
- * unpriced (the CostReport surfaces it, never a silent zero).
9662
+ * JsonlFileStore (M2-T01): the durable file store. One JSON entry per
9663
+ * line per run; the journal doubles as an event log. Meta records live
9664
+ * beside the journal and are replaced atomically, so listRuns never
9665
+ * parses payloads.
9666
+ *
9667
+ * Contract (DEF-4 tightening):
9668
+ * - A1 atomicity: a torn trailing line (crash mid-append) is never
9669
+ * visible in load; the incomplete fragment is dropped and overwritten
9670
+ * by the next append. Whole records on that line are data, never
9671
+ * fragment (RV701): a crash that persisted every JSON byte but not
9672
+ * the '\n' leaves a parseable tail that load serves and append
9673
+ * terminates before writing, and repair salvages complete records a
9674
+ * glued line carries instead of discarding the line, so an entry a
9675
+ * load has served can never be un-served by a later repair.
9676
+ * - A2 total per-run order: load returns append order, stable across
9677
+ * calls (the kernel's per-run queue serializes appends).
9678
+ * - A3 read-your-writes: append resolves after the line is written.
9679
+ * - A4 opaque payload: entries round-trip byte-for-byte as JSON; unknown
9680
+ * kinds and fields pass through untouched.
9681
+ *
9682
+ * Leasing is NOT implemented here: LeasableStore ships with
9683
+ * @rulvar/store-sqlite (M5); JsonlFileStore is single-writer by
9684
+ * convention.
9374
9685
  */
9375
- function resolvePricing(ref, table, capsPricing) {
9376
- return table?.models[ref] ?? capsPricing;
9377
- }
9378
- /** The tier a full prompt lands in: the highest threshold strictly below it. */
9379
- function tierFor(pricing, inputTokens) {
9380
- let tier;
9381
- for (const candidate of pricing.tiers ?? []) if (inputTokens > candidate.aboveInputTokens && (tier === void 0 || candidate.aboveInputTokens > tier.aboveInputTokens)) tier = candidate;
9382
- return tier;
9686
+ const JOURNAL_SUFFIX = ".jsonl";
9687
+ const META_SUFFIX = ".meta.json";
9688
+ function safeName(runId) {
9689
+ if (!/^[A-Za-z0-9._-]+$/.test(runId)) throw new JournalOrderViolation(`JsonlFileStore: runId '${runId}' is not filesystem-safe ([A-Za-z0-9._-] only)`);
9690
+ return runId;
9383
9691
  }
9384
9692
  /**
9385
- * Decomposes one usage against one pricing row into the four billing
9386
- * components. Under the Usage invariant inputTokens is the FULL prompt
9387
- * including cache reads and writes, so the input rate bills only the
9388
- * uncached remainder and cache tokens bill at their own rates, never
9389
- * twice; a row that omits a cache rate bills those tokens at the plain
9390
- * input rate rather than silently for free. A row may carry
9391
- * long-context tiers: the highest threshold strictly below the full
9392
- * prompt re-prices the ENTIRE request (input-side rates scale by
9393
- * inputMultiplier, the output rate by outputMultiplier). Cache writes
9394
- * price at the 5m premium rate by default; when the usage carries the
9395
- * TTL split (RV810: `cacheWrite5mTokens` and `cacheWrite1hTokens`,
9396
- * filled by adapters whose provider distinguishes write TTLs), the 1h
9397
- * share prices at `cacheWrite1hUsdPerMTok` (falling back to the plain
9398
- * write rate when the row lacks it) and everything the 1h share does
9399
- * not claim, the 5m share plus any unattributed remainder an upstream
9400
- * invariant violation left, bills at the write rate, never silently
9401
- * for free. The component's `tokens` stays the WHOLE
9402
- * `cacheWriteTokens` either way, so statement reconciliation keys are
9403
- * unchanged.
9693
+ * Whole JSON values glued on one line, split apart without parser
9694
+ * ambiguity (RV701): depth is tracked outside string literals only, and
9695
+ * every candidate must still round-trip JSON.parse. A line that is not a
9696
+ * clean concatenation from its first byte salvages its whole prefix
9697
+ * values and returns everything after them as the torn fragment, so the
9698
+ * caller keeps accepted records and drops exactly the unacknowledged
9699
+ * tail a crash tore.
9404
9700
  */
9405
- function priceComponentsOf(pricing, usage) {
9406
- const tier = tierFor(pricing, usage.inputTokens);
9407
- const inputMul = tier?.inputMultiplier ?? 1;
9408
- const outputMul = tier?.outputMultiplier ?? 1;
9409
- const uncachedInputTokens = Math.max(0, usage.inputTokens - usage.cacheReadTokens - usage.cacheWriteTokens);
9410
- const writeRate = pricing.cacheWriteUsdPerMTok ?? pricing.inputUsdPerMTok;
9411
- const write1hRate = pricing.cacheWrite1hUsdPerMTok ?? writeRate;
9412
- const write1hTokens = usage.cacheWrite5mTokens !== void 0 || usage.cacheWrite1hTokens !== void 0 ? usage.cacheWrite1hTokens ?? 0 : 0;
9413
- const writeDefaultTokens = Math.max(0, usage.cacheWriteTokens - write1hTokens);
9414
- return {
9415
- input: {
9416
- tokens: uncachedInputTokens,
9417
- usd: uncachedInputTokens / 1e6 * pricing.inputUsdPerMTok * inputMul
9418
- },
9419
- output: {
9420
- tokens: usage.outputTokens,
9421
- usd: usage.outputTokens / 1e6 * pricing.outputUsdPerMTok * outputMul
9422
- },
9423
- cachedInput: {
9424
- tokens: usage.cacheReadTokens,
9425
- usd: usage.cacheReadTokens / 1e6 * (pricing.cacheReadUsdPerMTok ?? pricing.inputUsdPerMTok) * inputMul
9426
- },
9427
- cacheWrite: {
9428
- tokens: usage.cacheWriteTokens,
9429
- usd: (writeDefaultTokens / 1e6 * writeRate + write1hTokens / 1e6 * write1hRate) * inputMul
9701
+ function splitConcatenatedJson(line) {
9702
+ const whole = [];
9703
+ let start = 0;
9704
+ let depth = 0;
9705
+ let inString = false;
9706
+ let escaped = false;
9707
+ for (let i = 0; i < line.length; i += 1) {
9708
+ const ch = line[i];
9709
+ if (inString) {
9710
+ if (escaped) escaped = false;
9711
+ else if (ch === "\\") escaped = true;
9712
+ else if (ch === "\"") inString = false;
9713
+ continue;
9714
+ }
9715
+ if (ch === "\"") {
9716
+ inString = true;
9717
+ continue;
9718
+ }
9719
+ if (ch === "{" || ch === "[") {
9720
+ depth += 1;
9721
+ continue;
9722
+ }
9723
+ if (ch === "}" || ch === "]") {
9724
+ depth -= 1;
9725
+ if (depth < 0) return {
9726
+ whole,
9727
+ fragment: line.slice(start)
9728
+ };
9729
+ if (depth === 0) {
9730
+ const candidate = line.slice(start, i + 1);
9731
+ try {
9732
+ whole.push(JSON.parse(candidate));
9733
+ } catch {
9734
+ return {
9735
+ whole,
9736
+ fragment: line.slice(start)
9737
+ };
9738
+ }
9739
+ start = i + 1;
9740
+ }
9430
9741
  }
9742
+ }
9743
+ return {
9744
+ whole,
9745
+ fragment: line.slice(start)
9431
9746
  };
9432
9747
  }
9748
+ var JsonlFileStore = class {
9749
+ dir;
9750
+ /**
9751
+ * The stored tail seq per run, lazily initialized from the file on the
9752
+ * first append this instance performs (obligation A5). Per instance by
9753
+ * design: cross-process writers are the lease seam's job.
9754
+ */
9755
+ lastSeq = /* @__PURE__ */ new Map();
9756
+ /**
9757
+ * The verify-only load switch (RV1512): with `repairOnLoad: false`,
9758
+ * `load` serves the salvageable records WITHOUT rewriting the file,
9759
+ * so an auditor's "verification" read never destroys the evidence
9760
+ * of a tear it found. The default keeps the owner semantics byte
9761
+ * for byte: a torn tail repairs on load exactly as documented in
9762
+ * the A1 model above. Mutations (`append`, `putMeta`, `delete`)
9763
+ * are unaffected by the flag; an auditor that must not write simply
9764
+ * does not call them.
9765
+ */
9766
+ repairOnLoad;
9767
+ constructor(options) {
9768
+ this.dir = options.dir;
9769
+ this.repairOnLoad = options.repairOnLoad !== false;
9770
+ mkdirSync(this.dir, { recursive: true });
9771
+ }
9772
+ journalPath(runId) {
9773
+ return join(this.dir, `${safeName(runId)}${JOURNAL_SUFFIX}`);
9774
+ }
9775
+ metaPath(runId) {
9776
+ return join(this.dir, `${safeName(runId)}${META_SUFFIX}`);
9777
+ }
9778
+ async append(runId, e) {
9779
+ let tail = this.lastSeq.get(runId);
9780
+ if (tail === void 0) {
9781
+ const existing = await this.load(runId);
9782
+ this.terminateUnterminatedTail(runId);
9783
+ const last = existing[existing.length - 1];
9784
+ tail = last !== void 0 && Number.isFinite(last.seq) ? last.seq : Number.NEGATIVE_INFINITY;
9785
+ this.lastSeq.set(runId, tail);
9786
+ }
9787
+ if (Number.isFinite(e.seq) && e.seq <= tail) throw new JournalOrderViolation(`JsonlFileStore: append of seq ${e.seq} to run '${runId}' is not after the stored tail seq ${tail}; a concurrent writer raced this journal from a stale tail`);
9788
+ appendFileSync(this.journalPath(runId), `${JSON.stringify(e)}\n`, "utf8");
9789
+ if (Number.isFinite(e.seq)) this.lastSeq.set(runId, e.seq);
9790
+ }
9791
+ async load(runId) {
9792
+ let raw;
9793
+ try {
9794
+ raw = readFileSync(this.journalPath(runId), "utf8");
9795
+ } catch (thrown) {
9796
+ if (thrown.code === "ENOENT") return [];
9797
+ throw thrown;
9798
+ }
9799
+ const lines = raw.split("\n");
9800
+ const entries = [];
9801
+ for (let i = 0; i < lines.length; i += 1) {
9802
+ const line = lines[i] ?? "";
9803
+ if (line === "") continue;
9804
+ try {
9805
+ entries.push(JSON.parse(line));
9806
+ } catch (thrown) {
9807
+ if (lines.slice(i + 1).every((rest) => rest === "")) {
9808
+ for (const value of splitConcatenatedJson(line).whole) entries.push(value);
9809
+ if (this.repairOnLoad) this.repairTornTail(runId, entries);
9810
+ break;
9811
+ }
9812
+ throw new JournalOrderViolation(`JsonlFileStore: corrupt journal line ${i + 1} of run '${runId}' (not the trailing line, so this is not a torn append)`, { cause: thrown });
9813
+ }
9814
+ }
9815
+ return entries;
9816
+ }
9817
+ /**
9818
+ * Restores the trailing '\n' of a parseable-but-unterminated tail
9819
+ * (RV701). One byte appended in place terminates the record exactly
9820
+ * where the crash left it; the file's bytes before it stay untouched.
9821
+ * No-op on a missing, empty, or already-terminated journal.
9822
+ */
9823
+ terminateUnterminatedTail(runId) {
9824
+ const path = this.journalPath(runId);
9825
+ let fd;
9826
+ try {
9827
+ fd = openSync(path, "r");
9828
+ } catch (thrown) {
9829
+ if (thrown.code === "ENOENT") return;
9830
+ throw thrown;
9831
+ }
9832
+ let needsNewline = false;
9833
+ try {
9834
+ const size = fstatSync(fd).size;
9835
+ if (size > 0) {
9836
+ const lastByte = /* @__PURE__ */ new Uint8Array(1);
9837
+ readSync(fd, lastByte, 0, 1, size - 1);
9838
+ needsNewline = lastByte[0] !== 10;
9839
+ }
9840
+ } finally {
9841
+ closeSync(fd);
9842
+ }
9843
+ if (needsNewline) appendFileSync(path, "\n", "utf8");
9844
+ }
9845
+ repairTornTail(runId, whole) {
9846
+ const path = this.journalPath(runId);
9847
+ const temp = `${path}.tmp`;
9848
+ writeFileSync(temp, whole.map((entry) => JSON.stringify(entry)).join("\n") + (whole.length > 0 ? "\n" : ""), "utf8");
9849
+ renameSync(temp, path);
9850
+ }
9851
+ async putMeta(m) {
9852
+ const path = this.metaPath(m.runId);
9853
+ const temp = `${path}.tmp`;
9854
+ writeFileSync(temp, JSON.stringify(m, null, 2), "utf8");
9855
+ renameSync(temp, path);
9856
+ }
9857
+ async getMeta(runId) {
9858
+ try {
9859
+ return JSON.parse(readFileSync(this.metaPath(runId), "utf8"));
9860
+ } catch {
9861
+ return;
9862
+ }
9863
+ }
9864
+ async listRuns(f) {
9865
+ const metas = [];
9866
+ for (const file of readdirSync(this.dir)) {
9867
+ if (!file.endsWith(META_SUFFIX)) continue;
9868
+ try {
9869
+ metas.push(JSON.parse(readFileSync(join(this.dir, file), "utf8")));
9870
+ } catch {}
9871
+ }
9872
+ return metas.filter((meta) => metaMatchesFilter(meta, f));
9873
+ }
9874
+ async delete(runId) {
9875
+ rmSync(this.journalPath(runId), { force: true });
9876
+ rmSync(this.metaPath(runId), { force: true });
9877
+ this.lastSeq.delete(runId);
9878
+ }
9879
+ };
9880
+ const TRANSCRIPT_SUFFIX = ".bin";
9433
9881
  /**
9434
- * Dollars from normalized usage against one pricing row: the sum of the
9435
- * {@link priceComponentsOf} terms in their declared order, byte for
9436
- * byte the historical expression (uncached input, output, cached input,
9437
- * cache writes).
9438
- */
9439
- function priceUsdOf(pricing, usage) {
9440
- const parts = priceComponentsOf(pricing, usage);
9441
- return parts.input.usd + parts.output.usd + parts.cachedInput.usd + parts.cacheWrite.usd;
9442
- }
9443
- /**
9444
- * The output tokens `remainingUsd` still buys from one pricing row after
9445
- * paying for an estimated prompt of `estimatedInputTokens`, priced with
9446
- * the same tier rules as settlement (the tier is selected by the
9447
- * estimated prompt). Floored to whole tokens; zero or negative means not
9448
- * even one output token fits, so the turn must not be dispatched.
9449
- * Undefined when the row prices output at zero (a free model needs no
9450
- * output bound).
9451
- */
9452
- function affordableOutputTokens(pricing, remainingUsd, estimatedInputTokens) {
9453
- const tier = tierFor(pricing, estimatedInputTokens);
9454
- const outputRate = pricing.outputUsdPerMTok * (tier?.outputMultiplier ?? 1);
9455
- if (outputRate <= 0) return;
9456
- const inputUsd = priceUsdOf(pricing, {
9457
- inputTokens: estimatedInputTokens,
9458
- outputTokens: 0,
9459
- cacheReadTokens: 0,
9460
- cacheWriteTokens: 0
9461
- });
9462
- return Math.floor((remainingUsd - inputUsd) / outputRate * 1e6);
9463
- }
9464
- const RATE_FIELDS = [
9465
- "inputUsdPerMTok",
9466
- "outputUsdPerMTok",
9467
- "cacheReadUsdPerMTok",
9468
- "cacheWriteUsdPerMTok",
9469
- "cacheWrite1hUsdPerMTok"
9470
- ];
9471
- const TIER_FIELDS = [
9472
- "aboveInputTokens",
9473
- "inputMultiplier",
9474
- "outputMultiplier"
9475
- ];
9476
- /**
9477
- * Compares a pricing seed against rates extracted from the provider's
9478
- * documented pricing page, in BOTH directions (RV902): a seed rate the
9479
- * page moved or dropped is a finding, and so is a documented billable
9480
- * rate the seed never declared, because a billable column missing from
9481
- * the seed is a silent underpricing channel (the 1h cache-write premium
9482
- * hid exactly there). Declared long-context tiers compare field by
9483
- * field. Returns human-readable findings, empty when the sides agree;
9484
- * the weekly rates audit (scripts/rates-audit.mjs) runs this exact
9485
- * comparator over the live pages, and the fault-injection kit drives it
9486
- * as a permanent gate (RV909). It verifies DOCUMENTATION, not billing:
9487
- * only a statement reconciliation over saved exports settles what the
9488
- * provider's meter actually charges.
9882
+ * File-backed TranscriptStore (M6-T02): blobs (transcripts, checkpoints,
9883
+ * persisted CompiledWorkflow sources) as one file per ref under `dir`,
9884
+ * so compiled runs resume across processes. Refs follow the
9885
+ * `<runId>/<name>` convention; nested segments become directories.
9886
+ *
9887
+ * Every ref is contained under `dir` (v1.36.0 review SEC-P1): each
9888
+ * segment must match `[A-Za-z0-9._-]` and be neither empty, '.', nor
9889
+ * '..', and the resolved path must stay under the resolved root. A '..'
9890
+ * segment used to pass the per-segment alphabet (dots are in it) and, via
9891
+ * `join`, escape the root; a caller passing an untrusted ref (or an
9892
+ * untrusted runId, which prefixes checkpoint and workflow-source refs)
9893
+ * could read, write, or delete `.bin` files outside `dir`.
9489
9894
  */
9490
- function compareRates(seed, page) {
9491
- const findings = [];
9492
- for (const field of RATE_FIELDS) {
9493
- const seedValue = seed[field];
9494
- const pageValue = page[field];
9495
- if (seedValue === void 0) {
9496
- if (pageValue !== void 0) findings.push(`${field}: the page shows ${String(pageValue)} but the seed declares no such rate`);
9497
- continue;
9895
+ var FileTranscriptStore = class {
9896
+ dir;
9897
+ constructor(options) {
9898
+ this.dir = options.dir;
9899
+ mkdirSync(this.dir, { recursive: true });
9900
+ }
9901
+ blobPath(ref) {
9902
+ const segments = ref.split("/");
9903
+ for (const segment of segments) if (segment === "" || segment === "." || segment === ".." || !/^[A-Za-z0-9._-]+$/.test(segment)) throw new JournalOrderViolation(`FileTranscriptStore: ref segment '${segment}' is not filesystem-safe`);
9904
+ const name = segments.pop() ?? "";
9905
+ const path = join(this.dir, ...segments, `${name}${TRANSCRIPT_SUFFIX}`);
9906
+ const root = resolve(this.dir);
9907
+ const resolved = resolve(path);
9908
+ if (resolved !== root && !resolved.startsWith(`${root}${sep}`)) throw new JournalOrderViolation(`FileTranscriptStore: ref '${ref}' resolves outside the configured root`);
9909
+ return path;
9910
+ }
9911
+ async put(ref, blob) {
9912
+ const path = this.blobPath(ref);
9913
+ mkdirSync(dirname(path), { recursive: true });
9914
+ const temp = `${path}.tmp`;
9915
+ writeFileSync(temp, blob);
9916
+ renameSync(temp, path);
9917
+ }
9918
+ async get(ref) {
9919
+ try {
9920
+ return new Uint8Array(readFileSync(this.blobPath(ref)));
9921
+ } catch (error) {
9922
+ if (error.code === "ENOENT") return null;
9923
+ throw error;
9498
9924
  }
9499
- if (pageValue === void 0) findings.push(`${field}: seed ${String(seedValue)} but the page shows no such rate`);
9500
- else if (!(Math.abs(seedValue - pageValue) <= 1e-9)) findings.push(`${field}: seed ${String(seedValue)} vs page ${String(pageValue)}`);
9501
9925
  }
9502
- const seedTiers = seed.tiers;
9503
- const pageTiers = page.tiers;
9504
- if (!Array.isArray(seedTiers)) {
9505
- if (Array.isArray(pageTiers) && pageTiers.length > 0) findings.push(`tiers: the page shows ${String(pageTiers.length)} but the seed declares none`);
9506
- } else if (!Array.isArray(pageTiers) || pageTiers.length !== seedTiers.length) findings.push(`tiers: seed declares ${String(seedTiers.length)}, page shows ${Array.isArray(pageTiers) ? String(pageTiers.length) : "none"}`);
9507
- else for (let i = 0; i < seedTiers.length; i += 1) for (const field of TIER_FIELDS) {
9508
- const seedValue = seedTiers[i]?.[field] ?? NaN;
9509
- const pageValue = pageTiers[i]?.[field] ?? NaN;
9510
- if (!(Math.abs(seedValue - pageValue) <= 1e-9)) findings.push(`tiers[${String(i)}].${field}: seed ${String(seedValue)} vs page ${String(pageValue)}`);
9926
+ async list(runId) {
9927
+ if (runId === "." || runId === "..") throw new JournalOrderViolation(`FileTranscriptStore: runId '${runId}' is not filesystem-safe`);
9928
+ const root = join(this.dir, safeName(runId));
9929
+ const refs = [];
9930
+ const walk = (dir, prefix) => {
9931
+ let names;
9932
+ try {
9933
+ names = readdirSync(dir);
9934
+ } catch {
9935
+ return;
9936
+ }
9937
+ for (const name of names) {
9938
+ const path = join(dir, name);
9939
+ if (statSync(path).isDirectory()) walk(path, `${prefix}${name}/`);
9940
+ else if (name.endsWith(TRANSCRIPT_SUFFIX)) refs.push(`${prefix}${name.slice(0, -4)}`);
9941
+ }
9942
+ };
9943
+ walk(root, `${runId}/`);
9944
+ return refs.sort();
9511
9945
  }
9512
- return findings;
9513
- }
9946
+ async delete(ref) {
9947
+ try {
9948
+ rmSync(this.blobPath(ref));
9949
+ } catch (error) {
9950
+ if (error.code !== "ENOENT") throw error;
9951
+ }
9952
+ }
9953
+ };
9514
9954
  //#endregion
9515
- //#region src/model/quota.ts
9955
+ //#region src/model/pricing.ts
9516
9956
  /**
9517
- * Quota rules and the in-process reference QuotaLimiter (RV-215).
9518
- * The rule model is shared by every reference implementation
9519
- * (memoryQuotaLimiter here, SqliteQuotaLimiter in
9520
- * @rulvar/store-sqlite): fixed one-minute windows aligned to the
9521
- * epoch, admission at reservation time, reconciliation to actual
9522
- * usage inside the same window. The hard guarantee is on
9957
+ * Resolves the pricing for a model: the versioned table wins; the
9958
+ * adapter-reported caps.pricing is the fallback; undefined means
9959
+ * unpriced (the CostReport surfaces it, never a silent zero).
9960
+ */
9961
+ function resolvePricing(ref, table, capsPricing) {
9962
+ return table?.models[ref] ?? capsPricing;
9963
+ }
9964
+ /** The tier a full prompt lands in: the highest threshold strictly below it. */
9965
+ function tierFor(pricing, inputTokens) {
9966
+ let tier;
9967
+ for (const candidate of pricing.tiers ?? []) if (inputTokens > candidate.aboveInputTokens && (tier === void 0 || candidate.aboveInputTokens > tier.aboveInputTokens)) tier = candidate;
9968
+ return tier;
9969
+ }
9970
+ /**
9971
+ * Decomposes one usage against one pricing row into the four billing
9972
+ * components. Under the Usage invariant inputTokens is the FULL prompt
9973
+ * including cache reads and writes, so the input rate bills only the
9974
+ * uncached remainder and cache tokens bill at their own rates, never
9975
+ * twice; a row that omits a cache rate bills those tokens at the plain
9976
+ * input rate rather than silently for free. A row may carry
9977
+ * long-context tiers: the highest threshold strictly below the full
9978
+ * prompt re-prices the ENTIRE request (input-side rates scale by
9979
+ * inputMultiplier, the output rate by outputMultiplier). Cache writes
9980
+ * price at the 5m premium rate by default; when the usage carries the
9981
+ * TTL split (RV810: `cacheWrite5mTokens` and `cacheWrite1hTokens`,
9982
+ * filled by adapters whose provider distinguishes write TTLs), the 1h
9983
+ * share prices at `cacheWrite1hUsdPerMTok` (falling back to the plain
9984
+ * write rate when the row lacks it) and everything the 1h share does
9985
+ * not claim, the 5m share plus any unattributed remainder an upstream
9986
+ * invariant violation left, bills at the write rate, never silently
9987
+ * for free. The component's `tokens` stays the WHOLE
9988
+ * `cacheWriteTokens` either way, so statement reconciliation keys are
9989
+ * unchanged.
9990
+ */
9991
+ function priceComponentsOf(pricing, usage) {
9992
+ const tier = tierFor(pricing, usage.inputTokens);
9993
+ const inputMul = tier?.inputMultiplier ?? 1;
9994
+ const outputMul = tier?.outputMultiplier ?? 1;
9995
+ const uncachedInputTokens = Math.max(0, usage.inputTokens - usage.cacheReadTokens - usage.cacheWriteTokens);
9996
+ const writeRate = pricing.cacheWriteUsdPerMTok ?? pricing.inputUsdPerMTok;
9997
+ const write1hRate = pricing.cacheWrite1hUsdPerMTok ?? writeRate;
9998
+ const write1hTokens = usage.cacheWrite5mTokens !== void 0 || usage.cacheWrite1hTokens !== void 0 ? usage.cacheWrite1hTokens ?? 0 : 0;
9999
+ const writeDefaultTokens = Math.max(0, usage.cacheWriteTokens - write1hTokens);
10000
+ return {
10001
+ input: {
10002
+ tokens: uncachedInputTokens,
10003
+ usd: uncachedInputTokens / 1e6 * pricing.inputUsdPerMTok * inputMul
10004
+ },
10005
+ output: {
10006
+ tokens: usage.outputTokens,
10007
+ usd: usage.outputTokens / 1e6 * pricing.outputUsdPerMTok * outputMul
10008
+ },
10009
+ cachedInput: {
10010
+ tokens: usage.cacheReadTokens,
10011
+ usd: usage.cacheReadTokens / 1e6 * (pricing.cacheReadUsdPerMTok ?? pricing.inputUsdPerMTok) * inputMul
10012
+ },
10013
+ cacheWrite: {
10014
+ tokens: usage.cacheWriteTokens,
10015
+ usd: (writeDefaultTokens / 1e6 * writeRate + write1hTokens / 1e6 * write1hRate) * inputMul
10016
+ }
10017
+ };
10018
+ }
10019
+ /**
10020
+ * Dollars from normalized usage against one pricing row: the sum of the
10021
+ * {@link priceComponentsOf} terms in their declared order, byte for
10022
+ * byte the historical expression (uncached input, output, cached input,
10023
+ * cache writes).
10024
+ */
10025
+ function priceUsdOf(pricing, usage) {
10026
+ const parts = priceComponentsOf(pricing, usage);
10027
+ return parts.input.usd + parts.output.usd + parts.cachedInput.usd + parts.cacheWrite.usd;
10028
+ }
10029
+ /**
10030
+ * The output tokens `remainingUsd` still buys from one pricing row after
10031
+ * paying for an estimated prompt of `estimatedInputTokens`, priced with
10032
+ * the same tier rules as settlement (the tier is selected by the
10033
+ * estimated prompt). Floored to whole tokens; zero or negative means not
10034
+ * even one output token fits, so the turn must not be dispatched.
10035
+ * Undefined when the row prices output at zero (a free model needs no
10036
+ * output bound).
10037
+ */
10038
+ function affordableOutputTokens(pricing, remainingUsd, estimatedInputTokens) {
10039
+ const tier = tierFor(pricing, estimatedInputTokens);
10040
+ const outputRate = pricing.outputUsdPerMTok * (tier?.outputMultiplier ?? 1);
10041
+ if (outputRate <= 0) return;
10042
+ const inputUsd = priceUsdOf(pricing, {
10043
+ inputTokens: estimatedInputTokens,
10044
+ outputTokens: 0,
10045
+ cacheReadTokens: 0,
10046
+ cacheWriteTokens: 0
10047
+ });
10048
+ return Math.floor((remainingUsd - inputUsd) / outputRate * 1e6);
10049
+ }
10050
+ const RATE_FIELDS = [
10051
+ "inputUsdPerMTok",
10052
+ "outputUsdPerMTok",
10053
+ "cacheReadUsdPerMTok",
10054
+ "cacheWriteUsdPerMTok",
10055
+ "cacheWrite1hUsdPerMTok"
10056
+ ];
10057
+ const TIER_FIELDS = [
10058
+ "aboveInputTokens",
10059
+ "inputMultiplier",
10060
+ "outputMultiplier"
10061
+ ];
10062
+ /**
10063
+ * Compares a pricing seed against rates extracted from the provider's
10064
+ * documented pricing page, in BOTH directions (RV902): a seed rate the
10065
+ * page moved or dropped is a finding, and so is a documented billable
10066
+ * rate the seed never declared, because a billable column missing from
10067
+ * the seed is a silent underpricing channel (the 1h cache-write premium
10068
+ * hid exactly there). Declared long-context tiers compare field by
10069
+ * field. Returns human-readable findings, empty when the sides agree;
10070
+ * the weekly rates audit (scripts/rates-audit.mjs) runs this exact
10071
+ * comparator over the live pages, and the fault-injection kit drives it
10072
+ * as a permanent gate (RV909). It verifies DOCUMENTATION, not billing:
10073
+ * only a statement reconciliation over saved exports settles what the
10074
+ * provider's meter actually charges.
10075
+ */
10076
+ function compareRates(seed, page) {
10077
+ const findings = [];
10078
+ for (const field of RATE_FIELDS) {
10079
+ const seedValue = seed[field];
10080
+ const pageValue = page[field];
10081
+ if (seedValue === void 0) {
10082
+ if (pageValue !== void 0) findings.push(`${field}: the page shows ${String(pageValue)} but the seed declares no such rate`);
10083
+ continue;
10084
+ }
10085
+ if (pageValue === void 0) findings.push(`${field}: seed ${String(seedValue)} but the page shows no such rate`);
10086
+ else if (!(Math.abs(seedValue - pageValue) <= 1e-9)) findings.push(`${field}: seed ${String(seedValue)} vs page ${String(pageValue)}`);
10087
+ }
10088
+ const seedTiers = seed.tiers;
10089
+ const pageTiers = page.tiers;
10090
+ if (!Array.isArray(seedTiers)) {
10091
+ if (Array.isArray(pageTiers) && pageTiers.length > 0) findings.push(`tiers: the page shows ${String(pageTiers.length)} but the seed declares none`);
10092
+ } else if (!Array.isArray(pageTiers) || pageTiers.length !== seedTiers.length) findings.push(`tiers: seed declares ${String(seedTiers.length)}, page shows ${Array.isArray(pageTiers) ? String(pageTiers.length) : "none"}`);
10093
+ else for (let i = 0; i < seedTiers.length; i += 1) for (const field of TIER_FIELDS) {
10094
+ const seedValue = seedTiers[i]?.[field] ?? NaN;
10095
+ const pageValue = pageTiers[i]?.[field] ?? NaN;
10096
+ if (!(Math.abs(seedValue - pageValue) <= 1e-9)) findings.push(`tiers[${String(i)}].${field}: seed ${String(seedValue)} vs page ${String(pageValue)}`);
10097
+ }
10098
+ return findings;
10099
+ }
10100
+ //#endregion
10101
+ //#region src/model/quota.ts
10102
+ /**
10103
+ * Quota rules and the in-process reference QuotaLimiter (RV-215).
10104
+ * The rule model is shared by every reference implementation
10105
+ * (memoryQuotaLimiter here, SqliteQuotaLimiter in
10106
+ * @rulvar/store-sqlite): fixed one-minute windows aligned to the
10107
+ * epoch, admission at reservation time, reconciliation to actual
10108
+ * usage inside the same window. The hard guarantee is on
9523
10109
  * `requestsPerMinute` (every wire attempt is exactly one request);
9524
10110
  * `tokensPerMinute` admits on the heuristic estimate and settles to
9525
10111
  * actual usage, so token windows are approximate at admission and
@@ -15743,6 +16329,94 @@ function statementFromRows(input) {
15743
16329
  })
15744
16330
  };
15745
16331
  }
16332
+ /**
16333
+ * Parses a delimited billing export (the CSV/TSV a provider console
16334
+ * hands a host) into the header-keyed rows {@link statementFromRows}
16335
+ * consumes (RV2908). The library deliberately hard-codes NO provider's
16336
+ * export format: the host owns the column map, this owns only the
16337
+ * delimited grammar, and the pair closes the last manual step between
16338
+ * a downloaded export and {@link reconcileStatement}.
16339
+ *
16340
+ * Fail-closed at the record, like the rest of this module: a data row
16341
+ * whose cell count differs from the header, a quote opened and never
16342
+ * closed, a stray quote inside an unquoted cell, an empty or duplicate
16343
+ * header name, all refuse typed with the line instead of flowing a
16344
+ * shifted column into a reconciliation, because a column shifted one
16345
+ * to the left prices `outputTokens` as dollars and calls it evidence.
16346
+ * RFC 4180 quoting is honored (quoted cells may carry the delimiter,
16347
+ * doubled quotes, and line breaks); CRLF and lone LF both delimit
16348
+ * records; one trailing empty line is an artifact of every exporter
16349
+ * and is ignored. Cells come back as raw strings, so an empty cell
16350
+ * reads as "the export does not carry this figure" downstream, exactly
16351
+ * the absence contract `statementFromRows` documents.
16352
+ */
16353
+ function statementRowsFromDelimited(text, options) {
16354
+ const delimiter = options?.delimiter ?? ",";
16355
+ const records = [];
16356
+ let cells = [];
16357
+ let cell = "";
16358
+ let quoted = false;
16359
+ let cellHadQuote = false;
16360
+ let line = 1;
16361
+ const endCell = () => {
16362
+ cells.push(cell);
16363
+ cell = "";
16364
+ cellHadQuote = false;
16365
+ };
16366
+ const endRecord = () => {
16367
+ endCell();
16368
+ records.push(cells);
16369
+ cells = [];
16370
+ };
16371
+ for (let index = 0; index < text.length; index += 1) {
16372
+ const char = text[index];
16373
+ if (quoted) {
16374
+ if (char === "\"") {
16375
+ if (text[index + 1] === "\"") {
16376
+ cell += "\"";
16377
+ index += 1;
16378
+ continue;
16379
+ }
16380
+ quoted = false;
16381
+ continue;
16382
+ }
16383
+ if (char === "\n") line += 1;
16384
+ cell += char;
16385
+ continue;
16386
+ }
16387
+ if (char === "\"") {
16388
+ if (cell.length > 0 || cellHadQuote) throw new ConfigError(`statementRowsFromDelimited: line ${String(line)} carries a quote inside an unquoted cell; quote the whole cell (RFC 4180) or fix the export`);
16389
+ quoted = true;
16390
+ cellHadQuote = true;
16391
+ continue;
16392
+ }
16393
+ if (char === delimiter) {
16394
+ endCell();
16395
+ continue;
16396
+ }
16397
+ if (char === "\r" && text[index + 1] === "\n") continue;
16398
+ if (char === "\n") {
16399
+ endRecord();
16400
+ line += 1;
16401
+ continue;
16402
+ }
16403
+ cell += char;
16404
+ }
16405
+ if (quoted) throw new ConfigError(`statementRowsFromDelimited: a quoted cell opened on line ${String(line)} never closes; the export is torn`);
16406
+ if (cell.length > 0 || cellHadQuote || cells.length > 0) endRecord();
16407
+ if (records.length === 0) throw new ConfigError("statementRowsFromDelimited: the export carries no header record");
16408
+ const header = records[0];
16409
+ const seen = /* @__PURE__ */ new Set();
16410
+ header.forEach((name, index) => {
16411
+ if (name.length === 0) throw new ConfigError(`statementRowsFromDelimited: header column ${String(index)} is empty; every column needs a name for the map to address`);
16412
+ if (seen.has(name)) throw new ConfigError(`statementRowsFromDelimited: header names column '${name}' twice; an ambiguous address cannot be mapped`);
16413
+ seen.add(name);
16414
+ });
16415
+ return records.slice(1).map((record, index) => {
16416
+ if (record.length !== header.length) throw new ConfigError(`statementRowsFromDelimited: data record ${String(index)} carries ${String(record.length)} cell(s) against ${String(header.length)} header column(s); a shifted column prices the wrong figure, so a ragged export refuses instead`);
16417
+ return Object.fromEntries(header.map((name, column) => [name, record[column]]));
16418
+ });
16419
+ }
15746
16420
  //#endregion
15747
16421
  //#region src/engine/persisted-terminal.ts
15748
16422
  const REFUSAL_MESSAGES = {
@@ -16488,484 +17162,196 @@ var AdmissionController = class {
16488
17162
  depth,
16489
17163
  ...evaluated.statsBefore === void 0 ? {} : { lineage: evaluated.statsBefore }
16490
17164
  };
16491
- if (evaluated.decision.kind === "reject") return {
16492
- verdict: {
16493
- kind: "reject",
16494
- reason: evaluated.decision.reason
16495
- },
16496
- statsBefore
16497
- };
16498
- if (this.terminationAccount !== void 0) {
16499
- if ((spec.ladderLength ?? 1) > this.terminationAccount.limits.kMax) return {
16500
- verdict: {
16501
- kind: "reject",
16502
- reason: { code: "ladder_exceeds_frozen" }
16503
- },
16504
- statsBefore
16505
- };
16506
- if (this.terminationAccount.spawnUnitsExhausted) return {
16507
- verdict: {
16508
- kind: "reject",
16509
- reason: { code: "termination_exhausted" }
16510
- },
16511
- statsBefore
16512
- };
16513
- }
16514
- if (depth > this.maxDepth) return {
16515
- verdict: {
16516
- kind: "reject",
16517
- reason: { code: "depth" }
16518
- },
16519
- statsBefore
16520
- };
16521
- if (childrenBefore >= this.maxChildrenPerNode) return {
16522
- verdict: {
16523
- kind: "reject",
16524
- reason: { code: "quota" }
16525
- },
16526
- statsBefore
16527
- };
16528
- if (this.maxTotalSpawns !== void 0 && this.admittedTotal >= this.maxTotalSpawns) return {
16529
- verdict: {
16530
- kind: "reject",
16531
- reason: { code: "lifetime" }
16532
- },
16533
- statsBefore
16534
- };
16535
- if (spec.roster !== void 0) {
16536
- const seatsRemaining = spec.roster.floor - spec.roster.admittedChildren;
16537
- if (seatsRemaining > 0) {
16538
- const perSeatProjectionUsd = this.projectedDispatchReserveUsd(spec);
16539
- const remainder = this.budget.remainderOf(spec.parentAccountScope);
16540
- if (remainder !== void 0 && remainder < seatsRemaining * perSeatProjectionUsd + spec.roster.liveExposureUsd) return {
16541
- verdict: {
16542
- kind: "reject",
16543
- reason: {
16544
- code: "roster_floor",
16545
- floor: spec.roster.floor,
16546
- admittedChildren: spec.roster.admittedChildren,
16547
- seatsRemaining,
16548
- perSeatProjectionUsd,
16549
- liveExposureUsd: spec.roster.liveExposureUsd,
16550
- remainderUsd: remainder
16551
- }
16552
- },
16553
- statsBefore
16554
- };
16555
- }
16556
- }
16557
- const spawnToolOrigin = spec.origin === "spawn_agent" || spec.origin === "parallel_agents";
16558
- let childCeilingUsd;
16559
- const parentRemainder = this.budget.remainderOf(spec.parentAccountScope);
16560
- if (spawnToolOrigin) {
16561
- if (spec.budgetUsd !== void 0) childCeilingUsd = spec.budgetUsd;
16562
- } else if (parentRemainder !== void 0) {
16563
- const fractionCap = this.childBudgetFraction * parentRemainder;
16564
- childCeilingUsd = spec.budgetUsd === void 0 ? fractionCap : Math.min(spec.budgetUsd, fractionCap);
16565
- } else if (spec.budgetUsd !== void 0) childCeilingUsd = spec.budgetUsd;
16566
- let reserveUsd = spec.estCostUsd ?? this.flatReserveUsd;
16567
- const source = spec.estCostUsd === void 0 ? "default" : "estCost";
16568
- let clampedBy;
16569
- if (childCeilingUsd !== void 0 && reserveUsd > childCeilingUsd) {
16570
- clampedBy = spec.budgetUsd !== void 0 && childCeilingUsd === spec.budgetUsd ? "explicit-budget" : "fraction-ceiling";
16571
- reserveUsd = childCeilingUsd;
16572
- }
16573
- const reserve = {
16574
- reserveUsd,
16575
- source
16576
- };
16577
- if (clampedBy !== void 0) reserve.clampedBy = clampedBy;
16578
- if (childCeilingUsd !== void 0) reserve.childCeilingUsd = childCeilingUsd;
16579
- if (this.budget.spawnHeadroom <= 0) return {
16580
- verdict: {
16581
- kind: "reject",
16582
- reason: { code: "lifetime" }
16583
- },
16584
- statsBefore
16585
- };
16586
- if (commitReserve) try {
16587
- this.budget.admitSpawn(reserveUsd, spec.parentAccountScope);
16588
- } catch {
16589
- return {
16590
- verdict: {
16591
- kind: "reject",
16592
- reason: { code: "budget" }
16593
- },
16594
- statsBefore
16595
- };
16596
- }
16597
- else {
16598
- const remainder = this.budget.remainderOf(spec.parentAccountScope);
16599
- const projection = this.projectedDispatchReserveUsd(spec);
16600
- if (remainder !== void 0 && (remainder <= 0 || remainder < projection + (spec.pendingReserveUsd ?? 0))) return {
16601
- verdict: {
16602
- kind: "reject",
16603
- reason: { code: "budget" }
16604
- },
16605
- statsBefore
16606
- };
16607
- }
16608
- this.childrenOf.set(nodeKey, childrenBefore + 1);
16609
- this.admittedTotal += 1;
16610
- const lineage = evaluated.decision.lineage;
16611
- this.registerLineageAdmit(lineage.logicalTaskId);
16612
- let spawnUnitsAfter = this.budget.spawnHeadroom;
16613
- if (this.terminationAccount !== void 0) {
16614
- const debited = this.terminationAccount.debitSpawn({
16615
- logicalTaskId: lineage.logicalTaskId,
16616
- isNew: spec.lineage === void 0,
16617
- ladderLength: spec.ladderLength ?? 1
16618
- });
16619
- if (!debited.ok) return {
16620
- verdict: {
16621
- kind: "reject",
16622
- reason: { code: "termination_exhausted" }
16623
- },
16624
- statsBefore
16625
- };
16626
- spawnUnitsAfter = debited.spawnUnitsAfter;
16627
- }
16628
- return {
16629
- verdict: {
16630
- kind: "admit",
16631
- reserve,
16632
- spawnUnitsAfter,
16633
- lineage: {
16634
- logicalTaskId: lineage.logicalTaskId,
16635
- isNew: spec.lineage === void 0,
16636
- depth
16637
- }
16638
- },
16639
- statsBefore,
16640
- nodeId: this.mintId(),
16641
- lineage,
16642
- ...this.terminationAccount === void 0 ? {} : { ladderLength: spec.ladderLength ?? 1 }
16643
- };
16644
- }
16645
- /**
16646
- * Resume roll-forward for an orchestrator child (M6-T07): restores the
16647
- * children-quota counter only. The budget seed already counts settled
16648
- * agent dispatches, and an in-flight child re-commits its reserve
16649
- * through the ctx.agent dispatch path.
16650
- */
16651
- recoverChild(nodeKey) {
16652
- this.childrenOf.set(nodeKey, (this.childrenOf.get(nodeKey) ?? 0) + 1);
16653
- this.admittedTotal += 1;
16654
- }
16655
- /**
16656
- * Resume roll-forward for a child that already SETTLED before the
16657
- * resume: re-registers the counters (maxChildrenPerNode, the lifetime
16658
- * cap, statsBefore fidelity) without committing any reserve; the spend
16659
- * itself sits in the root ledger seed.
16660
- */
16661
- recoverSettled(parentAccountScope) {
16662
- this.budget.admitRecovered(0, parentAccountScope);
16663
- this.childrenOf.set(parentAccountScope, (this.childrenOf.get(parentAccountScope) ?? 0) + 1);
16664
- this.admittedTotal += 1;
16665
- }
16666
- /**
16667
- * Resume roll-forward for an admission whose decision entry exists but
16668
- * whose child has NOT settled: re-applies the recorded reserve and
16669
- * counters without re-evaluating any limit (replay never
16670
- * re-evaluates admission; reserves are recovered, never
16671
- * re-estimated).
16672
- */
16673
- recoverInFlight(parentAccountScope, verdict) {
16674
- if (verdict.kind === "reject") return;
16675
- const reserveUsd = verdict.kind === "reuse_full" ? 0 : verdict.reserve.reserveUsd;
16676
- this.budget.admitRecovered(reserveUsd, parentAccountScope);
16677
- this.childrenOf.set(parentAccountScope, (this.childrenOf.get(parentAccountScope) ?? 0) + 1);
16678
- this.admittedTotal += 1;
16679
- }
16680
- };
16681
- //#endregion
16682
- //#region src/l0/telemetry-reduce.ts
16683
- const ZERO = {
16684
- inputTokens: 0,
16685
- outputTokens: 0,
16686
- cacheReadTokens: 0,
16687
- cacheWriteTokens: 0
16688
- };
16689
- /**
16690
- * Reduces one run's event stream (or any slice of it) to the invocation
16691
- * table. Feed it the events in emission order; both a live stream and a
16692
- * replayed one produce the same usage and cost columns.
16693
- */
16694
- function reduceInvocationTable(events) {
16695
- const rows = /* @__PURE__ */ new Map();
16696
- const order = [];
16697
- const openPhases = /* @__PURE__ */ new Map();
16698
- const byRole = {};
16699
- let totalCostUsd = 0;
16700
- const rowFor = (event) => {
16701
- let row = rows.get(event.spanId);
16702
- if (row === void 0) {
16703
- row = {
16704
- spanId: event.spanId,
16705
- agentType: event.agentType,
16706
- ...event.label === void 0 ? {} : { label: event.label },
16707
- usage: ZERO,
16708
- costUsd: 0,
16709
- costBasis: "aggregate-estimate",
16710
- usageApprox: false,
16711
- retryCount: 0,
16712
- replayed: event.replayed === true,
16713
- open: true,
16714
- phases: []
16715
- };
16716
- rows.set(event.spanId, row);
16717
- order.push(row);
16718
- }
16719
- return row;
16720
- };
16721
- for (const event of events) switch (event.type) {
16722
- case "agent:start": {
16723
- const row = rowFor(event);
16724
- row.role = event.role;
16725
- break;
16726
- }
16727
- case "agent:phase:start": {
16728
- const row = rowFor(event);
16729
- const phase = {
16730
- invocation: event.invocation,
16731
- role: event.role,
16732
- model: event.model,
16733
- durationMs: 0,
16734
- usage: ZERO,
16735
- costUsd: 0,
16736
- costBasis: "aggregate-estimate",
16737
- retries: 0,
16738
- replayed: event.replayed === true,
16739
- open: true
16740
- };
16741
- row.phases.push(phase);
16742
- openPhases.set(`${event.spanId}#${event.invocation}`, phase);
16743
- break;
16744
- }
16745
- case "agent:phase:end": {
16746
- const key = `${event.spanId}#${event.invocation}`;
16747
- let phase = openPhases.get(key);
16748
- if (phase === void 0) {
16749
- phase = {
16750
- invocation: event.invocation,
16751
- role: event.role,
16752
- model: event.model,
16753
- durationMs: 0,
16754
- usage: ZERO,
16755
- costUsd: 0,
16756
- costBasis: "aggregate-estimate",
16757
- retries: 0,
16758
- replayed: event.replayed === true,
16759
- open: true
16760
- };
16761
- rowFor(event).phases.push(phase);
16762
- }
16763
- openPhases.delete(key);
16764
- phase.open = false;
16765
- phase.role = event.role;
16766
- phase.model = event.model;
16767
- phase.durationMs = event.durationMs;
16768
- phase.usage = event.usage;
16769
- phase.costUsd = event.costUsd;
16770
- phase.costBasis = event.costBasis ?? "aggregate-estimate";
16771
- phase.outcome = event.outcome;
16772
- phase.retries = event.retries ?? 0;
16773
- const bucket = byRole[event.role] ??= {
16774
- usage: ZERO,
16775
- costUsd: 0,
16776
- costBasis: "per-call"
16777
- };
16778
- bucket.usage = sumUsage(bucket.usage, event.usage);
16779
- bucket.costUsd += event.costUsd;
16780
- if (phase.costBasis === "aggregate-estimate") bucket.costBasis = "aggregate-estimate";
16781
- break;
16782
- }
16783
- case "agent:end": {
16784
- const row = rowFor(event);
16785
- row.open = false;
16786
- row.status = event.status;
16787
- row.usage = event.usage;
16788
- row.costUsd = event.costUsd;
16789
- row.costBasis = event.costBasis ?? "aggregate-estimate";
16790
- row.usageApprox = event.usageApprox === true;
16791
- row.retryCount = event.retryCount ?? 0;
16792
- if (event.toolBudget !== void 0) row.toolBudget = event.toolBudget;
16793
- totalCostUsd += event.costUsd;
16794
- break;
16795
- }
16796
- default: break;
16797
- }
16798
- return {
16799
- agents: order,
16800
- byRole,
16801
- totalCostUsd
16802
- };
16803
- }
16804
- /**
16805
- * The label the claim-consistency judge invocation dispatches under
16806
- * (RV1502; named here since RV1604 so the critical-path reducer and the
16807
- * orchestrator share one constant): the judge rides role 'synthesize',
16808
- * and this label is what tells its wall apart from a real final
16809
- * composition in {@link reduceCriticalPath}.
16810
- */
16811
- const CLAIM_JUDGE_LABEL = "claim-consistency-judge";
16812
- /** Total length of the union of possibly overlapping intervals. */
16813
- function unionLength(intervals) {
16814
- const positive = intervals.filter((interval) => interval.to > interval.from);
16815
- if (positive.length === 0) return 0;
16816
- const sorted = [...positive].sort((a, b) => a.from - b.from);
16817
- let total = 0;
16818
- let from = sorted[0]?.from ?? 0;
16819
- let to = sorted[0]?.to ?? 0;
16820
- for (const interval of sorted.slice(1)) if (interval.from > to) {
16821
- total += to - from;
16822
- from = interval.from;
16823
- to = interval.to;
16824
- } else if (interval.to > to) to = interval.to;
16825
- return total + (to - from);
16826
- }
16827
- function reduceCriticalPath(events) {
16828
- let runStart;
16829
- let runEnd;
16830
- const startBySpan = /* @__PURE__ */ new Map();
16831
- let lastWorkerEnd;
16832
- let workerSpans = 0;
16833
- let synthesisMs = 0;
16834
- let finalCompositionMs = 0;
16835
- let semanticJudgeMs = 0;
16836
- const coordinationModel = [];
16837
- const coordinationTools = [];
16838
- const synthesisSpans = [];
16839
- const spanOf = (durationMs) => Number.isFinite(durationMs) && durationMs > 0 ? durationMs : 0;
16840
- for (const event of events) {
16841
- const at = Date.parse(event.ts);
16842
- if (!Number.isFinite(at)) continue;
16843
- switch (event.type) {
16844
- case "run:start":
16845
- runStart ??= at;
16846
- break;
16847
- case "run:end":
16848
- runEnd = at;
16849
- break;
16850
- case "agent:start":
16851
- startBySpan.set(event.spanId, {
16852
- role: event.role,
16853
- at,
16854
- ...event.label === void 0 ? {} : { label: event.label }
16855
- });
16856
- break;
16857
- case "agent:phase:end":
16858
- if (startBySpan.get(event.spanId)?.role === "orchestrate") coordinationModel.push({
16859
- phase: event.role,
16860
- from: at - spanOf(event.durationMs),
16861
- to: at
16862
- });
16863
- break;
16864
- case "tool:end":
16865
- if (startBySpan.get(event.spanId)?.role === "orchestrate") coordinationTools.push({
16866
- name: event.toolName,
16867
- from: at - spanOf(event.durationMs),
16868
- to: at
16869
- });
16870
- break;
16871
- case "agent:end": {
16872
- const started = startBySpan.get(event.spanId);
16873
- if (started === void 0) break;
16874
- if (started.role === "synthesize") {
16875
- const wall = Math.max(0, at - started.at);
16876
- const judge = started.label === CLAIM_JUDGE_LABEL;
16877
- synthesisMs += wall;
16878
- if (judge) semanticJudgeMs += wall;
16879
- else finalCompositionMs += wall;
16880
- synthesisSpans.push({
16881
- from: started.at,
16882
- to: at,
16883
- judge
16884
- });
16885
- } else if (started.role !== "orchestrate") {
16886
- workerSpans += 1;
16887
- lastWorkerEnd = lastWorkerEnd === void 0 ? at : Math.max(lastWorkerEnd, at);
16888
- }
16889
- break;
17165
+ if (evaluated.decision.kind === "reject") return {
17166
+ verdict: {
17167
+ kind: "reject",
17168
+ reason: evaluated.decision.reason
17169
+ },
17170
+ statsBefore
17171
+ };
17172
+ if (this.terminationAccount !== void 0) {
17173
+ if ((spec.ladderLength ?? 1) > this.terminationAccount.limits.kMax) return {
17174
+ verdict: {
17175
+ kind: "reject",
17176
+ reason: { code: "ladder_exceeds_frozen" }
17177
+ },
17178
+ statsBefore
17179
+ };
17180
+ if (this.terminationAccount.spawnUnitsExhausted) return {
17181
+ verdict: {
17182
+ kind: "reject",
17183
+ reason: { code: "termination_exhausted" }
17184
+ },
17185
+ statsBefore
17186
+ };
17187
+ }
17188
+ if (depth > this.maxDepth) return {
17189
+ verdict: {
17190
+ kind: "reject",
17191
+ reason: { code: "depth" }
17192
+ },
17193
+ statsBefore
17194
+ };
17195
+ if (childrenBefore >= this.maxChildrenPerNode) return {
17196
+ verdict: {
17197
+ kind: "reject",
17198
+ reason: { code: "quota" }
17199
+ },
17200
+ statsBefore
17201
+ };
17202
+ if (this.maxTotalSpawns !== void 0 && this.admittedTotal >= this.maxTotalSpawns) return {
17203
+ verdict: {
17204
+ kind: "reject",
17205
+ reason: { code: "lifetime" }
17206
+ },
17207
+ statsBefore
17208
+ };
17209
+ if (spec.roster !== void 0) {
17210
+ const seatsRemaining = spec.roster.floor - spec.roster.admittedChildren;
17211
+ if (seatsRemaining > 0) {
17212
+ const perSeatProjectionUsd = this.projectedDispatchReserveUsd(spec);
17213
+ const remainder = this.budget.remainderOf(spec.parentAccountScope);
17214
+ if (remainder !== void 0 && remainder < seatsRemaining * perSeatProjectionUsd + spec.roster.liveExposureUsd) return {
17215
+ verdict: {
17216
+ kind: "reject",
17217
+ reason: {
17218
+ code: "roster_floor",
17219
+ floor: spec.roster.floor,
17220
+ admittedChildren: spec.roster.admittedChildren,
17221
+ seatsRemaining,
17222
+ perSeatProjectionUsd,
17223
+ liveExposureUsd: spec.roster.liveExposureUsd,
17224
+ remainderUsd: remainder
17225
+ }
17226
+ },
17227
+ statsBefore
17228
+ };
16890
17229
  }
16891
- default: break;
16892
17230
  }
16893
- }
16894
- const path = {
16895
- synthesisMs,
16896
- finalCompositionMs,
16897
- semanticJudgeMs,
16898
- workerSpans
16899
- };
16900
- if (runStart !== void 0 && runEnd !== void 0) path.runWallMs = Math.max(0, runEnd - runStart);
16901
- if (runEnd !== void 0 && lastWorkerEnd !== void 0) {
16902
- path.postFanInMs = Math.max(0, runEnd - lastWorkerEnd);
16903
- const windowFrom = Math.min(lastWorkerEnd, runEnd);
16904
- const windowTo = runEnd;
16905
- const clip = (interval) => {
16906
- if (interval.to < windowFrom || interval.from > windowTo) return;
17231
+ const spawnToolOrigin = spec.origin === "spawn_agent" || spec.origin === "parallel_agents";
17232
+ let childCeilingUsd;
17233
+ const parentRemainder = this.budget.remainderOf(spec.parentAccountScope);
17234
+ if (spawnToolOrigin) {
17235
+ if (spec.budgetUsd !== void 0) childCeilingUsd = spec.budgetUsd;
17236
+ } else if (parentRemainder !== void 0) {
17237
+ const fractionCap = this.childBudgetFraction * parentRemainder;
17238
+ childCeilingUsd = spec.budgetUsd === void 0 ? fractionCap : Math.min(spec.budgetUsd, fractionCap);
17239
+ } else if (spec.budgetUsd !== void 0) childCeilingUsd = spec.budgetUsd;
17240
+ let reserveUsd = spec.estCostUsd ?? this.flatReserveUsd;
17241
+ const source = spec.estCostUsd === void 0 ? "default" : "estCost";
17242
+ let clampedBy;
17243
+ if (childCeilingUsd !== void 0 && reserveUsd > childCeilingUsd) {
17244
+ clampedBy = spec.budgetUsd !== void 0 && childCeilingUsd === spec.budgetUsd ? "explicit-budget" : "fraction-ceiling";
17245
+ reserveUsd = childCeilingUsd;
17246
+ }
17247
+ const reserve = {
17248
+ reserveUsd,
17249
+ source
17250
+ };
17251
+ if (clampedBy !== void 0) reserve.clampedBy = clampedBy;
17252
+ if (childCeilingUsd !== void 0) reserve.childCeilingUsd = childCeilingUsd;
17253
+ if (this.budget.spawnHeadroom <= 0) return {
17254
+ verdict: {
17255
+ kind: "reject",
17256
+ reason: { code: "lifetime" }
17257
+ },
17258
+ statsBefore
17259
+ };
17260
+ if (commitReserve) try {
17261
+ this.budget.admitSpawn(reserveUsd, spec.parentAccountScope);
17262
+ } catch {
16907
17263
  return {
16908
- from: Math.max(interval.from, windowFrom),
16909
- to: Math.min(interval.to, windowTo)
17264
+ verdict: {
17265
+ kind: "reject",
17266
+ reason: { code: "budget" }
17267
+ },
17268
+ statsBefore
16910
17269
  };
16911
- };
16912
- const byPhase = {};
16913
- const modelClipped = [];
16914
- for (const interval of coordinationModel) {
16915
- const clipped = clip(interval);
16916
- if (clipped === void 0) continue;
16917
- byPhase[interval.phase] = (byPhase[interval.phase] ?? 0) + (clipped.to - clipped.from);
16918
- modelClipped.push(clipped);
16919
17270
  }
16920
- const synthesisClipped = [];
16921
- let judgeClippedMs = 0;
16922
- let compositionClippedMs = 0;
16923
- for (const span of synthesisSpans) {
16924
- const clipped = clip(span);
16925
- if (clipped === void 0) continue;
16926
- synthesisClipped.push(clipped);
16927
- if (span.judge) judgeClippedMs += clipped.to - clipped.from;
16928
- else compositionClippedMs += clipped.to - clipped.from;
17271
+ else {
17272
+ const remainder = this.budget.remainderOf(spec.parentAccountScope);
17273
+ const projection = this.projectedDispatchReserveUsd(spec);
17274
+ if (remainder !== void 0 && (remainder <= 0 || remainder < projection + (spec.pendingReserveUsd ?? 0))) return {
17275
+ verdict: {
17276
+ kind: "reject",
17277
+ reason: { code: "budget" }
17278
+ },
17279
+ statsBefore
17280
+ };
16929
17281
  }
16930
- const byName = {};
16931
- const callsByName = {};
16932
- const toolsClipped = [];
16933
- for (const interval of coordinationTools) {
16934
- const clipped = clip(interval);
16935
- if (clipped === void 0) continue;
16936
- byName[interval.name] = (byName[interval.name] ?? 0) + (clipped.to - clipped.from);
16937
- callsByName[interval.name] = (callsByName[interval.name] ?? 0) + 1;
16938
- toolsClipped.push(clipped);
17282
+ this.childrenOf.set(nodeKey, childrenBefore + 1);
17283
+ this.admittedTotal += 1;
17284
+ const lineage = evaluated.decision.lineage;
17285
+ this.registerLineageAdmit(lineage.logicalTaskId);
17286
+ let spawnUnitsAfter = this.budget.spawnHeadroom;
17287
+ if (this.terminationAccount !== void 0) {
17288
+ const debited = this.terminationAccount.debitSpawn({
17289
+ logicalTaskId: lineage.logicalTaskId,
17290
+ isNew: spec.lineage === void 0,
17291
+ ladderLength: spec.ladderLength ?? 1
17292
+ });
17293
+ if (!debited.ok) return {
17294
+ verdict: {
17295
+ kind: "reject",
17296
+ reason: { code: "termination_exhausted" }
17297
+ },
17298
+ statsBefore
17299
+ };
17300
+ spawnUnitsAfter = debited.spawnUnitsAfter;
16939
17301
  }
16940
- const lengthOf = (intervals) => intervals.reduce((sum, interval) => sum + (interval.to - interval.from), 0);
16941
- const coveredMs = unionLength([
16942
- ...modelClipped,
16943
- ...toolsClipped,
16944
- ...synthesisClipped
16945
- ]);
16946
- const modelOnlyMs = unionLength([...modelClipped, ...toolsClipped]) - unionLength(toolsClipped);
16947
- const breakdown = {
16948
- coordinationModelMs: lengthOf(modelClipped),
16949
- coordinationModelMsByPhase: byPhase,
16950
- coordinationModelOnlyMs: modelOnlyMs,
16951
- coordinationToolMs: lengthOf(toolsClipped),
16952
- coordinationToolMsByName: byName,
16953
- coordinationToolCallsByName: callsByName,
16954
- synthesisMs: lengthOf(synthesisClipped),
16955
- finalCompositionMs: compositionClippedMs,
16956
- semanticJudgeMs: judgeClippedMs,
16957
- coveredMs,
16958
- residueMs: Math.max(0, path.postFanInMs - coveredMs)
17302
+ return {
17303
+ verdict: {
17304
+ kind: "admit",
17305
+ reserve,
17306
+ spawnUnitsAfter,
17307
+ lineage: {
17308
+ logicalTaskId: lineage.logicalTaskId,
17309
+ isNew: spec.lineage === void 0,
17310
+ depth
17311
+ }
17312
+ },
17313
+ statsBefore,
17314
+ nodeId: this.mintId(),
17315
+ lineage,
17316
+ ...this.terminationAccount === void 0 ? {} : { ladderLength: spec.ladderLength ?? 1 }
16959
17317
  };
16960
- if (path.postFanInMs > 0) breakdown.residueShare = breakdown.residueMs / path.postFanInMs;
16961
- path.postFanIn = breakdown;
16962
17318
  }
16963
- if (path.runWallMs !== void 0 && path.runWallMs > 0) {
16964
- if (path.postFanInMs !== void 0) path.postFanInShare = path.postFanInMs / path.runWallMs;
16965
- path.synthesisShare = synthesisMs / path.runWallMs;
17319
+ /**
17320
+ * Resume roll-forward for an orchestrator child (M6-T07): restores the
17321
+ * children-quota counter only. The budget seed already counts settled
17322
+ * agent dispatches, and an in-flight child re-commits its reserve
17323
+ * through the ctx.agent dispatch path.
17324
+ */
17325
+ recoverChild(nodeKey) {
17326
+ this.childrenOf.set(nodeKey, (this.childrenOf.get(nodeKey) ?? 0) + 1);
17327
+ this.admittedTotal += 1;
16966
17328
  }
16967
- return path;
16968
- }
17329
+ /**
17330
+ * Resume roll-forward for a child that already SETTLED before the
17331
+ * resume: re-registers the counters (maxChildrenPerNode, the lifetime
17332
+ * cap, statsBefore fidelity) without committing any reserve; the spend
17333
+ * itself sits in the root ledger seed.
17334
+ */
17335
+ recoverSettled(parentAccountScope) {
17336
+ this.budget.admitRecovered(0, parentAccountScope);
17337
+ this.childrenOf.set(parentAccountScope, (this.childrenOf.get(parentAccountScope) ?? 0) + 1);
17338
+ this.admittedTotal += 1;
17339
+ }
17340
+ /**
17341
+ * Resume roll-forward for an admission whose decision entry exists but
17342
+ * whose child has NOT settled: re-applies the recorded reserve and
17343
+ * counters without re-evaluating any limit (replay never
17344
+ * re-evaluates admission; reserves are recovered, never
17345
+ * re-estimated).
17346
+ */
17347
+ recoverInFlight(parentAccountScope, verdict) {
17348
+ if (verdict.kind === "reject") return;
17349
+ const reserveUsd = verdict.kind === "reuse_full" ? 0 : verdict.reserve.reserveUsd;
17350
+ this.budget.admitRecovered(reserveUsd, parentAccountScope);
17351
+ this.childrenOf.set(parentAccountScope, (this.childrenOf.get(parentAccountScope) ?? 0) + 1);
17352
+ this.admittedTotal += 1;
17353
+ }
17354
+ };
16969
17355
  //#endregion
16970
17356
  //#region src/model/profile-card.ts
16971
17357
  function toolNamesOf(profile) {
@@ -18708,6 +19094,7 @@ function createCtx(internals, rootWorkflow) {
18708
19094
  agentType,
18709
19095
  role: primaryRole,
18710
19096
  budgetAccount: state.budgetScope ?? "run",
19097
+ ...opts.label === void 0 ? {} : { label: opts.label },
18711
19098
  ...opts[kFinalizeReserve] === true ? { finalizeReserve: true } : {}
18712
19099
  },
18713
19100
  transcriptRef: result.transcriptRef
@@ -21106,6 +21493,8 @@ function pairDraftClaims(draftText, rows, options) {
21106
21493
  const max = requirePositiveInteger(options?.max ?? 40, "pairDraftClaims max");
21107
21494
  const maxPoolPerPair = requirePositiveInteger(options?.maxPoolPerPair ?? 3, "pairDraftClaims maxPoolPerPair");
21108
21495
  const maxExcerptChars = requirePositiveInteger(options?.maxExcerptChars ?? 400, "pairDraftClaims maxExcerptChars");
21496
+ const targetShare = options?.targetCoverageShare;
21497
+ if (targetShare !== void 0 && (typeof targetShare !== "number" || !Number.isFinite(targetShare) || targetShare <= 0 || targetShare > 1)) throw new ConfigError(`pairDraftClaims targetCoverageShare must be a number in (0, 1]; got ` + JSON.stringify(targetShare));
21109
21498
  const poolByPath = /* @__PURE__ */ new Map();
21110
21499
  for (const row of rows) for (const sentence of sentencesOf(row.text)) {
21111
21500
  const anchors = anchorsOf(sentence, pattern);
@@ -21197,13 +21586,37 @@ function pairDraftClaims(draftText, rows, options) {
21197
21586
  });
21198
21587
  }
21199
21588
  }
21200
- const reported = (critical === void 0 ? candidates : [...candidates.filter((candidate) => candidate.critical), ...candidates.filter((candidate) => !candidate.critical)]).slice(0, max);
21589
+ const ordered = critical === void 0 ? candidates : [...candidates.filter((candidate) => candidate.critical), ...candidates.filter((candidate) => !candidate.critical)];
21590
+ let reported;
21591
+ let maxCut;
21592
+ let targetSentences;
21593
+ if (targetShare === void 0) {
21594
+ reported = ordered.slice(0, max);
21595
+ maxCut = candidates.length > reported.length;
21596
+ } else {
21597
+ targetSentences = Math.min(draftCitingSentences, Math.ceil(targetShare * draftCitingSentences));
21598
+ const covering = /* @__PURE__ */ new Set();
21599
+ const wanted = [];
21600
+ for (const candidate of ordered) {
21601
+ if (candidate.critical) {
21602
+ wanted.push(candidate);
21603
+ covering.add(candidate.sentence);
21604
+ continue;
21605
+ }
21606
+ if (covering.size >= targetSentences || covering.has(candidate.sentence)) continue;
21607
+ wanted.push(candidate);
21608
+ covering.add(candidate.sentence);
21609
+ }
21610
+ reported = wanted.slice(0, max);
21611
+ maxCut = wanted.length > reported.length;
21612
+ }
21201
21613
  const coveredSentences = new Set(reported.map((candidate) => candidate.sentence));
21202
21614
  const fold = {
21203
21615
  pairs: reported.map((candidate) => candidate.pair),
21204
- truncated: candidates.length > reported.length,
21616
+ truncated: maxCut,
21205
21617
  draftCitingSentences,
21206
- coveredCitingSentences: coveredSentences.size
21618
+ coveredCitingSentences: coveredSentences.size,
21619
+ ...targetSentences === void 0 ? {} : { targetCoveredSentences: targetSentences }
21207
21620
  };
21208
21621
  if (critical !== void 0) {
21209
21622
  const reportedAnchors = new Set(reported.map((candidate) => candidate.pair.anchor));
@@ -22010,10 +22423,14 @@ function validateOrchestrateOptions(opts) {
22010
22423
  if (consistency.runFacts !== true) throw new ConfigError("orchestrate claimConsistency.runFactTerms rides the runFacts pass; set claimConsistency.runFacts true");
22011
22424
  if (!Array.isArray(consistency.runFactTerms) || consistency.runFactTerms.some((term) => typeof term !== "string" || term.length === 0)) throw new ConfigError("orchestrate claimConsistency.runFactTerms must be an array of nonempty strings; got " + JSON.stringify(consistency.runFactTerms));
22012
22425
  }
22013
- for (const [label, ratio] of [["minimumCoverageRatio", consistency.minimumCoverageRatio], ["runFactCoverageRatio", consistency.runFactCoverageRatio]]) if (ratio !== void 0 && (typeof ratio !== "number" || !Number.isFinite(ratio) || ratio <= 0 || ratio > 1)) throw new ConfigError(`orchestrate claimConsistency.${label} must be a number in (0, 1]; got ` + JSON.stringify(ratio));
22426
+ for (const [label, ratio] of [
22427
+ ["minimumCoverageRatio", consistency.minimumCoverageRatio],
22428
+ ["runFactCoverageRatio", consistency.runFactCoverageRatio],
22429
+ ["coverageTarget", consistency.coverageTarget]
22430
+ ]) if (ratio !== void 0 && (typeof ratio !== "number" || !Number.isFinite(ratio) || ratio <= 0 || ratio > 1)) throw new ConfigError(`orchestrate claimConsistency.${label} must be a number in (0, 1]; got ` + JSON.stringify(ratio));
22014
22431
  if (consistency.runFactCoverageRatio !== void 0 && consistency.runFacts !== true) throw new ConfigError("orchestrate claimConsistency.runFactCoverageRatio rides the runFacts pass; set claimConsistency.runFacts true");
22015
22432
  if (consistency.onLowCoverage !== void 0 && consistency.onLowCoverage !== "report" && consistency.onLowCoverage !== "fail") throw new ConfigError("orchestrate claimConsistency.onLowCoverage must be 'report' or 'fail'; got " + JSON.stringify(consistency.onLowCoverage));
22016
- if (consistency.onLowCoverage !== void 0 && consistency.minimumCoverageRatio === void 0 && consistency.runFactCoverageRatio === void 0) throw new ConfigError("orchestrate claimConsistency.onLowCoverage needs a declared floor; set minimumCoverageRatio or runFactCoverageRatio");
22433
+ if (consistency.onLowCoverage !== void 0 && consistency.minimumCoverageRatio === void 0 && consistency.runFactCoverageRatio === void 0 && consistency.coverageTarget === void 0) throw new ConfigError("orchestrate claimConsistency.onLowCoverage needs a declared floor; set minimumCoverageRatio, runFactCoverageRatio, or coverageTarget");
22017
22434
  if (consistency.judge !== void 0) {
22018
22435
  const judge = consistency.judge;
22019
22436
  if (typeof judge !== "object" || judge === null || Array.isArray(judge)) throw new ConfigError(`orchestrate claimConsistency.judge must be an object; got ${JSON.stringify(consistency.judge)}`);
@@ -23685,6 +24102,7 @@ function makeOrchestratorWorkflow(goal, opts) {
23685
24102
  const noteOpts = {
23686
24103
  role: "synthesize",
23687
24104
  result: "full",
24105
+ label: SYNTHESIS_NOTE_LABEL,
23688
24106
  tools: finishOnly,
23689
24107
  limits: spec.noteLimits ?? { maxTurns: 2 },
23690
24108
  ...spec.model === void 0 ? {} : { model: spec.model },
@@ -24021,7 +24439,8 @@ function makeOrchestratorWorkflow(goal, opts) {
24021
24439
  max: spec.max ?? 40,
24022
24440
  ...spec.maxPoolPerPair === void 0 ? {} : { maxPoolPerPair: spec.maxPoolPerPair },
24023
24441
  ...spec.maxExcerptChars === void 0 ? {} : { maxExcerptChars: spec.maxExcerptChars },
24024
- ...spec.critical === void 0 ? {} : { critical: spec.critical }
24442
+ ...spec.critical === void 0 ? {} : { critical: spec.critical },
24443
+ ...spec.coverageTarget === void 0 ? {} : { targetCoverageShare: spec.coverageTarget }
24025
24444
  });
24026
24445
  const runFold = spec.runFacts === true ? pairRunFactClaims(draftText, {
24027
24446
  text: `The run ${internals.runId} made ${String(factWires)} provider wire requests across ${String(poolChildren)} accepted children, with token totals ${String(factInput)} input and ${String(factOutput)} output (the run's own recorded execution facts; harness-observed, not production evidence). ${factRows.join(" ")}`,
@@ -24034,7 +24453,8 @@ function makeOrchestratorWorkflow(goal, opts) {
24034
24453
  ]
24035
24454
  }, {
24036
24455
  ...spec.runFactTerms === void 0 ? {} : { terms: spec.runFactTerms },
24037
- ...spec.maxExcerptChars === void 0 ? {} : { maxExcerptChars: spec.maxExcerptChars }
24456
+ ...spec.maxExcerptChars === void 0 ? {} : { maxExcerptChars: spec.maxExcerptChars },
24457
+ ...spec.coverageTarget === void 0 ? {} : { max: Number.MAX_SAFE_INTEGER }
24038
24458
  }) : void 0;
24039
24459
  const allPairs = runFold === void 0 ? fold.pairs : [...fold.pairs, ...runFold.pairs];
24040
24460
  const onFound = spec.onFound ?? "report";
@@ -24044,6 +24464,7 @@ function makeOrchestratorWorkflow(goal, opts) {
24044
24464
  pairs: allPairs.length,
24045
24465
  truncated: fold.truncated,
24046
24466
  coveredCitingSentences: fold.coveredCitingSentences,
24467
+ ...spec.coverageTarget === void 0 ? {} : { coverageTarget: spec.coverageTarget },
24047
24468
  ...fold.criticalUncovered === void 0 ? {} : {
24048
24469
  criticalUncovered: fold.criticalUncovered,
24049
24470
  criticalUncoveredTotal: fold.criticalUncoveredTotal ?? 0
@@ -24054,14 +24475,15 @@ function makeOrchestratorWorkflow(goal, opts) {
24054
24475
  runFactCandidates: runFold.candidates
24055
24476
  },
24056
24477
  ...(() => {
24478
+ const coverageFloor = spec.minimumCoverageRatio ?? spec.coverageTarget;
24057
24479
  const coverageRatio = fold.draftCitingSentences === 0 ? 1 : fold.coveredCitingSentences / fold.draftCitingSentences;
24058
24480
  const runFactRatio = runFold === void 0 || runFold.candidates === 0 ? void 0 : runFold.pairs.length / runFold.candidates;
24059
- const belowCoverage = spec.minimumCoverageRatio !== void 0 && fold.draftCitingSentences > 0 && coverageRatio < spec.minimumCoverageRatio;
24481
+ const belowCoverage = coverageFloor !== void 0 && fold.draftCitingSentences > 0 && coverageRatio < coverageFloor;
24060
24482
  const belowRunFacts = spec.runFactCoverageRatio !== void 0 && runFactRatio !== void 0 && runFactRatio < spec.runFactCoverageRatio;
24061
24483
  if (!belowCoverage && !belowRunFacts) return {};
24062
24484
  return { lowCoverage: {
24063
24485
  coverageRatio,
24064
- ...spec.minimumCoverageRatio === void 0 ? {} : { coverageFloor: spec.minimumCoverageRatio },
24486
+ ...coverageFloor === void 0 ? {} : { coverageFloor },
24065
24487
  ...runFactRatio === void 0 ? {} : { runFactRatio },
24066
24488
  ...spec.runFactCoverageRatio === void 0 ? {} : { runFactFloor: spec.runFactCoverageRatio }
24067
24489
  } };
@@ -24558,6 +24980,7 @@ function makeOrchestratorWorkflow(goal, opts) {
24558
24980
  const synthesisOpts = {
24559
24981
  role: "synthesize",
24560
24982
  result: "full",
24983
+ label: FINAL_COMPOSITION_LABEL,
24561
24984
  tools: synthesisTools,
24562
24985
  [kExposureWait]: true,
24563
24986
  limits: spec.limits ?? { maxTurns: 4 },
@@ -28316,4 +28739,4 @@ function createSandboxBridge(ctx, options) {
28316
28739
  };
28317
28740
  }
28318
28741
  //#endregion
28319
- export { AWAIT_SCHEMA, AdmissionController, AdmissionRejectedError, AgentCallError, BUDGET_ABORT_REASON, BudgetExhaustedError, CANCEL_AGENT_SCHEMA, CHECKPOINT_FORMAT_V1, CLAIM_JUDGE_LABEL, CLAIM_STATEMENT_MAX_CHARS, CLAIM_TTL_DAYS, COMPACTION_SUMMARY_PREFIX, CURRENT_HASH_VERSION, ConfigError, DECISION_CHAIN_KINDS, DEFAULT_ANCHOR_PATTERN, DEFAULT_ARTIFACT_PATTERN, DEFAULT_CHILD_BUDGET_FRACTION, DEFAULT_CHILD_RESULT_PAGE_CHARS, DEFAULT_CITATION_PATTERN, DEFAULT_CITATION_SAMPLE, DEFAULT_CLAIM_JUDGE_MAX_TURNS, DEFAULT_COMPACTION_THRESHOLD, DEFAULT_ESCALATION_LIMITS, DEFAULT_EVIDENCE_CALLS_PER_ENTRY, DEFAULT_EVIDENCE_GRADE_PHRASES, DEFAULT_EVIDENCE_MIN_SHARE, DEFAULT_EVIDENCE_OVERHEAD_CALLS, DEFAULT_FINISH_MAX_REPAIRS, DEFAULT_FLAT_RESERVE_USD, DEFAULT_MAX_CHILDREN_PER_NODE, DEFAULT_MAX_CLAIM_PAIRS, DEFAULT_MAX_CONTRADICTIONS, DEFAULT_MAX_DEPTH, DEFAULT_MAX_EXCERPT_CHARS, DEFAULT_MAX_OSCILLATIONS_PER_KEY, DEFAULT_MAX_PAIR_EXCERPT_CHARS, DEFAULT_MAX_PINNED_WORKTREES, DEFAULT_MAX_POOL_PER_PAIR, DEFAULT_MAX_QUOTA_DENIALS, DEFAULT_MAX_REVISIONS_PER_RUN, DEFAULT_MAX_RUN_FACT_PAIRS, DEFAULT_MAX_TOTAL_SPAWNS, DEFAULT_MAX_TURNS, DEFAULT_MODEL_RETRY_ATTEMPTS, DEFAULT_NO_PROGRESS_TURNS, DEFAULT_PER_RUN_CONCURRENCY, DEFAULT_RETRY_POLICY, DEFAULT_STREAM_IDLE_TIMEOUT_MS, DEFAULT_SYNTHESIS_MAX_TURNS, DEFAULT_SYNTHESIS_NOTE_MAX_TURNS, DedupIndex, DeterminismError, EMIT_RESULT_TOOL, EMPTY_AUTHORITY_HASH, EMPTY_SCHEMA_HASH, EMPTY_TOOLSET_HASH, ESCALATE_TOOL_NAME, ESCALATION_REPORT_SCHEMA, ESCALATION_REQUEST_SCHEMA, EVENT_SEGMENT_STRIDE, EXPOSURE_WAIT_SWEEP_MS, EscalationDecisionAbortedError, EventBus, ExternalRegistry, FINALIZE_SYNTHESIS_INSTRUCTION, FINISH_SCHEMA, FINISH_SECTIONAL_SCHEMA, FINISH_TOOL_NAME, FUTURE_RATES_TOLERANCE_MS, FailRunError, FileModelKnowledgeStore, FileTranscriptStore, GET_CHILD_RESULT_SCHEMA, GET_CHILD_RESULT_TOOL_NAME, GET_SETTLED_CHILD_RESULTS_SCHEMA, GET_SETTLED_CHILD_RESULTS_TOOL_NAME, GitWorktreeProvider, IMPLEMENTATION_PROFILE_LIMITS, INBOX_PROPOSAL_TTL_DAYS, IN_FLIGHT_EXPOSURE_REFUSAL_PREFIX, InMemoryStore, InMemoryTranscriptStore, InProcessRunner, InvalidResolutionError, JOURNAL_ENVELOPE_MARKER, JournalCompatibilityError, JournalMatcher, JournalMissError, JournalOrderViolation, JournalSealedError, JsonlFileStore, KB_ACTIVE_CLAIMS_CAP, KB_CARD_RENDER_BUDGET_CHARS, KeyedLimiter, KnowledgeCasError, LARGE_VALUE_WARN_BYTES, LEGACY_LTID_PREFIX, LEGACY_SIGNATURE_INPUTS, LINEAGE_SIG_VERSION, LeaseHeldError, LineageIndex, MASKED_SECRET, MAX_CHILD_RESULT_PAGE_CHARS, MAX_CRITICAL_UNCOVERED, MAX_DEPTH_CEILING, MAX_RUN_FACTS_SHEET_CHARS, MAX_RUN_ID_LENGTH, MAX_TIMER_DELAY_MS, ModelRetry, NoProgressDetector, NonSerializableValueError, ORCHESTRATE_WORKFLOW_NAME, OrchestratorCapConfigError, PARALLEL_AGENTS_SCHEMA, PROGRESS_REPORT_TOOL_NAME, ParallelSiteCounter, PlanInvariantError, QUOTA_WINDOW_MS, READ_CHILD_ARTIFACT_SCHEMA, READ_CHILD_ARTIFACT_TOOL_NAME, RESEARCH_PROFILE_LIMITS, REVIEW_PROFILE_LIMITS, ROLE_EFFORT_DEFAULTS, ROOT_ACCOUNT, ROOT_SCOPE, RUN_FACTS_ANCHOR, RUN_PROFILES, RUN_SETTLE_DECISION_TYPE, ReplayPlanHashMismatch, Replayer, ResolutionArbiter, ResolutionFold, RulvarError, RunBudget, SANDBOX_AGENT_OPT_KEYS, SPAWN_ADMISSION_DECISION_TYPE, SPAWN_AGENT_SCHEMA, SandboxError, ScriptRejected, Semaphore, SettlementError, SpanRegistry, SupersededError, TERMINAL_TELEMETRY_SCOPE, TOOL_NAME_PATTERN, TerminationAccount, WAIT_FOR_EVENTS_SCHEMA, WAIT_FOR_EVENTS_TOOL_NAME, WAKE_SUMMARY_RENDER_BUDGET_CHARS, accountSpendFromJournal, admissionReserveUsd, affordableOutputTokens, agentErrorFromWire, agentErrorToWire, agentResultWire, agentScope, applyClaimOps, applyStructuredOutputTier, approachSigCoarse, approachSigOf, archiveDeprecatedModelOps, assertFencedWrites, assertSafeRunId, atCompactionThreshold, attestToolset, auditRun, auditRuns, buildAbandonFold, buildAdapterRegistry, buildCostReport, buildDeriverRegistry, buildOrchestratorTools, buildTerminationInitValue, buildToolContext, canRideLoopTurn, canonicalIsolationTag, canonicalizeLadder, canonicalizeSchema, capIssues, capsHashOf, checkFloors, checkpointRefFor, childCoveragePrefix, childRostersFromJournal, citationTargetsValidator, citedValueValidator, claimCoverageOf, claimExpired, claimExpiry, claimIssues, claimOpIssues, classifyAgentError, classifyAttemptOutcome, collectDeclaredLadders, compactMessages, compareRates, compilePermissionChain, compilePermissionPreset, compileSecretMasker, compileVerifiedLayer, constantTimeEqual, costReportFromJournal, countsAgainstLimit, createCanonicalIdMinter, createCtx, createEngine, createEnvelopeEncryption, createSandboxBridge, currentOnlyKeyRing, decodeCheckpoint, dedupeRepeatedClaims, defineWorkflow, deriveContentKey, deriverV1, deriverV2, digestOf, dispatchProjectionReserveUsd, dispositionHook, emptyDigestBlocks, emptyToolset, encodeCheckpoint, enforceToolsetAttestation, entryUsageSlices, escalateTool, evaluatePermission, evaluateReuse, evidenceGradeValidator, evidencePreservedValidator, executeWorkflow, executionFactsOf, exhaustionCodeOf, extractCandidate, failoverTriggerOf, fallbackTriggerOf, filterClaimsForRun, finalizeFires, findContradictions, finishContract, foldLedger, foldTermination, formatCharacterValidator, formatRePrompt, formatScopePath, hasFencedWrites, hasMetaLookup, hashRunArgs, hashRunOutput, hashWorkflowBody, hashWorkflowSource, headingStructureValidator, identityJcs, implementationAgentProfile, invoiceFromJournal, isEscalated, isSchemaPairSpec, isStandardSchemaSpec, isStrictCompatibleSchema, journalPricingSnapshot, kMaxOf, knowledgeHash, ladderLengthOf, ladderRungChoice, lastRunSettle, latestProgressReport, lexShellCommand, liftRetainedParts, lineageWeightOf, localKeyProvider, logicalRunTelemetry, makeOrchestratorWorkflow, maskSecrets, maskSecretsDeep, maskSecretsJson, matchArgvPattern, matchShellCommand, mcp, memoryQuotaLimiter, mergeQuotaDenial, mergeUsageLimits, metaMatchesFilter, minMatchesValidator, modelEpochOf, modelKnowledgeCard, modelSpecIdentity, needsSeparateExtract, nextFailover, nodeLinkKey, normalizeApproachTag, normalizeEntry, normalizeFallbacks, orchestrate, orchestratorAdmissionEstCostUsd, pairDraftClaims, pairRunFactClaims, parallelScope, parseModelRef, parseScopePath, persistedTerminalEnvelope, phiInitialOf, pilotAgentProfile, pipelineScope, planNodeScope, preflightEstimate, priceComponentsOf, priceEntryBilling, priceEntryUsage, priceUsdOf, profileCard, profileRegistrySnapshotHash, progressReportTool, projectHistory, projectIdentity, projectToJsonSchema, proposalStatement, providerOf, quotaActualRequestsDelta, quotaActualTokens, quotaEstimateTokens, quotaRuleAdmission, quotaRuleKey, quotaRuleMatches, readRunMeta, readTerminationInit, reconcileRunMeta, reconcileStatement, reduceAuditTrail, reduceCriticalPath, reduceDecisionChain, reduceInvocationTable, registryKeyRing, remeasureQueue, replayDisposition, repositoryResearchToolset, requiredFieldsValidator, requiredSectionsValidator, researchAgentProfile, resolveModelInvocation, resolvePricing, resolveToolset, retryClassOf, retryDelayMs, reviewAgentProfile, roleConfiguredInRouting, roundOneDisposition, runAgent, runProfile, sanitizeTerminalText, sanitizeTokenCount, sanitizeUsage, sanitizeUsageDelta, scanJournalCompatibility, schemaHash, schemaHashOfSpec, sectionCitationsValidator, sectionPatternCountValidator, selectStructuredOutputTier, selfTestFinishValidation, shouldCompact, snapshotQuotaRules, snapshotUsage, spawnDepthOf, spliceSections, statementFromRows, stripFencedBlocks, sumUsage, summarizeInstruction, summarizeOutput, terminalEnvelopeOf, terminationConfigDrift, tierWithinCaps, toApprovalDecision, toJournalValue, tool, toolAuthority, toolContract, toolContractHash, toolsetAuthorityHash, toolsetHash, ttlState, usageViolations, validateDetachedResolution, validateEditorialCommit, validateEngineQuotaConfig, validateEntryShape, validateEscalationLimits, validateEscalationReport, validateQuotaRules, validateRetryPolicy, validateSchemaSpec, validateTerminationLimits, validateToolsetAttestation, validateUsageLimits, wordCountValidator, workflowScope, workflowSourceRef, wrapJournalStore, wrapTranscriptStore };
28742
+ export { AWAIT_SCHEMA, AdmissionController, AdmissionRejectedError, AgentCallError, BUDGET_ABORT_REASON, BudgetExhaustedError, CANCEL_AGENT_SCHEMA, CHECKPOINT_FORMAT_V1, CLAIM_JUDGE_LABEL, CLAIM_STATEMENT_MAX_CHARS, CLAIM_TTL_DAYS, COMPACTION_SUMMARY_PREFIX, CURRENT_HASH_VERSION, ConfigError, DECISION_CHAIN_KINDS, DEFAULT_ANCHOR_PATTERN, DEFAULT_ARTIFACT_PATTERN, DEFAULT_CHILD_BUDGET_FRACTION, DEFAULT_CHILD_RESULT_PAGE_CHARS, DEFAULT_CITATION_PATTERN, DEFAULT_CITATION_SAMPLE, DEFAULT_CLAIM_JUDGE_MAX_TURNS, DEFAULT_COMPACTION_THRESHOLD, DEFAULT_ESCALATION_LIMITS, DEFAULT_EVIDENCE_CALLS_PER_ENTRY, DEFAULT_EVIDENCE_GRADE_PHRASES, DEFAULT_EVIDENCE_MIN_SHARE, DEFAULT_EVIDENCE_OVERHEAD_CALLS, DEFAULT_FINISH_MAX_REPAIRS, DEFAULT_FLAT_RESERVE_USD, DEFAULT_MAX_CHILDREN_PER_NODE, DEFAULT_MAX_CLAIM_PAIRS, DEFAULT_MAX_CONTRADICTIONS, DEFAULT_MAX_DEPTH, DEFAULT_MAX_EXCERPT_CHARS, DEFAULT_MAX_OSCILLATIONS_PER_KEY, DEFAULT_MAX_PAIR_EXCERPT_CHARS, DEFAULT_MAX_PINNED_WORKTREES, DEFAULT_MAX_POOL_PER_PAIR, DEFAULT_MAX_QUOTA_DENIALS, DEFAULT_MAX_REVISIONS_PER_RUN, DEFAULT_MAX_RUN_FACT_PAIRS, DEFAULT_MAX_TOTAL_SPAWNS, DEFAULT_MAX_TURNS, DEFAULT_MODEL_RETRY_ATTEMPTS, DEFAULT_NO_PROGRESS_TURNS, DEFAULT_PER_RUN_CONCURRENCY, DEFAULT_RETRY_POLICY, DEFAULT_STREAM_IDLE_TIMEOUT_MS, DEFAULT_SYNTHESIS_MAX_TURNS, DEFAULT_SYNTHESIS_NOTE_MAX_TURNS, DedupIndex, DeterminismError, EMIT_RESULT_TOOL, EMPTY_AUTHORITY_HASH, EMPTY_SCHEMA_HASH, EMPTY_TOOLSET_HASH, ESCALATE_TOOL_NAME, ESCALATION_REPORT_SCHEMA, ESCALATION_REQUEST_SCHEMA, EVENT_SEGMENT_STRIDE, EXPOSURE_WAIT_SWEEP_MS, EscalationDecisionAbortedError, EventBus, ExternalRegistry, FINALIZE_SYNTHESIS_INSTRUCTION, FINAL_COMPOSITION_LABEL, FINISH_SCHEMA, FINISH_SECTIONAL_SCHEMA, FINISH_TOOL_NAME, FUTURE_RATES_TOLERANCE_MS, FailRunError, FileModelKnowledgeStore, FileTranscriptStore, GET_CHILD_RESULT_SCHEMA, GET_CHILD_RESULT_TOOL_NAME, GET_SETTLED_CHILD_RESULTS_SCHEMA, GET_SETTLED_CHILD_RESULTS_TOOL_NAME, GitWorktreeProvider, IMPLEMENTATION_PROFILE_LIMITS, INBOX_PROPOSAL_TTL_DAYS, IN_FLIGHT_EXPOSURE_REFUSAL_PREFIX, InMemoryStore, InMemoryTranscriptStore, InProcessRunner, InvalidResolutionError, JOURNAL_ENVELOPE_MARKER, JournalCompatibilityError, JournalMatcher, JournalMissError, JournalOrderViolation, JournalSealedError, JsonlFileStore, KB_ACTIVE_CLAIMS_CAP, KB_CARD_RENDER_BUDGET_CHARS, KeyedLimiter, KnowledgeCasError, LARGE_VALUE_WARN_BYTES, LEGACY_LTID_PREFIX, LEGACY_SIGNATURE_INPUTS, LINEAGE_SIG_VERSION, LeaseHeldError, LineageIndex, MASKED_SECRET, MAX_CHILD_RESULT_PAGE_CHARS, MAX_CRITICAL_UNCOVERED, MAX_DEPTH_CEILING, MAX_RUN_FACTS_SHEET_CHARS, MAX_RUN_ID_LENGTH, MAX_TIMER_DELAY_MS, ModelRetry, NoProgressDetector, NonSerializableValueError, ORCHESTRATE_WORKFLOW_NAME, OrchestratorCapConfigError, PARALLEL_AGENTS_SCHEMA, PROGRESS_REPORT_TOOL_NAME, ParallelSiteCounter, PlanInvariantError, QUOTA_WINDOW_MS, READ_CHILD_ARTIFACT_SCHEMA, READ_CHILD_ARTIFACT_TOOL_NAME, RESEARCH_PROFILE_LIMITS, REVIEW_PROFILE_LIMITS, ROLE_EFFORT_DEFAULTS, ROOT_ACCOUNT, ROOT_SCOPE, RUN_FACTS_ANCHOR, RUN_PROFILES, RUN_SETTLE_DECISION_TYPE, ReplayPlanHashMismatch, Replayer, ResolutionArbiter, ResolutionFold, RulvarError, RunBudget, SANDBOX_AGENT_OPT_KEYS, SPAWN_ADMISSION_DECISION_TYPE, SPAWN_AGENT_SCHEMA, SYNTHESIS_NOTE_LABEL, SandboxError, ScriptRejected, Semaphore, SettlementError, SpanRegistry, SupersededError, TERMINAL_TELEMETRY_SCOPE, TOOL_NAME_PATTERN, TerminationAccount, WAIT_FOR_EVENTS_SCHEMA, WAIT_FOR_EVENTS_TOOL_NAME, WAKE_SUMMARY_RENDER_BUDGET_CHARS, accountSpendFromJournal, admissionReserveUsd, affordableOutputTokens, agentErrorFromWire, agentErrorToWire, agentResultWire, agentScope, applyClaimOps, applyStructuredOutputTier, approachSigCoarse, approachSigOf, archiveDeprecatedModelOps, assertFencedWrites, assertSafeRunId, atCompactionThreshold, attestToolset, auditRun, auditRuns, buildAbandonFold, buildAdapterRegistry, buildCostReport, buildDeriverRegistry, buildOrchestratorTools, buildTerminationInitValue, buildToolContext, canRideLoopTurn, canonicalIsolationTag, canonicalizeLadder, canonicalizeSchema, capIssues, capsHashOf, checkFloors, checkpointRefFor, childCoveragePrefix, childRostersFromJournal, citationTargetsValidator, citedValueValidator, claimCoverageOf, claimExpired, claimExpiry, claimIssues, claimOpIssues, classifyAgentError, classifyAttemptOutcome, collectDeclaredLadders, compactMessages, compareRates, compilePermissionChain, compilePermissionPreset, compileSecretMasker, compileVerifiedLayer, constantTimeEqual, costReportFromJournal, countsAgainstLimit, createCanonicalIdMinter, createCtx, createEngine, createEnvelopeEncryption, createSandboxBridge, criticalPathFromJournal, currentOnlyKeyRing, decodeCheckpoint, dedupeRepeatedClaims, defineWorkflow, deriveContentKey, deriverV1, deriverV2, digestOf, dispatchProjectionReserveUsd, dispositionHook, emptyDigestBlocks, emptyToolset, encodeCheckpoint, enforceToolsetAttestation, entryUsageSlices, escalateTool, evaluatePermission, evaluateReuse, evidenceGradeValidator, evidencePreservedValidator, executeWorkflow, executionFactsOf, exhaustionCodeOf, extractCandidate, failoverTriggerOf, fallbackTriggerOf, filterClaimsForRun, finalizeFires, findContradictions, finishContract, foldLedger, foldTermination, formatCharacterValidator, formatRePrompt, formatScopePath, hasFencedWrites, hasMetaLookup, hashRunArgs, hashRunOutput, hashWorkflowBody, hashWorkflowSource, headingStructureValidator, identityJcs, implementationAgentProfile, invoiceFromJournal, isEscalated, isSchemaPairSpec, isStandardSchemaSpec, isStrictCompatibleSchema, journalPricingSnapshot, kMaxOf, knowledgeHash, ladderLengthOf, ladderRungChoice, lastRunSettle, latestProgressReport, lexShellCommand, liftRetainedParts, lineageWeightOf, localKeyProvider, logicalRunTelemetry, makeOrchestratorWorkflow, maskSecrets, maskSecretsDeep, maskSecretsJson, matchArgvPattern, matchShellCommand, mcp, memoryQuotaLimiter, mergeQuotaDenial, mergeUsageLimits, metaMatchesFilter, minMatchesValidator, modelEpochOf, modelKnowledgeCard, modelSpecIdentity, needsSeparateExtract, nextFailover, nodeLinkKey, normalizeApproachTag, normalizeEntry, normalizeFallbacks, orchestrate, orchestratorAdmissionEstCostUsd, pairDraftClaims, pairRunFactClaims, parallelScope, parseModelRef, parseScopePath, persistedTerminalEnvelope, phiInitialOf, pilotAgentProfile, pipelineScope, planNodeScope, preflightEstimate, priceComponentsOf, priceEntryBilling, priceEntryUsage, priceUsdOf, profileCard, profileRegistrySnapshotHash, progressReportTool, projectHistory, projectIdentity, projectToJsonSchema, proposalStatement, providerOf, quotaActualRequestsDelta, quotaActualTokens, quotaEstimateTokens, quotaRuleAdmission, quotaRuleKey, quotaRuleMatches, readRunMeta, readTerminationInit, reconcileRunMeta, reconcileStatement, reduceAuditTrail, reduceCriticalPath, reduceDecisionChain, reduceInvocationTable, registryKeyRing, remeasureQueue, replayDisposition, repositoryResearchToolset, requiredFieldsValidator, requiredSectionsValidator, researchAgentProfile, resolveModelInvocation, resolvePricing, resolveToolset, retryClassOf, retryDelayMs, reviewAgentProfile, roleConfiguredInRouting, roundOneDisposition, runAgent, runProfile, sanitizeTerminalText, sanitizeTokenCount, sanitizeUsage, sanitizeUsageDelta, scanJournalCompatibility, schemaHash, schemaHashOfSpec, sectionCitationsValidator, sectionPatternCountValidator, selectStructuredOutputTier, selfTestFinishValidation, shouldCompact, snapshotQuotaRules, snapshotUsage, spawnDepthOf, spliceSections, statementFromRows, statementRowsFromDelimited, stripFencedBlocks, sumUsage, summarizeInstruction, summarizeOutput, synthesisCandidatesFromJournal, terminalEnvelopeOf, terminationConfigDrift, tierWithinCaps, toApprovalDecision, toJournalValue, tool, toolAuthority, toolContract, toolContractHash, toolsetAuthorityHash, toolsetHash, ttlState, usageViolations, validateDetachedResolution, validateEditorialCommit, validateEngineQuotaConfig, validateEntryShape, validateEscalationLimits, validateEscalationReport, validateQuotaRules, validateRetryPolicy, validateSchemaSpec, validateTerminationLimits, validateToolsetAttestation, validateUsageLimits, wordCountValidator, workflowScope, workflowSourceRef, wrapJournalStore, wrapTranscriptStore };