@sema-agent/core 5.58.0 → 5.60.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/CHANGELOG.md +88 -0
- package/dist/brain/anthropic.js +15 -5
- package/dist/brain/errors.d.ts +18 -1
- package/dist/brain/errors.js +7 -1
- package/dist/brain/input-too-long.d.ts +57 -0
- package/dist/brain/input-too-long.js +35 -0
- package/dist/brain/route-adjudicator.d.ts +8 -1
- package/dist/brain/route-adjudicator.js +8 -1
- package/dist/brain/stream-engine.js +9 -1
- package/dist/core/auto-compaction.js +2 -2
- package/dist/core/checkpoint-store.d.ts +116 -19
- package/dist/core/checkpoint-store.js +15 -8
- package/dist/core/context-edit.d.ts +243 -41
- package/dist/core/context-edit.js +247 -32
- package/dist/core/governance-codes.d.ts +37 -10
- package/dist/core/governance-codes.js +57 -1
- package/dist/core/locked-config.d.ts +36 -4
- package/dist/core/locked-config.js +34 -1
- package/dist/core/mcp.js +10 -6
- package/dist/core/memory-engine/consolidation-driver.d.ts +6 -2
- package/dist/core/memory-engine/consolidation-driver.js +54 -5
- package/dist/core/memory-engine/consolidation.d.ts +73 -1
- package/dist/core/memory-engine/consolidation.js +21 -1
- package/dist/core/memory-engine/content-origin.d.ts +24 -2
- package/dist/core/memory-engine/content-origin.js +6 -1
- package/dist/core/memory-engine/engine.d.ts +97 -8
- package/dist/core/memory-engine/engine.js +112 -20
- package/dist/core/memory-engine/file-backend.d.ts +13 -1
- package/dist/core/memory-engine/file-backend.js +3 -0
- package/dist/core/memory-engine/index.d.ts +4 -3
- package/dist/core/memory-engine/layout.js +20 -6
- package/dist/core/memory-engine/types.d.ts +17 -0
- package/dist/core/memory.d.ts +10 -0
- package/dist/core/park-selfcheck.js +1 -0
- package/dist/core/permission-rule-consent.js +9 -5
- package/dist/core/permission-rule-model.d.ts +42 -1
- package/dist/core/permission-rule-model.js +12 -0
- package/dist/core/runner/prepare-config-doors.d.ts +22 -1
- package/dist/core/runner/prepare-config-doors.js +36 -0
- package/dist/core/runner/prepare-task.d.ts +28 -1
- package/dist/core/runner/prepare-task.js +109 -11
- package/dist/core/runner/runtask.js +45 -8
- package/dist/core/store-contracts/checkpoint-store-contract.js +32 -0
- package/dist/core/tool-policy.d.ts +74 -0
- package/dist/core/tool-policy.js +80 -1
- package/dist/core/tools.js +1 -1
- package/dist/core/trace.d.ts +36 -0
- package/dist/core/types.d.ts +172 -22
- package/dist/core/types.js +4 -3
- package/dist/core/untrusted-text.d.ts +11 -0
- package/dist/core/untrusted-text.js +1 -0
- package/dist/engine/llm/types.d.ts +21 -2
- package/dist/engine/loop/agent-loop.js +7 -1
- package/dist/engine/loop/types.d.ts +4 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +2 -2
- package/dist/tools/fs/fs-bash.js +1 -2
- package/package.json +1 -1
- package/test/export-surface.snapshot.json +1771 -1
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
*/
|
|
23
23
|
import type { EngineNotice } from "../types.js";
|
|
24
24
|
import { type ConsolidationGateRow, type ConsolidationRunStopReason, type MemoryConsolidationOptions } from "./consolidation.js";
|
|
25
|
-
import { type ConsolidationPlanSummary } from "./engine.js";
|
|
25
|
+
import { type ConsolidationPlanFoldEvidence, type ConsolidationPlanSummary } from "./engine.js";
|
|
26
26
|
import { mintLlmConsolidationPlan, type ConsolidationDriveCycleRow, type ConsolidationDriveEngine, type ConsolidationFoldState, type DistillerChatFn, type LlmConsolidationPlan, type LlmConsolidationPlanProduct, type LlmDistillerContract } from "./distiller.js";
|
|
27
27
|
export declare const CONSOLIDATION_DRIVER_RUNS_FILE = "distiller-runs.json";
|
|
28
28
|
/** §1.6-4 — the mint archive: what was shown to the model and what it answered, verbatim, per run
|
|
@@ -114,10 +114,14 @@ export declare function readConsolidationDriverRun(controlDir: string, scope: st
|
|
|
114
114
|
* run row's `planArchive` (written under the attempt guard) names the one that counts. */
|
|
115
115
|
export declare function distillerPlanArchiveName(runId: string, attempt: number): string;
|
|
116
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.
|
|
117
|
+
/** The engine face this verb consumes — `MemoryEngine` satisfies it structurally.
|
|
118
|
+
* (`readConsolidationPlanFoldEvidence` joined in design/376 slice ③, #436: the conservation
|
|
119
|
+
* wall's terminal witness is member-level, so the face is REQUIRED — an engine without it could
|
|
120
|
+
* only offer the store-delta counts an interleaved fold can numerically offset.) */
|
|
118
121
|
export interface ConsolidationDriverEngine extends ConsolidationDriveEngine {
|
|
119
122
|
readConsolidationGate(scope: string): ConsolidationGateRow | undefined;
|
|
120
123
|
listConsolidationPlans(): ConsolidationPlanSummary[];
|
|
124
|
+
readConsolidationPlanFoldEvidence(planId: string): ConsolidationPlanFoldEvidence;
|
|
121
125
|
readConsolidationFoldState(scope: string): Promise<ConsolidationFoldState>;
|
|
122
126
|
readonly controlPlaneRoot: string;
|
|
123
127
|
}
|
|
@@ -182,6 +182,7 @@ export async function runMemoryConsolidationDriver(engine, scope, opts) {
|
|
|
182
182
|
return settle("parked", undefined, parkedSeat);
|
|
183
183
|
}
|
|
184
184
|
let staleResidue = [];
|
|
185
|
+
let staleResidueMemberIds = new Set();
|
|
185
186
|
const distill = async (candidates, snapshot, s) => {
|
|
186
187
|
if (run.planCache === undefined) {
|
|
187
188
|
let minted;
|
|
@@ -233,11 +234,12 @@ export async function runMemoryConsolidationDriver(engine, scope, opts) {
|
|
|
233
234
|
writeDriverRun(controlDir, run);
|
|
234
235
|
archiveDistillerPlan(controlDir, run.runId, plan, run.attempt);
|
|
235
236
|
}
|
|
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
|
+
staleResidueMemberIds = new Set();
|
|
236
242
|
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
243
|
const ids = p.inputIds ?? [];
|
|
242
244
|
const live = ids.filter((id) => offered.has(id));
|
|
243
245
|
if (live.length === 0)
|
|
@@ -245,7 +247,11 @@ export async function runMemoryConsolidationDriver(engine, scope, opts) {
|
|
|
245
247
|
const stale = p.inputRevs !== undefined && live.some((id) => p.inputRevs[id] !== undefined && offered.get(id) !== p.inputRevs[id]);
|
|
246
248
|
const vanished = live.length < ids.length;
|
|
247
249
|
const unanchored = p.inputRevs === undefined;
|
|
248
|
-
|
|
250
|
+
if (!(stale || vanished || unanchored))
|
|
251
|
+
return [];
|
|
252
|
+
for (const id of live)
|
|
253
|
+
staleResidueMemberIds.add(id);
|
|
254
|
+
return [{ name: p.name, targets: live.length }];
|
|
249
255
|
});
|
|
250
256
|
const replayView = run.planCache !== undefined && run.planCache.products.some((p) => p.inputRevs === undefined)
|
|
251
257
|
? { ...run.planCache, products: run.planCache.products.filter((p) => p.inputRevs !== undefined) }
|
|
@@ -329,6 +335,7 @@ export async function runMemoryConsolidationDriver(engine, scope, opts) {
|
|
|
329
335
|
const writeResidue = run.writeFailures.map((w) => ({ name: w.key, targets: w.members }));
|
|
330
336
|
const driverResidue = [...staleResidue, ...writeResidue];
|
|
331
337
|
if (drive.converged) {
|
|
338
|
+
const plannedIds = [...new Set((run.planCache?.products ?? []).flatMap((p) => p.inputIds ?? []))];
|
|
332
339
|
const plannedMembers = (run.planCache?.products ?? []).reduce((a, p) => a + (p.inputIds?.length ?? 0), 0);
|
|
333
340
|
const rowAccount = run.entriesSuperseded ?? 0;
|
|
334
341
|
const storeAccount = run.foldBaseline !== undefined ? Math.max(0, drive.foldState.after.superseded - run.foldBaseline.superseded) : rowAccount;
|
|
@@ -339,6 +346,24 @@ export async function runMemoryConsolidationDriver(engine, scope, opts) {
|
|
|
339
346
|
run.residue = driverResidue;
|
|
340
347
|
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
348
|
}
|
|
349
|
+
if (plannedIds.length > 0) {
|
|
350
|
+
const evidence = collectRunFoldEvidence(engine, scope, run.requestId);
|
|
351
|
+
if (evidence.skipped.length > 0) {
|
|
352
|
+
const named = evidence.skipped.slice(0, 5).map((s) => `${s.planId} (${s.state})`).join(", ");
|
|
353
|
+
announce([
|
|
354
|
+
`memory consolidation conservation wall for scope ${inlineUntrusted(scope, 80)}: ${evidence.skipped.length} same-scope plan file(s) answered unreadable and were SKIPPED as foreign to run ${run.runId} — they attest nothing for this run and need out-of-band repair: ${named}${evidence.skipped.length > 5 ? ", …" : ""}`,
|
|
355
|
+
]);
|
|
356
|
+
}
|
|
357
|
+
if (evidence.unreadable !== undefined) {
|
|
358
|
+
run.residue = driverResidue;
|
|
359
|
+
return settle("driver_failed", drive, `the drive reached a fixpoint but the member-level fold evidence is incomplete: plan ${evidence.unreadable} — one of THIS run's own cycles — answered ${evidence.unreadableState} (control-plane damage on run-owned evidence, not a transient race); a converged label needs attestable members; the library stands as committed and a fresh run re-mints over the current bytes`);
|
|
360
|
+
}
|
|
361
|
+
const missing = plannedIds.filter((id) => !evidence.targets.has(id) && !staleResidueMemberIds.has(id));
|
|
362
|
+
if (missing.length > 0) {
|
|
363
|
+
run.residue = driverResidue;
|
|
364
|
+
return settle("driver_failed", drive, `the drive reached a fixpoint but ${missing.length} of ${plannedIds.length} planned member(s) left the candidate set without a fold this run's plans attest (deleted, excluded, or superseded by another writer mid-run; e.g. ${missing.slice(0, 5).join(", ")}) — a store-delta count can be numerically offset by an unrelated interleaved fold, so member identity is the final judge; the entries stand (or revive with their exclusion) and a fresh run re-mints over the current library`);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
342
367
|
if (driverResidue.length > 0) {
|
|
343
368
|
run.residue = driverResidue;
|
|
344
369
|
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`);
|
|
@@ -356,6 +381,30 @@ export async function runMemoryConsolidationDriver(engine, scope, opts) {
|
|
|
356
381
|
return settle("parked", drive, stop.reason);
|
|
357
382
|
return settle("refused", drive, stop?.reason ?? "the drive stopped without a stated reason");
|
|
358
383
|
}
|
|
384
|
+
function collectRunFoldEvidence(engine, scope, requestId) {
|
|
385
|
+
const cycleId = new RegExp(`^${requestId.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}-c\\d+$`);
|
|
386
|
+
const targets = new Set();
|
|
387
|
+
const skipped = [];
|
|
388
|
+
for (const summary of engine.listConsolidationPlans()) {
|
|
389
|
+
if (summary.corrupt === true)
|
|
390
|
+
continue;
|
|
391
|
+
if (summary.scope !== scope)
|
|
392
|
+
continue;
|
|
393
|
+
const ev = engine.readConsolidationPlanFoldEvidence(summary.planId);
|
|
394
|
+
if (ev.state !== "ok") {
|
|
395
|
+
if (ev.state === "corrupt" && ev.requestId !== undefined && cycleId.test(ev.requestId)) {
|
|
396
|
+
return { targets, unreadable: summary.planId, unreadableState: ev.state, skipped };
|
|
397
|
+
}
|
|
398
|
+
skipped.push({ planId: summary.planId, state: ev.state });
|
|
399
|
+
continue;
|
|
400
|
+
}
|
|
401
|
+
if (ev.scope !== scope || !cycleId.test(ev.requestId))
|
|
402
|
+
continue;
|
|
403
|
+
for (const t of ev.supersededTargets)
|
|
404
|
+
targets.add(t);
|
|
405
|
+
}
|
|
406
|
+
return { targets, skipped };
|
|
407
|
+
}
|
|
359
408
|
async function recoverSeat(engine, scope, { sleep, windowMs }) {
|
|
360
409
|
await engine.reconcileConsolidation(scope);
|
|
361
410
|
let seat = engine.readConsolidationGate(scope)?.openPlanId;
|
|
@@ -91,6 +91,25 @@ export interface MemoryConsolidationOptions {
|
|
|
91
91
|
maxDirectedPatchesPerPlan?: number;
|
|
92
92
|
minRunIntervalMs?: number;
|
|
93
93
|
minSessionsBetweenRuns?: number;
|
|
94
|
+
/** design/376 C-2② — the scan-refusal fuse's ratio leg, in (0, 1]: scan findings refuse the
|
|
95
|
+
* affected PRODUCT (disclosed beside eligibility drift), and the whole plan refuses only when
|
|
96
|
+
* more than max(1, ⌊ratio × submitted products⌋) of them fail the scan (the floor keeps the
|
|
97
|
+
* per-product tolerance alive on 1-4 product plans — the drive lane's common size) — see
|
|
98
|
+
* {@link scanRefusalCeiling}. Default 0.25. */
|
|
99
|
+
scanRefusalRatioCap?: number;
|
|
100
|
+
/** design/376 C-2② — the fuse's absolute leg (positive integer): the large-batch upper bound on
|
|
101
|
+
* tolerated scan refusals per plan, so the ratio cannot scale the tolerance without limit.
|
|
102
|
+
* Default 8 (the point where the 0.25 ratio hands over at 32 submitted products — the family's
|
|
103
|
+
* "小 N" quantification; below that the ratio leg alone governs). */
|
|
104
|
+
scanRefusalMaxPerPlan?: number;
|
|
105
|
+
/** design/376 C-3 — the EXPLICIT escape hatch for the unconditional instruction-form gate:
|
|
106
|
+
* instruction-form consolidation products (`type: "feedback"` / v2 hint carriers) are refused
|
|
107
|
+
* by default REGARDLESS of the visible set's markers — a clean-set instruction product is the
|
|
108
|
+
* model-reachable privilege route (N ordinary notes in, one instruction entry out). `true` is
|
|
109
|
+
* the only accepted value (a posture declaration, like {@link multiNode}); it opens the
|
|
110
|
+
* CLEAN-set arm only — a marked visible set refuses instruction forms under the laundering
|
|
111
|
+
* hard gate no matter what (privilege and hard gate enter and exit through one door). */
|
|
112
|
+
allowInstructionProducts?: true;
|
|
94
113
|
/** D-13a — the multi-node POSTURE declaration: present ⇒ `lease` must be injected (constructor
|
|
95
114
|
* refusal otherwise). Multi-node without declaring is OUT OF CONTRACT (structurally
|
|
96
115
|
* undetectable — the declaration turns a silent omission into an explicit choice). */
|
|
@@ -113,6 +132,8 @@ export declare const CONSOLIDATION_DEFAULTS: {
|
|
|
113
132
|
readonly maxDirectedPatchesPerPlan: 32;
|
|
114
133
|
readonly minRunIntervalMs: number;
|
|
115
134
|
readonly minSessionsBetweenRuns: 5;
|
|
135
|
+
readonly scanRefusalRatioCap: 0.25;
|
|
136
|
+
readonly scanRefusalMaxPerPlan: 8;
|
|
116
137
|
};
|
|
117
138
|
/**
|
|
118
139
|
* §2.2 — the HARD throttle floor: even a `force` run cannot start a second same-scope run within
|
|
@@ -142,6 +163,9 @@ export interface ScreenedConsolidationOptions {
|
|
|
142
163
|
maxDirectedPatchesPerPlan: number;
|
|
143
164
|
minRunIntervalMs: number;
|
|
144
165
|
minSessionsBetweenRuns: number;
|
|
166
|
+
scanRefusalRatioCap: number;
|
|
167
|
+
scanRefusalMaxPerPlan: number;
|
|
168
|
+
allowInstructionProducts: boolean;
|
|
145
169
|
multiNode: boolean;
|
|
146
170
|
lease?: ConsolidationLeaseSeat;
|
|
147
171
|
}
|
|
@@ -159,6 +183,21 @@ export declare function screenConsolidationOptions(raw: MemoryConsolidationOptio
|
|
|
159
183
|
/** §1.2-2 — the supersession fuse ceiling: min(N-1, max(floor, ⌊ratio×N⌋)); N ≤ 1 ⇒ 0 (one plan
|
|
160
184
|
* may NEVER empty the active set — the N-1 ceiling is unconditional, G21). */
|
|
161
185
|
export declare function supersessionFuseCeiling(activeSetSize: number, opts: Pick<ScreenedConsolidationOptions, "supersedeRatioCap" | "supersedeAbsoluteFloor">): number;
|
|
186
|
+
/**
|
|
187
|
+
* design/376 C-2② — the scan-refusal fuse ceiling: how many scan-refused products ONE plan may
|
|
188
|
+
* carry before the whole plan refuses instead of committing around them.
|
|
189
|
+
* `min(max(1, ⌊ratio × submitted⌋), absolute max)`; the ratio leg governs small plans, the
|
|
190
|
+
* absolute leg is the large-batch upper bound, and the FLOOR of one keeps C-2①'s per-product
|
|
191
|
+
* tolerance alive at the drive lane's common plan sizes — under the default 0.25 ratio a bare
|
|
192
|
+
* floor answers 0 for every ≤3-product plan, so one stray refused product re-killed the whole
|
|
193
|
+
* plan there, exactly the shape C-2① was adopted to remove (an adopted review finding). The
|
|
194
|
+
* floor is NOT a hole for the fully-refused tiny plan: a plan whose every product is refused
|
|
195
|
+
* (and which carries no directed work) still dies at the freeze-emptied C-2③ backstop, never a
|
|
196
|
+
* quiet mostly-no-op — what the floor tolerates is exactly one refused product beside at least
|
|
197
|
+
* one survivor. Submitted count = the proposal's product count (per THIS plan, never a
|
|
198
|
+
* cross-run tally).
|
|
199
|
+
*/
|
|
200
|
+
export declare function scanRefusalCeiling(submittedProducts: number, opts: Pick<ScreenedConsolidationOptions, "scanRefusalRatioCap" | "scanRefusalMaxPerPlan">): number;
|
|
162
201
|
/** §3.1 — the type axis of the eligibility predicate: episodic entries are read-only evidence
|
|
163
202
|
* (never candidates, never supersession/directed targets, A-7); procedural promotion is a v3
|
|
164
203
|
* follow-on ticket. Unknown/absent types are ordinary semantic notes — eligible. */
|
|
@@ -231,6 +270,24 @@ export declare function updateConsolidationGate<T>(controlDir: string, fn: (file
|
|
|
231
270
|
export declare function readConsolidationGateFile(controlDir: string): ConsolidationGateFile;
|
|
232
271
|
/** True ⇔ the gate store file exists at all (G1's zero-file pin reads this negatively). */
|
|
233
272
|
export declare function consolidationGateFileExists(controlDir: string): boolean;
|
|
273
|
+
/**
|
|
274
|
+
* design/376 slice ③ (#437②) — the gate read face's DISCRIMINATED answer
|
|
275
|
+
* ({@link import("./engine.js").MemoryEngine.readConsolidationGateState}): `present` carries the
|
|
276
|
+
* row; `absent` means the store is readable and simply has no row for the scope (a never-run
|
|
277
|
+
* scope, or no gate file at all); `corrupt` means the store cannot be trusted and carries the
|
|
278
|
+
* refusal detail. The undiscriminated `readConsolidationGate` face folds corrupt into `undefined`
|
|
279
|
+
* — an operator (or a driver's scheduling read) could not tell a healthy never-run scope from a
|
|
280
|
+
* store every verb will refuse fail-closed.
|
|
281
|
+
*/
|
|
282
|
+
export type ConsolidationGateRead = {
|
|
283
|
+
state: "present";
|
|
284
|
+
row: ConsolidationGateRow;
|
|
285
|
+
} | {
|
|
286
|
+
state: "absent";
|
|
287
|
+
} | {
|
|
288
|
+
state: "corrupt";
|
|
289
|
+
detail: string;
|
|
290
|
+
};
|
|
234
291
|
/**
|
|
235
292
|
* §2.2 — the engine-minted session count (harvest-tail bookkeeping; ≤1 row write per terminal
|
|
236
293
|
* harvest; the OFF mode never calls this — D-9a zero-write). Returns the recommendation edge:
|
|
@@ -304,7 +361,15 @@ export interface ConsolidationDirectedPatch {
|
|
|
304
361
|
}
|
|
305
362
|
export type ConsolidationPlanState = "open" | "applying" | "completed" | "conflict" | "discarded";
|
|
306
363
|
export interface ConsolidationPlanFile {
|
|
307
|
-
|
|
364
|
+
/** Plan schema version. `2` ⇔ the plan carries {@link freezeRefusedInputIds} — that member is a
|
|
365
|
+
* LOAD-BEARING settle input (C-2④ keeps the named ids out of the completion fingerprint), and
|
|
366
|
+
* a reader that predates it would accept the file as v1, ignore the member, and settle a
|
|
367
|
+
* fingerprint that stamps the refused members (silently blinding the incremental face — the
|
|
368
|
+
* exact hole the field closes). Version-gating the member makes such a reader answer its
|
|
369
|
+
* "unrecognized version" corrupt arm instead: the plan parks loudly for the valve, never
|
|
370
|
+
* settles wrong (mixed-fleet crash window between the durable plan write and gate settlement).
|
|
371
|
+
* Plans without the member stay v1 — zero noise on the common path. */
|
|
372
|
+
v: 1 | 2;
|
|
308
373
|
planId: string;
|
|
309
374
|
scope: string;
|
|
310
375
|
requestId: string;
|
|
@@ -318,6 +383,13 @@ export interface ConsolidationPlanFile {
|
|
|
318
383
|
/** Frozen products — FULL entry bytes (id/frontmatter incl. distilled + origin/body/rev). */
|
|
319
384
|
products: MemoryEntry[];
|
|
320
385
|
productStates: Record<string, "pending" | "applied" | "conflict">;
|
|
386
|
+
/** design/376 C-2④ — the declared input ids of every product the FREEZE refused (scan findings
|
|
387
|
+
* and eligibility drift alike). Settlement keeps these ids OUT of the completion fingerprint:
|
|
388
|
+
* a scan-refused input's rev never moved, so stamping it would make every later incremental
|
|
389
|
+
* snapshot read it as unchanged and never offer it again — the refused member must stay a
|
|
390
|
+
* candidate. Absent on plans frozen before the field existed, and on plans with nothing
|
|
391
|
+
* refused (either way the settle filter simply has nothing extra to exclude). */
|
|
392
|
+
freezeRefusedInputIds?: string[];
|
|
321
393
|
directed: ConsolidationDirectedPatch[];
|
|
322
394
|
intents: Array<{
|
|
323
395
|
requestId: string;
|
|
@@ -40,6 +40,8 @@ export const CONSOLIDATION_DEFAULTS = {
|
|
|
40
40
|
maxDirectedPatchesPerPlan: 32,
|
|
41
41
|
minRunIntervalMs: 24 * 60 * 60 * 1000,
|
|
42
42
|
minSessionsBetweenRuns: 5,
|
|
43
|
+
scanRefusalRatioCap: 0.25,
|
|
44
|
+
scanRefusalMaxPerPlan: 8,
|
|
43
45
|
};
|
|
44
46
|
export const CONSOLIDATION_FORCE_THROTTLE_FLOOR_MS = 60_000;
|
|
45
47
|
export const CONSOLIDATION_SESSION_RING_MAX = 64;
|
|
@@ -70,12 +72,18 @@ export function screenConsolidationOptions(raw, provenance) {
|
|
|
70
72
|
maxDirectedPatchesPerPlan: num("maxDirectedPatchesPerPlan", raw.maxDirectedPatchesPerPlan, (n) => Number.isInteger(n) && n >= 1, "a positive integer"),
|
|
71
73
|
minRunIntervalMs: num("minRunIntervalMs", raw.minRunIntervalMs, (n) => n > 0, "a positive number of milliseconds"),
|
|
72
74
|
minSessionsBetweenRuns: num("minSessionsBetweenRuns", raw.minSessionsBetweenRuns, (n) => Number.isInteger(n) && n >= 0, "a non-negative integer"),
|
|
75
|
+
scanRefusalRatioCap: num("scanRefusalRatioCap", raw.scanRefusalRatioCap, (n) => n > 0 && n <= 1, "a number in (0, 1]"),
|
|
76
|
+
scanRefusalMaxPerPlan: num("scanRefusalMaxPerPlan", raw.scanRefusalMaxPerPlan, (n) => Number.isInteger(n) && n >= 1, "a positive integer"),
|
|
77
|
+
allowInstructionProducts: raw.allowInstructionProducts === true,
|
|
73
78
|
multiNode: raw.multiNode === true,
|
|
74
79
|
...(raw.lease !== undefined ? { lease: raw.lease } : {}),
|
|
75
80
|
};
|
|
76
81
|
if (raw.multiNode !== undefined && raw.multiNode !== true) {
|
|
77
82
|
throw coded(`MemoryEngineOptions.consolidation.multiNode must be exactly true when present (a posture declaration, not a boolean knob).`, "config.memory_consolidation");
|
|
78
83
|
}
|
|
84
|
+
if (raw.allowInstructionProducts !== undefined && raw.allowInstructionProducts !== true) {
|
|
85
|
+
throw coded(`MemoryEngineOptions.consolidation.allowInstructionProducts must be exactly true when present (a posture declaration, not a boolean knob — absent means the instruction-form gate stays closed).`, "config.memory_consolidation");
|
|
86
|
+
}
|
|
79
87
|
if (screened.multiNode && screened.lease === undefined) {
|
|
80
88
|
throw coded(`MemoryEngineOptions.consolidation.multiNode declared without a lease seat — a multi-node deployment MUST inject the host's global lease (the local plan-seat CAS does not reach across nodes). Inject consolidation.lease or remove the multiNode declaration.`, "config.memory_consolidation_lease_required");
|
|
81
89
|
}
|
|
@@ -89,6 +97,11 @@ export function supersessionFuseCeiling(activeSetSize, opts) {
|
|
|
89
97
|
return 0;
|
|
90
98
|
return Math.min(activeSetSize - 1, Math.max(opts.supersedeAbsoluteFloor, Math.floor(opts.supersedeRatioCap * activeSetSize)));
|
|
91
99
|
}
|
|
100
|
+
export function scanRefusalCeiling(submittedProducts, opts) {
|
|
101
|
+
if (submittedProducts <= 0)
|
|
102
|
+
return 0;
|
|
103
|
+
return Math.min(Math.max(1, Math.floor(opts.scanRefusalRatioCap * submittedProducts)), opts.scanRefusalMaxPerPlan);
|
|
104
|
+
}
|
|
92
105
|
export function consolidationTypeEligible(type) {
|
|
93
106
|
return type !== "episodic" && type !== "procedural";
|
|
94
107
|
}
|
|
@@ -186,7 +199,7 @@ function planShapeInvalid(raw, planId) {
|
|
|
186
199
|
if (typeof raw !== "object" || raw === null)
|
|
187
200
|
return "not an object";
|
|
188
201
|
const p = raw;
|
|
189
|
-
if (p["v"] !== 1)
|
|
202
|
+
if (p["v"] !== 1 && p["v"] !== 2)
|
|
190
203
|
return "unrecognized version";
|
|
191
204
|
if (p["planId"] !== planId)
|
|
192
205
|
return "planId mismatch";
|
|
@@ -248,6 +261,13 @@ function planShapeInvalid(raw, planId) {
|
|
|
248
261
|
}
|
|
249
262
|
if (!p["directed"].every((d) => intentIds.has(d.intentRequestId)))
|
|
250
263
|
return "directed patch references an undeclared intent";
|
|
264
|
+
const freezeRefused = p["freezeRefusedInputIds"];
|
|
265
|
+
if (freezeRefused !== undefined && (!Array.isArray(freezeRefused) || !freezeRefused.every((x) => typeof x === "string"))) {
|
|
266
|
+
return "malformed freezeRefusedInputIds";
|
|
267
|
+
}
|
|
268
|
+
if ((p["v"] === 2) !== (freezeRefused !== undefined)) {
|
|
269
|
+
return p["v"] === 2 ? "v2 plan without its freezeRefusedInputIds member" : "freezeRefusedInputIds on a v1 plan";
|
|
270
|
+
}
|
|
251
271
|
if (!Array.isArray(p["audit"]))
|
|
252
272
|
return "malformed audit trail";
|
|
253
273
|
return undefined;
|
|
@@ -22,13 +22,22 @@
|
|
|
22
22
|
* does not, the default is EXTERNAL (fail-closed: unknown = external), overridable only through the
|
|
23
23
|
* explicit `TaskSpec.memory.trustedTools` allowlist. A core-mounted built-in without a declaration
|
|
24
24
|
* defaults local: core owns those definitions, and the fail-closed default exists for tools core
|
|
25
|
-
* CANNOT vouch for, not for its own.
|
|
25
|
+
* CANNOT vouch for, not for its own. A declaration is honored only when it SPELLS a member of the
|
|
26
|
+
* closed vocabulary — an unreadable one is not a declaration.
|
|
27
|
+
*
|
|
28
|
+
* The three tier names are BOUNDARY-RELATIVE, not statements about topology: `"local"` means "does
|
|
29
|
+
* not bring content from outside the deployment's trust boundary", which a unix socket to another
|
|
30
|
+
* process the deployment itself runs satisfies and a loopback HTTP call to somebody else's service
|
|
31
|
+
* does not. Distance is not the axis; whose content it is, is.
|
|
26
32
|
*
|
|
27
33
|
* There is deliberately no "gate inactive" state: every mounted tool gets a class.
|
|
28
34
|
*/
|
|
29
35
|
import type { ToolContentOrigin } from "../types.js";
|
|
30
36
|
export interface ClassifyToolContentOriginInput {
|
|
31
|
-
/** The tool's own `contentOrigin` declaration (ToolSpec/AgentTool carry) — wins when present
|
|
37
|
+
/** The tool's own `contentOrigin` declaration (ToolSpec/AgentTool carry) — wins when present AND
|
|
38
|
+
* a member of the closed vocabulary ({@link isToolContentOrigin}); a present non-member is the
|
|
39
|
+
* unreadable-declaration case and classifies EXTERNAL. The annotation is intent, not a guarantee:
|
|
40
|
+
* the property rides tool objects untyped. */
|
|
32
41
|
declared?: ToolContentOrigin;
|
|
33
42
|
/** True ⇔ the name belongs to a protocol namespace (mcp__/a2a__ …) — an external channel. */
|
|
34
43
|
isProtocolTool: boolean;
|
|
@@ -37,6 +46,10 @@ export interface ClassifyToolContentOriginInput {
|
|
|
37
46
|
/** True ⇔ `TaskSpec.memory.trustedTools` names this tool (explicit host exemption). */
|
|
38
47
|
trusted: boolean;
|
|
39
48
|
}
|
|
49
|
+
/** The vocabulary in wire/message order — the one spelling list a refusal quotes back. */
|
|
50
|
+
export declare const TOOL_CONTENT_ORIGINS: readonly ToolContentOrigin[];
|
|
51
|
+
/** Runtime membership in the closed vocabulary. */
|
|
52
|
+
export declare function isToolContentOrigin(value: unknown): value is ToolContentOrigin;
|
|
40
53
|
export declare function classifyToolContentOrigin(input: ClassifyToolContentOriginInput): ToolContentOrigin;
|
|
41
54
|
/**
|
|
42
55
|
* Declare `origin` on a tool object built OUTSIDE `defineTool` — a raw AgentTool literal, which is
|
|
@@ -60,6 +73,15 @@ export interface AgentToolFace {
|
|
|
60
73
|
export interface AgentPoolTool {
|
|
61
74
|
name: string;
|
|
62
75
|
aliases?: readonly string[];
|
|
76
|
+
/** The pool row's own declaration — a STATIC surface the deployment writes when it composes the
|
|
77
|
+
* delegation tool, and the only class information this judgment has about the child's roster.
|
|
78
|
+
*
|
|
79
|
+
* A per-entry MCP declaration (design/378, `McpServerSpec.contentOrigin`) does NOT reach here: the
|
|
80
|
+
* pool is a declaration, not a live roster, and this judgment never sees the server entries. A
|
|
81
|
+
* deployment that hands a declared server's `mcp__` tools to children MIRRORS the value onto the
|
|
82
|
+
* matching pool rows; not mirroring it leaves them on the protocol arm, so the delegation reads
|
|
83
|
+
* external and the parent over-marks — the safe direction, and the reason this is a documented
|
|
84
|
+
* duty rather than a refusal. */
|
|
63
85
|
contentOrigin?: ToolContentOrigin;
|
|
64
86
|
}
|
|
65
87
|
/**
|
|
@@ -1,6 +1,11 @@
|
|
|
1
|
+
const TOOL_CONTENT_ORIGIN_SET = { external: true, execution: true, local: true };
|
|
2
|
+
export const TOOL_CONTENT_ORIGINS = Object.keys(TOOL_CONTENT_ORIGIN_SET);
|
|
3
|
+
export function isToolContentOrigin(value) {
|
|
4
|
+
return typeof value === "string" && Object.prototype.hasOwnProperty.call(TOOL_CONTENT_ORIGIN_SET, value);
|
|
5
|
+
}
|
|
1
6
|
export function classifyToolContentOrigin(input) {
|
|
2
7
|
if (input.declared !== undefined)
|
|
3
|
-
return input.declared;
|
|
8
|
+
return isToolContentOrigin(input.declared) ? input.declared : "external";
|
|
4
9
|
if (input.trusted)
|
|
5
10
|
return "local";
|
|
6
11
|
if (input.isProtocolTool)
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import type { EngineNotice } from "../types.js";
|
|
2
|
-
import { type ConsolidationGateRow, type ConsolidationPlanFile, type ConsolidationProposal, type ConsolidationRunStopReason, type MemoryConsolidationOptions } from "./consolidation.js";
|
|
2
|
+
import { type ConsolidationGateRead, type ConsolidationGateRow, type ConsolidationPlanFile, type ConsolidationProposal, type ConsolidationRunStopReason, type MemoryConsolidationOptions } from "./consolidation.js";
|
|
3
3
|
import { foreignDanglingSessionAccounts, type EffectiveSettlement, type HoldRow } from "./delegation-settlement.js";
|
|
4
4
|
import { type OriginClearanceRow } from "./origin-clearance.js";
|
|
5
5
|
import { type CommittedBinding, type EraseMemoryEntriesInput, type MemoryErasureAttestation, type TransferEvidence } from "./file-backend.js";
|
|
6
6
|
import { type MemoryExportBundle, type MemoryImportReport } from "./export-bundle.js";
|
|
7
7
|
import { type ChallengeAssignment, type ChallengeEvent, type ControlPlaneRebuildReceipt, type StrictControlPlaneLedger, type ChallengedHistoryRow, type LineagePendingTxn, type LineagePromotion, type MemoryPartitionIncidentSink, type RetrievedAccountRow, type SessionPollutionMarkOutcome, type SessionPollutionRecord } from "./layout.js";
|
|
8
|
-
import type { HarvestReport, MemoryAnnouncement, MemoryBackend, MemoryEntry, MemoryEntryOrigin, MemorySessionHandle, ScanFinding } from "./types.js";
|
|
8
|
+
import type { HarvestReport, MemoryAnnouncement, MemoryBackend, MemoryEntry, MemoryEntryOrigin, MemoryScopeEnumeration, MemorySessionHandle, ScanFinding } from "./types.js";
|
|
9
9
|
/**
|
|
10
10
|
* The CC `# Memory` instruction section — VERBATIM from the live capture
|
|
11
11
|
* (bench/cc-parity-toolsuite/reminders/live-capture-main-session-2026-07-08.md §12), with the
|
|
@@ -514,6 +514,36 @@ export interface ConsolidationPlanSummary {
|
|
|
514
514
|
directed?: number;
|
|
515
515
|
intents?: number;
|
|
516
516
|
}
|
|
517
|
+
/**
|
|
518
|
+
* design/376 slice ③ (#436) — one plan's MEMBER-LEVEL fold evidence
|
|
519
|
+
* ({@link MemoryEngine.readConsolidationPlanFoldEvidence}'s answer): which entry ids this plan's
|
|
520
|
+
* APPLIED products attest as superseded. Discriminated so an unreadable plan can never be told
|
|
521
|
+
* apart from an empty one by accident.
|
|
522
|
+
*/
|
|
523
|
+
export type ConsolidationPlanFoldEvidence = {
|
|
524
|
+
state: "ok";
|
|
525
|
+
scope: string;
|
|
526
|
+
/** The commit call's requestId, frozen on the plan — the durable run-attribution anchor
|
|
527
|
+
* (the driver's per-cycle requestIds share the run's prefix, so a cycle whose receipt was
|
|
528
|
+
* lost to a crash is still attributable to its run through the plan file). */
|
|
529
|
+
requestId: string;
|
|
530
|
+
planState: ConsolidationPlanFile["state"];
|
|
531
|
+
/** Every supersession-edge target of an APPLIED product, deduplicated and sorted. Products
|
|
532
|
+
* still pending/conflicted attest nothing — the store never saw their edges land. */
|
|
533
|
+
supersededTargets: string[];
|
|
534
|
+
} | {
|
|
535
|
+
state: "absent";
|
|
536
|
+
} | {
|
|
537
|
+
state: "corrupt";
|
|
538
|
+
detail: string;
|
|
539
|
+
/** Present when the plan FILE parsed and only its product frontmatter is damaged (the deep
|
|
540
|
+
* per-product walk's corrupt arm): the frozen attribution anchors survive the damage, so a
|
|
541
|
+
* caller can still attribute the damaged plan to a run — the conservation wall uses this
|
|
542
|
+
* to fail closed ONLY on a run-owned damaged plan while a foreign/historical one is
|
|
543
|
+
* skipped with disclosure. Absent when the file itself is unreadable/unparseable. */
|
|
544
|
+
scope?: string;
|
|
545
|
+
requestId?: string;
|
|
546
|
+
};
|
|
517
547
|
/** §2.2 — the recommendation notice, minted off the harvest report's advisory member (the runner's
|
|
518
548
|
* hold-notice consumption point is its natural home). Undefined ⇔ this harvest crossed no edge. */
|
|
519
549
|
export declare function memoryConsolidationRecommendedNotice(report: HarvestReport, sessionId?: string): EngineNotice | undefined;
|
|
@@ -956,10 +986,18 @@ export declare class MemoryEngine {
|
|
|
956
986
|
}>;
|
|
957
987
|
/**
|
|
958
988
|
* §1.2-2/§1.2-3 — verb ②: mechanical acceptance + plan FREEZE + apply-to-terminal. Structural
|
|
959
|
-
* violations (
|
|
960
|
-
*
|
|
961
|
-
* (an input's rev moved / an
|
|
962
|
-
*
|
|
989
|
+
* violations (out-of-snapshot attribution, caps, the instruction hard gate, the supersession
|
|
990
|
+
* fuse, an empty plan) refuse the WHOLE plan — zero partial freeze. Per-product refusals, each
|
|
991
|
+
* disclosed on the receipt (`products.refused`): eligibility drift (an input's rev moved / an
|
|
992
|
+
* input got challenged between snapshot and freeze, §1.2-2 冻结时资格复验) AND scan findings
|
|
993
|
+
* (design/376 C-2① — the explicit revision of 339 §1.2-2's whole-plan clause: one product's
|
|
994
|
+
* refused bytes must not permanently deny service to every other fold in the plan). The scan
|
|
995
|
+
* seat keeps two whole-plan backstops: the scan-refusal fuse (C-2②, {@link scanRefusalCeiling})
|
|
996
|
+
* refuses a plan saturated with scan-refused products, and a plan the freeze emptied entirely
|
|
997
|
+
* refuses rather than settling as a vacuous completed run (C-2③ — completion writes `lastRunAt`
|
|
998
|
+
* and stamps the full eligible set into the fingerprint, blinding every later incremental
|
|
999
|
+
* snapshot). Intent (directed) scan findings stay whole-plan: intents are host-authored, not
|
|
1000
|
+
* driver products.
|
|
963
1001
|
*/
|
|
964
1002
|
commitConsolidationPlan(cycleToken: string, proposal: ConsolidationProposal, opts: {
|
|
965
1003
|
requestId: string;
|
|
@@ -1009,7 +1047,12 @@ export declare class MemoryEngine {
|
|
|
1009
1047
|
* ABANDONED explicitly (audited; never a silent drop — unfulfilled and given-up stay two
|
|
1010
1048
|
* distinguishable account facts, G24) and the seat releases; `retry` = the plan's unfulfilled
|
|
1011
1049
|
* intents re-judge NOW into a NEW plan (fresh freeze, fresh baseRevs — the ONE authorized
|
|
1012
|
-
* re-judgment point) which applies immediately.
|
|
1050
|
+
* re-judgment point) which applies immediately. A retry with NOTHING to re-judge (a
|
|
1051
|
+
* products-only conflict plan, or every unfulfilled intent already credentialed) refuses
|
|
1052
|
+
* instead of settling a vacuous completed run — C-2③'s empty-plan law at this corridor's door
|
|
1053
|
+
* (the completion would burn the time gate and stamp the original cycle's eligible map into
|
|
1054
|
+
* the fingerprint); `discard` is the exit that releases the seat without wearing the
|
|
1055
|
+
* completed-run label. Both audited by requestId.
|
|
1013
1056
|
*/
|
|
1014
1057
|
resolveConsolidationPlan(planId: string, action: "discard" | "retry", opts: {
|
|
1015
1058
|
requestId: string;
|
|
@@ -1025,9 +1068,44 @@ export declare class MemoryEngine {
|
|
|
1025
1068
|
private reconstructIntentsForRetry;
|
|
1026
1069
|
/** §6.2 — host read face: every plan's summary (audit enumeration, zero model surface). */
|
|
1027
1070
|
listConsolidationPlans(): ConsolidationPlanSummary[];
|
|
1071
|
+
/**
|
|
1072
|
+
* design/376 slice ③ (#436) — host read face: one plan's MEMBER-LEVEL fold evidence (which ids
|
|
1073
|
+
* its APPLIED products supersede). This is the conservation wall's terminal witness: the
|
|
1074
|
+
* driver's other two witnesses (per-cycle row delta, since-open store delta) are both
|
|
1075
|
+
* store-COUNT readings, and an unrelated fold interleaved into the run's scope can numerically
|
|
1076
|
+
* offset a planned member that was deleted mid-run — masking the shortfall behind a balanced
|
|
1077
|
+
* count. Member identity cannot be offset: a planned member either appears among some
|
|
1078
|
+
* run-attributed plan's applied supersession targets, sits in named residue, or the run must
|
|
1079
|
+
* not wear the converged label. Committed plan-file read, side-effect-free; the answer carries
|
|
1080
|
+
* the plan's frozen `requestId` so a caller can attribute plans to a run even across the
|
|
1081
|
+
* commit→receipt crash window (the plan file is durable before any receipt is).
|
|
1082
|
+
*
|
|
1083
|
+
* SCOPE of the attestation, disclosed (an outside review probed it; recorded as the designed
|
|
1084
|
+
* limit rather than patched): this is the committed HISTORICAL fact "this plan's edge landed" —
|
|
1085
|
+
* it does not re-derive whether the edge still stands NOW. A target edit or a carrier challenge
|
|
1086
|
+
* later SUSPENDS the edge (`deriveSupersededSet`'s revival law), and a competing writer may
|
|
1087
|
+
* re-fold the revived member before the run's next snapshot; the attestation still exempts it
|
|
1088
|
+
* and the run keeps its converged label. That label stays materially true in every reachable
|
|
1089
|
+
* arm: the member's content is preserved in THIS run's committed carrier either way, the store
|
|
1090
|
+
* is at a fixpoint (re-covered by a standing edge, or explicitly tombstoned), and the next full
|
|
1091
|
+
* mint re-judges the live world. A LIVE-standing re-derivation here would instead false-red the
|
|
1092
|
+
* honestly converged re-covered case, and the ruled evidence form is the per-plan applied edge.
|
|
1093
|
+
*/
|
|
1094
|
+
readConsolidationPlanFoldEvidence(planId: string): ConsolidationPlanFoldEvidence;
|
|
1028
1095
|
/** §6.2 — host read face: one scope's gate row (undefined when the store has no row — reading
|
|
1029
|
-
* never creates the file, so the OFF deployment stays byte-identical, G1).
|
|
1096
|
+
* never creates the file, so the OFF deployment stays byte-identical, G1). A corrupt store
|
|
1097
|
+
* ALSO answers undefined here — this face cannot distinguish the two; a consumer that needs
|
|
1098
|
+
* the difference reads {@link readConsolidationGateState} (#437②). */
|
|
1030
1099
|
readConsolidationGate(scope: string): ConsolidationGateRow | undefined;
|
|
1100
|
+
/**
|
|
1101
|
+
* design/376 slice ③ (#437②) — host read face: one scope's gate row as a DISCRIMINATED answer.
|
|
1102
|
+
* The undiscriminated face above folds a corrupt store into `undefined`, so a healthy never-run
|
|
1103
|
+
* scope and a store every verb will refuse fail-closed were indistinguishable to an operator or
|
|
1104
|
+
* a scheduling read. `present` carries the row; `absent` = readable store, no row (reading never
|
|
1105
|
+
* creates the file — the OFF deployment stays byte-identical, G1); `corrupt` carries the
|
|
1106
|
+
* refusal detail. Additive; the undiscriminated face is unchanged.
|
|
1107
|
+
*/
|
|
1108
|
+
readConsolidationGateState(scope: string): ConsolidationGateRead;
|
|
1031
1109
|
/**
|
|
1032
1110
|
* design/376 片② — host read face: one scope's committed FOLD STATE, in the freeze's own
|
|
1033
1111
|
* accounting (`active` counts entries neither superseded nor excluded — byte-for-byte the
|
|
@@ -1050,6 +1128,17 @@ export declare class MemoryEngine {
|
|
|
1050
1128
|
* (design/376 §1.6-4); path knowledge is not authority — nothing here bypasses a verb.
|
|
1051
1129
|
*/
|
|
1052
1130
|
get controlPlaneRoot(): string;
|
|
1131
|
+
/**
|
|
1132
|
+
* design/376 slice ③ (#437③) — host read face: the store's scope enumeration, capability-probed
|
|
1133
|
+
* on the backend (`listScopes`, the audit-face probe family). A backend WITHOUT the face
|
|
1134
|
+
* answers `supported: false` — never an empty array: "cannot enumerate" and "hosts no scopes"
|
|
1135
|
+
* must stay two different answers, or every audit sweep over a non-enumerating backend would
|
|
1136
|
+
* silently claim a clean store. The File backend derives the answer from the store root's
|
|
1137
|
+
* durable scope registry (store-level fact — never a session roster); the SQL twins gain the
|
|
1138
|
+
* face through the store-contracts extension (separate ticket). A supporting backend's corrupt
|
|
1139
|
+
* registry THROWS fail-closed — the same reason an unsupported one never reads as empty.
|
|
1140
|
+
*/
|
|
1141
|
+
listMemoryScopes(): Promise<MemoryScopeEnumeration>;
|
|
1053
1142
|
/** The committed, side-effect-free audit read: the adoption-restricted committed view when the
|
|
1054
1143
|
* backend offers one (zero-copy File — ledger+shadow, no disk adoption), else the non-adopting
|
|
1055
1144
|
* retrieval view (copy-out File), else the backend itself (Pg/TiDB shapes — naturally committed
|