@sema-agent/core 5.45.0 → 5.47.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 +97 -0
- package/dist/agents/subagent.js +130 -3
- package/dist/core/checkpoint-store.d.ts +12 -0
- package/dist/core/governance-codes.d.ts +13 -0
- package/dist/core/governance-codes.js +33 -0
- package/dist/core/hooks.d.ts +9 -2
- package/dist/core/hooks.js +6 -5
- package/dist/core/memory-engine/content-origin.d.ts +3 -1
- package/dist/core/memory-engine/delegation-settlement.d.ts +318 -0
- package/dist/core/memory-engine/delegation-settlement.js +661 -0
- package/dist/core/memory-engine/engine.d.ts +209 -4
- package/dist/core/memory-engine/engine.js +885 -39
- package/dist/core/memory-engine/export-bundle.d.ts +10 -1
- package/dist/core/memory-engine/export-bundle.js +21 -0
- package/dist/core/memory-engine/file-backend.d.ts +34 -4
- package/dist/core/memory-engine/file-backend.js +168 -40
- package/dist/core/memory-engine/frontmatter.d.ts +69 -1
- package/dist/core/memory-engine/frontmatter.js +156 -2
- package/dist/core/memory-engine/header-hints.d.ts +17 -0
- package/dist/core/memory-engine/header-hints.js +6 -0
- package/dist/core/memory-engine/index.d.ts +7 -5
- package/dist/core/memory-engine/index.js +5 -3
- package/dist/core/memory-engine/layout.d.ts +39 -2
- package/dist/core/memory-engine/layout.js +27 -14
- package/dist/core/memory-engine/memory-backend-contract.js +108 -0
- package/dist/core/memory-engine/origin-clearance.d.ts +66 -0
- package/dist/core/memory-engine/origin-clearance.js +84 -0
- package/dist/core/memory-engine/provenance-wording.d.ts +50 -0
- package/dist/core/memory-engine/provenance-wording.js +15 -0
- package/dist/core/memory-engine/sync-client.d.ts +1 -1
- package/dist/core/memory-engine/sync-client.js +33 -1
- package/dist/core/memory-engine/tools.d.ts +64 -3
- package/dist/core/memory-engine/tools.js +37 -9
- package/dist/core/memory-engine/types.d.ts +145 -3
- package/dist/core/memory-engine/types.js +1 -1
- package/dist/core/reminder-mint.d.ts +70 -0
- package/dist/core/reminder-mint.js +25 -0
- package/dist/core/runner/git-status-frame.d.ts +3 -14
- package/dist/core/runner/git-status-frame.js +39 -14
- package/dist/core/runner/prepare-config-doors.d.ts +4 -0
- package/dist/core/runner/prepare-config-doors.js +15 -0
- package/dist/core/runner/prepare-hands-readface.d.ts +5 -11
- package/dist/core/runner/prepare-hands-readface.js +26 -0
- package/dist/core/runner/prepare-memory.d.ts +11 -0
- package/dist/core/runner/prepare-memory.js +61 -24
- package/dist/core/runner/prepare-task.d.ts +46 -1
- package/dist/core/runner/prepare-task.js +128 -23
- package/dist/core/runner/runtask.js +62 -55
- package/dist/core/session-reconcile.js +3 -2
- package/dist/core/side-query.d.ts +11 -1
- package/dist/core/side-query.js +3 -0
- package/dist/core/types.d.ts +85 -10
- package/dist/core/types.js +3 -0
- package/dist/engine/harness/types.d.ts +46 -1
- package/dist/engine/harness/types.js +11 -0
- package/dist/engine/session/import-validate.js +6 -1
- package/dist/engine/session/session.d.ts +20 -0
- package/dist/engine/session/session.js +26 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +3 -1
- package/dist/orchestration/run-workflow-tool.d.ts +16 -0
- package/dist/orchestration/run-workflow-tool.js +23 -3
- package/dist/orchestration/workflow-governance.d.ts +8 -1
- package/dist/prompt-assembly/epoch.js +2 -0
- package/dist/prompt-assembly/types.d.ts +6 -0
- package/dist/prompts/default.d.ts +13 -1
- package/dist/prompts/default.js +5 -1
- package/dist/tools/fs/fs-bash.d.ts +4 -0
- package/dist/tools/fs/fs-bash.js +1 -1
- package/dist/tools/fs/fs-read.d.ts +1 -1
- package/dist/tools/fs/fs-read.js +8 -7
- package/dist/tools/fs/fs-shared.d.ts +10 -4
- package/dist/tools/fs/fs-shared.js +6 -3
- package/dist/tools/fs/gh-rate-limit.d.ts +4 -1
- package/dist/tools/fs/gh-rate-limit.js +3 -2
- package/dist/tools/fs/index.d.ts +10 -2
- package/dist/tools/fs/index.js +2 -1
- package/dist/tools/task-list.d.ts +5 -1
- package/package.json +1 -1
- package/test/export-surface.snapshot.json +21 -2
|
@@ -98,6 +98,11 @@ export interface PrepareHandsReadFaceInput {
|
|
|
98
98
|
/** borrowed-mutable — the classify doctrine's probe installs (`.set`, only where the doctrine
|
|
99
99
|
* owns the seat). Same-object contract as above. */
|
|
100
100
|
reversibilityProbes: PrepareSafetyScanResult["reversibilityProbes"];
|
|
101
|
+
/** borrowed-readonly — design/319 (A ticket): the run's reminder provenance mark (minted/adopted
|
|
102
|
+
* by the driver's adoption ladder); threaded into the hands band so the fs-side engine mints
|
|
103
|
+
* (the Read cyber/dedup/offset/empty reminders, the gh rate-limit hint) stamp the same mark the
|
|
104
|
+
* system-prompt declaration names. */
|
|
105
|
+
reminderMark: string;
|
|
101
106
|
}
|
|
102
107
|
/** The phase's outputs (相 API 规则件 four-class form) — ALL settled before the return; the driver
|
|
103
108
|
* binds them as fresh consts (R-5) except the inverted-closure trio and the two shellGated bits,
|
|
@@ -173,17 +178,6 @@ export interface PrepareHandsReadFaceResult {
|
|
|
173
178
|
/** write-back — the Monitor twin of the bit above. */
|
|
174
179
|
shellGatedMonitor: boolean;
|
|
175
180
|
}
|
|
176
|
-
/** The hands-LESS resolver arm (#242) — SYNCHRONOUS by contract. The original P10 else-arm ran
|
|
177
|
-
* with ZERO awaits, so on a hands-less leg the stretch from the driver's A2A warning callback
|
|
178
|
-
* (deployment `onError`, which can queue a microtask) through the P11 SendMessage/delegation
|
|
179
|
-
* mounts was uninterruptible. An `async` phase call would open exactly that microtask window (the
|
|
180
|
-
* T1 hazard, here on the hands-less leg — a queued `spec.tools` mutation could suppress the
|
|
181
|
-
* SendMessage mount or plant a trusted-revive delegation entry). So this leg is its OWN plain sync
|
|
182
|
-
* seam the driver calls WITHOUT `await` (dispatching on the `handsEnabled` boolean it already
|
|
183
|
-
* owns — NOT on a `Promise` identity check, which a host that replaces `globalThis.Promise` could
|
|
184
|
-
* defeat; r3): the leg is byte-for-byte as synchronous as before the extraction.
|
|
185
|
-
* (Adversarial review 2026-08-18, verified against the a945be28 baseline: awaits 3981–4243 are all
|
|
186
|
-
* inside the hands branch; the else arm reached 4440/4463 with none.) */
|
|
187
181
|
export declare function resolveHandsLessReadFace(input: PrepareHandsReadFaceInput): PrepareHandsReadFaceResult;
|
|
188
182
|
/** The hands-MOUNT branch — the async half of the phase (see the module header for the contract).
|
|
189
183
|
* The driver awaits this ONLY on the hands leg (`handsEnabled === true`); the hands-less leg goes
|
|
@@ -12,8 +12,29 @@ let readFaceClampConsoleAnnounced = false;
|
|
|
12
12
|
export function __resetReadFaceClampAnnouncement() {
|
|
13
13
|
readFaceClampConsoleAnnounced = false;
|
|
14
14
|
}
|
|
15
|
+
function assertStringArraySeat(value, seat, entryLaw) {
|
|
16
|
+
if (value === undefined)
|
|
17
|
+
return;
|
|
18
|
+
if (!Array.isArray(value)) {
|
|
19
|
+
const e = new Error(`${seat} must be an array or absent (got ${value === null ? "null" : typeof value}) — a non-array here would be iterated character by character, so it refuses loudly instead.`);
|
|
20
|
+
e.code = "config.read_face_seat";
|
|
21
|
+
throw e;
|
|
22
|
+
}
|
|
23
|
+
if (entryLaw !== "strings")
|
|
24
|
+
return;
|
|
25
|
+
const entries = value;
|
|
26
|
+
for (const entry of entries) {
|
|
27
|
+
if (typeof entry !== "string") {
|
|
28
|
+
const e = new Error(`${seat} entries must be strings (got ${entry === null ? "null" : typeof entry}).`);
|
|
29
|
+
e.code = "config.read_face_seat";
|
|
30
|
+
throw e;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
15
34
|
export function resolveHandsLessReadFace(input) {
|
|
16
35
|
const { resume, fullShellReachable, spec, deps } = input;
|
|
36
|
+
assertStringArraySeat(deps.readDenyPatterns, "RunnerDeps.readDenyPatterns", "compiler-owned");
|
|
37
|
+
assertStringArraySeat(spec.readDenyPatterns, "TaskSpec.readDenyPatterns", "compiler-owned");
|
|
17
38
|
let handsLessResolvedFace;
|
|
18
39
|
let readDenyAdditionsNormalized = [];
|
|
19
40
|
{
|
|
@@ -56,6 +77,10 @@ export function resolveHandsLessReadFace(input) {
|
|
|
56
77
|
}
|
|
57
78
|
export async function prepareHandsMount(input) {
|
|
58
79
|
const { handsEnabled, executionEnv, taskRootFinal, rebaseRestoredPath, resume, session, sessionId, hostTaskId, taskScope, fullShellReachable, effectiveShellGate, toolFaceSnapshot, model, spec, deps, internals, memoryWriteGateRef, firstPartyOffload, tools, egressTools, irreversibilityTier, irreversibleTools, reversibilityProbes } = input;
|
|
80
|
+
assertStringArraySeat(spec.additionalDirectories, "TaskSpec.additionalDirectories", "strings");
|
|
81
|
+
assertStringArraySeat(spec.additionalReadDirectories, "TaskSpec.additionalReadDirectories", "strings");
|
|
82
|
+
assertStringArraySeat(deps.readDenyPatterns, "RunnerDeps.readDenyPatterns", "compiler-owned");
|
|
83
|
+
assertStringArraySeat(spec.readDenyPatterns, "TaskSpec.readDenyPatterns", "compiler-owned");
|
|
59
84
|
let readFileStateForCheckpoint;
|
|
60
85
|
let seedContextFiles;
|
|
61
86
|
let handsCwdRef;
|
|
@@ -193,6 +218,7 @@ export async function prepareHandsMount(input) {
|
|
|
193
218
|
...(readDenyBuiltinCfg.tiers !== undefined ? { readDenyBuiltinTiers: readDenyBuiltinCfg.tiers } : {}),
|
|
194
219
|
...(readDenyBuiltinCfg.exclude !== undefined ? { readDenyBuiltinExclude: readDenyBuiltinCfg.exclude } : {}),
|
|
195
220
|
readFace: liveReadFace,
|
|
221
|
+
reminderMark: input.reminderMark,
|
|
196
222
|
includeShell: handsIncludeShell,
|
|
197
223
|
readOnly: handsReadOnly,
|
|
198
224
|
...(handsCwdRef ? { cwdRef: handsCwdRef } : {}),
|
|
@@ -21,6 +21,10 @@ export interface PrepareMemoryInput {
|
|
|
21
21
|
* its roster (or cannot reach the store); the RB-276 index seed follows the same gate.
|
|
22
22
|
*/
|
|
23
23
|
writeToolsMounted: boolean;
|
|
24
|
+
/** design/319 (A ticket) — the run's reminder provenance mark: the memory engine's announcement
|
|
25
|
+
* block is an engine-authored `<system-reminder>`, so its open tag must carry the same mark the
|
|
26
|
+
* system-prompt declaration names. Threaded into every `engine.inject` call this phase builds. */
|
|
27
|
+
reminderMark: string;
|
|
24
28
|
/**
|
|
25
29
|
* The session-wide persistence verdict: `TaskSpec.memoryPersistenceCapable` (the deployment's own
|
|
26
30
|
* statement — the only honest channel for a custom memory writer persisting through its closure,
|
|
@@ -46,6 +50,13 @@ export interface PrepareMemoryInput {
|
|
|
46
50
|
* the memory store" stays a true statement instead of a disclosure the store then contradicts.
|
|
47
51
|
*/
|
|
48
52
|
memoryPersistenceDeclared?: boolean;
|
|
53
|
+
/**
|
|
54
|
+
* design/336 §13-3 — the memory-provenance master switch, screened at the config doors
|
|
55
|
+
* (`config.memory_provenance`, exact spellings, absent folded to `"carry"`). Every engine this
|
|
56
|
+
* phase constructs runs under it, and the pollution notices/tool wording state the mode-true
|
|
57
|
+
* behavior off it. See {@link RunnerDeps.memoryProvenance}.
|
|
58
|
+
*/
|
|
59
|
+
memoryProvenance: "off" | "carry";
|
|
49
60
|
/**
|
|
50
61
|
* design/178 ②-1 — whether the `memory_search`/`memory_get` pair PASSED its early mount conjuncts
|
|
51
62
|
* (exclusion + name occupancy, decided in prepare-task BEFORE this phase). True ⇒ this phase builds
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { sep } from "node:path";
|
|
1
|
+
import { isAbsolute, sep } from "node:path";
|
|
2
2
|
import { deliverEngineNotice } from "../types.js";
|
|
3
3
|
import { admitMemoryScopes } from "../memory-admission.js";
|
|
4
4
|
import { adoptLegacyRepoDirs, canonicalize, deriveRepoControlPlaneDir, deriveProjectControlDir, deriveProjectMemoryDir, deriveRepoMemoryDir, drainMemoryAnnouncements, enqueueMemoryAnnouncement, isContainedIn, lookupProjectIdHint, recordProjectIdHint, resolveMemoryEngineRoot } from "../memory-engine/layout.js";
|
|
5
5
|
import { classifyScopePlanes, derivePersonalControlDir, derivePersonalMemoryDir, mergeHarvestReports, mergeInjections, needsDualRoots, parsedProjectPlane } from "../memory-engine/dual-root.js";
|
|
6
6
|
import { normalizeMemorySpec } from "../memory.js";
|
|
7
|
-
import { MEMORY_ANNOUNCEMENT_READONLY_CODA, MEMORY_PREFERENCE_DISCIPLINE, MEMORY_READONLY_NOTICE,
|
|
7
|
+
import { MEMORY_ANNOUNCEMENT_READONLY_CODA, MEMORY_PREFERENCE_DISCIPLINE, MEMORY_READONLY_NOTICE, MemoryEngine, entryFileHeadCarriesOrigin, memoryHarvestQuarantinedNotice, memoryHoldNotices, memoryRecallDisciplineSegment, memorySessionPollutedNotice, pollutionContainmentCounts, } from "../memory-engine/engine.js";
|
|
8
8
|
import { createMemoryEngineTools } from "../memory-engine/tools.js";
|
|
9
9
|
import { assertScopeContractPlacement, parseScopeKey, resolveProjectId } from "../memory-engine/scope-contract.js";
|
|
10
10
|
import { FileMemoryEngineBackend } from "../memory-engine/file-backend.js";
|
|
@@ -135,7 +135,7 @@ export async function prepareMemory(input) {
|
|
|
135
135
|
const choosePersonalBackend = () => typeof pinned === "string" && pinned ? new FileMemoryEngineBackend(personalMemoryDir, { controlDir: personalControlDir }) : backend;
|
|
136
136
|
const createPersonalEngine = (personalBackend) => {
|
|
137
137
|
return {
|
|
138
|
-
engine: new MemoryEngine({ backend: personalBackend, memoryDir: personalMemoryDir, controlDir: personalControlDir, onIncident: onEngineIncident }),
|
|
138
|
+
engine: new MemoryEngine({ backend: personalBackend, memoryDir: personalMemoryDir, controlDir: personalControlDir, onIncident: onEngineIncident, provenance: input.memoryProvenance }),
|
|
139
139
|
backend: personalBackend,
|
|
140
140
|
};
|
|
141
141
|
};
|
|
@@ -155,7 +155,7 @@ export async function prepareMemory(input) {
|
|
|
155
155
|
: planeScopes(memorySpec.scopes, memorySpec.writeScope)).map((scope) => ({ scope, origin: originOf(scope) }));
|
|
156
156
|
const pollutedOpts = (engine) => {
|
|
157
157
|
const rec = engine.sessionPollution(sessionId);
|
|
158
|
-
return rec !== undefined ? { polluted: { reason: rec.reason } } : {};
|
|
158
|
+
return rec !== undefined ? { polluted: { reason: rec.reason, ...(rec.cause !== undefined ? { cause: rec.cause } : {}) } } : {};
|
|
159
159
|
};
|
|
160
160
|
let writeEngine;
|
|
161
161
|
let writeHandle;
|
|
@@ -194,20 +194,21 @@ export async function prepareMemory(input) {
|
|
|
194
194
|
memoryDir,
|
|
195
195
|
controlDir: identityKey !== undefined ? deriveProjectControlDir(engineRoot, identityKey) : deriveRepoControlPlaneDir(engineRoot, repoRoot),
|
|
196
196
|
onIncident: onEngineIncident,
|
|
197
|
+
provenance: input.memoryProvenance,
|
|
197
198
|
});
|
|
198
199
|
const personal = createPersonalEngine(personalBackendChosen);
|
|
199
200
|
const personalEngine = personal.engine;
|
|
200
201
|
const p = planes;
|
|
201
|
-
const projectHandle = await projectEngine.materialize(p.project, p.writePlane === "project" ? memorySpec.writeScope : null, { adoptionRestricted });
|
|
202
|
+
const projectHandle = await projectEngine.materialize(p.project, p.writePlane === "project" ? memorySpec.writeScope : null, { adoptionRestricted, sessionId });
|
|
202
203
|
materializedResidue.push(...planeScopes(p.project, p.writePlane === "project" ? memorySpec.writeScope : null));
|
|
203
|
-
const personalHandle = await personalEngine.materialize(p.personal, p.writePlane === "personal" ? memorySpec.writeScope : null, { adoptionRestricted });
|
|
204
|
+
const personalHandle = await personalEngine.materialize(p.personal, p.writePlane === "personal" ? memorySpec.writeScope : null, { adoptionRestricted, sessionId });
|
|
204
205
|
materializedResidue.push(...planeScopes(p.personal, p.writePlane === "personal" ? memorySpec.writeScope : null));
|
|
205
206
|
const writeIsPersonal = p.writePlane === "personal";
|
|
206
207
|
writeEngine = writeIsPersonal ? personalEngine : projectEngine;
|
|
207
208
|
writeHandle = writeIsPersonal ? personalHandle : projectHandle;
|
|
208
209
|
readOnlyEngine = writeIsPersonal ? projectEngine : personalEngine;
|
|
209
210
|
readOnlyHandle = writeIsPersonal ? projectHandle : personalHandle;
|
|
210
|
-
injectFn = () => mergeInjections(projectEngine.inject(projectHandle, { writeToolMounted: input.writeToolsMounted }), personalEngine.inject(personalHandle, { writeToolMounted: input.writeToolsMounted }));
|
|
211
|
+
injectFn = () => mergeInjections(projectEngine.inject(projectHandle, { writeToolMounted: input.writeToolsMounted, reminderMark: input.reminderMark }), personalEngine.inject(personalHandle, { writeToolMounted: input.writeToolsMounted, reminderMark: input.reminderMark }));
|
|
211
212
|
toolPlanes = [
|
|
212
213
|
{
|
|
213
214
|
backend: retrievalBackend(backend, adoptionRestricted || p.writePlane !== "project"),
|
|
@@ -243,11 +244,11 @@ export async function prepareMemory(input) {
|
|
|
243
244
|
else if (personalOnly) {
|
|
244
245
|
const personal = createPersonalEngine(choosePersonalBackend());
|
|
245
246
|
const personalEngine = personal.engine;
|
|
246
|
-
const handle = await personalEngine.materialize(memorySpec.scopes, memorySpec.writeScope, { adoptionRestricted });
|
|
247
|
+
const handle = await personalEngine.materialize(memorySpec.scopes, memorySpec.writeScope, { adoptionRestricted, sessionId });
|
|
247
248
|
materializedResidue.push(...planeScopes(memorySpec.scopes, memorySpec.writeScope));
|
|
248
249
|
writeEngine = personalEngine;
|
|
249
250
|
writeHandle = handle;
|
|
250
|
-
injectFn = () => personalEngine.inject(handle, { writeToolMounted: input.writeToolsMounted });
|
|
251
|
+
injectFn = () => personalEngine.inject(handle, { writeToolMounted: input.writeToolsMounted, reminderMark: input.reminderMark });
|
|
251
252
|
harvestBoth = () => personalEngine.harvest(handle, { ...pollutedOpts(personalEngine), sessionId, ...admitNothingOpts });
|
|
252
253
|
toolPlanes = [
|
|
253
254
|
{
|
|
@@ -264,12 +265,13 @@ export async function prepareMemory(input) {
|
|
|
264
265
|
memoryDir,
|
|
265
266
|
controlDir: identityKey !== undefined ? deriveProjectControlDir(engineRoot, identityKey) : deriveRepoControlPlaneDir(engineRoot, repoRoot),
|
|
266
267
|
onIncident: onEngineIncident,
|
|
268
|
+
provenance: input.memoryProvenance,
|
|
267
269
|
});
|
|
268
|
-
const handle = await engine.materialize(memorySpec.scopes, memorySpec.writeScope, { adoptionRestricted });
|
|
270
|
+
const handle = await engine.materialize(memorySpec.scopes, memorySpec.writeScope, { adoptionRestricted, sessionId });
|
|
269
271
|
materializedResidue.push(...planeScopes(memorySpec.scopes, memorySpec.writeScope));
|
|
270
272
|
writeEngine = engine;
|
|
271
273
|
writeHandle = handle;
|
|
272
|
-
injectFn = () => engine.inject(handle, { writeToolMounted: input.writeToolsMounted });
|
|
274
|
+
injectFn = () => engine.inject(handle, { writeToolMounted: input.writeToolsMounted, reminderMark: input.reminderMark });
|
|
273
275
|
harvestBoth = () => engine.harvest(handle, { ...pollutedOpts(engine), sessionId, ...admitNothingOpts });
|
|
274
276
|
toolPlanes = [
|
|
275
277
|
{
|
|
@@ -302,15 +304,18 @@ export async function prepareMemory(input) {
|
|
|
302
304
|
let pollutionAnnounced = false;
|
|
303
305
|
const announceHarvestContainment = (report) => {
|
|
304
306
|
const { count, moved, escalated } = pollutionContainmentCounts(report);
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
307
|
+
const indexOnly = input.memoryProvenance !== "off" && count === 0 && report.containment?.indexRolledBack === true;
|
|
308
|
+
if (count > 0 || indexOnly) {
|
|
309
|
+
let reason;
|
|
310
|
+
try {
|
|
311
|
+
reason = writeEngine.sessionPollution(sessionId)?.reason;
|
|
312
|
+
}
|
|
313
|
+
catch {
|
|
314
|
+
}
|
|
315
|
+
deliverEngineNotice(deps.onNotice, memoryHarvestQuarantinedNotice({ count, moved, escalated, ...(reason !== undefined ? { reason } : {}), sessionId, provenance: input.memoryProvenance, ...(indexOnly ? { indexRolledBack: true } : {}) }));
|
|
312
316
|
}
|
|
313
|
-
|
|
317
|
+
for (const notice of memoryHoldNotices(report, sessionId))
|
|
318
|
+
deliverEngineNotice(deps.onNotice, notice);
|
|
314
319
|
};
|
|
315
320
|
const harvestSafe = async (phase = "terminal") => {
|
|
316
321
|
try {
|
|
@@ -342,18 +347,40 @@ export async function prepareMemory(input) {
|
|
|
342
347
|
harvest: harvestSafe,
|
|
343
348
|
pollution: {
|
|
344
349
|
polluted: () => writeEngine.sessionPollution(sessionId),
|
|
345
|
-
markPolluted: (reason) => {
|
|
346
|
-
const outcome = writeEngine.markSessionPolluted(sessionId, reason);
|
|
350
|
+
markPolluted: (reason, cause) => {
|
|
351
|
+
const outcome = writeEngine.markSessionPolluted(sessionId, reason, cause);
|
|
347
352
|
if (outcome === "existed" || (outcome === "unpersisted" && pollutionAnnounced))
|
|
348
353
|
return;
|
|
349
354
|
pollutionAnnounced = true;
|
|
350
|
-
deliverEngineNotice(deps.onNotice, memorySessionPollutedNotice({ reason, sessionId }));
|
|
355
|
+
deliverEngineNotice(deps.onNotice, memorySessionPollutedNotice({ reason, sessionId, provenance: input.memoryProvenance }));
|
|
351
356
|
},
|
|
352
357
|
},
|
|
353
358
|
contentSafety: {
|
|
354
359
|
trustedTools: new Set(memorySpec.trustedTools ?? []),
|
|
355
360
|
execIsExternalContent: memorySpec.execIsExternalContent === true,
|
|
356
361
|
},
|
|
362
|
+
...(input.memoryProvenance !== "off"
|
|
363
|
+
? { settlement: { controlDir: writeEngine.controlPlaneDir, sessionId, provenance: "carry" } }
|
|
364
|
+
: {}),
|
|
365
|
+
...(input.memoryProvenance !== "off"
|
|
366
|
+
? {
|
|
367
|
+
recallTaint: {
|
|
368
|
+
judgeDeliveredPath: (absPath) => {
|
|
369
|
+
try {
|
|
370
|
+
if (!isAbsolute(absPath))
|
|
371
|
+
return false;
|
|
372
|
+
const roots = [writeHandle.memoryDir, ...(readOnlyHandle !== undefined ? [readOnlyHandle.memoryDir] : [])];
|
|
373
|
+
if (!roots.some((r) => isContainedIn(r, absPath)))
|
|
374
|
+
return false;
|
|
375
|
+
return entryFileHeadCarriesOrigin(absPath);
|
|
376
|
+
}
|
|
377
|
+
catch {
|
|
378
|
+
return false;
|
|
379
|
+
}
|
|
380
|
+
},
|
|
381
|
+
},
|
|
382
|
+
}
|
|
383
|
+
: {}),
|
|
357
384
|
};
|
|
358
385
|
effectiveMemoryScopes = {
|
|
359
386
|
state: "mounted",
|
|
@@ -361,8 +388,17 @@ export async function prepareMemory(input) {
|
|
|
361
388
|
scopes: mountedScopeRows,
|
|
362
389
|
writeScope: memorySpec.writeScope,
|
|
363
390
|
};
|
|
391
|
+
if (input.memoryProvenance !== "off") {
|
|
392
|
+
const session = memoryEngineSession;
|
|
393
|
+
const markDerived = (ids) => {
|
|
394
|
+
const shown = ids.slice(0, 3).join(", ") + (ids.length > 3 ? ", …" : "");
|
|
395
|
+
session.pollution.markPolluted(`this session read the content of external-origin memory entr${ids.length === 1 ? "y" : "ies"} ${shown}`, "derived");
|
|
396
|
+
};
|
|
397
|
+
for (const plane of toolPlanes)
|
|
398
|
+
plane.onTaintedDelivery = markDerived;
|
|
399
|
+
}
|
|
364
400
|
if (input.memorySearchToolsPlanned)
|
|
365
|
-
memoryTools = createMemoryEngineTools({ planes: toolPlanes, sessionPollution: () => writeEngine.sessionPollution(sessionId) });
|
|
401
|
+
memoryTools = createMemoryEngineTools({ planes: toolPlanes, sessionPollution: () => writeEngine.sessionPollution(sessionId), provenance: input.memoryProvenance });
|
|
366
402
|
}
|
|
367
403
|
catch (err) {
|
|
368
404
|
if (typeof err.code === "string" &&
|
|
@@ -411,7 +447,8 @@ export async function prepareMemory(input) {
|
|
|
411
447
|
if (blockBody.trim())
|
|
412
448
|
memoryBlock = blockBody;
|
|
413
449
|
if (memoryTools !== undefined) {
|
|
414
|
-
|
|
450
|
+
const segment = memoryRecallDisciplineSegment(input.memoryProvenance);
|
|
451
|
+
memoryBlock = memoryBlock !== undefined ? `${memoryBlock}\n\n${segment}` : segment;
|
|
415
452
|
}
|
|
416
453
|
if (memoryEngineSession.handle.writeScope !== null && input.writeToolsMounted && input.rosterCanPersist) {
|
|
417
454
|
memoryBlock = memoryBlock !== undefined ? `${memoryBlock}\n\n${MEMORY_PREFERENCE_DISCIPLINE}` : MEMORY_PREFERENCE_DISCIPLINE;
|
|
@@ -142,6 +142,11 @@ export interface Prepared {
|
|
|
142
142
|
* the `Session` contract instead. */
|
|
143
143
|
session: StoredSession;
|
|
144
144
|
sessionId: string;
|
|
145
|
+
/** design/319 (A ticket) — the session's reminder provenance mark this run mints under (adopted
|
|
146
|
+
* through the prepare adoption ladder: checkpoint seed → trusted fork channel → session entry →
|
|
147
|
+
* fresh mint). Every engine-authored `<system-reminder>` open tag in the run carries it, and the
|
|
148
|
+
* system prompt's Harness declaration names it. Always present on a completed prepare. */
|
|
149
|
+
reminderMark: string;
|
|
145
150
|
/** The ISOLATION-AWARE working-tree root for this task (a worktree's cwd when `isolation: "worktree"`, else
|
|
146
151
|
* `deps.rootPath ?? executionEnv.cwd`) — the same value the hands/LSP/policy/restore use. The Runner's
|
|
147
152
|
* rewind/snapshot path MUST key off THIS, not `deps.rootPath`, or a worktree-isolated turn snapshots the base
|
|
@@ -414,8 +419,13 @@ export interface Prepared {
|
|
|
414
419
|
polluted: () => {
|
|
415
420
|
at: number;
|
|
416
421
|
reason: string;
|
|
422
|
+
cause?: import("../memory-engine/types.js").MemoryOriginCause;
|
|
417
423
|
} | undefined;
|
|
418
|
-
|
|
424
|
+
/** design/336 §2.2 — `cause` is the structured mechanical fact the mark records (and the
|
|
425
|
+
* origin marker minted off it carries): `"observed"` for a witnessed external event,
|
|
426
|
+
* `"static"` for the capability over-approximation, `"derived"` for recall-taint. Absent
|
|
427
|
+
* folds to `"observed"` at mint time (the honest floor). */
|
|
428
|
+
markPolluted: (reason: string, cause?: import("../memory-engine/types.js").MemoryOriginCause) => void;
|
|
419
429
|
};
|
|
420
430
|
/** design/178 §3 — the task's content-safety config (normalized memory spec): the allowlist for
|
|
421
431
|
* UNDECLARED tools and the strict execution-class upgrade. Consumed by the tool wrap only. */
|
|
@@ -423,6 +433,30 @@ export interface Prepared {
|
|
|
423
433
|
trustedTools: ReadonlySet<string>;
|
|
424
434
|
execIsExternalContent: boolean;
|
|
425
435
|
};
|
|
436
|
+
/**
|
|
437
|
+
* design/336 §3.3 — the delegation-settlement handle: the control-plane coordinates a
|
|
438
|
+
* settlement writer needs, as PURE DATA. Consumers (the subagent background leg's write-ahead
|
|
439
|
+
* + terminal observation, the tool wrap's sync unattestable row) can outlive this prepared
|
|
440
|
+
* leg, so they rebuild their write handle from these fields alone — never from the live
|
|
441
|
+
* engine/session objects above. Present only under `memoryProvenance: "carry"` (the default):
|
|
442
|
+
* an `"off"` deployment keeps the pre-336 accepted-cost posture byte-level.
|
|
443
|
+
*/
|
|
444
|
+
settlement?: {
|
|
445
|
+
/** The WRITE plane's control-plane dir (the pollution-marker/lineage sidecar home). */
|
|
446
|
+
controlDir: string;
|
|
447
|
+
sessionId: string;
|
|
448
|
+
provenance: "carry";
|
|
449
|
+
};
|
|
450
|
+
/**
|
|
451
|
+
* design/336 §5.5 (file-face half) — the Read-tool recall-taint judgment, present only under
|
|
452
|
+
* `memoryProvenance: "carry"`. True ⇔ the delivered ABSOLUTE path sits inside a mounted memory
|
|
453
|
+
* plane and its head bytes carry a committed external-origin marker; the tool wrap then marks
|
|
454
|
+
* the session derived (same seat and cause as the memory_get propagation). Never throws;
|
|
455
|
+
* relative paths and unreadable files answer false (named residuals beside the Bash channel).
|
|
456
|
+
*/
|
|
457
|
+
recallTaint?: {
|
|
458
|
+
judgeDeliveredPath: (absPath: string) => boolean;
|
|
459
|
+
};
|
|
426
460
|
};
|
|
427
461
|
/** A per-task env minted by `RunnerDeps.executionEnvFactory` (design/48 remote seam) that THIS task owns
|
|
428
462
|
* and the Runner must tear down on task end. Undefined when the env came from a (caller-owned) static
|
|
@@ -1167,6 +1201,17 @@ export interface RunInternals {
|
|
|
1167
1201
|
* `TaskSpec` field — the untrusted-caller surface), mirrors `inheritedManifestScope`.
|
|
1168
1202
|
*/
|
|
1169
1203
|
insideFork?: boolean;
|
|
1204
|
+
/**
|
|
1205
|
+
* design/319 (A ticket) — the PARENT's reminder provenance mark, set ONLY by the Agent tool's
|
|
1206
|
+
* fork route on the child it spawns ("one declaration, one mark": a fork runs under the parent's
|
|
1207
|
+
* byte-identical system-prompt declaration, so its own engine mints must carry the PARENT's mark;
|
|
1208
|
+
* a spawn/clone context mints its own). Verified through the mint home's verify port at adoption
|
|
1209
|
+
* (`isValidReminderMark` — an unrecognized value re-mints, fail-safe); the forked SESSION's own
|
|
1210
|
+
* `reminder_mark` entry is the primary carrier (a store fork copies committed history), this
|
|
1211
|
+
* channel is the in-process belt over it. TRUSTED run-scoped channel (NOT a {@link TaskSpec}
|
|
1212
|
+
* field), mirroring `insideFork`.
|
|
1213
|
+
*/
|
|
1214
|
+
reminderMark?: string;
|
|
1170
1215
|
/**
|
|
1171
1216
|
* RB-204 P1 (codex review) — set UNCONDITIONALLY by every core spawn path (`createSubagentTool`'s
|
|
1172
1217
|
* `childInternals` — shared by all four spawn legs sync/steer/background/fork, its persisted
|