@sema-agent/core 7.12.0 → 7.14.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 +4 -9
- package/dist/agents/subagent.js +16 -12
- package/dist/agents/teacher.js +1 -1
- package/dist/brain/reasoning.d.ts +23 -0
- package/dist/brain/reasoning.js +35 -4
- package/dist/brain/stream-engine.js +35 -17
- package/dist/brain/timeout.d.ts +25 -4
- package/dist/brain/timeout.js +1 -1
- package/dist/core/ask-origin.d.ts +83 -9
- package/dist/core/ask-origin.js +31 -5
- package/dist/core/auto-mode.d.ts +3 -3
- package/dist/core/checkpoint-seat.d.ts +18 -0
- package/dist/core/checkpoint-seat.js +6 -0
- package/dist/core/checkpoint-store.d.ts +9 -16
- package/dist/core/checkpoint-store.js +7 -4
- package/dist/core/engine-notice.d.ts +21 -1
- package/dist/core/env-knob-announce.d.ts +16 -0
- package/dist/core/env-knob-announce.js +14 -0
- package/dist/core/gate-fold.js +2 -0
- package/dist/core/gate-lanes.d.ts +0 -40
- package/dist/core/gate-lanes.js +24 -29
- package/dist/core/gate-outcome.d.ts +42 -3
- package/dist/core/gate-outcome.js +22 -0
- package/dist/core/governance-codes.d.ts +1 -1
- package/dist/core/governance-codes.js +3 -1
- package/dist/core/hooks.d.ts +6 -17
- package/dist/core/hooks.js +7 -5
- package/dist/core/mcp.d.ts +3 -2
- package/dist/core/mcp.js +5 -11
- package/dist/core/memory-engine/engine.d.ts +2 -2
- package/dist/core/runner/advertised-writable-dirs.d.ts +74 -0
- package/dist/core/runner/advertised-writable-dirs.js +48 -0
- package/dist/core/runner/assemble-result.d.ts +50 -1
- package/dist/core/runner/assemble-result.js +4 -1
- package/dist/core/runner/compaction-seams.d.ts +42 -0
- package/dist/core/runner/compaction-seams.js +80 -0
- package/dist/core/runner/contracts.d.ts +44 -6
- package/dist/core/runner/denial-limit-arms.d.ts +7 -9
- package/dist/core/runner/denial-limit-arms.js +11 -12
- package/dist/core/runner/gate-exit.d.ts +15 -3
- package/dist/core/runner/gate-exit.js +6 -4
- package/dist/core/runner/permission-rule-lanes.d.ts +2 -1
- package/dist/core/runner/permission-rule-lanes.js +2 -1
- package/dist/core/runner/prepare-caps-and-workflow.d.ts +34 -4
- package/dist/core/runner/prepare-caps-and-workflow.js +47 -12
- package/dist/core/runner/prepare-gate-stations.d.ts +1 -1
- package/dist/core/runner/prepare-hands-readface.d.ts +10 -4
- package/dist/core/runner/prepare-hands-readface.js +5 -14
- package/dist/core/runner/prepare-inherited-gate.d.ts +5 -3
- package/dist/core/runner/prepare-inherited-gate.js +1 -1
- package/dist/core/runner/prepare-memory.d.ts +31 -34
- package/dist/core/runner/prepare-memory.js +73 -51
- package/dist/core/runner/prepare-policy-chain.js +8 -4
- package/dist/core/runner/prepare-prompt-inputs.d.ts +4 -0
- package/dist/core/runner/prepare-prompt-inputs.js +2 -2
- package/dist/core/runner/prepare-protocol-tools.js +5 -0
- package/dist/core/runner/prepare-question-face.d.ts +10 -8
- package/dist/core/runner/prepare-question-face.js +1 -3
- package/dist/core/runner/prepare-safety-scan.js +11 -16
- package/dist/core/runner/prepare-task.d.ts +9 -1
- package/dist/core/runner/prepare-task.js +63 -49
- package/dist/core/runner/prepare-wiring-manifest.d.ts +2 -2
- package/dist/core/runner/prepare-wiring-manifest.js +1 -1
- package/dist/core/runner/resume-claim.d.ts +2 -2
- package/dist/core/runner/resume-preflight.d.ts +2 -2
- package/dist/core/runner/run-attachment-seats.d.ts +2 -2
- package/dist/core/runner/run-compaction-machinery.js +2 -0
- package/dist/core/runner/run-git-lane.d.ts +1 -1
- package/dist/core/runner/run-harness-handlers.js +5 -1
- package/dist/core/runner/run-identity-wiring.d.ts +7 -0
- package/dist/core/runner/run-identity-wiring.js +3 -1
- package/dist/core/runner/run-leg.d.ts +7 -5
- package/dist/core/runner/run-leg.js +256 -5
- package/dist/core/runner/run-notification-lane.d.ts +4 -3
- package/dist/core/runner/run-recovery-lanes.d.ts +4 -17
- package/dist/core/runner/run-recovery-lanes.js +5 -4
- package/dist/core/runner/run-settle-and-teardown.d.ts +14 -10
- package/dist/core/runner/run-settle-and-teardown.js +112 -4
- package/dist/core/runner/run-stop-and-final-verify.d.ts +2 -2
- package/dist/core/runner/run-terminal-adoption.d.ts +16 -16
- package/dist/core/runner/run-terminal-adoption.js +87 -7
- package/dist/core/runner/run-turn-boundary.js +5 -4
- package/dist/core/runner/runtask.d.ts +10 -90
- package/dist/core/runner/runtask.js +77 -596
- package/dist/core/runner/stream-lifecycle-verbs.js +10 -1
- package/dist/core/runner/stream-settle-backstop.js +1 -1
- package/dist/core/runtime-caps.d.ts +21 -0
- package/dist/core/runtime-caps.js +5 -1
- package/dist/core/task-event.d.ts +11 -2
- package/dist/core/task-registry-shared.js +8 -6
- package/dist/core/task-result.d.ts +15 -0
- package/dist/core/task-stream.d.ts +10 -1
- package/dist/core/terminal-cause.d.ts +6 -2
- package/dist/core/tool-face.d.ts +8 -0
- package/dist/core/tool-face.js +1 -0
- package/dist/core/tool-policy.d.ts +39 -31
- package/dist/core/tool-policy.js +29 -5
- package/dist/core/tool-registry.d.ts +11 -3
- package/dist/core/tool-registry.js +7 -1
- package/dist/core/tool-roster.d.ts +26 -0
- package/dist/core/tool-roster.js +40 -7
- package/dist/core/tool-spec.d.ts +13 -12
- package/dist/core/types.d.ts +2 -1
- package/dist/core/types.js +1 -0
- package/dist/core/wiring-manifest.d.ts +16 -9
- package/dist/core/wiring-manifest.js +8 -3
- package/dist/index.d.ts +8 -7
- package/dist/index.js +7 -6
- package/dist/orchestration/run-workflow-tool.d.ts +8 -8
- package/dist/orchestration/run-workflow-tool.js +1 -1
- package/dist/orchestration/workflow-primitives.d.ts +4 -3
- package/dist/orchestration/workflow-primitives.js +3 -3
- package/dist/orchestration/workflow-types.d.ts +14 -0
- package/dist/orchestration/workflow.d.ts +41 -4
- package/dist/orchestration/workflow.js +23 -5
- package/dist/server/http.js +2 -1
- package/dist/tools/fs/fs-shared.js +9 -3
- package/dist/tools/fs/read-deny.d.ts +24 -8
- package/dist/tools/fs/read-deny.js +20 -1
- package/package.json +2 -1
- package/test/export-surface.snapshot.json +35 -1
|
@@ -25,28 +25,33 @@ import type { PromptEpochArtifact } from "../../prompt-assembly/artifact.js";
|
|
|
25
25
|
import { type WorkflowSizeGuideline } from "../../orchestration/workflow-size-guideline.js";
|
|
26
26
|
import { type AutoModeArmingRecipe } from "../auto-mode-arming.js";
|
|
27
27
|
import { type AutoModeDecider, type AutoModeDenialTracker } from "../auto-mode.js";
|
|
28
|
+
import { type CheckpointSeat } from "../checkpoint-seat.js";
|
|
28
29
|
import { type ComplianceCapability } from "../compliance.js";
|
|
29
30
|
import type { LockedPreflight } from "../locked-config.js";
|
|
30
31
|
import type { StoredSession } from "../session.js";
|
|
31
32
|
import type { OnAsk } from "../tool-policy.js";
|
|
32
33
|
import type { RunnerDeps, RuntimeCaps, TaskEvent, TaskSpec, ToolExecuteContext } from "../types.js";
|
|
33
|
-
import type {
|
|
34
|
+
import type { AutoModeArmFact } from "../wiring-manifest.js";
|
|
35
|
+
import type { AssertAllKeysHandled } from "../ask-origin.js";
|
|
34
36
|
import type { ReadFace } from "../../tools/fs/index.js";
|
|
35
37
|
import type { InheritedGate, Prepared, RunInternals, RunnerSelfSeat, ToolFaceSnapshot } from "./contracts.js";
|
|
36
38
|
import { type BlockedRef } from "./synthetic-tools.js";
|
|
37
39
|
import { RosterBuilder } from "../tool-roster.js";
|
|
38
40
|
export interface PrepareCapsAndWorkflowInput {
|
|
39
41
|
/** borrowed-readonly — the REBOUND spec, whole: the roster map reads `tools`; the mounts read `enableBlockedReport`,
|
|
40
|
-
* `enablePlanMode`, `interactiveTools
|
|
42
|
+
* `enablePlanMode`, `interactiveTools`; the caps/compliance stations read `principal`, `selfOrchestration`,
|
|
41
43
|
* `enableFork`, `getApiKeyAndHeaders`; the Workflow mount forwards `oneShot`, `handsReadOnly`, `interactiveTools`. The
|
|
42
44
|
* self-orchestration predicate takes the whole spec. Never mutated. */
|
|
43
45
|
spec: TaskSpec;
|
|
44
46
|
/** borrowed-readonly — the deployment seats, whole: `runtimeCapsResolver`, `compliancePostureResolver`, `onError`, `onNotice`,
|
|
45
|
-
* `autoMode`, `brain`, `models`,
|
|
47
|
+
* `autoMode`, `brain`, `models`, and the Workflow mount's dozen deps (`workflowScriptRunner`,
|
|
46
48
|
* `workflowGovernanceBaseline`, `workflowRunStore`, `workflowJournalStore`, `workflowScriptStore`, `builtinWorkflows`,
|
|
47
49
|
* `onWorkflowAgentSpawn`, `workflowCompletionNotifier`, `workflowLimits`, `agents`, `builtinAgents`, `onBackgroundChildEvent`).
|
|
48
50
|
* The self-orchestration predicate takes the whole record. */
|
|
49
51
|
deps: RunnerDeps;
|
|
52
|
+
/** borrowed-readonly — the seat this run hands its children (`checkpointSeatOf`, minted once by the driver right after the
|
|
53
|
+
* config doors): the plan-pair face reads the store it names, the Workflow mount hands the seat itself to its children. */
|
|
54
|
+
checkpointSeat: CheckpointSeat | undefined;
|
|
50
55
|
/** borrowed-readonly — the trusted spawn-side channel; the Workflow mount forwards `rootSessionId`, `placementRoot`,
|
|
51
56
|
* `onTaskNotification`, `workflowDepth`, and the capture-floor getter reads `memoryCaptureAncestors`. */
|
|
52
57
|
internals: Pick<RunInternals, "rootSessionId" | "placementRoot" | "onTaskNotification" | "workflowDepth" | "memoryCaptureAncestors" | "workflowParkedResume"> | undefined;
|
|
@@ -145,7 +150,8 @@ export interface PrepareCapsAndWorkflowResult {
|
|
|
145
150
|
/** owned — the observers grant (`allowObservers === true`, explicit opt-in). */
|
|
146
151
|
observersActive: boolean;
|
|
147
152
|
/** owned — the arming outcome as a closed reason (the manifest's auto-mode face). */
|
|
148
|
-
|
|
153
|
+
/** #529 / #692 C-d — the leg's auto-mode arming fact (`armed` ⇔ `reason === "armed"`; `deniedSource` only beside a STATED `denied`). */
|
|
154
|
+
autoModeArmReason: AutoModeArmFact;
|
|
149
155
|
/** owned — the armed classifier, or undefined when any arming arm failed. */
|
|
150
156
|
autoModeDecider: AutoModeDecider | undefined;
|
|
151
157
|
/** owned — the denial tracker built beside the decider (same face, same arming). */
|
|
@@ -162,5 +168,29 @@ export interface PrepareCapsAndWorkflowResult {
|
|
|
162
168
|
current: () => WorkflowSizeGuideline;
|
|
163
169
|
} | undefined;
|
|
164
170
|
}
|
|
171
|
+
/**
|
|
172
|
+
* The resolved-caps value screen (the caps-side twin of `refuseInvalidAutoModeSeat`): the arming arm reads
|
|
173
|
+
* `autoMode !== false`, so a resolver that returns a MARSHALLED value — a non-boolean member (`"false"`,
|
|
174
|
+
* `0`, `null`) or a non-record container (`false`, `"false"`, `[]`; the resolver is an HTTP-backed seam the
|
|
175
|
+
* type system does not validate at runtime) — would pass a value meant as a denial straight into arming.
|
|
176
|
+
* Either shape is treated as a disclosed resolver fault: announced through onError (phase config), and
|
|
177
|
+
* coined fail-closed — a bad MEMBER to `autoMode:false` (the rest of the record kept), a bad CONTAINER to
|
|
178
|
+
* the same degrade object the resolver-throw arm uses (every `!== false`-gated cap denied), with
|
|
179
|
+
* `faulted` raised so the posture-keyed consumers read it as an outage, not as a verdict. `undefined`, or
|
|
180
|
+
* a record whose `autoMode` is `undefined`/`true`/`false`, passes untouched (byte-identical).
|
|
181
|
+
*/
|
|
182
|
+
/** The supported capability keys, fenced total over {@link RuntimeCaps}: a key added to the type without a row here
|
|
183
|
+
* is a red build, so the snapshot below can never silently drop a new restriction. */
|
|
184
|
+
declare const RUNTIME_CAP_KEYS: {
|
|
185
|
+
readonly allowWorkflows: true;
|
|
186
|
+
readonly allowFork: true;
|
|
187
|
+
readonly allowObservers: true;
|
|
188
|
+
readonly forceDurableGate: true;
|
|
189
|
+
readonly autoMode: true;
|
|
190
|
+
readonly autoModeDenySource: true;
|
|
191
|
+
readonly allowMemoryOptOut: true;
|
|
192
|
+
};
|
|
193
|
+
export type RuntimeCapKeysCoverEveryCap = AssertAllKeysHandled<Exclude<keyof RuntimeCaps, keyof typeof RUNTIME_CAP_KEYS>>;
|
|
165
194
|
/** The M4 phase body — prepareTask's caps-and-workflow stretch, verbatim (see the module header). */
|
|
166
195
|
export declare function prepareCapsAndWorkflow(input: PrepareCapsAndWorkflowInput): Promise<PrepareCapsAndWorkflowResult>;
|
|
196
|
+
export {};
|
|
@@ -10,7 +10,7 @@ import { autoModeArmingRecipeOf } from "../auto-mode-arming.js";
|
|
|
10
10
|
import { AUTO_MODE_CLASSIFIER_LOW_REASONING_BUDGET_TOKENS, AUTO_MODE_CLASSIFIER_MAX_TOKENS } from "../auto-mode-defaults.js";
|
|
11
11
|
import { buildAutoModePrompt, renderAutoModeAction, renderAutoModeWindow } from "../auto-mode-prompt.js";
|
|
12
12
|
import { createAutoModeDecider, createAutoModeDenialTracker } from "../auto-mode.js";
|
|
13
|
-
import {
|
|
13
|
+
import { checkpointStoreOfSeat } from "../checkpoint-seat.js";
|
|
14
14
|
import { COMPLIANCE_CAPABILITIES, WEB_FETCH_TOOL_NAME, resolveComplianceDenies } from "../compliance.js";
|
|
15
15
|
import { createPresentPlanTool, createEnterPlanModeTool } from "../present-plan-tool.js";
|
|
16
16
|
import { resolveTaskModel } from "../roles.js";
|
|
@@ -19,6 +19,7 @@ import { defaultTaskRegistry } from "../task-registry.js";
|
|
|
19
19
|
import { emitTrace } from "../trace.js";
|
|
20
20
|
import { defineTool, isDefineToolProduct, rebindDefineToolCtx } from "../tools.js";
|
|
21
21
|
const MOUNT_REMEDY = "author it as a plain-object ToolSpec (own `name` and `execute`), hand the defineTool product itself, or wrap a product through the supported wrapper form (a fresh object re-stamped with `stampDefineToolBrand` whose rebind re-wraps the rebound product)";
|
|
22
|
+
import { AUTO_MODE_DENY_SOURCES, isAutoModeDenySource } from "../runtime-caps.js";
|
|
22
23
|
import { derivedRouteFallsBack } from "./derived-route-fallback.js";
|
|
23
24
|
import { REPORT_FINDINGS_TOOL_NAME, createReportBlockedTool, createReportFindingsTool } from "./synthetic-tools.js";
|
|
24
25
|
function classifierReplyText(reply) {
|
|
@@ -42,6 +43,24 @@ function assembleParentCaptureState(o, i, ctl, ancestors) {
|
|
|
42
43
|
});
|
|
43
44
|
return o instanceof Promise || i instanceof Promise ? Promise.all([o, i]).then(([ov, iv]) => build(ov, iv)) : build(o, i);
|
|
44
45
|
}
|
|
46
|
+
const RUNTIME_CAP_KEYS = {
|
|
47
|
+
allowWorkflows: true,
|
|
48
|
+
allowFork: true,
|
|
49
|
+
allowObservers: true,
|
|
50
|
+
forceDurableGate: true,
|
|
51
|
+
autoMode: true,
|
|
52
|
+
autoModeDenySource: true,
|
|
53
|
+
allowMemoryOptOut: true,
|
|
54
|
+
};
|
|
55
|
+
function snapshotRuntimeCaps(record) {
|
|
56
|
+
const out = {};
|
|
57
|
+
for (const key of Object.keys(RUNTIME_CAP_KEYS)) {
|
|
58
|
+
const v = record[key];
|
|
59
|
+
if (v !== undefined)
|
|
60
|
+
out[key] = v;
|
|
61
|
+
}
|
|
62
|
+
return out;
|
|
63
|
+
}
|
|
45
64
|
function screenAutoModeCap(caps, onError, sessionId) {
|
|
46
65
|
if (caps === undefined)
|
|
47
66
|
return { caps, faulted: false };
|
|
@@ -49,7 +68,20 @@ function screenAutoModeCap(caps, onError, sessionId) {
|
|
|
49
68
|
onError?.(new Error(`runtimeCapsResolver returned a non-record value (${JSON.stringify(caps)}) — read as a resolver fault: every per-principal capability is DENIED for this run (auto mode is not armed; asks flow the original chain). Return a RuntimeCaps object or undefined.`), { phase: "config", sessionId });
|
|
50
69
|
return { caps: { allowWorkflows: false, allowFork: false, autoMode: false }, faulted: true };
|
|
51
70
|
}
|
|
52
|
-
|
|
71
|
+
let record;
|
|
72
|
+
try {
|
|
73
|
+
record = snapshotRuntimeCaps(caps);
|
|
74
|
+
}
|
|
75
|
+
catch (err) {
|
|
76
|
+
onError?.(new Error(`runtimeCapsResolver's record threw while being read (${err instanceof Error ? err.message : String(err)}) — read as a resolver fault: every per-principal capability is DENIED for this run (auto mode is not armed; asks flow the original chain).`), { phase: "config", sessionId });
|
|
77
|
+
return { caps: { allowWorkflows: false, allowFork: false, autoMode: false }, faulted: true };
|
|
78
|
+
}
|
|
79
|
+
const { autoMode, autoModeDenySource } = record;
|
|
80
|
+
if (autoModeDenySource !== undefined && (!isAutoModeDenySource(autoModeDenySource) || autoMode !== false)) {
|
|
81
|
+
onError?.(new Error(`runtimeCapsResolver returned autoModeDenySource=${JSON.stringify(autoModeDenySource)} beside autoMode=${JSON.stringify(autoMode)} — read as a resolver fault: auto mode is DENIED for this run (the classifier is not armed; asks flow the original chain). Name a source only beside autoMode:false, and only one of ${AUTO_MODE_DENY_SOURCES.join("|")}.`), { phase: "config", sessionId });
|
|
82
|
+
const { autoModeDenySource: _refused, ...rest } = record;
|
|
83
|
+
return { caps: { ...rest, autoMode: false }, faulted: true };
|
|
84
|
+
}
|
|
53
85
|
if (record.autoMode === undefined || typeof record.autoMode === "boolean")
|
|
54
86
|
return { caps: record, faulted: false };
|
|
55
87
|
onError?.(new Error(`runtimeCapsResolver returned a non-boolean autoMode (${JSON.stringify(record.autoMode)}) — read as a resolver fault: auto mode is DENIED for this run (the classifier is not armed; asks flow the original chain). Return true, false, or omit the key.`), { phase: "config", sessionId });
|
|
@@ -67,17 +99,20 @@ function mintPersistedArming(am, laneRule, classifierSystemPrompt, onError, sess
|
|
|
67
99
|
}
|
|
68
100
|
return arming;
|
|
69
101
|
}
|
|
70
|
-
function autoModeArmReasonOf(intent, facePresent,
|
|
102
|
+
function autoModeArmReasonOf(intent, facePresent, caps, capsFaulted) {
|
|
71
103
|
if (!intent)
|
|
72
|
-
return "no_intent";
|
|
104
|
+
return { armed: false, reason: "no_intent" };
|
|
73
105
|
if (!facePresent)
|
|
74
|
-
return "no_face";
|
|
75
|
-
if (
|
|
76
|
-
|
|
77
|
-
|
|
106
|
+
return { armed: false, reason: "no_face" };
|
|
107
|
+
if (caps?.autoMode === false) {
|
|
108
|
+
if (capsFaulted)
|
|
109
|
+
return { armed: false, reason: "resolver_fault" };
|
|
110
|
+
return { armed: false, reason: "denied", ...(caps.autoModeDenySource !== undefined ? { deniedSource: caps.autoModeDenySource } : {}) };
|
|
111
|
+
}
|
|
112
|
+
return { armed: true, reason: "armed" };
|
|
78
113
|
}
|
|
79
114
|
export async function prepareCapsAndWorkflow(input) {
|
|
80
|
-
const { spec, deps, internals, session, sessionId, runId, hostTaskId, taskScope, taskRootFinal, model, thinking, lockedPreflight, toolEffects, toolFaceSnapshot, promptProfile, resolvedInteractionPosture, harnessRef, frozenOnAsk, carrierReadFace, forwardEvent, inheritedGateForChildren, enrichSpecToolCtx, maybeOffload, requestReview, enterPlanMode, autoModeIntent, peerLaneActive, peerSendMessageBuiltIn, runnerSelf } = input;
|
|
115
|
+
const { spec, deps, checkpointSeat, internals, session, sessionId, runId, hostTaskId, taskScope, taskRootFinal, model, thinking, lockedPreflight, toolEffects, toolFaceSnapshot, promptProfile, resolvedInteractionPosture, harnessRef, frozenOnAsk, carrierReadFace, forwardEvent, inheritedGateForChildren, enrichSpecToolCtx, maybeOffload, requestReview, enterPlanMode, autoModeIntent, peerLaneActive, peerSendMessageBuiltIn, runnerSelf } = input;
|
|
81
116
|
const roster = new RosterBuilder(toolEffects);
|
|
82
117
|
for (const t of spec.tools ?? []) {
|
|
83
118
|
const mounted = mountCallerTool(t);
|
|
@@ -112,7 +147,7 @@ export async function prepareCapsAndWorkflow(input) {
|
|
|
112
147
|
roster.mount(createReportFindingsTool(), { source: "builtin", mountedBy: "always" });
|
|
113
148
|
}
|
|
114
149
|
if (spec.enablePlanMode === true && spec.interactiveTools !== false) {
|
|
115
|
-
const planReviewFace =
|
|
150
|
+
const planReviewFace = checkpointStoreOfSeat(checkpointSeat) !== undefined;
|
|
116
151
|
if (spec.interactiveTools === true || planReviewFace) {
|
|
117
152
|
roster.mount(defineTool(createPresentPlanTool(requestReview)), { source: "builtin", mountedBy: "optional" });
|
|
118
153
|
if (planReviewFace) {
|
|
@@ -173,7 +208,7 @@ export async function prepareCapsAndWorkflow(input) {
|
|
|
173
208
|
let autoModeDecider;
|
|
174
209
|
let autoModeDenialTracking;
|
|
175
210
|
let autoModeArming;
|
|
176
|
-
const autoModeArmReason = autoModeArmReasonOf(autoModeIntent, deps.autoMode !== undefined, runtimeCaps
|
|
211
|
+
const autoModeArmReason = autoModeArmReasonOf(autoModeIntent, deps.autoMode !== undefined, runtimeCaps, runtimeCapsFaulted);
|
|
177
212
|
if (autoModeIntent && deps.autoMode !== undefined && runtimeCaps?.autoMode !== false) {
|
|
178
213
|
const am = deps.autoMode;
|
|
179
214
|
let classifierModel;
|
|
@@ -297,7 +332,7 @@ export async function prepareCapsAndWorkflow(input) {
|
|
|
297
332
|
},
|
|
298
333
|
...(spec.handsReadOnly === true ? { parentHandsReadOnly: true } : {}), ...(spec.interactiveTools === false ? { parentInteractiveTools: false } : {}),
|
|
299
334
|
onNotice: deps.onNotice,
|
|
300
|
-
|
|
335
|
+
...(checkpointSeat !== undefined ? { parentCheckpointStore: checkpointSeat } : {}),
|
|
301
336
|
parentCenterArtifactDigest: () => input.centerAdoption()?.artifact.artifactDigest,
|
|
302
337
|
parentCenterSourceRevision: () => input.centerAdoption()?.sourceRevision,
|
|
303
338
|
...(forwardEvent ? { forwardEvent } : {}),
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
* gate-machinery block, kept as the block's one guard here); the `tool_result` station keys off the hook record alone, as
|
|
20
20
|
* it always did.
|
|
21
21
|
*
|
|
22
|
-
* Every station this file holds is a `
|
|
22
|
+
* Every station this file holds is a `deniedBy` / settlement WRITE site the decomposition's next slice maps onto one
|
|
23
23
|
* gate outcome; nothing here changes a word, a source or an order — the stations are moved, not re-adjudicated.
|
|
24
24
|
*/
|
|
25
25
|
import type { AgentHarness, AgentTool } from "../../internal/harness.js";
|
|
@@ -3,6 +3,7 @@ import { type ReadFileState } from "../../tools/fs/safety.js";
|
|
|
3
3
|
import type { RunnerDeps, TaskSpec } from "../types.js";
|
|
4
4
|
import type { Model } from "../../internal/llm.js";
|
|
5
5
|
import type { AgentTool, ExecutionEnv, WorkspaceState } from "../../internal/harness.js";
|
|
6
|
+
import type { AdvertisedWritableDirs } from "./advertised-writable-dirs.js";
|
|
6
7
|
import type { StoredSession } from "../session.js";
|
|
7
8
|
import type { IrreversibilityTier, PrepareResume, ReversibilityProbes, RunInternals, ToolFaceSnapshot } from "./contracts.js";
|
|
8
9
|
import { RosterBuilder } from "../tool-roster.js";
|
|
@@ -161,13 +162,18 @@ export interface PrepareHandsReadFaceInput {
|
|
|
161
162
|
* when no file restore ran. Fail-closed by construction — dropping a read record only ever
|
|
162
163
|
* produces refusals. */
|
|
163
164
|
restoredFilePaths?: readonly string[];
|
|
164
|
-
/** borrowed-readonly — (ref cell; the REF itself is never replaced) design/138 S2-C
|
|
165
|
-
*
|
|
166
|
-
*
|
|
167
|
-
* prepareMemory. */
|
|
165
|
+
/** borrowed-readonly — (ref cell; the REF itself is never replaced) design/138 S2-C memory write gate:
|
|
166
|
+
* the band's beforeWrite closure reads `.current` call-time; prepareMemory (driver, BEFORE this phase
|
|
167
|
+
* since #691 — the memory root must be known before the toolkit compiles) installs the gate — the one
|
|
168
|
+
* writer; this phase only reads. Declared in the driver because the SAME ref feeds prepareMemory. */
|
|
168
169
|
memoryWriteGateRef: {
|
|
169
170
|
current?: BeforeWriteHook;
|
|
170
171
|
};
|
|
172
|
+
/** borrowed-mutable — #691: the advertised-writable-directory seat (see its module header for the one
|
|
173
|
+
* rule). Present on the hands leg only (a fence exists). The memory phase, which runs BEFORE this one,
|
|
174
|
+
* has already admitted its write root through it; this phase admits the deployment's scratchpad and
|
|
175
|
+
* folds `roots` into the write-capable containment allowlist before the toolkit compiles. */
|
|
176
|
+
advertisedWritableDirs?: AdvertisedWritableDirs;
|
|
171
177
|
/** borrowed-readonly — the driver's per-tool offload wrapper (design/158 posture + policy); the
|
|
172
178
|
* band rides through it exactly as caller/MCP tools do. */
|
|
173
179
|
firstPartyOffload: (tool: AgentTool) => AgentTool;
|
|
@@ -124,7 +124,7 @@ export function resolveHandsLessReadFace(input) {
|
|
|
124
124
|
}
|
|
125
125
|
export async function prepareHandsMount(input) {
|
|
126
126
|
const envHandToolNames = new Set();
|
|
127
|
-
const { handsEnabled, executionEnv, taskRootFinal, rebaseRestoredPath, resume, session, sessionId, hostTaskId, taskScope, fullShellReachable, effectiveShellGate, toolFaceSnapshot, model, spec, deps, internals, memoryWriteGateRef, firstPartyOffload, roster, egressTools, irreversibilityTier, irreversibleTools, reversibilityProbes } = input;
|
|
127
|
+
const { handsEnabled, executionEnv, taskRootFinal, rebaseRestoredPath, resume, session, sessionId, hostTaskId, taskScope, fullShellReachable, effectiveShellGate, toolFaceSnapshot, model, spec, deps, internals, memoryWriteGateRef, advertisedWritableDirs, firstPartyOffload, roster, egressTools, irreversibilityTier, irreversibleTools, reversibilityProbes } = input;
|
|
128
128
|
assertStringArraySeat(spec.additionalDirectories, "TaskSpec.additionalDirectories", "strings");
|
|
129
129
|
assertStringArraySeat(spec.additionalReadDirectories, "TaskSpec.additionalReadDirectories", "strings");
|
|
130
130
|
assertStringArraySeat(deps.readDenyPatterns, "RunnerDeps.readDenyPatterns", "compiler-owned");
|
|
@@ -178,19 +178,10 @@ export async function prepareHandsMount(input) {
|
|
|
178
178
|
};
|
|
179
179
|
await canonicalizeExtraDirs(spec.additionalDirectories, "additionalDirectories", additionalRootsCanonical);
|
|
180
180
|
await canonicalizeExtraDirs(spec.additionalReadDirectories, "additionalReadDirectories", additionalReadRootsCanonical);
|
|
181
|
-
if (
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
const exclusiveCreate = executionEnv.writeFileExclusive?.bind(executionEnv);
|
|
186
|
-
if (marker.ok && exclusiveCreate) {
|
|
187
|
-
const w = await exclusiveCreate(marker.value, "");
|
|
188
|
-
if (w.ok || w.error.code === "already_exists")
|
|
189
|
-
c = await executionEnv.canonicalPath(spec.envFacts.scratchpadDir);
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
if (c.ok)
|
|
193
|
-
additionalRootsCanonical.push(c.value);
|
|
181
|
+
if (advertisedWritableDirs !== undefined) {
|
|
182
|
+
if (spec.envFacts?.scratchpadDir)
|
|
183
|
+
await advertisedWritableDirs.admit({ path: spec.envFacts.scratchpadDir, advertiser: "scratchpad", materializeMarker: ".sema-scratchpad" });
|
|
184
|
+
additionalRootsCanonical.push(...advertisedWritableDirs.roots);
|
|
194
185
|
}
|
|
195
186
|
for (const a of [additionalRootsCanonical, additionalReadRootsCanonical])
|
|
196
187
|
a.splice(0, a.length, ...new Set(a));
|
|
@@ -51,9 +51,11 @@ export interface PrepareInheritedGateInput {
|
|
|
51
51
|
/** borrowed-readonly — the REBOUND spec. Read: `shellGate` (the own doctrine the chain fold tightens), `durableApproval`
|
|
52
52
|
* (the mandate tests + the children's copy), `onAsk`/`onQuestion` (the frozen seats), `handsReadOnly` / `interactiveTools`
|
|
53
53
|
* / `oneShot` / `clientContext` / `principal` / `additionalDirectories` / `additionalReadDirectories` / `envFacts` /
|
|
54
|
-
* `memoryPersistenceCapable` / `getApiKeyAndHeaders` / `
|
|
55
|
-
*
|
|
56
|
-
spec: Pick<TaskSpec, "shellGate" | "durableApproval" | "onAsk" | "onQuestion" | "handsReadOnly" | "interactiveTools" | "oneShot" | "clientContext" | "principal" | "additionalDirectories" | "additionalReadDirectories" | "envFacts" | "memoryPersistenceCapable" | "getApiKeyAndHeaders" | "
|
|
54
|
+
* `memoryPersistenceCapable` / `getApiKeyAndHeaders` / `backgroundScope` / `enablePlanMode` (the ctx enrichment's
|
|
55
|
+
* trusted seats). Never mutated. */
|
|
56
|
+
spec: Pick<TaskSpec, "shellGate" | "durableApproval" | "onAsk" | "onQuestion" | "handsReadOnly" | "interactiveTools" | "oneShot" | "clientContext" | "principal" | "additionalDirectories" | "additionalReadDirectories" | "envFacts" | "memoryPersistenceCapable" | "getApiKeyAndHeaders" | "backgroundScope" | "enablePlanMode" | "excludeAllTools">;
|
|
57
|
+
/** borrowed-readonly — the seat this run hands its children (`checkpointSeatOf`, minted once by the driver after the config doors): the ctx's `checkpointStoreForChildren`, absent when the run has no seat. */
|
|
58
|
+
checkpointSeat: import("../checkpoint-seat.js").CheckpointSeat | undefined;
|
|
57
59
|
/** borrowed-readonly — the deployment seats: `autoMode.denialLimit` (the rebuilt trackers' bounds), `memoryScopeAdmission` /
|
|
58
60
|
* `deploymentMemoryScopes` / `compliancePostureResolver` (the governed-provenance bit), `onError` (the screening-face crash
|
|
59
61
|
* lane), `onNotice` (the stop seat's notice), `executionEnv` (the hook-env identity predicate), `delegationEntryCaps` (resolved
|
|
@@ -222,7 +222,7 @@ export function prepareInheritedGate(input) {
|
|
|
222
222
|
delegationSettlement: delegationSettlementForChildren,
|
|
223
223
|
...(autoModeDecider ? { autoModeReview: { decider: autoModeDecider } } : {}),
|
|
224
224
|
...(spec.durableApproval !== undefined ? { durableApprovalForChildren: { ...spec.durableApproval } } : {}),
|
|
225
|
-
...(
|
|
225
|
+
...(input.checkpointSeat !== undefined ? { checkpointStoreForChildren: input.checkpointSeat } : {}),
|
|
226
226
|
taskId: hostTaskId,
|
|
227
227
|
...(internals?.parentTaskId !== undefined ? { parentTaskId: internals.parentTaskId } : {}),
|
|
228
228
|
...(internals?.parentSessionId !== undefined ? { parentSessionId: internals.parentSessionId } : {}),
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { BeforeWriteHook, EffectiveMemoryScopes, RunnerDeps, TaskSpec, ToolSpec } from "../types.js";
|
|
2
2
|
import { type CaptureEntitlementInput, type MemoryCapturePosture } from "./memory-capture-optout.js";
|
|
3
3
|
import type { Prepared } from "./contracts.js";
|
|
4
|
+
import type { AdvertisedWritableDirs } from "./advertised-writable-dirs.js";
|
|
4
5
|
export interface PrepareMemoryInput {
|
|
5
6
|
/** borrowed-readonly — the REBOUND spec. Read: `memory` (the scope declaration this phase
|
|
6
7
|
* normalizes) and `principal` (the org-admission subject). Never mutated. */
|
|
@@ -33,23 +34,23 @@ export interface PrepareMemoryInput {
|
|
|
33
34
|
* arms — a hands-less run that mounts its own `Write` counts, and an `excludeTools: ["Write"]`
|
|
34
35
|
* run reads as unmounted whatever the hands band did. Threaded into `engine.inject` so a run with
|
|
35
36
|
* a writable memory scope but no write channel is not instructed to call a tool that is not on
|
|
36
|
-
* its roster (or cannot reach the store); the RB-276 index seed follows the same gate.
|
|
37
|
+
* its roster (or cannot reach the store); the RB-276 index seed follows the same gate. Since #691 this is
|
|
38
|
+
* the FIRST conjunct of the write channel; the second is the root fence's admission of the write plane's
|
|
39
|
+
* root through {@link advertisedWritableDirs} — the channel is open only when both hold.
|
|
37
40
|
*/
|
|
38
41
|
writeToolsMounted: boolean;
|
|
42
|
+
/**
|
|
43
|
+
* borrowed-mutable — #691: the root fence's advertised-writable-directory seat (its module header carries the
|
|
44
|
+
* one rule). Present iff the hands band mounts this run (a fence exists between the roster's `Write` and the
|
|
45
|
+
* store); absent ⇒ a `Write` on the roster is a caller tool running in the host process, which no fence
|
|
46
|
+
* stands before, so admission holds trivially. This phase admits the WRITE plane's root when the channel's
|
|
47
|
+
* first conjunct holds (`writeToolsMounted`) and a write face exists; a read-only layering admits nothing.
|
|
48
|
+
*/
|
|
49
|
+
advertisedWritableDirs?: AdvertisedWritableDirs;
|
|
39
50
|
/** borrowed-readonly — design/319 (A ticket): the run's reminder provenance mark: the memory engine's announcement
|
|
40
51
|
* block is an engine-authored `<system-reminder>`, so its open tag must carry the same mark the
|
|
41
52
|
* system-prompt declaration names. Threaded into every `engine.inject` call this phase builds. */
|
|
42
53
|
reminderMark: string;
|
|
43
|
-
/**
|
|
44
|
-
* borrowed-readonly — the session-wide persistence verdict: `TaskSpec.memoryPersistenceCapable` (the deployment's own
|
|
45
|
-
* statement — the only honest channel for a custom memory writer persisting through its closure,
|
|
46
|
-
* which no inference can see) when set, else the KNOWN-store-path inference: a mounted,
|
|
47
|
-
* non-excluded file-write tool (Write/Edit/NotebookEdit) or a write-capable shell. A generic
|
|
48
|
-
* write-effect tool does NOT count — a mail sender's side effect is not a memory store, and
|
|
49
|
-
* counting it re-opened the silent-confabulation hole. `false` ⇒ this phase mounts
|
|
50
|
-
* {@link MEMORY_READONLY_NOTICE} where the engine is silent.
|
|
51
|
-
*/
|
|
52
|
-
rosterCanPersist: boolean;
|
|
53
54
|
/**
|
|
54
55
|
* borrowed-readonly — the raw `TaskSpec.memoryPersistenceCapable` DECLARATION, kept separate from the composed
|
|
55
56
|
* {@link rosterCanPersist} verdict because the two drive different arms:
|
|
@@ -184,35 +185,31 @@ export interface PrepareMemoryInput {
|
|
|
184
185
|
governedProvenance: boolean;
|
|
185
186
|
};
|
|
186
187
|
}
|
|
187
|
-
|
|
188
|
-
|
|
188
|
+
/** #691 — what {@link PrepareMemoryResult.composeInjection} hands back: the composed `# Memory` block (undefined
|
|
189
|
+
* when nothing mounts), the recall-discipline segment EXACTLY as appended (prepare-task's retraction strips this
|
|
190
|
+
* returned string — #493 单铸), and the RB-276 index seed files whose full disk text rides in the block. */
|
|
191
|
+
export interface MemoryInjectionComposition {
|
|
189
192
|
memoryBlock: string | undefined;
|
|
190
|
-
/**
|
|
191
|
-
* design/178 ②-1 — the `memory_search`/`memory_get` pair's ToolSpecs, present iff the engine
|
|
192
|
-
* session mounted AND `memorySearchToolsPlanned` was true. Built HERE because this phase owns the
|
|
193
|
-
* planes (backend + mounted scopes + per-plane retrieved-account recorder); prepare-task mounts
|
|
194
|
-
* them (atomic pair) and runs the remaining conjuncts.
|
|
195
|
-
*/
|
|
196
|
-
memoryTools?: ToolSpec[];
|
|
197
|
-
/**
|
|
198
|
-
* #493 单铸 — the recall-discipline segment EXACTLY as this phase appended it to
|
|
199
|
-
* {@link memoryBlock} (present iff {@link memoryTools} is). prepare-task's support-name
|
|
200
|
-
* retraction strips THIS string: the segment's composition now depends on the marked-presence
|
|
201
|
-
* signal only this phase holds, so a strip-site re-composition could mint a string that was
|
|
202
|
-
* never appended and leave the real one standing.
|
|
203
|
-
*/
|
|
204
193
|
recallDisciplineSegment?: string;
|
|
205
|
-
/**
|
|
206
|
-
* RB-276 — engine-materialized files whose FULL disk text rides in {@link memoryBlock}, for the
|
|
207
|
-
* caller to pre-mark READ through the parity-204 seeding lane (`prepare-task`'s `seedContextFiles`,
|
|
208
|
-
* which applies the SAME containment as a real Read — a memory dir outside the task root is simply
|
|
209
|
-
* skipped, exactly as the Write tool would refuse it there). Today this is the materialized
|
|
210
|
-
* MEMORY.md index; see `MemoryInjection.indexSeed` for the conditions that qualify it.
|
|
211
|
-
*/
|
|
212
194
|
seedFiles?: ReadonlyArray<{
|
|
213
195
|
path: string;
|
|
214
196
|
content: string;
|
|
215
197
|
}>;
|
|
198
|
+
}
|
|
199
|
+
export interface PrepareMemoryResult {
|
|
200
|
+
memoryEngineSession: Prepared["memoryEngineSession"];
|
|
201
|
+
memoryTools?: ToolSpec[];
|
|
202
|
+
/**
|
|
203
|
+
* owned — #691: the injection's COMPOSITION step, run by the driver AFTER the hands phase (the mount above runs
|
|
204
|
+
* BEFORE it). It performs the one destructive read this phase has — `inject()` drains the announcement queue —
|
|
205
|
+
* so a hands-phase refusal cannot consume announcements a later prepare should deliver, and it takes the
|
|
206
|
+
* roster-wide persistence verdict the driver can only read off the MOUNTED roster (`rosterCanPersist`: the
|
|
207
|
+
* `TaskSpec.memoryPersistenceCapable` declaration when set, else the known-store-path inference over the
|
|
208
|
+
* assembled tools). Call exactly once per prepare.
|
|
209
|
+
*/
|
|
210
|
+
composeInjection: (facts: {
|
|
211
|
+
rosterCanPersist: boolean;
|
|
212
|
+
}) => MemoryInjectionComposition;
|
|
216
213
|
/**
|
|
217
214
|
* The FROZEN org-admission verdict of this prepare: every org scope the task actually mounts
|
|
218
215
|
* (deployment-origin kept + request-origin admitted). Threaded into the run's child chain
|
|
@@ -74,6 +74,7 @@ export async function prepareMemory(input) {
|
|
|
74
74
|
let captureLineageIndeterminate = false;
|
|
75
75
|
let memoryEngineSession;
|
|
76
76
|
let memoryTools;
|
|
77
|
+
let writeChannelOpen = false;
|
|
77
78
|
let memoryMarkedEntriesServed = false;
|
|
78
79
|
let effectiveMemoryScopes;
|
|
79
80
|
const originOf = (scope) => scopeOriginsSnapshot !== undefined
|
|
@@ -373,7 +374,7 @@ export async function prepareMemory(input) {
|
|
|
373
374
|
writeHandle = writeIsPersonal ? personalHandle : projectHandle;
|
|
374
375
|
readOnlyEngine = writeIsPersonal ? projectEngine : personalEngine;
|
|
375
376
|
readOnlyHandle = writeIsPersonal ? projectHandle : personalHandle;
|
|
376
|
-
injectFn = () => mergeInjections(projectEngine.inject(projectHandle, { writeToolMounted:
|
|
377
|
+
injectFn = () => mergeInjections(projectEngine.inject(projectHandle, { writeToolMounted: writeChannelOpen, reminderMark: input.reminderMark }), personalEngine.inject(personalHandle, { writeToolMounted: writeChannelOpen, reminderMark: input.reminderMark }));
|
|
377
378
|
toolPlanes = [
|
|
378
379
|
{
|
|
379
380
|
backend: retrievalBackend(backend, adoptionRestricted || captureOptedOut || captureIndeterminate || p.writePlane !== "project"),
|
|
@@ -417,7 +418,7 @@ export async function prepareMemory(input) {
|
|
|
417
418
|
memoryMarkedEntriesServed = handle.markedEntriesPresent === true;
|
|
418
419
|
writeEngine = personalEngine;
|
|
419
420
|
writeHandle = handle;
|
|
420
|
-
injectFn = () => personalEngine.inject(handle, { writeToolMounted:
|
|
421
|
+
injectFn = () => personalEngine.inject(handle, { writeToolMounted: writeChannelOpen, reminderMark: input.reminderMark });
|
|
421
422
|
harvestBoth = async () => personalEngine.harvest(handle, { ...pollutedOpts(personalEngine), sessionId, ...(await admitNothingOptsNow()) });
|
|
422
423
|
toolPlanes = [
|
|
423
424
|
{
|
|
@@ -445,7 +446,7 @@ export async function prepareMemory(input) {
|
|
|
445
446
|
memoryMarkedEntriesServed = handle.markedEntriesPresent === true;
|
|
446
447
|
writeEngine = engine;
|
|
447
448
|
writeHandle = handle;
|
|
448
|
-
injectFn = () => engine.inject(handle, { writeToolMounted:
|
|
449
|
+
injectFn = () => engine.inject(handle, { writeToolMounted: writeChannelOpen, reminderMark: input.reminderMark });
|
|
449
450
|
harvestBoth = async () => engine.harvest(handle, { ...pollutedOpts(engine), sessionId, ...(await admitNothingOptsNow()) });
|
|
450
451
|
toolPlanes = [
|
|
451
452
|
{
|
|
@@ -457,6 +458,22 @@ export async function prepareMemory(input) {
|
|
|
457
458
|
},
|
|
458
459
|
];
|
|
459
460
|
}
|
|
461
|
+
const foreignUnderRoot = [];
|
|
462
|
+
if (input.writeToolsMounted && writeHandle.writeScope !== null && input.advertisedWritableDirs !== undefined) {
|
|
463
|
+
for (const d of [...writeEngine.readonlyDirNamesUnderRoot(writeHandle)].sort())
|
|
464
|
+
foreignUnderRoot.push(`another scope's directory ${d}`);
|
|
465
|
+
const controlPlane = canonicalize(writeEngine.controlPlaneDir);
|
|
466
|
+
if (isContainedIn(writeHandle.writableRoot, controlPlane))
|
|
467
|
+
foreignUnderRoot.push(`the engine's control plane ${controlPlane}`);
|
|
468
|
+
}
|
|
469
|
+
if (foreignUnderRoot.length > 0) {
|
|
470
|
+
deps.onError?.(new Error(`memory advertised writable directory refused: the write scope's root ${writeHandle.writableRoot} contains what is not the model's to touch (${foreignUnderRoot.join("; ")}) — the file tools' root fence is directory-granular, so admitting it would expose those files; memory writes through the file tools stay closed this session and the write instruction is not taught.`), { phase: "config", sessionId });
|
|
471
|
+
}
|
|
472
|
+
writeChannelOpen =
|
|
473
|
+
input.writeToolsMounted &&
|
|
474
|
+
writeHandle.writeScope !== null &&
|
|
475
|
+
foreignUnderRoot.length === 0 &&
|
|
476
|
+
(input.advertisedWritableDirs === undefined || (await input.advertisedWritableDirs.admit({ path: writeHandle.writableRoot, advertiser: "memory", expectCanonical: writeHandle.writableRoot })));
|
|
460
477
|
memoryWriteGateRef.current = async (w) => {
|
|
461
478
|
if (readOnlyEngine !== undefined && readOnlyHandle !== undefined) {
|
|
462
479
|
const ro = readOnlyEngine.gateWrite(readOnlyHandle, w.key, w.content);
|
|
@@ -690,56 +707,63 @@ export async function prepareMemory(input) {
|
|
|
690
707
|
}
|
|
691
708
|
}
|
|
692
709
|
}
|
|
693
|
-
let memoryBlock;
|
|
694
|
-
let seedFiles;
|
|
695
|
-
let recallDisciplineSegment;
|
|
696
710
|
if (memorySpec && memorySpec.enabled && !useMemoryEngine) {
|
|
697
711
|
const detail = "TaskSpec.memory is enabled but RunnerDeps.memoryBackend is not configured; memory is inactive for this task (the legacy memoryStore fallback was retired in design/138 S4 and the dep was removed in design/157 B19).";
|
|
698
712
|
deps.onError?.(new Error(detail), { phase: "config", sessionId });
|
|
699
713
|
}
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
let
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
(
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
714
|
+
const composeInjection = (facts) => {
|
|
715
|
+
let memoryBlock;
|
|
716
|
+
let seedFiles;
|
|
717
|
+
let recallDisciplineSegment;
|
|
718
|
+
if (memoryEngineSession) {
|
|
719
|
+
const injection = memoryEngineSession.inject();
|
|
720
|
+
let blockBody = injection.block;
|
|
721
|
+
if (captureOptedOut) {
|
|
722
|
+
blockBody = [MEMORY_CAPTURE_OPTOUT_NOTICE, injection.index, injection.announceBlock].filter((s) => Boolean(s && s.trim())).join("\n\n");
|
|
723
|
+
}
|
|
724
|
+
else if (captureIndeterminate && facts.rosterCanPersist) {
|
|
725
|
+
blockBody = [MEMORY_CAPTURE_INDETERMINATE_NOTICE, injection.index, injection.announceBlock].filter((s) => Boolean(s && s.trim())).join("\n\n");
|
|
726
|
+
}
|
|
727
|
+
else if (!facts.rosterCanPersist && injection.instruction === "" && injection.readOnlyNotice === undefined) {
|
|
728
|
+
blockBody = blockBody.trim() ? `${MEMORY_READONLY_NOTICE}\n\n${blockBody}` : MEMORY_READONLY_NOTICE;
|
|
729
|
+
}
|
|
730
|
+
else if (!facts.rosterCanPersist && injection.instruction !== "") {
|
|
731
|
+
blockBody = [MEMORY_READONLY_NOTICE, injection.index, injection.announceBlock].filter((s) => Boolean(s && s.trim())).join("\n\n");
|
|
732
|
+
}
|
|
733
|
+
else if (input.memoryPersistenceDeclared === true && injection.readOnlyNotice !== undefined) {
|
|
734
|
+
blockBody = [injection.index, injection.announceBlock].filter((s) => Boolean(s && s.trim())).join("\n\n");
|
|
735
|
+
}
|
|
736
|
+
if (!facts.rosterCanPersist &&
|
|
737
|
+
(injection.announceBlock?.trim() ?? "") !== "" &&
|
|
738
|
+
blockBody.includes(injection.announceBlock) &&
|
|
739
|
+
!blockBody.trimEnd().endsWith(MEMORY_ANNOUNCEMENT_READONLY_CODA)) {
|
|
740
|
+
blockBody = `${blockBody}\n\n${MEMORY_ANNOUNCEMENT_READONLY_CODA}`;
|
|
741
|
+
}
|
|
742
|
+
if (captureOptedOut &&
|
|
743
|
+
facts.rosterCanPersist &&
|
|
744
|
+
(injection.announceBlock?.trim() ?? "") !== "" &&
|
|
745
|
+
blockBody.includes(injection.announceBlock) &&
|
|
746
|
+
!blockBody.trimEnd().endsWith(MEMORY_ANNOUNCEMENT_CAPTURE_OPTOUT_CODA)) {
|
|
747
|
+
blockBody = `${blockBody}\n\n${MEMORY_ANNOUNCEMENT_CAPTURE_OPTOUT_CODA}`;
|
|
748
|
+
}
|
|
749
|
+
if (blockBody.trim())
|
|
750
|
+
memoryBlock = blockBody;
|
|
751
|
+
if (memoryTools !== undefined) {
|
|
752
|
+
recallDisciplineSegment = memoryRecallDisciplineSegment(input.memoryProvenance, { markedEntriesServed: memoryMarkedEntriesServed });
|
|
753
|
+
memoryBlock = memoryBlock !== undefined ? `${memoryBlock}\n\n${recallDisciplineSegment}` : recallDisciplineSegment;
|
|
754
|
+
}
|
|
755
|
+
if (memoryEngineSession.handle.writeScope !== null && writeChannelOpen && facts.rosterCanPersist && !captureOptedOut && !captureIndeterminate) {
|
|
756
|
+
memoryBlock = memoryBlock !== undefined ? `${memoryBlock}\n\n${MEMORY_PREFERENCE_DISCIPLINE}` : MEMORY_PREFERENCE_DISCIPLINE;
|
|
757
|
+
}
|
|
758
|
+
if (memoryBlock !== undefined && injection.indexSeed !== undefined && facts.rosterCanPersist && !captureOptedOut && !captureIndeterminate)
|
|
759
|
+
seedFiles = [injection.indexSeed];
|
|
739
760
|
}
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
761
|
+
return {
|
|
762
|
+
memoryBlock,
|
|
763
|
+
...(recallDisciplineSegment !== undefined ? { recallDisciplineSegment } : {}),
|
|
764
|
+
...(seedFiles !== undefined ? { seedFiles } : {}),
|
|
765
|
+
};
|
|
766
|
+
};
|
|
743
767
|
if (effectiveMemoryScopes === undefined) {
|
|
744
768
|
effectiveMemoryScopes = memorySpec
|
|
745
769
|
? memorySpec.enabled
|
|
@@ -749,12 +773,10 @@ export async function prepareMemory(input) {
|
|
|
749
773
|
}
|
|
750
774
|
return {
|
|
751
775
|
memoryEngineSession,
|
|
752
|
-
memoryBlock,
|
|
753
776
|
admittedOrgScopes,
|
|
754
777
|
ownOrgVerdict,
|
|
755
778
|
effectiveMemoryScopes,
|
|
756
779
|
...(memoryTools !== undefined ? { memoryTools } : {}),
|
|
757
|
-
|
|
758
|
-
...(seedFiles !== undefined ? { seedFiles } : {}),
|
|
780
|
+
composeInjection,
|
|
759
781
|
};
|
|
760
782
|
}
|