@sema-agent/core 5.36.0 → 5.38.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 +124 -0
- package/dist/agents/subagent.d.ts +10 -0
- package/dist/agents/subagent.js +6 -0
- package/dist/agents/teacher.js +3 -0
- package/dist/agents/team.d.ts +7 -1
- package/dist/agents/team.js +11 -9
- package/dist/agents/verify.js +3 -0
- package/dist/core/auto-mode-prompt-assets.d.ts +5 -3
- package/dist/core/auto-mode-prompt-assets.js +1 -1
- package/dist/core/checkpoint-store.d.ts +26 -1
- package/dist/core/governance-codes.js +4 -0
- package/dist/core/hooks.d.ts +129 -2
- package/dist/core/hooks.js +20 -3
- package/dist/core/memory-engine/engine.d.ts +142 -0
- package/dist/core/memory-engine/engine.js +264 -2
- package/dist/core/memory-engine/file-backend.d.ts +490 -16
- package/dist/core/memory-engine/file-backend.js +1099 -36
- package/dist/core/memory-engine/index.d.ts +2 -2
- package/dist/core/memory-engine/index.js +1 -1
- package/dist/core/memory-engine/layout.d.ts +42 -2
- package/dist/core/memory-engine/layout.js +76 -12
- package/dist/core/memory-engine/memory-backend-contract.d.ts +13 -0
- package/dist/core/memory-engine/memory-backend-contract.js +89 -0
- package/dist/core/protocol-table.d.ts +4 -4
- package/dist/core/runner/assemble-result.d.ts +5 -0
- package/dist/core/runner/assemble-result.js +1 -1
- package/dist/core/runner/prepare-config-doors.d.ts +17 -0
- package/dist/core/runner/prepare-config-doors.js +33 -2
- package/dist/core/runner/prepare-memory.d.ts +11 -1
- package/dist/core/runner/prepare-memory.js +48 -2
- package/dist/core/runner/prepare-task.d.ts +22 -2
- package/dist/core/runner/prepare-task.js +125 -42
- package/dist/core/runner/runtask.js +50 -11
- package/dist/core/tool-model-gate.d.ts +125 -0
- package/dist/core/tool-model-gate.js +303 -0
- package/dist/core/tool-policy.d.ts +1 -1
- package/dist/core/types.d.ts +284 -1
- package/dist/core/types.js +21 -0
- package/dist/core/untrusted-text.d.ts +1 -1
- package/dist/index.d.ts +5 -4
- package/dist/index.js +3 -2
- package/dist/orchestration/builtin-workflows.d.ts +68 -6
- package/dist/orchestration/builtin-workflows.js +26 -9
- package/dist/orchestration/run-workflow-tool.d.ts +10 -1
- package/dist/orchestration/run-workflow-tool.js +70 -27
- package/dist/orchestration/workflow-script-store.d.ts +8 -3
- package/dist/prompts/coordinator.d.ts +4 -1
- package/dist/prompts/coordinator.js +8 -0
- package/dist/prompts/default.d.ts +14 -4
- package/dist/prompts/default.js +2 -1
- package/dist/scenarios/full-body.d.ts +5 -0
- package/dist/scenarios/full-body.js +8 -4
- package/dist/tools/fs/fs-shared.d.ts +3 -2
- package/dist/tools/fs/fs-shared.js +19 -9
- package/package.json +1 -1
- package/test/export-surface.snapshot.json +24 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
export { MemoryEngine, buildMemoryInstruction, truncateIndex, MEMORY_INSTRUCTION_TEMPLATE, MEMORY_RECALL_DISCIPLINE, MEMORY_PREFERENCE_DISCIPLINE, MEMORY_READONLY_NOTICE, MEMORY_INDEX_MAX_LINES, MEMORY_INDEX_MAX_BYTES, STUB_ARCHIVED_LINE, DEFAULT_MAX_MEMORY_FILES, DEFAULT_HARVEST_DEADLINE_MS, DEFAULT_HARVEST_FILE_BUDGET, MASS_DELETION_FUSE_RATIO, renderAnnouncements, type MemoryEngineOptions, type MemoryInjection, } from "./engine.js";
|
|
1
|
+
export { MemoryEngine, buildMemoryInstruction, truncateIndex, MEMORY_INSTRUCTION_TEMPLATE, MEMORY_RECALL_DISCIPLINE, MEMORY_PREFERENCE_DISCIPLINE, MEMORY_READONLY_NOTICE, MEMORY_INDEX_MAX_LINES, MEMORY_INDEX_MAX_BYTES, STUB_ARCHIVED_LINE, DEFAULT_MAX_MEMORY_FILES, DEFAULT_HARVEST_DEADLINE_MS, DEFAULT_HARVEST_FILE_BUDGET, MASS_DELETION_FUSE_RATIO, renderAnnouncements, type MemoryEngineOptions, type MemoryInjection, type EntryProvenanceAccount, } from "./engine.js";
|
|
2
2
|
export { MEMORY_SEARCH_TOOL_NAME, MEMORY_GET_TOOL_NAME, MEMORY_ENGINE_TOOL_NAMES, type MemorySearchDetails, type MemorySearchHit, type MemoryGetDetails, } from "./tools.js";
|
|
3
3
|
export { scanMemoryWrite, scanMemoryFileName, scanRemediation, MEMORY_FILENAME_SEGMENT_RE } from "./scan.js";
|
|
4
|
-
export { FileMemoryEngineBackend, scanEntryFiles, MEMORY_INDEX_FILENAME, DEFAULT_MAX_ENTRY_DEPTH, type ScannedEntryFile } from "./file-backend.js";
|
|
4
|
+
export { FileMemoryEngineBackend, scanEntryFiles, MEMORY_INDEX_FILENAME, DEFAULT_MAX_ENTRY_DEPTH, type ScannedEntryFile, type TransferEvidence, type CommittedBinding, type CommittedEntrySnapshot, type CommittedScopeSnapshots, type EntryCustodyReport, erasureSelectHash, type EraseMemoryEntriesInput, type ErasureSelect, type ErasedBinding, type MemoryErasureAttestation, } from "./file-backend.js";
|
|
5
5
|
export { ControlPlaneCorruptError, deriveControlPlaneDir, deriveRepoControlPlaneDir, deriveRepoKey, deriveRepoMemoryDir, deriveProjectMemoryDir, deriveProjectControlDir, recordProjectIdHint, lookupProjectIdHint, PROJECT_ID_HINTS_FILE, resolveMemoryEngineRoot, scopeDirFor, scopeDirName, claimRootScope, rootScopeOf, enqueueMemoryAnnouncement, drainMemoryAnnouncements, peekMemoryAnnouncements, bumpScanFuse, scanFuseCount, clearScanFuse, ANNOUNCEMENTS_FILE, MEMORY_ANNOUNCEMENTS_MAX, SCAN_FUSE_FILE, SCAN_FUSE_THRESHOLD, LINEAGE_FILE, CHALLENGES_FILE, CHALLENGED_HISTORY_FILE, CHALLENGE_LEDGER_MAX_EVENTS, rebuildStrictControlPlaneLedger, type ControlPlaneRebuildReceipt, type StrictControlPlaneLedger, type ChallengeAppendResult, type ChallengeAssignment, type ChallengeEvent, type ChallengedHistoryRow, type LineagePendingTxn, type LineagePromotion, } from "./layout.js";
|
|
6
6
|
export { readV2HeaderHints, type V2HeaderHints } from "./header-hints.js";
|
|
7
7
|
export { parseEntryFile, serializeEntryFile, computeEntryRev, entryFromFile, type ParsedEntryFile } from "./frontmatter.js";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { MemoryEngine, buildMemoryInstruction, truncateIndex, MEMORY_INSTRUCTION_TEMPLATE, MEMORY_RECALL_DISCIPLINE, MEMORY_PREFERENCE_DISCIPLINE, MEMORY_READONLY_NOTICE, MEMORY_INDEX_MAX_LINES, MEMORY_INDEX_MAX_BYTES, STUB_ARCHIVED_LINE, DEFAULT_MAX_MEMORY_FILES, DEFAULT_HARVEST_DEADLINE_MS, DEFAULT_HARVEST_FILE_BUDGET, MASS_DELETION_FUSE_RATIO, renderAnnouncements, } from "./engine.js";
|
|
2
2
|
export { MEMORY_SEARCH_TOOL_NAME, MEMORY_GET_TOOL_NAME, MEMORY_ENGINE_TOOL_NAMES, } from "./tools.js";
|
|
3
3
|
export { scanMemoryWrite, scanMemoryFileName, scanRemediation, MEMORY_FILENAME_SEGMENT_RE } from "./scan.js";
|
|
4
|
-
export { FileMemoryEngineBackend, scanEntryFiles, MEMORY_INDEX_FILENAME, DEFAULT_MAX_ENTRY_DEPTH } from "./file-backend.js";
|
|
4
|
+
export { FileMemoryEngineBackend, scanEntryFiles, MEMORY_INDEX_FILENAME, DEFAULT_MAX_ENTRY_DEPTH, erasureSelectHash, } from "./file-backend.js";
|
|
5
5
|
export { ControlPlaneCorruptError, deriveControlPlaneDir, deriveRepoControlPlaneDir, deriveRepoKey, deriveRepoMemoryDir, deriveProjectMemoryDir, deriveProjectControlDir, recordProjectIdHint, lookupProjectIdHint, PROJECT_ID_HINTS_FILE, resolveMemoryEngineRoot, scopeDirFor, scopeDirName, claimRootScope, rootScopeOf, enqueueMemoryAnnouncement, drainMemoryAnnouncements, peekMemoryAnnouncements, bumpScanFuse, scanFuseCount, clearScanFuse, ANNOUNCEMENTS_FILE, MEMORY_ANNOUNCEMENTS_MAX, SCAN_FUSE_FILE, SCAN_FUSE_THRESHOLD, LINEAGE_FILE, CHALLENGES_FILE, CHALLENGED_HISTORY_FILE, CHALLENGE_LEDGER_MAX_EVENTS, rebuildStrictControlPlaneLedger, } from "./layout.js";
|
|
6
6
|
export { readV2HeaderHints } from "./header-hints.js";
|
|
7
7
|
export { parseEntryFile, serializeEntryFile, computeEntryRev, entryFromFile } from "./frontmatter.js";
|
|
@@ -380,15 +380,55 @@ export declare function reconcileLineage(controlDir: string, now: () => number):
|
|
|
380
380
|
* refuse — "the account is not settled, the content does not go on the table". Throws on a
|
|
381
381
|
* corrupt ledger (integrity unknown ≠ no latch). */
|
|
382
382
|
export declare function lineageLatchedIds(controlDir: string): Set<string>;
|
|
383
|
-
/** Every committed contribution of `sessionId` (the pollution-retroaction sweep input
|
|
384
|
-
* a corrupt ledger.
|
|
383
|
+
/** Every committed contribution of `sessionId` (the pollution-retroaction sweep input, and the
|
|
384
|
+
* erasure lane's sessionId-selector resolver). Throws on a corrupt ledger. Lookups are
|
|
385
|
+
* OWN-property (the twin law {@link lineageAccountOfEntry} states): a prototype-member sessionId
|
|
386
|
+
* such as "constructor" indexes truthy on EVERY plain object, so a bare lookup would attribute
|
|
387
|
+
* the WHOLE committed set to it — behind the erasure selector that phantom is a whole-store
|
|
388
|
+
* delete, the exact shape the selector type forbids. Belt over the null-proto parse rebuild:
|
|
389
|
+
* this guard stays honest even against a map minted elsewhere. */
|
|
385
390
|
export declare function lineageContributionsOfSession(controlDir: string, sessionId: string): Array<{
|
|
386
391
|
entryId: string;
|
|
387
392
|
lastRev: string;
|
|
388
393
|
}>;
|
|
394
|
+
/** design/178 v2-a §1.3 — the BY-ENTRY lineage account, from ONE parse of the ledger (the
|
|
395
|
+
* by-session read's transpose; the committed set is keyed by entryId, so the index is O(1) after
|
|
396
|
+
* the load — the load itself still parses and validates the whole account). BOTH halves come from
|
|
397
|
+
* the same read on purpose (adversarial r3): contributors (committed rows; pending rows are NOT
|
|
398
|
+
* contributions — the account is unsettled) and the dirty-latch fact (any pending row naming this
|
|
399
|
+
* id). Deriving them from two separate reads let a promotion land between them and assemble a
|
|
400
|
+
* scene no instant exhibited — empty ancestry AND no latch. Unknown entry ⇒ empty/false. Throws
|
|
401
|
+
* {@link ControlPlaneCorruptError} on a corrupt ledger — an audit answer is never assembled over
|
|
402
|
+
* an account whose integrity is unknown. Lookups are own-property (a prototype-member entryId
|
|
403
|
+
* such as "constructor" must answer the empty account, never a phantom). */
|
|
404
|
+
export declare function lineageAccountOfEntry(controlDir: string, entryId: string): {
|
|
405
|
+
contributors: Array<{
|
|
406
|
+
sessionId: string;
|
|
407
|
+
lastRev: string;
|
|
408
|
+
lastAt: number;
|
|
409
|
+
}>;
|
|
410
|
+
pendingLatched: boolean;
|
|
411
|
+
};
|
|
389
412
|
/** Drop an entry's lineage rows once the entry is DELETED (an applied tombstone) — space hygiene
|
|
390
413
|
* only, taken at the harvest that saw the applied delete (design/180 §10). */
|
|
391
414
|
export declare function clearLineageForEntries(controlDir: string, entryIds: readonly string[]): void;
|
|
415
|
+
/**
|
|
416
|
+
* design/178 v2-b §7.2-3 — capture-and-clear an erased id set's lineage in ONE strict-lock write:
|
|
417
|
+
* - the COMMITTED contributors of each id are captured (the attestation's testimony) and the rows
|
|
418
|
+
* dropped (the organic tombstone's space hygiene, same terminal state);
|
|
419
|
+
* - every PENDING txn's staged rows naming these ids are STRUCK in the same write — a promotion
|
|
420
|
+
* landing after this lock releases iterates rows the strike already removed, so a late harvest
|
|
421
|
+
* can never write a deleted id's lineage row back (the read and the clear being two separate
|
|
422
|
+
* lock spans was exactly the interleave that both missed a landing contributor and then erased
|
|
423
|
+
* the record of having missed it).
|
|
424
|
+
* Struck pending rows contribute their txn's sessionId to the returned set — EXCEPT rows carrying
|
|
425
|
+
* the structural `kind: "latch-only"` seat (an import protocol's synthetic latch is not a
|
|
426
|
+
* contributing session; the row is still struck). Returns id → contributing session ids
|
|
427
|
+
* (committed ∪ struck). NON-durable by design: a crash between the caller's journal close and this
|
|
428
|
+
* call loses the return set — the caller's replay re-runs it idempotently and its attestation
|
|
429
|
+
* carries the chain's pre-captured set (honest downgrade, never a fabricated rebuild).
|
|
430
|
+
*/
|
|
431
|
+
export declare function captureAndClearLineageForEntries(controlDir: string, entryIds: readonly string[]): Map<string, string[]>;
|
|
392
432
|
/** Full-ledger read (tests / host observability). Throws on corruption. */
|
|
393
433
|
export declare function readLineageRecord(controlDir: string): {
|
|
394
434
|
committed: Record<string, Record<string, LineageContribution>>;
|
|
@@ -375,7 +375,8 @@ export function claimRootScope(controlDir, scope) {
|
|
|
375
375
|
}
|
|
376
376
|
export function registerScope(memoryDir, controlDir, scope, opts) {
|
|
377
377
|
return lockedScopesUpdate(controlDir, (rec) => {
|
|
378
|
-
const
|
|
378
|
+
const registered = rec.scopes !== undefined && Object.prototype.hasOwnProperty.call(rec.scopes, scope) ? rec.scopes[scope] : undefined;
|
|
379
|
+
const dirName = rec.rootScope === scope ? "" : (registered ?? scopeDirName(scope));
|
|
379
380
|
for (const [other, otherDir] of Object.entries(rec.scopes ?? {})) {
|
|
380
381
|
if (other === scope || otherDir === "")
|
|
381
382
|
continue;
|
|
@@ -390,7 +391,7 @@ export function registerScope(memoryDir, controlDir, scope, opts) {
|
|
|
390
391
|
}
|
|
391
392
|
}
|
|
392
393
|
}
|
|
393
|
-
if (
|
|
394
|
+
if (registered === undefined) {
|
|
394
395
|
return { next: { ...rec, scopes: { ...rec.scopes, [scope]: dirName } }, result: dirName === "" ? memoryDir : join(memoryDir, dirName) };
|
|
395
396
|
}
|
|
396
397
|
return { result: dirName === "" ? memoryDir : join(memoryDir, dirName) };
|
|
@@ -403,7 +404,7 @@ export function scopeDirFor(memoryDir, controlDir, scope) {
|
|
|
403
404
|
const rec = readScopesRecord(controlDir);
|
|
404
405
|
if (rec.rootScope === scope)
|
|
405
406
|
return memoryDir;
|
|
406
|
-
const registered = rec.scopes
|
|
407
|
+
const registered = rec.scopes !== undefined && Object.prototype.hasOwnProperty.call(rec.scopes, scope) ? rec.scopes[scope] : undefined;
|
|
407
408
|
if (registered !== undefined)
|
|
408
409
|
return registered === "" ? memoryDir : join(memoryDir, registered);
|
|
409
410
|
return join(memoryDir, scopeDirName(scope));
|
|
@@ -769,8 +770,8 @@ export const USAGE_RETRIEVED_FILE = "usage-retrieved.json";
|
|
|
769
770
|
export const USAGE_RETRIEVED_MAX_IDS = 4096;
|
|
770
771
|
function coerceRetrievedAccount(raw) {
|
|
771
772
|
if (!raw || typeof raw !== "object" || Array.isArray(raw))
|
|
772
|
-
return
|
|
773
|
-
const out =
|
|
773
|
+
return Object.create(null);
|
|
774
|
+
const out = Object.create(null);
|
|
774
775
|
for (const [k, v] of Object.entries(raw)) {
|
|
775
776
|
const row = v;
|
|
776
777
|
if (row && typeof row === "object" && typeof row.count === "number" && Number.isFinite(row.count) && row.count > 0 && typeof row.lastAt === "number" && Number.isFinite(row.lastAt)) {
|
|
@@ -903,8 +904,9 @@ function readStrictSidecar(controlDir, fileName, what) {
|
|
|
903
904
|
return readStrictSidecarRaw(file, what);
|
|
904
905
|
}
|
|
905
906
|
function coerceLineage(raw) {
|
|
906
|
-
if (raw === undefined)
|
|
907
|
-
return { version: 1, committed:
|
|
907
|
+
if (raw === undefined) {
|
|
908
|
+
return { version: 1, committed: Object.create(null), pending: Object.create(null) };
|
|
909
|
+
}
|
|
908
910
|
if (!raw || typeof raw !== "object" || Array.isArray(raw))
|
|
909
911
|
throw new ControlPlaneCorruptError("memory lineage ledger has the wrong shape");
|
|
910
912
|
const r = raw;
|
|
@@ -942,7 +944,17 @@ function coerceLineage(raw) {
|
|
|
942
944
|
}
|
|
943
945
|
}
|
|
944
946
|
}
|
|
945
|
-
|
|
947
|
+
const committed = Object.create(null);
|
|
948
|
+
for (const [entryId, sessions] of Object.entries(r.committed)) {
|
|
949
|
+
const s = Object.create(null);
|
|
950
|
+
for (const [sid, c] of Object.entries(sessions))
|
|
951
|
+
s[sid] = c;
|
|
952
|
+
committed[entryId] = s;
|
|
953
|
+
}
|
|
954
|
+
const pending = Object.create(null);
|
|
955
|
+
for (const [txnId, t] of Object.entries(r.pending))
|
|
956
|
+
pending[txnId] = t;
|
|
957
|
+
return { version: 1, committed, pending };
|
|
946
958
|
}
|
|
947
959
|
export function stageLineagePending(controlDir, txnId, sessionId, rows, now) {
|
|
948
960
|
lockedStrictUpdate(controlDir, LINEAGE_FILE, "memory lineage ledger", coerceLineage, (rec) => {
|
|
@@ -981,7 +993,7 @@ export function promoteLineagePending(controlDir, txnId, now) {
|
|
|
981
993
|
for (const row of txn.rows) {
|
|
982
994
|
if (!applied.has(row.entryId))
|
|
983
995
|
continue;
|
|
984
|
-
const sessions = (rec.committed[row.entryId] ??=
|
|
996
|
+
const sessions = (rec.committed[row.entryId] ??= Object.create(null));
|
|
985
997
|
sessions[txn.sessionId] = { lastRev: row.rev, lastAt: at };
|
|
986
998
|
promoted.push({ entryId: row.entryId, sessionId: txn.sessionId, rev: row.rev });
|
|
987
999
|
}
|
|
@@ -1004,7 +1016,7 @@ export function adjudicateLineagePending(controlDir, txnId, action, now) {
|
|
|
1004
1016
|
for (const row of txn.rows) {
|
|
1005
1017
|
if (applied !== undefined && !applied.has(row.entryId))
|
|
1006
1018
|
continue;
|
|
1007
|
-
const sessions = (rec.committed[row.entryId] ??=
|
|
1019
|
+
const sessions = (rec.committed[row.entryId] ??= Object.create(null));
|
|
1008
1020
|
sessions[txn.sessionId] = { lastRev: row.rev, lastAt: at };
|
|
1009
1021
|
promoted.push({ entryId: row.entryId, sessionId: txn.sessionId, rev: row.rev });
|
|
1010
1022
|
}
|
|
@@ -1028,7 +1040,7 @@ export function reconcileLineage(controlDir, now) {
|
|
|
1028
1040
|
if (!applied.has(row.entryId))
|
|
1029
1041
|
continue;
|
|
1030
1042
|
at ??= now();
|
|
1031
|
-
const sessions = (rec.committed[row.entryId] ??=
|
|
1043
|
+
const sessions = (rec.committed[row.entryId] ??= Object.create(null));
|
|
1032
1044
|
sessions[txn.sessionId] = { lastRev: row.rev, lastAt: at };
|
|
1033
1045
|
promoted.push({ entryId: row.entryId, sessionId: txn.sessionId, rev: row.rev });
|
|
1034
1046
|
}
|
|
@@ -1050,12 +1062,25 @@ export function lineageContributionsOfSession(controlDir, sessionId) {
|
|
|
1050
1062
|
const rec = coerceLineage(readStrictSidecar(controlDir, LINEAGE_FILE, "memory lineage ledger"));
|
|
1051
1063
|
const out = [];
|
|
1052
1064
|
for (const [entryId, sessions] of Object.entries(rec.committed)) {
|
|
1053
|
-
const c = sessions[sessionId];
|
|
1065
|
+
const c = Object.prototype.hasOwnProperty.call(sessions, sessionId) ? sessions[sessionId] : undefined;
|
|
1054
1066
|
if (c !== undefined)
|
|
1055
1067
|
out.push({ entryId, lastRev: c.lastRev });
|
|
1056
1068
|
}
|
|
1057
1069
|
return out;
|
|
1058
1070
|
}
|
|
1071
|
+
export function lineageAccountOfEntry(controlDir, entryId) {
|
|
1072
|
+
const rec = coerceLineage(readStrictSidecar(controlDir, LINEAGE_FILE, "memory lineage ledger"));
|
|
1073
|
+
const sessions = Object.prototype.hasOwnProperty.call(rec.committed, entryId) ? rec.committed[entryId] : undefined;
|
|
1074
|
+
const contributors = sessions === undefined ? [] : Object.entries(sessions).map(([sessionId, c]) => ({ sessionId, lastRev: c.lastRev, lastAt: c.lastAt }));
|
|
1075
|
+
let pendingLatched = false;
|
|
1076
|
+
for (const txn of Object.values(rec.pending)) {
|
|
1077
|
+
if (txn.rows.some((r) => r.entryId === entryId)) {
|
|
1078
|
+
pendingLatched = true;
|
|
1079
|
+
break;
|
|
1080
|
+
}
|
|
1081
|
+
}
|
|
1082
|
+
return { contributors, pendingLatched };
|
|
1083
|
+
}
|
|
1059
1084
|
export function clearLineageForEntries(controlDir, entryIds) {
|
|
1060
1085
|
if (entryIds.length === 0)
|
|
1061
1086
|
return;
|
|
@@ -1070,6 +1095,45 @@ export function clearLineageForEntries(controlDir, entryIds) {
|
|
|
1070
1095
|
return { ...(changed ? { next: rec } : {}), result: undefined };
|
|
1071
1096
|
});
|
|
1072
1097
|
}
|
|
1098
|
+
export function captureAndClearLineageForEntries(controlDir, entryIds) {
|
|
1099
|
+
if (entryIds.length === 0)
|
|
1100
|
+
return new Map();
|
|
1101
|
+
return lockedStrictUpdate(controlDir, LINEAGE_FILE, "memory lineage ledger", coerceLineage, (rec) => {
|
|
1102
|
+
const want = new Set(entryIds);
|
|
1103
|
+
const captured = new Map();
|
|
1104
|
+
const add = (id, sessionId) => {
|
|
1105
|
+
const set = captured.get(id) ?? new Set();
|
|
1106
|
+
set.add(sessionId);
|
|
1107
|
+
captured.set(id, set);
|
|
1108
|
+
};
|
|
1109
|
+
let changed = false;
|
|
1110
|
+
for (const id of entryIds) {
|
|
1111
|
+
if (!Object.prototype.hasOwnProperty.call(rec.committed, id))
|
|
1112
|
+
continue;
|
|
1113
|
+
for (const sessionId of Object.keys(rec.committed[id] ?? {}))
|
|
1114
|
+
add(id, sessionId);
|
|
1115
|
+
delete rec.committed[id];
|
|
1116
|
+
changed = true;
|
|
1117
|
+
}
|
|
1118
|
+
for (const txn of Object.values(rec.pending)) {
|
|
1119
|
+
const keep = [];
|
|
1120
|
+
for (const row of txn.rows) {
|
|
1121
|
+
if (!want.has(row.entryId)) {
|
|
1122
|
+
keep.push(row);
|
|
1123
|
+
continue;
|
|
1124
|
+
}
|
|
1125
|
+
changed = true;
|
|
1126
|
+
if (row.kind !== "latch-only")
|
|
1127
|
+
add(row.entryId, txn.sessionId);
|
|
1128
|
+
}
|
|
1129
|
+
txn.rows = keep;
|
|
1130
|
+
}
|
|
1131
|
+
const result = new Map();
|
|
1132
|
+
for (const [id, sessions] of captured)
|
|
1133
|
+
result.set(id, [...sessions].sort());
|
|
1134
|
+
return { ...(changed ? { next: rec } : {}), result };
|
|
1135
|
+
});
|
|
1136
|
+
}
|
|
1073
1137
|
export function readLineageRecord(controlDir) {
|
|
1074
1138
|
const rec = coerceLineage(readStrictSidecar(controlDir, LINEAGE_FILE, "memory lineage ledger"));
|
|
1075
1139
|
return { committed: rec.committed, pending: rec.pending };
|
|
@@ -12,6 +12,19 @@ export interface MemoryBackendContractHooks {
|
|
|
12
12
|
skipCrossInstanceCas?: string;
|
|
13
13
|
/** Wrap each case (vitest `it`, sequential SQL chaining, …). Default: run sequentially. */
|
|
14
14
|
runAssertion?: (name: string, fn: () => Promise<void>) => void | Promise<void>;
|
|
15
|
+
/**
|
|
16
|
+
* design/178 v2-a §1.3 — the OPTIONAL audit-face clauses' report seat (#196
|
|
17
|
+
* `onOptionalMember` posture: absence is REPORTED, never a silent green — a run without this
|
|
18
|
+
* hook still passes on an absent face, but a certifying harness that wires it can tell
|
|
19
|
+
* "verified" from "the store never offered the face"). The three faces are capability probes on
|
|
20
|
+
* the concrete class (`committedSnapshotOf` / `committedSnapshotsOfScopes` / `custodyOf`); the
|
|
21
|
+
* `MemoryBackend` interface itself is untouched, so absence is legal — the engine's provenance
|
|
22
|
+
* assembly answers it as capability-absent.
|
|
23
|
+
*/
|
|
24
|
+
onOptionalMember?: (report: {
|
|
25
|
+
member: "committedSnapshotOf" | "committedSnapshotsOfScopes" | "custodyOf" | "eraseWithEvidence";
|
|
26
|
+
status: "verified" | "absent";
|
|
27
|
+
}) => void;
|
|
15
28
|
}
|
|
16
29
|
/** Run the full conformance suite. Throws (assert) on the first violated contract clause. */
|
|
17
30
|
export declare function memoryBackendContract(hooks: MemoryBackendContractHooks): Promise<void>;
|
|
@@ -295,6 +295,95 @@ export async function memoryBackendContract(hooks) {
|
|
|
295
295
|
assert.strictEqual(await b.getConsolidationCursor("s1"), "c-2");
|
|
296
296
|
assert.strictEqual(await b.getConsolidationCursor("s2"), "c-other");
|
|
297
297
|
});
|
|
298
|
+
defer("design/178 v2-a optional audit faces: committed snapshot / scope enumeration / custody (absent ⇒ reported)", async () => {
|
|
299
|
+
const b = await hooks.make();
|
|
300
|
+
const seats = [
|
|
301
|
+
["committedSnapshotOf", b.committedSnapshotOf],
|
|
302
|
+
["committedSnapshotsOfScopes", b.committedSnapshotsOfScopes],
|
|
303
|
+
["custodyOf", b.custodyOf],
|
|
304
|
+
];
|
|
305
|
+
for (const [member, seat] of seats) {
|
|
306
|
+
assert.ok(seat === undefined || typeof seat === "function", `${member} must be undefined (absent) or a function — a non-function occupant is a defect, not absence`);
|
|
307
|
+
}
|
|
308
|
+
const snapshotOf = typeof seats[0]?.[1] === "function" ? seats[0][1] : undefined;
|
|
309
|
+
const snapshotsOfScopes = typeof seats[1]?.[1] === "function" ? seats[1][1] : undefined;
|
|
310
|
+
const custodyOf = typeof seats[2]?.[1] === "function" ? seats[2][1] : undefined;
|
|
311
|
+
const e = entry("id-audit-0001", "s1", "audited", "audited body", { name: "Audited" });
|
|
312
|
+
await b.applyPatches([{ op: "add", id: e.id, entry: e }]);
|
|
313
|
+
if (typeof snapshotOf !== "function") {
|
|
314
|
+
hooks.onOptionalMember?.({ member: "committedSnapshotOf", status: "absent" });
|
|
315
|
+
}
|
|
316
|
+
else {
|
|
317
|
+
const ghost = await snapshotOf.call(b, "id-ghost-9999");
|
|
318
|
+
assert.strictEqual(ghost.state, "absent", "an unknown id answers state 'absent' — never a throw, never a guess");
|
|
319
|
+
const snap = await snapshotOf.call(b, e.id);
|
|
320
|
+
assert.strictEqual(snap.state, "row");
|
|
321
|
+
if (snap.state === "row") {
|
|
322
|
+
assert.strictEqual(snap.rev, e.rev, "the snapshot's rev is the committed rev");
|
|
323
|
+
assert.strictEqual(snap.binding.state, "bound", "a committed add answers a bound row");
|
|
324
|
+
if (snap.binding.state === "bound") {
|
|
325
|
+
assert.strictEqual(snap.binding.scope, "s1");
|
|
326
|
+
assert.strictEqual(snap.binding.slug, "audited");
|
|
327
|
+
}
|
|
328
|
+
assert.strictEqual(snap.content.state, "present", "a committed, in-place entry's content is servable");
|
|
329
|
+
if (snap.content.state === "present")
|
|
330
|
+
assert.strictEqual(snap.content.entry.rev, e.rev);
|
|
331
|
+
}
|
|
332
|
+
hooks.onOptionalMember?.({ member: "committedSnapshotOf", status: "verified" });
|
|
333
|
+
}
|
|
334
|
+
if (typeof snapshotsOfScopes !== "function") {
|
|
335
|
+
hooks.onOptionalMember?.({ member: "committedSnapshotsOfScopes", status: "absent" });
|
|
336
|
+
}
|
|
337
|
+
else {
|
|
338
|
+
const enumerated = await snapshotsOfScopes.call(b, ["s1"]);
|
|
339
|
+
if (!enumerated.complete)
|
|
340
|
+
assert.fail("a quiet store enumerates complete");
|
|
341
|
+
assert.deepStrictEqual(enumerated.rows.map((r) => r.id), [e.id], "the account-driven enumeration answers the committed row");
|
|
342
|
+
const foreign = await snapshotsOfScopes.call(b, ["s-elsewhere"]);
|
|
343
|
+
if (!foreign.complete)
|
|
344
|
+
assert.fail("a quiet store enumerates complete (foreign scope)");
|
|
345
|
+
assert.deepStrictEqual(foreign.rows, [], "a foreign scope enumerates empty, not the whole store");
|
|
346
|
+
hooks.onOptionalMember?.({ member: "committedSnapshotsOfScopes", status: "verified" });
|
|
347
|
+
}
|
|
348
|
+
if (typeof custodyOf !== "function") {
|
|
349
|
+
hooks.onOptionalMember?.({ member: "custodyOf", status: "absent" });
|
|
350
|
+
}
|
|
351
|
+
else {
|
|
352
|
+
const custody = await custodyOf.call(b, e.id);
|
|
353
|
+
assert.ok(custody.state === "complete" || custody.state === "damaged", "custody state is the two-valued store answer");
|
|
354
|
+
assert.ok(Array.isArray(custody.events));
|
|
355
|
+
for (const ev of custody.events) {
|
|
356
|
+
assert.strictEqual(typeof ev.ev, "string");
|
|
357
|
+
assert.strictEqual(typeof ev.channel, "string");
|
|
358
|
+
assert.strictEqual(typeof ev.at, "number");
|
|
359
|
+
}
|
|
360
|
+
hooks.onOptionalMember?.({ member: "custodyOf", status: "verified" });
|
|
361
|
+
}
|
|
362
|
+
});
|
|
363
|
+
defer("design/178 v2-b optional erasure face: evidenced delete / replay pinning / selector-swap refusal (absent ⇒ reported)", async () => {
|
|
364
|
+
const b = await hooks.make();
|
|
365
|
+
const seat = b.eraseWithEvidence;
|
|
366
|
+
assert.ok(seat === undefined || typeof seat === "function", "eraseWithEvidence must be undefined (absent) or a function — a non-function occupant is a defect, not absence");
|
|
367
|
+
if (typeof seat !== "function") {
|
|
368
|
+
hooks.onOptionalMember?.({ member: "eraseWithEvidence", status: "absent" });
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
const erase = seat;
|
|
372
|
+
const e = entry("id-erase-0001", "s1", "erasable", "erasable body", { name: "Erasable" });
|
|
373
|
+
await b.applyPatches([{ op: "add", id: e.id, entry: e }]);
|
|
374
|
+
const att = await erase.call(b, { requestId: "req-contract-1", select: { ids: [e.id, "id-ghost-8888"] } });
|
|
375
|
+
assert.strictEqual(att.v, 1, "the attestation rides the v:1 envelope");
|
|
376
|
+
assert.strictEqual(att.evidenceCapability, "journal", "a face-claiming backend answers the evidenced capability");
|
|
377
|
+
assert.deepStrictEqual(att.erased.map((r) => r.id), [e.id], "the existing id is erased");
|
|
378
|
+
assert.ok(typeof att.erased[0]?.evidenceEv === "string" && att.erased[0].evidenceEv.length > 0, "an evidenced erase names its evidence row");
|
|
379
|
+
assert.deepStrictEqual(att.notFound.map((r) => r.id), ["id-ghost-8888"], "the unknown id answers notFound — never a throw, never a silent drop");
|
|
380
|
+
assert.deepStrictEqual(await b.getByIds([e.id]), [], "an erased id is gone from the read faces");
|
|
381
|
+
const replay = await erase.call(b, { requestId: "req-contract-1", select: { ids: [e.id, "id-ghost-8888"] } });
|
|
382
|
+
assert.deepStrictEqual(replay.resolvedIds, att.resolvedIds, "a replay answers the FIRST resolution's pinned set");
|
|
383
|
+
assert.deepStrictEqual((replay.erasedPreviously ?? []).map((r) => r.id), [e.id], "a replay claims the prior erase as erasedPreviously, never a second erase");
|
|
384
|
+
await assert.rejects(async () => erase.call(b, { requestId: "req-contract-1", select: { ids: ["id-other-7777"] } }), (err) => err.code === "memory.erasure_selector_mismatch", "a reused requestId with a different selector refuses loudly");
|
|
385
|
+
hooks.onOptionalMember?.({ member: "eraseWithEvidence", status: "verified" });
|
|
386
|
+
});
|
|
298
387
|
if (!hooks.makeSibling) {
|
|
299
388
|
const reason = hooks.skipCrossInstanceCas?.trim();
|
|
300
389
|
if (reason) {
|
|
@@ -66,10 +66,10 @@ export declare const MCP_NAMESPACE: ProtocolNamespace<typeof MCP_PREFIX_NAME>;
|
|
|
66
66
|
/**
|
|
67
67
|
* The A2A tool namespace (roadmap 2026-08-03, appended for the A2A client/server work).
|
|
68
68
|
*
|
|
69
|
-
* NAME LAYER ONLY: this entry decides how an A2A tool name is SPELLED and how it decomposes.
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
69
|
+
* NAME LAYER ONLY: this entry decides how an A2A tool name is SPELLED and how it decomposes. The
|
|
70
|
+
* client leg (`a2a.ts`, `materializeA2aTools`) mints live names in this namespace since v5.3.0; the
|
|
71
|
+
* table entry predates it so that every table-driven site (`protocolOf`, the grouped display key, the
|
|
72
|
+
* `ToolOrigin` union) needed no change when the leg landed — and needs none for the server-side leg.
|
|
73
73
|
*/
|
|
74
74
|
export declare const A2A_NAMESPACE: ProtocolNamespace<typeof A2A_PREFIX_NAME>;
|
|
75
75
|
/** Every protocol namespace the engine knows. APPEND-ONLY: adding a protocol must not change how any
|
|
@@ -128,6 +128,11 @@ export interface ResultFlags {
|
|
|
128
128
|
* spawner folds it stricter-wins into follow-on legs; every other consumer may ignore it. */
|
|
129
129
|
effectiveReadFace?: TaskResult["effectiveReadFace"];
|
|
130
130
|
effectiveReadDenyPatterns?: TaskResult["effectiveReadDenyPatterns"];
|
|
131
|
+
/** design/178 v2 §2.3 (件①) — the memory-visibility observation, echoed on
|
|
132
|
+
* `TaskResult.effectiveMemoryScopes`. Pure pass-through on every terminal, same law as the
|
|
133
|
+
* read-posture seats above: present iff prepare completed (the memory-less states are their own
|
|
134
|
+
* values — absence means only "prepare never ran to completion"). */
|
|
135
|
+
effectiveMemoryScopes?: TaskResult["effectiveMemoryScopes"];
|
|
131
136
|
/** ruled 2026-08-04 — the usage-governance wait hint carried by the platform terminal the run adopted
|
|
132
137
|
* (`undefined` for every other cause: an expiring environment has no return time to give, and a store
|
|
133
138
|
* failure is not a window). Passed as data rather than read back off `threw` so the seat has a typed
|
|
@@ -162,5 +162,5 @@ export function assembleResult(spec, sessionId, final, stats, flags) {
|
|
|
162
162
|
void _internalCompaction;
|
|
163
163
|
if (flags.unpricedSpend)
|
|
164
164
|
delete publicStats.costMicroUsd;
|
|
165
|
-
return { taskId, sessionId, status, ...(flags.model !== undefined ? { model: flags.model } : {}), result: result.trim(), salvagedOutput, blockedReason, errorMessage, errorCode, ...(retryAfterMs !== undefined ? { retryAfterMs } : {}), checkpointToken, checkpointGate, ...(workspaceRestoreMode !== undefined ? { workspaceRestoreMode } : {}), ...(flags.rewindNotes !== undefined && flags.rewindNotes.length > 0 ? { rewindNotes: flags.rewindNotes } : {}), ...(flags.remoteEnvFailures !== undefined && flags.remoteEnvFailures.length > 0 ? { remoteEnvFailures: flags.remoteEnvFailures } : {}), ...(flags.strandedHumanAnswers !== undefined && flags.strandedHumanAnswers.length > 0 ? { strandedHumanAnswers: flags.strandedHumanAnswers } : {}), ...(flags.effectiveReadFace !== undefined ? { effectiveReadFace: flags.effectiveReadFace } : {}), ...(flags.effectiveReadDenyPatterns !== undefined && flags.effectiveReadDenyPatterns.length > 0 ? { effectiveReadDenyPatterns: flags.effectiveReadDenyPatterns } : {}), stats: publicStats };
|
|
165
|
+
return { taskId, sessionId, status, ...(flags.model !== undefined ? { model: flags.model } : {}), result: result.trim(), salvagedOutput, blockedReason, errorMessage, errorCode, ...(retryAfterMs !== undefined ? { retryAfterMs } : {}), checkpointToken, checkpointGate, ...(workspaceRestoreMode !== undefined ? { workspaceRestoreMode } : {}), ...(flags.rewindNotes !== undefined && flags.rewindNotes.length > 0 ? { rewindNotes: flags.rewindNotes } : {}), ...(flags.remoteEnvFailures !== undefined && flags.remoteEnvFailures.length > 0 ? { remoteEnvFailures: flags.remoteEnvFailures } : {}), ...(flags.strandedHumanAnswers !== undefined && flags.strandedHumanAnswers.length > 0 ? { strandedHumanAnswers: flags.strandedHumanAnswers } : {}), ...(flags.effectiveReadFace !== undefined ? { effectiveReadFace: flags.effectiveReadFace } : {}), ...(flags.effectiveReadDenyPatterns !== undefined && flags.effectiveReadDenyPatterns.length > 0 ? { effectiveReadDenyPatterns: flags.effectiveReadDenyPatterns } : {}), ...(flags.effectiveMemoryScopes !== undefined ? { effectiveMemoryScopes: flags.effectiveMemoryScopes } : {}), stats: publicStats };
|
|
166
166
|
}
|
|
@@ -107,6 +107,10 @@ export interface PrepareConfigDoorsResult {
|
|
|
107
107
|
exclude: readonly string[] | undefined;
|
|
108
108
|
defer: readonly string[] | undefined;
|
|
109
109
|
alwaysLoad: readonly string[] | undefined;
|
|
110
|
+
/** design/277 — the model-gate restore selector ({@link TaskSpec.restoreGatedTools}), fourth
|
|
111
|
+
* seat of the same frozen task-start snapshot: the gate decision and the delegation carrier
|
|
112
|
+
* read THIS, never the live spec. */
|
|
113
|
+
restoreGated: readonly string[] | true | undefined;
|
|
110
114
|
};
|
|
111
115
|
/** owned — the profile half of the RB-50 single decision point (model-independent by contract). */
|
|
112
116
|
promptProfile: "simple" | "classic";
|
|
@@ -127,6 +131,19 @@ export interface PrepareConfigDoorsResult {
|
|
|
127
131
|
compModel: Model | undefined;
|
|
128
132
|
/** owned — the mitigations half of the RB-50 decision point (model-family fact). */
|
|
129
133
|
fableMitigations: boolean;
|
|
134
|
+
/** owned, frozen — design/277 model-gate DISCLOSURE data (the application itself already
|
|
135
|
+
* happened on {@link spec}: its `tools` is the survivor rebind when anything was removed).
|
|
136
|
+
* prepareTask emits the notices from this after the doors return (the onNotice station);
|
|
137
|
+
* the doors stay side-effect-free on the announcement axis. */
|
|
138
|
+
modelGate: {
|
|
139
|
+
/** gate-class → removed wire names (sorted, unique); empty map = nothing removed. */
|
|
140
|
+
removedByClass: ReadonlyMap<string, readonly string[]>;
|
|
141
|
+
/** stamped classes with no merged-table row (inert tags, fail-open — announce material). */
|
|
142
|
+
unknownClasses: readonly string[];
|
|
143
|
+
/** a not-in-force `SEMA_TOOL_MODEL_GATE` value outside the closed set (discard-announce
|
|
144
|
+
* material; the in-force arm never lands here — it throws at the door). */
|
|
145
|
+
discardedEnvRaw: string | undefined;
|
|
146
|
+
};
|
|
130
147
|
/** owned — validated deployment governance windows (undefined = ungoverned). */
|
|
131
148
|
usageWindows: readonly UsageWindow[] | undefined;
|
|
132
149
|
/** owned, out-param cell — created EMPTY here; the brain-call wiring later installs into
|
|
@@ -4,6 +4,7 @@ import { resolveCheckpointStore } from "../checkpoint-store.js";
|
|
|
4
4
|
import { preflightLockedConfig } from "../locked-config.js";
|
|
5
5
|
import { assertRetentionCapability } from "../retention.js";
|
|
6
6
|
import { resolveModel, resolveTaskModel, roleModelIfSet } from "../roles.js";
|
|
7
|
+
import { applyToolModelGate, assertRestoreGatedToolsValue, modelIdTail } from "../tool-model-gate.js";
|
|
7
8
|
import { resolveUsageWindows } from "../usage-window-store.js";
|
|
8
9
|
import { deriveAskEffective, resolveAskSeamForm, resolveQuestionSeam } from "../wiring-manifest.js";
|
|
9
10
|
const TASK_LIMIT_KEY_DICT = {
|
|
@@ -73,7 +74,7 @@ export function resolveTaskLimits(limits) {
|
|
|
73
74
|
return limits;
|
|
74
75
|
}
|
|
75
76
|
export function isFableFamilyModelId(id) {
|
|
76
|
-
const tail = id
|
|
77
|
+
const tail = modelIdTail(id);
|
|
77
78
|
return /^claude-fable-\d/.test(tail) || /^claude-mythos-5(?!\d)/.test(tail);
|
|
78
79
|
}
|
|
79
80
|
export function resolveModelPromptTraits(model, spec, internals) {
|
|
@@ -85,10 +86,16 @@ export function resolveModelPromptTraits(model, spec, internals) {
|
|
|
85
86
|
export function prepareConfigDoors(input) {
|
|
86
87
|
const { deps, sessions, resume, internals } = input;
|
|
87
88
|
let spec = input.spec;
|
|
89
|
+
assertRestoreGatedToolsValue(spec.restoreGatedTools);
|
|
88
90
|
const toolFaceSnapshot = {
|
|
89
91
|
exclude: spec.excludeTools ? Object.freeze([...spec.excludeTools]) : undefined,
|
|
90
92
|
defer: spec.deferTools ? Object.freeze([...spec.deferTools]) : undefined,
|
|
91
93
|
alwaysLoad: spec.alwaysLoadTools ? Object.freeze([...spec.alwaysLoadTools]) : undefined,
|
|
94
|
+
restoreGated: spec.restoreGatedTools === true
|
|
95
|
+
? true
|
|
96
|
+
: spec.restoreGatedTools !== undefined
|
|
97
|
+
? Object.freeze([...spec.restoreGatedTools])
|
|
98
|
+
: undefined,
|
|
92
99
|
};
|
|
93
100
|
const promptProfile = resolveModelPromptTraits({ id: "" }, spec, internals).promptProfile;
|
|
94
101
|
if (spec.resumeAt !== undefined) {
|
|
@@ -223,11 +230,34 @@ export function prepareConfigDoors(input) {
|
|
|
223
230
|
throw e;
|
|
224
231
|
}
|
|
225
232
|
const perTask = spec.agents;
|
|
226
|
-
spec = {
|
|
233
|
+
spec = {
|
|
234
|
+
...spec,
|
|
235
|
+
tools: pool.map((t) => {
|
|
236
|
+
if (typeof t.withAgents !== "function")
|
|
237
|
+
return t;
|
|
238
|
+
const rebuilt = t.withAgents(perTask);
|
|
239
|
+
return t.modelGate !== undefined && rebuilt.modelGate === undefined ? { ...rebuilt, modelGate: t.modelGate } : rebuilt;
|
|
240
|
+
}),
|
|
241
|
+
};
|
|
227
242
|
}
|
|
228
243
|
const resolvedRole = resolveTaskModel(spec, deps);
|
|
229
244
|
const model = resolvedRole.model;
|
|
230
245
|
const fableMitigations = resolveModelPromptTraits(model, spec, internals).fableMitigations;
|
|
246
|
+
const gateDecision = applyToolModelGate({
|
|
247
|
+
tools: spec.tools,
|
|
248
|
+
modelId: model.id,
|
|
249
|
+
depsSeat: deps.toolModelGate,
|
|
250
|
+
restoreGated: toolFaceSnapshot.restoreGated,
|
|
251
|
+
envRaw: process.env.SEMA_TOOL_MODEL_GATE,
|
|
252
|
+
});
|
|
253
|
+
if (gateDecision.survivors !== undefined) {
|
|
254
|
+
spec = { ...spec, tools: gateDecision.survivors };
|
|
255
|
+
}
|
|
256
|
+
const modelGate = Object.freeze({
|
|
257
|
+
removedByClass: gateDecision.removedByClass,
|
|
258
|
+
unknownClasses: gateDecision.unknownClasses,
|
|
259
|
+
discardedEnvRaw: gateDecision.discardedEnvRaw,
|
|
260
|
+
});
|
|
231
261
|
const thinking = spec.thinking ?? resolvedRole.thinking ?? model.defaultThinking;
|
|
232
262
|
const compModel = spec.compactionModel
|
|
233
263
|
? resolveModel(spec.compactionModel, deps.models)
|
|
@@ -243,6 +273,7 @@ export function prepareConfigDoors(input) {
|
|
|
243
273
|
thinking,
|
|
244
274
|
compModel,
|
|
245
275
|
fableMitigations,
|
|
276
|
+
modelGate,
|
|
246
277
|
usageWindows,
|
|
247
278
|
brainCallGuardrailRef,
|
|
248
279
|
brainCallGuardrailMs,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { BeforeWriteHook, RunnerDeps, TaskSpec, ToolSpec } from "../types.js";
|
|
1
|
+
import type { BeforeWriteHook, EffectiveMemoryScopes, RunnerDeps, TaskSpec, ToolSpec } from "../types.js";
|
|
2
2
|
import type { Prepared } from "./prepare-task.js";
|
|
3
3
|
export interface PrepareMemoryInput {
|
|
4
4
|
spec: TaskSpec;
|
|
@@ -106,5 +106,15 @@ export interface PrepareMemoryResult {
|
|
|
106
106
|
/** The session's own frozen verdict to persist at suspend — present iff this leg ADJUDICATED an
|
|
107
107
|
* org plane (including adjudicated-empty); `undefined` for an org-less task. */
|
|
108
108
|
ownOrgVerdict: import("../memory-admission.js").OwnOrgAdmissionVerdict | undefined;
|
|
109
|
+
/**
|
|
110
|
+
* design/178 v2 §2.3 (件①) — the memory-visibility OBSERVATION this phase minted for
|
|
111
|
+
* `TaskResult.effectiveMemoryScopes` (see {@link EffectiveMemoryScopes} for the state law).
|
|
112
|
+
* ALWAYS present when the phase returns: the memory-less outcomes are their own values
|
|
113
|
+
* (`none` / `memoryless`), never an absent seat — absence on the RESULT means only "prepare
|
|
114
|
+
* never completed" (this phase threw). Minted AFTER the materialize outcome (the fail-open arm
|
|
115
|
+
* sits between the admission verdict and the mount — an earlier stamp would report a mount that
|
|
116
|
+
* never happened); the deliberate-refusal `config.memory_*` throws produce no value at all.
|
|
117
|
+
*/
|
|
118
|
+
effectiveMemoryScopes: EffectiveMemoryScopes;
|
|
109
119
|
}
|
|
110
120
|
export declare function prepareMemory(input: PrepareMemoryInput): Promise<PrepareMemoryResult>;
|