@rulvar/core 1.98.0 → 1.99.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 +69 -9
- package/dist/index.js +60 -9
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -4440,6 +4440,17 @@ interface AgentResult<T> {
|
|
|
4440
4440
|
* recoveries; absent when zero.
|
|
4441
4441
|
*/
|
|
4442
4442
|
schemaRecoveredTerminalExchanges?: number;
|
|
4443
|
+
/**
|
|
4444
|
+
* The evidence floor refusal detail (RV507): present ONLY when an
|
|
4445
|
+
* enforced contract refused an otherwise-ok settle. The ctx layer
|
|
4446
|
+
* folds it into the journaled terminal error data and memoizes the
|
|
4447
|
+
* outcome (the refusal is deterministic from the paid transcript, so
|
|
4448
|
+
* a rerun would only re-pay the same bounded failure).
|
|
4449
|
+
*/
|
|
4450
|
+
evidenceFloor?: {
|
|
4451
|
+
recordedEntries: number;
|
|
4452
|
+
minEntries: number;
|
|
4453
|
+
};
|
|
4443
4454
|
}
|
|
4444
4455
|
/** One 429's provider-normalized limits, per (provider, model). */
|
|
4445
4456
|
interface RateLimitObservation {
|
|
@@ -4667,6 +4678,20 @@ interface RunAgentOptions<S extends SchemaSpec = JsonSchema> {
|
|
|
4667
4678
|
save(state: CheckpointState): Promise<void>;
|
|
4668
4679
|
};
|
|
4669
4680
|
limits: EffectiveUsageLimits;
|
|
4681
|
+
/**
|
|
4682
|
+
* The resolved evidence contract of the invocation (RV507): under
|
|
4683
|
+
* enforce 'refuse' an ok settle whose message window carries fewer
|
|
4684
|
+
* successful `record_evidence` executions (result `recorded: true`)
|
|
4685
|
+
* than `minEntries` is refused as a typed 'terminal' error carrying
|
|
4686
|
+
* the machine-readable counter and threshold. Window-derived exactly
|
|
4687
|
+
* like the terminal partial, so live and resumed segments count the
|
|
4688
|
+
* same total. Absent, and under 'warn', the loop is byte-identical to
|
|
4689
|
+
* before.
|
|
4690
|
+
*/
|
|
4691
|
+
evidenceContract?: {
|
|
4692
|
+
minEntries: number;
|
|
4693
|
+
enforce?: "warn" | "refuse";
|
|
4694
|
+
};
|
|
4670
4695
|
/** Emits agent:stream deltas when true (telemetry only). */
|
|
4671
4696
|
stream?: boolean;
|
|
4672
4697
|
/** Host or sibling cancellation. */
|
|
@@ -6736,7 +6761,10 @@ declare const DEFAULT_EVIDENCE_MIN_SHARE = .95;
|
|
|
6736
6761
|
* {@link DEFAULT_EVIDENCE_MIN_SHARE}, the plan's 95 percent gate,
|
|
6737
6762
|
* compared as a ceiling on the required count so an exact boundary like
|
|
6738
6763
|
* 19 of 20 passes) must appear literally in the result text. Zero child
|
|
6739
|
-
* citations pass vacuously
|
|
6764
|
+
* citations pass vacuously UNLESS `requireNonEmptyPool: true` (RV507):
|
|
6765
|
+
* for an evidence-critical run the empty pool IS the failure, so that
|
|
6766
|
+
* mode refuses it with an `empty child citation pool` reason instead of
|
|
6767
|
+
* the vacuous pass. With `requireKnown: true` the contract also
|
|
6740
6768
|
* runs in reverse: every citation in the RESULT must appear in some
|
|
6741
6769
|
* child's output, so a fabricated but pattern valid citation is
|
|
6742
6770
|
* rejected instead of silently counting as evidence. Rejection reasons
|
|
@@ -6750,6 +6778,7 @@ declare function evidencePreservedValidator(options?: {
|
|
|
6750
6778
|
flags?: string;
|
|
6751
6779
|
minShare?: number;
|
|
6752
6780
|
requireKnown?: boolean;
|
|
6781
|
+
requireNonEmptyPool?: boolean;
|
|
6753
6782
|
name?: string;
|
|
6754
6783
|
}): FinishValidator;
|
|
6755
6784
|
/**
|
|
@@ -7500,6 +7529,19 @@ interface OrchestrateAcceptance {
|
|
|
7500
7529
|
*/
|
|
7501
7530
|
acceptPartialChildren?: boolean;
|
|
7502
7531
|
/**
|
|
7532
|
+
* The spawned-roster floor (RV507): finish is rejected when FEWER
|
|
7533
|
+
* than this many children were spawned, under BOTH child policies.
|
|
7534
|
+
* 'all-ok' alone treats zero spawned children as vacuously complete
|
|
7535
|
+
* (spawn nothing you do not need to succeed), which lets a
|
|
7536
|
+
* fan-out-shaped task settle ok without ever fanning out; the floor
|
|
7537
|
+
* makes the intended decomposition binding. The journaled decision
|
|
7538
|
+
* (and a rejection's error data) carries the actual
|
|
7539
|
+
* `spawnedChildren` beside the configured floor, so a resume rolls
|
|
7540
|
+
* the same verdict forward. Positive integer; policy only, never
|
|
7541
|
+
* part of any identity.
|
|
7542
|
+
*/
|
|
7543
|
+
minSpawnedChildren?: number;
|
|
7544
|
+
/**
|
|
7503
7545
|
* The terminal-output salvage switch (the 1.64.0 experiment review,
|
|
7504
7546
|
* P0.4 + P1.1; default false). When true, a child that settled
|
|
7505
7547
|
* 'limit' CARRYING a terminal output counts as a successful child for
|
|
@@ -8129,21 +8171,26 @@ interface AgentProfile {
|
|
|
8129
8171
|
estCost?: number;
|
|
8130
8172
|
/**
|
|
8131
8173
|
* The declared evidence contract of the profile's task (RV303, the
|
|
8132
|
-
* seventh comparison experiment): how many
|
|
8133
|
-
* spawned agent MUST record, and the declared
|
|
8134
|
-
* them.
|
|
8135
|
-
*
|
|
8174
|
+
* seventh comparison experiment; runtime enforcement RV507): how many
|
|
8175
|
+
* evidence entries the spawned agent MUST record, and the declared
|
|
8176
|
+
* call estimates behind them. Under the default `enforce: 'warn'` it
|
|
8177
|
+
* is purely declarative, like estCost: {@link preflightEstimate}
|
|
8178
|
+
* compares the resulting call floor
|
|
8136
8179
|
* (`minEntries * estCallsPerEntry + overheadCalls`, defaults 3 and 8)
|
|
8137
8180
|
* against the spawn's effective executed-call ceiling and warns
|
|
8138
8181
|
* `tool-cap-below-evidence-floor` when the cap cannot fit the
|
|
8139
|
-
* contract.
|
|
8140
|
-
*
|
|
8182
|
+
* contract. Under `enforce: 'refuse'` the floor additionally binds at
|
|
8183
|
+
* the terminal: an ok settle with fewer successful `record_evidence`
|
|
8184
|
+
* executions than `minEntries` becomes a typed error terminal. The
|
|
8185
|
+
* experiment shape: 14 mandatory entries against an 84-call cap that
|
|
8186
|
+
* two workers exhausted at 10 recorded entries.
|
|
8141
8187
|
*/
|
|
8142
8188
|
evidenceContract?: EvidenceContract;
|
|
8143
8189
|
}
|
|
8144
8190
|
/**
|
|
8145
|
-
* A declared evidence floor
|
|
8146
|
-
*
|
|
8191
|
+
* A declared evidence floor (RV303): preflight judges tool caps
|
|
8192
|
+
* against it, and under `enforce: 'refuse'` the runtime refuses an ok
|
|
8193
|
+
* settle below it (RV507); see {@link AgentProfile.evidenceContract}.
|
|
8147
8194
|
*/
|
|
8148
8195
|
interface EvidenceContract {
|
|
8149
8196
|
/** Evidence entries the task must record; positive integer. */
|
|
@@ -8152,6 +8199,19 @@ interface EvidenceContract {
|
|
|
8152
8199
|
estCallsPerEntry?: number;
|
|
8153
8200
|
/** Estimated non-evidence overhead calls; default 8. */
|
|
8154
8201
|
overheadCalls?: number;
|
|
8202
|
+
/**
|
|
8203
|
+
* What the floor does at the child's terminal settle (RV507). The
|
|
8204
|
+
* default 'warn' keeps the historical behavior: the contract is a
|
|
8205
|
+
* preflight signal only. 'refuse' turns an ok finish whose message
|
|
8206
|
+
* window carries fewer successful `record_evidence` executions
|
|
8207
|
+
* (result `recorded: true`; duplicates and verification errors never
|
|
8208
|
+
* count) than `minEntries` into a typed error terminal (kind
|
|
8209
|
+
* 'terminal') whose journaled error data carries the machine-readable
|
|
8210
|
+
* `evidenceFloor: { recordedEntries, minEntries }`; the outcome is
|
|
8211
|
+
* memoized, so a resume rolls the refusal forward instead of
|
|
8212
|
+
* re-paying the invocation. Non-ok terminals are never re-judged.
|
|
8213
|
+
*/
|
|
8214
|
+
enforce?: "warn" | "refuse";
|
|
8155
8215
|
}
|
|
8156
8216
|
/**
|
|
8157
8217
|
* Per-spawn options. The
|
package/dist/index.js
CHANGED
|
@@ -2781,17 +2781,19 @@ function requireTimerDelayMs(value, site) {
|
|
|
2781
2781
|
if (typeof value !== "number" || !Number.isInteger(value) || value < 1 || value > 2147483647) refuse(site, "an integer between 1 and 2147483647 ms (the Node timer maximum)", value);
|
|
2782
2782
|
}
|
|
2783
2783
|
/**
|
|
2784
|
-
* A declared evidence contract (RV303): minEntries
|
|
2785
|
-
* estCallsPerEntry positive integers, overheadCalls a nonnegative
|
|
2786
|
-
* integer
|
|
2787
|
-
*
|
|
2784
|
+
* A declared evidence contract (RV303, enforcement RV507): minEntries
|
|
2785
|
+
* and estCallsPerEntry positive integers, overheadCalls a nonnegative
|
|
2786
|
+
* integer, enforce one of 'warn' | 'refuse'. Shared by the profile
|
|
2787
|
+
* intake and the preflight spawn spec so both boundaries refuse the
|
|
2788
|
+
* same shapes with the same wording.
|
|
2788
2789
|
*/
|
|
2789
2790
|
function validateEvidenceContract(value, site) {
|
|
2790
|
-
if (typeof value !== "object" || value === null || Array.isArray(value)) throw new ConfigError(`${site} must be { minEntries, estCallsPerEntry?, overheadCalls? }; got ${typeof value}`);
|
|
2791
|
-
const { minEntries, estCallsPerEntry, overheadCalls } = value;
|
|
2791
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) throw new ConfigError(`${site} must be { minEntries, estCallsPerEntry?, overheadCalls?, enforce? }; got ${typeof value}`);
|
|
2792
|
+
const { minEntries, estCallsPerEntry, overheadCalls, enforce } = value;
|
|
2792
2793
|
requirePositiveInteger(minEntries, `${site}.minEntries`);
|
|
2793
2794
|
if (estCallsPerEntry !== void 0) requirePositiveInteger(estCallsPerEntry, `${site}.estCallsPerEntry`);
|
|
2794
2795
|
if (overheadCalls !== void 0) requireNonNegativeInteger(overheadCalls, `${site}.overheadCalls`);
|
|
2796
|
+
if (enforce !== void 0 && enforce !== "warn" && enforce !== "refuse") throw new ConfigError(`${site}.enforce must be 'warn' or 'refuse'; got ${JSON.stringify(enforce)}`);
|
|
2795
2797
|
}
|
|
2796
2798
|
//#endregion
|
|
2797
2799
|
//#region src/knowledge/file-store.ts
|
|
@@ -12515,6 +12517,24 @@ async function runAgent(options) {
|
|
|
12515
12517
|
}
|
|
12516
12518
|
endPhase(extractPhase, phaseOutcome(), extractServed);
|
|
12517
12519
|
}
|
|
12520
|
+
const evidenceFloor = options.evidenceContract;
|
|
12521
|
+
let evidenceRefusal;
|
|
12522
|
+
if (evidenceFloor?.enforce === "refuse" && status === "ok") {
|
|
12523
|
+
const recordedEntries = messages.reduce((count, message) => count + message.parts.filter((part) => part.type === "tool-result" && part.name === "record_evidence" && part.result?.recorded === true).length, 0);
|
|
12524
|
+
if (recordedEntries < evidenceFloor.minEntries) {
|
|
12525
|
+
status = "error";
|
|
12526
|
+
output = null;
|
|
12527
|
+
evidenceRefusal = {
|
|
12528
|
+
recordedEntries,
|
|
12529
|
+
minEntries: evidenceFloor.minEntries
|
|
12530
|
+
};
|
|
12531
|
+
agentError = {
|
|
12532
|
+
kind: "terminal",
|
|
12533
|
+
retryable: false
|
|
12534
|
+
};
|
|
12535
|
+
errorMessage = `evidence contract unmet: ${String(recordedEntries)} of ${String(evidenceFloor.minEntries)} required evidence entries recorded`;
|
|
12536
|
+
}
|
|
12537
|
+
}
|
|
12518
12538
|
const limitPartial = status === "limit" ? latestProgressReport(messages) : void 0;
|
|
12519
12539
|
if (limitPartial !== void 0) await saveBoundary();
|
|
12520
12540
|
let transcriptRef = "";
|
|
@@ -12560,6 +12580,7 @@ async function runAgent(options) {
|
|
|
12560
12580
|
const schemaRejectedTerminalExchanges = terminalName === void 0 ? 0 : messages.reduce((count, message) => count + message.parts.filter((part) => part.type === "tool-result" && part.name === terminalName && part.isError === true && part.result?.error === terminalSchemaRejectionMessage(terminalName)).length, 0);
|
|
12561
12581
|
if (schemaRejectedTerminalExchanges > 0) result.schemaRejectedTerminalExchanges = schemaRejectedTerminalExchanges;
|
|
12562
12582
|
if (schemaRecoveredTerminalExchanges > 0) result.schemaRecoveredTerminalExchanges = schemaRecoveredTerminalExchanges;
|
|
12583
|
+
if (evidenceRefusal !== void 0) result.evidenceFloor = evidenceRefusal;
|
|
12563
12584
|
if (usageApprox) result.usageApprox = true;
|
|
12564
12585
|
if (transportRetries > 0) result.transportRetries = transportRetries;
|
|
12565
12586
|
if (rateLimitObservations.size > 0) result.rateLimitObservations = [...rateLimitObservations.values()];
|
|
@@ -14754,6 +14775,7 @@ function createCtx(internals, rootWorkflow) {
|
|
|
14754
14775
|
if (finalize !== void 0) runAgentOptions.finalize = finalize;
|
|
14755
14776
|
runAgentOptions.summarize = summarize;
|
|
14756
14777
|
if (profile?.compaction !== void 0) runAgentOptions.compaction = profile.compaction;
|
|
14778
|
+
if (profile?.evidenceContract !== void 0) runAgentOptions.evidenceContract = profile.evidenceContract;
|
|
14757
14779
|
if (loopFallbacks.length > 0) runAgentOptions.fallbacks = loopFallbacks;
|
|
14758
14780
|
if (retryPolicy !== void 0) runAgentOptions.retry = { policy: retryPolicy };
|
|
14759
14781
|
if (internals.quota !== void 0) {
|
|
@@ -14974,6 +14996,18 @@ function createCtx(internals, rootWorkflow) {
|
|
|
14974
14996
|
};
|
|
14975
14997
|
}
|
|
14976
14998
|
}
|
|
14999
|
+
if (result.evidenceFloor !== void 0 && terminalPatch.error !== void 0) {
|
|
15000
|
+
terminalPatch.memoizeOutcome = true;
|
|
15001
|
+
const priorData = terminalPatch.error.data;
|
|
15002
|
+
const dataRecord = typeof priorData === "object" && priorData !== null && !Array.isArray(priorData) ? priorData : {};
|
|
15003
|
+
terminalPatch.error = {
|
|
15004
|
+
...terminalPatch.error,
|
|
15005
|
+
data: {
|
|
15006
|
+
...dataRecord,
|
|
15007
|
+
evidenceFloor: result.evidenceFloor
|
|
15008
|
+
}
|
|
15009
|
+
};
|
|
15010
|
+
}
|
|
14977
15011
|
if (checkpointWritten) terminalPatch.checkpointRef = ckptRef;
|
|
14978
15012
|
const terminal = await internals.replayer.appendTerminal(running.seq, terminalPatch);
|
|
14979
15013
|
internals.events.emit({
|
|
@@ -16216,7 +16250,10 @@ function listCitations(values) {
|
|
|
16216
16250
|
* {@link DEFAULT_EVIDENCE_MIN_SHARE}, the plan's 95 percent gate,
|
|
16217
16251
|
* compared as a ceiling on the required count so an exact boundary like
|
|
16218
16252
|
* 19 of 20 passes) must appear literally in the result text. Zero child
|
|
16219
|
-
* citations pass vacuously
|
|
16253
|
+
* citations pass vacuously UNLESS `requireNonEmptyPool: true` (RV507):
|
|
16254
|
+
* for an evidence-critical run the empty pool IS the failure, so that
|
|
16255
|
+
* mode refuses it with an `empty child citation pool` reason instead of
|
|
16256
|
+
* the vacuous pass. With `requireKnown: true` the contract also
|
|
16220
16257
|
* runs in reverse: every citation in the RESULT must appear in some
|
|
16221
16258
|
* child's output, so a fabricated but pattern valid citation is
|
|
16222
16259
|
* rejected instead of silently counting as evidence. Rejection reasons
|
|
@@ -16245,6 +16282,7 @@ function evidencePreservedValidator(options) {
|
|
|
16245
16282
|
for (const match of child.text.match(new RegExp(pattern, globalFlags)) ?? []) cited.add(match);
|
|
16246
16283
|
}
|
|
16247
16284
|
const reasons = [];
|
|
16285
|
+
if (cited.size === 0 && options?.requireNonEmptyPool === true) reasons.push("empty child citation pool: no ok child output contains a citation matching the pattern");
|
|
16248
16286
|
if (cited.size > 0) {
|
|
16249
16287
|
const missing = [...cited].filter((citation) => !input.text.includes(citation));
|
|
16250
16288
|
const preserved = cited.size - missing.length;
|
|
@@ -16774,6 +16812,8 @@ function validateOrchestrateOptions(opts) {
|
|
|
16774
16812
|
if (acceptPartial !== void 0 && typeof acceptPartial !== "boolean") throw new ConfigError(`orchestrate acceptance.acceptPartialChildren must be a boolean; got ${typeof acceptPartial}`);
|
|
16775
16813
|
const acceptOutput = opts.acceptance.acceptValidatedTerminalOutputOnLimit;
|
|
16776
16814
|
if (acceptOutput !== void 0 && typeof acceptOutput !== "boolean") throw new ConfigError("orchestrate acceptance.acceptValidatedTerminalOutputOnLimit must be a boolean; got " + typeof acceptOutput);
|
|
16815
|
+
const minSpawned = opts.acceptance.minSpawnedChildren;
|
|
16816
|
+
if (minSpawned !== void 0) requirePositiveInteger(minSpawned, "orchestrate acceptance.minSpawnedChildren");
|
|
16777
16817
|
}
|
|
16778
16818
|
if (opts.finishValidation !== void 0) {
|
|
16779
16819
|
const fv = opts.finishValidation;
|
|
@@ -18530,7 +18570,10 @@ function makeOrchestratorWorkflow(goal, opts) {
|
|
|
18530
18570
|
degradedReasons.push(status === "running" ? `child ${record.nodeId} was still running when finish validated` : `child ${record.nodeId} settled '${status}'`);
|
|
18531
18571
|
}
|
|
18532
18572
|
const childPolicy = opts.acceptance.childPolicy;
|
|
18533
|
-
const
|
|
18573
|
+
const minSpawned = opts.acceptance.minSpawnedChildren;
|
|
18574
|
+
const rosterMet = minSpawned === void 0 || sortedRecords.length >= minSpawned;
|
|
18575
|
+
if (!rosterMet) degradedReasons.push(`only ${String(sortedRecords.length)} children were spawned; the acceptance policy requires at least ${String(minSpawned)} spawned children`);
|
|
18576
|
+
const accepted = rosterMet && (childPolicy === "all-ok" ? hardDegraded === 0 : (childStatusCounts.ok ?? 0) + salvaged.length + salvagedOutput.length >= childPolicy.minSuccessful);
|
|
18534
18577
|
decision = {
|
|
18535
18578
|
decisionType: "orchestrator_acceptance",
|
|
18536
18579
|
verdict: accepted ? "accepted" : "rejected",
|
|
@@ -18538,6 +18581,10 @@ function makeOrchestratorWorkflow(goal, opts) {
|
|
|
18538
18581
|
childPolicy,
|
|
18539
18582
|
childStatusCounts,
|
|
18540
18583
|
degradedReasons,
|
|
18584
|
+
...minSpawned === void 0 ? {} : {
|
|
18585
|
+
minSpawnedChildren: minSpawned,
|
|
18586
|
+
spawnedChildren: sortedRecords.length
|
|
18587
|
+
},
|
|
18541
18588
|
...salvaged.length === 0 ? {} : { salvagedPartialChildren: salvaged },
|
|
18542
18589
|
...salvagedOutput.length === 0 ? {} : { salvagedTerminalOutputChildren: salvagedOutput },
|
|
18543
18590
|
...accepted || opts.synthesis === void 0 ? {} : { synthesisSkipped: "synthesis_skipped_by_acceptance" }
|
|
@@ -18559,13 +18606,17 @@ function makeOrchestratorWorkflow(goal, opts) {
|
|
|
18559
18606
|
msg: "orchestrator synthesis skipped",
|
|
18560
18607
|
data: { reason: decision.synthesisSkipped }
|
|
18561
18608
|
}, callingState.spanId);
|
|
18562
|
-
const required = decision.childPolicy === "all-ok" ? "every child ok" : `at least ${String(decision.childPolicy.minSuccessful)} children ok
|
|
18609
|
+
const required = (decision.childPolicy === "all-ok" ? "every child ok" : `at least ${String(decision.childPolicy.minSuccessful)} children ok`) + (decision.minSpawnedChildren === void 0 ? "" : `, with at least ${String(decision.minSpawnedChildren)} spawned children`);
|
|
18563
18610
|
throw new FailRunError(`the orchestrator acceptance policy rejected the finish: ${String(decision.childStatusCounts.ok ?? 0)} children settled 'ok' but the policy requires ${required}; degraded: ${decision.degradedReasons.join("; ")}`, { data: {
|
|
18564
18611
|
source: "orchestrator_acceptance",
|
|
18565
18612
|
completion: "rejected",
|
|
18566
18613
|
childPolicy: decision.childPolicy,
|
|
18567
18614
|
childStatusCounts: decision.childStatusCounts,
|
|
18568
18615
|
degradedReasons: decision.degradedReasons,
|
|
18616
|
+
...decision.minSpawnedChildren === void 0 ? {} : {
|
|
18617
|
+
minSpawnedChildren: decision.minSpawnedChildren,
|
|
18618
|
+
spawnedChildren: decision.spawnedChildren ?? 0
|
|
18619
|
+
},
|
|
18569
18620
|
...decision.salvagedPartialChildren === void 0 ? {} : { salvagedPartialChildren: decision.salvagedPartialChildren },
|
|
18570
18621
|
...decision.salvagedTerminalOutputChildren === void 0 ? {} : { salvagedTerminalOutputChildren: decision.salvagedTerminalOutputChildren },
|
|
18571
18622
|
...decision.synthesisSkipped === void 0 ? {} : { synthesisSkipped: decision.synthesisSkipped }
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rulvar/core",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.99.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",
|