@rulvar/core 1.64.0 → 1.65.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 CHANGED
@@ -6169,6 +6169,16 @@ interface FinishValidationChild {
6169
6169
  * same serialization the child result evidence tools page.
6170
6170
  */
6171
6171
  readonly text: string;
6172
+ /**
6173
+ * Present and true ONLY when acceptance.acceptValidatedTerminalOutputOnLimit
6174
+ * is configured and this child settled 'limit' CARRYING a terminal
6175
+ * output (the finalization reserve summary that, for a schema child,
6176
+ * already validated against the declared output schema). Acceptance
6177
+ * will count such a child as a success, so evidencePreservedValidator
6178
+ * treats its text as part of the cited evidence pool. Absent in every
6179
+ * other configuration, keeping the old pool exactly.
6180
+ */
6181
+ readonly salvageableOutput?: boolean;
6172
6182
  }
6173
6183
  /** What a {@link FinishValidator} judges. */
6174
6184
  interface FinishValidationInput {
@@ -6790,6 +6800,29 @@ interface OrchestrateAcceptance {
6790
6800
  * decision, so a resume rolls the same verdict forward.
6791
6801
  */
6792
6802
  acceptPartialChildren?: boolean;
6803
+ /**
6804
+ * The terminal-output salvage switch (the 1.64.0 experiment review,
6805
+ * P0.4 + P1.1; default false). When true, a child that settled
6806
+ * 'limit' CARRYING a terminal output counts as a successful child for
6807
+ * the policy, exactly like acceptPartialChildren counts a
6808
+ * partial-bearing one. A limit terminal carries an output ONLY when
6809
+ * the child's limits.finalizationReserve summary turn produced one
6810
+ * AND, for a schema child, that summary already validated against the
6811
+ * declared output schema (an invalid summary keeps output null and is
6812
+ * never salvaged), so validation runs BEFORE acceptance by
6813
+ * construction. The verdict then reports completion 'partial' (never
6814
+ * 'complete'), lists the children in `salvagedTerminalOutputChildren`
6815
+ * on the result envelope, and keeps a per-child note in
6816
+ * degradedReasons. A child carrying BOTH an output and a progress
6817
+ * partial salvages by its output. The child's digest and
6818
+ * get_child_result surface the output unconditionally (paid, journaled
6819
+ * evidence is never withheld); this option gates only the acceptance
6820
+ * fold, the evidencePreservedValidator cited pool (via
6821
+ * FinishValidationChild.salvageableOutput), and the coordination
6822
+ * prompt line. The whole fold is journaled in the single acceptance
6823
+ * decision, so a resume rolls the same verdict forward.
6824
+ */
6825
+ acceptValidatedTerminalOutputOnLimit?: boolean;
6793
6826
  }
6794
6827
  /** How many rejected finishes are repaired by default: the plan's repair once. */
6795
6828
  declare const DEFAULT_FINISH_MAX_REPAIRS = 1;
package/dist/index.js CHANGED
@@ -14689,6 +14689,10 @@ function summarizeOutput(result) {
14689
14689
  if (result.status === "ok") raw = typeof result.output === "string" ? result.output : JSON.stringify(result.output ?? null);
14690
14690
  else {
14691
14691
  raw = result.errorMessage ?? `terminal status ${result.status}`;
14692
+ if (result.status === "limit" && result.output !== null && result.output !== void 0) {
14693
+ const final = typeof result.output === "string" ? result.output : JSON.stringify(result.output);
14694
+ raw = `${raw}; final: ${final}`;
14695
+ }
14692
14696
  if (result.partial !== void 0) raw = `${raw}; partial: ${JSON.stringify(result.partial)}`;
14693
14697
  }
14694
14698
  return truncateToBudget(raw, 400);
@@ -15075,9 +15079,11 @@ function pageOf(content, rawOffset, rawMaxChars) {
15075
15079
  function serializeChildOutput(result) {
15076
15080
  if (result.status !== "ok") {
15077
15081
  const base = result.errorMessage ?? `terminal status ${result.status}`;
15078
- if (result.partial !== void 0) return JSON.stringify({
15082
+ const limitOutput = result.status === "limit" && result.output !== null && result.output !== void 0;
15083
+ if (limitOutput || result.partial !== void 0) return JSON.stringify({
15079
15084
  error: base,
15080
- partial: result.partial
15085
+ ...limitOutput ? { output: result.output } : {},
15086
+ ...result.partial === void 0 ? {} : { partial: result.partial }
15081
15087
  });
15082
15088
  return base;
15083
15089
  }
@@ -15104,6 +15110,8 @@ function validateOrchestrateOptions(opts) {
15104
15110
  if (policy !== "all-ok") requirePositiveInteger(minSuccessful, "orchestrate acceptance.childPolicy.minSuccessful");
15105
15111
  const acceptPartial = opts.acceptance.acceptPartialChildren;
15106
15112
  if (acceptPartial !== void 0 && typeof acceptPartial !== "boolean") throw new ConfigError(`orchestrate acceptance.acceptPartialChildren must be a boolean; got ${typeof acceptPartial}`);
15113
+ const acceptOutput = opts.acceptance.acceptValidatedTerminalOutputOnLimit;
15114
+ if (acceptOutput !== void 0 && typeof acceptOutput !== "boolean") throw new ConfigError("orchestrate acceptance.acceptValidatedTerminalOutputOnLimit must be a boolean; got " + typeof acceptOutput);
15107
15115
  }
15108
15116
  if (opts.finishValidation !== void 0) {
15109
15117
  const fv = opts.finishValidation;
@@ -15174,8 +15182,10 @@ function finishValidationPromptLines(spec) {
15174
15182
  * keeps byte-identical coordination prompts.
15175
15183
  */
15176
15184
  function acceptancePromptLines(acceptance) {
15177
- if (acceptance?.acceptPartialChildren !== true) return [];
15178
- return ["Partial salvage is on: a child that ends at its limit AFTER recording progress with report_progress counts as a partial success for acceptance; its digest carries the partial and get_child_result (when enabled) pages the full report. When the gap matters, respawn a NARROWED child carrying the partial instead of repeating the task."];
15185
+ const lines = [];
15186
+ if (acceptance?.acceptPartialChildren === true) lines.push("Partial salvage is on: a child that ends at its limit AFTER recording progress with report_progress counts as a partial success for acceptance; its digest carries the partial and get_child_result (when enabled) pages the full report. When the gap matters, respawn a NARROWED child carrying the partial instead of repeating the task.");
15187
+ if (acceptance?.acceptValidatedTerminalOutputOnLimit === true) lines.push("Terminal-output salvage is on: a child that ends at its limit WITH a final answer (its finalization reserve summary, already validated against the declared output schema) counts as a successful child for acceptance; its digest carries it after the 'final:' marker and get_child_result (when enabled) pages it in full.");
15188
+ return lines;
15179
15189
  }
15180
15190
  /**
15181
15191
  * Resolves per-spawn dispatch options against the engine registries
@@ -16080,11 +16090,13 @@ function makeOrchestratorWorkflow(goal, opts) {
16080
16090
  let decision = known.find((candidate) => candidate.callId === call.id);
16081
16091
  if (decision === void 0) {
16082
16092
  const result = call.result ?? null;
16093
+ const salvageOutputOn = opts?.acceptance?.acceptValidatedTerminalOutputOnLimit === true;
16083
16094
  const children = [...records.values()].sort((a, b) => a.spawnOrdinal - b.spawnOrdinal).map((record) => ({
16084
16095
  handle: record.handle,
16085
16096
  nodeId: record.nodeId,
16086
16097
  status: record.settled?.status ?? "running",
16087
- text: record.settled === void 0 ? "" : serializeChildOutput(record.settled)
16098
+ text: record.settled === void 0 ? "" : serializeChildOutput(record.settled),
16099
+ ...salvageOutputOn && record.settled?.status === "limit" && record.settled.output !== null && record.settled.output !== void 0 ? { salvageableOutput: true } : {}
16088
16100
  }));
16089
16101
  const input = {
16090
16102
  result,
@@ -16542,13 +16554,20 @@ function makeOrchestratorWorkflow(goal, opts) {
16542
16554
  const childStatusCounts = {};
16543
16555
  const degradedReasons = [];
16544
16556
  const salvaged = [];
16557
+ const salvagedOutput = [];
16545
16558
  let hardDegraded = 0;
16546
16559
  const acceptPartial = opts.acceptance.acceptPartialChildren === true;
16560
+ const acceptOutput = opts.acceptance.acceptValidatedTerminalOutputOnLimit === true;
16547
16561
  const sortedRecords = [...records.values()].sort((a, b) => a.spawnOrdinal - b.spawnOrdinal);
16548
16562
  for (const record of sortedRecords) {
16549
16563
  const status = record.settled?.status ?? "running";
16550
16564
  childStatusCounts[status] = (childStatusCounts[status] ?? 0) + 1;
16551
16565
  if (status === "ok") continue;
16566
+ if (acceptOutput && status === "limit" && record.settled?.output !== null && record.settled?.output !== void 0) {
16567
+ salvagedOutput.push(record.nodeId);
16568
+ degradedReasons.push(`child ${record.nodeId} accepted with its validated terminal output (settled 'limit' after the finalization reserve summary)`);
16569
+ continue;
16570
+ }
16552
16571
  if (acceptPartial && status === "limit" && record.settled?.partial !== void 0) {
16553
16572
  salvaged.push(record.nodeId);
16554
16573
  degradedReasons.push(`child ${record.nodeId} accepted as partial (settled 'limit' with a structured partial)`);
@@ -16558,7 +16577,7 @@ function makeOrchestratorWorkflow(goal, opts) {
16558
16577
  degradedReasons.push(status === "running" ? `child ${record.nodeId} was still running when finish validated` : `child ${record.nodeId} settled '${status}'`);
16559
16578
  }
16560
16579
  const childPolicy = opts.acceptance.childPolicy;
16561
- const accepted = childPolicy === "all-ok" ? hardDegraded === 0 : (childStatusCounts.ok ?? 0) + salvaged.length >= childPolicy.minSuccessful;
16580
+ const accepted = childPolicy === "all-ok" ? hardDegraded === 0 : (childStatusCounts.ok ?? 0) + salvaged.length + salvagedOutput.length >= childPolicy.minSuccessful;
16562
16581
  decision = {
16563
16582
  decisionType: "orchestrator_acceptance",
16564
16583
  verdict: accepted ? "accepted" : "rejected",
@@ -16566,7 +16585,8 @@ function makeOrchestratorWorkflow(goal, opts) {
16566
16585
  childPolicy,
16567
16586
  childStatusCounts,
16568
16587
  degradedReasons,
16569
- ...salvaged.length === 0 ? {} : { salvagedPartialChildren: salvaged }
16588
+ ...salvaged.length === 0 ? {} : { salvagedPartialChildren: salvaged },
16589
+ ...salvagedOutput.length === 0 ? {} : { salvagedTerminalOutputChildren: salvagedOutput }
16570
16590
  };
16571
16591
  await internals.replayer.appendSinglePhase({
16572
16592
  scope: callingState.scope,
@@ -16586,7 +16606,8 @@ function makeOrchestratorWorkflow(goal, opts) {
16586
16606
  childPolicy: decision.childPolicy,
16587
16607
  childStatusCounts: decision.childStatusCounts,
16588
16608
  degradedReasons: decision.degradedReasons,
16589
- ...decision.salvagedPartialChildren === void 0 ? {} : { salvagedPartialChildren: decision.salvagedPartialChildren }
16609
+ ...decision.salvagedPartialChildren === void 0 ? {} : { salvagedPartialChildren: decision.salvagedPartialChildren },
16610
+ ...decision.salvagedTerminalOutputChildren === void 0 ? {} : { salvagedTerminalOutputChildren: decision.salvagedTerminalOutputChildren }
16590
16611
  } });
16591
16612
  }
16592
16613
  return {
@@ -16594,7 +16615,8 @@ function makeOrchestratorWorkflow(goal, opts) {
16594
16615
  completion: decision.completion,
16595
16616
  childStatusCounts: decision.childStatusCounts,
16596
16617
  degradedReasons: decision.degradedReasons,
16597
- ...decision.salvagedPartialChildren === void 0 ? {} : { salvagedPartialChildren: decision.salvagedPartialChildren }
16618
+ ...decision.salvagedPartialChildren === void 0 ? {} : { salvagedPartialChildren: decision.salvagedPartialChildren },
16619
+ ...decision.salvagedTerminalOutputChildren === void 0 ? {} : { salvagedTerminalOutputChildren: decision.salvagedTerminalOutputChildren }
16598
16620
  };
16599
16621
  });
16600
16622
  }
@@ -17315,7 +17337,7 @@ function evidencePreservedValidator(options) {
17315
17337
  validate: (input) => {
17316
17338
  const cited = /* @__PURE__ */ new Set();
17317
17339
  for (const child of input.children ?? []) {
17318
- if (child.status !== "ok") continue;
17340
+ if (child.status !== "ok" && child.salvageableOutput !== true) continue;
17319
17341
  for (const match of child.text.match(new RegExp(pattern, globalFlags)) ?? []) cited.add(match);
17320
17342
  }
17321
17343
  const reasons = [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rulvar/core",
3
- "version": "1.64.0",
3
+ "version": "1.65.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",