@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
@@ -0,0 +1,378 @@
1
+ import { join } from "node:path";
2
+ import { uuidv7 } from "../../internal/harness.js";
3
+ import { atomicWriteFileSync, ensureDirExists, lockedStrictUpdate, readStrictSidecar, enqueueMemoryAnnouncement, ControlPlaneCorruptError } from "./layout.js";
4
+ import { inlineUntrusted } from "../untrusted-text.js";
5
+ import { CONSOLIDATION_FORCE_THROTTLE_FLOOR_MS, screenConsolidationOptions, supersessionFuseCeiling, } from "./consolidation.js";
6
+ import { memoryConsolidationIncompleteNotice } from "./engine.js";
7
+ import { CONSOLIDATION_DRIVE_ABORT, CONSOLIDATION_SNAPSHOT_PARKED, MEMORY_DISTILLER_CONTRACT_V1, driveConsolidationToFixpoint, isAliasModelId, llmPlanDistiller, mintLlmConsolidationPlan, } from "./distiller.js";
8
+ export const CONSOLIDATION_DRIVER_RUNS_FILE = "distiller-runs.json";
9
+ export const CONSOLIDATION_DRIVER_PLANS_DIR = "distiller-plans";
10
+ function coerceRunsFile(raw) {
11
+ if (raw === undefined)
12
+ return { v: 1, scopes: {} };
13
+ if (typeof raw !== "object" || raw === null || raw.v !== 1 || typeof raw.scopes !== "object" || raw.scopes === null) {
14
+ throw new ControlPlaneCorruptError("memory consolidation driver run account has an unrecognized shape (fail-closed sidecar)");
15
+ }
16
+ for (const [scope, rowRaw] of Object.entries(raw.scopes)) {
17
+ const r = rowRaw;
18
+ if (typeof r !== "object" || r === null || typeof r["runId"] !== "string" || (r["state"] !== "pending" && r["state"] !== "settled") || typeof r["cyclesDone"] !== "number") {
19
+ throw new ControlPlaneCorruptError(`memory consolidation driver run row for scope ${JSON.stringify(scope)} is malformed (fail-closed sidecar)`);
20
+ }
21
+ }
22
+ return raw;
23
+ }
24
+ export function readConsolidationDriverRun(controlDir, scope) {
25
+ return coerceRunsFile(readStrictSidecar(controlDir, CONSOLIDATION_DRIVER_RUNS_FILE, "memory consolidation driver run account")).scopes[scope];
26
+ }
27
+ function writeDriverRun(controlDir, row) {
28
+ lockedStrictUpdate(controlDir, CONSOLIDATION_DRIVER_RUNS_FILE, "memory consolidation driver run account", coerceRunsFile, (file) => {
29
+ const cur = file.scopes[row.scope];
30
+ if (cur === undefined || cur.runId !== row.runId || (cur.attempt ?? 0) !== row.attempt) {
31
+ const e = new Error(`memory consolidation driver run seat for scope ${JSON.stringify(row.scope)} was taken over by a newer invocation${cur !== undefined ? ` (run ${cur.runId} attempt ${cur.attempt ?? 0})` : ""} — this worker stops without settling; the newer claim owns the account.`);
32
+ e.code = "memory.consolidation_driver_superseded";
33
+ e[CONSOLIDATION_DRIVE_ABORT] = true;
34
+ throw e;
35
+ }
36
+ file.scopes[row.scope] = row;
37
+ return { next: file, result: undefined };
38
+ });
39
+ }
40
+ function claimDriverRun(controlDir, scope, fresh) {
41
+ return lockedStrictUpdate(controlDir, CONSOLIDATION_DRIVER_RUNS_FILE, "memory consolidation driver run account", coerceRunsFile, (file) => {
42
+ const cur = file.scopes[scope];
43
+ if (cur !== undefined && cur.state === "pending") {
44
+ cur.attempt = (cur.attempt ?? 0) + 1;
45
+ file.scopes[scope] = cur;
46
+ return { next: file, result: { row: structuredClone(cur), resumed: true } };
47
+ }
48
+ const row = fresh();
49
+ file.scopes[scope] = row;
50
+ return { next: file, result: { row: structuredClone(row), resumed: false } };
51
+ });
52
+ }
53
+ export function distillerPlanArchiveName(runId, attempt) {
54
+ return `${runId}-a${attempt}.json`;
55
+ }
56
+ export function archiveDistillerPlan(controlDir, runId, plan, attempt = 1) {
57
+ ensureDirExists(join(controlDir, CONSOLIDATION_DRIVER_PLANS_DIR));
58
+ const name = distillerPlanArchiveName(runId, attempt);
59
+ atomicWriteFileSync(join(controlDir, CONSOLIDATION_DRIVER_PLANS_DIR, name), `${JSON.stringify(plan, null, 2)}\n`);
60
+ return name;
61
+ }
62
+ function driverConfigRefusal(message) {
63
+ const e = new Error(message);
64
+ e.code = "config.memory_consolidation_driver";
65
+ return e;
66
+ }
67
+ class DriverMintFailure extends Error {
68
+ detail;
69
+ constructor(detail) {
70
+ super(detail);
71
+ this.detail = detail;
72
+ }
73
+ }
74
+ const codeOf = (err) => {
75
+ const c = err?.code;
76
+ return typeof c === "string" ? c : undefined;
77
+ };
78
+ const MAX_THROTTLE_WAITS_PER_SNAPSHOT = 4;
79
+ const MAX_BUSY_WAITS = 2;
80
+ export async function runMemoryConsolidationDriver(engine, scope, opts) {
81
+ if (typeof opts?.chat !== "function")
82
+ throw driverConfigRefusal("runMemoryConsolidationDriver needs an explicit chat seat (opts.chat) — the engine holds no model seat and a driver that silently skips would publish nothing as a model's answer.");
83
+ if (typeof opts.model !== "string" || opts.model.trim() === "" || isAliasModelId(opts.model)) {
84
+ throw driverConfigRefusal(`runMemoryConsolidationDriver needs an EXPLICIT model id, got ${JSON.stringify(opts.model)} — an alias can be re-pointed upstream and would move the run with no trace in the archive.`);
85
+ }
86
+ if (opts.mintRepairBudget !== undefined && (typeof opts.mintRepairBudget !== "number" || !Number.isFinite(opts.mintRepairBudget) || opts.mintRepairBudget < 0 || opts.mintRepairBudget > 1)) {
87
+ throw driverConfigRefusal(`runMemoryConsolidationDriver: mintRepairBudget must be a number in [0, 1] when present (got ${String(opts.mintRepairBudget)}) — an unevaluable budget is refused loudly, never folded to the default.`);
88
+ }
89
+ if (opts.maxCycles !== undefined && (!Number.isInteger(opts.maxCycles) || opts.maxCycles < 1)) {
90
+ throw driverConfigRefusal(`runMemoryConsolidationDriver: maxCycles must be a positive integer when present (got ${String(opts.maxCycles)}).`);
91
+ }
92
+ const contract = opts.contract ?? MEMORY_DISTILLER_CONTRACT_V1;
93
+ if (typeof contract?.version !== "string" || contract.version === "" || typeof contract?.grouping?.instructions !== "string" || typeof contract?.writing?.instructions !== "string") {
94
+ throw driverConfigRefusal("runMemoryConsolidationDriver: opts.contract does not carry the contract shape (version + grouping/writing instructions).");
95
+ }
96
+ const screened = screenConsolidationOptions(opts.consolidation ?? {}, "carry");
97
+ const mintRepairBudget = opts.mintRepairBudget ?? 0.1;
98
+ const now = opts.now ?? Date.now;
99
+ const rawSleep = opts.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
100
+ const sleep = async (ms) => {
101
+ try {
102
+ await rawSleep(ms);
103
+ }
104
+ catch (err) {
105
+ const e = typeof err === "object" && err !== null ? err : new Error(String(err));
106
+ Reflect.set(e, CONSOLIDATION_DRIVE_ABORT, true);
107
+ throw e;
108
+ }
109
+ };
110
+ const throttleWindowMs = Math.min(screened.minRunIntervalMs, CONSOLIDATION_FORCE_THROTTLE_FLOOR_MS);
111
+ const controlDir = engine.controlPlaneRoot;
112
+ const parkedSeat = await recoverSeat(engine, scope, { sleep, windowMs: throttleWindowMs });
113
+ const openFold = await engine.readConsolidationFoldState(scope);
114
+ const { row: run, resumed } = claimDriverRun(controlDir, scope, () => ({
115
+ runId: uuidv7(),
116
+ scope,
117
+ attempt: 1,
118
+ requestId: opts.requestId ?? `consolidation-run-${uuidv7()}`,
119
+ contractVersion: contract.version,
120
+ model: opts.model,
121
+ startedAt: now(),
122
+ state: "pending",
123
+ cyclesDone: 0,
124
+ entriesSuperseded: 0,
125
+ foldBaseline: { superseded: openFold.superseded },
126
+ planIds: [],
127
+ usage: { calls: 0, promptTokens: 0, completionTokens: 0, retries: 0, truncations: 0 },
128
+ repairs: { hallucinatedMembers: 0, duplicateMembers: 0, nonIntegerMembers: 0, emptyGroups: 0, oversizeGroups: 0 },
129
+ writeFailures: [],
130
+ }));
131
+ if (resumed && run.planCache === undefined && (run.model !== opts.model || run.contractVersion !== contract.version)) {
132
+ run.model = opts.model;
133
+ run.contractVersion = contract.version;
134
+ }
135
+ const announceFailures = [];
136
+ const announce = (items) => {
137
+ try {
138
+ enqueueMemoryAnnouncement(controlDir, { kind: "gate", at: now(), items });
139
+ }
140
+ catch (err) {
141
+ announceFailures.push(err instanceof Error ? err.message : String(err));
142
+ }
143
+ };
144
+ const settle = (outcome, drive, stopDetail) => {
145
+ run.state = "settled";
146
+ run.outcome = outcome;
147
+ run.settledAt = now();
148
+ if (stopDetail !== undefined)
149
+ run.stopDetail = stopDetail;
150
+ run.residue = run.residue ?? drive?.stop?.fuseBlocked;
151
+ writeDriverRun(controlDir, run);
152
+ const notices = [];
153
+ if (outcome !== "converged") {
154
+ const notice = memoryConsolidationIncompleteNotice({ scope, runId: run.runId, stop: outcome, residueProducts: run.residue?.length ?? 0, cyclesDone: run.cyclesDone });
155
+ notices.push(notice);
156
+ announce([notice.message]);
157
+ }
158
+ return {
159
+ runId: run.runId,
160
+ scope,
161
+ resumed,
162
+ outcome,
163
+ converged: outcome === "converged",
164
+ ...(run.stopDetail !== undefined ? { stopDetail: run.stopDetail } : {}),
165
+ cycles: drive?.cycles ?? [],
166
+ cyclesDone: run.cyclesDone,
167
+ productsCommitted: drive?.productsCommitted ?? 0,
168
+ entriesSuperseded: drive?.entriesSuperseded ?? 0,
169
+ planIds: [...run.planIds],
170
+ model: run.model,
171
+ contractVersion: run.contractVersion,
172
+ usage: { ...run.usage },
173
+ repairs: { ...run.repairs },
174
+ writeFailures: [...run.writeFailures],
175
+ residue: run.residue ?? [],
176
+ ...(run.planArchive !== undefined ? { planArchive: run.planArchive } : {}),
177
+ notices,
178
+ ...(announceFailures.length > 0 ? { announceFailures: [...announceFailures] } : {}),
179
+ };
180
+ };
181
+ if (parkedSeat !== undefined) {
182
+ return settle("parked", undefined, parkedSeat);
183
+ }
184
+ let staleResidue = [];
185
+ const distill = async (candidates, snapshot, s) => {
186
+ if (run.planCache === undefined) {
187
+ let minted;
188
+ try {
189
+ minted = await mintLlmConsolidationPlan({
190
+ candidates,
191
+ chat: opts.chat,
192
+ model: opts.model,
193
+ ...(opts.baseUrl !== undefined ? { baseUrl: opts.baseUrl } : {}),
194
+ maxInputsPerProduct: screened.maxInputsPerProduct,
195
+ contract,
196
+ onProgress: opts.onProgress ?? null,
197
+ });
198
+ }
199
+ catch (err) {
200
+ if (codeOf(err) === "config.memory_consolidation_driver")
201
+ throw err;
202
+ throw new DriverMintFailure(`the model seat failed during the mint: ${err instanceof Error ? err.message : String(err)}`);
203
+ }
204
+ const transcript = minted.ok ? minted.plan.minting.transcript : minted.calls;
205
+ run.usage.calls += transcript.length;
206
+ for (const c of transcript) {
207
+ run.usage.promptTokens += c.usage?.promptTokens ?? 0;
208
+ run.usage.completionTokens += c.usage?.completionTokens ?? 0;
209
+ }
210
+ if (!minted.ok) {
211
+ throw new DriverMintFailure(minted.reason);
212
+ }
213
+ const plan = minted.plan;
214
+ run.usage.retries += plan.minting.parseRepairs.retries;
215
+ run.usage.truncations += plan.minting.parseRepairs.lengthCapped.length;
216
+ run.repairs = {
217
+ hallucinatedMembers: plan.minting.repairs.hallucinatedMembers,
218
+ duplicateMembers: plan.minting.repairs.duplicateMembers,
219
+ nonIntegerMembers: plan.minting.repairs.nonIntegerMembers,
220
+ emptyGroups: plan.minting.repairs.emptyGroups,
221
+ oversizeGroups: plan.minting.repairs.oversizeGroups.length,
222
+ };
223
+ run.writeFailures = plan.minting.writeFailures.map((w) => ({ key: w.key, members: w.members, reason: w.reason })) ?? [];
224
+ const dropped = plan.minting.repairs.hallucinatedMembers + plan.minting.repairs.duplicateMembers + plan.minting.repairs.nonIntegerMembers;
225
+ const claimed = plan.products.reduce((a, p) => a + p.inputIds.length, 0) + plan.minting.writeFailures.reduce((a, w) => a + w.members, 0);
226
+ const total = dropped + claimed;
227
+ const ratio = total === 0 ? (dropped > 0 ? 1 : 0) : dropped / total;
228
+ if (ratio > mintRepairBudget) {
229
+ throw new DriverMintFailure(`the mint needed structural repairs on ${dropped} of ${total} member claim(s) (${(ratio * 100).toFixed(1)}% > mintRepairBudget ${(mintRepairBudget * 100).toFixed(1)}%) — a plan repaired this much does not represent the model's intent, and committing it would fold entries the harness chose, not the model (hallucinated ${plan.minting.repairs.hallucinatedMembers}, duplicate ${plan.minting.repairs.duplicateMembers}, nonInteger ${plan.minting.repairs.nonIntegerMembers}; emptied groups ${plan.minting.repairs.emptyGroups}, oversize ${plan.minting.repairs.oversizeGroups.length})`);
230
+ }
231
+ run.planArchive = distillerPlanArchiveName(run.runId, run.attempt);
232
+ run.planCache = { maxInputsPerProduct: plan.maxInputsPerProduct, products: plan.products };
233
+ writeDriverRun(controlDir, run);
234
+ archiveDistillerPlan(controlDir, run.runId, plan, run.attempt);
235
+ }
236
+ staleResidue = (run.planCache?.products ?? []).flatMap((p) => {
237
+ const offered = new Map();
238
+ for (const c of candidates)
239
+ if (typeof c?.entry?.id === "string")
240
+ offered.set(c.entry.id, c.entry.rev);
241
+ const ids = p.inputIds ?? [];
242
+ const live = ids.filter((id) => offered.has(id));
243
+ if (live.length === 0)
244
+ return [];
245
+ const stale = p.inputRevs !== undefined && live.some((id) => p.inputRevs[id] !== undefined && offered.get(id) !== p.inputRevs[id]);
246
+ const vanished = live.length < ids.length;
247
+ const unanchored = p.inputRevs === undefined;
248
+ return stale || vanished || unanchored ? [{ name: p.name, targets: live.length }] : [];
249
+ });
250
+ const replayView = run.planCache !== undefined && run.planCache.products.some((p) => p.inputRevs === undefined)
251
+ ? { ...run.planCache, products: run.planCache.products.filter((p) => p.inputRevs !== undefined) }
252
+ : run.planCache;
253
+ return llmPlanDistiller(replayView)(candidates, snapshot, s);
254
+ };
255
+ let firstSnapshotOfNewRun = !resumed;
256
+ const takeSnapshot = async (_cycle) => {
257
+ writeDriverRun(controlDir, run);
258
+ let force = !firstSnapshotOfNewRun;
259
+ let throttleWaits = 0;
260
+ let busyWaits = 0;
261
+ for (;;) {
262
+ try {
263
+ const snap = await engine.snapshotConsolidationCandidates(scope, { full: true, ...(force ? { force: { requestId: run.requestId } } : {}) });
264
+ firstSnapshotOfNewRun = false;
265
+ return snap;
266
+ }
267
+ catch (err) {
268
+ const code = codeOf(err);
269
+ if (code === "memory.consolidation_gated") {
270
+ if (!force) {
271
+ force = true;
272
+ continue;
273
+ }
274
+ if (throttleWaits >= MAX_THROTTLE_WAITS_PER_SNAPSHOT)
275
+ throw err;
276
+ throttleWaits += 1;
277
+ const lastRunAt = engine.readConsolidationGate(scope)?.lastRunAt;
278
+ const until = (lastRunAt ?? now()) + throttleWindowMs;
279
+ run.nextEligibleAt = until;
280
+ writeDriverRun(controlDir, run);
281
+ const waitMs = Math.max(1, until - now());
282
+ await sleep(waitMs);
283
+ continue;
284
+ }
285
+ if (code === "memory.consolidation_busy") {
286
+ if (busyWaits >= MAX_BUSY_WAITS)
287
+ throw err;
288
+ busyWaits += 1;
289
+ const parked = await recoverSeat(engine, scope, { sleep, windowMs: throttleWindowMs });
290
+ if (parked !== undefined) {
291
+ const e = new Error(parked);
292
+ e[CONSOLIDATION_SNAPSHOT_PARKED] = true;
293
+ throw e;
294
+ }
295
+ continue;
296
+ }
297
+ throw err;
298
+ }
299
+ }
300
+ };
301
+ let drive;
302
+ try {
303
+ drive = await driveConsolidationToFixpoint(engine, scope, {
304
+ distill,
305
+ readFoldState: () => engine.readConsolidationFoldState(scope),
306
+ options: screened,
307
+ fuseCeilingOf: supersessionFuseCeiling,
308
+ requestIdPrefix: run.requestId,
309
+ maxCycles: opts.maxCycles ?? 64,
310
+ takeSnapshot,
311
+ onCycle: (row) => {
312
+ if (row.committed.products > 0 || row.committed.superseded > 0)
313
+ run.cyclesDone += 1;
314
+ run.entriesSuperseded = (run.entriesSuperseded ?? 0) + row.committed.superseded;
315
+ if (row.planId !== undefined)
316
+ run.planIds.push(row.planId);
317
+ run.nextEligibleAt = now() + throttleWindowMs;
318
+ writeDriverRun(controlDir, run);
319
+ },
320
+ });
321
+ }
322
+ catch (err) {
323
+ if (err instanceof DriverMintFailure) {
324
+ announce([`memory consolidation driver run ${run.runId} for scope ${inlineUntrusted(scope, 80)} FAILED before any write: ${inlineUntrusted(err.detail, 300)}`]);
325
+ return settle("driver_failed", undefined, err.detail);
326
+ }
327
+ throw err;
328
+ }
329
+ const writeResidue = run.writeFailures.map((w) => ({ name: w.key, targets: w.members }));
330
+ const driverResidue = [...staleResidue, ...writeResidue];
331
+ if (drive.converged) {
332
+ const plannedMembers = (run.planCache?.products ?? []).reduce((a, p) => a + (p.inputIds?.length ?? 0), 0);
333
+ const rowAccount = run.entriesSuperseded ?? 0;
334
+ const storeAccount = run.foldBaseline !== undefined ? Math.max(0, drive.foldState.after.superseded - run.foldBaseline.superseded) : rowAccount;
335
+ const supersededAttested = Math.max(rowAccount, storeAccount);
336
+ const residueTargets = driverResidue.reduce((a, r) => a + r.targets, 0);
337
+ const accounted = supersededAttested + residueTargets;
338
+ if (plannedMembers > accounted) {
339
+ run.residue = driverResidue;
340
+ return settle("driver_failed", drive, `the drive reached a fixpoint but the account does not balance: ${plannedMembers} planned member(s), ${supersededAttested} superseded attested for this run (row account ${rowAccount}, store delta ${run.foldBaseline !== undefined ? storeAccount : "unavailable — the row predates the store anchor"}), ${residueTargets} in named residue — ${plannedMembers - accounted} member(s) left the candidate set without a fold the store attests (deleted or excluded mid-run${run.foldBaseline !== undefined ? "" : ", or folded in a window whose account write did not survive"}); their entries stand (or revive with their exclusion), and a fresh run re-mints over the current library`);
341
+ }
342
+ if (driverResidue.length > 0) {
343
+ run.residue = driverResidue;
344
+ return settle("driver_failed", drive, `the drive reached the surviving plan's fixpoint, but the driver's own half left ${driverResidue.length} group(s) unfolded (${staleResidue.length} stale against edited inputs, ${writeResidue.length} writing-pass failure(s)) — their members stay as they were; a fresh run re-mints over the current bytes`);
345
+ }
346
+ return settle("converged", drive);
347
+ }
348
+ if (driverResidue.length > 0)
349
+ run.residue = [...(drive.stop?.fuseBlocked ?? []), ...driverResidue];
350
+ const stop = drive.stop;
351
+ if (stop?.fuseBlocked !== undefined)
352
+ return settle("fuse_residue", drive, stop.reason);
353
+ if (stop?.exhausted === true)
354
+ return settle("max_cycles", drive, stop.reason);
355
+ if (stop?.parked === true)
356
+ return settle("parked", drive, stop.reason);
357
+ return settle("refused", drive, stop?.reason ?? "the drive stopped without a stated reason");
358
+ }
359
+ async function recoverSeat(engine, scope, { sleep, windowMs }) {
360
+ await engine.reconcileConsolidation(scope);
361
+ let seat = engine.readConsolidationGate(scope)?.openPlanId;
362
+ if (seat === undefined)
363
+ return undefined;
364
+ const summary = engine.listConsolidationPlans().find((p) => p.planId === seat);
365
+ if (summary?.corrupt === true || summary?.state === "conflict") {
366
+ return `plan ${seat} holds the ${JSON.stringify(scope)} seat in a ${summary.corrupt === true ? "corrupt" : "conflict"} state — resolveConsolidationPlan(planId, "retry" | "discard") is the host valve; the run parks rather than stacking a second plan on the seat`;
367
+ }
368
+ await sleep(windowMs);
369
+ await engine.reconcileConsolidation(scope);
370
+ seat = engine.readConsolidationGate(scope)?.openPlanId;
371
+ if (seat === undefined)
372
+ return undefined;
373
+ const again = engine.listConsolidationPlans().find((p) => p.planId === seat);
374
+ if (again?.corrupt === true || again?.state === "conflict") {
375
+ return `plan ${seat} holds the ${JSON.stringify(scope)} seat in a ${again.corrupt === true ? "corrupt" : "conflict"} state — resolveConsolidationPlan(planId, "retry" | "discard") is the host valve; the run parks rather than stacking a second plan on the seat`;
376
+ }
377
+ return undefined;
378
+ }
@@ -126,7 +126,7 @@ export declare const CONSOLIDATION_FORCE_THROTTLE_FLOOR_MS = 60000;
126
126
  /** The bounded distinct-session ring (§2.3 sessionsSince dedup). */
