@sema-agent/core 7.13.0 → 7.15.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 +110 -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 +30 -11
- package/dist/core/ask-origin.js +10 -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 +23 -31
- package/dist/core/checkpoint-store.js +7 -4
- package/dist/core/engine-notice.d.ts +3 -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.js +21 -12
- package/dist/core/gate-outcome.d.ts +42 -3
- package/dist/core/gate-outcome.js +22 -0
- package/dist/core/governance-codes.js +1 -1
- package/dist/core/hooks.d.ts +9 -19
- package/dist/core/hooks.js +6 -4
- 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 +5 -0
- package/dist/core/runner/contracts.d.ts +2 -2
- package/dist/core/runner/denial-limit-arms.d.ts +7 -9
- package/dist/core/runner/denial-limit-arms.js +8 -10
- 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 +3 -1
- package/dist/core/runner/permission-rule-lanes.js +1 -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-defer-classify.d.ts +1 -1
- package/dist/core/runner/prepare-defer-classify.js +12 -10
- package/dist/core/runner/prepare-gate-stations.d.ts +4 -5
- package/dist/core/runner/prepare-gate-stations.js +2 -2
- package/dist/core/runner/prepare-hands-readface.d.ts +18 -12
- package/dist/core/runner/prepare-hands-readface.js +32 -43
- package/dist/core/runner/prepare-inherited-gate.d.ts +7 -5
- 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-park-ask.d.ts +5 -6
- package/dist/core/runner/prepare-park-ask.js +3 -3
- package/dist/core/runner/prepare-policy-chain.d.ts +2 -2
- package/dist/core/runner/prepare-policy-chain.js +7 -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-question-face.d.ts +10 -8
- package/dist/core/runner/prepare-question-face.js +1 -3
- package/dist/core/runner/prepare-safety-scan.js +1 -1
- package/dist/core/runner/prepare-task.js +60 -53
- 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/run-compaction-machinery.js +2 -0
- package/dist/core/runner/run-harness-handlers.js +3 -1
- package/dist/core/runner/stream-settle-backstop.js +1 -1
- package/dist/core/runner/tool-defer-gate.d.ts +86 -0
- package/dist/core/runner/tool-defer-gate.js +57 -0
- package/dist/core/runner/tool-disclosure.d.ts +0 -36
- package/dist/core/runner/tool-disclosure.js +0 -43
- package/dist/core/runner-deps.d.ts +16 -9
- 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-spec.d.ts +8 -2
- package/dist/core/terminal-cause.d.ts +6 -2
- package/dist/core/tool-policy.d.ts +34 -28
- package/dist/core/tool-policy.js +29 -5
- package/dist/core/tool-roster.js +2 -0
- package/dist/core/tool-spec.d.ts +20 -16
- package/dist/core/types.d.ts +3 -2
- 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 +7 -6
- package/dist/index.js +5 -4
- 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/tools/fs/fs-bash.d.ts +41 -0
- package/dist/tools/fs/fs-bash.js +86 -29
- 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 +29 -1
|
@@ -2,7 +2,7 @@ import { emitTrace } from "../trace.js";
|
|
|
2
2
|
import { StubExecutionEnv } from "../stub-env.js";
|
|
3
3
|
import { hasBackgroundShell } from "../background-shell.js";
|
|
4
4
|
import { defaultTaskRegistry } from "../task-registry.js";
|
|
5
|
-
import { bashReversibilityProbe, compileReadDeny, createHandsToolkit, deploymentReadFaceClampNotice, pdfModelCapabilitiesOf, resolveReadDenyBuiltins, inheritSessionReadFileState, resolveReadFace, seedReadFileStateFromContext, seedReadFileStateFromTranscript, } from "../../tools/fs/index.js";
|
|
5
|
+
import { bashReadBoundaryProbe, bashReversibilityProbe, compileReadDeny, createHandsToolkit, deploymentReadFaceClampNotice, pdfModelCapabilitiesOf, resolveReadDenyBuiltins, inheritSessionReadFileState, resolveReadFace, seedReadFileStateFromContext, seedReadFileStateFromTranscript, } from "../../tools/fs/index.js";
|
|
6
6
|
import { resolveKey, sha256 } from "../../tools/fs/safety.js";
|
|
7
7
|
import { normalizeFileText } from "../../tools/fs/encoding.js";
|
|
8
8
|
import { wholeFileRecordsFromTranscript } from "./session-file-state-replay.js";
|
|
@@ -117,14 +117,13 @@ export function resolveHandsLessReadFace(input) {
|
|
|
117
117
|
attachmentRootCanonical: undefined,
|
|
118
118
|
readDenyMatcher: undefined,
|
|
119
119
|
envHandToolNames: new Set(),
|
|
120
|
-
|
|
121
|
-
shellGatedMonitor: false,
|
|
120
|
+
shellGatedTools: new Set(),
|
|
122
121
|
sealReadStateSeat: undefined,
|
|
123
122
|
};
|
|
124
123
|
}
|
|
125
124
|
export async function prepareHandsMount(input) {
|
|
126
125
|
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;
|
|
126
|
+
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
127
|
assertStringArraySeat(spec.additionalDirectories, "TaskSpec.additionalDirectories", "strings");
|
|
129
128
|
assertStringArraySeat(spec.additionalReadDirectories, "TaskSpec.additionalReadDirectories", "strings");
|
|
130
129
|
assertStringArraySeat(deps.readDenyPatterns, "RunnerDeps.readDenyPatterns", "compiler-owned");
|
|
@@ -140,8 +139,7 @@ export async function prepareHandsMount(input) {
|
|
|
140
139
|
let resolvedReadFace;
|
|
141
140
|
let readDenyAdditionsNormalized = [];
|
|
142
141
|
let handsLessResolvedFace;
|
|
143
|
-
|
|
144
|
-
let shellGatedMonitor = false;
|
|
142
|
+
const shellGatedTools = new Set();
|
|
145
143
|
const additionalRootsCanonical = [];
|
|
146
144
|
const additionalReadRootsCanonical = [];
|
|
147
145
|
let attachmentRootCanonical;
|
|
@@ -178,19 +176,10 @@ export async function prepareHandsMount(input) {
|
|
|
178
176
|
};
|
|
179
177
|
await canonicalizeExtraDirs(spec.additionalDirectories, "additionalDirectories", additionalRootsCanonical);
|
|
180
178
|
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);
|
|
179
|
+
if (advertisedWritableDirs !== undefined) {
|
|
180
|
+
if (spec.envFacts?.scratchpadDir)
|
|
181
|
+
await advertisedWritableDirs.admit({ path: spec.envFacts.scratchpadDir, advertiser: "scratchpad", materializeMarker: ".sema-scratchpad" });
|
|
182
|
+
additionalRootsCanonical.push(...advertisedWritableDirs.roots);
|
|
194
183
|
}
|
|
195
184
|
for (const a of [additionalRootsCanonical, additionalReadRootsCanonical])
|
|
196
185
|
a.splice(0, a.length, ...new Set(a));
|
|
@@ -354,34 +343,35 @@ export async function prepareHandsMount(input) {
|
|
|
354
343
|
}
|
|
355
344
|
roster.mountAll(band.map((t) => (envHandToolNames.add(t.name), firstPartyOffload(t))), { source: "builtin", mountedBy: "hands" });
|
|
356
345
|
const shellGate = effectiveShellGate;
|
|
357
|
-
if (
|
|
358
|
-
|
|
359
|
-
`
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
const bashEffectiveTier = shellGate === "always" || bashTierBefore === "always" ? "always" : "maybe";
|
|
366
|
-
irreversibilityTier.set("Bash", bashEffectiveTier);
|
|
367
|
-
irreversibleTools.add("Bash");
|
|
346
|
+
if (!(executionEnv instanceof StubExecutionEnv) && spec.handsReadOnly !== true) {
|
|
347
|
+
if (shellGate === "off") {
|
|
348
|
+
deps.onError?.(new Error(`shell gate doctrine is "off" while a real writable shell (Bash) is mounted — the read boundary is ` +
|
|
349
|
+
`still judged on every command (a deny-listed or out-of-root read asks; the ask is mandated), but no ` +
|
|
350
|
+
`residual shell-risk classifier is installed: writes, egress and unknown commands are adjudicated by ` +
|
|
351
|
+
`the ordinary policy/hook chain only. Set spec.shellGate to "classify" or "always" if this deployment ` +
|
|
352
|
+
`expects doctrine-gated shell behavior.`), { phase: "config", sessionId, classification: "shell-gate-off" });
|
|
353
|
+
}
|
|
368
354
|
const shellReadBoundary = () => ({
|
|
369
355
|
roots: [rootCanonical, ...additionalRootsCanonical, ...additionalReadRootsCanonical],
|
|
370
356
|
...(handsCwdRef?.current !== undefined ? { cwd: handsCwdRef.current } : {}),
|
|
371
357
|
denyMatch: (p) => readDenyMatcher?.matchPath(p)?.pattern ?? null,
|
|
372
358
|
...(resolvedReadFace !== undefined ? { face: resolvedReadFace } : {}),
|
|
373
359
|
});
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
const
|
|
380
|
-
irreversibilityTier.set(
|
|
381
|
-
irreversibleTools.add(
|
|
382
|
-
if (
|
|
383
|
-
reversibilityProbes.set("
|
|
384
|
-
|
|
360
|
+
const installShellSeat = (name) => {
|
|
361
|
+
const tierBefore = irreversibilityTier.get(name);
|
|
362
|
+
const seatOwned = !egressTools.has(name) && tierBefore !== "always" && tierBefore !== "maybe";
|
|
363
|
+
if (seatOwned)
|
|
364
|
+
shellGatedTools.add(name);
|
|
365
|
+
const tier = shellGate === "always" || tierBefore === "always" ? "always" : "maybe";
|
|
366
|
+
irreversibilityTier.set(name, tier);
|
|
367
|
+
irreversibleTools.add(name);
|
|
368
|
+
if (seatOwned && tier === "maybe") {
|
|
369
|
+
reversibilityProbes.set(name, shellGate === "classify" ? bashReversibilityProbe(undefined, shellReadBoundary) : bashReadBoundaryProbe(shellReadBoundary));
|
|
370
|
+
}
|
|
371
|
+
};
|
|
372
|
+
installShellSeat("Bash");
|
|
373
|
+
if (backgroundTaskToolsActive)
|
|
374
|
+
installShellSeat("Monitor");
|
|
385
375
|
}
|
|
386
376
|
}
|
|
387
377
|
return {
|
|
@@ -400,8 +390,7 @@ export async function prepareHandsMount(input) {
|
|
|
400
390
|
attachmentRootCanonical,
|
|
401
391
|
readDenyMatcher,
|
|
402
392
|
envHandToolNames,
|
|
403
|
-
|
|
404
|
-
shellGatedMonitor,
|
|
393
|
+
shellGatedTools,
|
|
405
394
|
sealReadStateSeat,
|
|
406
395
|
};
|
|
407
396
|
}
|
|
@@ -43,7 +43,7 @@ import type { DelegationContentSafety } from "../memory-engine/delegation-proven
|
|
|
43
43
|
import type { SessionPermissionRules } from "../session-policy-store.js";
|
|
44
44
|
import type { SessionStore } from "../session.js";
|
|
45
45
|
import { type OnAsk } from "../tool-policy.js";
|
|
46
|
-
import type { RunnerDeps, RuntimeCaps, TaskEvent, TaskSpec, ToolExecuteContext } from "../types.js";
|
|
46
|
+
import type { RunnerDeps, RuntimeCaps, ShellGateDoctrine, TaskEvent, TaskSpec, ToolExecuteContext } from "../types.js";
|
|
47
47
|
import type { ActiveSkillScope } from "./active-skill-scope.js";
|
|
48
48
|
import type { InheritedGate, Prepared, PrepareResume, RunInternals, ToolFaceSnapshot } from "./contracts.js";
|
|
49
49
|
import { type StopForDenialLimit } from "./denial-limit-arms.js";
|
|
@@ -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
|
|
@@ -192,7 +194,7 @@ export interface PrepareInheritedGateResult {
|
|
|
192
194
|
/** owned — the monotonic governed-provenance bit for this leg. */
|
|
193
195
|
orgGovernedProvenance: boolean;
|
|
194
196
|
/** owned — the spec's own shellGate tightened by the inherited doctrine. */
|
|
195
|
-
effectiveShellGate:
|
|
197
|
+
effectiveShellGate: ShellGateDoctrine;
|
|
196
198
|
/** borrowed-mutable — THIS task's own session-rule snapshot for its children's chain. Writers (R-3): the policy-chain
|
|
197
199
|
* station's session-policy read, once per leg, when a store is wired and the task carries its own sessionId. Read by
|
|
198
200
|
* `inheritedGateForChildren` at spawn time. */
|
|
@@ -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
|
}
|
|
@@ -25,7 +25,7 @@ import { type CheckpointStore } from "../checkpoint-store.js";
|
|
|
25
25
|
import type { SessionStore } from "../session.js";
|
|
26
26
|
import { type OnAsk, type ToolCallRequest, type ToolPolicy } from "../tool-policy.js";
|
|
27
27
|
import { type ToolResultStore } from "../tool-result-store.js";
|
|
28
|
-
import type { RunnerDeps, RuntimeCaps, TaskSpec } from "../types.js";
|
|
28
|
+
import type { RunnerDeps, RuntimeCaps, ShellGateDoctrine, TaskSpec } from "../types.js";
|
|
29
29
|
import type { CwdRef } from "../../tools/fs/fs-shared.js";
|
|
30
30
|
import { type ContentAskBinding } from "./content-ask-bindings.js";
|
|
31
31
|
import type { AskLane, ParkAsk, Prepared, SuspendSaga } from "./contracts.js";
|
|
@@ -108,12 +108,11 @@ export interface PrepareParkAskInput {
|
|
|
108
108
|
maxSuspends: number;
|
|
109
109
|
/** borrowed-mutable — the run-scoped remote-lifecycle failure log. Writer here: a refused `suspendVM` (`push`). */
|
|
110
110
|
remoteEnvFailures: Prepared["remoteEnvFailures"];
|
|
111
|
-
/** borrowed-readonly —
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
shellGatedMonitor: boolean;
|
|
111
|
+
/** borrowed-readonly — the shell-seat provenance set: the shell-running tools whose tier is the engine's shell seat's, not a
|
|
112
|
+
* deployment mark (the risk descriptor's `shellGated` is the gated tool's membership). */
|
|
113
|
+
shellGatedTools: ReadonlySet<string>;
|
|
115
114
|
/** borrowed-readonly — the effective shell-gate doctrine persisted on the descriptor. */
|
|
116
|
-
effectiveShellGate:
|
|
115
|
+
effectiveShellGate: ShellGateDoctrine;
|
|
117
116
|
/** borrowed-readonly — the EFFECTIVE durable-approval opt-in (the caller's, or the synthesized mandate scope): picks the
|
|
118
117
|
* gate kind, the scope and the ttl. */
|
|
119
118
|
durableApproval: TaskSpec["durableApproval"];
|
|
@@ -22,7 +22,7 @@ class ParkRefusal extends Error {
|
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
24
|
export function prepareParkAsk(input) {
|
|
25
|
-
const { askLane, saga, spec, deps, sessionId, checkpointStore, toolRosterDeltas, parkLaneArmed, contentAskRoutable, liveQuestionFace, mountedQuestionTool, contentAskBindings, lateStrandedAnswers, discloseStrandedAnswers, onAsk, runtimeCaps, inheritedUnavailableAsks, basePolicyForResumeEdit, budgetSnapshot, handsCwdRef, offloadStore, ownedEnv, incompleteSuspendAdapter, session, sessions, suspendChainBase, maxSuspends, remoteEnvFailures,
|
|
25
|
+
const { askLane, saga, spec, deps, sessionId, checkpointStore, toolRosterDeltas, parkLaneArmed, contentAskRoutable, liveQuestionFace, mountedQuestionTool, contentAskBindings, lateStrandedAnswers, discloseStrandedAnswers, onAsk, runtimeCaps, inheritedUnavailableAsks, basePolicyForResumeEdit, budgetSnapshot, handsCwdRef, offloadStore, ownedEnv, incompleteSuspendAdapter, session, sessions, suspendChainBase, maxSuspends, remoteEnvFailures, shellGatedTools, effectiveShellGate, durableApproval, priorLedger, liveSpendRef, resourceTotal, faceCheckpointState, f012CheckpointState, orgAdmissionCheckpointState, ruleOffersOf, now, humanReviewRef, abortController, harness, pausedRef } = input;
|
|
26
26
|
if (askLane === undefined || saga === undefined)
|
|
27
27
|
return { parkAsk: undefined };
|
|
28
28
|
const { composedCallSignal, approvalPreviewOf } = askLane;
|
|
@@ -232,8 +232,8 @@ export function prepareParkAsk(input) {
|
|
|
232
232
|
...(shadowedRule !== undefined ? { shadowedRule } : {}),
|
|
233
233
|
...(probeReason !== undefined ? { probeReason } : {}),
|
|
234
234
|
...(probeCause !== undefined ? { probeCause } : {}),
|
|
235
|
-
shellGated:
|
|
236
|
-
|
|
235
|
+
shellGated: shellGatedTools.has(req.toolName),
|
|
236
|
+
shellGateDoctrine: effectiveShellGate,
|
|
237
237
|
});
|
|
238
238
|
gate =
|
|
239
239
|
safety !== undefined || realApproval !== undefined
|
|
@@ -78,8 +78,8 @@ export interface PreparePolicyChainInput {
|
|
|
78
78
|
/** borrowed-readonly — the caller tools' names, the first-await snapshot (the sandbox boundary predicate: a caller tool
|
|
79
79
|
* is never contained by the hand tools' env). */
|
|
80
80
|
ownToolNames: ReadonlySet<string>;
|
|
81
|
-
/** borrowed-readonly —
|
|
82
|
-
|
|
81
|
+
/** borrowed-readonly — the shell-seat provenance set (the offer factory's `shellGated` mandate term, read per call). */
|
|
82
|
+
shellGatedTools: ReadonlySet<string>;
|
|
83
83
|
/** borrowed-readonly — the tracked-cwd seat, read per call by the lanes and the offer factory (never written here). */
|
|
84
84
|
handsCwdRef: CwdRef | undefined;
|
|
85
85
|
/** borrowed-readonly — the compiled read-deny judge, or undefined (the offer factory's directory negative control). */
|
|
@@ -5,7 +5,7 @@ import { resolveCheckpointStore } from "../checkpoint-store.js";
|
|
|
5
5
|
import { cloneObserverInput, resolveHookTimeoutMs } from "../hooks.js";
|
|
6
6
|
import { isNamespacedCoveringRuleName, namespacedRuleNameCovers } from "../permission-rules.js";
|
|
7
7
|
import { isIsolated } from "../remote-env.js";
|
|
8
|
-
import { askApproverIdentity, checkToolPolicyProjection, combinePolicies, createTranscriptIntegrityPolicy, createUnverifiableDeletePolicy, isLiveApproverSeat, resolveAsk, toolPolicyNameSets } from "../tool-policy.js";
|
|
8
|
+
import { askApproverIdentity, carryEngineAttestations, carryPrecedingSettlement, checkToolPolicyProjection, combinePolicies, createTranscriptIntegrityPolicy, createUnverifiableDeletePolicy, isLiveApproverSeat, resolveAsk, toolPolicyNameSets } from "../tool-policy.js";
|
|
9
9
|
import { RETIRED_TOOL_NAMES } from "../tool-name-aliases.js";
|
|
10
10
|
import { NAMESPACED_NAME_SHAPES, protocolOf } from "../protocol-table.js";
|
|
11
11
|
import { emitTrace } from "../trace.js";
|
|
@@ -16,7 +16,7 @@ import { askGrantShapeOf } from "./inherited-ask-grants.js";
|
|
|
16
16
|
import { createPermissionRuleLanes, inheritedAskRuleEvidence, createRuleOffersOf } from "./permission-rule-lanes.js";
|
|
17
17
|
import { createSessionRulePolicy } from "./session-rule-policy.js";
|
|
18
18
|
export async function preparePolicyChain(input) {
|
|
19
|
-
const { spec, deps, internals, sessionId, runId, hostTaskId, taskRootFinal, executionEnv, lockedPreflight, tools, harnessTools, toolEffects, egressTools, irreversibilityTier, axisExplicitNegatives, ownToolNames,
|
|
19
|
+
const { spec, deps, internals, sessionId, runId, hostTaskId, taskRootFinal, executionEnv, lockedPreflight, tools, harnessTools, toolEffects, egressTools, irreversibilityTier, axisExplicitNegatives, ownToolNames, shellGatedTools, handsCwdRef, readDenyMatcher, questionToolMounted, runtimeCaps, inheritedAncestorRules, inheritedParentConstraints, seedInheritedGate, hasSkillManifest, skillScope, frozenOnAsk, hookEnvSource, notifyOwnHookCrash, autoModeDecider, stopForDenialLimit, delegation, abortController, now, ownSessionRulesRef } = input;
|
|
20
20
|
const policy = lockedPreflight.toolPolicy;
|
|
21
21
|
const auditPolicyNames = (auditee) => {
|
|
22
22
|
const nameGroups = toolPolicyNameSets(auditee);
|
|
@@ -265,7 +265,7 @@ export async function preparePolicyChain(input) {
|
|
|
265
265
|
localOwnerDeclared: deps.localOwnerRules === true,
|
|
266
266
|
egressTools,
|
|
267
267
|
irreversibilityTier,
|
|
268
|
-
|
|
268
|
+
shellGatedTools,
|
|
269
269
|
taskRoot: taskRootFinal,
|
|
270
270
|
cwdRef: handsCwdRef,
|
|
271
271
|
deniesDirectoryRead: readDenyMatcher !== undefined ? (d) => readDenyMatcher.matchPath(d) !== null : undefined,
|
|
@@ -557,7 +557,10 @@ export async function preparePolicyChain(input) {
|
|
|
557
557
|
for (const layer of recheckLayers) {
|
|
558
558
|
const d = await layer.check(finalReq, signal);
|
|
559
559
|
if (d.action === "deny") {
|
|
560
|
-
|
|
560
|
+
const carried = { ...d, updatedInput: decision.updatedInput };
|
|
561
|
+
carryEngineAttestations(d, carried);
|
|
562
|
+
carryPrecedingSettlement(decision, carried);
|
|
563
|
+
return carried;
|
|
561
564
|
}
|
|
562
565
|
if (d.updatedInput !== undefined) {
|
|
563
566
|
return {
|
|
@@ -28,6 +28,7 @@ import type { MaterializedMcp } from "../mcp.js";
|
|
|
28
28
|
import type { ResolvedRole } from "../roles.js";
|
|
29
29
|
import type { RunnerDeps, TaskSpec } from "../types.js";
|
|
30
30
|
import type { PrepareResume, PromptFeatureFlags, RunInternals, ToolFaceSnapshot } from "./contracts.js";
|
|
31
|
+
import type { AdvertisedWritableDirs } from "./advertised-writable-dirs.js";
|
|
31
32
|
import { type GitStatusLaneRef } from "./git-status-frame.js";
|
|
32
33
|
export interface PreparePromptInputsInput {
|
|
33
34
|
/** borrowed-readonly — the REBOUND spec (`spec′`). Read: `promptProvider`, `hooks` (flag), `systemPrompt`
|
|
@@ -90,6 +91,9 @@ export interface PreparePromptInputsInput {
|
|
|
90
91
|
additionalRootsCanonical: readonly string[];
|
|
91
92
|
/** borrowed-readonly — the canonical extra read-only roots (copied into the env facts). */
|
|
92
93
|
additionalReadRootsCanonical: readonly string[];
|
|
94
|
+
/** borrowed-readonly — #691: the advertised-writable-directory seat (hands only). The scratchpad section is
|
|
95
|
+
* rendered only when the seat did not SKIP the scratchpad — a directory the fence refused is not advertised. */
|
|
96
|
+
advertisedWritableDirs?: AdvertisedWritableDirs;
|
|
93
97
|
/** borrowed-readonly — the acquired session's id (operator-line context). */
|
|
94
98
|
sessionId: string;
|
|
95
99
|
}
|
|
@@ -4,7 +4,7 @@ import { probeGitStatusLane } from "./git-status-frame.js";
|
|
|
4
4
|
import { fenceMcpServerInstructions } from "./turn-attachments.js";
|
|
5
5
|
const ULTRA_REASONING_TIERS = new Set(["xhigh", "max"]);
|
|
6
6
|
export async function preparePromptInputs(input) {
|
|
7
|
-
const { spec, deps, failClosedReason, lockedPreflight, selfOrchestrationActive, executionEnv, reminderMark, resolvedReadFace, toolFaceSnapshot, promptProfile, fableMitigations, internals, thinking, ownedEnv, delegation, resolvedRole, mcp, model, externalContentTargetActive, resume, handsEnabled, taskRootFinal, additionalRootsCanonical, additionalReadRootsCanonical, sessionId } = input;
|
|
7
|
+
const { spec, deps, failClosedReason, lockedPreflight, selfOrchestrationActive, executionEnv, reminderMark, resolvedReadFace, toolFaceSnapshot, promptProfile, fableMitigations, internals, thinking, ownedEnv, delegation, resolvedRole, mcp, model, externalContentTargetActive, resume, handsEnabled, taskRootFinal, additionalRootsCanonical, additionalReadRootsCanonical, sessionId, advertisedWritableDirs } = input;
|
|
8
8
|
const provider = spec.promptProvider ?? deps.promptProvider ?? defaultPromptProvider;
|
|
9
9
|
const promptPolicyEnabled = Boolean(lockedPreflight.toolPolicy);
|
|
10
10
|
const promptHooks = spec.hooks ?? deps.hooks;
|
|
@@ -51,7 +51,7 @@ export async function preparePromptInputs(input) {
|
|
|
51
51
|
envFacts.sandboxPkgSource = spec.envFacts.pkgSource;
|
|
52
52
|
if (spec.envFacts?.egress === "none" || spec.envFacts?.egress === "allowlist" || spec.envFacts?.egress === "full")
|
|
53
53
|
envFacts.sandboxEgress = spec.envFacts.egress;
|
|
54
|
-
if (spec.envFacts?.scratchpadDir)
|
|
54
|
+
if (spec.envFacts?.scratchpadDir && advertisedWritableDirs?.verdictOf("scratchpad") !== "skipped")
|
|
55
55
|
envFacts.scratchpadDir = spec.envFacts.scratchpadDir;
|
|
56
56
|
if (externalContentTargetActive)
|
|
57
57
|
envFacts.externalContentTarget = true;
|