@sema-agent/core 7.33.8 → 7.34.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 +339 -0
- 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/open-responses.js +1 -0
- package/dist/brain/openai.js +1 -0
- package/dist/brain/stream-engine.d.ts +3 -0
- package/dist/brain/stream-engine.js +2 -1
- package/dist/core/a2a.d.ts +3 -1
- package/dist/core/a2a.js +1 -1
- package/dist/core/ask-origin.d.ts +351 -154
- package/dist/core/ask-origin.js +223 -45
- 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 +50 -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 +17 -1
- package/dist/core/gate-lanes.js +111 -46
- package/dist/core/gate-outcome.d.ts +40 -7
- package/dist/core/gate-outcome.js +26 -4
- package/dist/core/governance-codes.d.ts +1 -1
- package/dist/core/governance-codes.js +12 -0
- package/dist/core/hooks.d.ts +50 -29
- package/dist/core/hooks.js +1 -3
- package/dist/core/mcp.js +1 -1
- package/dist/core/mode-policy.d.ts +182 -0
- package/dist/core/mode-policy.js +179 -0
- package/dist/core/oracle-isolation.d.ts +3 -3
- package/dist/core/permission-rule-model.d.ts +16 -3
- package/dist/core/permission-rule-model.js +36 -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 +35 -8
- package/dist/core/persisted-rule-arms.d.ts +10 -9
- package/dist/core/persisted-rule-arms.js +9 -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/classifier-ladder.d.ts +43 -1
- package/dist/core/runner/classifier-ladder.js +29 -0
- package/dist/core/runner/contracts.d.ts +37 -35
- package/dist/core/runner/denial-limit-arms.d.ts +30 -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/gate-exit.d.ts +30 -4
- package/dist/core/runner/gate-exit.js +28 -7
- package/dist/core/runner/permission-rule-lanes.d.ts +9 -5
- package/dist/core/runner/permission-rule-lanes.js +44 -40
- 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 +5 -3
- package/dist/core/runner/prepare-ask-lane.js +1 -1
- 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-gate-stations.d.ts +33 -6
- package/dist/core/runner/prepare-gate-stations.js +32 -19
- 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 +22 -22
- package/dist/core/runner/prepare-inherited-gate.js +37 -23
- package/dist/core/runner/prepare-park-ask.d.ts +2 -2
- package/dist/core/runner/prepare-park-ask.js +2 -1
- package/dist/core/runner/prepare-policy-chain.d.ts +22 -2
- package/dist/core/runner/prepare-policy-chain.js +39 -12
- 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 +51 -22
- 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-identity-wiring.js +1 -1
- package/dist/core/runner/run-leg.js +24 -15
- package/dist/core/runner/run-notification-lane.js +1 -0
- 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.js +3 -0
- 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/sensitive-path-policy.d.ts +4 -2
- package/dist/core/sensitive-path-policy.js +39 -30
- 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 +54 -25
- package/dist/core/tool-policy.js +43 -2
- package/dist/core/tool-registry.d.ts +2 -0
- package/dist/core/tool-registry.js +3 -0
- package/dist/core/tool-spec.d.ts +8 -9
- package/dist/core/trace.d.ts +7 -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 +3 -0
- package/dist/engine/execution-env/node-execution-env.js +11 -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/index.d.ts +15 -5
- package/dist/index.js +12 -3
- 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/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 +49 -0
- package/dist/tools/fs/file-read-check.js +71 -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 +42 -11
- package/dist/tools/fs/shell-read-check.js +188 -13
- 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 +103 -11
- package/wire-changes/core@7.34.0.json +2621 -0
|
@@ -283,7 +283,7 @@ export function createTurnBoundary(input) {
|
|
|
283
283
|
cadenceTurn: rs.counters.cadenceTurns,
|
|
284
284
|
todoMounted: todoToolMounted,
|
|
285
285
|
taskListMounted: taskToolsMounted,
|
|
286
|
-
planActive: prepared.
|
|
286
|
+
planActive: prepared.permissionMode.current === "plan",
|
|
287
287
|
config: {
|
|
288
288
|
todoReminder: rs.attach.attachmentsCfg?.todoReminder === true,
|
|
289
289
|
...(rs.attach.attachmentsCfg?.todoReminderMode !== undefined ? { todoReminderMode: rs.attach.attachmentsCfg.todoReminderMode } : {}),
|
|
@@ -40,6 +40,7 @@ import { streamReap } from "./stream-reap.js";
|
|
|
40
40
|
import { streamSteerVerb } from "./stream-steer-verb.js";
|
|
41
41
|
import { streamLifecycleVerbs } from "./stream-lifecycle-verbs.js";
|
|
42
42
|
import { streamHaltVerbs } from "./stream-halt-verbs.js";
|
|
43
|
+
import { streamModeVerb } from "./stream-mode-verb.js";
|
|
43
44
|
import { runNotificationLane } from "./run-notification-lane.js";
|
|
44
45
|
import { assertNoRetiredRunnerDeps } from "./retired-runner-deps.js";
|
|
45
46
|
import { runLeg } from "./run-leg.js";
|
|
@@ -417,6 +418,7 @@ export class Runner {
|
|
|
417
418
|
const { steer } = streamSteerVerb({ spec, internals, queue, acceptedSteerInputs, live, ready, orTimeout, readyTimeoutMs: READY_TIMEOUT_MS, steeringError, runner: this.depsSeat });
|
|
418
419
|
const { notify, optOutMemoryCapture, reconnectMcpServer, compact, detach, interrupt } = streamLifecycleVerbs({ live, ready, orTimeout, steeringError, notifyRef, captureOptOutRef, mcpRedialRef, manualCompactRef, detachHub });
|
|
419
420
|
const { halt, destroy } = streamHaltVerbs({ spec, live, ready, orTimeout, readyTimeoutMs: READY_TIMEOUT_MS, run, reapSuspended, runner: this.depsSeat });
|
|
421
|
+
const { setPermissionMode } = streamModeVerb({ live, ready, orTimeout, steeringError });
|
|
420
422
|
return {
|
|
421
423
|
[Symbol.asyncIterator]: () => queue[Symbol.asyncIterator](),
|
|
422
424
|
result: async () => {
|
|
@@ -430,6 +432,7 @@ export class Runner {
|
|
|
430
432
|
steer,
|
|
431
433
|
notify,
|
|
432
434
|
optOutMemoryCapture,
|
|
435
|
+
setPermissionMode,
|
|
433
436
|
reconnectMcpServer,
|
|
434
437
|
compact,
|
|
435
438
|
detach,
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The TaskStream façade's PERMISSION-MODE verb: `setPermissionMode` — the one entrance a running leg's mode changes
|
|
3
|
+
* through (a shell's mode toggle, a serving layer's `permission_mode` control frame). ONE read of the argument at the
|
|
4
|
+
* wire boundary, the steer lane's liveness gate, then the leg's live mode seat: the requested word is rewritten, the
|
|
5
|
+
* fold against the deployment's deny bits and the classifier face is re-judged (a fold is announced by the seat), a
|
|
6
|
+
* leg that became `auto` arms its classifier, and the same three-member receipt the wiring manifest carries comes
|
|
7
|
+
* back — EVERY call answers one, whether or not the effective mode moved, so a caller never has to render
|
|
8
|
+
* optimistically. The next tool call reads the new mode at the gate's arbitration point; a child leg derives its own
|
|
9
|
+
* effective mode from this seat on its next read (no forwarding frame).
|
|
10
|
+
*/
|
|
11
|
+
import type { TaskStream } from "../types.js";
|
|
12
|
+
import type { LiveHandle, TaskStreamLiveSeat } from "./contracts.js";
|
|
13
|
+
export interface StreamModeVerbInput {
|
|
14
|
+
/** borrowed-readonly — the stream's live state: `resultValue` (the finished-task refusal) and `handle` (the mode seat rides it). */
|
|
15
|
+
live: TaskStreamLiveSeat;
|
|
16
|
+
/** borrowed-readonly — the readiness promise the verb awaits before it touches the handle. */
|
|
17
|
+
ready: Promise<LiveHandle | undefined>;
|
|
18
|
+
/** borrowed-readonly — the driver's timeout race: a wedged prepare cannot hang the verb on `ready`. */
|
|
19
|
+
orTimeout: <T>(p: Promise<T>) => Promise<T | undefined>;
|
|
20
|
+
/** borrowed-readonly — the driver's refusal factory, the steer verb's own `steeringError` seat. */
|
|
21
|
+
steeringError: (msg: string, code?: string) => Error;
|
|
22
|
+
}
|
|
23
|
+
export interface StreamModeVerbResult {
|
|
24
|
+
/** The stream's `setPermissionMode` verb. */
|
|
25
|
+
setPermissionMode: TaskStream["setPermissionMode"];
|
|
26
|
+
}
|
|
27
|
+
export declare function streamModeVerb(input: StreamModeVerbInput): StreamModeVerbResult;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { isPermissionMode, PERMISSION_MODES } from "../ask-origin.js";
|
|
2
|
+
export function streamModeVerb(input) {
|
|
3
|
+
const { live, ready, orTimeout, steeringError } = input;
|
|
4
|
+
const setPermissionMode = async (mode) => {
|
|
5
|
+
const modeIn = mode;
|
|
6
|
+
if (!isPermissionMode(modeIn)) {
|
|
7
|
+
const e = new Error(`cannot set the permission mode: it must be one of ${PERMISSION_MODES.join("/")} (got ${JSON.stringify(modeIn)})`);
|
|
8
|
+
e.code = "steering.invalid_content";
|
|
9
|
+
throw e;
|
|
10
|
+
}
|
|
11
|
+
if (live.resultValue)
|
|
12
|
+
throw steeringError("the task has already finished");
|
|
13
|
+
const h = live.handle ?? (await orTimeout(ready));
|
|
14
|
+
if (!h)
|
|
15
|
+
throw steeringError("the task is not running");
|
|
16
|
+
return h.permissionMode.steer(modeIn);
|
|
17
|
+
};
|
|
18
|
+
return { setPermissionMode };
|
|
19
|
+
}
|
|
@@ -419,9 +419,9 @@ export interface AttachmentInputs {
|
|
|
419
419
|
todoMounted: boolean;
|
|
420
420
|
/** Whether the task-family tools are mounted. Both mounted ⇒ task_reminder wins (CC isTodoV2 branch). */
|
|
421
421
|
taskListMounted: boolean;
|
|
422
|
-
/**
|
|
423
|
-
* here simply stops the producer — no re-entry cycle
|
|
424
|
-
*
|
|
422
|
+
/** Whether the leg's effective permission mode is `plan` right now (the one mode seat, whichever entrance set it:
|
|
423
|
+
* the spec's word, a steer, the model's EnterPlanMode). A false here simply stops the producer — no re-entry cycle
|
|
424
|
+
* reset (documented deviation from CC's countPlanModeAttachmentsSinceLastExit). */
|
|
425
425
|
planActive: boolean;
|
|
426
426
|
/** SR-3 budget_usd lane (CC 198 `QFm` producer :480424-480428 + renderer :600225; 220 `_Fy` @609096
|
|
427
427
|
* with the mount at @607474): the run's cost progress against its $ ceiling, in USD. Only passed
|
|
@@ -761,7 +761,7 @@ function mcpDroppedToolsBound(inp) {
|
|
|
761
761
|
const single = first === undefined ? 0 : MCP_DROPPED_FIXED_MAX_BYTES + bytes(first.tool) + bytes(first.server) + bytes(first.reason) + 32;
|
|
762
762
|
return Math.max(MCP_DROPPED_BATCH_MAX_BYTES, single);
|
|
763
763
|
}
|
|
764
|
-
const PLAN_MODE_FULL_BODY = `Plan mode is active. The user indicated that they do not want you to execute yet -- you MUST NOT make any edits, run any non-readonly tools (including changing configs or making commits), or otherwise make any changes to the system. This supercedes any other instructions you have received. Instead, you should: research and analyze with read-only tools, and when you have formed a concrete plan, present it with the ${PRESENT_PLAN_TOOL_NAME} tool for approval. That tool inherently requests plan approval — never ask for approval via plain text.`;
|
|
764
|
+
const PLAN_MODE_FULL_BODY = `Plan mode is active. The user indicated that they do not want you to execute yet -- you MUST NOT make any edits, run any non-readonly tools (including changing configs or making commits), or otherwise make any changes to the system — every such call asks the user first, and is refused where nobody can answer. This supercedes any other instructions you have received. Instead, you should: research and analyze with read-only tools, and when you have formed a concrete plan, present it with the ${PRESENT_PLAN_TOOL_NAME} tool for approval. That tool inherently requests plan approval — never ask for approval via plain text.`;
|
|
765
765
|
const PLAN_MODE_SPARSE_BODY = `Plan mode still active (see full instructions earlier in conversation). Read-only: no edits, commits, or config changes. When your plan is ready, present it with the ${PRESENT_PLAN_TOOL_NAME} tool for approval. Never ask about plan approval via plain text.`;
|
|
766
766
|
const TRUNCATION_MARKER = "\n…[truncated]";
|
|
767
767
|
const MIN_KEEP_BYTES = 160;
|
|
@@ -24,6 +24,12 @@ import type { ExecutionEnv } from "../../internal/harness.js";
|
|
|
24
24
|
import type { RunnerDeps } from "../runner-deps.js";
|
|
25
25
|
import type { ToolRoster } from "../tool-roster.js";
|
|
26
26
|
import { type WriteProtectionMount } from "../write-protect.js";
|
|
27
|
+
import type { ShellReadBoundary } from "../../tools/fs/shell-read-judge.js";
|
|
28
|
+
/** The leg's mount plus the same judge over a bare ABSOLUTE target — what the shell station asks about each write target
|
|
29
|
+
* a command names (`ShellReadBoundary.writeProtectionJudge`): `true` ⇔ a row covers it. */
|
|
30
|
+
export interface LegWriteProtection extends WriteProtectionMount {
|
|
31
|
+
readonly judgeTarget: NonNullable<ShellReadBoundary["writeProtectionJudge"]>;
|
|
32
|
+
}
|
|
27
33
|
export declare function mountWriteProtection(input: {
|
|
28
34
|
/** borrowed-readonly — the deployment seats: the table (`writeProtectedPaths`; absent ⇒ the default
|
|
29
35
|
* table, `[]` ⇒ opted out), the data root the memory library is materialized under
|
|
@@ -38,4 +44,4 @@ export declare function mountWriteProtection(input: {
|
|
|
38
44
|
roster: ToolRoster;
|
|
39
45
|
sessionId: string;
|
|
40
46
|
runId: string;
|
|
41
|
-
}):
|
|
47
|
+
}): LegWriteProtection | undefined;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { deliverEngineNotice } from "../engine-notice.js";
|
|
2
2
|
import { protectivePathTargetOf } from "../tool-registry.js";
|
|
3
|
-
import {
|
|
3
|
+
import { compileWriteProtection, writeProtectionMountOver } from "../write-protect.js";
|
|
4
4
|
const ABSENCE_REASON = { symlink: "unresolved-symlink", target: "unresolvable-target" };
|
|
5
5
|
export function mountWriteProtection(input) {
|
|
6
6
|
const announced = new Set();
|
|
7
|
-
const
|
|
7
|
+
const matcher = compileWriteProtection(input.deps.writeProtectedPaths, {
|
|
8
8
|
...(input.deps.memoryEngineDir !== undefined ? { dataRoot: input.deps.memoryEngineDir } : {}),
|
|
9
9
|
...(input.env !== undefined ? { env: input.env } : {}),
|
|
10
10
|
rootPath: input.rootPath,
|
|
@@ -25,7 +25,10 @@ export function mountWriteProtection(input) {
|
|
|
25
25
|
});
|
|
26
26
|
},
|
|
27
27
|
});
|
|
28
|
-
if (
|
|
28
|
+
if (matcher === undefined)
|
|
29
|
+
return undefined;
|
|
30
|
+
const mount = { ...writeProtectionMountOver(matcher, input.rootPath), judgeTarget: async (target, opts) => (await matcher.judge(target, { ...(opts?.signal !== undefined ? { signal: opts.signal } : {}), ...(opts?.canonical === true ? { canonical: true } : {}) })).kind === "hit" };
|
|
31
|
+
if (mount.targetView !== "spelling-only")
|
|
29
32
|
return mount;
|
|
30
33
|
const covered = input.roster.entries
|
|
31
34
|
.filter((e) => [e.name, ...e.aliases].some((name) => {
|
|
@@ -204,12 +204,11 @@ export interface RunnerDeps {
|
|
|
204
204
|
*/
|
|
205
205
|
readDenyPatterns?: readonly import("../tools/fs/read-deny.js").ReadDenyEntry[];
|
|
206
206
|
/**
|
|
207
|
-
* The deployment's built-in deny-table TIER selection — the
|
|
208
|
-
*
|
|
209
|
-
*
|
|
210
|
-
*
|
|
211
|
-
*
|
|
212
|
-
* refuses a read or demotes a shell one. A leg's active tiers are REPORTED on its wiring manifest
|
|
207
|
+
* The deployment's built-in deny-table TIER selection — the switch for the built-in table. EXACTLY the
|
|
208
|
+
* listed tiers of {@link import("../tools/fs/read-deny.js").READ_FACE_BUILTIN_DENY_TABLE} are active (`[]` =
|
|
209
|
+
* none — explicit and legal: nothing from that table refuses a read); ABSENT =
|
|
210
|
+
* {@link import("../tools/fs/read-deny.js").READ_DENY_DEFAULT_TIERS} (`credentials` and the home-anchored
|
|
211
|
+
* `agent-config`). A leg's active tiers are REPORTED on its wiring manifest
|
|
213
212
|
* ({@link import("./wiring-manifest.js").WiringManifest.readDeny}). Composes with
|
|
214
213
|
* {@link readDenyBuiltinExclude} (tiers first, then row removal); never touches the additions
|
|
215
214
|
* seats or the write faces. Unknown tier names refuse loudly at prepare (#123). DEPLOYMENT seat
|
|
@@ -633,9 +632,9 @@ export interface RunnerDeps {
|
|
|
633
632
|
* `autoMode` (the per-principal deny bit of the user-enabled auto mode), `allowMemoryOptOut` (may this
|
|
634
633
|
* principal declare a capture opt-out), and `forceDurableGate` (forces a run onto the durable-approval
|
|
635
634
|
* path so a policy `ask` suspends to the wire — where a checkpoint store is wired; a store-less leg
|
|
636
|
-
* announces `config.durable_gate_unavailable`)
|
|
637
|
-
*
|
|
638
|
-
* (
|
|
635
|
+
* announces `config.durable_gate_unavailable`), and `bypassPermissions` (the deny bit of the
|
|
636
|
+
* `bypassPermissions` mode — a denied request folds to `default`, announced). `allowUltracode` is a shell/UX
|
|
637
|
+
* concern (reasoning-tier, not a core primitive).
|
|
639
638
|
*/
|
|
640
639
|
runtimeCapsResolver?: (principal: string | undefined) => RuntimeCaps | undefined | Promise<RuntimeCaps | undefined>;
|
|
641
640
|
/**
|
|
@@ -705,7 +704,7 @@ export interface RunnerDeps {
|
|
|
705
704
|
/**
|
|
706
705
|
* design/143 批2b — the auto-mode classifier's DEPLOYMENT assembly face. This is the
|
|
707
706
|
* CAPABILITY arm of the three-arm arming: a run arms auto mode only when the task carries the
|
|
708
|
-
* auto-mode INTENT (
|
|
707
|
+
* auto-mode INTENT (`TaskSpec.permissionMode: "auto"`, or the mode inherited on the chain by an
|
|
709
708
|
* engine-spawned child) AND this face is present AND the per-principal DENY bit is not set
|
|
710
709
|
* ({@link RuntimeCaps.autoMode}` !== false` — absent is not a denial).
|
|
711
710
|
*
|
|
@@ -717,6 +716,14 @@ export interface RunnerDeps {
|
|
|
717
716
|
* `summarize → default`; tier default `flash`) via the brain's simple-completion pipe — same
|
|
718
717
|
* lane as compaction/consolidation, no tools, no session writes.
|
|
719
718
|
*/
|
|
719
|
+
/**
|
|
720
|
+
* The deployment's A2A peer switches. `isolatePeers: true` makes every A2A peer skill's egress default an
|
|
721
|
+
* OPERATOR mandate: its ask is `operator_forced` (kept under `bypassPermissions`, not settled by `dontAsk`)
|
|
722
|
+
* instead of the releasable `tool_marks_a2a`. Absent ⇒ the default (a permission mode may release it, disclosed).
|
|
723
|
+
*/
|
|
724
|
+
a2a?: {
|
|
725
|
+
isolatePeers?: true;
|
|
726
|
+
};
|
|
720
727
|
autoMode?: {
|
|
721
728
|
/** Rule overrides for the four permissions sections (omitted section = CC defaults verbatim). */
|
|
722
729
|
rules?: import("./auto-mode-prompt.js").AutoModeRules;
|
|
@@ -726,9 +733,15 @@ export interface RunnerDeps {
|
|
|
726
733
|
sessionContext?: readonly string[];
|
|
727
734
|
/** Transcript-window bounds for the classify prompt (defaults: 40 entries / 2000 chars each). */
|
|
728
735
|
window?: import("./auto-mode-prompt.js").AutoModeWindowOptions;
|
|
729
|
-
/** Classify round-trip cap, ms
|
|
730
|
-
*
|
|
731
|
-
*
|
|
736
|
+
/** Classify round-trip cap, ms — ONE ladder rung (see `classifierFallback`), not the whole classification;
|
|
737
|
+
* default `AUTO_MODE_DEFAULT_TIMEOUT_MS` (180 000, sized for a self-hosted gateway's queue + prefill; worst
|
|
738
|
+
* case two rungs = 360 s). A rung past the cap did not rule: the walk moves to the next candidate; with none
|
|
739
|
+
* left the classification is `unavailable` — the call goes on to a person where the leg has an approval seat
|
|
740
|
+
* (a live approver or a durable park), and is DENIED with a sentence that says so where it has none; the
|
|
741
|
+
* user hears it either way (`classifier.unavailable`). The next call is classified afresh. A present value
|
|
742
|
+
* that is not a number of milliseconds in [1, 2147483647] is REFUSED loudly at every leg's prepare while this face
|
|
743
|
+
* is present, whatever mode the leg starts in (`config.auto_mode_timeout_invalid`) — never clamped, never read as the
|
|
744
|
+
* default; there is no OFF. */
|
|
732
745
|
timeoutMs?: number;
|
|
733
746
|
/**
|
|
734
747
|
* What a classification does when the classifier SEAT does not rule (the leg threw / rejected, the cap
|
|
@@ -744,8 +757,8 @@ export interface RunnerDeps {
|
|
|
744
757
|
* (worst case two `timeoutMs` caps); `"none"` buys that back for the deny.
|
|
745
758
|
*
|
|
746
759
|
* A value outside the closed set ({@link import("./auto-mode.js").AUTO_MODE_CLASSIFIER_FALLBACKS}) is
|
|
747
|
-
* REFUSED loudly at prepare
|
|
748
|
-
* as the default.
|
|
760
|
+
* REFUSED loudly at every leg's prepare while this face is present, whatever mode the leg starts in
|
|
761
|
+
* (`config.auto_mode_classifier_fallback_invalid`) — never clamped, never read as the default.
|
|
749
762
|
*/
|
|
750
763
|
classifierFallback?: import("./auto-mode.js").AutoModeClassifierFallback;
|
|
751
764
|
/**
|
|
@@ -764,8 +777,9 @@ export interface RunnerDeps {
|
|
|
764
777
|
* the run STOPS with `TaskResult.errorCode = "classifier.denial_limit"` (a notice of the same
|
|
765
778
|
* code is minted).
|
|
766
779
|
*
|
|
767
|
-
* Every member optional (defaults 3 / 20 / 120_000). A present member with a bad value is
|
|
768
|
-
* loudly at prepare
|
|
780
|
+
* Every member optional (defaults 3 / 20 / 120_000). A present member with a bad value, a member that is
|
|
781
|
+
* not one of the three, or a value that is not a record, is refused loudly at every leg's prepare while this face is present, whatever mode the
|
|
782
|
+
* leg starts in (`config.auto_mode_denial_limit_invalid`) — never clamped, never silently read as the default.
|
|
769
783
|
* See {@link import("./auto-mode.js").AutoModeDenialLimitOptions}.
|
|
770
784
|
*/
|
|
771
785
|
denialLimit?: import("./auto-mode.js").AutoModeDenialLimitOptions;
|
|
@@ -1354,23 +1368,6 @@ export interface RunnerDeps {
|
|
|
1354
1368
|
* deployment wires the factory.
|
|
1355
1369
|
*/
|
|
1356
1370
|
executionEnvFactory?: import("./remote-env.js").ExecutionEnvFactory;
|
|
1357
|
-
/**
|
|
1358
|
-
* design/77 §1 Gate-1 (oracleIsolation) — a **DISTINCT** trusted control-plane factory that mints the
|
|
1359
|
-
* ISOLATED grader env an out-of-process oracle grades in (the 4th auto-promote gate). It is a SIBLING of
|
|
1360
|
-
* {@link executionEnvFactory}, never the same env: the worker runs in `executionEnvFactory`'s env, the
|
|
1361
|
-
* grader in `graderEnvFactory`'s — so a worker can't corrupt the env that judges it. It inherits the exact
|
|
1362
|
-
* same trust contract: `TaskSpec` deliberately has NO env field (design/44 §7 Q4), so an untrusted caller
|
|
1363
|
-
* can never inject/swap a grader env and grade itself; only the trusted deployment wires this factory. The
|
|
1364
|
-
* RESERVED SEAM, stated plainly (ruled 2026-08-04, field-liveness review): the engine has NO call
|
|
1365
|
-
* site for this factory today — nothing mints, wraps, or asserts through it yet. The intended wirer
|
|
1366
|
-
* is the hardened-grader path a deployment builds on its remote-env side (the k8s pod-spec-patch
|
|
1367
|
-
* seam is the documented server-side half); when that live runner lands, the env it mints is to be
|
|
1368
|
-
* wrapped in a {@link import("./oracle-isolation.js").GraderEnv} and asserted via
|
|
1369
|
-
* {@link import("./oracle-isolation.js").assertOracleIsolation} before any positive oracle verdict.
|
|
1370
|
-
* **Auto-promote stays mandate-OFF until the four gates are implemented + verified end-to-end** (design/77
|
|
1371
|
-
* §7); this seam only makes auto-accept implementable — it does not turn it on.
|
|
1372
|
-
*/
|
|
1373
|
-
graderEnvFactory?: import("./remote-env.js").ExecutionEnvFactory;
|
|
1374
1371
|
/**
|
|
1375
1372
|
* Containment root for the file tools (design/44 §4 invariant 7): every path must resolve within it,
|
|
1376
1373
|
* else the tool rejects (`PathNotInRoot`). Default `process.cwd()`. A defense-in-depth guard rail at the
|
|
@@ -81,8 +81,8 @@ export interface WorkflowGovernanceBaseline {
|
|
|
81
81
|
* · GRANT-shaped, `=== true` opts in / absent is OFF: {@link allowObservers};
|
|
82
82
|
* · FORCE-shaped, `true` mandates / absent is the caller's opt-in default: {@link forceDurableGate}
|
|
83
83
|
* (binds only where a checkpoint store is wired; announced `config.durable_gate_unavailable` where none is).
|
|
84
|
-
* These
|
|
85
|
-
*
|
|
84
|
+
* These seven are the caps the ENGINE enforces — {@link bypassPermissions} is the seventh, the deny bit of the
|
|
85
|
+
* `bypassPermissions` mode; `allowUltracode` stays a shell-UX concern (see {@link RunnerDeps.runtimeCapsResolver}).
|
|
86
86
|
*/
|
|
87
87
|
/**
|
|
88
88
|
* #692 C-d — WHICH plane's ratchet denied auto mode, when the resolver knows: the three settings sources
|
|
@@ -135,7 +135,7 @@ export interface RuntimeCaps {
|
|
|
135
135
|
/**
|
|
136
136
|
* The per-principal auto-mode DENY bit (CC 2.1.250 polarity — the mirror of the settings-plane
|
|
137
137
|
* `permissions.disableAutoMode` ratchet: an organization may only take auto mode AWAY; it is the
|
|
138
|
-
* USER who turns it on,
|
|
138
|
+
* USER who turns it on, by requesting the mode at {@link TaskSpec.permissionMode}).
|
|
139
139
|
*
|
|
140
140
|
* **POLARITY: `=== false` DENIES; ABSENT IS NOT A DENIAL.** This is the family's deny-shaped
|
|
141
141
|
* member (like {@link allowWorkflows} / {@link allowFork}), NOT a grant-shaped one like
|
|
@@ -157,6 +157,15 @@ export interface RuntimeCaps {
|
|
|
157
157
|
* manifest) — a source named for a denial that did not happen is a marshalling error, never a fact.
|
|
158
158
|
*/
|
|
159
159
|
autoModeDenySource?: AutoModeDenySource;
|
|
160
|
+
/**
|
|
161
|
+
* The per-principal `bypassPermissions` DENY bit — the engine seat of the settings-plane
|
|
162
|
+
* `permissions.disableBypassPermissionsMode: "disable"` ratchet. **`=== false` DENIES; ABSENT IS NOT A DENIAL**
|
|
163
|
+
* (the family's deny shape, like {@link autoMode}). A task that requests `bypassPermissions` on a leg whose
|
|
164
|
+
* caps carry `false` runs in `default` instead, announced once (`config.permission_mode_unavailable`,
|
|
165
|
+
* `by: "operator_deny"`) and reported on `wiring_manifest.permissionMode` — a mode declaration is folded, never
|
|
166
|
+
* a 4xx. A steer into `bypassPermissions` on such a leg folds the same way.
|
|
167
|
+
*/
|
|
168
|
+
bypassPermissions?: boolean;
|
|
160
169
|
/**
|
|
161
170
|
* design/383 §3.1 — may this principal declare a session memory-capture OPT-OUT
|
|
162
171
|
* (`TaskSpec.memory.capture: "off"` / the Runner flip verb)? `false` = the deployment REQUIRES
|
|
@@ -25,8 +25,8 @@ export declare const SAFETY_AXIS_VOCABULARY: {
|
|
|
25
25
|
readonly safetyAxis: readonly ["egress", "irreversible", "shell"];
|
|
26
26
|
/** `RiskDescriptor.severity` — ToolEmu-style tier (5 = most severe), the inbox triage key (checkpoint-store.ts:85). */
|
|
27
27
|
readonly severity: readonly [1, 2, 3, 4, 5];
|
|
28
|
-
/** `TaskSpec.shellGate` (declared in core/types.ts) —
|
|
29
|
-
readonly shellGate: readonly ["
|
|
28
|
+
/** `TaskSpec.shellGate` (declared in core/types.ts) — the operator's shell floor; one value (`always`). */
|
|
29
|
+
readonly shellGate: readonly ["always"];
|
|
30
30
|
/** `ToolPolicy` decision — per-tool adjudication; `deny` short-circuits the `deny > ask > allow` fold (tool-policy.ts). */
|
|
31
31
|
readonly permissionDecision: readonly ["allow", "ask", "deny"];
|
|
32
32
|
};
|
|
@@ -4,7 +4,7 @@ export const SAFETY_AXIS_VOCABULARY = {
|
|
|
4
4
|
irreversibility: ["never", "maybe", "always"],
|
|
5
5
|
safetyAxis: ["egress", "irreversible", "shell"],
|
|
6
6
|
severity: [1, 2, 3, 4, 5],
|
|
7
|
-
shellGate: ["
|
|
7
|
+
shellGate: ["always"],
|
|
8
8
|
permissionDecision: ["allow", "ask", "deny"],
|
|
9
9
|
};
|
|
10
10
|
const _toolEffectExhaustive = true;
|
|
@@ -11,7 +11,9 @@ import type { ToolPolicy } from "./tool-policy.js";
|
|
|
11
11
|
*
|
|
12
12
|
* @contract sensitive_paths.agent_config_tier_is_shared — the write deny set's agent-configuration
|
|
13
13
|
* block IS the read face's `agent-config` tier, row for row: one table, two judges, and the two faces
|
|
14
|
-
* cannot drift into disagreeing about which files are an agent's own configuration. The
|
|
14
|
+
* cannot drift into disagreeing about which files are an agent's own configuration. The block carries each
|
|
15
|
+
* row's RUN: the read rows are home-anchored (a project's same-named file reads), while this write set judges
|
|
16
|
+
* each run anywhere, so a project's own configuration file stays a refused write here. The CONFIGURATION
|
|
15
17
|
* DIRECTORIES (`.claude`, `.sema`) are on neither face's deny side — their write guard is ASK
|
|
16
18
|
* severity, where an approver or a persisted allow rule can clear it. Their `hooks/` and `agents/`
|
|
17
19
|
* sub-directories are the two exceptions and they are NOT part of this mirror: a hook script is an executable
|
|
@@ -30,7 +32,7 @@ export declare function agentConfigDenyPatterns(): readonly string[];
|
|
|
30
32
|
* globs): each pattern is a `/`-separated run of path SEGMENTS; `*` matches any run of non-slash chars
|
|
31
33
|
* within one segment and is the ONLY metacharacter (everything else, `?` included, is literal); the run
|
|
32
34
|
* may match ANYWHERE in the canonical path (so `.ssh` covers `~/.ssh/…` and any other `.ssh` directory —
|
|
33
|
-
* home-anchoring like `~/` is NOT part of the syntax
|
|
35
|
+
* home-anchoring like `~/` is NOT part of the syntax: a pattern opening with it is refused at wiring). A pattern that reduces to zero
|
|
34
36
|
* segments is a wiring ERROR (it would guard nothing) and throws rather than being dropped.
|
|
35
37
|
* Remember this policy guards WRITES only; reads are out of scope by design.
|
|
36
38
|
*
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { homedir } from "node:os";
|
|
2
2
|
import { isAbsolute, join, resolve } from "node:path";
|
|
3
3
|
import { canonicalizeTarget, expandHomeTilde, isAbsoluteForFamily, isWinFormPath, pathFamilyOf, writeTargetPath } from "../tools/fs/safety.js";
|
|
4
|
-
import { compileSegmentPattern, matchSegmentPatterns, READ_FACE_BUILTIN_DENY_TABLE } from "../tools/fs/read-deny.js";
|
|
4
|
+
import { compileSegmentPattern, matchSegmentPatterns, READ_FACE_BUILTIN_DENY_TABLE, readDenyPatternRun } from "../tools/fs/read-deny.js";
|
|
5
5
|
import { isProtectedWrite, protectivePathTargetOf } from "./tool-registry.js";
|
|
6
6
|
import { pathTargetBaseOf } from "./effective-path-target.js";
|
|
7
7
|
import { symlinkChainHops } from "./path-identity.js";
|
|
8
8
|
export function agentConfigDenyPatterns() {
|
|
9
|
-
return READ_FACE_BUILTIN_DENY_TABLE.filter((row) => row.tier === "agent-config").map((row) => row.pattern);
|
|
9
|
+
return READ_FACE_BUILTIN_DENY_TABLE.filter((row) => row.tier === "agent-config").map((row) => readDenyPatternRun(row.pattern).run);
|
|
10
10
|
}
|
|
11
11
|
export const RECOMMENDED_SENSITIVE_PATTERNS = [
|
|
12
12
|
".env",
|
|
@@ -95,6 +95,11 @@ export function relativeUnderRoot(targetKey, rootKey) {
|
|
|
95
95
|
function compilePatterns(patterns) {
|
|
96
96
|
const out = [];
|
|
97
97
|
for (const raw of patterns) {
|
|
98
|
+
const { homeAnchored, run } = readDenyPatternRun(raw);
|
|
99
|
+
if (homeAnchored) {
|
|
100
|
+
throw new Error(`createSensitivePathPolicy: pattern ${JSON.stringify(raw)} opens with the home word "~", which this write list does not place — its patterns are judged anywhere in the real target. ` +
|
|
101
|
+
`Spell the segments without it (${JSON.stringify(run)}), which covers the home's file and every same-named one.`);
|
|
102
|
+
}
|
|
98
103
|
const compiled = compileSegmentPattern(raw, CASE_INSENSITIVE_FS ? "unicode" : "none");
|
|
99
104
|
if (compiled === null) {
|
|
100
105
|
throw new Error(`createSensitivePathPolicy: pattern ${JSON.stringify(raw)} contains no path segments and would guard nothing. ` +
|
|
@@ -111,7 +116,33 @@ export function createSensitivePathPolicy(opts) {
|
|
|
111
116
|
const compiled = compilePatterns(opts.patterns);
|
|
112
117
|
const dataRootPaths = dataRootSpellings(opts.dataRoot);
|
|
113
118
|
const guardedByName = opts.tools !== undefined ? new Set(opts.tools) : undefined;
|
|
119
|
+
const family = (cwd) => pathFamilyOf({ root: opts.rootPath, cwd: cwd ?? opts.env.cwd });
|
|
120
|
+
const judgeTarget = async (path, fam, base, signal) => {
|
|
121
|
+
const canon = await canonicalizeTarget(opts.env, path, signal, fam, base);
|
|
122
|
+
if (!canon.ok)
|
|
123
|
+
return { kind: "unresolved", unresolvedSymlink: canon.unresolvedSymlink === true, cause: canon.refused ?? canon.message };
|
|
124
|
+
let relative = null;
|
|
125
|
+
for (const rootKey of await canonicalDataRootKeys(opts.env, dataRootPaths, signal)) {
|
|
126
|
+
relative = relativeUnderRoot(canon.key, rootKey);
|
|
127
|
+
if (relative !== null)
|
|
128
|
+
break;
|
|
129
|
+
}
|
|
130
|
+
if (relative === "")
|
|
131
|
+
return { kind: "clear" };
|
|
132
|
+
const hit = matchSensitive(relative ?? canon.key, compiled, canon.aliasResolved === true);
|
|
133
|
+
if (hit)
|
|
134
|
+
return { kind: "hit", pattern: hit, via: "target" };
|
|
135
|
+
if (relative === null) {
|
|
136
|
+
for (const hop of await symlinkChainHops(opts.env, path, signal, fam, base)) {
|
|
137
|
+
const viaHop = matchSensitive(hop, compiled, false);
|
|
138
|
+
if (viaHop)
|
|
139
|
+
return { kind: "hit", pattern: viaHop, via: "chain" };
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return { kind: "clear" };
|
|
143
|
+
};
|
|
114
144
|
return {
|
|
145
|
+
...(compiled.length > 0 ? { writeTargetGuard: async (target, signal) => (await judgeTarget(target, family(undefined), undefined, signal)).kind !== "clear" } : {}),
|
|
115
146
|
async check(req, signal) {
|
|
116
147
|
if (compiled.length === 0)
|
|
117
148
|
return { action: "allow" };
|
|
@@ -121,45 +152,23 @@ export function createSensitivePathPolicy(opts) {
|
|
|
121
152
|
const path = writeTargetPath(req, protective);
|
|
122
153
|
if (typeof path !== "string" || path.length === 0)
|
|
123
154
|
return { action: "allow" };
|
|
124
|
-
const
|
|
125
|
-
if (
|
|
155
|
+
const judged = await judgeTarget(path, family(req.cwd), pathTargetBaseOf(protective, { root: opts.rootPath, cwd: req.cwd }), signal);
|
|
156
|
+
if (judged.kind === "unresolved") {
|
|
126
157
|
return {
|
|
127
158
|
action: "deny",
|
|
128
|
-
message:
|
|
159
|
+
message: judged.unresolvedSymlink
|
|
129
160
|
? `write to "${path}" is blocked: it is a symlink whose real target could not be resolved (it could point onto a guarded sensitive path)`
|
|
130
|
-
: `write to "${path}" is blocked: its target could not be resolved on this tree (${
|
|
161
|
+
: `write to "${path}" is blocked: its target could not be resolved on this tree (${judged.cause}), so the sensitive-path guard cannot judge it`,
|
|
131
162
|
decisionReason: "safety",
|
|
132
163
|
};
|
|
133
164
|
}
|
|
134
|
-
|
|
135
|
-
for (const rootKey of await canonicalDataRootKeys(opts.env, dataRootPaths, signal)) {
|
|
136
|
-
relative = relativeUnderRoot(canon.key, rootKey);
|
|
137
|
-
if (relative !== null)
|
|
138
|
-
break;
|
|
139
|
-
}
|
|
140
|
-
const judged = relative ?? canon.key;
|
|
141
|
-
if (relative === "")
|
|
142
|
-
return { action: "allow" };
|
|
143
|
-
const hit = matchSensitive(judged, compiled, canon.aliasResolved === true);
|
|
144
|
-
if (hit) {
|
|
165
|
+
if (judged.kind === "hit") {
|
|
145
166
|
return {
|
|
146
167
|
action: "deny",
|
|
147
|
-
message: `write to "${path}" is blocked: its real target resolves onto the guarded sensitive path "${
|
|
168
|
+
message: judged.via === "target" ? `write to "${path}" is blocked: its real target resolves onto the guarded sensitive path "${judged.pattern}"` : `write to "${path}" is blocked: its symlink chain passes through the guarded sensitive path "${judged.pattern}"`,
|
|
148
169
|
decisionReason: "safety",
|
|
149
170
|
};
|
|
150
171
|
}
|
|
151
|
-
if (relative === null) {
|
|
152
|
-
for (const hop of await symlinkChainHops(opts.env, path, signal, pathFamilyOf({ root: opts.rootPath, cwd: req.cwd ?? opts.env.cwd }), pathTargetBaseOf(protective, { root: opts.rootPath, cwd: req.cwd }))) {
|
|
153
|
-
const viaHop = matchSensitive(hop, compiled, false);
|
|
154
|
-
if (viaHop) {
|
|
155
|
-
return {
|
|
156
|
-
action: "deny",
|
|
157
|
-
message: `write to "${path}" is blocked: its symlink chain passes through the guarded sensitive path "${viaHop}"`,
|
|
158
|
-
decisionReason: "safety",
|
|
159
|
-
};
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
172
|
return { action: "allow" };
|
|
164
173
|
},
|
|
165
174
|
};
|
|
@@ -370,7 +370,7 @@ export type TaskEvent = ({
|
|
|
370
370
|
* document/worktree/todo/task/task-list/task-output/task-stop/agent/fork/workflow-run/web-fetch/
|
|
371
371
|
* web-search/cron-create/cron-delete/cron-list/tool-search/repo-map/monitor-start/
|
|
372
372
|
* enter-plan-mode/exit-plan-mode/report-findings/schedule-wakeup/send-message/agent-transcript/
|
|
373
|
-
* a2a/path_not_in_root/readonly_out_of_root), surfaced on the live wire so a CC-faithful shell
|
|
373
|
+
* a2a/path_not_in_root/readonly_out_of_root/read_unestablished), surfaced on the live wire so a CC-faithful shell
|
|
374
374
|
* renders rich cards (red/green diff, stdout panes, file cards) without re-parsing the
|
|
375
375
|
* model-facing string. Absent when the tool carries no card shape or when the serialized card
|
|
376
376
|
* exceeds the size bound (omitted whole; a consumer falls back to `output`).
|
|
@@ -622,7 +622,7 @@ export interface TaskResult {
|
|
|
622
622
|
* posture rides every leg that continues this work, not just the delegation subtree.
|
|
623
623
|
*
|
|
624
624
|
* **In-presence condition:** present iff non-empty. Built-in entries are never listed — they are the
|
|
625
|
-
* DEPLOYMENT's own selection (
|
|
625
|
+
* DEPLOYMENT's own selection (the default tiers unless it says otherwise), re-compiled per leg and reported on its wiring
|
|
626
626
|
* manifest ({@link import("./wiring-manifest.js").WiringManifest.readDeny}). Same terminal coverage as
|
|
627
627
|
* {@link effectiveReadFace}: rides any terminal of a leg that ran; absent on prepare failures.
|
|
628
628
|
*
|