127
127
  export declare const CONSOLIDATION_SESSION_RING_MAX = 64;
128
128
  /**
129
- * codex r2 — the seat's FREEZE GRACE: reconcile's orphan-release arm treats a seat whose plan file
129
+ * the seat's FREEZE GRACE: reconcile's orphan-release arm treats a seat whose plan file
130
130
  * is absent as crashed ONLY once the claim is at least this old. Calibration: the freeze between
131
131
  * the seat CAS and the plan file's durable write is committed READS only (headers + a bounded
132
132
  * getByIds) — seconds on any healthy store; ten minutes is orders of magnitude past it, while a
@@ -177,13 +177,23 @@ export interface ConsolidationGateRow {
177
177
  /** The plan-seat mutex: the one non-terminal (or conflict-parked) plan of this scope. */
178
178
  openPlanId?: string;
179
179
  /** When the seat was claimed (ms epoch) — the reconcile orphan-release arm's staleness anchor
180
- * (codex r2: a freeze legitimately computes for a while between the seat CAS and the plan
180
+ * (a freeze legitimately computes for a while between the seat CAS and the plan
181
181
  * file's durable write; an absent file alone must never read as a crash). */
182
182
  seatClaimedAt?: number;
183
183
  /** §1.4.5 collapse-fuse baseline (last observed active-set size). */
