@rulvar/core 1.105.0 → 1.107.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.
- package/dist/index.d.ts +11 -3
- package/dist/index.js +65 -18
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -5978,8 +5978,9 @@ declare class AdmissionController {
|
|
|
5978
5978
|
declare function buildCostReport(attribution: CostAttribution, totalUsd: number, abandoned?: CostReport["abandoned"]): CostReport;
|
|
5979
5979
|
/**
|
|
5980
5980
|
* The pure journal fold: the complete CostReport from terminal entries,
|
|
5981
|
-
* the same summation the kernel ledger uses (terminal
|
|
5982
|
-
* once, priced per servedBy slice, abandoned
|
|
5981
|
+
* the same summation the kernel ledger uses (each terminal entry's
|
|
5982
|
+
* usage enters the sum once, priced per servedBy slice, abandoned
|
|
5983
|
+
* subtrees contribute zero).
|
|
5983
5984
|
* The orchestrator block folds too: spend attributed to the
|
|
5984
5985
|
* orchestrator sub-account, the reserve-funded share of it, the armed
|
|
5985
5986
|
* wake count, and the at-cap freeze flag from the journaled cap
|
|
@@ -6873,7 +6874,14 @@ declare const DEFAULT_EVIDENCE_MIN_SHARE = .95;
|
|
|
6873
6874
|
* list the missing (and unknown) citations, capped at 20, so the repair
|
|
6874
6875
|
* turn can restore them. Purely textual and deterministic; checking
|
|
6875
6876
|
* that cited targets EXIST on disk is host territory (a custom
|
|
6876
|
-
* validator), not this contract.
|
|
6877
|
+
* validator), not this contract. Intake is fail closed (RV610): a
|
|
6878
|
+
* pattern that can match the empty string is refused typed (an empty
|
|
6879
|
+
* match would enter the pool as fabricated evidence and defeat
|
|
6880
|
+
* `requireNonEmptyPool`), zero-length matches never enter the pool
|
|
6881
|
+
* even when a lookaround produces them in context, and the strict-mode
|
|
6882
|
+
* booleans must be real booleans, so a stray `'true'` can never
|
|
6883
|
+
* silently disable the mode it names. Default name
|
|
6884
|
+
* 'evidence-preserved'.
|
|
6877
6885
|
*/
|
|
6878
6886
|
declare function evidencePreservedValidator(options?: {
|
|
6879
6887
|
pattern?: string;
|
package/dist/index.js
CHANGED
|
@@ -2363,6 +2363,7 @@ function entryUsageSlices(entry) {
|
|
|
2363
2363
|
usage: entry.usage
|
|
2364
2364
|
}];
|
|
2365
2365
|
}
|
|
2366
|
+
const foldOverflow = (seq, servedBy) => new ConfigError(`cost accounting overflow: the price sum for entry seq ${String(seq)} became non-finite at model ${servedBy} (individually finite prices overflowed); no public report may carry a non-finite number`);
|
|
2366
2367
|
/**
|
|
2367
2368
|
* The single pricing fold over one terminal entry, shared by the kernel
|
|
2368
2369
|
* ledger and the CostReport fold so a run's total and its per-model
|
|
@@ -2388,6 +2389,7 @@ function priceEntryUsage(entry, priceUsd) {
|
|
|
2388
2389
|
continue;
|
|
2389
2390
|
}
|
|
2390
2391
|
result.usd += usd;
|
|
2392
|
+
if (!Number.isFinite(result.usd)) throw foldOverflow(entry.seq, slice.servedBy);
|
|
2391
2393
|
result.priced.push({
|
|
2392
2394
|
...slice,
|
|
2393
2395
|
usd
|
|
@@ -2486,6 +2488,7 @@ function priceEntryBilling(entry, priceUsd) {
|
|
|
2486
2488
|
continue;
|
|
2487
2489
|
}
|
|
2488
2490
|
usd += price;
|
|
2491
|
+
if (!Number.isFinite(usd)) throw foldOverflow(entry.seq, record.servedBy);
|
|
2489
2492
|
units.push({
|
|
2490
2493
|
source: "call",
|
|
2491
2494
|
servedBy: record.servedBy,
|
|
@@ -2512,6 +2515,7 @@ function priceEntryBilling(entry, priceUsd) {
|
|
|
2512
2515
|
continue;
|
|
2513
2516
|
}
|
|
2514
2517
|
usd += price;
|
|
2518
|
+
if (!Number.isFinite(usd)) throw foldOverflow(entry.seq, slice.servedBy);
|
|
2515
2519
|
units.push({
|
|
2516
2520
|
source: "slice",
|
|
2517
2521
|
servedBy: slice.servedBy,
|
|
@@ -2827,6 +2831,31 @@ function validateEvidenceContract(value, site) {
|
|
|
2827
2831
|
if (overheadCalls !== void 0) requireNonNegativeInteger(overheadCalls, `${site}.overheadCalls`);
|
|
2828
2832
|
if (enforce !== void 0 && enforce !== "warn" && enforce !== "refuse") throw new ConfigError(`${site}.enforce must be 'warn' or 'refuse'; got ${JSON.stringify(enforce)}`);
|
|
2829
2833
|
}
|
|
2834
|
+
/**
|
|
2835
|
+
* Walks a finished public accounting object (a cost report, an
|
|
2836
|
+
* invoice) and throws a typed ConfigError on the first non-finite
|
|
2837
|
+
* number (RV610): JSON serializes Infinity and NaN as null, so a
|
|
2838
|
+
* non-finite value in a published report is silent telemetry
|
|
2839
|
+
* corruption, never a representable answer. Individually finite
|
|
2840
|
+
* amounts can overflow in accumulation, which is exactly why the
|
|
2841
|
+
* boundary is guarded and not only the per-item validations.
|
|
2842
|
+
*/
|
|
2843
|
+
function requireFiniteNumbersDeep(value, site) {
|
|
2844
|
+
const walk = (node, path) => {
|
|
2845
|
+
if (typeof node === "number") {
|
|
2846
|
+
if (!Number.isFinite(node)) throw new ConfigError(`cost accounting overflow at ${path}: the value is ${String(node)}; no public report or invoice may carry a non-finite number (JSON would serialize it as null)`);
|
|
2847
|
+
return;
|
|
2848
|
+
}
|
|
2849
|
+
if (Array.isArray(node)) {
|
|
2850
|
+
node.forEach((item, index) => {
|
|
2851
|
+
walk(item, `${path}[${String(index)}]`);
|
|
2852
|
+
});
|
|
2853
|
+
return;
|
|
2854
|
+
}
|
|
2855
|
+
if (typeof node === "object" && node !== null) for (const [key, item] of Object.entries(node)) walk(item, `${path}.${key}`);
|
|
2856
|
+
};
|
|
2857
|
+
walk(value, site);
|
|
2858
|
+
}
|
|
2830
2859
|
//#endregion
|
|
2831
2860
|
//#region src/knowledge/file-store.ts
|
|
2832
2861
|
/**
|
|
@@ -8310,8 +8339,9 @@ function buildCostReport(attribution, totalUsd, abandoned = {
|
|
|
8310
8339
|
}
|
|
8311
8340
|
/**
|
|
8312
8341
|
* The pure journal fold: the complete CostReport from terminal entries,
|
|
8313
|
-
* the same summation the kernel ledger uses (terminal
|
|
8314
|
-
* once, priced per servedBy slice, abandoned
|
|
8342
|
+
* the same summation the kernel ledger uses (each terminal entry's
|
|
8343
|
+
* usage enters the sum once, priced per servedBy slice, abandoned
|
|
8344
|
+
* subtrees contribute zero).
|
|
8315
8345
|
* The orchestrator block folds too: spend attributed to the
|
|
8316
8346
|
* orchestrator sub-account, the reserve-funded share of it, the armed
|
|
8317
8347
|
* wake count, and the at-cap freeze flag from the journaled cap
|
|
@@ -8370,7 +8400,7 @@ function costReportFromJournal(entries, priceUsd) {
|
|
|
8370
8400
|
if (facts.finalizeReserve === true) reserveUsedUsd += priced.usd;
|
|
8371
8401
|
}
|
|
8372
8402
|
}
|
|
8373
|
-
|
|
8403
|
+
const report = {
|
|
8374
8404
|
totalUsd,
|
|
8375
8405
|
grossUsd: totalUsd + abandonedUsd,
|
|
8376
8406
|
abandoned: {
|
|
@@ -8392,6 +8422,8 @@ function costReportFromJournal(entries, priceUsd) {
|
|
|
8392
8422
|
unpriced,
|
|
8393
8423
|
...usageApprox ? { usageApprox: true } : {}
|
|
8394
8424
|
};
|
|
8425
|
+
requireFiniteNumbersDeep(report, "costReport");
|
|
8426
|
+
return report;
|
|
8395
8427
|
}
|
|
8396
8428
|
//#endregion
|
|
8397
8429
|
//#region src/engine/invoice.ts
|
|
@@ -8628,7 +8660,7 @@ function invoiceFromJournal(entries, priceUsd, options) {
|
|
|
8628
8660
|
}
|
|
8629
8661
|
const unallocatedUsd = allocateRows(rows, entries, priceUsd, report.grossUsd);
|
|
8630
8662
|
const usageApprox = report.usageApprox === true || report.abandoned.usageApprox === true;
|
|
8631
|
-
|
|
8663
|
+
const invoice = {
|
|
8632
8664
|
rows,
|
|
8633
8665
|
totalUsd: report.grossUsd,
|
|
8634
8666
|
netUsd: report.totalUsd,
|
|
@@ -8645,6 +8677,8 @@ function invoiceFromJournal(entries, priceUsd, options) {
|
|
|
8645
8677
|
...usageApprox ? { usageApprox: true } : {},
|
|
8646
8678
|
...options?.pricing === void 0 ? {} : { pricing: options.pricing }
|
|
8647
8679
|
};
|
|
8680
|
+
requireFiniteNumbersDeep(invoice, "invoice");
|
|
8681
|
+
return invoice;
|
|
8648
8682
|
}
|
|
8649
8683
|
//#endregion
|
|
8650
8684
|
//#region src/model/pricing.ts
|
|
@@ -16510,26 +16544,39 @@ function listCitations(values) {
|
|
|
16510
16544
|
* list the missing (and unknown) citations, capped at 20, so the repair
|
|
16511
16545
|
* turn can restore them. Purely textual and deterministic; checking
|
|
16512
16546
|
* that cited targets EXIST on disk is host territory (a custom
|
|
16513
|
-
* validator), not this contract.
|
|
16547
|
+
* validator), not this contract. Intake is fail closed (RV610): a
|
|
16548
|
+
* pattern that can match the empty string is refused typed (an empty
|
|
16549
|
+
* match would enter the pool as fabricated evidence and defeat
|
|
16550
|
+
* `requireNonEmptyPool`), zero-length matches never enter the pool
|
|
16551
|
+
* even when a lookaround produces them in context, and the strict-mode
|
|
16552
|
+
* booleans must be real booleans, so a stray `'true'` can never
|
|
16553
|
+
* silently disable the mode it names. Default name
|
|
16554
|
+
* 'evidence-preserved'.
|
|
16514
16555
|
*/
|
|
16515
16556
|
function evidencePreservedValidator(options) {
|
|
16516
16557
|
const pattern = options?.pattern ?? "[\\w./-]+\\.\\w+:\\d+";
|
|
16517
16558
|
const flags = options?.flags ?? "";
|
|
16518
16559
|
const globalFlags = flags.includes("g") ? flags : `${flags}g`;
|
|
16560
|
+
let probe;
|
|
16519
16561
|
try {
|
|
16520
|
-
new RegExp(pattern,
|
|
16562
|
+
probe = new RegExp(pattern, flags.replace("g", ""));
|
|
16521
16563
|
} catch (thrown) {
|
|
16522
16564
|
throw new ConfigError(`evidencePreservedValidator pattern does not compile: ${thrown instanceof Error ? thrown.message : String(thrown)}`);
|
|
16523
16565
|
}
|
|
16566
|
+
if (probe.test("")) throw new ConfigError(`evidencePreservedValidator pattern must not be able to match the empty string (an empty match would enter the citation pool as fabricated evidence); got ${JSON.stringify(pattern)}`);
|
|
16524
16567
|
const minShare = options?.minShare ?? .95;
|
|
16525
16568
|
if (typeof minShare !== "number" || !Number.isFinite(minShare) || minShare <= 0 || minShare > 1) throw new ConfigError(`evidencePreservedValidator minShare must be a number in (0, 1]; got ${String(minShare)}`);
|
|
16569
|
+
for (const option of ["requireKnown", "requireNonEmptyPool"]) {
|
|
16570
|
+
const value = options?.[option];
|
|
16571
|
+
if (value !== void 0 && typeof value !== "boolean") throw new ConfigError(`evidencePreservedValidator ${option} must be a boolean when given; got ${JSON.stringify(value)}`);
|
|
16572
|
+
}
|
|
16526
16573
|
return {
|
|
16527
16574
|
name: options?.name ?? "evidence-preserved",
|
|
16528
16575
|
validate: (input) => {
|
|
16529
16576
|
const cited = /* @__PURE__ */ new Set();
|
|
16530
16577
|
for (const child of input.children ?? []) {
|
|
16531
16578
|
if (child.status !== "ok" && child.salvageableOutput !== true) continue;
|
|
16532
|
-
for (const match of child.text.match(new RegExp(pattern, globalFlags)) ?? []) cited.add(match);
|
|
16579
|
+
for (const match of child.text.match(new RegExp(pattern, globalFlags)) ?? []) if (match.length > 0) cited.add(match);
|
|
16533
16580
|
}
|
|
16534
16581
|
const reasons = [];
|
|
16535
16582
|
if (cited.size === 0 && options?.requireNonEmptyPool === true) reasons.push("empty child citation pool: no ok child output contains a citation matching the pattern");
|
|
@@ -16539,7 +16586,7 @@ function evidencePreservedValidator(options) {
|
|
|
16539
16586
|
if (preserved < Math.ceil(minShare * cited.size - 1e-9)) reasons.push(`evidence preservation ${String(preserved)} of ${String(cited.size)} child citations is below the required share ${String(minShare)}; missing: ${listCitations(missing)}`);
|
|
16540
16587
|
}
|
|
16541
16588
|
if (options?.requireKnown === true) {
|
|
16542
|
-
const fabricated = [...new Set(input.text.match(new RegExp(pattern, globalFlags)) ?? [])].filter((citation) => !cited.has(citation));
|
|
16589
|
+
const fabricated = [...new Set(input.text.match(new RegExp(pattern, globalFlags)) ?? [])].filter((citation) => citation.length > 0 && !cited.has(citation));
|
|
16543
16590
|
if (fabricated.length > 0) reasons.push(`unknown citations not present in any child report: ${listCitations(fabricated)}`);
|
|
16544
16591
|
}
|
|
16545
16592
|
return reasons.length === 0 ? ok : {
|
|
@@ -17623,7 +17670,7 @@ function makeOrchestratorWorkflow(goal, opts) {
|
|
|
17623
17670
|
}, { childScope });
|
|
17624
17671
|
const dispatched = internals.replayer.snapshot().find((entry) => entry.seq === record.handle);
|
|
17625
17672
|
if (dispatched !== void 0) {
|
|
17626
|
-
for (const prior of internals.replayer.snapshot()) if (prior.kind === "agent" && prior.status === "running" && prior.seq !== record.handle && prior.scope === dispatched.scope && prior.key === dispatched.key &&
|
|
17673
|
+
for (const prior of internals.replayer.snapshot()) if (prior.kind === "agent" && prior.status === "running" && prior.seq !== record.handle && prior.scope === dispatched.scope && prior.key === dispatched.key && !records.has(prior.seq)) records.set(prior.seq, record);
|
|
17627
17674
|
}
|
|
17628
17675
|
}
|
|
17629
17676
|
for (const entry of internals.replayer.snapshot()) {
|
|
@@ -17700,7 +17747,7 @@ function makeOrchestratorWorkflow(goal, opts) {
|
|
|
17700
17747
|
extension?.onWake?.(digest);
|
|
17701
17748
|
};
|
|
17702
17749
|
const buildDigest = (ordinal) => {
|
|
17703
|
-
const undelivered = [...
|
|
17750
|
+
const undelivered = [...byOrdinal.values()].filter((record) => record.settled !== void 0 && !deliveredNodeIds.has(record.nodeId)).sort((a, b) => a.spawnOrdinal - b.spawnOrdinal);
|
|
17704
17751
|
const escalations = [];
|
|
17705
17752
|
for (const record of undelivered) {
|
|
17706
17753
|
const settled = record.settled;
|
|
@@ -17891,7 +17938,7 @@ function makeOrchestratorWorkflow(goal, opts) {
|
|
|
17891
17938
|
})) throw new ConfigError("child_terminal can never fire: every referenced child already settled and was delivered in a prior digest");
|
|
17892
17939
|
}
|
|
17893
17940
|
if (trigger.kind === "escalation") {
|
|
17894
|
-
if (![...
|
|
17941
|
+
if (![...byOrdinal.values()].some((record) => record.settled === void 0 || record.settled.status === "escalated" && !deliveredNodeIds.has(record.nodeId))) throw new ConfigError("escalation can never fire: no live or undelivered escalated children");
|
|
17895
17942
|
}
|
|
17896
17943
|
}
|
|
17897
17944
|
const ordinal = wakeOrdinal;
|
|
@@ -17902,9 +17949,9 @@ function makeOrchestratorWorkflow(goal, opts) {
|
|
|
17902
17949
|
await internals.replayer.flush();
|
|
17903
17950
|
const entryRef = external.pending().find((item) => item.key === wakeKey && item.scope === wakeScope)?.entryRef;
|
|
17904
17951
|
const isReady = (trigger) => {
|
|
17905
|
-
const undelivered = [...
|
|
17952
|
+
const undelivered = [...byOrdinal.values()].filter((record) => record.settled !== void 0 && !deliveredNodeIds.has(record.nodeId));
|
|
17906
17953
|
switch (trigger.kind) {
|
|
17907
|
-
case "quiescence": return [...
|
|
17954
|
+
case "quiescence": return [...byOrdinal.values()].every((record) => record.settled !== void 0) && (extension?.quiescent?.() ?? true);
|
|
17908
17955
|
case "child_terminal":
|
|
17909
17956
|
if (trigger.handles === void 0) return undelivered.length > 0;
|
|
17910
17957
|
return trigger.handles.some((handle) => undelivered.some((record) => record.handle === handle));
|
|
@@ -18142,7 +18189,7 @@ function makeOrchestratorWorkflow(goal, opts) {
|
|
|
18142
18189
|
*/
|
|
18143
18190
|
const validationChildren = () => {
|
|
18144
18191
|
const salvageOutputOn = opts?.acceptance?.acceptValidatedTerminalOutputOnLimit === true;
|
|
18145
|
-
return [...
|
|
18192
|
+
return [...byOrdinal.values()].sort((a, b) => a.spawnOrdinal - b.spawnOrdinal).map((record) => ({
|
|
18146
18193
|
handle: record.handle,
|
|
18147
18194
|
nodeId: record.nodeId,
|
|
18148
18195
|
status: record.settled?.status ?? "running",
|
|
@@ -18378,7 +18425,7 @@ function makeOrchestratorWorkflow(goal, opts) {
|
|
|
18378
18425
|
return {
|
|
18379
18426
|
forcedFinishFallback: true,
|
|
18380
18427
|
planHash: capValue?.snapshot?.planHash ?? "",
|
|
18381
|
-
completed: [...
|
|
18428
|
+
completed: [...byOrdinal.values()].filter((record) => record.settled !== void 0).sort((a, b) => a.spawnOrdinal - b.spawnOrdinal).map((record) => digestOf(record, record.settled))
|
|
18382
18429
|
};
|
|
18383
18430
|
};
|
|
18384
18431
|
/**
|
|
@@ -18453,7 +18500,7 @@ function makeOrchestratorWorkflow(goal, opts) {
|
|
|
18453
18500
|
*/
|
|
18454
18501
|
const reconcileIncremental = async (draft, spec) => {
|
|
18455
18502
|
synthesisSettleFrozen = true;
|
|
18456
|
-
const settledRecords = [...
|
|
18503
|
+
const settledRecords = [...byOrdinal.values()].filter((record) => record.settled !== void 0).sort((a, b) => a.spawnOrdinal - b.spawnOrdinal);
|
|
18457
18504
|
const sections = [];
|
|
18458
18505
|
for (const record of settledRecords) {
|
|
18459
18506
|
const settled = record.settled;
|
|
@@ -18646,7 +18693,7 @@ function makeOrchestratorWorkflow(goal, opts) {
|
|
|
18646
18693
|
const fullContext = spec.context === "full";
|
|
18647
18694
|
const synthesisToolNames = /* @__PURE__ */ new Set([FINISH_TOOL_NAME, ...exposeTools ? [GET_CHILD_RESULT_TOOL_NAME, READ_CHILD_ARTIFACT_TOOL_NAME] : []]);
|
|
18648
18695
|
const synthesisTools = buildOrchestratorTools(orchestratorRuntime, fullCardText, { childResultTools: exposeTools }).filter((tool) => synthesisToolNames.has(tool.name));
|
|
18649
|
-
const settledEntries = [...
|
|
18696
|
+
const settledEntries = [...byOrdinal.values()].filter((record) => record.settled !== void 0).sort((a, b) => a.spawnOrdinal - b.spawnOrdinal).map((record) => [record.handle, record]);
|
|
18650
18697
|
const settledDigests = settledEntries.map(([handle, record]) => ({
|
|
18651
18698
|
...exposeTools ? { handle } : {},
|
|
18652
18699
|
...digestOf(record, record.settled)
|
|
@@ -18899,7 +18946,7 @@ function makeOrchestratorWorkflow(goal, opts) {
|
|
|
18899
18946
|
let hardDegraded = 0;
|
|
18900
18947
|
const acceptPartial = opts.acceptance.acceptPartialChildren === true;
|
|
18901
18948
|
const acceptOutput = opts.acceptance.acceptValidatedTerminalOutputOnLimit === true;
|
|
18902
|
-
const sortedRecords = [...
|
|
18949
|
+
const sortedRecords = [...byOrdinal.values()].sort((a, b) => a.spawnOrdinal - b.spawnOrdinal);
|
|
18903
18950
|
for (const record of sortedRecords) {
|
|
18904
18951
|
const status = record.settled?.status ?? "running";
|
|
18905
18952
|
childStatusCounts[status] = (childStatusCounts[status] ?? 0) + 1;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rulvar/core",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.107.0",
|
|
4
4
|
"description": "Rulvar core: L0 contracts, journal kernel, ctx primitives, agent runtime, model router, tool system, dynamic orchestrator, InMemory and JSONL stores, event stream.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|