@sema-agent/core 7.33.8 → 7.35.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 +462 -3
- package/dist/agents/cross-session-judge.d.ts +16 -7
- package/dist/agents/cross-session-judge.js +18 -15
- package/dist/agents/peer-session-drain.d.ts +4 -0
- package/dist/agents/peer-session-drain.js +5 -2
- package/dist/agents/repair-loop.d.ts +1 -1
- package/dist/agents/subagent.d.ts +3 -0
- package/dist/agents/subagent.js +12 -3
- package/dist/brain/anthropic.js +1 -0
- package/dist/brain/errors.d.ts +23 -1
- package/dist/brain/errors.js +19 -2
- package/dist/brain/open-responses.js +1 -0
- package/dist/brain/openai.js +1 -0
- package/dist/brain/status-sink.d.ts +7 -1
- package/dist/brain/stream-engine.d.ts +3 -0
- package/dist/brain/stream-engine.js +2 -1
- package/dist/brain/stream-shared.d.ts +3 -1
- package/dist/brain/stream-shared.js +16 -4
- package/dist/core/a2a.d.ts +3 -1
- package/dist/core/a2a.js +1 -1
- package/dist/core/ask-origin.d.ts +379 -154
- package/dist/core/ask-origin.js +224 -45
- package/dist/core/ask-question.js +2 -1
- package/dist/core/ask-unresolvable-notice.d.ts +5 -2
- package/dist/core/ask-unresolvable-notice.js +4 -3
- package/dist/core/auto-mode-arming.d.ts +17 -4
- package/dist/core/auto-mode-arming.js +31 -6
- package/dist/core/auto-mode-defaults.d.ts +16 -7
- package/dist/core/auto-mode-defaults.js +2 -1
- package/dist/core/auto-mode-directive.d.ts +18 -30
- package/dist/core/auto-mode-directive.js +9 -18
- package/dist/core/auto-mode-prompt.d.ts +62 -1
- package/dist/core/auto-mode-prompt.js +42 -4
- package/dist/core/auto-mode-rebuild.d.ts +2 -1
- package/dist/core/auto-mode-rebuild.js +1 -0
- package/dist/core/auto-mode.d.ts +32 -3
- package/dist/core/auto-mode.js +80 -41
- package/dist/core/checkpoint-store.d.ts +54 -35
- package/dist/core/checkpoint-store.js +58 -3
- package/dist/core/child-clamp-seats.js +1 -0
- package/dist/core/classifier-unavailable-arm.d.ts +57 -0
- package/dist/core/classifier-unavailable-arm.js +17 -0
- package/dist/core/effective-path-target.d.ts +12 -0
- package/dist/core/effective-path-target.js +18 -7
- package/dist/core/engine-notice.d.ts +53 -2
- package/dist/core/fs-write-gate-policy.d.ts +5 -1
- package/dist/core/fs-write-gate-policy.js +4 -1
- package/dist/core/gate-fold.js +19 -3
- package/dist/core/gate-lanes.js +116 -50
- package/dist/core/gate-outcome.d.ts +40 -7
- package/dist/core/gate-outcome.js +26 -4
- package/dist/core/governance-codes.d.ts +18 -2
- package/dist/core/governance-codes.js +23 -1
- package/dist/core/hooks.d.ts +53 -29
- package/dist/core/hooks.js +1 -3
- package/dist/core/mcp.js +1 -1
- package/dist/core/message-utils.d.ts +2 -1
- package/dist/core/message-utils.js +2 -1
- package/dist/core/mode-policy.d.ts +185 -0
- package/dist/core/mode-policy.js +184 -0
- package/dist/core/oracle-isolation.d.ts +3 -3
- package/dist/core/permission-rule-model.d.ts +20 -3
- package/dist/core/permission-rule-model.js +40 -3
- package/dist/core/permission-rule-provider.d.ts +22 -11
- package/dist/core/permission-rule-provider.js +17 -7
- package/dist/core/permission-rules.d.ts +23 -1
- package/dist/core/permission-rules.js +56 -9
- package/dist/core/persisted-rule-arms.d.ts +14 -10
- package/dist/core/persisted-rule-arms.js +13 -6
- package/dist/core/present-plan-tool.d.ts +1 -1
- package/dist/core/present-plan-tool.js +3 -3
- package/dist/core/read-only-shell.d.ts +19 -19
- package/dist/core/read-only-shell.js +38 -12
- package/dist/core/retired-keys.d.ts +9 -3
- package/dist/core/retired-keys.js +5 -1
- package/dist/core/runner/active-skill-scope.js +5 -4
- package/dist/core/runner/assemble-result.js +2 -2
- package/dist/core/runner/classifier-ladder.d.ts +43 -1
- package/dist/core/runner/classifier-ladder.js +29 -0
- package/dist/core/runner/contracts.d.ts +62 -38
- package/dist/core/runner/denial-limit-arms.d.ts +35 -7
- package/dist/core/runner/denial-limit-arms.js +15 -9
- package/dist/core/runner/env-capability-census.js +1 -1
- package/dist/core/runner/file-history-request.d.ts +26 -0
- package/dist/core/runner/file-history-request.js +90 -0
- package/dist/core/runner/gate-exit.d.ts +30 -4
- package/dist/core/runner/gate-exit.js +28 -7
- package/dist/core/runner/permission-rule-lanes.d.ts +11 -6
- package/dist/core/runner/permission-rule-lanes.js +55 -48
- package/dist/core/runner/persisted-path-rows.d.ts +64 -0
- package/dist/core/runner/persisted-path-rows.js +89 -0
- package/dist/core/runner/prepare-artifact.d.ts +5 -1
- package/dist/core/runner/prepare-artifact.js +9 -3
- package/dist/core/runner/prepare-ask-lane.d.ts +6 -4
- package/dist/core/runner/prepare-ask-lane.js +3 -3
- package/dist/core/runner/prepare-caps-and-workflow.d.ts +39 -13
- package/dist/core/runner/prepare-caps-and-workflow.js +58 -29
- package/dist/core/runner/prepare-file-history.d.ts +9 -3
- package/dist/core/runner/prepare-file-history.js +4 -90
- package/dist/core/runner/prepare-gate-stations.d.ts +37 -9
- package/dist/core/runner/prepare-gate-stations.js +46 -29
- package/dist/core/runner/prepare-hands-readface.d.ts +40 -17
- package/dist/core/runner/prepare-hands-readface.js +64 -53
- package/dist/core/runner/prepare-inherited-gate.d.ts +30 -23
- package/dist/core/runner/prepare-inherited-gate.js +45 -25
- package/dist/core/runner/prepare-park-ask.d.ts +6 -4
- package/dist/core/runner/prepare-park-ask.js +7 -5
- package/dist/core/runner/prepare-policy-chain.d.ts +25 -4
- package/dist/core/runner/prepare-policy-chain.js +203 -180
- package/dist/core/runner/prepare-protocol-tools.d.ts +28 -0
- package/dist/core/runner/prepare-protocol-tools.js +13 -3
- package/dist/core/runner/prepare-run-refs.d.ts +6 -6
- package/dist/core/runner/prepare-run-refs.js +9 -4
- package/dist/core/runner/prepare-safety-scan.d.ts +5 -0
- package/dist/core/runner/prepare-safety-scan.js +19 -3
- package/dist/core/runner/prepare-suspend-saga.d.ts +5 -5
- package/dist/core/runner/prepare-suspend-saga.js +6 -6
- package/dist/core/runner/prepare-task.js +56 -24
- package/dist/core/runner/prepare-wiring-manifest.d.ts +10 -6
- package/dist/core/runner/prepare-wiring-manifest.js +5 -3
- package/dist/core/runner/read-root-grant.d.ts +9 -0
- package/dist/core/runner/read-root-grant.js +12 -0
- package/dist/core/runner/retired-runner-deps.js +1 -0
- package/dist/core/runner/run-brain-sinks.js +3 -1
- package/dist/core/runner/run-identity-wiring.js +1 -1
- package/dist/core/runner/run-leg.js +29 -18
- package/dist/core/runner/run-notification-lane.d.ts +3 -0
- package/dist/core/runner/run-notification-lane.js +3 -1
- package/dist/core/runner/run-telemetry-and-budget-seats.d.ts +2 -2
- package/dist/core/runner/run-telemetry-and-budget-seats.js +1 -1
- package/dist/core/runner/run-turn-boundary.js +1 -1
- package/dist/core/runner/runtask.d.ts +1 -0
- package/dist/core/runner/runtask.js +8 -11
- package/dist/core/runner/session-mode-seats.d.ts +48 -0
- package/dist/core/runner/session-mode-seats.js +73 -0
- package/dist/core/runner/session-rule-policy.js +3 -2
- package/dist/core/runner/stream-mode-verb.d.ts +27 -0
- package/dist/core/runner/stream-mode-verb.js +19 -0
- package/dist/core/runner/turn-attachments.d.ts +3 -3
- package/dist/core/runner/turn-attachments.js +1 -1
- package/dist/core/runner/write-protection-station.d.ts +7 -1
- package/dist/core/runner/write-protection-station.js +6 -3
- package/dist/core/runner-deps.d.ts +31 -34
- package/dist/core/runtime-caps.d.ts +12 -3
- package/dist/core/safety-axis-vocab.d.ts +2 -2
- package/dist/core/safety-axis-vocab.js +1 -1
- package/dist/core/secret-env.d.ts +30 -58
- package/dist/core/secret-env.js +149 -586
- package/dist/core/sensitive-path-policy.d.ts +4 -2
- package/dist/core/sensitive-path-policy.js +41 -31
- package/dist/core/store-contracts/checkpoint-store-contract.js +2 -1
- package/dist/core/task-event.d.ts +1 -1
- package/dist/core/task-result.d.ts +1 -1
- package/dist/core/task-spec.d.ts +41 -89
- package/dist/core/task-stream.d.ts +16 -0
- package/dist/core/terminal-cause.js +1 -0
- package/dist/core/tighten-task-spec.d.ts +2 -2
- package/dist/core/tighten-task-spec.js +12 -14
- package/dist/core/tool-catalog-entries.js +4 -3
- package/dist/core/tool-policy.d.ts +84 -30
- package/dist/core/tool-policy.js +76 -16
- package/dist/core/tool-registry.d.ts +38 -0
- package/dist/core/tool-registry.js +25 -1
- package/dist/core/tool-roster.d.ts +17 -2
- package/dist/core/tool-roster.js +11 -1
- package/dist/core/tool-spec.d.ts +8 -9
- package/dist/core/trace.d.ts +18 -6
- package/dist/core/types.d.ts +2 -1
- package/dist/core/wiring-manifest.d.ts +31 -5
- package/dist/core/wiring-manifest.js +28 -1
- package/dist/core/write-protect.d.ts +11 -1
- package/dist/core/write-protect.js +26 -15
- package/dist/engine/execution-env/node-execution-env.d.ts +6 -1
- package/dist/engine/execution-env/node-execution-env.js +16 -3
- package/dist/engine/harness/agent-harness.js +2 -1
- package/dist/engine/harness/types.d.ts +6 -7
- package/dist/engine/llm/types.d.ts +2 -4
- package/dist/engine/loop/types.d.ts +7 -0
- package/dist/engine/lsp/node-lsp-manager.js +1 -1
- package/dist/index.d.ts +18 -8
- package/dist/index.js +15 -6
- package/dist/orchestration/workflow-governance.js +1 -1
- package/dist/orchestration/workflow-primitives.d.ts +1 -1
- package/dist/orchestration/workflow-size-guideline.js +2 -2
- package/dist/stores/file/index.d.ts +5 -3
- package/dist/stores/file/session-store.d.ts +8 -2
- package/dist/stores/file/session-store.js +5 -5
- package/dist/tools/artifact/artifact-tool.d.ts +17 -0
- package/dist/tools/artifact/artifact-tool.js +16 -1
- package/dist/tools/fs/bash-readonly-classifier.d.ts +28 -7
- package/dist/tools/fs/bash-readonly-classifier.js +88 -0
- package/dist/tools/fs/containment-face.d.ts +9 -3
- package/dist/tools/fs/containment-face.js +2 -0
- package/dist/tools/fs/file-read-check.d.ts +50 -0
- package/dist/tools/fs/file-read-check.js +79 -0
- package/dist/tools/fs/fs-bash.d.ts +3 -3
- package/dist/tools/fs/fs-read.d.ts +1 -0
- package/dist/tools/fs/fs-search-tools.d.ts +2 -2
- package/dist/tools/fs/fs-search-tools.js +4 -4
- package/dist/tools/fs/fs-shared.js +20 -6
- package/dist/tools/fs/fs-write.d.ts +4 -3
- package/dist/tools/fs/fs-write.js +9 -9
- package/dist/tools/fs/index.d.ts +39 -3
- package/dist/tools/fs/index.js +38 -15
- package/dist/tools/fs/read-deny-home.d.ts +93 -0
- package/dist/tools/fs/read-deny-home.js +225 -0
- package/dist/tools/fs/read-deny.d.ts +176 -34
- package/dist/tools/fs/read-deny.js +268 -61
- package/dist/tools/fs/safety.d.ts +6 -1
- package/dist/tools/fs/safety.js +18 -5
- package/dist/tools/fs/search.d.ts +3 -0
- package/dist/tools/fs/search.js +47 -9
- package/dist/tools/fs/shell-read-check.d.ts +52 -13
- package/dist/tools/fs/shell-read-check.js +199 -20
- package/dist/tools/fs/shell-read-establish.d.ts +7 -3
- package/dist/tools/fs/shell-read-establish.js +134 -21
- package/dist/tools/fs/shell-read-identity.d.ts +7 -0
- package/dist/tools/fs/shell-read-identity.js +21 -0
- package/dist/tools/fs/shell-read-judge.d.ts +92 -32
- package/dist/tools/fs/shell-read-judge.js +43 -18
- package/dist/tools/fs/shell-read-plan.d.ts +4 -0
- package/dist/tools/fs/shell-read-plan.js +26 -4
- package/dist/tools/fs/shell-read-write-targets.d.ts +73 -0
- package/dist/tools/fs/shell-read-write-targets.js +173 -0
- package/package.json +1 -1
- package/test/export-surface.snapshot.json +117 -11
- package/wire-changes/core@7.34.0.json +2621 -0
- package/wire-changes/core@7.35.0.json +584 -0
|
@@ -24,18 +24,22 @@
|
|
|
24
24
|
*/
|
|
25
25
|
import type { AgentHarness, AgentTool } from "../../internal/harness.js";
|
|
26
26
|
import type { AskClass } from "../ask-class.js";
|
|
27
|
+
import type { Arbitration, ToolMarkSource } from "../ask-origin.js";
|
|
28
|
+
import type { PermissionModeController } from "../mode-policy.js";
|
|
27
29
|
import type { AutoModeDecider, AutoModeDenialTracker } from "../auto-mode.js";
|
|
28
30
|
import { type ComplianceCapability } from "../compliance.js";
|
|
29
31
|
import { type HookEnvCapabilities, type HookInvocationIdentity, type Hooks } from "../hooks.js";
|
|
30
32
|
import type { ToolPolicy } from "../tool-policy.js";
|
|
31
33
|
import { type GateOutcome } from "../gate-outcome.js";
|
|
32
34
|
import type { RunnerDeps, TaskSpec, ToolEffect } from "../types.js";
|
|
35
|
+
import { type WiringManifest } from "../wiring-manifest.js";
|
|
33
36
|
import type { WriteProtectionCheck } from "../write-protect.js";
|
|
34
37
|
import type { ShellReadCheck } from "../../tools/fs/shell-read-check.js";
|
|
35
38
|
import type { CwdRef } from "../../tools/fs/fs-shared.js";
|
|
36
39
|
import type { AskLane, IrreversibilityTier, ParkAsk, Prepared, ReversibilityProbes, StopForDenialLimit } from "./contracts.js";
|
|
37
40
|
import type { PermissionRuleLanes } from "./permission-rule-lanes.js";
|
|
38
41
|
import { type ToolRosterDeltaSeat } from "../tool-roster.js";
|
|
42
|
+
import type { ToolCallLike } from "../tool-registry.js";
|
|
39
43
|
import { type FrameSettlement } from "../frame-settlement.js";
|
|
40
44
|
/**
|
|
41
45
|
* The parent-thread human-rejection boundary — judged ONCE here, from the pass's own record (CC parity:
|
|
@@ -134,8 +138,6 @@ export interface PrepareGateStationsInput {
|
|
|
134
138
|
hookIdentity: HookInvocationIdentity;
|
|
135
139
|
/** borrowed-readonly — the leg's reminder provenance mark (every model-facing deny reason and hook context carries it). */
|
|
136
140
|
reminderMark: string;
|
|
137
|
-
/** borrowed-readonly — the plan-mode latch (`Prepared.planModeRef`); `active` is read per call. Not written here. */
|
|
138
|
-
planModeRef: Prepared["planModeRef"];
|
|
139
141
|
/** borrowed-readonly — the per-task identity (the trace rows' taskId). */
|
|
140
142
|
hostTaskId: string;
|
|
141
143
|
/** borrowed-readonly — the acquired session id (the settlement defects' error tag). */
|
|
@@ -147,6 +149,12 @@ export interface PrepareGateStationsInput {
|
|
|
147
149
|
/** borrowed-readonly — the hand's live tracked cwd, or undefined; `current` is read per screening / per post seat. Not
|
|
148
150
|
* written here. */
|
|
149
151
|
handsCwdRef: CwdRef | undefined;
|
|
152
|
+
/** borrowed-readonly — the leg's read-deny table in force, read at a release: the active built-in tiers and the rows
|
|
153
|
+
* beside them (the deployment's and the task's patterns, the read-face rule rows). Empty ⇒ no table. */
|
|
154
|
+
readDenyInForce: () => {
|
|
155
|
+
readonly tiers: readonly string[];
|
|
156
|
+
readonly rows: readonly string[];
|
|
157
|
+
};
|
|
150
158
|
/** borrowed-readonly — the read-only env capability face every hook context carries, or undefined. */
|
|
151
159
|
hookEnvFace: HookEnvCapabilities | undefined;
|
|
152
160
|
/** borrowed-readonly — the leg's write-protection gate input, compiled ONCE by the wiring-manifest phase
|
|
@@ -167,12 +175,32 @@ export interface PrepareGateStationsInput {
|
|
|
167
175
|
readonly signal: AbortSignal;
|
|
168
176
|
};
|
|
169
177
|
/** borrowed-readonly — the shell-seat provenance set: the shell-running tools whose tier is the engine's shell seat's, not a
|
|
170
|
-
* deployment mark (the gate's `shellGated` attribution is the
|
|
178
|
+
* deployment mark (the gate's `shellGated` attribution is the membership of the name the call's tier was read under). */
|
|
171
179
|
shellGatedTools: ReadonlySet<string>;
|
|
172
|
-
/** borrowed-readonly — the
|
|
173
|
-
autoModeDecider:
|
|
174
|
-
|
|
175
|
-
|
|
180
|
+
/** borrowed-readonly — the armed classifier's live seat (read per call: a leg steered into `auto` arms after prepare). */
|
|
181
|
+
autoModeDecider: {
|
|
182
|
+
readonly current: AutoModeDecider | undefined;
|
|
183
|
+
};
|
|
184
|
+
/** borrowed-readonly — the denial tracker's live seat beside the decider's. */
|
|
185
|
+
autoModeDenialTracking: {
|
|
186
|
+
readonly current: AutoModeDenialTracker | undefined;
|
|
187
|
+
};
|
|
188
|
+
/** borrowed-readonly — the leg's ONE ask derivation (`wiring_manifest.ask`: form + effective), read for the classifier's seat. */
|
|
189
|
+
askRoute: WiringManifest["ask"];
|
|
190
|
+
/** borrowed-readonly — the leg's live permission-mode seat: the gate's arbitration point reads `current` per call. */
|
|
191
|
+
permissionMode: PermissionModeController;
|
|
192
|
+
/** borrowed-readonly — WHO marked each tool's seat (the safety scan's map, the protocol fold's rows); read per call into
|
|
193
|
+
* the gate's class derivation. */
|
|
194
|
+
toolMarkSources: ReadonlyMap<string, ReadonlySet<ToolMarkSource>>;
|
|
195
|
+
/** borrowed-readonly — the deployment's A2A isolation switch (`RunnerDeps.a2a.isolatePeers`), when set. */
|
|
196
|
+
a2aIsolatePeers?: true;
|
|
197
|
+
/** borrowed-readonly — the run's invocation id (the released-ask account's detail). */
|
|
198
|
+
runId: string;
|
|
199
|
+
/** borrowed-mutable — the inherited stations' mode arbitrations, keyed by tool call (the policy-chain phase writes; the
|
|
200
|
+
* gate's exit reads and this station deletes at the call's end, the fold-record lifecycle). */
|
|
201
|
+
ancestorArbitrations: Map<string, Exclude<Arbitration, {
|
|
202
|
+
kind: "ask";
|
|
203
|
+
}>>;
|
|
176
204
|
/** borrowed-readonly — the headless denial-limit stop seat (wired unconditionally). */
|
|
177
205
|
stopForDenialLimit: StopForDenialLimit;
|
|
178
206
|
/** borrowed-readonly — the persisted allow-rule lane, or undefined (absent entirely with no store). */
|
|
@@ -184,8 +212,8 @@ export interface PrepareGateStationsInput {
|
|
|
184
212
|
questionToolMounted: boolean;
|
|
185
213
|
/** borrowed-readonly — whether sandbox admission is armed (isolated env + park prerequisites). */
|
|
186
214
|
sandboxAdmissionArmed: boolean;
|
|
187
|
-
/** borrowed-readonly — the per-
|
|
188
|
-
sandboxBoundaryCapable: (
|
|
215
|
+
/** borrowed-readonly — the per-call boundary-capability fact the sandbox admission reads (the call's wire name and names). */
|
|
216
|
+
sandboxBoundaryCapable: (call: Pick<ToolCallLike, "toolName" | "face">) => boolean;
|
|
189
217
|
/** borrowed-readonly — the sandbox-admission trace sink (the deferred ancestor disclosure and the gate's own). */
|
|
190
218
|
emitSandboxAdmitted: (info: {
|
|
191
219
|
toolCallId: string;
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import { SEND_MESSAGE_TOOL_NAME } from "../../agents/send-message-tool.js";
|
|
2
2
|
import { complianceCallDenial } from "../compliance.js";
|
|
3
3
|
import { cloneObserverInput, formatHookFeedback, hookSeatExpiredError, runHookSeat, runToolGate } from "../hooks.js";
|
|
4
|
-
import { PRESENT_PLAN_TOOL_NAME } from "../present-plan-tool.js";
|
|
5
4
|
import { screenGateOutcome } from "../gate-outcome.js";
|
|
6
5
|
import { directDeny } from "./gate-exit.js";
|
|
7
6
|
import { emitTrace } from "../trace.js";
|
|
8
7
|
import { deliverEngineNotice } from "../engine-notice.js";
|
|
9
8
|
import { askUnresolvableNotice } from "../ask-unresolvable-notice.js";
|
|
9
|
+
import { askReachesPerson } from "../wiring-manifest.js";
|
|
10
|
+
import { classifierUnavailableSeat } from "./classifier-ladder.js";
|
|
10
11
|
import { PATH_CONFINABLE_WRITE_TOOLS } from "./session-rule-policy.js";
|
|
11
|
-
import { toolCallFaceOf } from "../tool-roster.js";
|
|
12
|
+
import { anyNamed, callAnswersTo, callFaceSeat, firstNamed, rosterToolName, ruleNamesOf, toolCallFaceOf } from "../tool-roster.js";
|
|
12
13
|
import { noteHostRewrite } from "../frame-settlement.js";
|
|
13
14
|
function screenGateResult(result) {
|
|
14
15
|
if (result.suspend !== undefined)
|
|
@@ -42,7 +43,29 @@ export function maybeHumanRejectionHalt(input) {
|
|
|
42
43
|
};
|
|
43
44
|
}
|
|
44
45
|
export function prepareGateStations(input) {
|
|
45
|
-
const { askLane, parkAsk, tools, toolRosterDeltas, toolEffects, deps, toolCallGateArmedRef, effectivePolicy, hooks, egressTools, irreversibleTools, spec, complianceDenies, harness, blockedToolCalls, inheritedAskGrants, inheritedUnavailableAsks, foldAskClasses, ancestorSandboxAdmissions, preToolContexts, gateOutcomes, batchHaltRef, blockedTracked, hookIdentity, reminderMark,
|
|
46
|
+
const { askLane, parkAsk, tools, toolRosterDeltas, toolEffects, deps, toolCallGateArmedRef, effectivePolicy, hooks, egressTools, irreversibleTools, spec, complianceDenies, harness, blockedToolCalls, inheritedAskGrants, inheritedUnavailableAsks, foldAskClasses, ancestorSandboxAdmissions, preToolContexts, gateOutcomes, batchHaltRef, blockedTracked, hookIdentity, reminderMark, hostTaskId, sessionId, ownGatePreToolUse, hookTimeoutMs, handsCwdRef, hookEnvFace, writeProtectionCheck, shellReadCheck, irreversibilityTier, reversibilityProbes, abortController, shellGatedTools, autoModeDecider, autoModeDenialTracking, askRoute, permissionMode, toolMarkSources, a2aIsolatePeers, ancestorArbitrations, runId, stopForDenialLimit, permissionRuleLane, permissionRuleOrgLane, questionToolMounted, sandboxAdmissionArmed, sandboxBoundaryCapable, emitSandboxAdmitted, delegation, notifyOwnHookCrash, frameSettlement } = input;
|
|
47
|
+
const releasesAnnounced = new Map();
|
|
48
|
+
const onModeReleased = (info) => {
|
|
49
|
+
const table = info.readUnestablished === true ? input.readDenyInForce() : undefined;
|
|
50
|
+
if (table !== undefined && table.tiers.length + table.rows.length > 0) {
|
|
51
|
+
const named = [...(table.tiers.length > 0 ? [`built-in tiers ${table.tiers.join(", ")}`] : []), ...table.rows.slice(0, 8), ...(table.rows.length > 8 ? [`${table.rows.length - 8} more`] : [])].join("; ");
|
|
52
|
+
deliverEngineNotice(deps.onNotice, {
|
|
53
|
+
code: "permission.read_deny_unapplied",
|
|
54
|
+
message: `the ${info.release.mode} permission mode released "${info.toolName}" although a read-deny table is in force (${named}): the read boundary could not establish what this command reads, so no row of the table was applied to it`,
|
|
55
|
+
detail: { mode: info.release.mode, toolCallId: info.toolCallId, tiers: [...table.tiers], rows: table.rows.slice(0, 8), rowCount: table.rows.length, sessionId, runId },
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
const key = `${info.release.mode}\u0000${info.release.askClass}`;
|
|
59
|
+
const count = (releasesAnnounced.get(key) ?? 0) + 1;
|
|
60
|
+
releasesAnnounced.set(key, count);
|
|
61
|
+
if (count !== 1)
|
|
62
|
+
return;
|
|
63
|
+
deliverEngineNotice(deps.onNotice, {
|
|
64
|
+
code: "permission.ask_released_by_mode",
|
|
65
|
+
message: `the ${info.release.mode} permission mode released a ${info.release.askClass} ask on "${info.toolName}" without putting it to anyone (further releases of this pair in this run ride each call's tool_end.gate.disposition.released)`,
|
|
66
|
+
detail: { mode: info.release.mode, askClass: info.release.askClass, ...(info.release.origin !== undefined ? { origin: info.release.origin } : {}), ...(info.release.mandate !== undefined ? { mandate: info.release.mandate } : {}), count, firstToolCallId: info.toolCallId, sessionId, runId },
|
|
67
|
+
});
|
|
68
|
+
};
|
|
46
69
|
if (askLane !== undefined && parkAsk !== undefined) {
|
|
47
70
|
const { adjudicate, resolveAskBound, notifyPermissionDenied, notifyHookError } = askLane;
|
|
48
71
|
const { resolveContentAsk, suspendAsk } = parkAsk;
|
|
@@ -62,11 +85,15 @@ export function prepareGateStations(input) {
|
|
|
62
85
|
writeProtectionArmed ||
|
|
63
86
|
shellReadCheck !== undefined;
|
|
64
87
|
if (toolCallGateArmedRef.armed) {
|
|
65
|
-
harness.on("tool_call", async (
|
|
88
|
+
harness.on("tool_call", async (called) => {
|
|
89
|
+
const e = { ...called, toolName: rosterToolName(toolRosterDeltas.current, called.toolName) };
|
|
90
|
+
const call = { toolName: e.toolName, ...callFaceSeat(toolCallFaceOf(toolRosterDeltas.current, called.toolName)) };
|
|
91
|
+
const names = ruleNamesOf(call);
|
|
92
|
+
const tierSeat = firstNamed(irreversibilityTier, names);
|
|
66
93
|
blockedToolCalls.delete(e.toolCallId);
|
|
67
94
|
inheritedAskGrants.delete(e.toolCallId);
|
|
68
95
|
{
|
|
69
|
-
const complianceDeny = complianceCallDenial(complianceDenies,
|
|
96
|
+
const complianceDeny = names.map((n) => complianceCallDenial(complianceDenies, n)).find((d) => d !== undefined);
|
|
70
97
|
if (complianceDeny !== undefined) {
|
|
71
98
|
if (blockedTracked)
|
|
72
99
|
blockedToolCalls.add(e.toolCallId);
|
|
@@ -78,22 +105,6 @@ export function prepareGateStations(input) {
|
|
|
78
105
|
return { block: true, reason: formatHookFeedback(complianceDeny, reminderMark), preToolContext: [] };
|
|
79
106
|
}
|
|
80
107
|
}
|
|
81
|
-
if (planModeRef.active && (toolEffects.get(e.toolName) ?? "write") !== "read") {
|
|
82
|
-
if (blockedTracked)
|
|
83
|
-
blockedToolCalls.add(e.toolCallId);
|
|
84
|
-
const planDenyReason = `Plan mode is active — "${e.toolName}" is a write/mutating tool and is read-only-blocked. ` +
|
|
85
|
-
`Research with read-only tools, then call ${PRESENT_PLAN_TOOL_NAME} with your plan to get it approved before acting.`;
|
|
86
|
-
const gate = directDeny("plan_mode");
|
|
87
|
-
gateOutcomes.set(e.toolCallId, gate);
|
|
88
|
-
if (notifyPermissionDenied) {
|
|
89
|
-
await notifyPermissionDenied({ toolName: e.toolName, input: cloneObserverInput(e.input), toolCallId: e.toolCallId, reason: planDenyReason, gate, identity: hookIdentity });
|
|
90
|
-
}
|
|
91
|
-
return {
|
|
92
|
-
block: true,
|
|
93
|
-
reason: formatHookFeedback(planDenyReason, reminderMark),
|
|
94
|
-
preToolContext: [],
|
|
95
|
-
};
|
|
96
|
-
}
|
|
97
108
|
let result;
|
|
98
109
|
try {
|
|
99
110
|
result = await runToolGate({
|
|
@@ -106,22 +117,27 @@ export function prepareGateStations(input) {
|
|
|
106
117
|
hookTimeoutMs,
|
|
107
118
|
...(handsCwdRef !== undefined ? { trackedCwd: () => handsCwdRef.current } : {}),
|
|
108
119
|
...(hookEnvFace !== undefined ? { hookEnv: hookEnvFace } : {}),
|
|
109
|
-
toolFaceOf: (name) => toolCallFaceOf(toolRosterDeltas.current, name),
|
|
120
|
+
toolFaceOf: (name) => toolCallFaceOf(toolRosterDeltas.current, name === e.toolName ? called.toolName : name),
|
|
110
121
|
adjudicate,
|
|
111
122
|
resolveAsk: resolveAskBound,
|
|
112
123
|
suspendAsk,
|
|
113
124
|
resolveContentAsk,
|
|
114
|
-
egress: egressTools
|
|
115
|
-
peerMessage:
|
|
125
|
+
egress: anyNamed(egressTools, names),
|
|
126
|
+
peerMessage: callAnswersTo(call, SEND_MESSAGE_TOOL_NAME),
|
|
116
127
|
...(writeProtectionCheck !== undefined ? { writeProtectionCheck } : {}),
|
|
117
128
|
...(shellReadCheck !== undefined ? { shellReadCheck } : {}),
|
|
118
|
-
irreversibility:
|
|
119
|
-
reversibilityProbe: reversibilityProbes.get(
|
|
129
|
+
irreversibility: tierSeat?.value,
|
|
130
|
+
reversibilityProbe: tierSeat !== undefined ? reversibilityProbes.get(tierSeat.name) : undefined,
|
|
120
131
|
abortSignal: abortController.signal,
|
|
121
132
|
...(notifyPermissionDenied ? { permissionDenied: notifyPermissionDenied } : {}),
|
|
122
133
|
onHookError: notifyHookError,
|
|
123
|
-
shellGated: shellGatedTools.has(
|
|
124
|
-
...(autoModeDecider ? { autoMode: { decider: autoModeDecider, ...(autoModeDenialTracking !== undefined ? { denialTracking: autoModeDenialTracking } : {}) } } : {}),
|
|
134
|
+
shellGated: tierSeat !== undefined && shellGatedTools.has(tierSeat.name),
|
|
135
|
+
...(autoModeDecider.current !== undefined ? { autoMode: { decider: autoModeDecider.current, ...(autoModeDenialTracking.current !== undefined ? { denialTracking: autoModeDenialTracking.current } : {}), unavailable: classifierUnavailableSeat({ station: "leg", ...(autoModeDecider.current.timeoutMs !== undefined ? { budgetMs: autoModeDecider.current.timeoutMs } : {}), approvalSeat: askReachesPerson(askRoute), onNotice: deps.onNotice, sessionId, runId }) } } : {}),
|
|
136
|
+
permissionMode: () => permissionMode.current,
|
|
137
|
+
...(() => { const s = firstNamed(toolMarkSources, names)?.value; return s !== undefined ? { toolMarkSources: s } : {}; })(),
|
|
138
|
+
...(a2aIsolatePeers === true ? { a2aIsolatePeers: true } : {}),
|
|
139
|
+
onModeReleased,
|
|
140
|
+
ancestorArbitrationOf: (toolCallId) => ancestorArbitrations.get(toolCallId),
|
|
125
141
|
onHeadlessDenialLimit: stopForDenialLimit,
|
|
126
142
|
onAskUnresolvable: (info) => deliverEngineNotice(deps.onNotice, askUnresolvableNotice({ sessionId, ...info })),
|
|
127
143
|
...(permissionRuleLane
|
|
@@ -171,7 +187,7 @@ export function prepareGateStations(input) {
|
|
|
171
187
|
? {
|
|
172
188
|
sandboxAdmission: {
|
|
173
189
|
askClassesOf: (toolCallId) => foldAskClasses.get(toolCallId),
|
|
174
|
-
boundaryCapable: sandboxBoundaryCapable(
|
|
190
|
+
boundaryCapable: sandboxBoundaryCapable(call),
|
|
175
191
|
onAdmitted: emitSandboxAdmitted,
|
|
176
192
|
},
|
|
177
193
|
}
|
|
@@ -182,6 +198,7 @@ export function prepareGateStations(input) {
|
|
|
182
198
|
inheritedUnavailableAsks.delete(e.toolCallId);
|
|
183
199
|
inheritedAskGrants.delete(e.toolCallId);
|
|
184
200
|
foldAskClasses.delete(e.toolCallId);
|
|
201
|
+
ancestorArbitrations.delete(e.toolCallId);
|
|
185
202
|
}
|
|
186
203
|
const ancestorAdmitted = ancestorSandboxAdmissions.get(e.toolCallId);
|
|
187
204
|
ancestorSandboxAdmissions.delete(e.toolCallId);
|
|
@@ -1,14 +1,16 @@
|
|
|
1
|
-
import { type BeforeWriteHook, type CwdRef, type ReadDenyMatcher, type ContainmentFace } from "../../tools/fs/index.js";
|
|
1
|
+
import { type BeforeWriteHook, type CwdRef, type ReadDenyMatcher, type PathRuleDenyRow, type ContainmentFace } from "../../tools/fs/index.js";
|
|
2
2
|
import { type ReadFileState } from "../../tools/fs/safety.js";
|
|
3
3
|
import { type ShellReadAdmission, type ShellReadCheck } from "../../tools/fs/shell-read-check.js";
|
|
4
|
+
import type { ShellReadBoundary } from "../../tools/fs/shell-read-judge.js";
|
|
4
5
|
import type { RunnerDeps, ShellGateDoctrine, TaskSpec } from "../types.js";
|
|
5
6
|
import type { Model } from "../../internal/llm.js";
|
|
6
7
|
import type { AgentHarness, AgentTool, ExecutionEnv, WorkspaceState } from "../../internal/harness.js";
|
|
7
8
|
import type { AdvertisedWritableDirs } from "./advertised-writable-dirs.js";
|
|
8
9
|
import { type ArtifactStoreRef, type GrantReadRoot } from "./read-root-grant.js";
|
|
9
10
|
import type { StoredSession } from "../session.js";
|
|
10
|
-
import type { IrreversibilityTier, PrepareResume, ReversibilityProbes, RunInternals, ToolFaceSnapshot, ToolProgressLane } from "./contracts.js";
|
|
11
|
+
import type { IrreversibilityTier, PrepareResume, ReversibilityProbes, RunInternals, ToolFaceSnapshot, ToolProgressLane, WriteFaceSeat } from "./contracts.js";
|
|
11
12
|
import { RosterBuilder } from "../tool-roster.js";
|
|
13
|
+
import type { PermissionMode } from "../ask-origin.js";
|
|
12
14
|
/** Test seam (mirrors `__resetMalformedNoticeSeatAnnouncement`): never called by production code.
|
|
13
15
|
* Deliberately asymmetric — it resets only the console latch: the WeakSet arm needs no seam
|
|
14
16
|
* because a test resets it by minting a fresh sink function (identity IS the ledger key), while
|
|
@@ -51,13 +53,31 @@ export interface PrepareHandsReadFaceInput {
|
|
|
51
53
|
/** borrowed-readonly — the registry scope (`internals?.registryScope ?? spec.principal ??
|
|
52
54
|
* "default"`, computed in the driver); the band's task-registry scope. */
|
|
53
55
|
taskScope: string;
|
|
56
|
+
/** borrowed-readonly — the deny tables' permission-rule rows: the path deny rules of the leg's caller policy and of every
|
|
57
|
+
* ancestor's (`toolPolicyPathDenyRules`); joined to the read face's matcher and to the shell station's write face. */
|
|
58
|
+
pathDenyRules: readonly PathRuleDenyRow[];
|
|
59
|
+
/** borrowed-readonly — the two judges a shell command's write targets meet for the mode's file-command placement: the
|
|
60
|
+
* leg's write-protection judge (read per judgment — it is compiled at the manifest phase, before any gate is mounted)
|
|
61
|
+
* and the chain's write-target guard (`toolPolicyWriteTargetGuard`). Absent ⇒ neither (a standalone phase drive). */
|
|
62
|
+
writeGuards?: {
|
|
63
|
+
readonly protection: () => ShellReadBoundary["writeProtectionJudge"];
|
|
64
|
+
readonly target: ShellReadBoundary["writeTargetGuard"];
|
|
65
|
+
};
|
|
66
|
+
/** borrowed-readonly — the directories of the person's persisted `Read(//dir/**)` allow rows: read directories of this
|
|
67
|
+
* run, canonicalized and admitted exactly as `additionalReadDirectories` entries are. */
|
|
68
|
+
persistedReadDirectories?: readonly string[];
|
|
69
|
+
/** borrowed-readonly — the leg's mode seat, read LIVE by the write face ({@link writeFaceReader}): its mode's face
|
|
70
|
+
* is the resolver's row 3′ on every write. */
|
|
71
|
+
permissionMode: {
|
|
72
|
+
readonly current: PermissionMode;
|
|
73
|
+
};
|
|
54
74
|
/** borrowed-readonly — the D-6 spec-time full-shell reachability precomputation; an input to the
|
|
55
75
|
* face resolver on BOTH arms (the driver's post-exclusion roster assertion pins it to the
|
|
56
76
|
* mounted truth — that assertion is driver-side, after this phase). */
|
|
57
77
|
fullShellReachable: boolean;
|
|
58
|
-
/** borrowed-readonly — the
|
|
59
|
-
*
|
|
60
|
-
effectiveShellGate: ShellGateDoctrine;
|
|
78
|
+
/** borrowed-readonly — the operator's shell FLOOR (`"always"`: the spec's, or the chain's), or undefined (no floor:
|
|
79
|
+
* the shape tier installs on its own and the permission mode arbitrates its asks). */
|
|
80
|
+
effectiveShellGate: ShellGateDoctrine | undefined;
|
|
61
81
|
/** borrowed-readonly — B-1's frozen task-start face snapshot; only `.exclude` is read here (the
|
|
62
82
|
* Monitor-mount honesty bit for the gh rate-limit hint). */
|
|
63
83
|
toolFaceSnapshot: ToolFaceSnapshot;
|
|
@@ -123,8 +143,8 @@ export interface PrepareHandsReadFaceInput {
|
|
|
123
143
|
/** borrowed-mutable — the shellGate fold `.add`s Bash/Monitor here. Same-object contract as
|
|
124
144
|
* above. */
|
|
125
145
|
irreversibleTools: Set<string>;
|
|
126
|
-
/** borrowed-mutable — the
|
|
127
|
-
* owns the
|
|
146
|
+
/** borrowed-mutable — the shape tier's probe installs (`.set`, only where the shell seat
|
|
147
|
+
* owns the tier). Same-object contract as above. */
|
|
128
148
|
reversibilityProbes: ReversibilityProbes;
|
|
129
149
|
/** borrowed-readonly — the run's reminder provenance mark (minted/adopted
|
|
130
150
|
* by the driver's adoption ladder); threaded into the hands band so the fs-side engine mints
|
|
@@ -184,9 +204,12 @@ export interface PrepareHandsReadFaceResult {
|
|
|
184
204
|
/** owned — real shell + write hands + background-capable env: gates the driver's TaskOutput/
|
|
185
205
|
* TaskStop/Monitor/SendMessage mounts (P11) and the Monitor arm of the shellGate fold here. */
|
|
186
206
|
backgroundTaskToolsActive: boolean;
|
|
187
|
-
/** owned — this run's
|
|
188
|
-
* where a shell command reads, for every shell face this leg mounts, under every
|
|
207
|
+
/** owned — this run's READ STATIONS as the one gate input (`ToolGateInput.shellReadCheck`): the shell's (ONE judgment of
|
|
208
|
+
* where a shell command reads, for every shell face this leg mounts, under every mode and floor) and the file tools'
|
|
209
|
+
* (an out-of-root Read / Grep / Glob target is a question). ⇔ hands with a real shell, or hands with an approval face. */
|
|
189
210
|
shellReadCheck: ShellReadCheck | undefined;
|
|
211
|
+
/** owned — the shell read station is mounted (⇔ a real shell): the wiring manifest's cell. */
|
|
212
|
+
shellReadStationArmed: boolean;
|
|
190
213
|
/** owned — the same station's pre-spawn ADMISSION, published for the P11 face that spawns a shell command
|
|
191
214
|
* and mounts LATER than the band (`Monitor`): the SAME function the band's shell faces got. ⇔ a real shell. */
|
|
192
215
|
shellReadAdmission: ShellReadAdmission | undefined;
|
|
@@ -194,13 +217,13 @@ export interface PrepareHandsReadFaceResult {
|
|
|
194
217
|
* (the hands-less value lives in `handsLessResolvedFace`). The driver assigns this onto its
|
|
195
218
|
* outer `let` for the call-time closures (ctx enrichment / checkpoint mint / envFacts). */
|
|
196
219
|
resolvedReadFace: ContainmentFace | undefined;
|
|
197
|
-
/** write-back — the run's
|
|
198
|
-
* hands-less
|
|
199
|
-
*
|
|
200
|
-
resolvedWriteFace:
|
|
201
|
-
/** write-back — the hands-LESS legs'
|
|
202
|
-
*
|
|
203
|
-
handsLessResolvedWriteFace:
|
|
220
|
+
/** write-back — the run's WRITE face in force where hands mount, read per call ({@link writeFaceReader}); undefined ⇔
|
|
221
|
+
* no hands (the hands-less reader is `handsLessResolvedWriteFace`). The read seat's twin, resolved by the SAME order on
|
|
222
|
+
* its OWN seats — the two never read each other. */
|
|
223
|
+
resolvedWriteFace: WriteFaceSeat | undefined;
|
|
224
|
+
/** write-back — the hands-LESS legs' write face in force (the posture is a property of the RUN, not the mount): the
|
|
225
|
+
* delegation carriers and the checkpoint mint read it on every leg. */
|
|
226
|
+
handsLessResolvedWriteFace: WriteFaceSeat | undefined;
|
|
204
227
|
/** write-back (D-3 hold) — the NORMALIZED deny-set additions in force (deployment ∪ task ∪
|
|
205
228
|
* resume-seed; zero-builtin compile — the persisted/carrier form, never the judged union's
|
|
206
229
|
* post-dedup remainder). Driver assigns onto its outer `let`. */
|
|
@@ -234,7 +257,7 @@ export interface PrepareHandsReadFaceResult {
|
|
|
234
257
|
* task additions). Undefined ⇔ hands-less (its consumers only exist with hands). */
|
|
235
258
|
readDenyMatcher: ReadDenyMatcher | undefined;
|
|
236
259
|
/** owned — the shell-seat PROVENANCE set: the shell-running tools whose tier the engine's shell seat (read
|
|
237
|
-
* boundary + shellGate
|
|
260
|
+
* boundary + shape tier + shellGate floor) is the SOLE POSITIVE source of — no explicit deployment mark on that name.
|
|
238
261
|
* The one seat every consumer reads (risk grading, the gate's tighten attribution, the persisted-rule mandate
|
|
239
262
|
* boundary): `shellGatedTools.has(toolName)`. Minted here, never grown afterwards; empty when the seat fold
|
|
240
263
|
* did not run (no real writable shell). */
|
|
@@ -1,19 +1,22 @@
|
|
|
1
1
|
import { emitTrace } from "../trace.js";
|
|
2
2
|
import { StubExecutionEnv } from "../stub-env.js";
|
|
3
3
|
import { hasBackgroundShell } from "../background-shell.js";
|
|
4
|
-
import { boundaryIsTheSandbox } from "../remote-env.js";
|
|
5
4
|
import { defaultTaskRegistry } from "../task-registry.js";
|
|
6
|
-
import { bashReversibilityProbe, activeReadDenyBuiltinTiers, compileReadDeny, readDenySeat, createHandsToolkit, deploymentFaceClampNotice, pdfModelCapabilitiesOf, inheritSessionReadFileState, resolveContainmentFace, seedReadFileStateFromContext, seedReadFileStateFromTranscript, } from "../../tools/fs/index.js";
|
|
5
|
+
import { bashReversibilityProbe, activeReadDenyBuiltinTiers, readDenyBuiltinConfigOf, compileReadDeny, compileWriteDeny, withRuleRowsWhereTheyLead, readDenySeat, createHandsToolkit, deploymentFaceClampNotice, pdfModelCapabilitiesOf, inheritSessionReadFileState, backgroundOutputReadExemption, resolveContainmentFace, seedReadFileStateFromContext, seedReadFileStateFromTranscript, } from "../../tools/fs/index.js";
|
|
7
6
|
import { resolveKey, sha256 } from "../../tools/fs/safety.js";
|
|
7
|
+
import { placeReadDenyAtHome, whereItsAnsweredPrefixLeads } from "../../tools/fs/read-deny-home.js";
|
|
8
8
|
import { createShellOverflowSpoolFence } from "../../tools/fs/fs-shared.js";
|
|
9
9
|
import { createShellReadStation } from "../../tools/fs/shell-read-check.js";
|
|
10
|
+
import { composeReadChecks, createFileReadStation } from "../../tools/fs/file-read-check.js";
|
|
11
|
+
import { readTargetToolNames } from "../tool-registry.js";
|
|
10
12
|
import { normalizeFileText } from "../../tools/fs/encoding.js";
|
|
11
13
|
import { wholeFileRecordsFromTranscript } from "./session-file-state-replay.js";
|
|
12
14
|
import { deliverEngineNotice } from "../types.js";
|
|
13
|
-
import { readRootGrantOver } from "./read-root-grant.js";
|
|
15
|
+
import { readRootGrantOver, rootAdmittingArtifactStore } from "./read-root-grant.js";
|
|
14
16
|
import { sessionSeatAnchorHolds } from "./session-read-file-states.js";
|
|
15
17
|
import { rebaseWorkspacePath } from "./workspace-path.js";
|
|
16
18
|
import { RosterBuilder } from "../tool-roster.js";
|
|
19
|
+
import { modePolicyOf } from "../mode-policy.js";
|
|
17
20
|
const faceClampAnnouncedSinks = { read: new WeakSet(), write: new WeakSet() };
|
|
18
21
|
const faceClampConsoleAnnounced = { read: false, write: false };
|
|
19
22
|
function announceDeploymentFaceClamp(kind, sink) {
|
|
@@ -52,6 +55,15 @@ function assertStringArraySeat(value, seat, entryLaw) {
|
|
|
52
55
|
}
|
|
53
56
|
}
|
|
54
57
|
}
|
|
58
|
+
function writeFaceReader(input, onDeploymentClamp) {
|
|
59
|
+
const { spec, deps, resume, fullShellReachable, permissionMode } = input;
|
|
60
|
+
const seats = { kind: "write", readOnlyMount: spec.handsReadOnly === true, orgGoverned: deps.permissionRuleStore?.partitions.org === true, fullShellReachable };
|
|
61
|
+
resolveContainmentFace({ ...seats, specFace: spec.writeFace, depsFace: deps.writeFace, ...(onDeploymentClamp !== undefined ? { onDeploymentClamp } : {}) });
|
|
62
|
+
const recorded = resume === undefined ? "live" : resume.seed.writeFace?.face;
|
|
63
|
+
const folded = (word) => (recorded === "roots" ? "roots" : word === "open" && recorded !== "open" && recorded !== "live" ? undefined : word);
|
|
64
|
+
const faceUnder = (modeFace) => resolveContainmentFace({ ...seats, specFace: folded(spec.writeFace), depsFace: folded(deps.writeFace), ...(modeFace !== undefined ? { modeFace } : {}) });
|
|
65
|
+
return { inForce: () => faceUnder(modePolicyOf(permissionMode.current).writeFace), fenced: faceUnder("open") === "roots" };
|
|
66
|
+
}
|
|
55
67
|
export function resolveHandsLessReadFace(input) {
|
|
56
68
|
const { resume, fullShellReachable, spec, deps } = input;
|
|
57
69
|
assertStringArraySeat(deps.readDenyPatterns, "RunnerDeps.readDenyPatterns", "compiler-owned");
|
|
@@ -71,21 +83,9 @@ export function resolveHandsLessReadFace(input) {
|
|
|
71
83
|
});
|
|
72
84
|
const seed = resume?.seed.readFace;
|
|
73
85
|
handsLessResolvedFace = resume !== undefined && (seed === undefined || seed.face === "roots") ? "roots" : liveFace;
|
|
74
|
-
|
|
75
|
-
kind: "write",
|
|
76
|
-
specFace: spec.writeFace,
|
|
77
|
-
depsFace: deps.writeFace,
|
|
78
|
-
readOnlyMount: spec.handsReadOnly === true,
|
|
79
|
-
orgGoverned: deps.permissionRuleStore?.partitions.org === true,
|
|
80
|
-
fullShellReachable,
|
|
81
|
-
});
|
|
82
|
-
const seedWrite = resume?.seed.writeFace;
|
|
83
|
-
handsLessResolvedWriteFace = resume !== undefined && (seedWrite === undefined || seedWrite.face === "roots") ? "roots" : liveWriteFace;
|
|
86
|
+
handsLessResolvedWriteFace = writeFaceReader(input);
|
|
84
87
|
const denyAdditions = [...(deps.readDenyPatterns ?? []), ...(spec.readDenyPatterns ?? []), ...(seed?.denyEntries ?? [])];
|
|
85
|
-
readDenyBuiltinTiersActive = activeReadDenyBuiltinTiers({
|
|
86
|
-
...(deps.readDenyBuiltinTiers !== undefined ? { tiers: deps.readDenyBuiltinTiers } : {}),
|
|
87
|
-
...(deps.readDenyBuiltinExclude !== undefined ? { exclude: deps.readDenyBuiltinExclude } : {}),
|
|
88
|
-
});
|
|
88
|
+
readDenyBuiltinTiersActive = activeReadDenyBuiltinTiers(readDenyBuiltinConfigOf({ tiers: deps.readDenyBuiltinTiers, exclude: deps.readDenyBuiltinExclude }));
|
|
89
89
|
if (denyAdditions.length > 0) {
|
|
90
90
|
readDenyAdditionsNormalized = compileReadDeny(denyAdditions, "readDenyPatterns", { tiers: [] }).entries;
|
|
91
91
|
}
|
|
@@ -99,6 +99,7 @@ export function resolveHandsLessReadFace(input) {
|
|
|
99
99
|
rebaseWsPath: (p) => p,
|
|
100
100
|
backgroundTaskToolsActive: false,
|
|
101
101
|
shellReadCheck: undefined,
|
|
102
|
+
shellReadStationArmed: false,
|
|
102
103
|
shellReadAdmission: undefined,
|
|
103
104
|
resolvedReadFace: undefined,
|
|
104
105
|
resolvedWriteFace: undefined,
|
|
@@ -133,6 +134,8 @@ export async function prepareHandsMount(input) {
|
|
|
133
134
|
let rebaseWsPath = (p) => p;
|
|
134
135
|
let backgroundTaskToolsActive = false;
|
|
135
136
|
let shellReadStation;
|
|
137
|
+
let shellReadTools = new Set();
|
|
138
|
+
let fileReadStation;
|
|
136
139
|
let resolvedReadFace;
|
|
137
140
|
let resolvedWriteFace;
|
|
138
141
|
let readDenyAdditionsNormalized = [];
|
|
@@ -176,6 +179,10 @@ export async function prepareHandsMount(input) {
|
|
|
176
179
|
};
|
|
177
180
|
await canonicalizeExtraDirs(spec.additionalDirectories, "additionalDirectories", additionalRootsCanonical);
|
|
178
181
|
await canonicalizeExtraDirs(spec.additionalReadDirectories, "additionalReadDirectories", additionalReadRootsCanonical);
|
|
182
|
+
const standingCanonical = [];
|
|
183
|
+
await canonicalizeExtraDirs(input.persistedReadDirectories, "persistedReadRule", standingCanonical);
|
|
184
|
+
artifactStoreRef.standing = { keys: standingCanonical.filter((k) => !additionalReadRootsCanonical.includes(k) && !additionalRootsCanonical.includes(k)), readRoots: additionalReadRootsCanonical };
|
|
185
|
+
additionalReadRootsCanonical.push(...standingCanonical);
|
|
179
186
|
if (advertisedWritableDirs !== undefined) {
|
|
180
187
|
if (spec.envFacts?.scratchpadDir)
|
|
181
188
|
await advertisedWritableDirs.admit({ path: spec.envFacts.scratchpadDir, advertiser: "scratchpad", materializeMarker: ".sema-scratchpad" });
|
|
@@ -212,11 +219,10 @@ export async function prepareHandsMount(input) {
|
|
|
212
219
|
}
|
|
213
220
|
const seedReadFaceSection = resume?.seed.readFace;
|
|
214
221
|
const readDenyAdditions = [...(deps.readDenyPatterns ?? []), ...(spec.readDenyPatterns ?? []), ...(seedReadFaceSection?.denyEntries ?? [])];
|
|
215
|
-
const readDenyBuiltinCfg = {
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
readDenyMatcher = compileReadDeny(readDenyAdditions, "readDenyPatterns", readDenyBuiltinCfg);
|
|
222
|
+
const readDenyBuiltinCfg = readDenyBuiltinConfigOf({ tiers: deps.readDenyBuiltinTiers, exclude: deps.readDenyBuiltinExclude });
|
|
223
|
+
const pathDenyRules = await withRuleRowsWhereTheyLead(input.pathDenyRules, (head) => whereItsAnsweredPrefixLeads(executionEnv, head));
|
|
224
|
+
const readDenyPlacement = await placeReadDenyAtHome(executionEnv, (homes, linked) => compileReadDeny(readDenyAdditions, "readDenyPatterns", readDenyBuiltinCfg, pathDenyRules, homes, linked));
|
|
225
|
+
readDenyMatcher = readDenyPlacement.matcher;
|
|
220
226
|
readDenyBuiltinTiersActive = activeReadDenyBuiltinTiers(readDenyBuiltinCfg);
|
|
221
227
|
readDenyAdditionsNormalized = compileReadDeny(readDenyAdditions, "readDenyPatterns", { tiers: [] }).entries;
|
|
222
228
|
let liveReadFace = resolveContainmentFace({
|
|
@@ -231,19 +237,7 @@ export async function prepareHandsMount(input) {
|
|
|
231
237
|
if (resume !== undefined && (seedReadFaceSection === undefined || seedReadFaceSection.face === "roots"))
|
|
232
238
|
liveReadFace = "roots";
|
|
233
239
|
resolvedReadFace = liveReadFace;
|
|
234
|
-
|
|
235
|
-
kind: "write",
|
|
236
|
-
specFace: spec.writeFace,
|
|
237
|
-
depsFace: deps.writeFace,
|
|
238
|
-
readOnlyMount: handsReadOnly,
|
|
239
|
-
orgGoverned: deps.permissionRuleStore?.partitions.org === true,
|
|
240
|
-
fullShellReachable,
|
|
241
|
-
onDeploymentClamp: () => announceDeploymentFaceClamp("write", deps.onNotice),
|
|
242
|
-
});
|
|
243
|
-
const seedWriteFaceSection = resume?.seed.writeFace;
|
|
244
|
-
if (resume !== undefined && (seedWriteFaceSection === undefined || seedWriteFaceSection.face === "roots"))
|
|
245
|
-
liveWriteFace = "roots";
|
|
246
|
-
resolvedWriteFace = liveWriteFace;
|
|
240
|
+
resolvedWriteFace = writeFaceReader(input, () => announceDeploymentFaceClamp("write", deps.onNotice));
|
|
247
241
|
const sessionSeats = input.internals?.sessionReadStates;
|
|
248
242
|
let seatLeafId;
|
|
249
243
|
try {
|
|
@@ -297,31 +291,55 @@ export async function prepareHandsMount(input) {
|
|
|
297
291
|
}
|
|
298
292
|
: undefined;
|
|
299
293
|
const denySeat = readDenySeat(readDenyMatcher);
|
|
294
|
+
const writeDeny = compileWriteDeny(pathDenyRules);
|
|
295
|
+
const writeDenySeat = writeDeny.ruleRows.length > 0 ? { writeDenyMatch: (p) => writeDeny.matchPath(p)?.pattern ?? null } : {};
|
|
300
296
|
const shellReadBoundary = () => ({
|
|
301
297
|
roots: [rootCanonical, ...additionalRootsCanonical, ...additionalReadRootsCanonical],
|
|
302
298
|
...(handsCwdRef?.current !== undefined ? { cwd: handsCwdRef.current } : {}),
|
|
303
299
|
...(executionEnv.homeDir !== undefined ? { homeDir: executionEnv.homeDir } : {}),
|
|
304
300
|
...denySeat,
|
|
301
|
+
...writeDenySeat,
|
|
302
|
+
writeRoots: [rootCanonical, ...additionalRootsCanonical],
|
|
303
|
+
...((judge) => (judge !== undefined ? { writeProtectionJudge: judge } : {}))(input.writeGuards?.protection()),
|
|
304
|
+
...(input.writeGuards?.target !== undefined ? { writeTargetGuard: input.writeGuards.target } : {}),
|
|
305
305
|
...(resolvedReadFace !== undefined ? { face: resolvedReadFace } : {}),
|
|
306
306
|
});
|
|
307
307
|
if (handsIncludeShell) {
|
|
308
308
|
const spoolFence = handsReadOnly ? createShellOverflowSpoolFence(executionEnv) : undefined;
|
|
309
|
-
const readonlyBoundary = () => spoolFence !== undefined ? { roots: [rootCanonical, ...additionalRootsCanonical, ...additionalReadRootsCanonical], cwd: rootCanonical, ...denySeat, alsoInside: spoolFence } : undefined;
|
|
309
|
+
const readonlyBoundary = () => spoolFence !== undefined ? { roots: [rootCanonical, ...additionalRootsCanonical, ...additionalReadRootsCanonical], cwd: rootCanonical, ...denySeat, ...writeDenySeat, alsoInside: spoolFence } : undefined;
|
|
310
|
+
shellReadTools = new Set(handsReadOnly ? ["Bash"] : backgroundTaskToolsActive ? ["Bash", "Monitor"] : ["Bash"]);
|
|
310
311
|
shellReadStation = createShellReadStation({
|
|
311
312
|
env: executionEnv,
|
|
312
|
-
tools:
|
|
313
|
-
seatOf: () => (handsReadOnly ? "readonly" :
|
|
313
|
+
tools: shellReadTools,
|
|
314
|
+
seatOf: () => (handsReadOnly ? "readonly" : "full_shell"),
|
|
314
315
|
boundaryOf: () => readonlyBoundary() ?? shellReadBoundary(), rootsCanonical: true,
|
|
315
316
|
});
|
|
316
317
|
}
|
|
318
|
+
if (!handsReadOnly) {
|
|
319
|
+
const bgOutputExempt = backgroundOutputReadExemption(defaultTaskRegistry, { taskOwner: hostTaskId, taskScope, ...(sessionId !== undefined ? { sessionId } : {}) }, executionEnv);
|
|
320
|
+
fileReadStation = createFileReadStation({
|
|
321
|
+
env: executionEnv,
|
|
322
|
+
rootCanonical,
|
|
323
|
+
roots: () => [...additionalRootsCanonical, ...additionalReadRootsCanonical],
|
|
324
|
+
...(readDenyMatcher !== undefined ? { readDeny: readDenyMatcher } : {}),
|
|
325
|
+
...(liveReadFace !== undefined ? { readFace: liveReadFace } : {}),
|
|
326
|
+
...("homeDir" in executionEnv && executionEnv.homeDir !== undefined ? { homeDir: executionEnv.homeDir } : {}),
|
|
327
|
+
tools: readTargetToolNames(),
|
|
328
|
+
excluded: (key) => artifactStoreRef.current !== undefined && rootAdmittingArtifactStore(artifactStoreRef.current, [key]) !== undefined,
|
|
329
|
+
exempt: (key) => bgOutputExempt?.(key) === true,
|
|
330
|
+
});
|
|
331
|
+
}
|
|
317
332
|
const band = createHandsToolkit(executionEnv, readFileState, rootCanonical, {
|
|
318
333
|
...(additionalRootsCanonical.length > 0 ? { additionalRoots: additionalRootsCanonical } : {}),
|
|
319
334
|
additionalReadRoots: additionalReadRootsCanonical,
|
|
320
335
|
...(readDenyAdditions.length > 0 ? { readDenyPatterns: readDenyAdditions } : {}),
|
|
336
|
+
...(pathDenyRules.length > 0 ? { pathDenyRules } : {}),
|
|
337
|
+
readDenyHomes: readDenyPlacement.homes,
|
|
338
|
+
readDenyHomeLinks: readDenyPlacement.linked,
|
|
321
339
|
...(readDenyBuiltinCfg.tiers !== undefined ? { readDenyBuiltinTiers: readDenyBuiltinCfg.tiers } : {}),
|
|
322
340
|
...(readDenyBuiltinCfg.exclude !== undefined ? { readDenyBuiltinExclude: readDenyBuiltinCfg.exclude } : {}),
|
|
323
341
|
readFace: liveReadFace,
|
|
324
|
-
|
|
342
|
+
writeFaceLive: resolvedWriteFace.inForce,
|
|
325
343
|
reminderMark: input.reminderMark,
|
|
326
344
|
reminderDisclosureCounts: input.reminderDisclosureCounts,
|
|
327
345
|
...(input.trackFileEdit !== undefined ? { trackFileEdit: input.trackFileEdit } : {}),
|
|
@@ -329,6 +347,7 @@ export async function prepareHandsMount(input) {
|
|
|
329
347
|
includeShell: handsIncludeShell,
|
|
330
348
|
readOnly: handsReadOnly,
|
|
331
349
|
...(shellReadStation !== undefined ? { shellReadAdmission: shellReadStation.admit } : {}),
|
|
350
|
+
...(fileReadStation !== undefined ? { readAdmission: fileReadStation.admitted } : {}),
|
|
332
351
|
...(handsCwdRef ? { cwdRef: handsCwdRef } : {}),
|
|
333
352
|
taskRegistry: defaultTaskRegistry,
|
|
334
353
|
taskOwner: hostTaskId,
|
|
@@ -368,27 +387,18 @@ export async function prepareHandsMount(input) {
|
|
|
368
387
|
}
|
|
369
388
|
}
|
|
370
389
|
roster.mountAll(band.map((t) => (envHandToolNames.add(t.name), firstPartyOffload(t))), { source: "builtin", mountedBy: "hands" });
|
|
371
|
-
const
|
|
390
|
+
const shellFloor = effectiveShellGate;
|
|
372
391
|
if (!(executionEnv instanceof StubExecutionEnv) && spec.handsReadOnly !== true) {
|
|
373
|
-
if (shellGate === "off") {
|
|
374
|
-
deps.onError?.(new Error(`shell gate doctrine is "off" while a real writable shell (Bash) is mounted — the read boundary is ` +
|
|
375
|
-
`still judged on every command (a deny-listed read is refused; an out-of-root read asks, and the ask is mandated${boundaryIsTheSandbox(executionEnv) ? "; under an isolated sandbox the paths are judged as written" : "; the paths are judged as the filesystem resolves them, symlinks and patterns included"}), but no ` +
|
|
376
|
-
`residual shell-risk classifier is installed: writes, egress and unknown commands are adjudicated by ` +
|
|
377
|
-
`the ordinary policy/hook chain only. Set spec.shellGate to "classify" or "always" if this deployment ` +
|
|
378
|
-
`expects doctrine-gated shell behavior.`), { phase: "config", sessionId, classification: "shell-gate-off" });
|
|
379
|
-
}
|
|
380
392
|
const installShellSeat = (name) => {
|
|
381
393
|
const tierBefore = irreversibilityTier.get(name);
|
|
382
394
|
const seatOwned = !egressTools.has(name) && tierBefore !== "always" && tierBefore !== "maybe";
|
|
383
|
-
|
|
384
|
-
shellGatedTools.add(name);
|
|
385
|
-
if (shellGate === "off" && tierBefore !== "always")
|
|
386
|
-
return;
|
|
387
|
-
const tier = shellGate === "always" || tierBefore === "always" ? "always" : "maybe";
|
|
395
|
+
const tier = shellFloor === "always" || tierBefore === "always" ? "always" : "maybe";
|
|
388
396
|
irreversibilityTier.set(name, tier);
|
|
389
397
|
irreversibleTools.add(name);
|
|
390
398
|
if (seatOwned && tier === "maybe")
|
|
391
399
|
reversibilityProbes.set(name, bashReversibilityProbe(undefined, shellReadBoundary));
|
|
400
|
+
if (seatOwned)
|
|
401
|
+
shellGatedTools.add(name);
|
|
392
402
|
};
|
|
393
403
|
installShellSeat("Bash");
|
|
394
404
|
if (backgroundTaskToolsActive)
|
|
@@ -403,7 +413,8 @@ export async function prepareHandsMount(input) {
|
|
|
403
413
|
wsSnapshot,
|
|
404
414
|
rebaseWsPath,
|
|
405
415
|
backgroundTaskToolsActive,
|
|
406
|
-
shellReadCheck: shellReadStation
|
|
416
|
+
shellReadCheck: composeReadChecks([...(shellReadStation !== undefined ? [{ check: shellReadStation.check, tools: shellReadTools }] : []), ...(fileReadStation !== undefined ? [fileReadStation] : [])]),
|
|
417
|
+
shellReadStationArmed: shellReadStation !== undefined,
|
|
407
418
|
shellReadAdmission: shellReadStation?.admit,
|
|
408
419
|
resolvedReadFace,
|
|
409
420
|
resolvedWriteFace,
|