184
184
  activeSetBaseline?: number;
185
185
  /** Once-per-crossing edge for the recommendation notice. */
186
186
  lastRecommendedAt?: number;
187
+ /** The last force requestId whose FORCED announcement was enqueued (additive; design/376 §1.1-4):
188
+ * a RUN-scoped force carries ONE requestId across every cycle of a multi-cycle run, and the
189
+ * audit announcement is deduplicated on it — one disclosure per run, never one per cycle (a
190
+ * cold start would otherwise flood the next session's injection face with a dozen identical
191
+ * lines). A NEW requestId re-arms the disclosure. The dedup is the ONLY record: this row keeps
192
+ * just the LAST id (no history structure exists — the gate row is mutated in place, the
193
+ * announcement queue is drained destructively, and plan files do not mark forced-ness), so
194
+ * per-force facts beyond the one announcement are not persisted, and a caller that reuses a
195
+ * fixed requestId across runs keeps the disclosure silent for as long as it does. */
196
+ lastForcedRequestId?: string;
187
197
  /** Run watermark: increments on every completed/discarded settlement (the stale-proposal gate). */
188
198
  epoch: number;
189
199
  /** §1.2-1 — the last COMPLETED run's full-eligible-set fingerprint (id → committed rev):
@@ -375,4 +385,38 @@ export declare class ConsolidationRefusedError extends Error {
375
385
  }
376
386
  /** A NotePatch list for one frozen product (the §1.3 add-only arm's one spelling). */
