@sema-agent/core 7.45.0 → 7.46.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 +246 -1
- package/dist/agents/extra-tool-names.d.ts +65 -0
- package/dist/agents/extra-tool-names.js +124 -0
- package/dist/agents/subagent.d.ts +17 -5
- package/dist/agents/subagent.js +33 -25
- package/dist/agents/tool-filter.d.ts +17 -1
- package/dist/agents/tool-filter.js +7 -0
- package/dist/core/agent-config-dir.d.ts +20 -0
- package/dist/core/agent-config-dir.js +49 -0
- package/dist/core/allow-entry.d.ts +5 -3
- package/dist/core/allow-entry.js +3 -1
- package/dist/core/ask-origin.js +1 -1
- package/dist/core/checkpoint-store.d.ts +39 -2
- package/dist/core/effective-path-target.d.ts +136 -4
- package/dist/core/effective-path-target.js +110 -2
- package/dist/core/engine-notice.d.ts +5 -0
- package/dist/core/gate-fold.js +3 -0
- package/dist/core/gate-lanes.js +9 -3
- package/dist/core/governance-codes.d.ts +3 -1
- package/dist/core/governance-codes.js +3 -0
- package/dist/core/hooks.d.ts +3 -0
- package/dist/core/path-rule-binding.d.ts +65 -0
- package/dist/core/path-rule-binding.js +99 -0
- package/dist/core/permission-rule-model.d.ts +47 -18
- package/dist/core/permission-rule-model.js +38 -42
- package/dist/core/permission-rule-provider.d.ts +7 -0
- package/dist/core/permission-rule-provider.js +10 -3
- package/dist/core/permission-rule-store.d.ts +8 -0
- package/dist/core/permission-rule-store.js +6 -2
- package/dist/core/permission-rule-sync.js +2 -2
- package/dist/core/permission-rules.d.ts +22 -40
- package/dist/core/permission-rules.js +50 -43
- package/dist/core/runner/child-tree.d.ts +97 -0
- package/dist/core/runner/child-tree.js +104 -0
- package/dist/core/runner/construction-doors.d.ts +5 -0
- package/dist/core/runner/construction-doors.js +6 -0
- package/dist/core/runner/contracts.d.ts +46 -0
- package/dist/core/runner/permission-rule-lanes.d.ts +34 -25
- package/dist/core/runner/permission-rule-lanes.js +18 -12
- package/dist/core/runner/persisted-path-rows.d.ts +31 -7
- package/dist/core/runner/persisted-path-rows.js +31 -12
- package/dist/core/runner/prepare-ask-lane.d.ts +4 -0
- package/dist/core/runner/prepare-ask-lane.js +4 -2
- package/dist/core/runner/prepare-execution-env.d.ts +5 -2
- package/dist/core/runner/prepare-execution-env.js +10 -3
- package/dist/core/runner/prepare-gate-stations.d.ts +3 -0
- package/dist/core/runner/prepare-gate-stations.js +2 -1
- package/dist/core/runner/prepare-hands-readface.d.ts +8 -4
- package/dist/core/runner/prepare-hands-readface.js +6 -6
- package/dist/core/runner/prepare-inherited-gate.d.ts +8 -1
- package/dist/core/runner/prepare-inherited-gate.js +8 -2
- package/dist/core/runner/prepare-park-ask.d.ts +3 -0
- package/dist/core/runner/prepare-park-ask.js +5 -3
- package/dist/core/runner/prepare-policy-chain.d.ts +14 -2
- package/dist/core/runner/prepare-policy-chain.js +11 -4
- package/dist/core/runner/prepare-suspend-saga.d.ts +10 -0
- package/dist/core/runner/prepare-suspend-saga.js +7 -1
- package/dist/core/runner/prepare-task.js +37 -18
- package/dist/core/runner/prepare-turn-wiring.d.ts +2 -0
- package/dist/core/runner/prepare-turn-wiring.js +2 -1
- package/dist/core/runner/prepare-workspace-restore.d.ts +3 -0
- package/dist/core/runner/prepare-workspace-restore.js +1 -0
- package/dist/core/runner/resume-apply.js +17 -3
- package/dist/core/runner/resume-checkpoint-screen.js +13 -0
- package/dist/core/runner/run-leg.js +5 -1
- package/dist/core/runner/runtask.js +2 -2
- package/dist/core/runner/sub-agent-deny-lists.d.ts +1 -1
- package/dist/core/runner/write-protection-station.d.ts +3 -2
- package/dist/core/runner/write-protection-station.js +1 -0
- package/dist/core/runner-deps.d.ts +14 -0
- package/dist/core/sensitive-path-policy.d.ts +8 -0
- package/dist/core/sensitive-path-policy.js +18 -2
- package/dist/core/shell-wrapper-projection.d.ts +11 -0
- package/dist/core/shell-wrapper-projection.js +56 -0
- package/dist/core/shell-wrapper-table.d.ts +144 -34
- package/dist/core/shell-wrapper-table.js +40 -20
- package/dist/core/synthetic-read-verdict.d.ts +3 -0
- package/dist/core/synthetic-read-verdict.js +3 -1
- package/dist/core/tool-catalog-entries.d.ts +21 -0
- package/dist/core/tool-catalog-entries.js +9 -0
- package/dist/core/tool-policy.d.ts +30 -8
- package/dist/core/tool-policy.js +9 -7
- package/dist/core/tool-spec.d.ts +3 -2
- package/dist/core/write-protect.d.ts +4 -0
- package/dist/core/write-protect.js +30 -9
- package/dist/index.d.ts +5 -3
- package/dist/index.js +4 -2
- package/dist/orchestration/run-spec.js +8 -7
- package/dist/orchestration/workflow.js +1 -1
- package/dist/shell/lex.d.ts +7 -0
- package/dist/shell/lex.js +17 -0
- package/dist/tools/fs/bash-program-position.d.ts +8 -87
- package/dist/tools/fs/bash-program-position.js +33 -69
- package/dist/tools/fs/bash-readonly-classifier.d.ts +17 -444
- package/dist/tools/fs/bash-readonly-classifier.js +169 -1225
- package/dist/tools/fs/index.d.ts +3 -2
- package/dist/tools/fs/program-read-model.d.ts +694 -0
- package/dist/tools/fs/program-read-model.js +1252 -0
- package/dist/tools/fs/program-read-model.rows.d.ts +18 -0
- package/dist/tools/fs/program-read-model.rows.js +613 -0
- package/dist/tools/fs/read-deny-home.d.ts +19 -1
- package/dist/tools/fs/read-deny-home.js +62 -24
- package/dist/tools/fs/read-deny.d.ts +54 -14
- package/dist/tools/fs/read-deny.js +104 -50
- package/dist/tools/fs/removal-targets.d.ts +7 -6
- package/dist/tools/fs/removal-targets.js +22 -22
- package/dist/tools/fs/safety.d.ts +3 -0
- package/dist/tools/fs/safety.js +10 -3
- package/dist/tools/fs/shell-read-argv.d.ts +85 -0
- package/dist/tools/fs/shell-read-argv.js +386 -0
- package/dist/tools/fs/shell-read-bindings.d.ts +273 -0
- package/dist/tools/fs/shell-read-bindings.js +746 -0
- package/dist/tools/fs/shell-read-check.js +23 -15
- package/dist/tools/fs/shell-read-establish.js +79 -5
- package/dist/tools/fs/shell-read-judge.d.ts +11 -1
- package/dist/tools/fs/shell-read-judge.js +9 -1
- package/dist/tools/fs/shell-read-plan.d.ts +31 -7
- package/dist/tools/fs/shell-read-plan.js +299 -393
- package/dist/tools/fs/shell-read-word.d.ts +29 -26
- package/dist/tools/fs/shell-read-word.js +30 -38
- package/dist/tools/fs/shell-read-write-targets.d.ts +15 -64
- package/dist/tools/fs/shell-read-write-targets.js +83 -136
- package/dist/tools/fs/shell-reentry-text.d.ts +38 -6
- package/dist/tools/fs/shell-reentry-text.js +90 -53
- package/package.json +1 -1
- package/test/export-surface.snapshot.json +17 -3
- package/wire-changes/core@7.46.0.json +1522 -0
|
@@ -2,8 +2,8 @@ import { persistedRuleMandateOf, ruleClearanceOf } from "../ask-origin.js";
|
|
|
2
2
|
import {} from "../hooks.js";
|
|
3
3
|
import { adjudicatePersistedNameRules, adjudicatePersistedPathRules, adjudicatePersistedRules, callPathFaceOf, faceAskSpeaksFor, laneToolNamesOf, nameRuleSubjectOf, ruleToolGrammarOf, segmentCoverageOf, suggestRuleForName, suggestRulesForCommand } from "../permission-rule-model.js";
|
|
4
4
|
import { isAbsolutePathForm } from "../../tools/fs/safety.js";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
5
|
+
import { callTargetViewsOf } from "../effective-path-target.js";
|
|
6
|
+
import { bindPathRules, persistedRowAnchors, projectScopeIdentity } from "../path-rule-binding.js";
|
|
7
7
|
import { heldBy, seatOf, wireOf } from "../tool-name-readings.js";
|
|
8
8
|
import { orgRuleVerdictFor, settleOrgVerdictWithin } from "../permission-rule-org.js";
|
|
9
9
|
import { ASK_USER_QUESTION_TOOL_NAME } from "../ask-question.js";
|
|
@@ -13,6 +13,7 @@ import { strongestAnswer } from "../persisted-rule-arms.js";
|
|
|
13
13
|
import { gateWaitFailureOf, withGateTimeout } from "../gate-bounded-await.js";
|
|
14
14
|
import { ORG_ADJUDICATION_TIMEOUT_MS } from "../permission-rule-org.js";
|
|
15
15
|
export const PERSISTED_RULE_TOOL = "Bash";
|
|
16
|
+
const NO_LEADS = new Map();
|
|
16
17
|
const HANDOFF_BOUND = 256;
|
|
17
18
|
export function inheritedAskRuleEvidence(provider) {
|
|
18
19
|
const org = provider?.partitions.org === true ? "not_adjudicated" : "not_wired";
|
|
@@ -40,12 +41,11 @@ export function persistedRuleAnswerOf(verdict) {
|
|
|
40
41
|
return persistedRuleHitOf(verdict);
|
|
41
42
|
}
|
|
42
43
|
export function pathRuleLaneAnswer(table, req, ctx) {
|
|
43
|
-
const
|
|
44
|
-
const
|
|
45
|
-
const allow = effectivePathTargetOf(req, declaredPathTargetOf(req), bases);
|
|
44
|
+
const tighten = callTargetViewsOf(req, "tighten");
|
|
45
|
+
const allow = callTargetViewsOf(req, "allow");
|
|
46
46
|
if (tighten === undefined && allow === undefined)
|
|
47
47
|
return undefined;
|
|
48
|
-
return persistedRuleAnswerOf(adjudicatePersistedPathRules(table, { tool: req.toolName, cwd: ctx.root, sessionId: ctx.sessionId, face: callPathFaceOf(req) }, { ...(tighten !== undefined ? { tighten } : {}), ...(allow !== undefined ? { allow } : {}) },
|
|
48
|
+
return persistedRuleAnswerOf(adjudicatePersistedPathRules(table, { tool: req.toolName, cwd: ctx.root, sessionId: ctx.sessionId, face: callPathFaceOf(req), ...(ctx.scopeIdentity !== undefined ? { scopeIdentity: ctx.scopeIdentity } : {}) }, { ...(tighten !== undefined ? { tighten } : {}), ...(allow !== undefined ? { allow } : {}) }, ctx.anchorsOf, ctx.leads));
|
|
49
49
|
}
|
|
50
50
|
export function orgRuleLane(cfg) {
|
|
51
51
|
return {
|
|
@@ -141,8 +141,12 @@ export function createPermissionRuleLanes(cfg) {
|
|
|
141
141
|
}
|
|
142
142
|
if (view.unreadable !== undefined)
|
|
143
143
|
skipped(view.unreadable, view.unreadableSources);
|
|
144
|
+
cfg.onRowsDropped(view.droppedRows);
|
|
144
145
|
try {
|
|
145
|
-
const
|
|
146
|
+
const table = view.rules.filter((r) => r.behavior !== "allow" || cfg.stripsAllow?.(r, req) !== true);
|
|
147
|
+
const leads = judged.some(([, g]) => g === "path") ? await cfg.leads.resolve(bindPathRules(table.filter((r) => r.match === "path" || r.match === "subpath").map((r) => ({ parsed: r, ruleText: r.rule, rule: r })), (spec) => anchorsOf(spec.rule))) : NO_LEADS;
|
|
148
|
+
const scopeIdentity = await projectScopeIdentity(table, cfg.rootCanonical, cfg.leads);
|
|
149
|
+
const answer = strongestAnswer(judged.map(([n, grammar]) => judgeAgainst(table, { ...req, toolName: n }, grammar, leads, scopeIdentity)));
|
|
146
150
|
return view.unreadable !== undefined && (answer === undefined || "segmentCoverage" in answer) ? { unreadable: true } : answer;
|
|
147
151
|
}
|
|
148
152
|
catch (err) {
|
|
@@ -151,20 +155,22 @@ export function createPermissionRuleLanes(cfg) {
|
|
|
151
155
|
return { unreadable: true, reason: why };
|
|
152
156
|
}
|
|
153
157
|
};
|
|
154
|
-
const
|
|
158
|
+
const anchorsOf = (rule) => persistedRowAnchors(rule.scope, cfg.chainTop);
|
|
159
|
+
const judgeAgainst = (table, req, grammar, leads, scopeIdentity) => {
|
|
155
160
|
const liveCwd = cfg.liveCwd();
|
|
161
|
+
const scope = scopeIdentity !== undefined ? { scopeIdentity } : {};
|
|
156
162
|
if (grammar === "path")
|
|
157
|
-
return pathRuleLaneAnswer(table, req, { root: cfg.root, sessionId: cfg.sessionId,
|
|
163
|
+
return pathRuleLaneAnswer(table, req, { root: cfg.root, sessionId: cfg.sessionId, anchorsOf, leads, ...scope });
|
|
158
164
|
if (grammar === "name")
|
|
159
|
-
return persistedRuleAnswerOf(adjudicatePersistedNameRules(table, { tool: req.toolName, name: nameRuleSubjectOf(req.toolName, req.args), cwd: cfg.root, sessionId: cfg.sessionId }));
|
|
165
|
+
return persistedRuleAnswerOf(adjudicatePersistedNameRules(table, { tool: req.toolName, name: nameRuleSubjectOf(req.toolName, req.args), cwd: cfg.root, sessionId: cfg.sessionId, ...scope }));
|
|
160
166
|
const command = req.args?.command;
|
|
161
167
|
if (typeof command !== "string")
|
|
162
168
|
return undefined;
|
|
163
169
|
const execCwd = liveCwd !== undefined ? { execCwd: liveCwd } : {};
|
|
164
|
-
const verdict = adjudicatePersistedRules(table, { tool: req.toolName, command, cwd: cfg.root, sessionId: cfg.sessionId, ...execCwd });
|
|
170
|
+
const verdict = adjudicatePersistedRules(table, { tool: req.toolName, command, cwd: cfg.root, sessionId: cfg.sessionId, ...execCwd, ...scope });
|
|
165
171
|
if (verdict !== undefined)
|
|
166
172
|
return persistedRuleAnswerOf(verdict);
|
|
167
|
-
const coverage = segmentCoverageOf(command, { persisted: table }, { tool: req.toolName, cwd: cfg.root, sessionId: cfg.sessionId, ...execCwd });
|
|
173
|
+
const coverage = segmentCoverageOf(command, { persisted: table }, { tool: req.toolName, cwd: cfg.root, sessionId: cfg.sessionId, ...execCwd, ...scope });
|
|
168
174
|
return coverage !== undefined ? { segmentCoverage: coverage } : undefined;
|
|
169
175
|
};
|
|
170
176
|
const skipped = (why, sources) => (cfg.onDisclosure(why), cfg.onStoreUnreadable("call", sources), { unreadable: true });
|
|
@@ -3,14 +3,16 @@
|
|
|
3
3
|
* per call).
|
|
4
4
|
*/
|
|
5
5
|
import { type EffectivePermissionRules, type PermissionRuleStoreProvider } from "../permission-rule-provider.js";
|
|
6
|
-
import type
|
|
6
|
+
import { type PathRuleBases } from "../permission-rule-model.js";
|
|
7
|
+
import { type BoundPathRule, type HeadLeads } from "../path-rule-binding.js";
|
|
7
8
|
import type { RunnerDeps } from "../runner-deps.js";
|
|
8
9
|
/** The person's persisted PATH rows a leg's read face takes at prepare ({@link persistedPathRowsOf}). */
|
|
9
10
|
export interface PersistedPathRows {
|
|
10
11
|
/** Every live `Read(//dir/**)` allow row's directory (lexical, absolute): a standing yes to reads under it. */
|
|
11
12
|
readonly readDirectories: readonly string[];
|
|
12
|
-
/** Every live face-keyed path DENY row (`Read(...)`, `Edit(...)`),
|
|
13
|
-
|
|
13
|
+
/** Every live face-keyed path DENY row (`Read(...)`, `Edit(...)`), bound at its anchors (a project row at its scope root,
|
|
14
|
+
* the rest at the chain's top leg). */
|
|
15
|
+
readonly denyRows: readonly BoundPathRule[];
|
|
14
16
|
}
|
|
15
17
|
/**
|
|
16
18
|
* The person's persisted PATH rows, read ONCE at prepare from the same store query the gate's lanes read per call: a
|
|
@@ -22,19 +24,28 @@ export interface PersistedPathRows {
|
|
|
22
24
|
* rows. A read that failed (or did not answer in time) ⇒ no rows, and a partition the read could not read ⇒ none of ITS
|
|
23
25
|
* rows (the partition that was read contributes its own): what could not be read is skipped for this run's tables — its
|
|
24
26
|
* directories widen nothing and its deny rows judge nothing here — disclosed on the trace and announced to the person
|
|
25
|
-
* (`onUnread`); the next prepare reads again, as it would for a row the store gained mid-run. A deny row
|
|
26
|
-
*
|
|
27
|
+
* (`onUnread`); the next prepare reads again, as it would for a row the store gained mid-run. A deny row is anchored by
|
|
28
|
+
* its scope (`persistedRowAnchors`: a project row at its own scope root, a global / session row at the chain's top leg,
|
|
29
|
+
* `~/` at the top leg's home); one whose pattern needs an anchor the chain does not supply (`~/` with no declared home) is
|
|
30
|
+
* left to the per-call lane and disclosed.
|
|
27
31
|
*/
|
|
28
32
|
export declare function persistedPathRowsOf(cfg: {
|
|
29
33
|
provider: PermissionRuleStoreProvider | undefined;
|
|
30
34
|
localOwnerDeclared: boolean;
|
|
31
35
|
principal: string | undefined;
|
|
32
36
|
sessionId: string;
|
|
37
|
+
/** The leg's own root: which project rows apply to this leg (either spelling of it — {@link projectScopeIdentity}). */
|
|
33
38
|
root: string;
|
|
34
|
-
|
|
39
|
+
rootCanonical: string | undefined;
|
|
40
|
+
/** The leg's resolution of locations (a project row's scope root, where it leads). */
|
|
41
|
+
leads: HeadLeads;
|
|
42
|
+
/** The anchors of the global / session rows and of `~/`: the chain's top leg's root and home. */
|
|
43
|
+
chainTop: PathRuleBases;
|
|
35
44
|
onDisclosure: (message: string) => void;
|
|
36
45
|
/** The person is told the store (or the named sources of it) was skipped ({@link ruleStoreUnreadableNotice}). */
|
|
37
46
|
onUnread: (at: "prepare", sources?: EffectivePermissionRules["unreadableSources"]) => void;
|
|
47
|
+
/** The person is told rows were left out ({@link ruleRowsDroppedNotice}). */
|
|
48
|
+
onRowsDropped: (rows: EffectivePermissionRules["droppedRows"]) => void;
|
|
38
49
|
/** The run's cut: a read that has not answered by then (or by the gate's await bound) reads as unreadable. */
|
|
39
50
|
signal?: AbortSignal;
|
|
40
51
|
}): Promise<PersistedPathRows>;
|
|
@@ -48,6 +59,16 @@ export declare function ruleStoreUnreadableNotice(onNotice: RunnerDeps["onNotice
|
|
|
48
59
|
readonly sessionId: string;
|
|
49
60
|
readonly runId: string;
|
|
50
61
|
}): (at: "prepare" | "call", sources?: EffectivePermissionRules["unreadableSources"]) => void;
|
|
62
|
+
/**
|
|
63
|
+
* The run's ONE `permission.rule_row_dropped` notice (audience user): the person's store served project-scoped rows whose
|
|
64
|
+
* scope root is not a complete absolute path, left out of the checks; minted by whichever read met them first (the
|
|
65
|
+
* prepare read of the path rows, or a call's own read on the rule lane), once per run. The sentence names no path; the
|
|
66
|
+
* rows (bounded) ride the detail. The trace keeps every read's line.
|
|
67
|
+
*/
|
|
68
|
+
export declare function ruleRowsDroppedNotice(onNotice: RunnerDeps["onNotice"], ids: {
|
|
69
|
+
readonly sessionId: string;
|
|
70
|
+
readonly runId: string;
|
|
71
|
+
}): (rows: EffectivePermissionRules["droppedRows"]) => void;
|
|
51
72
|
/** {@link persistedPathRowsOf} for one task's prepare: only where a read face mounts (hands), disclosed on the run's trace,
|
|
52
73
|
* cut by the run's own signals. `undefined` = nothing to read, returned synchronously: a run with no person's store (or
|
|
53
74
|
* no hands) gains no suspension point on its prepare. */
|
|
@@ -56,9 +77,12 @@ export declare function persistedPathRowsOfTask(t: {
|
|
|
56
77
|
principal: string | undefined;
|
|
57
78
|
sessionId: string;
|
|
58
79
|
root: string;
|
|
59
|
-
|
|
80
|
+
rootCanonical: string | undefined;
|
|
81
|
+
leads: HeadLeads;
|
|
82
|
+
chainTop: PathRuleBases;
|
|
60
83
|
taskId: string;
|
|
61
84
|
handsEnabled: boolean;
|
|
62
85
|
signals: readonly AbortSignal[];
|
|
63
86
|
onUnread: (at: "prepare", sources?: EffectivePermissionRules["unreadableSources"]) => void;
|
|
87
|
+
onRowsDropped: (rows: EffectivePermissionRules["droppedRows"]) => void;
|
|
64
88
|
}): Promise<PersistedPathRows> | undefined;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { ownerStoreOf } from "../permission-rule-provider.js";
|
|
2
|
-
import { PATH_RULE_FACES, READ_RULE_TOOL, eligiblePersisted
|
|
3
|
-
import {
|
|
4
|
-
import { pathRuleDenyRowOf } from "../permission-rules.js";
|
|
2
|
+
import { PATH_RULE_FACES, READ_RULE_TOOL, eligiblePersisted } from "../permission-rule-model.js";
|
|
3
|
+
import { bindPathRules, persistedRowAnchors, projectScopeIdentity } from "../path-rule-binding.js";
|
|
5
4
|
import { inlineUntrusted } from "../untrusted-text.js";
|
|
6
5
|
import { DEFAULT_GATE_AWAIT_TIMEOUT_MS, gateWaitFailureOf, withGateTimeout } from "../gate-bounded-await.js";
|
|
7
6
|
import { thrownText } from "../thrown-value.js";
|
|
@@ -21,11 +20,11 @@ export async function persistedPathRowsOf(cfg) {
|
|
|
21
20
|
}
|
|
22
21
|
if (view.unreadable !== undefined)
|
|
23
22
|
skipped(cfg, view.unreadable, view.unreadableSources);
|
|
24
|
-
|
|
25
|
-
const
|
|
26
|
-
const
|
|
23
|
+
cfg.onRowsDropped(view.droppedRows);
|
|
24
|
+
const scopeIdentity = await projectScopeIdentity(view.rules, cfg.rootCanonical, cfg.leads);
|
|
25
|
+
const call = { tool: READ_RULE_TOOL, cwd: cfg.root, sessionId: cfg.sessionId, ...(scopeIdentity !== undefined ? { scopeIdentity } : {}) };
|
|
27
26
|
const readDirectories = [];
|
|
28
|
-
const
|
|
27
|
+
const denySpecs = [];
|
|
29
28
|
for (const rule of view.rules) {
|
|
30
29
|
if (rule.status !== "live")
|
|
31
30
|
continue;
|
|
@@ -37,12 +36,15 @@ export async function persistedPathRowsOf(cfg) {
|
|
|
37
36
|
const faces = PATH_RULE_FACES[rule.tool]?.deny;
|
|
38
37
|
if (rule.behavior !== "deny" || faces === undefined || (rule.match !== "path" && rule.match !== "subpath") || !eligiblePersisted(rule, { ...call, tool: rule.tool }, "deny"))
|
|
39
38
|
continue;
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
39
|
+
denySpecs.push({ parsed: rule, ruleText: rule.rule, faces, rule });
|
|
40
|
+
}
|
|
41
|
+
const denyRows = [];
|
|
42
|
+
for (const row of bindPathRules(denySpecs, (spec) => persistedRowAnchors(spec.rule.scope, cfg.chainTop))) {
|
|
43
|
+
if (row.unanchored !== undefined) {
|
|
44
|
+
cfg.onDisclosure(`the persisted deny rule ${inlineUntrusted(row.rule, 200)} needs a ${row.unanchored.join(" / ")} base this task does not declare — the shell's file commands and the search tools do not judge it on this leg`);
|
|
43
45
|
continue;
|
|
44
46
|
}
|
|
45
|
-
denyRows.push(
|
|
47
|
+
denyRows.push(row);
|
|
46
48
|
}
|
|
47
49
|
return { readDirectories, denyRows };
|
|
48
50
|
}
|
|
@@ -68,6 +70,20 @@ export function ruleStoreUnreadableNotice(onNotice, ids) {
|
|
|
68
70
|
deliverEngineNotice(onNotice, { code: "permission.rule_store_unavailable", message: SKIPPED_STORE_SENTENCE[at](which), detail: { at, sessionId: ids.sessionId, runId: ids.runId } });
|
|
69
71
|
};
|
|
70
72
|
}
|
|
73
|
+
export function ruleRowsDroppedNotice(onNotice, ids) {
|
|
74
|
+
let sent = false;
|
|
75
|
+
return (rows) => {
|
|
76
|
+
if (sent || rows === undefined || rows.length === 0)
|
|
77
|
+
return;
|
|
78
|
+
sent = true;
|
|
79
|
+
const one = rows.length === 1;
|
|
80
|
+
deliverEngineNotice(onNotice, {
|
|
81
|
+
code: "permission.rule_row_dropped",
|
|
82
|
+
message: `${rows.length} of your persisted permission rule rows ${one ? "is" : "are"} scoped to a project directory that is not written as a complete absolute path, so ${one ? "it takes" : "they take"} no part in this run's checks — a deny or ask row among them protects nothing. Rewrite ${one ? "it" : "them"} with the project's full path, or remove ${one ? "it" : "them"} (this notice's detail names ${one ? "it" : "them"}).`,
|
|
83
|
+
detail: { rows: rows.slice(0, 8).map((r) => ({ rule: inlineUntrusted(r.rule, 200), scopeRoot: inlineUntrusted(r.scopeRoot, 200) })), count: rows.length, sessionId: ids.sessionId, runId: ids.runId },
|
|
84
|
+
});
|
|
85
|
+
};
|
|
86
|
+
}
|
|
71
87
|
function readsPersistedPathRows(provider, principal, localOwnerDeclared) {
|
|
72
88
|
return provider.partitions.durable && ((principal !== undefined && principal !== "") || localOwnerDeclared);
|
|
73
89
|
}
|
|
@@ -81,9 +97,12 @@ export function persistedPathRowsOfTask(t) {
|
|
|
81
97
|
principal: t.principal,
|
|
82
98
|
sessionId: t.sessionId,
|
|
83
99
|
root: t.root,
|
|
84
|
-
|
|
100
|
+
rootCanonical: t.rootCanonical,
|
|
101
|
+
leads: t.leads,
|
|
102
|
+
chainTop: t.chainTop,
|
|
85
103
|
onDisclosure: (message) => emitTrace(t.deps.tracer, () => ({ kind: "permission.rule_store_unreadable", version: 1, taskId: t.taskId, message, ts: Date.now() })),
|
|
86
104
|
onUnread: t.onUnread,
|
|
105
|
+
onRowsDropped: t.onRowsDropped,
|
|
87
106
|
signal: AbortSignal.any([...t.signals]),
|
|
88
107
|
});
|
|
89
108
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { CallTargetStamp } from "../effective-path-target.js";
|
|
1
2
|
import type { AgentTool } from "../../internal/harness.js";
|
|
2
3
|
import type { AdvertisedWritableDirs } from "./advertised-writable-dirs.js";
|
|
3
4
|
import type { AutoModeDenialTracker } from "../auto-mode.js";
|
|
@@ -30,6 +31,9 @@ export interface PrepareAskLaneInput {
|
|
|
30
31
|
handsCwdRef: CwdRef | undefined;
|
|
31
32
|
/** borrowed-readonly — the root the hand tools resolve root-relative spellings against, or undefined (hands-less). */
|
|
32
33
|
handsRoot: string | undefined;
|
|
34
|
+
/** borrowed-readonly — the leg's target-reading stamp ({@link CallTargetStamp}), or undefined (hands-less): the request a
|
|
35
|
+
* policy judges carries a reading of its own args. Called at RUN time; a seat with nothing to stamp answers undefined. */
|
|
36
|
+
stampCallTarget: CallTargetStamp | undefined;
|
|
33
37
|
/** borrowed-readonly — the run's advertised-writable-directory seat, or undefined (hands-less / nothing advertised). `roots` is read at RUN time, never captured: it is the fence's own admitted list and the write gate's exemption domain must be that same list. Not written here. */
|
|
34
38
|
advertisedWritableDirs: Pick<AdvertisedWritableDirs, "roots"> | undefined;
|
|
35
39
|
/** borrowed-readonly — the shared mount roster; read at RUN time by the approval-preview projection (alias-aware
|
|
@@ -59,14 +59,16 @@ function resolveApprovalPreview(tools, toolName, args) {
|
|
|
59
59
|
}
|
|
60
60
|
}
|
|
61
61
|
export function prepareAskLane(input) {
|
|
62
|
-
const { gateMachineryActive, abortController, effectivePolicy, budgetSnapshot, handsCwdRef, handsRoot, advertisedWritableDirs, tools, inheritedUnavailableAsks, inheritedAskGrants, onAsk, humanReviewRef, now, ruleOffersOf, grantBindingOf, askSourceIdentity, riskAxesOf, autoModeDenialTracking, spec, deps, sessionId, runId, hooks, hookTimeoutMs, notifyOwnHookCrash, grantReadRoot } = input;
|
|
62
|
+
const { gateMachineryActive, abortController, effectivePolicy, budgetSnapshot, handsCwdRef, handsRoot, stampCallTarget, advertisedWritableDirs, tools, inheritedUnavailableAsks, inheritedAskGrants, onAsk, humanReviewRef, now, ruleOffersOf, grantBindingOf, askSourceIdentity, riskAxesOf, autoModeDenialTracking, spec, deps, sessionId, runId, hooks, hookTimeoutMs, notifyOwnHookCrash, grantReadRoot } = input;
|
|
63
63
|
if (!gateMachineryActive)
|
|
64
64
|
return { askLane: undefined };
|
|
65
65
|
const composedCallSignal = (callSignal) => composeCallSignal(abortController.signal, callSignal);
|
|
66
66
|
const adjudicate = effectivePolicy
|
|
67
67
|
? (req, callSignal) => {
|
|
68
68
|
const signal = composedCallSignal(callSignal);
|
|
69
|
-
|
|
69
|
+
const stampedReq = () => ({ ...req, budget: budgetSnapshot, ...(handsCwdRef !== undefined ? { cwd: handsCwdRef.current } : {}), ...(handsRoot !== undefined ? { root: handsRoot } : {}), ...advertisedWritableRootsSeat(advertisedWritableDirs) });
|
|
70
|
+
const withReading = stampCallTarget?.(stampedReq(), signal);
|
|
71
|
+
return raceAbort(withReading === undefined ? Promise.resolve(effectivePolicy.check(stampedReq(), signal)) : withReading.then((r) => effectivePolicy.check(r, signal)), signal, () => ({
|
|
70
72
|
action: "deny",
|
|
71
73
|
message: "policy check aborted (task timed out or cancelled)",
|
|
72
74
|
}));
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ExecutionEnv } from "../../internal/harness.js";
|
|
2
2
|
import type { RunnerDeps, TaskSpec } from "../types.js";
|
|
3
|
-
import type { PrepareResume, RollbackStack, RunInternals } from "./contracts.js";
|
|
3
|
+
import type { ChildPlacement, PrepareResume, RollbackStack, RunInternals } from "./contracts.js";
|
|
4
4
|
/**
|
|
5
5
|
* The ONE reference to a factory-minted env, shared by the driver's detach guard and the rollback entry
|
|
6
6
|
* this phase registers. `current` is the minted env, or `undefined` when the run has none / after the
|
|
@@ -19,7 +19,7 @@ export interface PrepareExecutionEnvInput {
|
|
|
19
19
|
spec: Pick<TaskSpec, "taskId">;
|
|
20
20
|
/** borrowed-readonly — the trusted spawn-side requests: the explicit cwd (a contract), the isolation
|
|
21
21
|
* request and the parent's cwd (both threaded to the factory ctx). */
|
|
22
|
-
internals: Pick<RunInternals, "requestedCwd" | "isolation" | "parentCwd"> | undefined;
|
|
22
|
+
internals: Pick<RunInternals, "requestedCwd" | "isolation" | "parentCwd" | "callerRoot" | "worktreeOffered"> | undefined;
|
|
23
23
|
/** borrowed-readonly — the resume leg, read only to refuse an explicit cwd that a checkpoint workspace
|
|
24
24
|
* restore would overrule. */
|
|
25
25
|
resume: Pick<PrepareResume, "workspaceHandle"> | undefined;
|
|
@@ -50,5 +50,8 @@ export interface PrepareExecutionEnvResult {
|
|
|
50
50
|
/** borrowed-readonly — the INITIAL task root (the workspace-restore phase may re-root it and returns the
|
|
51
51
|
* settled root under the NEW name `taskRootFinal`). */
|
|
52
52
|
taskRootInitial: string;
|
|
53
|
+
/** borrowed-readonly — where a CHILD landed against where its caller asked ({@link childPlacementOnLeg}); absent on a leg no
|
|
54
|
+
* delegation caller placed (no `callerRoot`). */
|
|
55
|
+
placement: ChildPlacement | undefined;
|
|
53
56
|
}
|
|
54
57
|
export declare function prepareExecutionEnv(input: PrepareExecutionEnvInput): Promise<PrepareExecutionEnvResult>;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { StubExecutionEnv } from "../stub-env.js";
|
|
2
2
|
import { hasDestroy } from "../remote-env.js";
|
|
3
3
|
import { announceMalformedCapabilities } from "./env-capability-census.js";
|
|
4
|
-
import { deploymentCanRerootChild } from "./child-tree.js";
|
|
4
|
+
import { childPlacementOnLeg, deploymentCanRerootChild, worktreeNotHonored } from "./child-tree.js";
|
|
5
5
|
import { settleTeardownLeg } from "./teardown-bounded.js";
|
|
6
6
|
export async function prepareExecutionEnv(input) {
|
|
7
7
|
const { deps, spec, internals, resume, placementRootResolved, sessionId, forgetOnThrow, rollback } = input;
|
|
@@ -69,8 +69,15 @@ export async function prepareExecutionEnv(input) {
|
|
|
69
69
|
}
|
|
70
70
|
}
|
|
71
71
|
const handsEnabled = ownedEnv !== undefined || deps.executionEnv !== undefined;
|
|
72
|
-
const taskRootInitial = internals?.isolation === "worktree" || internals?.requestedCwd !== undefined || !deps.rootPath ? executionEnv.cwd : deps.rootPath;
|
|
73
|
-
|
|
72
|
+
const taskRootInitial = internals?.isolation === "worktree" || internals?.requestedCwd !== undefined || internals?.worktreeOffered === true || !deps.rootPath ? executionEnv.cwd : deps.rootPath;
|
|
73
|
+
const placement = internals?.callerRoot === undefined ? undefined : await childPlacementOnLeg(executionEnv, { ...internals, callerRoot: internals.callerRoot }, taskRootInitial);
|
|
74
|
+
if (internals?.worktreeOffered === true && placement === "caller_tree") {
|
|
75
|
+
await settleTeardownLeg(() => forgetOnThrow(), "forgetOnThrow (worktree-not-honored leg)", (err) => deps.onError?.(err, { phase: "config", sessionId }));
|
|
76
|
+
await settleTeardownLeg(() => (ownedEnv && hasDestroy(ownedEnv) ? ownedEnv.destroy() : undefined), "ownedEnv.destroy (worktree-not-honored leg)", (err) => deps.onError?.(err, { phase: "config", sessionId }));
|
|
77
|
+
ownedEnvSeat.current = undefined;
|
|
78
|
+
throw worktreeNotHonored(executionEnv.cwd);
|
|
79
|
+
}
|
|
80
|
+
return { executionEnv, ownedEnvSeat, ownedEnvObservedAt, handsEnabled, taskRootInitial, placement };
|
|
74
81
|
}
|
|
75
82
|
function cwdConflictsRestoreError(requestedCwd) {
|
|
76
83
|
const e = new Error(`RunInternals.requestedCwd ("${requestedCwd}") cannot be combined with a checkpoint workspace restore — ` +
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
* gate outcome; nothing here changes a word, a source or an order — the stations are moved, not re-adjudicated.
|
|
25
25
|
*/
|
|
26
26
|
import type { AgentHarness, AgentTool } from "../../internal/harness.js";
|
|
27
|
+
import type { CallTargetStamp } from "../effective-path-target.js";
|
|
27
28
|
import type { AskClass } from "../ask-class.js";
|
|
28
29
|
import type { Arbitration, ToolMarkSource } from "../ask-origin.js";
|
|
29
30
|
import { type PermissionModeController } from "../mode-policy.js";
|
|
@@ -152,6 +153,8 @@ export interface PrepareGateStationsInput {
|
|
|
152
153
|
/** borrowed-readonly — the hand's live tracked cwd, or undefined; `current` is read per screening / per post seat. Not
|
|
153
154
|
* written here. */
|
|
154
155
|
handsCwdRef: CwdRef | undefined;
|
|
156
|
+
/** borrowed-readonly — the leg's target-reading stamp, or undefined (hands-less): the gate stamps its request with it. */
|
|
157
|
+
stampCallTarget: CallTargetStamp | undefined;
|
|
155
158
|
/** borrowed-readonly — the leg's read-deny table in force, read at a release: the active built-in tiers and the rows
|
|
156
159
|
* beside them (the deployment's and the task's patterns, the read-face rule rows). Empty ⇒ no table. */
|
|
157
160
|
readDenyInForce: () => {
|
|
@@ -46,7 +46,7 @@ export function maybeHumanRejectionHalt(input) {
|
|
|
46
46
|
};
|
|
47
47
|
}
|
|
48
48
|
export function prepareGateStations(input) {
|
|
49
|
-
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, readFace, irreversibilityTier, reversibilityProbes, abortController, shellGatedTools, autoModeDecider, autoModeDenialTracking, askRoute, permissionMode, toolMarkSources, a2aIsolatePeers, ancestorArbitrations, runId, stopForDenialLimit, permissionRuleLane, permissionRuleOrgLane, questionToolMounted, sandboxAdmissionArmed, sandboxBoundaryCapable, emitSandboxAdmitted, delegation, notifyOwnHookCrash, frameSettlement, seatInputsFor } = input;
|
|
49
|
+
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, stampCallTarget, hookEnvFace, writeProtectionCheck, shellReadCheck, readFace, irreversibilityTier, reversibilityProbes, abortController, shellGatedTools, autoModeDecider, autoModeDenialTracking, askRoute, permissionMode, toolMarkSources, a2aIsolatePeers, ancestorArbitrations, runId, stopForDenialLimit, permissionRuleLane, permissionRuleOrgLane, questionToolMounted, sandboxAdmissionArmed, sandboxBoundaryCapable, emitSandboxAdmitted, delegation, notifyOwnHookCrash, frameSettlement, seatInputsFor } = input;
|
|
50
50
|
const gatePreToolUse = ownGatePreToolUse !== undefined ? (name, args, ctx) => ownGatePreToolUse(name, args, { ...ctx, permissionMode: permissionMode.observed }) : undefined;
|
|
51
51
|
const releasesAnnounced = new Map();
|
|
52
52
|
const onModeReleased = (info) => {
|
|
@@ -120,6 +120,7 @@ export function prepareGateStations(input) {
|
|
|
120
120
|
...(gatePreToolUse !== undefined ? { preToolUse: gatePreToolUse } : {}),
|
|
121
121
|
hookTimeoutMs,
|
|
122
122
|
...(handsCwdRef !== undefined ? { trackedCwd: () => handsCwdRef.current } : {}),
|
|
123
|
+
...(stampCallTarget !== undefined ? { stampCallTarget } : {}),
|
|
123
124
|
...(hookEnvFace !== undefined ? { hookEnv: hookEnvFace } : {}),
|
|
124
125
|
toolFaceOf: (name) => toolCallFaceOf(toolRosterDeltas.current, name === e.toolName ? called.toolName : name),
|
|
125
126
|
adjudicate,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type BeforeWriteHook, type CwdRef, type PathRuleDenyRow, type ContainmentFace } from "../../tools/fs/index.js";
|
|
2
2
|
import { type ReadFileState } from "../../tools/fs/safety.js";
|
|
3
3
|
import { type PlacedReadDenyMatcher } from "../../tools/fs/read-deny-home.js";
|
|
4
|
+
import type { HeadLeads } from "../path-rule-binding.js";
|
|
4
5
|
import { type ShellReadAdmission, type ShellReadCheck } from "../../tools/fs/shell-read-check.js";
|
|
5
6
|
import type { ShellReadBoundary } from "../../tools/fs/shell-read-judge.js";
|
|
6
7
|
import type { RunnerDeps, ShellGateDoctrine, TaskSpec } from "../types.js";
|
|
@@ -54,9 +55,12 @@ export interface PrepareHandsReadFaceInput {
|
|
|
54
55
|
/** borrowed-readonly — the registry scope (`internals?.registryScope ?? spec.principal ??
|
|
55
56
|
* "default"`, computed in the driver); the band's task-registry scope. */
|
|
56
57
|
taskScope: string;
|
|
57
|
-
/** borrowed-readonly — the deny tables' permission-rule rows: the path deny rules of the leg's caller policy and
|
|
58
|
-
* ancestor's (`
|
|
59
|
-
|
|
58
|
+
/** borrowed-readonly — the deny tables' permission-rule rows: the face-keyed path deny rules of the leg's caller policy and
|
|
59
|
+
* of every ancestor's, each bound where it was installed (`toolPolicyPathRules`), and the person's deny rows; joined to the
|
|
60
|
+
* read face's matcher and to the shell station's write face. */
|
|
61
|
+
denyRuleRows: readonly PathRuleDenyRow[];
|
|
62
|
+
/** borrowed-readonly — the leg's one resolution of where rule heads lead (the rows' twins, the same the gate's lanes read). */
|
|
63
|
+
headLeads: HeadLeads;
|
|
60
64
|
/** borrowed-readonly — the two judges a shell command's write targets meet for the mode's file-command placement: the
|
|
61
65
|
* leg's write-protection judge (read per judgment — it is compiled at the manifest phase, before any gate is mounted)
|
|
62
66
|
* and the chain's write-target guard (`toolPolicyWriteTargetGuard`). Absent ⇒ neither (a standalone phase drive). */
|
|
@@ -93,7 +97,7 @@ export interface PrepareHandsReadFaceInput {
|
|
|
93
97
|
* object (R-4: `deps.onError?.()` keeps its receiver; a new deps read must widen this Pick).
|
|
94
98
|
* `readDenyBuiltinTiers`/`readDenyBuiltinExclude` are SNAPSHOTTED before the first await that
|
|
95
99
|
* precedes the toolkit compile (the caller retains these arrays by reference). */
|
|
96
|
-
deps: Pick<RunnerDeps, "onError" | "onNotice" | "tracer" | "readDenyPatterns" | "readDenyBuiltinTiers" | "readDenyBuiltinExclude" | "readFace" | "writeFace" | "permissionRuleStore" | "hands">;
|
|
100
|
+
deps: Pick<RunnerDeps, "onError" | "onNotice" | "tracer" | "readDenyPatterns" | "readDenyBuiltinTiers" | "readDenyBuiltinExclude" | "agentConfigDir" | "readFace" | "writeFace" | "permissionRuleStore" | "hands">;
|
|
97
101
|
/** borrowed-readonly — the trusted spawn-side seats the band forwards: the finished-command
|
|
98
102
|
* notification lane, the mid-flight detach hub, and the Runner's per-session
|
|
99
103
|
* read-state seats (absent on a standalone prepare ⇒ the transcript-replay rung covers alone). */
|
|
@@ -2,9 +2,9 @@ import { emitTrace } from "../trace.js";
|
|
|
2
2
|
import { StubExecutionEnv } from "../stub-env.js";
|
|
3
3
|
import { hasBackgroundShell } from "../background-shell.js";
|
|
4
4
|
import { defaultTaskRegistry } from "../task-registry.js";
|
|
5
|
-
import { bashReversibilityProbe, activeReadDenyBuiltinTiers, readDenyBuiltinConfigOf, compileReadDeny, compileWriteDeny,
|
|
5
|
+
import { bashReversibilityProbe, activeReadDenyBuiltinTiers, readDenyBuiltinConfigOf, compileReadDeny, compileWriteDeny, rowsWhereTheyLead, readDenySeat, createHandsToolkit, deploymentFaceClampNotice, pdfModelCapabilitiesOf, inheritSessionReadFileState, backgroundOutputReadExemption, resolveContainmentFace, seedReadFileStateFromContext, seedReadFileStateFromTranscript, } from "../../tools/fs/index.js";
|
|
6
6
|
import { resolveKey, sha256 } from "../../tools/fs/safety.js";
|
|
7
|
-
import { placeReadDenyAtHome
|
|
7
|
+
import { placeReadDenyAtHome } 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
10
|
import { composeReadChecks, createFileReadStation } from "../../tools/fs/file-read-check.js";
|
|
@@ -220,8 +220,8 @@ export async function prepareHandsMount(input) {
|
|
|
220
220
|
const seedReadFaceSection = resume?.seed.readFace;
|
|
221
221
|
const readDenyAdditions = [...(deps.readDenyPatterns ?? []), ...(spec.readDenyPatterns ?? []), ...(seedReadFaceSection?.denyEntries ?? [])];
|
|
222
222
|
const readDenyBuiltinCfg = readDenyBuiltinConfigOf({ tiers: deps.readDenyBuiltinTiers, exclude: deps.readDenyBuiltinExclude });
|
|
223
|
-
const
|
|
224
|
-
const readDenyPlacement = await placeReadDenyAtHome(executionEnv, (homes, linked) => compileReadDeny(readDenyAdditions, "readDenyPatterns", readDenyBuiltinCfg,
|
|
223
|
+
const denyRuleRows = rowsWhereTheyLead(input.denyRuleRows, await input.headLeads.resolve(input.denyRuleRows));
|
|
224
|
+
const readDenyPlacement = await placeReadDenyAtHome(executionEnv, (homes, linked) => compileReadDeny(readDenyAdditions, "readDenyPatterns", readDenyBuiltinCfg, denyRuleRows, homes, linked), undefined, undefined, deps.agentConfigDir);
|
|
225
225
|
readDenyMatcher = readDenyPlacement.matcher;
|
|
226
226
|
readDenyBuiltinTiersActive = activeReadDenyBuiltinTiers(readDenyBuiltinCfg);
|
|
227
227
|
readDenyAdditionsNormalized = compileReadDeny(readDenyAdditions, "readDenyPatterns", { tiers: [] }).entries;
|
|
@@ -291,7 +291,7 @@ export async function prepareHandsMount(input) {
|
|
|
291
291
|
}
|
|
292
292
|
: undefined;
|
|
293
293
|
const denySeat = readDenySeat(readDenyMatcher);
|
|
294
|
-
const writeDeny = compileWriteDeny(
|
|
294
|
+
const writeDeny = compileWriteDeny(denyRuleRows);
|
|
295
295
|
const writeDenySeat = writeDeny.ruleRows.length > 0 ? { writeDenyMatch: (p) => writeDeny.matchPath(p)?.pattern ?? null } : {};
|
|
296
296
|
const shellReadBoundary = () => ({
|
|
297
297
|
roots: [rootCanonical, ...additionalRootsCanonical, ...additionalReadRootsCanonical],
|
|
@@ -334,7 +334,7 @@ export async function prepareHandsMount(input) {
|
|
|
334
334
|
...(additionalRootsCanonical.length > 0 ? { additionalRoots: additionalRootsCanonical } : {}),
|
|
335
335
|
additionalReadRoots: additionalReadRootsCanonical,
|
|
336
336
|
...(readDenyAdditions.length > 0 ? { readDenyPatterns: readDenyAdditions } : {}),
|
|
337
|
-
...(
|
|
337
|
+
...(denyRuleRows.length > 0 ? { pathDenyRules: denyRuleRows } : {}),
|
|
338
338
|
readDenyHomes: readDenyPlacement.homes,
|
|
339
339
|
readDenyHomeLinks: readDenyPlacement.linked,
|
|
340
340
|
...(readDenyBuiltinCfg.tiers !== undefined ? { readDenyBuiltinTiers: readDenyBuiltinCfg.tiers } : {}),
|
|
@@ -43,7 +43,8 @@ import type { OwnOrgAdmissionVerdict } from "../memory-admission.js";
|
|
|
43
43
|
import type { DelegationContentSafety } from "../memory-engine/delegation-provenance.js";
|
|
44
44
|
import { type SessionPermissionRules } from "../session-policy-store.js";
|
|
45
45
|
import type { SessionStore } from "../session.js";
|
|
46
|
-
import { type OnAsk } from "../tool-policy.js";
|
|
46
|
+
import { type OnAsk, type ToolPolicy } from "../tool-policy.js";
|
|
47
|
+
import type { PathRuleBases } from "../permission-rule-model.js";
|
|
47
48
|
import type { PermissionMode, RunnerDeps, RuntimeCaps, ShellGateDoctrine, TaskEvent, TaskSpec, ToolExecuteContext, ToolSpec } from "../types.js";
|
|
48
49
|
import { PermissionModeController } from "../mode-policy.js";
|
|
49
50
|
import { type SkillGrantTable } from "../skill-grant.js";
|
|
@@ -89,6 +90,12 @@ export interface PrepareInheritedGateInput {
|
|
|
89
90
|
ownedEnv: ExecutionEnv | undefined;
|
|
90
91
|
/** borrowed-readonly — the frozen preflight; only `toolPolicy` (the RESOLVED caller policy the children's chain carries). */
|
|
91
92
|
lockedPreflight: Pick<LockedPreflight, "toolPolicy">;
|
|
93
|
+
/** borrowed-readonly — the chain's top leg's anchors as this leg reads them (handed on to children), and how this leg
|
|
94
|
+
* installs the policies its chain hands it (`chainPolicyOnLeg`). */
|
|
95
|
+
pathRuleAnchors: {
|
|
96
|
+
readonly chainTop: PathRuleBases;
|
|
97
|
+
readonly onLeg: (policy: ToolPolicy, position: number) => ToolPolicy;
|
|
98
|
+
};
|
|
92
99
|
/** borrowed-readonly — the frozen tool-face snapshot (the ctx forwards exclude / defer / alwaysLoad / restoreGated; the
|
|
93
100
|
* full-shell predicate reads the exclusion of Bash). */
|
|
94
101
|
toolFaceSnapshot: ToolFaceSnapshot;
|
|
@@ -5,6 +5,7 @@ import { createPreToolUseConstraintPolicy } from "../hooks.js";
|
|
|
5
5
|
import { continuesLineage } from "../session-policy-store.js";
|
|
6
6
|
import { StubExecutionEnv } from "../stub-env.js";
|
|
7
7
|
import { askApproverIdentity, durableMandateSourceOf } from "../tool-policy.js";
|
|
8
|
+
import { chainAnchorRecords } from "./child-tree.js";
|
|
8
9
|
import { deliverEngineNotice } from "../types.js";
|
|
9
10
|
import { PermissionModeController, modeRequestAfterResume } from "../mode-policy.js";
|
|
10
11
|
import { SessionModeSeats } from "./session-mode-seats.js";
|
|
@@ -13,7 +14,7 @@ import { frozenSkillGrants } from "../skill-grant.js";
|
|
|
13
14
|
import { attachRebuiltDenialTrackers, createDenialLimitStop } from "./denial-limit-arms.js";
|
|
14
15
|
import { asThrownError } from "../thrown-value.js";
|
|
15
16
|
export function prepareInheritedGate(input) {
|
|
16
|
-
const { spec, deps, askSeat, internals, resume, sessions, sessionId, runId, hostTaskId, taskRootFinal, executionEnv, ownedEnv, lockedPreflight, toolFaceSnapshot, mountsCallerTool, promptProfile, resolvedInteractionPosture, permissionModeSeat, handsEnabled, reportUsage, harnessRef, skillGrants, forwardEvent, subagentRetain, worktreeIsolation, requestReview, requestStopAfterTurn, enterPlanMode, reminderMark, reminderDisclosureCounts, fileHistoryEnabled, historyScope, historyRoot, historyFs, abortRun } = input;
|
|
17
|
+
const { spec, deps, askSeat, internals, resume, sessions, sessionId, runId, hostTaskId, taskRootFinal, executionEnv, ownedEnv, lockedPreflight, pathRuleAnchors, toolFaceSnapshot, mountsCallerTool, promptProfile, resolvedInteractionPosture, permissionModeSeat, handsEnabled, reportUsage, harnessRef, skillGrants, forwardEvent, subagentRetain, worktreeIsolation, requestReview, requestStopAfterTurn, enterPlanMode, reminderMark, reminderDisclosureCounts, fileHistoryEnabled, historyScope, historyRoot, historyFs, abortRun } = input;
|
|
17
18
|
const liveInheritedGate = internals?.inheritedGate;
|
|
18
19
|
const seedInheritedGate = resume?.seed.inheritedGate;
|
|
19
20
|
const inheritedAncestorRules = (() => {
|
|
@@ -35,7 +36,11 @@ export function prepareInheritedGate(input) {
|
|
|
35
36
|
})();
|
|
36
37
|
const modeSeed = recordedModeSeed(seedInheritedGate);
|
|
37
38
|
const inheritedShellGate = liveInheritedGate?.shellGate ?? modeSeed.shellGate;
|
|
38
|
-
const
|
|
39
|
+
const askView = askSeat.reach === "none" ? liveInheritedGate?.parentConstraints?.map((pc) => (typeof pc.onAsk === "function" ? ((({ onAsk: _person, ...rest }) => rest)(pc)) : pc)) : liveInheritedGate?.parentConstraints;
|
|
40
|
+
const chainView = askView?.map((pc, position) => {
|
|
41
|
+
const policy = pathRuleAnchors.onLeg(pc.policy, position);
|
|
42
|
+
return policy === pc.policy ? pc : { ...pc, policy };
|
|
43
|
+
});
|
|
39
44
|
const inheritedParentConstraints = attachRebuiltDenialTrackers(chainView, deps.autoMode?.denialLimit);
|
|
40
45
|
const parentSeat = liveInheritedGate?.permissionModeCeiling;
|
|
41
46
|
const ceiling = parentSeat ?? liveInheritedGate?.permissionMode;
|
|
@@ -186,6 +191,7 @@ export function prepareInheritedGate(input) {
|
|
|
186
191
|
permissionMode: permissionMode.current,
|
|
187
192
|
permissionModeCeiling: legModeCeiling,
|
|
188
193
|
...(parentConstraints.length > 0 ? { parentConstraints } : {}),
|
|
194
|
+
...chainAnchorRecords(pathRuleAnchors.chainTop, parentConstraints),
|
|
189
195
|
admittedOrgScopes: memoryAdmittedOrgScopesRef.current,
|
|
190
196
|
...(orgGovernedProvenance ? { orgAdmissionGoverned: true } : {}),
|
|
191
197
|
};
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
* closures, so the orchestrator grows no branch.
|
|
21
21
|
*/
|
|
22
22
|
import type { AgentHarness, AgentMessage, AgentTool, ExecutionEnv } from "../../internal/harness.js";
|
|
23
|
+
import type { CallTargetStamp } from "../effective-path-target.js";
|
|
23
24
|
import { type OnQuestion } from "../ask-question.js";
|
|
24
25
|
import { type CheckpointStore } from "../checkpoint-store.js";
|
|
25
26
|
import type { GrantBindingOf } from "../skill-grant-binding.js";
|
|
@@ -96,6 +97,8 @@ export interface PrepareParkAskInput {
|
|
|
96
97
|
handsCwdRef: CwdRef | undefined;
|
|
97
98
|
/** borrowed-readonly — the root the live gate stamps beside the cwd ({@link import("./prepare-ask-lane.js").PrepareAskLaneInput.handsRoot}). */
|
|
98
99
|
handsRoot: string | undefined;
|
|
100
|
+
/** borrowed-readonly — the live gate's target-reading stamp ({@link import("./prepare-ask-lane.js").PrepareAskLaneInput.stampCallTarget}). */
|
|
101
|
+
stampCallTarget: CallTargetStamp | undefined;
|
|
99
102
|
/** borrowed-readonly — the SAME seat the live gate stamps from ({@link import("./prepare-ask-lane.js").PrepareAskLaneInput.advertisedWritableDirs}): a parked call's projection re-adjudication asks one question about one call, so it must ask it against the same admitted list. Read at park time, not written here. */
|
|
100
103
|
advertisedWritableDirs: Pick<AdvertisedWritableDirs, "roots"> | undefined;
|
|
101
104
|
/** borrowed-readonly — the run's offload store, or undefined (a volatile store holding results refuses the park). */
|
|
@@ -27,7 +27,7 @@ const wordlessDemand = (toolName) => {
|
|
|
27
27
|
throw new Error(`a real-approval demand for "${toolName}" reached the park with no origin word — the origin stamp precedes every park`);
|
|
28
28
|
};
|
|
29
29
|
export function prepareParkAsk(input) {
|
|
30
|
-
const { askLane, saga, spec, deps, sessionId, checkpointStore, toolRosterDeltas, parkLaneArmed, contentAskRoutable, liveQuestionFace, mountedQuestionTool, contentAskBindings, lateStrandedAnswers, discloseStrandedAnswers, onAsk, runtimeCaps, inheritedUnavailableAsks, basePolicyForResumeEdit, budgetSnapshot, handsCwdRef, handsRoot, advertisedWritableDirs, offloadStore, ownedEnv, incompleteSuspendAdapter, session, sessions, suspendChainBase, maxSuspends, remoteEnvFailures, shellGatedTools, irreversibilityTier, toolMarkSources, effectiveShellGate, durableApproval, priorLedger, liveSpendRef, resourceTotal, faceCheckpointState, f012CheckpointState, orgAdmissionCheckpointState, ruleOffersOf, grantBindingOf, now, humanReviewRef, abortController, harness, pausedRef } = input;
|
|
30
|
+
const { askLane, saga, spec, deps, sessionId, checkpointStore, toolRosterDeltas, parkLaneArmed, contentAskRoutable, liveQuestionFace, mountedQuestionTool, contentAskBindings, lateStrandedAnswers, discloseStrandedAnswers, onAsk, runtimeCaps, inheritedUnavailableAsks, basePolicyForResumeEdit, budgetSnapshot, handsCwdRef, handsRoot, stampCallTarget, advertisedWritableDirs, offloadStore, ownedEnv, incompleteSuspendAdapter, session, sessions, suspendChainBase, maxSuspends, remoteEnvFailures, shellGatedTools, irreversibilityTier, toolMarkSources, effectiveShellGate, durableApproval, priorLedger, liveSpendRef, resourceTotal, faceCheckpointState, f012CheckpointState, orgAdmissionCheckpointState, ruleOffersOf, grantBindingOf, now, humanReviewRef, abortController, harness, pausedRef } = input;
|
|
31
31
|
if (askLane === undefined || saga === undefined)
|
|
32
32
|
return { parkAsk: undefined };
|
|
33
33
|
const { composedCallSignal, approvalPreviewOf } = askLane;
|
|
@@ -159,7 +159,7 @@ export function prepareParkAsk(input) {
|
|
|
159
159
|
if (!presented.ok) {
|
|
160
160
|
throw new ParkRefusal(`the stored form of "${req.toolName}"'s arguments could not be presented for re-adjudication (${describeThrown(presented.cause)})`, { cause: presented.cause });
|
|
161
161
|
}
|
|
162
|
-
const
|
|
162
|
+
const reprojectedReq = () => ({
|
|
163
163
|
toolName: req.toolName,
|
|
164
164
|
args: presented.value,
|
|
165
165
|
toolCallId: req.toolCallId,
|
|
@@ -167,7 +167,9 @@ export function prepareParkAsk(input) {
|
|
|
167
167
|
...callFaceSeat(req.face),
|
|
168
168
|
...(handsCwdRef !== undefined ? { cwd: handsCwdRef.current } : {}), ...(handsRoot !== undefined ? { root: handsRoot } : {}),
|
|
169
169
|
...advertisedWritableRootsSeat(advertisedWritableDirs),
|
|
170
|
-
}
|
|
170
|
+
});
|
|
171
|
+
const withReading = stampCallTarget?.(reprojectedReq(), cutSignal);
|
|
172
|
+
const reprojectedRaced = await raceSettlementAgainstSignal(withReading === undefined ? Promise.resolve(basePolicyForResumeEdit.check(reprojectedReq(), cutSignal)) : withReading.then((r) => basePolicyForResumeEdit.check(r, cutSignal)), cutSignal);
|
|
171
173
|
if (reprojectedRaced.tag === "aborted")
|
|
172
174
|
return undefined;
|
|
173
175
|
if (reprojectedRaced.tag === "threw")
|
|
@@ -27,6 +27,8 @@ import { type Hooks } from "../hooks.js";
|
|
|
27
27
|
import type { LockedPreflight } from "../locked-config.js";
|
|
28
28
|
import type { SessionPermissionRules } from "../session-policy-store.js";
|
|
29
29
|
import { type OnAsk, type ToolPolicy } from "../tool-policy.js";
|
|
30
|
+
import type { PathRuleBases } from "../permission-rule-model.js";
|
|
31
|
+
import type { HeadLeads } from "../path-rule-binding.js";
|
|
30
32
|
import type { RunnerDeps, RuntimeCaps, TaskSpec, ToolEffect } from "../types.js";
|
|
31
33
|
import type { CwdRef } from "../../tools/fs/fs-shared.js";
|
|
32
34
|
import type { ReadDenyMatcher } from "../../tools/fs/read-deny.js";
|
|
@@ -65,9 +67,17 @@ export interface PreparePolicyChainInput {
|
|
|
65
67
|
additionalRootsCanonical: readonly string[];
|
|
66
68
|
/** borrowed-readonly — the run's resolved env (path rules resolve in it; the sandbox declaration is read off it). */
|
|
67
69
|
executionEnv: ExecutionEnv;
|
|
68
|
-
/** borrowed-readonly — the frozen preflight: `toolPolicy` (the caller slot) and
|
|
69
|
-
* resume-edit override). */
|
|
70
|
+
/** borrowed-readonly — the frozen preflight as installed on this leg: `toolPolicy` (the caller slot) and
|
|
71
|
+
* `basePolicyForResumeEdit` (the lock-aware resume-edit override), their path rules anchored where this leg installed them. */
|
|
70
72
|
lockedPreflight: Pick<LockedPreflight, "toolPolicy" | "basePolicyForResumeEdit">;
|
|
73
|
+
/** borrowed-readonly — the path rules' anchors on this leg: `leg` (this leg's root and environment home — where this
|
|
74
|
+
* leg's own lists are installed) and `chainTop` (the top leg's: the person's global / session rows and every `~/`). */
|
|
75
|
+
pathRuleAnchors: {
|
|
76
|
+
readonly leg: PathRuleBases;
|
|
77
|
+
readonly chainTop: PathRuleBases;
|
|
78
|
+
};
|
|
79
|
+
/** borrowed-readonly — the leg's one resolution of where rule heads lead (the rows' twins). */
|
|
80
|
+
headLeads: HeadLeads;
|
|
71
81
|
/** borrowed-readonly — the run's ONE tool roster, read only here (the name audit's universe: names + aliases). */
|
|
72
82
|
tools: ReadonlyArray<AgentTool>;
|
|
73
83
|
/** borrowed-readonly — the harness list (placeholders keep their deferred names), the audit universe's second half. */
|
|
@@ -96,6 +106,8 @@ export interface PreparePolicyChainInput {
|
|
|
96
106
|
readDenyMatcher: ReadDenyMatcher | undefined;
|
|
97
107
|
/** borrowed-readonly — the run's one notice for a person's rule store that could not be read (`ruleStoreUnreadableNotice`). */
|
|
98
108
|
announceRuleStoreSkipped: (at: "prepare" | "call") => void;
|
|
109
|
+
/** borrowed-readonly — the run's one notice for rows the person's store served that were left out (`ruleRowsDroppedNotice`). */
|
|
110
|
+
announceRuleRowsDropped: (rows: import("../permission-rule-provider.js").EffectivePermissionRules["droppedRows"]) => void;
|
|
99
111
|
/** borrowed-readonly — whether the question tool mounted (the lanes' question-tool exclusion). */
|
|
100
112
|
questionToolMounted: boolean;
|
|
101
113
|
/** borrowed-readonly — the resolved per-principal entitlements; only `forceDurableGate` (the mandate-park facility and
|