@sema-agent/core 5.57.0 → 5.58.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 (183) hide show
  1. package/CHANGELOG.md +48 -0
  2. package/dist/agents/cascade.d.ts +1 -1
  3. package/dist/agents/cumulative-stats.d.ts +1 -1
  4. package/dist/agents/observer.d.ts +2 -2
  5. package/dist/agents/peer-admission.d.ts +1 -1
  6. package/dist/agents/retain-ledger.d.ts +2 -2
  7. package/dist/agents/roster-store.d.ts +8 -8
  8. package/dist/agents/send-message-tool.d.ts +2 -2
  9. package/dist/agents/subagent-steps.d.ts +1 -1
  10. package/dist/agents/subagent.d.ts +13 -13
  11. package/dist/agents/team.d.ts +5 -5
  12. package/dist/agents/tool-filter.d.ts +2 -2
  13. package/dist/agents/verify.d.ts +1 -1
  14. package/dist/bench/metrics.d.ts +35 -35
  15. package/dist/brain/degrading.d.ts +1 -1
  16. package/dist/brain/errors.d.ts +3 -3
  17. package/dist/brain/reasoning.d.ts +2 -2
  18. package/dist/brain/repetition.d.ts +1 -1
  19. package/dist/brain/status-sink.d.ts +2 -2
  20. package/dist/brain/stream-shared.d.ts +1 -1
  21. package/dist/config/catalog.d.ts +5 -5
  22. package/dist/core/arg-summary.d.ts +4 -4
  23. package/dist/core/ask-class.d.ts +2 -2
  24. package/dist/core/ask-question.d.ts +1 -1
  25. package/dist/core/auto-compaction.d.ts +15 -15
  26. package/dist/core/auto-mode.d.ts +5 -5
  27. package/dist/core/background-agent-store.d.ts +20 -20
  28. package/dist/core/background-shell.d.ts +4 -4
  29. package/dist/core/checkpoint-store.d.ts +35 -27
  30. package/dist/core/context-edit.d.ts +1 -1
  31. package/dist/core/context-guard.d.ts +1 -1
  32. package/dist/core/exec-output-tail.d.ts +6 -6
  33. package/dist/core/file-snapshot-store.d.ts +8 -8
  34. package/dist/core/git-worktree-env.d.ts +3 -3
  35. package/dist/core/governance-codes.js +2 -0
  36. package/dist/core/hooks.d.ts +73 -33
  37. package/dist/core/hooks.js +87 -25
  38. package/dist/core/image-downsample.d.ts +1 -1
  39. package/dist/core/locked-config.d.ts +1 -1
  40. package/dist/core/lsp.d.ts +1 -1
  41. package/dist/core/mailbox-store.d.ts +1 -1
  42. package/dist/core/mcp.d.ts +3 -3
  43. package/dist/core/memory-engine/consolidation-driver.d.ts +207 -0
  44. package/dist/core/memory-engine/consolidation-driver.js +378 -0
  45. package/dist/core/memory-engine/consolidation.d.ts +46 -2
  46. package/dist/core/memory-engine/consolidation.js +1 -0
  47. package/dist/core/memory-engine/data-plane.d.ts +1 -1
  48. package/dist/core/memory-engine/distiller.d.ts +550 -0
  49. package/dist/core/memory-engine/distiller.js +598 -0
  50. package/dist/core/memory-engine/dual-root.d.ts +1 -1
  51. package/dist/core/memory-engine/engine.d.ts +47 -3
  52. package/dist/core/memory-engine/engine.js +37 -3
  53. package/dist/core/memory-engine/file-backend.d.ts +1 -1
  54. package/dist/core/memory-engine/index.d.ts +4 -2
  55. package/dist/core/memory-engine/index.js +4 -2
  56. package/dist/core/memory-engine/origin-clearance.d.ts +1 -1
  57. package/dist/core/memory-engine/scope-contract.d.ts +4 -4
  58. package/dist/core/memory-engine/sync-client.d.ts +16 -16
  59. package/dist/core/memory-engine/sync.d.ts +4 -4
  60. package/dist/core/memory-recall.d.ts +1 -1
  61. package/dist/core/memory.d.ts +2 -2
  62. package/dist/core/permission-rule-consent.d.ts +185 -36
  63. package/dist/core/permission-rule-consent.js +219 -44
  64. package/dist/core/permission-rule-model.d.ts +194 -31
  65. package/dist/core/permission-rule-model.js +93 -35
  66. package/dist/core/permission-rules.d.ts +9 -9
  67. package/dist/core/remote-env.d.ts +8 -8
  68. package/dist/core/roles.d.ts +3 -3
  69. package/dist/core/roles.js +1 -0
  70. package/dist/core/runner/assemble-result.d.ts +2 -2
  71. package/dist/core/runner/compaction-call-options.d.ts +3 -3
  72. package/dist/core/runner/memory-consolidation-driver.d.ts +49 -0
  73. package/dist/core/runner/memory-consolidation-driver.js +60 -0
  74. package/dist/core/runner/memory-consolidation.d.ts +1 -1
  75. package/dist/core/runner/prepare-config-doors.d.ts +3 -3
  76. package/dist/core/runner/prepare-task.d.ts +21 -21
  77. package/dist/core/runner/prepare-task.js +21 -14
  78. package/dist/core/runner/prepare-workspace-restore.d.ts +2 -2
  79. package/dist/core/runner/runtask.d.ts +11 -11
  80. package/dist/core/runner/session-rule-policy.d.ts +1 -1
  81. package/dist/core/runner/teardown-bounded.d.ts +1 -1
  82. package/dist/core/runner/tool-disclosure.d.ts +2 -2
  83. package/dist/core/runner/turn-attachments.d.ts +11 -11
  84. package/dist/core/scheduler.d.ts +5 -5
  85. package/dist/core/secret-env.d.ts +1 -1
  86. package/dist/core/sensitive-path-policy.d.ts +1 -1
  87. package/dist/core/session-policy-store.d.ts +2 -2
  88. package/dist/core/session-reconcile.d.ts +2 -2
  89. package/dist/core/session-store.d.ts +3 -3
  90. package/dist/core/session.d.ts +1 -1
  91. package/dist/core/shutdown-debug.d.ts +2 -2
  92. package/dist/core/side-query.d.ts +2 -2
  93. package/dist/core/spec-contract.d.ts +1 -1
  94. package/dist/core/store-contracts/contract-harness.d.ts +2 -2
  95. package/dist/core/store-contracts/contract-kit-version.d.ts +2 -2
  96. package/dist/core/store-contracts/mailbox-store-contract.d.ts +1 -1
  97. package/dist/core/store-contracts/mailbox-store-contract.js +1 -1
  98. package/dist/core/task-notification.d.ts +5 -5
  99. package/dist/core/task-registry-agent.d.ts +12 -12
  100. package/dist/core/task-registry-monitor.d.ts +1 -1
  101. package/dist/core/task-registry-shared.d.ts +41 -41
  102. package/dist/core/task-registry.d.ts +12 -12
  103. package/dist/core/tool-detach.d.ts +2 -2
  104. package/dist/core/tool-errors.d.ts +3 -3
  105. package/dist/core/tool-policy.d.ts +55 -28
  106. package/dist/core/tool-result-budget.d.ts +1 -1
  107. package/dist/core/tool-result-store.d.ts +2 -2
  108. package/dist/core/tools.d.ts +1 -1
  109. package/dist/core/trace.d.ts +26 -23
  110. package/dist/core/types.d.ts +123 -70
  111. package/dist/core/untrusted-egress.d.ts +1 -1
  112. package/dist/core/untrusted-text.d.ts +7 -7
  113. package/dist/core/wiring-manifest.d.ts +5 -5
  114. package/dist/core/workflow-journal-store.d.ts +14 -14
  115. package/dist/core/workflow-run-store-contract.d.ts +1 -1
  116. package/dist/core/workflow-run-store-contract.js +1 -1
  117. package/dist/core/workflow-run-store.d.ts +4 -4
  118. package/dist/engine/compaction/compaction.d.ts +3 -3
  119. package/dist/engine/compaction/utils.d.ts +2 -2
  120. package/dist/engine/execution-env/kill-tree.d.ts +1 -1
  121. package/dist/engine/execution-env/node-execution-env.d.ts +8 -8
  122. package/dist/engine/harness/agent-harness.d.ts +6 -6
  123. package/dist/engine/harness/messages.d.ts +1 -1
  124. package/dist/engine/harness/types.d.ts +10 -10
  125. package/dist/engine/llm/types.d.ts +14 -14
  126. package/dist/engine/loop/agent-loop.d.ts +3 -3
  127. package/dist/engine/loop/types.d.ts +4 -4
  128. package/dist/engine/lsp/node-lsp-manager.d.ts +2 -2
  129. package/dist/engine/session/import-validate.d.ts +1 -1
  130. package/dist/engine/session/log-digest.d.ts +1 -1
  131. package/dist/engine/session/memory-repo.d.ts +2 -2
  132. package/dist/engine/session/session.d.ts +4 -4
  133. package/dist/fixtures/index.d.ts +4 -4
  134. package/dist/index.d.ts +5 -4
  135. package/dist/index.js +3 -2
  136. package/dist/orchestration/goal.d.ts +1 -1
  137. package/dist/orchestration/run-spec.d.ts +1 -1
  138. package/dist/orchestration/run-workflow-tool.d.ts +12 -12
  139. package/dist/orchestration/workflow-governance.d.ts +4 -4
  140. package/dist/orchestration/workflow-observe.d.ts +1 -1
  141. package/dist/orchestration/workflow-script-runner.d.ts +1 -1
  142. package/dist/orchestration/workflow-script-store.d.ts +9 -9
  143. package/dist/orchestration/workflow-size-guideline.d.ts +1 -1
  144. package/dist/orchestration/workflow-types.d.ts +5 -5
  145. package/dist/orchestration/workflow.d.ts +10 -10
  146. package/dist/prompt-assembly/artifact-store.d.ts +1 -1
  147. package/dist/prompt-assembly/artifact.d.ts +1 -1
  148. package/dist/prompt-assembly/assemble.d.ts +1 -1
  149. package/dist/prompt-assembly/composer.d.ts +2 -2
  150. package/dist/prompt-assembly/epoch.d.ts +2 -2
  151. package/dist/prompt-assembly/event-registry.d.ts +1 -1
  152. package/dist/prompt-assembly/explain.d.ts +3 -3
  153. package/dist/prompt-assembly/tool-catalog.d.ts +1 -1
  154. package/dist/prompt-assembly/turn-snapshot.d.ts +4 -4
  155. package/dist/prompt-assembly/types.d.ts +12 -12
  156. package/dist/prompts/coordinator.d.ts +1 -1
  157. package/dist/prompts/default.d.ts +8 -8
  158. package/dist/prompts/simple-sections.d.ts +3 -3
  159. package/dist/prompts/supervisor.d.ts +2 -2
  160. package/dist/scenarios/full-body.d.ts +3 -3
  161. package/dist/scenarios/scenario-registry.d.ts +1 -1
  162. package/dist/stores/cc/sidecar-transcript.d.ts +3 -3
  163. package/dist/stores/file/fs-atomic.d.ts +2 -2
  164. package/dist/stores/file/index.d.ts +1 -1
  165. package/dist/stores/file/session-store.d.ts +2 -2
  166. package/dist/stores/file/workflow-journal-store.d.ts +4 -4
  167. package/dist/tools/fs/bash-readonly-classifier.d.ts +1 -1
  168. package/dist/tools/fs/encoding.d.ts +4 -4
  169. package/dist/tools/fs/fs-bash.d.ts +3 -3
  170. package/dist/tools/fs/fs-pdf.d.ts +1 -1
  171. package/dist/tools/fs/fs-shared.d.ts +6 -6
  172. package/dist/tools/fs/index.d.ts +2 -2
  173. package/dist/tools/fs/notebook.d.ts +1 -1
  174. package/dist/tools/fs/pdf.d.ts +1 -1
  175. package/dist/tools/fs/read-deny.d.ts +1 -1
  176. package/dist/tools/fs/safety.d.ts +9 -9
  177. package/dist/tools/fs/search.d.ts +2 -2
  178. package/dist/tools/monitor.d.ts +3 -3
  179. package/dist/tools/task-list.d.ts +2 -2
  180. package/dist/tools/web.d.ts +4 -4
  181. package/dist/tools/worktree.d.ts +5 -5
  182. package/package.json +1 -1
  183. package/test/export-surface.snapshot.json +56 -3