377
387
  export declare function productAddPatch(product: MemoryEntry): NotePatch;
388
+ /**
389
+ * Why a driver run stopped — ONE closed set shared verbatim by the run receipt and the
390
+ * `memory.consolidation_incomplete` notice (two vocabularies for the same fact would let an
391
+ * operator-facing channel and the host-facing receipt drift). Non-`converged` values mark a
392
+ * library in a PARTIAL fold — a transitional state the readings show is worse than unfolded, so
393
+ * it must never pass silently:
394
+ * - `converged` — nothing left to fold (the only completed-run value; includes the
395
+ * empty-plan no-op: a model that proposes nothing commits nothing);
396
+ * - `fuse_residue` — the supersession fuse admits no remaining product; the residue is named
397
+ * and the next run's full snapshot re-distills the shrunken library;
398
+ * - `max_cycles` — the cycle bound elapsed before a fixpoint;
399
+ * - `parked` — a plan settled conflict (or a corrupt plan holds the seat); the host
400
+ * valve `resolveConsolidationPlan` is the only way forward;
401
+ * - `refused` — an engine verb refused and the drive could not continue this run;
402
+ * - `driver_failed` — the DRIVER half could not complete. Two grains, one verdict: a failed
403
+ * MINT (model unreachable/unreadable/over the repair budget) settles with
404
+ * zero writes, zero seat and an unburned time gate; a run whose surviving
405
+ * plan reached its fixpoint while the driver's own half left groups
406
+ * unfolded (writing-pass failures, or products dropped stale against
407
+ * inputs edited mid-run) settles here TOO — committed cycles stand
408
+ * (add-only, no rollback), the residue is named, and a fresh run re-mints
409
+ * over the current bytes. The clean `converged` label never covers a
410
+ * driver-side residue.
411
+ * Extending this set is a consumer-visible closed-set expansion (release-note duty: every
412
+ * downstream that pinned the enumeration becomes a named party).
413
+ *
414
+ * The type DERIVES from the array (never `satisfies` beside it): the array exists precisely for
415
+ * downstream enumeration, and two independent declarations would let a new union arm skip the
416
+ * array with no compile error — the derived form makes an edit to either side a type error at
417
+ * the other.
418
+ */
419
+ export declare const CONSOLIDATION_RUN_STOP_REASONS: readonly ["converged", "fuse_residue", "max_cycles", "parked", "refused", "driver_failed"];
420
+ /** See the doc on {@link CONSOLIDATION_RUN_STOP_REASONS} — the single source this derives from. */
421
+ export type ConsolidationRunStopReason = (typeof CONSOLIDATION_RUN_STOP_REASONS)[number];
378
422
  export {};
