@rulvar/core 1.100.0 → 1.101.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 +42 -9
- package/dist/index.js +90 -10
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -7909,6 +7909,32 @@ interface OrchestrateSynthesis {
|
|
|
7909
7909
|
* accordingly).
|
|
7910
7910
|
*/
|
|
7911
7911
|
context?: "digests" | "full";
|
|
7912
|
+
/**
|
|
7913
|
+
* The conditional synthesis gate (RV510, the ninth comparison
|
|
7914
|
+
* experiment: synthesis returned the byte-identical draft after
|
|
7915
|
+
* 101.3 s and 0.5512 USD, 57.3% of post-fan-in wall time). With
|
|
7916
|
+
* `true`, before the 'single' synthesis span starts the coordination
|
|
7917
|
+
* draft is run through the FULL declared finish contract (the same
|
|
7918
|
+
* `finishValidation.validators` that would bind the synthesis
|
|
7919
|
+
* finish): a draft that passes skips the synthesis invocation
|
|
7920
|
+
* entirely under a journaled 'orchestrator_synthesis_skip' decision
|
|
7921
|
+
* with reason 'synthesis_skipped_by_valid_draft' (the existing skip
|
|
7922
|
+
* vocabulary; the info log and the acceptance envelope carry it), and
|
|
7923
|
+
* a resume rolls the journaled skip forward with zero paid calls. A
|
|
7924
|
+
* draft that fails any validator goes to synthesis exactly as before,
|
|
7925
|
+
* with the repair budget untouched (the gate is a pre-pass, never a
|
|
7926
|
+
* journaled validation verdict). Deterministic by construction: only
|
|
7927
|
+
* the declared contract judges, never a semantic delta heuristic.
|
|
7928
|
+
* Requires `finishValidation` (a ConfigError at intake otherwise:
|
|
7929
|
+
* without a contract there is nothing to judge the draft valid by),
|
|
7930
|
+
* which transitively limits it to mode 'single'. With a configured
|
|
7931
|
+
* `budget.synthesisReserveUsd` the held money is released unconsumed
|
|
7932
|
+
* on the skip and no reserve lifecycle journals: there was no
|
|
7933
|
+
* synthesis invocation to account. Default false: the gate, the
|
|
7934
|
+
* decision entry, and the envelope field are all absent, byte for
|
|
7935
|
+
* byte.
|
|
7936
|
+
*/
|
|
7937
|
+
skipWhenDraftValid?: boolean;
|
|
7912
7938
|
}
|
|
7913
7939
|
/**
|
|
7914
7940
|
* The deterministic reconciliation envelope an 'incremental' synthesis
|
|
@@ -7943,15 +7969,22 @@ interface IncrementalSynthesisResult {
|
|
|
7943
7969
|
* notes were already paid during the run; the skipped step is the free
|
|
7944
7970
|
* deterministic reconciliation). 'synthesis_skipped_by_budget_cap': the
|
|
7945
7971
|
* orchestrator budget cap froze the plan, and a capped run settles
|
|
7946
|
-
* through the reserved finalizer, never synthesis.
|
|
7947
|
-
*
|
|
7948
|
-
*
|
|
7949
|
-
*
|
|
7950
|
-
*
|
|
7951
|
-
*
|
|
7952
|
-
*
|
|
7953
|
-
|
|
7954
|
-
|
|
7972
|
+
* through the reserved finalizer, never synthesis.
|
|
7973
|
+
* 'synthesis_skipped_by_valid_draft' (RV510): the opt-in
|
|
7974
|
+
* `synthesis.skipWhenDraftValid` gate ran the coordination draft
|
|
7975
|
+
* through the full declared finish contract and every validator
|
|
7976
|
+
* passed, so the synthesis invocation had nothing to add and never
|
|
7977
|
+
* started; unlike the other two reasons the run still settles ok with
|
|
7978
|
+
* the draft as its result. The reason is frozen into the journaled
|
|
7979
|
+
* decision that caused the skip (the acceptance decision, the
|
|
7980
|
+
* budget-cap decision, or the 'orchestrator_synthesis_skip' decision),
|
|
7981
|
+
* spread into the typed FailRunError data on the failing paths and
|
|
7982
|
+
* into the acceptance envelope on the valid-draft path, and announced
|
|
7983
|
+
* by an info 'orchestrator synthesis skipped' log event; it is absent
|
|
7984
|
+
* everywhere when synthesis is not configured or actually ran, so
|
|
7985
|
+
* existing runs stay byte identical.
|
|
7986
|
+
*/
|
|
7987
|
+
type OrchestrateSynthesisSkipReason = "synthesis_skipped_by_acceptance" | "synthesis_skipped_by_budget_cap" | "synthesis_skipped_by_valid_draft";
|
|
7955
7988
|
declare const ORCHESTRATE_WORKFLOW_NAME = "rulvar-orchestrate";
|
|
7956
7989
|
/**
|
|
7957
7990
|
* Resolves per-spawn dispatch options against the engine registries
|
package/dist/index.js
CHANGED
|
@@ -16973,6 +16973,11 @@ function validateOrchestrateOptions(opts) {
|
|
|
16973
16973
|
if (synthesis.dedupeClaims !== void 0 && typeof synthesis.dedupeClaims !== "boolean") throw new ConfigError("orchestrate synthesis.dedupeClaims must be a boolean; got " + typeof synthesis.dedupeClaims);
|
|
16974
16974
|
const symmetry = synthesis;
|
|
16975
16975
|
if (symmetry.exposeChildResultTools !== void 0 && typeof symmetry.exposeChildResultTools !== "boolean") throw new ConfigError("orchestrate synthesis.exposeChildResultTools must be a boolean; got " + typeof symmetry.exposeChildResultTools);
|
|
16976
|
+
const conditional = synthesis;
|
|
16977
|
+
if (conditional.skipWhenDraftValid !== void 0) {
|
|
16978
|
+
if (typeof conditional.skipWhenDraftValid !== "boolean") throw new ConfigError("orchestrate synthesis.skipWhenDraftValid must be a boolean; got " + typeof conditional.skipWhenDraftValid);
|
|
16979
|
+
if (conditional.skipWhenDraftValid && opts.finishValidation === void 0) throw new ConfigError("orchestrate synthesis.skipWhenDraftValid requires finishValidation: without a declared finish contract there is nothing to judge the draft valid by");
|
|
16980
|
+
}
|
|
16976
16981
|
if (symmetry.context !== void 0 && symmetry.context !== "digests" && symmetry.context !== "full") throw new ConfigError("orchestrate synthesis.context must be 'digests' or 'full'; got " + JSON.stringify(symmetry.context));
|
|
16977
16982
|
if (synthesis.noteLimits !== void 0) validateUsageLimits(synthesis.noteLimits, "orchestrate synthesis.noteLimits");
|
|
16978
16983
|
if (synthesis.effort !== void 0 && ![
|
|
@@ -17980,6 +17985,24 @@ function makeOrchestratorWorkflow(goal, opts) {
|
|
|
17980
17985
|
if (decision.contractHash !== void 0) return decision.contractHash === hash;
|
|
17981
17986
|
return internals.replayer.snapshot().filter((entry) => entry.kind === "decision" && entry.scope === callingState.scope && entry.value?.decisionType === "orchestrator_finish_validation_bundle").length <= 1;
|
|
17982
17987
|
};
|
|
17988
|
+
/**
|
|
17989
|
+
* The children snapshot (RV-202): spawn order, pure reads of the
|
|
17990
|
+
* records the orchestrator already tracks, so validators can hold
|
|
17991
|
+
* a finish result (or the RV510 draft pre-pass) against the
|
|
17992
|
+
* evidence the children produced. Only the JOURNALED verdict of
|
|
17993
|
+
* validateFinish survives; on replay the snapshot is never rebuilt
|
|
17994
|
+
* because the entry is read by call id there.
|
|
17995
|
+
*/
|
|
17996
|
+
const validationChildren = () => {
|
|
17997
|
+
const salvageOutputOn = opts?.acceptance?.acceptValidatedTerminalOutputOnLimit === true;
|
|
17998
|
+
return [...records.values()].sort((a, b) => a.spawnOrdinal - b.spawnOrdinal).map((record) => ({
|
|
17999
|
+
handle: record.handle,
|
|
18000
|
+
nodeId: record.nodeId,
|
|
18001
|
+
status: record.settled?.status ?? "running",
|
|
18002
|
+
text: record.settled === void 0 ? "" : serializeChildOutput(record.settled),
|
|
18003
|
+
...salvageOutputOn && record.settled?.status === "limit" && record.settled.output !== null && record.settled.output !== void 0 ? { salvageableOutput: true } : {}
|
|
18004
|
+
}));
|
|
18005
|
+
};
|
|
17983
18006
|
const validateFinish = async (call) => {
|
|
17984
18007
|
if (validationSpec === void 0) return { ok: true };
|
|
17985
18008
|
const maxRepairs = validationSpec.maxRepairs ?? 1;
|
|
@@ -17987,18 +18010,10 @@ function makeOrchestratorWorkflow(goal, opts) {
|
|
|
17987
18010
|
let decision = known.find((candidate) => candidate.callId === call.id);
|
|
17988
18011
|
if (decision === void 0) {
|
|
17989
18012
|
const result = call.result ?? null;
|
|
17990
|
-
const salvageOutputOn = opts?.acceptance?.acceptValidatedTerminalOutputOnLimit === true;
|
|
17991
|
-
const children = [...records.values()].sort((a, b) => a.spawnOrdinal - b.spawnOrdinal).map((record) => ({
|
|
17992
|
-
handle: record.handle,
|
|
17993
|
-
nodeId: record.nodeId,
|
|
17994
|
-
status: record.settled?.status ?? "running",
|
|
17995
|
-
text: record.settled === void 0 ? "" : serializeChildOutput(record.settled),
|
|
17996
|
-
...salvageOutputOn && record.settled?.status === "limit" && record.settled.output !== null && record.settled.output !== void 0 ? { salvageableOutput: true } : {}
|
|
17997
|
-
}));
|
|
17998
18013
|
const input = {
|
|
17999
18014
|
result,
|
|
18000
18015
|
text: typeof result === "string" ? result : JSON.stringify(result),
|
|
18001
|
-
children
|
|
18016
|
+
children: validationChildren()
|
|
18002
18017
|
};
|
|
18003
18018
|
const failed = [];
|
|
18004
18019
|
for (const validator of validationSpec.validators) {
|
|
@@ -18392,11 +18407,75 @@ function makeOrchestratorWorkflow(goal, opts) {
|
|
|
18392
18407
|
* facts; absent everywhere when no reserve is configured.
|
|
18393
18408
|
*/
|
|
18394
18409
|
let synthesisReserveLifecycle;
|
|
18410
|
+
/**
|
|
18411
|
+
* Set exactly when the RV510 gate skipped the synthesis span (live
|
|
18412
|
+
* pass and resume roll-forward alike); the acceptance envelope
|
|
18413
|
+
* reports the machine reason through it.
|
|
18414
|
+
*/
|
|
18415
|
+
let synthesisSkippedByValidDraft = false;
|
|
18395
18416
|
const runSynthesis = async (draft) => {
|
|
18396
18417
|
const spec = opts?.synthesis;
|
|
18397
18418
|
if (spec === void 0) return draft;
|
|
18398
18419
|
await recoveryDone;
|
|
18399
18420
|
if (spec.mode === "incremental") return await reconcileIncremental(draft, spec);
|
|
18421
|
+
if (spec.skipWhenDraftValid === true && validationSpec !== void 0) {
|
|
18422
|
+
const skipKey = "synthesis-draft-valid-skip";
|
|
18423
|
+
const announceSkip = (entryRef) => {
|
|
18424
|
+
internals.events.emit({
|
|
18425
|
+
type: "log",
|
|
18426
|
+
level: "info",
|
|
18427
|
+
msg: "orchestrator synthesis skipped",
|
|
18428
|
+
data: {
|
|
18429
|
+
reason: "synthesis_skipped_by_valid_draft",
|
|
18430
|
+
skipDecisionRef: entryRef
|
|
18431
|
+
}
|
|
18432
|
+
}, callingState.spanId);
|
|
18433
|
+
};
|
|
18434
|
+
const prior = internals.replayer.snapshot().find((entry) => entry.kind === "decision" && entry.scope === callingState.scope && entry.key === skipKey);
|
|
18435
|
+
if (prior !== void 0) {
|
|
18436
|
+
synthesisSkippedByValidDraft = true;
|
|
18437
|
+
announceSkip(prior.seq);
|
|
18438
|
+
return draft;
|
|
18439
|
+
}
|
|
18440
|
+
const draftValue = draft ?? null;
|
|
18441
|
+
const input = {
|
|
18442
|
+
result: draftValue,
|
|
18443
|
+
text: typeof draftValue === "string" ? draftValue : JSON.stringify(draftValue),
|
|
18444
|
+
children: validationChildren()
|
|
18445
|
+
};
|
|
18446
|
+
let allPassed = true;
|
|
18447
|
+
for (const validator of validationSpec.validators) {
|
|
18448
|
+
let verdict;
|
|
18449
|
+
try {
|
|
18450
|
+
verdict = validator.validate(input);
|
|
18451
|
+
} catch (thrown) {
|
|
18452
|
+
throw new ConfigError(`finish validator '${validator.name}' threw instead of returning a verdict during the skipWhenDraftValid pre-pass: ` + (thrown instanceof Error ? thrown.message : String(thrown)));
|
|
18453
|
+
}
|
|
18454
|
+
if (!verdict.ok) {
|
|
18455
|
+
allPassed = false;
|
|
18456
|
+
break;
|
|
18457
|
+
}
|
|
18458
|
+
}
|
|
18459
|
+
if (allPassed) {
|
|
18460
|
+
const skipEntry = await internals.replayer.appendSinglePhase({
|
|
18461
|
+
scope: callingState.scope,
|
|
18462
|
+
key: skipKey,
|
|
18463
|
+
kind: "decision",
|
|
18464
|
+
status: "ok",
|
|
18465
|
+
spanId: internals.spans.mint(callingState.spanId),
|
|
18466
|
+
site: "orchestrator-synthesis-skip",
|
|
18467
|
+
value: {
|
|
18468
|
+
decisionType: "orchestrator_synthesis_skip",
|
|
18469
|
+
reason: "synthesis_skipped_by_valid_draft",
|
|
18470
|
+
validators: validationSpec.validators.map((validator) => validator.name)
|
|
18471
|
+
}
|
|
18472
|
+
});
|
|
18473
|
+
if (orchestratorAccount !== void 0 && (opts?.budget?.synthesisReserveUsd ?? 0) > 0) internals.budget.releaseSynthesisReserve(orchestratorAccount);
|
|
18474
|
+
synthesisSkippedByValidDraft = true;
|
|
18475
|
+
announceSkip(skipEntry.seq);
|
|
18476
|
+
return draft;
|
|
18477
|
+
}
|
|
18478
|
+
}
|
|
18400
18479
|
const exposeTools = spec.exposeChildResultTools === true;
|
|
18401
18480
|
const fullContext = spec.context === "full";
|
|
18402
18481
|
const synthesisToolNames = /* @__PURE__ */ new Set([FINISH_TOOL_NAME, ...exposeTools ? [GET_CHILD_RESULT_TOOL_NAME, READ_CHILD_ARTIFACT_TOOL_NAME] : []]);
|
|
@@ -18746,7 +18825,8 @@ function makeOrchestratorWorkflow(goal, opts) {
|
|
|
18746
18825
|
...decision.salvagedPartialChildren === void 0 ? {} : { salvagedPartialChildren: decision.salvagedPartialChildren },
|
|
18747
18826
|
...decision.salvagedTerminalOutputChildren === void 0 ? {} : { salvagedTerminalOutputChildren: decision.salvagedTerminalOutputChildren },
|
|
18748
18827
|
...envelopeSchemaRecovered === 0 ? {} : { schemaRecoveredFinishExchanges: envelopeSchemaRecovered },
|
|
18749
|
-
...synthesisReserveLifecycle === void 0 ? {} : { synthesisReserve: synthesisReserveLifecycle }
|
|
18828
|
+
...synthesisReserveLifecycle === void 0 ? {} : { synthesisReserve: synthesisReserveLifecycle },
|
|
18829
|
+
...synthesisSkippedByValidDraft ? { synthesisSkipped: "synthesis_skipped_by_valid_draft" } : {}
|
|
18750
18830
|
};
|
|
18751
18831
|
});
|
|
18752
18832
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rulvar/core",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.101.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",
|