@@ -253,17 +253,7 @@ export function normalizeOrgGateVerdict(answer, unreadable) {
253
253
  return clean({ status: "unavailable", disclosures: [unreadable] });
254
254
  return clean({ status: "available", verdict: clean({ behavior: behavior, rule }), ...revisionCell });
255
255
  }
256
- export function normalizePersistedRuleHit(hit) {
257
- if (typeof hit === "string")
258
- return { hit: { rule: hit } };
259
- if (!isPlainOwnRecord(hit))
260
- return {};
261
- if (ownDataValue(hit, "unreadable") === true)
262
- return { unreadable: true };
263
- const rule = ownDataValue(hit, "rule");
264
- if (typeof rule !== "string")
265
- return {};
266
- const dots = ownDataValue(hit, "dots");
256
+ function copiedUsableDots(dots) {
267
257
  let copied;
268
258
  try {
269
259
  if (Array.isArray(dots)) {
@@ -279,7 +269,53 @@ export function normalizePersistedRuleHit(hit) {
279
269
  copied = undefined;
280
270
  }
281
271
  const usable = copied?.every((d) => typeof d.actor === "string" && typeof d.counter === "number" && Number.isFinite(d.counter)) === true && copied.length > 0;
282
- return { hit: { rule, ...(usable ? { dots: copied } : {}) } };
272
+ return usable ? { dots: copied } : {};
273
+ }
274
+ export function normalizePersistedRuleHit(hit) {
275
+ if (typeof hit === "string")
276
+ return { hit: { rules: [{ rule: hit }] } };
277
+ if (!isPlainOwnRecord(hit))
278
+ return {};
279
+ if (ownDataValue(hit, "unreadable") === true)
280
+ return { unreadable: true };
281
+ const rulesRaw = ownDataValue(hit, "rules");
282
+ if (rulesRaw === undefined) {
283
+ const coverageRaw = ownDataValue(hit, "segmentCoverage");
284
+ let rows;
285
+ try {
286
+ if (Array.isArray(coverageRaw)) {
287
+ const walked = [];
288
+ for (let i = 0; i < coverageRaw.length; i++) {
289
+ const r = coverageRaw[i];
290
+ walked.push(isPlainOwnRecord(r) ? { segment: ownDataValue(r, "segment"), covered: ownDataValue(r, "covered") } : { segment: undefined, covered: undefined });
291
+ }
292
+ rows = walked.every((r) => typeof r.segment === "string" && typeof r.covered === "boolean") ? walked : undefined;
293
+ }
294
+ }
295
+ catch {
296
+ rows = undefined;
297
+ }
298
+ return rows !== undefined && rows.length > 0 ? { coverage: rows.map((r) => ({ segment: r.segment, covered: r.covered })) } : {};
299
+ }
300
+ let members;
301
+ try {
302
+ if (Array.isArray(rulesRaw)) {
303
+ const walked = [];
304
+ for (let i = 0; i < rulesRaw.length; i++) {
305
+ const m = rulesRaw[i];
306
+ walked.push(isPlainOwnRecord(m) ? { rule: ownDataValue(m, "rule"), dots: ownDataValue(m, "dots") } : { rule: undefined, dots: undefined });
307
+ }
308
+ members = walked;
309
+ }
310
+ }
311
+ catch {
312
+ members = undefined;
313
+ }
314
+ if (members === undefined || members.length === 0)
315
+ return {};
316
+ if (!members.every((m) => typeof m.rule === "string"))
317
+ return {};
318
+ return { hit: { rules: members.map((m) => ({ rule: m.rule, ...copiedUsableDots(m.dots) })) } };
283
319
  }
284
320
  function preToolUseCrashReason(subject, err) {
285
321
  const raw = err instanceof Error ? err.message.trim() || err.name : String(err);
@@ -672,6 +708,7 @@ export async function runToolGate(input) {
672
708
  }
673
709
  const persistedRuleMandate = persistedRuleMandateOf(input);
674
710
  let personalEvidence = { dotsAbsent: input.persistedRules === undefined ? "not_wired" : "not_adjudicated" };
711
+ let laneCoverage;
675
712
  if (input.persistedRules &&
676
713
  !orgRealApprovalRequired &&
677
714
  decision.action === "ask" &&
@@ -689,21 +726,44 @@ export async function runToolGate(input) {
689
726
  }
690
727
  return await settleOrgVerdictWithin(pendingHit.then(normalizePersistedRuleHit).catch(() => ({ unreadable: true })), { unreadable: true }, { ...(input.abortSignal !== undefined ? { signal: input.abortSignal } : {}), timeoutMs: ORG_ADJUDICATION_TIMEOUT_MS });
691
728
  })();
692
- const hitEntry = answer.hit;
693
- const hit = hitEntry?.rule;
729
+ const hitRules = answer.hit?.rules;
730
+ laneCoverage = answer.coverage;
694
731
  personalEvidence = { dotsAbsent: answer.unreadable === true ? "unavailable" : "no_match" };
695
- if (hit !== undefined && persistedRuleMandate === undefined && decision.matchedAskRule === undefined) {
732
+ const disclosedRuleSet = (rules) => {
733
+ const shown = rules.slice(0, 5).map((r) => inlineUntrusted(r.rule, 200));
734
+ const rest = rules.length - shown.length;
735
+ return rest > 0 ? `${shown.join(", ")} and ${rest} more` : shown.join(", ");
736
+ };
737
+ const unionDotsOf = (rules) => {
738
+ const seen = new Set();
739
+ const union = [];
740
+ for (const r of rules) {
741
+ if (r.dots === undefined || r.dots.length === 0)
742
+ return { dotsAbsent: "not_reported" };
743
+ for (const d of r.dots) {
744
+ const key = JSON.stringify([d.actor, d.counter]);
745
+ if (!seen.has(key)) {
746
+ seen.add(key);
747
+ union.push(d);
748
+ }
749
+ }
750
+ }
751
+ return { dots: union };
752
+ };
753
+ if (hitRules !== undefined && persistedRuleMandate === undefined && decision.matchedAskRule === undefined) {
696
754
  decision = {
697
755
  action: "allow",
698
- message: `a persisted allow rule (${hit}) covers this call`,
756
+ message: hitRules.length === 1
757
+ ? `a persisted allow rule (${disclosedRuleSet(hitRules)}) covers this call`
758
+ : `persisted allow rules (${disclosedRuleSet(hitRules)}) cover every segment of this call`,
699
759
  decisionReason: "persisted_rule",
700
760
  ...(policyRewrite !== undefined ? { updatedInput: policyRewrite } : {}),
701
761
  };
702
- await notifier.notifyAsync(() => input.persistedRules?.onResolved?.({ toolName: req.toolName, toolCallId, rule: hit }), "toolGate.persistedRuleResolved");
762
+ await notifier.notifyAsync(() => input.persistedRules?.onResolved?.({ toolName: req.toolName, toolCallId, rules: hitRules.map((r) => r.rule) }), "toolGate.persistedRuleResolved");
703
763
  }
704
- else if (hit !== undefined) {
705
- personalEvidence = hitEntry?.dots !== undefined && hitEntry.dots.length > 0 ? { dots: hitEntry.dots } : { dotsAbsent: "not_reported" };
706
- const shownRule = inlineUntrusted(hit, 200);
764
+ else if (hitRules !== undefined) {
765
+ personalEvidence = unionDotsOf(hitRules);
766
+ const shownRule = disclosedRuleSet(hitRules);
707
767
  const mandateNoun = persistedRuleMandate === "operator_always"
708
768
  ? "this deployment mandates per-call confirmation for shell commands (shellGate: always)"
709
769
  : persistedRuleMandate !== undefined
@@ -712,12 +772,14 @@ export async function runToolGate(input) {
712
772
  decision = {
713
773
  ...decision,
714
774
  persistedRuleShadowed: shownRule,
715
- message: `${decision.message !== undefined ? `${decision.message} ` : ""}(a persisted allow rule (${shownRule}) matches this call but does not clear the ask — ${mandateNoun})`,
775
+ message: hitRules.length === 1
776
+ ? `${decision.message !== undefined ? `${decision.message} ` : ""}(a persisted allow rule (${shownRule}) matches this call but does not clear the ask — ${mandateNoun})`
777
+ : `${decision.message !== undefined ? `${decision.message} ` : ""}(persisted allow rules (${shownRule}) match this call but do not clear the ask — ${mandateNoun})`,
716
778
  };
717
779
  }
718
780
  }
719
781
  if (decision.action === "ask") {
720
- decision = { ...decision, ruleEvidence: mintRuleEvidence(personalEvidence) };
782
+ decision = { ...decision, ruleEvidence: mintRuleEvidence(personalEvidence), ...(laneCoverage !== undefined ? { segmentCoverage: laneCoverage } : {}) };
721
783
  }
722
784
  if (input.autoMode &&
723
785
  !orgRealApprovalRequired &&
@@ -779,7 +841,7 @@ export async function runToolGate(input) {
779
841
  ? { origin: orgAskOrigin !== undefined ? `org_${orgAskOrigin}` : "policy" }
780
842
  : undefined;
781
843
  if (suspendAsk && decision.action === "ask") {
782
- const suspended = await suspendAsk(req, currentInput, safety, undefined, realApprovalOf(decision), decision.action === "ask" ? decision.persistedRuleShadowed : undefined, decision.action === "ask" ? decision.decisionReason : undefined, decision.action === "ask" ? decision.probeReason : undefined, decision.action === "ask" ? decision.probeCause : undefined);
844
+ const suspended = await suspendAsk(req, currentInput, safety, undefined, realApprovalOf(decision), decision.action === "ask" ? decision.persistedRuleShadowed : undefined, decision.action === "ask" ? decision.decisionReason : undefined, decision.action === "ask" ? decision.probeReason : undefined, decision.action === "ask" ? decision.probeCause : undefined, decision.action === "ask" ? decision.segmentCoverage : undefined);
783
845
  if (suspended) {
784
846
  if ("parkFailed" in suspended)
785
847
  parkFailed = suspended.parkFailed;
@@ -798,7 +860,7 @@ export async function runToolGate(input) {
798
860
  req.args = outcome.presentedInput;
799
861
  }
800
862
  if (suspendAsk && outcome.parkDeclined && parkFailed === undefined) {
801
- const suspended = await suspendAsk(req, currentInput, safety, true, realApprovalOf(decision), decision.action === "ask" ? decision.persistedRuleShadowed : undefined, decision.action === "ask" ? decision.decisionReason : undefined, decision.action === "ask" ? decision.probeReason : undefined, decision.action === "ask" ? decision.probeCause : undefined);
863
+ const suspended = await suspendAsk(req, currentInput, safety, true, realApprovalOf(decision), decision.action === "ask" ? decision.persistedRuleShadowed : undefined, decision.action === "ask" ? decision.decisionReason : undefined, decision.action === "ask" ? decision.probeReason : undefined, decision.action === "ask" ? decision.probeCause : undefined, decision.action === "ask" ? decision.segmentCoverage : undefined);
802
864
  if (suspended) {
803
865
  if ("parkFailed" in suspended)
804
866
  parkFailed = suspended.parkFailed;
@@ -830,7 +892,7 @@ export async function runToolGate(input) {
830
892
  askDenyResolution = resolved.resolution;
831
893
  decision = resolved;
832
894
  if (resolved.action === "deny" && resolved.approverUnavailable === true && suspendAsk && parkFailed === undefined) {
833
- const suspended = await suspendAsk(req, currentInput, safety, true, realApprovalOf(askBeforeResolve), askBeforeResolve.action === "ask" ? askBeforeResolve.persistedRuleShadowed : undefined, askBeforeResolve.action === "ask" ? askBeforeResolve.decisionReason : undefined, askBeforeResolve.action === "ask" ? askBeforeResolve.probeReason : undefined, askBeforeResolve.action === "ask" ? askBeforeResolve.probeCause : undefined);
895
+ const suspended = await suspendAsk(req, currentInput, safety, true, realApprovalOf(askBeforeResolve), askBeforeResolve.action === "ask" ? askBeforeResolve.persistedRuleShadowed : undefined, askBeforeResolve.action === "ask" ? askBeforeResolve.decisionReason : undefined, askBeforeResolve.action === "ask" ? askBeforeResolve.probeReason : undefined, askBeforeResolve.action === "ask" ? askBeforeResolve.probeCause : undefined, askBeforeResolve.action === "ask" ? askBeforeResolve.segmentCoverage : undefined);
834
896
  if (suspended) {
835
897
  if ("parkFailed" in suspended)
836
898
  parkFailed = suspended.parkFailed;
@@ -36,7 +36,7 @@ export declare const IMAGE_MAX_HEIGHT = 2000;
36
36
  * replaced by an explanatory text block with the size + path; a within-limit image passes through unchanged.
37
37
  */
38
38
  /**
39
- * Optional image-resize seam (design/116 review CONFIRM-1, clay 2026-07-02 加 seam 保体验): given an
39
+ * Optional image-resize seam (design/116 review CONFIRM-1, 裁定 2026-07-02 加 seam 保体验): given an
40
40
  * over-limit image, return a smaller re-encoded one — or undefined when it can't. CC resizes with sharp
41
41
  * (imageResizer.ts: fit within 2000x2000, JPEG quality ladder) so the model still SEES a degraded image;
42
42
  * core must not hard-depend on a native image library, so the capability is injected (or auto-detected).
@@ -46,7 +46,7 @@ export interface LockedPreflight {
46
46
  * always enforced, resolved ONCE here. With `toolPolicy` locked, the deployment's own
47
47
  * `deps.toolPolicy` is the whole answer (a spec-supplied policy has already been refused). */
48
48
  toolPolicy: ToolPolicy | undefined;
49
- /** F-012 (codex round 8): the effective resume-edit re-adjudication OVERRIDE, resolved lock-aware in
49
+ /** F-012: the effective resume-edit re-adjudication OVERRIDE, resolved lock-aware in
50
50
  * the SAME slot as {@link toolPolicy}. With `toolPolicy` locked, a spec-supplied
51
51
  * `basePolicyForResumeEdit` has already been refused wholesale, so this is the deployment's own
52
52
  * `deps.basePolicyForResumeEdit` (or undefined — the caller-policy fallback then applies at the use
@@ -50,7 +50,7 @@ export interface LspSymbolInfo {
50
50
  positionUnknown?: boolean;
51
51
  }
52
52
  /**
53
- * RB-232(黑板 [1937]): WHY a "none" result was "none". Absent `reason` = a GENUINE empty answer (the server
53
+ * RB-232: WHY a "none" result was "none". Absent `reason` = a GENUINE empty answer (the server
54
54
  * answered null/[] — today's behavior byte-for-byte, and what an external manager returning bare
55
55
  * `{kind:"none"}` keeps getting). The failure reasons exist because one literal "No results." used to cover
56
56
  * both real emptiness and every transport-level fault — a findReferences that TIMED OUT read as "no
@@ -110,7 +110,7 @@ export interface MailboxStore {
110
110
  maxAgeMs?: number;
111
111
  }): Promise<number>;
112
112
  }
113
- /** RB-250② (2026-07-28, 黑板 [1937]) class fix — the box age for `reap` is the MAX `sentAt`, not the
113
+ /** RB-250② (2026-07-28) class fix — the box age for `reap` is the MAX `sentAt`, not the
114
114
  * last array element: `sentAt` is caller-supplied, so append order need not be time order, and taking
115
115
  * the tail let a box whose freshest message was mid-array be swept — both bundled backends discarded
116
116
  * a brand-new message while the CC adapter (stores/cc/mailbox-store.ts, the in-package reference)
@@ -209,7 +209,7 @@ export interface MaterializedMcp {
209
209
  */
210
210
  statuses: McpServerStatus[];
211
211
  /**
212
- * [1605] RefreshMcpTools (CC 2.1.218 parity, cli-锚形) — re-read the tool list of one server (or
212
+ * RefreshMcpTools (CC 2.1.218 parity, anchored to the CLI form) — re-read the tool list of one server (or
213
213
  * every CONNECTED server when `server` is omitted) over the EXISTING connection. NEVER dials or
214
214
  * re-dials: a dead/never-connected server reports `not_connected`; a live listTools failure
215
215
  * reports `failed` (the connection is left as-is). A refreshed entry carries the NEW intake
@@ -225,7 +225,7 @@ export interface MaterializedMcp {
225
225
  refresh: (server?: string) => Promise<McpRefreshResult[]>;
226
226
  dispose: () => Promise<void>;
227
227
  }
228
- /** [1605] One per-server entry of {@link MaterializedMcp.refresh}. `added`/`removed` are namespaced
228
+ /** One per-server entry of {@link MaterializedMcp.refresh}. `added`/`removed` are namespaced
229
229
  * (`mcp__<server>__<tool>`) names; `tools`/`axes`/`dropped` are present only on `"refreshed"`.
230
230
  * The model-facing refresh receipt renders each `dropped` entry with ITS OWN `reason` (schema
231
231
  * gate, same-listing name collision, …) — never a blanket label; a consumer matching that receipt
@@ -654,7 +654,7 @@ export declare function listEntryFingerprint(entry: unknown): string | undefined
654
654
  * page cap — a bounded multiplier, not a new unbounded surface. Per-entry admission stays where it
655
655
  * already is (the intake schema gate, which also drops a name a later page repeats).
656
656
  *
657
- * TIME, the reason `budgetMs` exists (codex review, confirmed): the page cap bounds REQUESTS, not
657
+ * TIME, the reason `budgetMs` exists (confirmed): the page cap bounds REQUESTS, not
658
658
  * latency. Giving every page its own full request timeout would let a server that stalls each page hold
659
659
  * a connect (or a refresh) for up to twenty timeouts — twenty minutes at the SDK's 60s default, where
660
660
  * an unpaginated listing cost one. So the WALK gets the budget a single listing used to have: page one
@@ -0,0 +1,207 @@
1
+ /**
2
+ * design/376 片② — the host CONSOLIDATION DRIVER verb: one call that takes a long-accumulated,
3
+ * never-consolidated library to its fold fixpoint through the four engine verbs, as a PERSISTENT
4
+ * MULTI-CYCLE RUN.
5
+ *
6
+ * Why a run is a first-class object: under factory defaults a cold-start consolidation is a
7
+ * dozen-plus cycles with a hard 60-second throttle floor between them — a persistent job measured
8
+ * in minutes, not a call. The engine deliberately keeps NO driver state (its gate row settles per
9
+ * plan and clears the session count), so without a durable run account a process exit between two
10
+ * cycles would strand the work: no open plan to reconcile, no session edge to re-arm, and a naive
11
+ * re-entry would re-mint the whole plan at full model cost. The run row under the control plane is
12
+ * the resume seat: `runMemoryConsolidation` first looks for a PENDING run of the scope and
13
+ * CONTINUES it (same plan cache, same force requestId, zero new mint) — the idempotent re-entry
14
+ * GD-15 pins.
15
+ *
16
+ * Boundary reminders (design/339 §1.1, unchanged by this module):
17
+ * - the engine adjudicates and commits; this driver only proposes and schedules — every write
18
+ * goes through `commitConsolidationPlan`, nothing here touches the store;
19
+ * - the engine holds no model seat; the model reaches this module as an explicit chat function;
20
+ * - core still owns no timer: the throttle waits here run INSIDE a host's explicit call (or the
21
+ * auto lane's task-boundary tick), never on a background schedule of core's own.
22
+ */
23
+ import type { EngineNotice } from "../types.js";
24
+ import { type ConsolidationGateRow, type ConsolidationRunStopReason, type MemoryConsolidationOptions } from "./consolidation.js";
25
+ import { type ConsolidationPlanSummary } from "./engine.js";
26
+ import { mintLlmConsolidationPlan, type ConsolidationDriveCycleRow, type ConsolidationDriveEngine, type ConsolidationFoldState, type DistillerChatFn, type LlmConsolidationPlan, type LlmConsolidationPlanProduct, type LlmDistillerContract } from "./distiller.js";
27
+ export declare const CONSOLIDATION_DRIVER_RUNS_FILE = "distiller-runs.json";
28
+ /** §1.6-4 — the mint archive: what was shown to the model and what it answered, verbatim, per run
29
+ * (the fold law's audit other-half; strict-family placement beside the plan workbook). */
30
+ export declare const CONSOLIDATION_DRIVER_PLANS_DIR = "distiller-plans";
31
+ export interface ConsolidationDriverRunRow {
32
+ runId: string;
33
+ scope: string;
34
+ /** The WRITER claim: every open/resume bumps this under the run
35
+ * store's lock, and every later write of the row is conditional on (runId, attempt) still
36
+ * standing — of two invocations that raced onto one scope, exactly one keeps writing; the
37
+ * superseded one stops with a coded error instead of silently clobbering the winner's counters
38
+ * or terminal state. (Model spend under a genuine concurrent double-call is still at-least-once
39
+ * — the claim serializes the ACCOUNT; the engine's plan-seat CAS serializes the store, and
40
+ * cross-node cost suppression is the engine lease seat's job.) */
41
+ attempt: number;
42
+ /** The run-scoped force credential: ONE requestId across every forced snapshot of the run (the
43
+ * engine dedups the FORCED audit announcement on it — one line per run). */
44
+ requestId: string;
45
+ /** The contract version of the run's surviving MINT — stamped at open, RE-stamped (with
46
+ * {@link model}) by a resume that re-mints. */
47
+ contractVersion: string;
48
+ /** The id that minted the run's surviving plan: stamped at open; RE-stamped on a resume that
49
+ * re-mints (the plan cache absent — nothing of the opening mint survived, so the current seat
50
+ * mints every byte this run commits). The per-attempt plan archive is the byte-level anchor
51
+ * either way. */
52
+ model: string;
53
+ startedAt: number;
54
+ state: "pending" | "settled";
55
+ /** Terminal only; the ONE closed set the receipt and the `_incomplete` notice share. */
56
+ outcome?: ConsolidationRunStopReason;
57
+ settledAt?: number;
58
+ stopDetail?: string;
59
+ /** The minted plan's replayable half (mint ONCE per run; each cycle filters, never re-mints). */
60
+ planCache?: {
61
+ maxInputsPerProduct: number;
62
+ products: LlmConsolidationPlanProduct[];
63
+ };
64
+ /** The archived mint transcript's file name under {@link CONSOLIDATION_DRIVER_PLANS_DIR}. */
65
+ planArchive?: string;
66
+ /** COMMITTED cycles only (the store moved): a refused/fuse-blocked/zero-delta cycle row does
67
+ * not count — the receipt's "committed cycle(s)" label and the incomplete notice's zero-cycle
68
+ * branch both read this number as writes. */
69
+ cyclesDone: number;
70
+ /** Store-delta superseded total across EVERY invocation of this run (the conservation wall's
71
+ * evidence half — see the convergence check in the verb). Accumulated per cycle AFTER the
72
+ * commit, so a crash between a cycle's commit and its row write loses that cycle's delta —
73
+ * {@link foldBaseline} is the wall's immune second witness for exactly that window. */
74
+ entriesSuperseded: number;
75
+ /** The scope's fold-state `superseded` count at run OPEN (fresh claim; absent on rows opened
76
+ * before the field existed). The conservation wall's STORE anchor: `final.superseded − this`
77
+ * attests the run's folds even when a crash lands between a cycle's commit and its account
78
+ * write — the seat serializes this scope's folds, so the since-open delta is this run's. */
79
+ foldBaseline?: {
80
+ superseded: number;
81
+ };
82
+ planIds: string[];
83
+ usage: {
84
+ calls: number;
85
+ promptTokens: number;
86
+ completionTokens: number;
87
+ retries: number;
88
+ truncations: number;
89
+ };
90
+ repairs: {
91
+ hallucinatedMembers: number;
92
+ duplicateMembers: number;
93
+ nonIntegerMembers: number;
94
+ emptyGroups: number;
95
+ oversizeGroups: number;
96
+ };
97
+ writeFailures: Array<{
98
+ key: string;
99
+ members: number;
100
+ reason: string | null;
101
+ }>;
102
+ residue?: Array<{
103
+ name: string;
104
+ targets: number;
105
+ }>;
106
+ /** The next moment a snapshot can pass the hard throttle (resume scheduling hint). */
107
+ nextEligibleAt?: number;
108
+ }
109
+ /** Host read face: the scope's latest driver run row (pending = resumable; settled = audit). */
110
+ export declare function readConsolidationDriverRun(controlDir: string, scope: string): ConsolidationDriverRunRow | undefined;
111
+ /** Archive one mint (prompts + transcript verbatim) beside the run account. The name carries the
112
+ * CLAIM (`attempt`): two invocations that raced onto one run each archive
113
+ * under their own name, so a superseded worker can never overwrite the winning transcript — the
114
+ * run row's `planArchive` (written under the attempt guard) names the one that counts. */
115
+ export declare function distillerPlanArchiveName(runId: string, attempt: number): string;
116
+ export declare function archiveDistillerPlan(controlDir: string, runId: string, plan: LlmConsolidationPlan, attempt?: number): string;
117
+ /** The engine face this verb consumes — `MemoryEngine` satisfies it structurally. */
118
+ export interface ConsolidationDriverEngine extends ConsolidationDriveEngine {
119
+ readConsolidationGate(scope: string): ConsolidationGateRow | undefined;
120
+ listConsolidationPlans(): ConsolidationPlanSummary[];
121
+ readConsolidationFoldState(scope: string): Promise<ConsolidationFoldState>;
122
+ readonly controlPlaneRoot: string;
123
+ }
124
+ export interface RunMemoryConsolidationOptions {
125
+ /** The explicit model seat (design/339 §1.1: the ENGINE holds none). Build one with
126
+ * `openAiCompatChatSeat` or hand any JSON-only completion function. */
127
+ chat: DistillerChatFn;
128
+ /** The explicit model id, recorded into the run and the archive (alias spellings refuse). */
129
+ model: string;
130
+ /** Audit locator recorded into the archive when the seat is endpoint-shaped. */
131
+ baseUrl?: string;
132
+ /** The frozen prompt contract; default = the product v1 (dl-3 byte-identical). A different
133
+ * version here is a deployment's explicit measured choice. */
134
+ contract?: LlmDistillerContract;
135
+ /** The PROTOCOL knobs — pass the same object the engine was constructed with
136
+ * (`MemoryEngineOptions.consolidation`); the driver derives its throttle window and per-plan
137
+ * caps from it. Defaults = the factory defaults. */
138
+ consolidation?: MemoryConsolidationOptions;
139
+ /** Abort the run after this many cycles in THIS call (the run row stays pending only on crash;
140
+ * a reached bound settles `max_cycles`). Positive integer; default 64. */
141
+ maxCycles?: number;
142
+ /** R2 — the structural-repair budget over the mint's member references: the ratio of member
143
+ * claims the sanitiser had to DROP (hallucinated + duplicate + non-integer) to all member
144
+ * claims the model made. Above this, the plan does not represent the model's intent and the
145
+ * run aborts with zero writes rather than committing a harness-repaired fold. In [0, 1];
146
+ * default 0.10. (Empty groups are the same drops counted at group grain; oversize groups are
147
+ * not repairs — they ride on, named, and the schedule reports them infeasible. Format repairs
148
+ * and retries are disclosed separately and bounded by the contract's own retry count.) */
149
+ mintRepairBudget?: number;
150
+ /** The run's force credential; defaults to a minted id. A RESUMED run keeps its original. */
151
+ requestId?: string;
152
+ /** Host seams for the throttle waits (tests and hosts with their own clocks). */
153
+ now?: () => number;
154
+ sleep?: (ms: number) => Promise<void>;
155
+ onProgress?: Parameters<typeof mintLlmConsolidationPlan>[0]["onProgress"];
156
+ }
157
+ export interface ConsolidationRunReceipt {
158
+ runId: string;
159
+ scope: string;
160
+ /** Whether this call CONTINUED a pending run (no new mint) instead of starting one. */
161
+ resumed: boolean;
162
+ outcome: ConsolidationRunStopReason;
163
+ converged: boolean;
164
+ stopDetail?: string;
165
+ /** This call's cycles (store-delta accounted); `cyclesDone` is the run total across resumes. */
166
+ cycles: ConsolidationDriveCycleRow[];
167
+ cyclesDone: number;
168
+ productsCommitted: number;
169
+ entriesSuperseded: number;
170
+ planIds: string[];
171
+ model: string;
172
+ contractVersion: string;
173
+ usage: ConsolidationDriverRunRow["usage"];
174
+ repairs: ConsolidationDriverRunRow["repairs"];
175
+ writeFailures: ConsolidationDriverRunRow["writeFailures"];
176
+ residue: Array<{
177
+ name: string;
178
+ targets: number;
179
+ }>;
180
+ planArchive?: string;
181
+ /** Advisory notices minted for this run (the `_incomplete` disclosure when not converged). */
182
+ notices: EngineNotice[];
183
+ /** A failed announcement enqueue is disclosed, never allowed to fail the settled run. */
184
+ announceFailures?: string[];
185
+ }
186
+ /**
187
+ * Run (or RESUME) one scope's consolidation as a persistent multi-cycle job. See the module note
188
+ * for the shape; the per-refusal behavior implements the design/376 §1.2 failure table:
189
+ *
190
+ * - `_disabled` / a corrupt gate / an unreadable governance account BEFORE any commit: the coded
191
+ * error is rethrown (a configuration/store fault the host must repair — a pending run row, if
192
+ * one exists, stays resumable after the repair);
193
+ * - `_gated` (time/session gate): retried under the run's force requestId — the run-level force
194
+ * the design names; the FORCED audit line is engine-deduplicated to once per run;
195
+ * - `_gated` (hard throttle): the driver WAITS the window out (`min(minRunIntervalMs, 60s)`) and
196
+ * continues — a throttled cycle is scheduling, not a terminal;
197
+ * - `_busy`: never a blind snapshot retry (the seat refuses snapshots too — a blind loop spins
198
+ * forever): `reconcileConsolidation` first; a conflict/corrupt plan parks the run for the host
199
+ * valve; a live competing worker is waited out briefly, then reported;
200
+ * - model failures / a mint over the repair budget: the run settles `driver_failed` with ZERO
201
+ * writes — the mint sits between snapshot and commit, so no seat was taken, `lastRunAt` never
202
+ * moved and the time gate is not burned (the failure is free to retry);
203
+ * - a conflict plan mid-run parks (`parked`); fuse residue and the cycle bound settle
204
+ * `fuse_residue` / `max_cycles` with the residue named — the next run's FULL snapshot
205
+ * re-distills the shrunken library (cross-run = re-mint; in-run = plan cache).
206
+ */
207
+ export declare function runMemoryConsolidationDriver(engine: ConsolidationDriverEngine, scope: string, opts: RunMemoryConsolidationOptions): Promise<ConsolidationRunReceipt>;