@@ -340,3 +340,4 @@ export class ConsolidationRefusedError extends Error {
340
340
  export function productAddPatch(product) {
341
341
  return { op: "add", id: product.id, entry: product, guard: "absent" };
342
342
  }
343
+ export const CONSOLIDATION_RUN_STOP_REASONS = ["converged", "fuse_residue", "max_cycles", "parked", "refused", "driver_failed"];
@@ -4,7 +4,7 @@ import type { HarvestRejection, MemoryBackend, MemoryEntry, NotePatch, PatchRepo
4
4
  export declare const REMOTE_HARVEST_PER_FILE_BYTES: number;
5
5
  /** Missing-file ratio above which the harvest refuses wholesale (engine MASS_DELETION_FUSE_RATIO). */
6
6
  export declare const REMOTE_MASS_DELETION_FUSE_RATIO = 0.5;
7
- /** File-count cap per harvest (host DEFAULT_MAX_MEMORY_FILES parity — codex H10). */
7
+ /** File-count cap per harvest (host DEFAULT_MAX_MEMORY_FILES parity). */
8
8
  export declare const REMOTE_HARVEST_MAX_FILES = 500;
9
9
  /** Max DIRECTORY nesting depth below the memory root (host DEFAULT_MAX_ENTRY_DEPTH parity — the
10
10
  * host's `scanEntryFiles` walks a subdir only while `depth + 1 <= maxDepth`, so it counts directory