@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
|
@@ -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,13 @@
|
|
|
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
|
+
import { toolNameSetHas } from "./tool-roster.js";
|
|
8
9
|
export function agentConfigDenyPatterns() {
|
|
9
|
-
return READ_FACE_BUILTIN_DENY_TABLE.filter((row) => row.tier === "agent-config").map((row) => row.pattern);
|
|
10
|
+
return READ_FACE_BUILTIN_DENY_TABLE.filter((row) => row.tier === "agent-config").map((row) => readDenyPatternRun(row.pattern).run);
|
|
10
11
|
}
|
|
11
12
|
export const RECOMMENDED_SENSITIVE_PATTERNS = [
|
|
12
13
|
".env",
|
|
@@ -95,6 +96,11 @@ export function relativeUnderRoot(targetKey, rootKey) {
|
|
|
95
96
|
function compilePatterns(patterns) {
|
|
96
97
|
const out = [];
|
|
97
98
|
for (const raw of patterns) {
|
|
99
|
+
const { homeAnchored, run } = readDenyPatternRun(raw);
|
|
100
|
+
if (homeAnchored) {
|
|
101
|
+
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. ` +
|
|
102
|
+
`Spell the segments without it (${JSON.stringify(run)}), which covers the home's file and every same-named one.`);
|
|
103
|
+
}
|
|
98
104
|
const compiled = compileSegmentPattern(raw, CASE_INSENSITIVE_FS ? "unicode" : "none");
|
|
99
105
|
if (compiled === null) {
|
|
100
106
|
throw new Error(`createSensitivePathPolicy: pattern ${JSON.stringify(raw)} contains no path segments and would guard nothing. ` +
|
|
@@ -111,55 +117,59 @@ export function createSensitivePathPolicy(opts) {
|
|
|
111
117
|
const compiled = compilePatterns(opts.patterns);
|
|
112
118
|
const dataRootPaths = dataRootSpellings(opts.dataRoot);
|
|
113
119
|
const guardedByName = opts.tools !== undefined ? new Set(opts.tools) : undefined;
|
|
120
|
+
const family = (cwd) => pathFamilyOf({ root: opts.rootPath, cwd: cwd ?? opts.env.cwd });
|
|
121
|
+
const judgeTarget = async (path, fam, base, signal) => {
|
|
122
|
+
const canon = await canonicalizeTarget(opts.env, path, signal, fam, base);
|
|
123
|
+
if (!canon.ok)
|
|
124
|
+
return { kind: "unresolved", unresolvedSymlink: canon.unresolvedSymlink === true, cause: canon.refused ?? canon.message };
|
|
125
|
+
let relative = null;
|
|
126
|
+
for (const rootKey of await canonicalDataRootKeys(opts.env, dataRootPaths, signal)) {
|
|
127
|
+
relative = relativeUnderRoot(canon.key, rootKey);
|
|
128
|
+
if (relative !== null)
|
|
129
|
+
break;
|
|
130
|
+
}
|
|
131
|
+
if (relative === "")
|
|
132
|
+
return { kind: "clear" };
|
|
133
|
+
const hit = matchSensitive(relative ?? canon.key, compiled, canon.aliasResolved === true);
|
|
134
|
+
if (hit)
|
|
135
|
+
return { kind: "hit", pattern: hit, via: "target" };
|
|
136
|
+
if (relative === null) {
|
|
137
|
+
for (const hop of await symlinkChainHops(opts.env, path, signal, fam, base)) {
|
|
138
|
+
const viaHop = matchSensitive(hop, compiled, false);
|
|
139
|
+
if (viaHop)
|
|
140
|
+
return { kind: "hit", pattern: viaHop, via: "chain" };
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return { kind: "clear" };
|
|
144
|
+
};
|
|
114
145
|
return {
|
|
146
|
+
...(compiled.length > 0 ? { writeTargetGuard: async (target, signal) => (await judgeTarget(target, family(undefined), undefined, signal)).kind !== "clear" } : {}),
|
|
115
147
|
async check(req, signal) {
|
|
116
148
|
if (compiled.length === 0)
|
|
117
149
|
return { action: "allow" };
|
|
118
|
-
if (guardedByName !== undefined ? !guardedByName
|
|
150
|
+
if (guardedByName !== undefined ? !toolNameSetHas(guardedByName, req) : !isProtectedWrite(req))
|
|
119
151
|
return { action: "allow" };
|
|
120
152
|
const protective = protectivePathTargetOf(req);
|
|
121
153
|
const path = writeTargetPath(req, protective);
|
|
122
154
|
if (typeof path !== "string" || path.length === 0)
|
|
123
155
|
return { action: "allow" };
|
|
124
|
-
const
|
|
125
|
-
if (
|
|
156
|
+
const judged = await judgeTarget(path, family(req.cwd), pathTargetBaseOf(protective, { root: opts.rootPath, cwd: req.cwd }), signal);
|
|
157
|
+
if (judged.kind === "unresolved") {
|
|
126
158
|
return {
|
|
127
159
|
action: "deny",
|
|
128
|
-
message:
|
|
160
|
+
message: judged.unresolvedSymlink
|
|
129
161
|
? `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 (${
|
|
162
|
+
: `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
163
|
decisionReason: "safety",
|
|
132
164
|
};
|
|
133
165
|
}
|
|
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) {
|
|
166
|
+
if (judged.kind === "hit") {
|
|
145
167
|
return {
|
|
146
168
|
action: "deny",
|
|
147
|
-
message: `write to "${path}" is blocked: its real target resolves onto the guarded sensitive path "${
|
|
169
|
+
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
170
|
decisionReason: "safety",
|
|
149
171
|
};
|
|
150
172
|
}
|
|
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
173
|
return { action: "allow" };
|
|
164
174
|
},
|
|
165
175
|
};
|
|
@@ -301,7 +301,7 @@ export async function checkpointStoreContract(make, runAssertion) {
|
|
|
301
301
|
const stored = (await store.get(bitless.token)).pendingAction;
|
|
302
302
|
assert.equal("hasBidiControls" in stored, false, "projection backfill must not write back to the row");
|
|
303
303
|
});
|
|
304
|
-
run("design/388 B18 × B6 identity + face group: a row's
|
|
304
|
+
run("design/388 B18 × B6 identity + face group: a row's eight optional members (capabilityId/contractId/shapeDigest/implementationRevision/rosterDigest + family/pathTarget/names) survive the round-trip verbatim; an absent group stays absent", async () => {
|
|
305
305
|
const store = make();
|
|
306
306
|
const carried = createCheckpointFixture({ token: mintCheckpointToken(), sessionId: "id-carried" });
|
|
307
307
|
const group = {
|
|
@@ -312,6 +312,7 @@ export async function checkpointStoreContract(make, runAssertion) {
|
|
|
312
312
|
rosterDigest: "sha256:fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210",
|
|
313
313
|
family: "file-write",
|
|
314
314
|
pathTarget: { param: "destination", aliases: ["path"], access: "create", skillScopeEligible: true },
|
|
315
|
+
names: ["AcmeWrite", "acme_write"],
|
|
315
316
|
};
|
|
316
317
|
Object.assign(carried.pendingAction, group);
|
|
317
318
|
const bare = createCheckpointFixture({ token: mintCheckpointToken(), sessionId: "id-bare" });
|
|
@@ -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
|
*
|
package/dist/core/task-spec.d.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* beside it in `task-limits.ts`; the terminal record it produces in `task-result.ts`. Layer 0
|
|
5
5
|
* vocabulary; `types.ts` re-exports the three, so no consumer's import changes.
|
|
6
6
|
*/
|
|
7
|
+
import type { PermissionMode } from "./ask-origin.js";
|
|
7
8
|
import type { TSchema } from "typebox";
|
|
8
9
|
import type { ThinkingLevel } from "../internal/harness.js";
|
|
9
10
|
import type { ActorAssertion, Model, ResilienceOptions } from "../internal/llm.js";
|
|
@@ -31,15 +32,12 @@ export type ImageInput = {
|
|
|
31
32
|
url: string;
|
|
32
33
|
};
|
|
33
34
|
/** A single self-contained task with its full configuration. */
|
|
34
|
-
/** The shell-gate
|
|
35
|
-
* shell-gated ask (`RiskDescriptor.shellGateDoctrine`) and
|
|
36
|
-
*
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
* today (`"bypass"` — the host answers every card itself, automatically). The engine only RENDERS it,
|
|
41
|
-
* so adding a word here changes what the model is told and nothing else. */
|
|
42
|
-
export type ApproverPosture = "bypass";
|
|
35
|
+
/** The shell-gate FLOOR vocabulary — {@link TaskSpec.shellGate}'s value set, the word the engine persists on a
|
|
36
|
+
* shell-gated ask (`RiskDescriptor.shellGateDoctrine`) and carries down a delegation chain. One value: `"always"`
|
|
37
|
+
* (every shell command is confirmed, under every permission mode — an operator's mandate). The shape tier itself
|
|
38
|
+
* is installed on every full shell regardless; what a permission mode does with the resulting ask is the gate's
|
|
39
|
+
* arbitration, never a doctrine word. */
|
|
40
|
+
export type ShellGateDoctrine = "always";
|
|
43
41
|
export interface TaskSpec {
|
|
44
42
|
taskId?: string;
|
|
45
43
|
/** The instruction / latest user message for this turn. */
|
|
@@ -791,8 +789,8 @@ export interface TaskSpec {
|
|
|
791
789
|
/**
|
|
792
790
|
* Interactive read-boundary whitelist (ruled 2026-08-05, both-arms ruling, arm A): extra directories
|
|
793
791
|
* the READ faces may access — and nothing else. Joins the containment allowlist of the read-only
|
|
794
|
-
* tools (read_file / grep / glob / repo_map / `bash_readonly`) and the
|
|
795
|
-
*
|
|
792
|
+
* tools (read_file / grep / glob / repo_map / `bash_readonly`) and the shell read station's
|
|
793
|
+
* boundary (so a provably read-only command like `head <dir>/settings.json` auto-allows instead
|
|
796
794
|
* of asking on every call), but is NEVER admitted by the write faces (edit_file / write_file /
|
|
797
795
|
* notebook_edit refuse these paths exactly as before). Use `additionalDirectories` when the task
|
|
798
796
|
* should also write there. Same canonicalization and fail-closed skip rules as
|
|
@@ -805,7 +803,7 @@ export interface TaskSpec {
|
|
|
805
803
|
* design/199 件B — TASK-layer ADDITIONS to the built-in sensitive-path READ deny set
|
|
806
804
|
* ({@link import("../tools/fs/read-deny.js").READ_FACE_DEFAULT_DENY_ENTRIES}). Judged by the
|
|
807
805
|
* structured read faces (Read/Grep/Glob/RepoMap, their traversals, the compaction attachment reader) and
|
|
808
|
-
* by the shell READ STATION under every
|
|
806
|
+
* by the shell READ STATION under every mode — the full shell (`Bash`, `Monitor`, whatever
|
|
809
807
|
* {@link shellGate} says) and the `bash_readonly` shell mounted by {@link handsReadOnly} alike: a
|
|
810
808
|
* deny-listed shell read is REFUSED at the gate (`deniedBy: "read_boundary"`), never asked, with the same
|
|
811
809
|
* sentence the structured faces refuse with — one deny judge for every read face — in BOTH containment
|
|
@@ -814,8 +812,8 @@ export interface TaskSpec {
|
|
|
814
812
|
* {@link RunnerDeps.readDenyPatterns}; `[]` ≡ absent (union identity); NOTHING at the task layer
|
|
815
813
|
* can remove a built-in — the one removal channel is the DEPLOYMENT's built-in configuration
|
|
816
814
|
* ({@link RunnerDeps.readDenyBuiltinTiers} / {@link RunnerDeps.readDenyBuiltinExclude}, #245
|
|
817
|
-
* revision of D-4), which no TaskSpec key reaches
|
|
818
|
-
*
|
|
815
|
+
* revision of D-4), which no TaskSpec key reaches (an unconfigured deployment judges the default tiers,
|
|
816
|
+
* {@link import("../tools/fs/read-deny.js").READ_DENY_DEFAULT_TIERS}). Bad entry shapes refuse loudly at prepare
|
|
819
817
|
* (#123). The write faces are untouched.
|
|
820
818
|
*/
|
|
821
819
|
readDenyPatterns?: readonly import("../tools/fs/read-deny.js").ReadDenyEntry[];
|
|
@@ -890,87 +888,41 @@ export interface TaskSpec {
|
|
|
890
888
|
*/
|
|
891
889
|
enableFork?: boolean;
|
|
892
890
|
/**
|
|
893
|
-
*
|
|
894
|
-
* `
|
|
895
|
-
*
|
|
896
|
-
*
|
|
897
|
-
*
|
|
898
|
-
*
|
|
899
|
-
*
|
|
900
|
-
*
|
|
901
|
-
*
|
|
902
|
-
* reads is not this doctrine's question under any value: the gate's shell read station judges every
|
|
903
|
-
* shell call's read candidates against the deny set and the containment roots (a deny-listed read is
|
|
904
|
-
* refused, an out-of-root read is a mandated ask) whether `shellGate` is `"off"`, `"classify"` or
|
|
905
|
-
* `"always"`. An egress command (e.g. `curl`) suspends under
|
|
906
|
-
* the IRREVERSIBLE axis (the shell mark is irreversibility, not egress) — the kind is non-budgetable either
|
|
907
|
-
* way, so the budget resolver still never auto-approves it; the axis label is informational.
|
|
908
|
-
* NOT unconditional: a POSITIVE per-tool mark on the shell tool keeps its seat — an explicit
|
|
909
|
-
* `irreversibility:"always"` is never downgraded (the tier combine is tighten-only) and an explicit
|
|
910
|
-
* `"maybe"` keeps its own probe (a probe-less explicit `"maybe"` stays fail-closed ask; the doctrine's
|
|
911
|
-
* generic probe installs only on doctrine-owned seats). An explicit `"never"` is NOT a mark — the
|
|
912
|
-
* doctrine governs that seat as if unmarked.
|
|
913
|
-
* No effect under `handsReadOnly` (that mounts `bash_readonly`, already allowlisted) or with no execution env.
|
|
914
|
-
* **Re-supply on resume:** like `toolPolicy`/`tools`/`durableApproval`, `shellGate` is part of the resume
|
|
915
|
-
* task config — a resume that omits it leaves the resumed run's SUBSEQUENT `bash` calls ungated (the approved
|
|
916
|
-
* pending call itself runs once, having been adjudicated). Re-pass the same value the original task used.
|
|
891
|
+
* The OPERATOR's shell floor: `"always"` ⇒ every `bash` / `Monitor` command is a confirmed ask (an
|
|
892
|
+
* `operator_forced` ask — kept under `bypassPermissions`, not settled by `dontAsk`; a standing allow rule never
|
|
893
|
+
* clears it). Absent ⇒ no floor: the shape tier the engine installs on every full shell (a provably read-only
|
|
894
|
+
* single command runs, everything else asks) is arbitrated by the task's {@link permissionMode}. The retired
|
|
895
|
+
* values `"off"` / `"classify"` are refused at the door (`config.shell_gate_value_retired`): the tier is no longer a
|
|
896
|
+
* doctrine a caller chooses, and the read boundary is judged on every shell call under every mode. A positive
|
|
897
|
+
* per-tool mark keeps its seat (an explicit `irreversibility:"always"` is never downgraded). No effect under
|
|
898
|
+
* `handsReadOnly` or with no execution env. Re-supplied on resume like `toolPolicy` (a checkpoint carries the
|
|
899
|
+
* chain's floor). Delegation: the floor travels the inherited-gate lane; a child cannot lift it.
|
|
917
900
|
*/
|
|
918
901
|
shellGate?: ShellGateDoctrine;
|
|
919
902
|
/**
|
|
920
|
-
* The
|
|
921
|
-
*
|
|
922
|
-
*
|
|
923
|
-
*
|
|
924
|
-
*
|
|
925
|
-
*
|
|
926
|
-
* classifier RULES still enter ONLY through that face, never through this seat), and
|
|
927
|
-
* - {@link RuntimeCaps.autoMode} is not `false` (the per-principal DENY bit; ABSENT is NOT a denial).
|
|
903
|
+
* The task's PERMISSION MODE — the ONE key that says how the engine answers a surviving ask (six words,
|
|
904
|
+
* {@link import("./ask-origin.js").PERMISSION_MODES}; spelled as the wire spells it). ROOT task absent ⇒
|
|
905
|
+
* `"default"`; an engine-spawned CHILD absent ⇒ its parent's effective mode (a child's own declaration is
|
|
906
|
+
* adopted only where the reference implementation's rank table admits it under the parent's — a folded
|
|
907
|
+
* declaration is disclosed on `wiring_manifest.permissionMode.foldedBy: "inherited_ceiling"` and announced,
|
|
908
|
+
* never silent). A value outside the set is refused at the door (`config.permission_mode_invalid`).
|
|
928
909
|
*
|
|
929
|
-
*
|
|
930
|
-
*
|
|
931
|
-
*
|
|
932
|
-
*
|
|
933
|
-
*
|
|
934
|
-
*
|
|
935
|
-
*
|
|
910
|
+
* What the mode DOES is one table and one predicate ({@link import("./ask-origin.js").MODE_ARBITRATION},
|
|
911
|
+
* {@link import("./ask-origin.js").arbitrateByMode}) read after every rule, tighten and classifier station:
|
|
912
|
+
* `bypassPermissions` releases every ask but the immune classes (an explicit ask rule, a hook's ask, the
|
|
913
|
+
* question tool, an unverifiable recursive delete, an org rule, an operator's forced confirmation, unreadable
|
|
914
|
+
* governance, the classifier's denial-limit fallback) and discloses each release; `dontAsk` refuses every ask
|
|
915
|
+
* but a hook's or an operator authority's; `auto` arms the classifier (`RunnerDeps.autoMode` present and
|
|
916
|
+
* `RuntimeCaps.autoMode` not `false`, else folded to `default` and announced); `plan` reads only and mounts the
|
|
917
|
+
* plan pair; `acceptEdits` writes inside the root without asking; `default` asks. The mode also installs its own
|
|
918
|
+
* seats ({@link import("./mode-policy.js").modePolicyOf}).
|
|
936
919
|
*
|
|
937
|
-
*
|
|
938
|
-
*
|
|
939
|
-
* (`
|
|
940
|
-
*
|
|
941
|
-
* suspend leg resolved (this seat, or the bit its chain carried) is recorded on the checkpoint's
|
|
942
|
-
* data half (`CheckpointState.inheritedGate.autoModeRequested`) and read back as one more intent
|
|
943
|
-
* source, so a redemption in another process arms as the suspend leg did without re-passing the
|
|
944
|
-
* seat; re-passing it is still honoured (the sources fold by OR). Intent only — the resuming
|
|
945
|
-
* deployment's face and the resuming principal's deny bit are judged afresh on every leg.
|
|
946
|
-
*/
|
|
947
|
-
autoModeRequested?: true;
|
|
948
|
-
/**
|
|
949
|
-
* The host's declared APPROVER POSTURE for this task — a typed fact seat, the same ownership split
|
|
950
|
-
* as {@link shellGate} and {@link autoModeRequested}: the service layer declares it (it is the layer
|
|
951
|
-
* that knows who sits at the cards), core only CONSUMES it. `"bypass"` says the host answers every
|
|
952
|
-
* approval card itself, automatically.
|
|
953
|
-
*
|
|
954
|
-
* **The gate does not read it.** Cards are raised, routed and recorded byte-identically with and
|
|
955
|
-
* without this seat — no rule is relaxed, no ask is skipped, no decision is pre-empted. The seat's
|
|
956
|
-
* one effect is that the ENGINE tells the model which posture the run is under, in its own marked
|
|
957
|
-
* `<system-reminder>` on the first turn. That is the whole point of the seat: a posture is a fact of
|
|
958
|
-
* the run, and a host that instead splices the sentence into the objective hands the model
|
|
959
|
-
* reminder-shaped text with no engine mark, which the mark declaration tells it to treat as data and
|
|
960
|
-
* flag. One fact, one seat, one mint.
|
|
961
|
-
*
|
|
962
|
-
* Absent ⇒ no posture declared, zero bytes rendered (byte-identical to the pre-seat shape). Only the
|
|
963
|
-
* literal `"bypass"` is a value: any other present value is refused at the door
|
|
964
|
-
* (`config.approver_posture_invalid`) rather than folded back to absence — a marshalled typo must not
|
|
965
|
-
* silently un-declare a posture the host meant to declare.
|
|
966
|
-
*
|
|
967
|
-
* **Inheritance / resume:** session-wide, exactly like {@link autoModeRequested} — an engine-spawned
|
|
968
|
-
* child inherits it through the trusted constraint chain (`InheritedGate.approverPosture`), and the
|
|
969
|
-
* resolved value is recorded on the checkpoint's data half so a redemption in another process still
|
|
970
|
-
* knows the posture without the seat being re-passed. Re-passing it is honoured; the sources fold
|
|
971
|
-
* seat-first.
|
|
920
|
+
* `bypassPermissions` is a REQUEST: it takes effect only where the deployment does not deny it
|
|
921
|
+
* ({@link RuntimeCaps.bypassPermissions} not `false`); a denied request runs in `default`, announced
|
|
922
|
+
* (`config.permission_mode_unavailable`). A running leg changes mode through `TaskStream.setPermissionMode`.
|
|
923
|
+
* Persists on the checkpoint's data half (`CheckpointState.inheritedGate.permissionMode`), re-judged on every leg.
|
|
972
924
|
*/
|
|
973
|
-
|
|
925
|
+
permissionMode?: PermissionMode;
|
|
974
926
|
/** Task-scoped MCP servers, materialized into tools then disposed. */
|
|
975
927
|
mcp?: McpServerSpec[];
|
|
976
928
|
/** Task-scoped A2A peers (remote agents), whose advertised skills mount as `a2a__<peer>__<skill>`
|
|
@@ -275,6 +275,22 @@ export interface TaskStream extends AsyncIterable<TaskEvent> {
|
|
|
275
275
|
}): Promise<{
|
|
276
276
|
outcome: "created" | "existed";
|
|
277
277
|
}>;
|
|
278
|
+
/**
|
|
279
|
+
* Change the running leg's PERMISSION MODE (`TaskSpec.permissionMode`'s live half — a shell's mode toggle, a serving
|
|
280
|
+
* layer's `permission_mode` control frame). The requested word is rewritten on the leg's mode seat; the deployment's
|
|
281
|
+
* fold is re-judged (`bypassPermissions` where `RuntimeCaps.bypassPermissions` is `false`, or `auto` with no
|
|
282
|
+
* classifier face / a `false` deny bit, runs `default` and is announced `config.permission_mode_unavailable`); a leg
|
|
283
|
+
* that became `auto` arms its classifier now — an arming that fails rolls the steer back (the word it replaced stands)
|
|
284
|
+
* and the verb rejects with the arming's code; the NEXT tool call reads the new mode at the gate's arbitration point
|
|
285
|
+
* (a tightening and a loosening both take effect one call later, on one seat), and a child leg derives its own
|
|
286
|
+
* effective mode from this seat on its next read. Resolves the same three-member receipt `wiring_manifest.permissionMode`
|
|
287
|
+
* carries — `{ requested, effective, foldedBy? }` — on EVERY call, moved or not, so a caller renders what the engine
|
|
288
|
+
* says and never what it sent. Typed refusals: `steering.invalid_content` (a word outside the closed set) and the
|
|
289
|
+
* steer-family `steering.not_running` once the task has finished. AUTHORITY (steer-lane law): holding this stream IS
|
|
290
|
+
* the capability handle; a service exposing the verb owns the principal check at its own face — and a deployment that
|
|
291
|
+
* must never let a caller reach `bypassPermissions` says so with the deny bit, which this verb honours.
|
|
292
|
+
*/
|
|
293
|
+
setPermissionMode(mode: import("./ask-origin.js").PermissionMode): Promise<import("./mode-policy.js").PermissionModeReceipt>;
|
|
278
294
|
/**
|
|
279
295
|
* **RE-DIAL one MCP server** on the running task, the action `RefreshMcpTools` deliberately
|
|
280
296
|
* is not (its contract is "never dials or re-dials"). Core holds no MCP connection across task
|
|
@@ -4,6 +4,7 @@ export const TERMINAL_CAUSE_IS_REPLAYABLE = {
|
|
|
4
4
|
blocked: false,
|
|
5
5
|
paused: false,
|
|
6
6
|
};
|
|
7
|
+
Object.freeze(TERMINAL_CAUSE_IS_REPLAYABLE);
|
|
7
8
|
export function isTerminalCauseKind(kind) {
|
|
8
9
|
return typeof kind === "string" && Object.hasOwn(TERMINAL_CAUSE_IS_REPLAYABLE, kind);
|
|
9
10
|
}
|
|
@@ -13,8 +13,8 @@ export declare class TaskSpecTightenError extends Error {
|
|
|
13
13
|
* would drop the base policy entirely.
|
|
14
14
|
* - **`handsReadOnly`** — `true` is stricter. `base:true` + `override:false` ⇒ **throw** (loosening). Otherwise
|
|
15
15
|
* the stricter (`true` if either is true) wins.
|
|
16
|
-
* - **`shellGate`** — `
|
|
17
|
-
*
|
|
16
|
+
* - **`shellGate`** — one value (`"always"`, the operator floor). Either side set ⇒ the floor stands; it cannot
|
|
17
|
+
* be loosened because absence is the only other state and an override cannot unset a base value.
|
|
18
18
|
* - **`onAsk`** — no safe automatic merge (the approver and the policy must be coordinated). Both set to
|
|
19
19
|
* DIFFERENT values ⇒ **throw**; equal ⇒ keep; one set ⇒ that one.
|
|
20
20
|
* - **`hooks`** — no standard composition (stacking hook results is caller-specific). Both set ⇒ **throw**;
|
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
import { combinePolicies } from "./tool-policy.js";
|
|
2
|
-
|
|
3
|
-
off: 0,
|
|
4
|
-
classify: 1,
|
|
5
|
-
always: 2,
|
|
6
|
-
};
|
|
2
|
+
import { clampPermissionMode } from "./ask-origin.js";
|
|
7
3
|
export class TaskSpecTightenError extends Error {
|
|
8
4
|
constructor(message) {
|
|
9
5
|
super(message);
|
|
@@ -51,16 +47,18 @@ export function tightenTaskSpec(base, overrides) {
|
|
|
51
47
|
if (base.handsReadOnly !== undefined || overrides.handsReadOnly !== undefined) {
|
|
52
48
|
merged.handsReadOnly = base.handsReadOnly === true || overrides.handsReadOnly === true;
|
|
53
49
|
}
|
|
54
|
-
if (base.
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
throw new TaskSpecTightenError(`tightenTaskSpec: cannot loosen shellGate from "${baseGate}" to "${overrideGate}" (override may only tighten)`);
|
|
50
|
+
if (base.permissionMode !== undefined && overrides.permissionMode !== undefined) {
|
|
51
|
+
const { effective, clamped } = clampPermissionMode(overrides.permissionMode, base.permissionMode);
|
|
52
|
+
if (clamped) {
|
|
53
|
+
throw new TaskSpecTightenError(`tightenTaskSpec: permissionMode override "${overrides.permissionMode}" would loosen the base "${base.permissionMode}" (the override may only tighten; a bypass override needs a bypass base, acceptEdits and auto never adopt each other)`);
|
|
59
54
|
}
|
|
60
|
-
merged.
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
55
|
+
merged.permissionMode = effective;
|
|
56
|
+
}
|
|
57
|
+
else if (base.permissionMode !== undefined || overrides.permissionMode !== undefined) {
|
|
58
|
+
merged.permissionMode = overrides.permissionMode ?? base.permissionMode;
|
|
59
|
+
}
|
|
60
|
+
if (base.shellGate !== undefined || overrides.shellGate !== undefined) {
|
|
61
|
+
merged.shellGate = base.shellGate ?? overrides.shellGate;
|
|
64
62
|
}
|
|
65
63
|
if (base.onAsk !== undefined && overrides.onAsk !== undefined && base.onAsk !== overrides.onAsk) {
|
|
66
64
|
throw new TaskSpecTightenError("tightenTaskSpec: conflicting onAsk on base and overrides — onAsk cannot be merged automatically " +
|
|
@@ -56,7 +56,7 @@ export const TOOL_CATALOG_ENTRIES = [
|
|
|
56
56
|
},
|
|
57
57
|
{
|
|
58
58
|
id: "bash", name: "Bash", source: "builtin", definedIn: "src/tools/fs/fs-bash.ts", mountedBy: ["hands"],
|
|
59
|
-
cards: ["bash", "readonly_out_of_root", "bash_invalid_timeout"],
|
|
59
|
+
cards: ["bash", "readonly_out_of_root", "read_unestablished", "bash_invalid_timeout"],
|
|
60
60
|
face: {
|
|
61
61
|
family: "shell", effect: "write", contentOrigin: "execution", contract: c(FULL_SHELL_CONTRACT_ID),
|
|
62
62
|
ruleFace: SHELL_RULE_FACE,
|
|
@@ -66,7 +66,7 @@ export const TOOL_CATALOG_ENTRIES = [
|
|
|
66
66
|
},
|
|
67
67
|
{
|
|
68
68
|
id: "bash-readonly", name: "Bash", source: "builtin", definedIn: "src/tools/fs/fs-bash.ts", mountedBy: ["hands"],
|
|
69
|
-
cards: ["bash", "readonly_out_of_root", "bash_invalid_timeout"],
|
|
69
|
+
cards: ["bash", "readonly_out_of_root", "read_unestablished", "bash_invalid_timeout"],
|
|
70
70
|
face: {
|
|
71
71
|
family: "shell", effect: "read", contract: c(READONLY_SHELL_CONTRACT_ID),
|
|
72
72
|
ruleFace: SHELL_RULE_FACE,
|
|
@@ -108,7 +108,7 @@ export const TOOL_CATALOG_ENTRIES = [
|
|
|
108
108
|
{ id: "task-stop-hands", name: "TaskStop", source: "builtin", definedIn: "src/tools/fs/fs-bash.ts", mountedBy: ["hands"], cards: [], face: { family: "delegate", effect: "write", contract: TASK_STOP_CONTRACT } },
|
|
109
109
|
{ id: "task-output", name: "TaskOutput", source: "builtin", definedIn: "src/core/task-registry.ts", mountedBy: ["delegation"], cards: ["task-output"], face: { family: "delegate", effect: "read", contract: TASK_OUTPUT_CONTRACT } },
|
|
110
110
|
{ id: "task-stop", name: "TaskStop", source: "builtin", definedIn: "src/core/task-registry.ts", mountedBy: ["delegation"], cards: ["task-stop"], face: { family: "delegate", effect: "write", contract: TASK_STOP_CONTRACT } },
|
|
111
|
-
{ id: "monitor", name: "Monitor", source: "builtin", definedIn: "src/tools/monitor.ts", mountedBy: ["backgroundTasks"], cards: ["monitor-start", "readonly_out_of_root"], face: { family: "other", effect: "write", contract: c("core.monitor@1"), offloadThresholdChars: 10_000 } },
|
|
111
|
+
{ id: "monitor", name: "Monitor", source: "builtin", definedIn: "src/tools/monitor.ts", mountedBy: ["backgroundTasks"], cards: ["monitor-start", "readonly_out_of_root", "read_unestablished"], face: { family: "other", effect: "write", contract: c("core.monitor@1"), offloadThresholdChars: 10_000 } },
|
|
112
112
|
{ id: "send-message", name: "SendMessage", source: "builtin", definedIn: "src/agents/send-message-tool.ts", mountedBy: ["delegation", "hostInternals", "peerLane"], cards: ["send-message"], face: { family: "protocol", effect: "write", contract: c("core.send_message@1") } },
|
|
113
113
|
{ id: "agent-transcript", name: "AgentTranscript", source: "builtin", definedIn: "src/agents/agent-transcript-tool.ts", mountedBy: ["delegation"], cards: ["agent-transcript"], face: { family: "delegate", effect: "read", contract: c("core.agent_transcript@1") } },
|
|
114
114
|
{ id: "list-agents", name: "ListAgents", source: "builtin", definedIn: "src/agents/list-agents-tool.ts", mountedBy: ["delegation", "peerLane"], cards: ["list-agents"], face: { family: "protocol", effect: "read", aliases: ["ListPeers"], contract: c("core.list_agents@1") } },
|
|
@@ -156,6 +156,7 @@ export const ENGINE_CARDS = Object.freeze({
|
|
|
156
156
|
path_not_in_root: "src/tools/fs/safety.ts",
|
|
157
157
|
read_path_denied: "src/tools/fs/safety.ts",
|
|
158
158
|
readonly_out_of_root: "src/tools/fs/shell-read-check.ts",
|
|
159
|
+
read_unestablished: "src/tools/fs/shell-read-check.ts",
|
|
159
160
|
});
|
|
160
161
|
const byId = new Map();
|
|
161
162
|
for (const e of TOOL_CATALOG_ENTRIES) {
|