@sema-agent/core 7.44.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 +418 -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 +65 -38
- package/dist/agents/tool-filter.d.ts +82 -5
- package/dist/agents/tool-filter.js +134 -4
- package/dist/core/agent-config-dir.d.ts +20 -0
- package/dist/core/agent-config-dir.js +49 -0
- package/dist/core/agent-definition.d.ts +2 -2
- package/dist/core/allow-entry.d.ts +30 -4
- package/dist/core/allow-entry.js +32 -5
- package/dist/core/ask-origin.d.ts +23 -4
- package/dist/core/ask-origin.js +11 -5
- package/dist/core/checkpoint-store.d.ts +41 -4
- package/dist/core/decision-receipt.js +1 -1
- package/dist/core/effective-path-target.d.ts +143 -10
- package/dist/core/effective-path-target.js +113 -9
- package/dist/core/engine-notice.d.ts +25 -3
- package/dist/core/engine-notice.js +12 -6
- package/dist/core/gate-fold.js +3 -0
- package/dist/core/gate-lanes.js +11 -3
- package/dist/core/gate-outcome.d.ts +5 -0
- package/dist/core/gate-outcome.js +2 -0
- package/dist/core/governance-codes.d.ts +3 -1
- package/dist/core/governance-codes.js +3 -0
- package/dist/core/hooks.d.ts +5 -0
- package/dist/core/memory-engine/content-origin.d.ts +0 -34
- package/dist/core/memory-engine/content-origin.js +9 -4
- package/dist/core/memory-engine/layout.d.ts +5 -1
- package/dist/core/memory-engine/layout.js +1 -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 -36
- package/dist/core/permission-rules.js +50 -40
- package/dist/core/real-approval.d.ts +9 -6
- package/dist/core/real-approval.js +5 -5
- package/dist/core/runner/child-tree.d.ts +110 -0
- package/dist/core/runner/child-tree.js +107 -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 +54 -2
- 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 +6 -0
- package/dist/core/runner/prepare-ask-lane.js +4 -2
- package/dist/core/runner/prepare-config-doors.js +3 -1
- package/dist/core/runner/prepare-execution-env.d.ts +5 -2
- package/dist/core/runner/prepare-execution-env.js +11 -3
- package/dist/core/runner/prepare-gate-stations.d.ts +6 -0
- package/dist/core/runner/prepare-gate-stations.js +3 -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-listings.d.ts +4 -4
- package/dist/core/runner/prepare-listings.js +2 -22
- package/dist/core/runner/prepare-park-ask.d.ts +5 -0
- package/dist/core/runner/prepare-park-ask.js +6 -4
- package/dist/core/runner/prepare-policy-chain.d.ts +16 -2
- package/dist/core/runner/prepare-policy-chain.js +14 -5
- 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 +41 -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/resume-policy-outcome.js +2 -1
- package/dist/core/runner/run-leg.js +5 -1
- package/dist/core/runner/run-notification-lane.js +2 -2
- package/dist/core/runner/runtask.d.ts +2 -0
- package/dist/core/runner/runtask.js +4 -2
- package/dist/core/runner/stream-steer-verb.js +2 -2
- package/dist/core/runner/sub-agent-deny-lists.d.ts +32 -0
- package/dist/core/runner/sub-agent-deny-lists.js +121 -0
- package/dist/core/runner/synthetic-tools.d.ts +1 -1
- package/dist/core/runner/synthetic-tools.js +3 -2
- 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/session-policy-store.d.ts +6 -2
- package/dist/core/session-policy-store.js +2 -1
- 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/skill-grant.d.ts +10 -2
- package/dist/core/skill-grant.js +20 -4
- package/dist/core/synthetic-read-verdict.d.ts +3 -0
- package/dist/core/synthetic-read-verdict.js +3 -1
- package/dist/core/task-notification.d.ts +12 -1
- package/dist/core/task-notification.js +13 -1
- package/dist/core/task-spec.d.ts +5 -3
- 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 +45 -12
- package/dist/core/tool-policy.js +18 -20
- package/dist/core/tool-spec.d.ts +23 -4
- package/dist/core/types.d.ts +1 -1
- package/dist/core/types.js +1 -1
- package/dist/core/write-protect.d.ts +4 -0
- package/dist/core/write-protect.js +30 -9
- package/dist/index.d.ts +10 -6
- package/dist/index.js +9 -5
- package/dist/orchestration/run-spec.js +8 -7
- package/dist/orchestration/workflow.js +3 -2
- 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 +83 -0
- package/dist/tools/fs/removal-targets.js +236 -0
- 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 -37
- 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 +49 -3
- package/wire-changes/core@7.45.0.json +1084 -0
- package/wire-changes/core@7.46.0.json +1522 -0
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import type { RunnerDeps } from "../types.js";
|
|
2
|
+
import type { ExecutionEnv } from "../../internal/harness.js";
|
|
3
|
+
import type { PathRuleBases } from "../permission-rule-model.js";
|
|
4
|
+
import type { ChildPlacement, InheritedGate, RunInternals } from "./contracts.js";
|
|
5
|
+
import type { ToolPolicy } from "../tool-policy.js";
|
|
6
|
+
import { CheckpointError } from "../checkpoint-store.js";
|
|
7
|
+
/**
|
|
8
|
+
* Whether this deployment can place a child agent in a tree OTHER than the caller's — the one fact behind
|
|
9
|
+
* every request of that shape: an explicit `cwd`, a workflow agent's `isolation: "worktree"`, and the
|
|
10
|
+
* delegation tool's per-call or per-definition `isolation: "worktree"`.
|
|
11
|
+
*
|
|
12
|
+
* Only an `executionEnvFactory` can: it builds one execution env per task, so a child can be rooted
|
|
13
|
+
* wherever the request says. A static `executionEnv` is ONE shared env with ONE cwd — every child that
|
|
14
|
+
* runs on it runs in the caller's root, whatever tree it was asked to work in. A request the deployment
|
|
15
|
+
* cannot honor is refused before the child starts (and, for a worktree, before one is minted); each site
|
|
16
|
+
* keeps its own code and sentence for the refusal, but none decides the condition itself.
|
|
17
|
+
*/
|
|
18
|
+
export declare function deploymentCanRerootChild(deps: Pick<RunnerDeps, "executionEnvFactory">): boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Where a child the caller asked to place at `asked` actually landed (`childRoot`, the root its environment gave it) — the
|
|
21
|
+
* ONE judgment every reader of that fact takes
|
|
22
|
+
* (the delegation tool's worktree request, and the anchors of the rules the child inherits):
|
|
23
|
+
* · `honored` — the environment's root is where the child was asked to be (canonical on both sides);
|
|
24
|
+
* · `caller_tree` — it is not, and it lies inside the caller's own tree (`callerRoot`): the same filesystem, the wrong
|
|
25
|
+
* directory — a worktree request then fails (the child would write the caller's checkout);
|
|
26
|
+
* · `elsewhere` — neither: another filesystem namespace (a sandbox the factory minted). The child runs there, it is not
|
|
27
|
+
* in the worktree it was offered, and it takes the caller's path rules again at its own anchors.
|
|
28
|
+
* A path the environment cannot canonicalize is compared as spelled. Residual: two namespaces that hold the same path
|
|
29
|
+
* string, with a factory claiming to honor it, read `honored`.
|
|
30
|
+
*/
|
|
31
|
+
export declare function childPlacementOf(env: Pick<ExecutionEnv, "canonicalPath">, childRoot: string, asked: string | undefined, callerRoot: string): Promise<ChildPlacement>;
|
|
32
|
+
/** A delegated leg's placement: the place asked for is the hint (a minted worktree, an explicit cwd, the caller's own cwd)
|
|
33
|
+
* — or, for a worktree the factory mints itself (`isolation`), that worktree on the caller's host (a remote environment's
|
|
34
|
+
* is another namespace). A leg no delegation caller placed (no `callerRoot`) has no placement and reads nothing. */
|
|
35
|
+
export declare function childPlacementOnLeg(env: ExecutionEnv, internals: Pick<RunInternals, "isolation" | "parentCwd"> & {
|
|
36
|
+
readonly callerRoot: string;
|
|
37
|
+
}, root: string): Promise<ChildPlacement>;
|
|
38
|
+
/** The refusal of a worktree the caller minted for a child its environment placed in the caller's own tree. */
|
|
39
|
+
export declare function worktreeNotHonored(cwd: string): Error & {
|
|
40
|
+
code: "isolation.unavailable";
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* The chain's top leg's anchors as THIS leg reads them (the person's global / session path rows and every `~/` row anchor
|
|
44
|
+
* there): the chain's own (`live` as recorded, else the checkpoint's `seed` rebased — {@link recordedAnchors}), unless this
|
|
45
|
+
* leg is a child placed `elsewhere` or no chain names one — then this leg is the top. A recorded root that is not an
|
|
46
|
+
* absolute path anchors nothing: the rows it would anchor read `unreadable`.
|
|
47
|
+
*/
|
|
48
|
+
export declare function chainTopAnchors(cfg: {
|
|
49
|
+
leg: PathRuleBases;
|
|
50
|
+
live: InheritedGate["pathRuleAnchors"];
|
|
51
|
+
seed: InheritedGate["pathRuleAnchors"];
|
|
52
|
+
placement: ChildPlacement | undefined;
|
|
53
|
+
rebase: (p: string) => string;
|
|
54
|
+
}): PathRuleBases;
|
|
55
|
+
/** The installation records a leg reads: the live chain's (as recorded), else the checkpoint's (through the workspace rebase). */
|
|
56
|
+
export interface ChainInstallations {
|
|
57
|
+
readonly live: InheritedGate["pathRuleInstallations"];
|
|
58
|
+
readonly seed: InheritedGate["pathRuleInstallations"];
|
|
59
|
+
}
|
|
60
|
+
/** Why no usable record says where a chain entry was installed: no record (a chain written before the record existed), a
|
|
61
|
+
* record that does not cover the chain position for position, or a member that names no absolute root. */
|
|
62
|
+
export type ChainInstallationGap = "absent" | "misaligned" | "invalid";
|
|
63
|
+
/**
|
|
64
|
+
* The chain entries a leg could not install where they were installed: each UNINSTALLED entry (a host re-supplying a
|
|
65
|
+
* chain) whose path rules depend on the installing leg, with no usable record of that leg. Such a chain is refused —
|
|
66
|
+
* installing those entries anywhere else (the chain's top leg, this leg) would move what they protect. Entries whose rules
|
|
67
|
+
* do not depend on it, and installed entries, never need a record.
|
|
68
|
+
*/
|
|
69
|
+
export declare function chainInstallationGaps(chain: readonly ToolPolicy[], installations: ChainInstallations, rebase: (p: string) => string): Array<{
|
|
70
|
+
readonly position: number;
|
|
71
|
+
readonly gap: ChainInstallationGap;
|
|
72
|
+
}>;
|
|
73
|
+
/** The refusal of a chain with {@link chainInstallationGaps} (`resume.parent_constraint_mismatch`, `detail.reason` naming
|
|
74
|
+
* the first gap's form); `pending` says the checkpoint is still pending (the refusal came before the claim). */
|
|
75
|
+
export declare function chainInstallationRefusal(gaps: ReadonlyArray<{
|
|
76
|
+
readonly position: number;
|
|
77
|
+
readonly gap: ChainInstallationGap;
|
|
78
|
+
}>, pending: boolean): CheckpointError;
|
|
79
|
+
/**
|
|
80
|
+
* How a leg holds a caller policy — its own (`TaskSpec.toolPolicy` / `RunnerDeps.toolPolicy`) or one its delegation chain
|
|
81
|
+
* hands it: a policy is installed where it ENTERS the chain, and a child holds its ancestor's installation (one object,
|
|
82
|
+
* the same anchors) — its own caller slot included when that slot names the policy an ancestor installed. Installed again:
|
|
83
|
+
* on a child placed `elsewhere` (another filesystem namespace — the ancestor's anchors name another namespace's files), at
|
|
84
|
+
* this leg's anchors; a chain entry handed uninstalled (a host re-supplying a chain), where its installing leg recorded it
|
|
85
|
+
* (`InheritedGate.pathRuleInstallations`, live or from the checkpoint). An uninstalled entry whose rules depend on that
|
|
86
|
+
* leg and whose record is not usable refuses the leg ({@link chainInstallationRefusal}); one whose rules do not, reads
|
|
87
|
+
* the same wherever it is installed (the chain's top leg).
|
|
88
|
+
*/
|
|
89
|
+
export declare function chainPolicyOnLeg(cfg: {
|
|
90
|
+
leg: PathRuleBases;
|
|
91
|
+
chainTop: PathRuleBases;
|
|
92
|
+
placement: ChildPlacement | undefined;
|
|
93
|
+
chain: readonly ToolPolicy[];
|
|
94
|
+
installations: ChainInstallations;
|
|
95
|
+
rebase: (p: string) => string;
|
|
96
|
+
}): {
|
|
97
|
+
own: (policy: ToolPolicy | undefined) => ToolPolicy | undefined;
|
|
98
|
+
chained: (policy: ToolPolicy, position: number) => ToolPolicy;
|
|
99
|
+
};
|
|
100
|
+
/** The installation record of a chain entry (its root and home), `null` for an entry with no path rules installed. */
|
|
101
|
+
export declare function installationRecordOf(policy: ToolPolicy): {
|
|
102
|
+
readonly root: string;
|
|
103
|
+
readonly home?: string;
|
|
104
|
+
} | null;
|
|
105
|
+
/** What a leg hands its children about anchors (data half): the chain's top leg's anchors — the person's global / session
|
|
106
|
+
* rows and every `~/` row anchor there — and where each entry handed down was installed, position for position (a chain
|
|
107
|
+
* re-supplied uninstalled is installed there again; absent when no entry is an installation). */
|
|
108
|
+
export declare function chainAnchorRecords(chainTop: PathRuleBases, chain: readonly {
|
|
109
|
+
readonly policy: ToolPolicy;
|
|
110
|
+
}[]): Pick<InheritedGate, "pathRuleAnchors" | "pathRuleInstallations">;
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { isRemoteExecutionEnv } from "../remote-env.js";
|
|
2
|
+
import { isAbsolutePathForm } from "../../tools/fs/safety.js";
|
|
3
|
+
import { installPolicyAnchors, installationAnchorsOf, installedFrom } from "../path-rule-binding.js";
|
|
4
|
+
import { CheckpointError } from "../checkpoint-store.js";
|
|
5
|
+
export function deploymentCanRerootChild(deps) {
|
|
6
|
+
return deps.executionEnvFactory !== undefined;
|
|
7
|
+
}
|
|
8
|
+
export async function childPlacementOf(env, childRoot, asked, callerRoot) {
|
|
9
|
+
const canon = async (p) => {
|
|
10
|
+
try {
|
|
11
|
+
const r = await env.canonicalPath(p);
|
|
12
|
+
return r.ok ? r.value : p;
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return p;
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
const root = await canon(childRoot);
|
|
19
|
+
if (asked !== undefined && (await canon(asked)) === root)
|
|
20
|
+
return "honored";
|
|
21
|
+
const tree = await canon(callerRoot);
|
|
22
|
+
const sep = tree.includes("\\") && !tree.includes("/") ? "\\" : "/";
|
|
23
|
+
return root === tree || root.startsWith(tree.endsWith(sep) ? tree : tree + sep) ? "caller_tree" : "elsewhere";
|
|
24
|
+
}
|
|
25
|
+
export function childPlacementOnLeg(env, internals, root) {
|
|
26
|
+
const asked = internals.isolation === "worktree" ? (isRemoteExecutionEnv(env) ? undefined : root) : internals.parentCwd;
|
|
27
|
+
return childPlacementOf(env, root, asked, internals.callerRoot);
|
|
28
|
+
}
|
|
29
|
+
export function worktreeNotHonored(cwd) {
|
|
30
|
+
return Object.assign(new Error(`isolation "worktree" was requested but cannot be honored: the execution environment factory placed the sub-agent at "${cwd}", inside the caller's own tree, instead of the worktree it was offered. The sub-agent was not started (fail-closed — no silent fallback to the shared working tree).`), { code: "isolation.unavailable" });
|
|
31
|
+
}
|
|
32
|
+
const AS_RECORDED = (p) => p;
|
|
33
|
+
function recordedAnchors(r, rebase) {
|
|
34
|
+
if (r === null || r === undefined)
|
|
35
|
+
return undefined;
|
|
36
|
+
const read = (v) => {
|
|
37
|
+
if (typeof v !== "string")
|
|
38
|
+
return undefined;
|
|
39
|
+
const p = rebase(v);
|
|
40
|
+
return isAbsolutePathForm(p) ? p : undefined;
|
|
41
|
+
};
|
|
42
|
+
const [root, home] = [read(r.root), read(r.home)];
|
|
43
|
+
return { ...(root !== undefined ? { root } : {}), ...(home !== undefined ? { home } : {}) };
|
|
44
|
+
}
|
|
45
|
+
export function chainTopAnchors(cfg) {
|
|
46
|
+
if (cfg.placement === "elsewhere")
|
|
47
|
+
return cfg.leg;
|
|
48
|
+
return (cfg.live !== undefined ? recordedAnchors(cfg.live, AS_RECORDED) : recordedAnchors(cfg.seed, cfg.rebase)) ?? cfg.leg;
|
|
49
|
+
}
|
|
50
|
+
const GAP_REASON = { absent: "installation_record_absent", misaligned: "installation_record_misaligned", invalid: "installation_record_invalid" };
|
|
51
|
+
function dependsOnInstallation(policy) {
|
|
52
|
+
if (installedFrom(policy) !== undefined)
|
|
53
|
+
return false;
|
|
54
|
+
return typeof policy.pathRules === "function" && policy.pathRules({}).some((row) => row.unanchored !== undefined);
|
|
55
|
+
}
|
|
56
|
+
function recordedInstallation(installations, chainLength, position, rebase) {
|
|
57
|
+
const [record, read] = installations.live !== undefined ? [installations.live, AS_RECORDED] : [installations.seed, rebase];
|
|
58
|
+
if (record === undefined)
|
|
59
|
+
return "absent";
|
|
60
|
+
if (record.length !== chainLength)
|
|
61
|
+
return "misaligned";
|
|
62
|
+
const a = recordedAnchors(record[position], read);
|
|
63
|
+
return a?.root === undefined ? "invalid" : a;
|
|
64
|
+
}
|
|
65
|
+
export function chainInstallationGaps(chain, installations, rebase) {
|
|
66
|
+
return chain.flatMap((policy, position) => {
|
|
67
|
+
if (!dependsOnInstallation(policy))
|
|
68
|
+
return [];
|
|
69
|
+
const r = recordedInstallation(installations, chain.length, position, rebase);
|
|
70
|
+
return typeof r === "string" ? [{ position, gap: r }] : [];
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
export function chainInstallationRefusal(gaps, pending) {
|
|
74
|
+
const first = gaps[0];
|
|
75
|
+
const form = { absent: "absent (a chain written before installation records existed, or one built without them)", misaligned: "misaligned (it does not cover the chain entry for entry)", invalid: "invalid for that entry (it names no absolute root)" }[first.gap];
|
|
76
|
+
return new CheckpointError("resume.parent_constraint_mismatch", `the parent-constraint chain carries ${gaps.length} polic${gaps.length === 1 ? "y" : "ies"} (entry ${gaps.map((g) => g.position).join(", ")}) whose path rules resolve against the leg that installed ${gaps.length === 1 ? "it" : "them"}, and the installation record is ${form} — installing ${gaps.length === 1 ? "it" : "them"} anywhere else would move what ${gaps.length === 1 ? "it protects" : "they protect"}, so the chain is refused` +
|
|
77
|
+
(pending ? " before the claim (the checkpoint stays pending). A task parked before 7.46.0 under such a chain cannot be resumed by this version: let a worker of the version that parked it finish it, or let it expire and submit the task again." : "."), { reason: GAP_REASON[first.gap] });
|
|
78
|
+
}
|
|
79
|
+
export function chainPolicyOnLeg(cfg) {
|
|
80
|
+
if (cfg.placement !== "elsewhere") {
|
|
81
|
+
const gaps = chainInstallationGaps(cfg.chain, cfg.installations, cfg.rebase);
|
|
82
|
+
if (gaps.length > 0)
|
|
83
|
+
throw chainInstallationRefusal(gaps, false);
|
|
84
|
+
}
|
|
85
|
+
const recorded = (position) => {
|
|
86
|
+
const r = recordedInstallation(cfg.installations, cfg.chain.length, position, cfg.rebase);
|
|
87
|
+
return typeof r === "string" ? undefined : r;
|
|
88
|
+
};
|
|
89
|
+
const chained = (policy, position) => (cfg.placement === "elsewhere" ? installPolicyAnchors(policy, cfg.leg) : installedFrom(policy) !== undefined ? policy : installPolicyAnchors(policy, recorded(position) ?? cfg.chainTop)) ?? policy;
|
|
90
|
+
const own = (policy) => {
|
|
91
|
+
if (policy === undefined)
|
|
92
|
+
return undefined;
|
|
93
|
+
const position = cfg.placement === "elsewhere" ? -1 : cfg.chain.findIndex((c) => (installedFrom(c) ?? c) === (installedFrom(policy) ?? policy));
|
|
94
|
+
return position >= 0 ? chained(cfg.chain[position], position) : installPolicyAnchors(policy, cfg.leg);
|
|
95
|
+
};
|
|
96
|
+
return { own, chained };
|
|
97
|
+
}
|
|
98
|
+
export function installationRecordOf(policy) {
|
|
99
|
+
const a = installationAnchorsOf(policy);
|
|
100
|
+
return a?.root === undefined ? null : { root: a.root, ...(a.home !== undefined ? { home: a.home } : {}) };
|
|
101
|
+
}
|
|
102
|
+
export function chainAnchorRecords(chainTop, chain) {
|
|
103
|
+
return {
|
|
104
|
+
...(chainTop.root !== undefined ? { pathRuleAnchors: { root: chainTop.root, ...(chainTop.home !== undefined ? { home: chainTop.home } : {}) } } : {}),
|
|
105
|
+
...(chain.some((pc) => installationAnchorsOf(pc.policy)?.root !== undefined) ? { pathRuleInstallations: chain.map((pc) => installationRecordOf(pc.policy)) } : {}),
|
|
106
|
+
};
|
|
107
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { RunnerDeps } from "../types.js";
|
|
2
|
+
/** The retired-seat door ({@link assertNoRetiredRunnerDeps}), then the seats whose value is screened at construction and
|
|
3
|
+
* never again: the declared configuration root must be the absolute path of an existing directory — a root that names no
|
|
4
|
+
* directory guards nothing, so it is refused here rather than read as absent. */
|
|
5
|
+
export declare function assertRunnerDepsDoors(deps: RunnerDeps): void;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { checkedAgentConfigDir } from "../agent-config-dir.js";
|
|
2
|
+
import { assertNoRetiredRunnerDeps } from "./retired-runner-deps.js";
|
|
3
|
+
export function assertRunnerDepsDoors(deps) {
|
|
4
|
+
assertNoRetiredRunnerDeps(deps);
|
|
5
|
+
checkedAgentConfigDir(deps.agentConfigDir, "RunnerDeps.agentConfigDir");
|
|
6
|
+
}
|
|
@@ -31,7 +31,7 @@ import type { MaterializedA2a } from "../a2a.js";
|
|
|
31
31
|
import type { CompactionForkContext, CompactionPhaseDurations, MaybeCompactOptions, RapidRefillState } from "../auto-compaction.js";
|
|
32
32
|
import type { AutoModeArmingRecipe } from "../auto-mode-arming.js";
|
|
33
33
|
import type { AutoModeDecider, AutoModeDenialTracker, DenialLimitFallbackFace } from "../auto-mode.js";
|
|
34
|
-
import type { SettlementKind } from "../gate-outcome.js";
|
|
34
|
+
import type { HostDecider, SettlementKind } from "../gate-outcome.js";
|
|
35
35
|
import type { CacheBreakDetector, ToolFingerprintInput } from "../cache-break-detector.js";
|
|
36
36
|
import type { Checkpoint, CheckpointState, CheckpointStore, CheckpointToken, PendingSteerEntry, PlatformLimitReason, ReopenReason, ResourceLedger, ResourceLimitReason, ResumeOutcome, SerializedCheckpointState } from "../checkpoint-store.js";
|
|
37
37
|
import type { ExecutionOutcomeRecordWord } from "../checkpoint-execution-record.js";
|
|
@@ -391,6 +391,10 @@ export interface Prepared {
|
|
|
391
391
|
/** The per-task logical cwd ref when a real shell is mounted (else undefined). The Runner
|
|
392
392
|
* reads `cwdRef.current` after each tool to detect a `cd` move and emit `workspace_changed`. */
|
|
393
393
|
cwdRef?: CwdRef;
|
|
394
|
+
/** The root the hand tools resolve root-relative spellings against (present with hands; read-only hands included). */
|
|
395
|
+
handsRoot?: string;
|
|
396
|
+
/** The leg's target-reading stamp (present with hands): the resume belts stamp the executed bytes' request with it. */
|
|
397
|
+
stampCallTarget?: import("../effective-path-target.js").CallTargetStamp;
|
|
394
398
|
/** The run's TOOL-PROGRESS lane seat, late-bound for the same reason `harnessRef` is (the tool band is
|
|
395
399
|
* built during prepare; the lane needs `pushContent` and `ident`, which exist only after it returns).
|
|
396
400
|
* The driver binds `current` right after `pushContent` exists. Always present on a completed prepare —
|
|
@@ -442,7 +446,7 @@ export interface Prepared {
|
|
|
442
446
|
toolCallId: string;
|
|
443
447
|
toolName: string;
|
|
444
448
|
args: unknown;
|
|
445
|
-
decidedBy:
|
|
449
|
+
decidedBy: HostDecider;
|
|
446
450
|
}) => Promise<{
|
|
447
451
|
readonly refused: string;
|
|
448
452
|
} | {
|
|
@@ -1415,6 +1419,29 @@ export interface InheritedGate {
|
|
|
1415
1419
|
* had a governance surface (their opaque posture is untouched). Serializable; persists on checkpoints.
|
|
1416
1420
|
*/
|
|
1417
1421
|
orgAdmissionGoverned?: true;
|
|
1422
|
+
/**
|
|
1423
|
+
* The chain's TOP leg's root and environment home (data half, serializable; persists on the checkpoint's data half):
|
|
1424
|
+
* the anchors of the person's global and session path rows and of every `~/` row on this chain — a child judges them
|
|
1425
|
+
* where the chain was installed, not at its own root (a project row anchors at its own scope root). Emitted by every leg
|
|
1426
|
+
* that hands a chain down (a top leg emits its own). Absent on an older chain ⇒ the child is its own top. A child placed
|
|
1427
|
+
* in ANOTHER filesystem namespace (its environment did not put it where it was asked, outside the caller's tree) ignores
|
|
1428
|
+
* it and takes its own leg as the top.
|
|
1429
|
+
*/
|
|
1430
|
+
pathRuleAnchors?: {
|
|
1431
|
+
readonly root: string;
|
|
1432
|
+
readonly home?: string;
|
|
1433
|
+
};
|
|
1434
|
+
/**
|
|
1435
|
+
* Where each `parentConstraints` entry was INSTALLED (data half, serializable; persists on the checkpoint's data half):
|
|
1436
|
+
* position for position with `parentConstraints`, the root and home its path rules are anchored at (`null` for an entry
|
|
1437
|
+
* with no path rules). A chain re-supplied with uninstalled policies (a resume in another process, a host-built chain)
|
|
1438
|
+
* installs each where this records — never at another leg's anchors. Absent on an older chain: an uninstalled entry is
|
|
1439
|
+
* installed at the chain's top leg (`pathRuleAnchors`).
|
|
1440
|
+
*/
|
|
1441
|
+
pathRuleInstallations?: ReadonlyArray<{
|
|
1442
|
+
readonly root: string;
|
|
1443
|
+
readonly home?: string;
|
|
1444
|
+
} | null>;
|
|
1418
1445
|
/** Live ancestor caller-policy constraints (opaque half), ancestors first. `policy` is the ancestor's
|
|
1419
1446
|
* RESOLVED caller policy (`spec.toolPolicy ?? deps.toolPolicy` — the same slot the ancestor itself
|
|
1420
1447
|
* enforces, so a deps-level baseline is inherited too).
|
|
@@ -2135,6 +2162,20 @@ export interface RunInternals {
|
|
|
2135
2162
|
* callers (ctx-injection posture, same as `parentCwd`).
|
|
2136
2163
|
*/
|
|
2137
2164
|
requestedCwd?: string;
|
|
2165
|
+
/**
|
|
2166
|
+
* The SPAWNING run's own root, filled ONLY by core delegation callers beside `parentCwd` (ctx-injection posture): with
|
|
2167
|
+
* it the child's prepare judges where its environment actually placed it against where it was asked
|
|
2168
|
+
* (`childPlacementOf`, child-tree.ts: honored / inside the caller's tree / elsewhere) — the fact the
|
|
2169
|
+
* worktree request below and the anchors of the rules the child inherits read. Absent ⇒ no placement is judged (a top
|
|
2170
|
+
* leg, a caller that does not delegate through core).
|
|
2171
|
+
*/
|
|
2172
|
+
callerRoot?: string;
|
|
2173
|
+
/**
|
|
2174
|
+
* `parentCwd` is a worktree the caller minted for THIS child (the delegation tool's `isolation: "worktree"`): a child
|
|
2175
|
+
* its environment placed inside the caller's own tree instead is refused before it starts (`isolation.unavailable`).
|
|
2176
|
+
* Filled ONLY by the core delegation tool; never a {@link TaskSpec} field.
|
|
2177
|
+
*/
|
|
2178
|
+
worktreeOffered?: true;
|
|
2138
2179
|
/**
|
|
2139
2180
|
* Subagent steer verb (dogfood finding 2026-07-03, "中途插话"): the host run's opt-in
|
|
2140
2181
|
* SUBAGENT-STEER-HANDLE sink. When set, `createSubagentTool` runs each child via `runTaskStream`
|
|
@@ -2235,6 +2276,10 @@ export interface RunInternals {
|
|
|
2235
2276
|
* non-isolated run reports its plain root with `isolated: false` — the observation face stays complete, while
|
|
2236
2277
|
* the honest non-claim keeps a consumer from mistaking a SHARED tree for its own worktree.
|
|
2237
2278
|
*/
|
|
2279
|
+
/** Where a delegated child's environment placed it against where its caller asked (`childPlacementOf`, child-tree.ts):
|
|
2280
|
+
* `honored` — where it was asked; `caller_tree` — elsewhere inside the caller's own tree; `elsewhere` — another
|
|
2281
|
+
* filesystem namespace. */
|
|
2282
|
+
export type ChildPlacement = "honored" | "caller_tree" | "elsewhere";
|
|
2238
2283
|
export interface ResolvedWorkspace {
|
|
2239
2284
|
/** The task's effective working root — the same value every fence / LSP / prompt consumer uses. */
|
|
2240
2285
|
cwd: string;
|
|
@@ -2245,6 +2290,9 @@ export interface ResolvedWorkspace {
|
|
|
2245
2290
|
* or names an unrelated directory — on the host. `isolated && !remote` is the only combination under
|
|
2246
2291
|
* which `cwd` is a host path a consumer may diff / merge / remove. */
|
|
2247
2292
|
remote: boolean;
|
|
2293
|
+
/** Where a delegated child's environment placed it against where its caller asked; absent on a leg no delegation
|
|
2294
|
+
* caller placed. */
|
|
2295
|
+
placement?: ChildPlacement;
|
|
2248
2296
|
}
|
|
2249
2297
|
/**
|
|
2250
2298
|
* The resolver's answer, with the WITHHELD cause beside the value.
|
|
@@ -2748,6 +2796,10 @@ export interface RunnerSelfSeat {
|
|
|
2748
2796
|
toolPolicy?: RunnerDeps["toolPolicy"];
|
|
2749
2797
|
hooks?: RunnerDeps["hooks"];
|
|
2750
2798
|
};
|
|
2799
|
+
/** Whether a child task THIS runner runs can be placed in a tree other than the caller's (an explicit cwd, a worktree):
|
|
2800
|
+
* true only when its deps carry an `executionEnvFactory` (live read — `swapDeps` replaces the deps). The delegation tool asks
|
|
2801
|
+
* the runner that will run the child, not the one that ran the parent leg. Absent on a hand-built seat ⇒ read as "cannot say" ⇒ no; a hand-built seat answers for the runner its `runTask` forwards to. */
|
|
2802
|
+
canRerootChild?(): boolean;
|
|
2751
2803
|
/** Fire `RunnerDeps.onTaskOutcome` through the swallow-guarded chokepoint (thin compositions emit at their terminal state). */
|
|
2752
2804
|
emitTaskOutcome(outcome: TaskOutcome): void;
|
|
2753
2805
|
/** Run a task to completion. `internals` is the TRUSTED run-scoped channel (never a TaskSpec field). */
|
|
@@ -28,8 +28,10 @@
|
|
|
28
28
|
import type { AskRuleEvidence, ToolCallRequest } from "../tool-policy.js";
|
|
29
29
|
import { persistedRuleMandateOf, type RuleClearanceFacts } from "../ask-origin.js";
|
|
30
30
|
import { type OrgGateVerdict, type PersistedRuleAnswer, type PersistedRuleHit, type PersistedRuleUnreadable } from "../hooks.js";
|
|
31
|
-
import type { PersistedRule, PersistedRuleVerdict, RuleOffer, SegmentCoverage } from "../permission-rule-model.js";
|
|
32
|
-
import { type
|
|
31
|
+
import type { PersistedRule, PersistedRuleVerdict, RuleOffer, ScopeRootIdentity, SegmentCoverage } from "../permission-rule-model.js";
|
|
32
|
+
import { type PathRuleBases } from "../permission-rule-model.js";
|
|
33
|
+
import { type HeadLeads } from "../path-rule-binding.js";
|
|
34
|
+
import type { ToolCallLike } from "../tool-registry.js";
|
|
33
35
|
import { type OrgRuleResolution } from "../permission-rule-org.js";
|
|
34
36
|
import { type EffectivePermissionRules, type PermissionRuleStoreProvider } from "../permission-rule-provider.js";
|
|
35
37
|
/** The one SHELL tool the persisted-rule lane's COMMAND grammar speaks for (the rule-offer face mints for it and the
|
|
@@ -87,28 +89,25 @@ export declare function persistedRuleHitOf(verdict: PersistedRuleVerdict | undef
|
|
|
87
89
|
* question nobody can answer. Every other verdict is {@link persistedRuleHitOf}'s hit. */
|
|
88
90
|
export declare function persistedRuleAnswerOf(verdict: PersistedRuleVerdict | undefined): PersistedRuleHit | PersistedRuleUnreadable | undefined;
|
|
89
91
|
/**
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
92
|
+
* The persisted-rule lane's PATH arm, for every path-targeting tool (and the entry lists' path rows): the call's target
|
|
93
|
+
* read through the ONE reader every lane shares ({@link callTargetViewsOf} — the reading's views, else the call's own
|
|
94
|
+
* lexical view), its rows anchored by `ctx.anchorsOf` (a stored row: a project row at its scope root, the rest at the
|
|
95
|
+
* chain's top leg; a list's row: where the list was installed) and judged where their heads lead too (`ctx.leads`). The
|
|
96
|
+
* precedence walk, the admission predicates and the Read-only allow are the model's (`adjudicatePersistedPathRules`); which
|
|
97
|
+
* project rows apply is judged against the leg's own root (`ctx.root`). An ALLOW here clears ASKS only — the read fences
|
|
98
|
+
* judge inside the tool exactly as they would with no rule anywhere — and an unresolvable path is no verdict on the allow
|
|
99
|
+
* side and the fail-closed ask on the tightening side.
|
|
96
100
|
*
|
|
97
|
-
* TWO readings of the call's path slot, one per direction: the tightening arms judge
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
* target (the face's OWN declaration only — a same-name tool that declares no path target is unconfirmable
|
|
101
|
-
* and no grant admits it). The precedence walk, the admission predicates and the Read-only allow are the
|
|
102
|
-
* model's (`adjudicatePersistedPathRules`). An ALLOW here clears ASKS only — the read fences (workspace
|
|
103
|
-
* containment, the sensitive-read deny face) judge inside the tool exactly as they would with no rule
|
|
104
|
-
* anywhere — and an unresolvable path is simply no verdict (fail toward asking on the allow side, no
|
|
105
|
-
* refusal on the tightening side: a rule about a path speaks about no other shape).
|
|
101
|
+
* TWO readings of the call's path slot, one per direction: the tightening arms judge the PROTECTIVE reading (the object
|
|
102
|
+
* face's declaration, the catalog's for the name as the floor); the allow arm the DECLARED one (the face's OWN
|
|
103
|
+
* declaration only — a same-name tool that declares no path target is unconfirmable and no grant admits it).
|
|
106
104
|
*/
|
|
107
|
-
export declare function pathRuleLaneAnswer(table: readonly PersistedRule[], req: Pick<ToolCallRequest, "toolName" | "args" | "face">, ctx: {
|
|
105
|
+
export declare function pathRuleLaneAnswer(table: readonly PersistedRule[], req: Pick<ToolCallRequest, "toolName" | "args" | "face" | "targetReading" | "root" | "cwd">, ctx: {
|
|
108
106
|
root: string | undefined;
|
|
109
107
|
sessionId: string | undefined;
|
|
110
|
-
|
|
111
|
-
|
|
108
|
+
anchorsOf: (rule: PersistedRule) => PathRuleBases;
|
|
109
|
+
leads: ReadonlyMap<string, string | undefined>;
|
|
110
|
+
scopeIdentity?: ScopeRootIdentity;
|
|
112
111
|
}): PersistedRuleHit | PersistedRuleUnreadable | undefined;
|
|
113
112
|
/** The two lanes prepare hands the gate. Each is `undefined` when its partition is not configured, so
|
|
114
113
|
* a deployment without one keeps a decision path byte-identical to a build without the feature. */
|
|
@@ -152,15 +151,23 @@ export declare function createPermissionRuleLanes(cfg: {
|
|
|
152
151
|
principal: string | undefined;
|
|
153
152
|
sessionId: string;
|
|
154
153
|
/** The isolation-aware task root — the same value the file fence, the prompt cwd and the session rules
|
|
155
|
-
* use, so a project-scoped rule agrees with every other place that says "this project". */
|
|
154
|
+
* use, so a project-scoped rule agrees with every other place that says "this project" (which project rows apply). */
|
|
156
155
|
root: string;
|
|
156
|
+
/** The same root's canonical spelling (`undefined`: none could be read): a project row applies when its scope root covers
|
|
157
|
+
* either spelling of it — the row's root resolved where it leads ({@link projectScopeIdentity}). */
|
|
158
|
+
rootCanonical: string | undefined;
|
|
159
|
+
/** The anchors of the person's global and session rows and of every `~/` row: the root and home of the chain's top leg
|
|
160
|
+
* (a project row anchors at its own scope root). */
|
|
161
|
+
chainTop: PathRuleBases;
|
|
162
|
+
/** The leg's resolution of the locations rule heads pass through (the rows' twins). */
|
|
163
|
+
leads: HeadLeads;
|
|
157
164
|
/** The LIVE tracked working directory: the base a relative cd resolves against
|
|
158
165
|
* after an earlier observable `cd`; read per call. */
|
|
159
166
|
liveCwd: () => string | undefined;
|
|
160
|
-
/** The EXECUTION ENVIRONMENT's home directory (`ExecutionEnv.homeDir`, adapter-declared),
|
|
161
|
-
*
|
|
162
|
-
*
|
|
163
|
-
*
|
|
167
|
+
/** The EXECUTION ENVIRONMENT's home directory (`ExecutionEnv.homeDir`, adapter-declared), screened here: a declared
|
|
168
|
+
* value that is not an absolute path is refused, loudly (a bad declaration is a wiring defect, not an absence). The
|
|
169
|
+
* `~/` rows anchor at the chain's top leg's home (`chainTop.home`); `undefined` there = no home was declared: a `~/`
|
|
170
|
+
* deny/ask row then reads UNREADABLE for every call (a fail-closed ask), never the engine process's home. */
|
|
164
171
|
home: string | undefined;
|
|
165
172
|
/** True iff the reserved question-tool NAME resolves to the ENGINE's own content-ask tool on this leg. */
|
|
166
173
|
questionToolMounted: boolean;
|
|
@@ -173,6 +180,8 @@ export declare function createPermissionRuleLanes(cfg: {
|
|
|
173
180
|
/** The person is told their store (or the named sources of it) was skipped for a call (the run's one notice,
|
|
174
181
|
* `ruleStoreUnreadableNotice`). */
|
|
175
182
|
onStoreUnreadable: (at: "call", sources?: EffectivePermissionRules["unreadableSources"]) => void;
|
|
183
|
+
/** The person is told the store served rows that were left out (the run's one `permission.rule_row_dropped`). */
|
|
184
|
+
onRowsDropped: (rows: EffectivePermissionRules["droppedRows"]) => void;
|
|
176
185
|
/** The run's cut: a read it interrupts is abandoned quietly — a cancelled wait is no fact about the store. */
|
|
177
186
|
signal?: AbortSignal;
|
|
178
187
|
/** The allow layer's auto strip over a stored ALLOW row (true ⇒ out of this call's table BEFORE the coverage is chosen). */
|
|
@@ -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;
|