@sema-agent/core 7.33.7 → 7.34.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +357 -0
- package/dist/agents/cross-session-judge.d.ts +16 -7
- package/dist/agents/cross-session-judge.js +18 -15
- package/dist/agents/peer-session-drain.d.ts +4 -0
- package/dist/agents/peer-session-drain.js +5 -2
- package/dist/agents/repair-loop.d.ts +1 -1
- package/dist/agents/subagent.d.ts +3 -0
- package/dist/agents/subagent.js +12 -3
- package/dist/brain/anthropic.js +1 -0
- package/dist/brain/open-responses.js +1 -0
- package/dist/brain/openai.js +1 -0
- package/dist/brain/stream-engine.d.ts +3 -0
- package/dist/brain/stream-engine.js +2 -1
- package/dist/core/a2a.d.ts +3 -1
- package/dist/core/a2a.js +1 -1
- package/dist/core/ask-origin.d.ts +351 -154
- package/dist/core/ask-origin.js +223 -45
- package/dist/core/ask-unresolvable-notice.d.ts +5 -2
- package/dist/core/ask-unresolvable-notice.js +4 -3
- package/dist/core/auto-mode-arming.d.ts +17 -4
- package/dist/core/auto-mode-arming.js +31 -6
- package/dist/core/auto-mode-defaults.d.ts +16 -7
- package/dist/core/auto-mode-defaults.js +2 -1
- package/dist/core/auto-mode-directive.d.ts +18 -30
- package/dist/core/auto-mode-directive.js +9 -18
- package/dist/core/auto-mode-prompt.d.ts +62 -1
- package/dist/core/auto-mode-prompt.js +42 -4
- package/dist/core/auto-mode-rebuild.d.ts +2 -1
- package/dist/core/auto-mode-rebuild.js +1 -0
- package/dist/core/auto-mode.d.ts +32 -3
- package/dist/core/auto-mode.js +80 -41
- package/dist/core/checkpoint-store.d.ts +50 -35
- package/dist/core/checkpoint-store.js +58 -3
- package/dist/core/child-clamp-seats.js +1 -0
- package/dist/core/classifier-unavailable-arm.d.ts +57 -0
- package/dist/core/classifier-unavailable-arm.js +17 -0
- package/dist/core/effective-path-target.d.ts +12 -0
- package/dist/core/effective-path-target.js +18 -7
- package/dist/core/engine-notice.d.ts +53 -2
- package/dist/core/fs-write-gate-policy.d.ts +5 -1
- package/dist/core/fs-write-gate-policy.js +4 -1
- package/dist/core/gate-fold.js +17 -1
- package/dist/core/gate-lanes.js +111 -46
- package/dist/core/gate-outcome.d.ts +40 -7
- package/dist/core/gate-outcome.js +26 -4
- package/dist/core/governance-codes.d.ts +1 -1
- package/dist/core/governance-codes.js +12 -0
- package/dist/core/hooks.d.ts +50 -29
- package/dist/core/hooks.js +1 -3
- package/dist/core/mcp.js +1 -1
- package/dist/core/mode-policy.d.ts +182 -0
- package/dist/core/mode-policy.js +179 -0
- package/dist/core/oracle-isolation.d.ts +3 -3
- package/dist/core/permission-rule-model.d.ts +16 -3
- package/dist/core/permission-rule-model.js +36 -3
- package/dist/core/permission-rule-provider.d.ts +22 -11
- package/dist/core/permission-rule-provider.js +17 -7
- package/dist/core/permission-rules.d.ts +23 -1
- package/dist/core/permission-rules.js +35 -8
- package/dist/core/persisted-rule-arms.d.ts +10 -9
- package/dist/core/persisted-rule-arms.js +9 -6
- package/dist/core/present-plan-tool.d.ts +1 -1
- package/dist/core/present-plan-tool.js +3 -3
- package/dist/core/read-only-shell.d.ts +19 -19
- package/dist/core/read-only-shell.js +38 -12
- package/dist/core/retired-keys.d.ts +9 -3
- package/dist/core/retired-keys.js +5 -1
- package/dist/core/runner/classifier-ladder.d.ts +43 -1
- package/dist/core/runner/classifier-ladder.js +29 -0
- package/dist/core/runner/contracts.d.ts +37 -35
- package/dist/core/runner/denial-limit-arms.d.ts +30 -7
- package/dist/core/runner/denial-limit-arms.js +15 -9
- package/dist/core/runner/env-capability-census.js +1 -1
- package/dist/core/runner/gate-exit.d.ts +30 -4
- package/dist/core/runner/gate-exit.js +28 -7
- package/dist/core/runner/permission-rule-lanes.d.ts +9 -5
- package/dist/core/runner/permission-rule-lanes.js +44 -40
- package/dist/core/runner/persisted-path-rows.d.ts +64 -0
- package/dist/core/runner/persisted-path-rows.js +89 -0
- package/dist/core/runner/prepare-artifact.d.ts +5 -1
- package/dist/core/runner/prepare-artifact.js +9 -3
- package/dist/core/runner/prepare-ask-lane.d.ts +5 -3
- package/dist/core/runner/prepare-ask-lane.js +1 -1
- package/dist/core/runner/prepare-caps-and-workflow.d.ts +39 -13
- package/dist/core/runner/prepare-caps-and-workflow.js +58 -29
- package/dist/core/runner/prepare-gate-stations.d.ts +33 -6
- package/dist/core/runner/prepare-gate-stations.js +32 -19
- package/dist/core/runner/prepare-hands-readface.d.ts +40 -17
- package/dist/core/runner/prepare-hands-readface.js +64 -53
- package/dist/core/runner/prepare-inherited-gate.d.ts +22 -22
- package/dist/core/runner/prepare-inherited-gate.js +37 -23
- package/dist/core/runner/prepare-park-ask.d.ts +2 -2
- package/dist/core/runner/prepare-park-ask.js +2 -1
- package/dist/core/runner/prepare-policy-chain.d.ts +22 -2
- package/dist/core/runner/prepare-policy-chain.js +39 -12
- package/dist/core/runner/prepare-protocol-tools.d.ts +28 -0
- package/dist/core/runner/prepare-protocol-tools.js +13 -3
- package/dist/core/runner/prepare-run-refs.d.ts +6 -6
- package/dist/core/runner/prepare-run-refs.js +9 -4
- package/dist/core/runner/prepare-safety-scan.d.ts +5 -0
- package/dist/core/runner/prepare-safety-scan.js +19 -3
- package/dist/core/runner/prepare-suspend-saga.d.ts +5 -5
- package/dist/core/runner/prepare-suspend-saga.js +6 -6
- package/dist/core/runner/prepare-task.js +51 -22
- package/dist/core/runner/prepare-wiring-manifest.d.ts +10 -6
- package/dist/core/runner/prepare-wiring-manifest.js +5 -3
- package/dist/core/runner/read-root-grant.d.ts +9 -0
- package/dist/core/runner/read-root-grant.js +12 -0
- package/dist/core/runner/retired-runner-deps.js +1 -0
- package/dist/core/runner/run-identity-wiring.js +1 -1
- package/dist/core/runner/run-leg.js +24 -15
- package/dist/core/runner/run-notification-lane.js +1 -0
- package/dist/core/runner/run-telemetry-and-budget-seats.d.ts +2 -2
- package/dist/core/runner/run-telemetry-and-budget-seats.js +1 -1
- package/dist/core/runner/run-turn-boundary.js +1 -1
- package/dist/core/runner/runtask.js +3 -0
- package/dist/core/runner/stream-mode-verb.d.ts +27 -0
- package/dist/core/runner/stream-mode-verb.js +19 -0
- package/dist/core/runner/turn-attachments.d.ts +3 -3
- package/dist/core/runner/turn-attachments.js +1 -1
- package/dist/core/runner/write-protection-station.d.ts +7 -1
- package/dist/core/runner/write-protection-station.js +6 -3
- package/dist/core/runner-deps.d.ts +31 -34
- package/dist/core/runtime-caps.d.ts +12 -3
- package/dist/core/safety-axis-vocab.d.ts +2 -2
- package/dist/core/safety-axis-vocab.js +1 -1
- package/dist/core/secret-env.d.ts +16 -2
- package/dist/core/secret-env.js +25 -1
- package/dist/core/sensitive-path-policy.d.ts +4 -2
- package/dist/core/sensitive-path-policy.js +39 -30
- package/dist/core/spec-contract.d.ts +15 -0
- package/dist/core/spec-contract.js +19 -10
- package/dist/core/task-event.d.ts +1 -1
- package/dist/core/task-result.d.ts +1 -1
- package/dist/core/task-spec.d.ts +41 -89
- package/dist/core/task-stream.d.ts +16 -0
- package/dist/core/terminal-cause.js +1 -0
- package/dist/core/tighten-task-spec.d.ts +2 -2
- package/dist/core/tighten-task-spec.js +12 -14
- package/dist/core/tool-catalog-entries.js +4 -3
- package/dist/core/tool-policy.d.ts +54 -25
- package/dist/core/tool-policy.js +43 -2
- package/dist/core/tool-registry.d.ts +2 -0
- package/dist/core/tool-registry.js +3 -0
- package/dist/core/tool-spec.d.ts +8 -9
- package/dist/core/trace.d.ts +7 -6
- package/dist/core/types.d.ts +2 -1
- package/dist/core/wiring-manifest.d.ts +31 -5
- package/dist/core/wiring-manifest.js +28 -1
- package/dist/core/write-protect.d.ts +11 -1
- package/dist/core/write-protect.js +26 -15
- package/dist/engine/execution-env/kill-tree.js +4 -1
- package/dist/engine/execution-env/node-execution-env.d.ts +3 -0
- package/dist/engine/execution-env/node-execution-env.js +34 -35
- package/dist/engine/harness/types.d.ts +6 -7
- package/dist/engine/llm/types.d.ts +2 -4
- package/dist/engine/loop/types.d.ts +7 -0
- package/dist/index.d.ts +15 -5
- package/dist/index.js +12 -3
- package/dist/orchestration/run-spec.d.ts +4 -0
- package/dist/orchestration/run-spec.js +4 -3
- package/dist/orchestration/workflow-governance.js +1 -1
- package/dist/orchestration/workflow-primitives.d.ts +1 -1
- package/dist/orchestration/workflow-size-guideline.js +2 -2
- package/dist/stores/file/fs-atomic.js +2 -1
- package/dist/tools/artifact/artifact-tool.d.ts +17 -0
- package/dist/tools/artifact/artifact-tool.js +16 -1
- package/dist/tools/fs/bash-readonly-classifier.d.ts +28 -7
- package/dist/tools/fs/bash-readonly-classifier.js +88 -0
- package/dist/tools/fs/containment-face.d.ts +9 -3
- package/dist/tools/fs/containment-face.js +2 -0
- package/dist/tools/fs/file-read-check.d.ts +49 -0
- package/dist/tools/fs/file-read-check.js +71 -0
- package/dist/tools/fs/fs-bash.d.ts +3 -3
- package/dist/tools/fs/fs-read.d.ts +1 -0
- package/dist/tools/fs/fs-search-tools.d.ts +2 -2
- package/dist/tools/fs/fs-search-tools.js +4 -4
- package/dist/tools/fs/fs-shared.js +20 -6
- package/dist/tools/fs/fs-write.d.ts +4 -3
- package/dist/tools/fs/fs-write.js +9 -9
- package/dist/tools/fs/index.d.ts +39 -3
- package/dist/tools/fs/index.js +38 -15
- package/dist/tools/fs/read-deny-home.d.ts +93 -0
- package/dist/tools/fs/read-deny-home.js +225 -0
- package/dist/tools/fs/read-deny.d.ts +176 -34
- package/dist/tools/fs/read-deny.js +268 -61
- package/dist/tools/fs/safety.d.ts +6 -1
- package/dist/tools/fs/safety.js +18 -5
- package/dist/tools/fs/search.d.ts +3 -0
- package/dist/tools/fs/search.js +47 -9
- package/dist/tools/fs/shell-read-check.d.ts +42 -11
- package/dist/tools/fs/shell-read-check.js +188 -13
- package/dist/tools/fs/shell-read-establish.d.ts +7 -3
- package/dist/tools/fs/shell-read-establish.js +134 -21
- package/dist/tools/fs/shell-read-identity.d.ts +7 -0
- package/dist/tools/fs/shell-read-identity.js +21 -0
- package/dist/tools/fs/shell-read-judge.d.ts +92 -32
- package/dist/tools/fs/shell-read-judge.js +43 -18
- package/dist/tools/fs/shell-read-plan.d.ts +4 -0
- package/dist/tools/fs/shell-read-plan.js +26 -4
- package/dist/tools/fs/shell-read-write-targets.d.ts +73 -0
- package/dist/tools/fs/shell-read-write-targets.js +173 -0
- package/package.json +3 -1
- package/test/export-surface.snapshot.json +103 -11
- package/wire-changes/core@7.33.8.json +165 -0
- package/wire-changes/core@7.34.0.json +2621 -0
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import { canonicalizeTarget, isAbsolutePathForm, joinForFamily, normalizeAbsPathLexically, pathFamilyOf } from "./safety.js";
|
|
2
|
+
import { DEFAULT_GATE_AWAIT_TIMEOUT_MS, withGateTimeout } from "../../core/gate-bounded-await.js";
|
|
3
|
+
import { readDenyPatternRun, readDenyRunHead } from "./read-deny.js";
|
|
4
|
+
export function declaredReadDenyHomes(env) {
|
|
5
|
+
const home = env.homeDir;
|
|
6
|
+
return home !== undefined && isAbsolutePathForm(home) ? [home] : [];
|
|
7
|
+
}
|
|
8
|
+
export async function readDenyHomesOf(env, signal) {
|
|
9
|
+
return (await readDenyHomesAnswered(env, signal)).homes;
|
|
10
|
+
}
|
|
11
|
+
async function readDenyHomesAnswered(env, signal) {
|
|
12
|
+
const home = declaredReadDenyHomes(env)[0];
|
|
13
|
+
if (home === undefined)
|
|
14
|
+
return { homes: [], whole: true };
|
|
15
|
+
const canonical = await env.canonicalPath(home, signal);
|
|
16
|
+
if (canonical.ok)
|
|
17
|
+
return { homes: canonical.value !== home ? [home, canonical.value] : [home], whole: true };
|
|
18
|
+
return { homes: [home], whole: canonical.error.code === "not_found" || canonical.error.code === "not_directory" };
|
|
19
|
+
}
|
|
20
|
+
export async function readDenyHomeLinksOf(env, entries, homes, signal) {
|
|
21
|
+
const rows = [];
|
|
22
|
+
const familyOf = new Map();
|
|
23
|
+
for (const { pattern, caseSensitive } of entries) {
|
|
24
|
+
const { homeAnchored, run } = readDenyPatternRun(pattern);
|
|
25
|
+
if (!homeAnchored)
|
|
26
|
+
continue;
|
|
27
|
+
const head = run.split("/").filter(Boolean).slice(0, readDenyRunHead(run)).join("/");
|
|
28
|
+
if (head.length === 0)
|
|
29
|
+
continue;
|
|
30
|
+
const spelled = homes.map((home) => {
|
|
31
|
+
const family = pathFamilyOf({ root: home });
|
|
32
|
+
const at = normalizeAbsPathLexically(joinForFamily(family, home, head));
|
|
33
|
+
familyOf.set(at, family);
|
|
34
|
+
return at;
|
|
35
|
+
});
|
|
36
|
+
rows.push({ pattern, caseSensitive: caseSensitive === true, spelled });
|
|
37
|
+
}
|
|
38
|
+
const resolved = new Map(await Promise.all([...familyOf].map(async ([at, family]) => [at, await whereItLeads(env, at, family, signal)])));
|
|
39
|
+
const out = new Map();
|
|
40
|
+
const whole = [...resolved.values()].every((location) => location !== undefined);
|
|
41
|
+
for (const { pattern, caseSensitive, spelled } of rows) {
|
|
42
|
+
const fold = (p) => (caseSensitive ? p : p.replace(/[A-Z]/g, (c) => c.toLowerCase()));
|
|
43
|
+
const own = new Set(spelled.map(fold));
|
|
44
|
+
const locations = [];
|
|
45
|
+
for (const at of spelled) {
|
|
46
|
+
const location = resolved.get(at);
|
|
47
|
+
if (location !== undefined && !own.has(fold(location)) && !locations.includes(location))
|
|
48
|
+
locations.push(location);
|
|
49
|
+
}
|
|
50
|
+
if (locations.length > 0)
|
|
51
|
+
out.set(pattern, locations);
|
|
52
|
+
}
|
|
53
|
+
return { linked: out, whole };
|
|
54
|
+
}
|
|
55
|
+
export async function whereItLeads(env, at, family = pathFamilyOf({ root: at }), signal) {
|
|
56
|
+
try {
|
|
57
|
+
const key = await canonicalizeTarget(env, at, signal, family);
|
|
58
|
+
return key.ok ? normalizeAbsPathLexically(key.key) : undefined;
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return undefined;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
export async function whereItsAnsweredPrefixLeads(env, at, family = pathFamilyOf({ root: at }), signal) {
|
|
65
|
+
const walk = async () => {
|
|
66
|
+
const rest = [];
|
|
67
|
+
for (let here = normalizeAbsPathLexically(at);;) {
|
|
68
|
+
const led = await whereItLeads(env, here, family, signal);
|
|
69
|
+
if (led !== undefined)
|
|
70
|
+
return rest.reduce((p, name) => joinForFamily(family, p, name), led);
|
|
71
|
+
const parent = normalizeAbsPathLexically(`${here}/..`);
|
|
72
|
+
if (parent === here)
|
|
73
|
+
return undefined;
|
|
74
|
+
rest.unshift(here.slice(parent.length).replace(/^[\\/]+/, ""));
|
|
75
|
+
here = parent;
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
return await withGateTimeout(walk(), DEFAULT_GATE_AWAIT_TIMEOUT_MS, signal, "ruleRowHead").catch(() => undefined);
|
|
79
|
+
}
|
|
80
|
+
export async function placeReadDenyAtHome(env, compile, homes, signal) {
|
|
81
|
+
let at = homes;
|
|
82
|
+
if (at === undefined) {
|
|
83
|
+
const answer = await withGateTimeout(readDenyHomesAnswered(env, signal), DEFAULT_GATE_AWAIT_TIMEOUT_MS, signal, "readDenyHomes");
|
|
84
|
+
if (!answer.whole)
|
|
85
|
+
return { matcher: compile([]), homes: [], linked: new Map(), whole: false };
|
|
86
|
+
at = answer.homes;
|
|
87
|
+
}
|
|
88
|
+
const atHomes = compile(at);
|
|
89
|
+
const { linked, whole } = await readDenyHomeLinksOf(env, atHomes.entries, at, signal);
|
|
90
|
+
return { matcher: linked.size === 0 ? atHomes : compile(at, linked), homes: at, linked, whole };
|
|
91
|
+
}
|
|
92
|
+
export function readDenyPlacedLater(unplaced, place) {
|
|
93
|
+
let current = unplaced;
|
|
94
|
+
let whole = false;
|
|
95
|
+
let pending;
|
|
96
|
+
const again = () => (pending ??= place()
|
|
97
|
+
.then((placed) => {
|
|
98
|
+
current = placed.matcher;
|
|
99
|
+
whole = placed.whole;
|
|
100
|
+
}, () => undefined)
|
|
101
|
+
.finally(() => {
|
|
102
|
+
pending = undefined;
|
|
103
|
+
}));
|
|
104
|
+
void again();
|
|
105
|
+
const ready = async () => {
|
|
106
|
+
if (pending !== undefined)
|
|
107
|
+
await pending;
|
|
108
|
+
if (!whole)
|
|
109
|
+
await again();
|
|
110
|
+
};
|
|
111
|
+
const matcher = {
|
|
112
|
+
get entries() {
|
|
113
|
+
return current.entries;
|
|
114
|
+
},
|
|
115
|
+
get ruleRows() {
|
|
116
|
+
return current.ruleRows;
|
|
117
|
+
},
|
|
118
|
+
matchPath: (path, opts) => current.matchPath(path, opts),
|
|
119
|
+
matchTarget: (canonicalKey, lexicalView, opts) => current.matchTarget(canonicalKey, lexicalView, opts),
|
|
120
|
+
get rgExclusionGlobs() {
|
|
121
|
+
return current.rgExclusionGlobs;
|
|
122
|
+
},
|
|
123
|
+
get rgProbeGlobs() {
|
|
124
|
+
return current.rgProbeGlobs;
|
|
125
|
+
},
|
|
126
|
+
unexcludableUnder: (scope) => current.unexcludableUnder?.(scope) ?? false,
|
|
127
|
+
rowAtOrBelow: (path) => current.rowAtOrBelow?.(path) ?? null,
|
|
128
|
+
};
|
|
129
|
+
return { matcher, ready };
|
|
130
|
+
}
|
|
131
|
+
const HOME_CHAINS = new WeakMap();
|
|
132
|
+
function homeChainOf(env) {
|
|
133
|
+
const identity = env.fileIdentity;
|
|
134
|
+
const home = declaredReadDenyHomes(env)[0];
|
|
135
|
+
if (typeof identity !== "function" || home === undefined)
|
|
136
|
+
return undefined;
|
|
137
|
+
let perEnv = HOME_CHAINS.get(env);
|
|
138
|
+
if (perEnv === undefined)
|
|
139
|
+
HOME_CHAINS.set(env, (perEnv = new Map()));
|
|
140
|
+
const known = perEnv;
|
|
141
|
+
let chain = known.get(home);
|
|
142
|
+
if (chain === undefined) {
|
|
143
|
+
const reading = withGateTimeout((async () => {
|
|
144
|
+
const canonical = await env.canonicalPath(home);
|
|
145
|
+
if (!canonical.ok)
|
|
146
|
+
return { index: undefined, whole: false };
|
|
147
|
+
const out = new Map();
|
|
148
|
+
let whole = true;
|
|
149
|
+
for (let dir = normalizeAbsPathLexically(canonical.value);;) {
|
|
150
|
+
const id = await identityOf(env, identity, dir);
|
|
151
|
+
if (id?.ok === true) {
|
|
152
|
+
if (!out.has(id.value))
|
|
153
|
+
out.set(id.value, dir);
|
|
154
|
+
}
|
|
155
|
+
else
|
|
156
|
+
whole = false;
|
|
157
|
+
const parent = normalizeAbsPathLexically(`${dir}/..`);
|
|
158
|
+
if (parent === dir)
|
|
159
|
+
break;
|
|
160
|
+
dir = parent;
|
|
161
|
+
}
|
|
162
|
+
return { index: out.size > 0 ? out : undefined, whole };
|
|
163
|
+
})(), DEFAULT_GATE_AWAIT_TIMEOUT_MS, undefined, "homeChain").catch(() => ({ index: undefined, whole: false }));
|
|
164
|
+
chain = reading;
|
|
165
|
+
known.set(home, reading);
|
|
166
|
+
const drop = () => {
|
|
167
|
+
if (known.get(home) === reading)
|
|
168
|
+
known.delete(home);
|
|
169
|
+
};
|
|
170
|
+
void reading.then((c) => (c.whole ? undefined : drop()));
|
|
171
|
+
}
|
|
172
|
+
return chain.then((c) => c.index);
|
|
173
|
+
}
|
|
174
|
+
async function identityOf(env, identity, path, signal) {
|
|
175
|
+
try {
|
|
176
|
+
return await identity.call(env, path, signal);
|
|
177
|
+
}
|
|
178
|
+
catch {
|
|
179
|
+
return undefined;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
export function homeViewsOf(env, signal) {
|
|
183
|
+
const chain = homeChainOf(env);
|
|
184
|
+
const identity = env.fileIdentity;
|
|
185
|
+
const views = new Map();
|
|
186
|
+
const viewOf = (path, leafIsFile) => {
|
|
187
|
+
const key = `${leafIsFile ? "f" : "d"}:${path}`;
|
|
188
|
+
let v = views.get(key);
|
|
189
|
+
if (v === undefined) {
|
|
190
|
+
v = (async () => {
|
|
191
|
+
const index = await chain;
|
|
192
|
+
if (index === undefined || typeof identity !== "function")
|
|
193
|
+
return undefined;
|
|
194
|
+
if (!leafIsFile) {
|
|
195
|
+
const id = await identityOf(env, identity, path, signal);
|
|
196
|
+
if (id === undefined)
|
|
197
|
+
return undefined;
|
|
198
|
+
if (id.ok) {
|
|
199
|
+
const at = index.get(id.value);
|
|
200
|
+
if (at !== undefined)
|
|
201
|
+
return at;
|
|
202
|
+
}
|
|
203
|
+
else if (id.error.code !== "not_found" && id.error.code !== "not_directory")
|
|
204
|
+
return undefined;
|
|
205
|
+
}
|
|
206
|
+
const parent = normalizeAbsPathLexically(`${path}/..`);
|
|
207
|
+
if (parent === path)
|
|
208
|
+
return undefined;
|
|
209
|
+
const up = await viewOf(parent, false);
|
|
210
|
+
return up === undefined ? undefined : joinForFamily(pathFamilyOf({ root: path }), up, path.slice(parent.length).replace(/^[\\/]+/, ""));
|
|
211
|
+
})();
|
|
212
|
+
views.set(key, v);
|
|
213
|
+
}
|
|
214
|
+
return v;
|
|
215
|
+
};
|
|
216
|
+
return {
|
|
217
|
+
async of(path, opts) {
|
|
218
|
+
if (chain === undefined || !isAbsolutePathForm(path))
|
|
219
|
+
return undefined;
|
|
220
|
+
const spelled = normalizeAbsPathLexically(path);
|
|
221
|
+
const view = await viewOf(spelled, opts?.leaf === "file");
|
|
222
|
+
return view === undefined || view === spelled ? undefined : view;
|
|
223
|
+
},
|
|
224
|
+
};
|
|
225
|
+
}
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* One deny entry: a `/`-separated run of path SEGMENTS. `*` matches any run of non-separator
|
|
3
3
|
* characters within one segment and is the ONLY metacharacter (everything else is literal). The run
|
|
4
|
-
* may match ANYWHERE in the judged path (`.ssh` covers `~/.ssh/…` and any other `.ssh` directory)
|
|
4
|
+
* may match ANYWHERE in the judged path (`.ssh` covers `~/.ssh/…` and any other `.ssh` directory) —
|
|
5
|
+
* unless the pattern opens with the HOME word ({@link READ_DENY_HOME_WORD}): `~/<run>` matches only where
|
|
6
|
+
* the run starts right below the execution environment's home directory (`~/.sema/settings.json` is the
|
|
7
|
+
* home's file, never a project's `.sema/settings.json`). Only a FIRST segment that is exactly `~` is the
|
|
8
|
+
* word; `~user` and a `~` further along are literal segments.
|
|
5
9
|
* String shorthand ≡ `{ pattern }`. Matching is ASCII-case-insensitive by default (a case-insensitive
|
|
6
10
|
* filesystem serves `.SSH` and `.ssh` as the same directory); `caseSensitive: true` opts a single
|
|
7
11
|
* entry out. Unicode normalization (NFC/NFD) and volume-level case semantics are NOT modeled (v1
|
|
@@ -12,6 +16,20 @@ export type ReadDenyEntry = string | {
|
|
|
12
16
|
pattern: string;
|
|
13
17
|
caseSensitive?: boolean;
|
|
14
18
|
};
|
|
19
|
+
/** The HOME word of the pattern grammar ({@link ReadDenyEntry}): a FIRST segment spelled exactly this anchors the run
|
|
20
|
+
* right below the execution environment's home directory. */
|
|
21
|
+
export declare const READ_DENY_HOME_WORD = "~";
|
|
22
|
+
/** A pattern read as its anchor and its run: `~/a/b` ⇒ the run `a/b`, home-anchored; any other pattern ⇒ itself,
|
|
23
|
+
* unanchored. The one reader of the home word — the compiler, the traversal globs, the classifier's rendering and the
|
|
24
|
+
* write face's mirror all take the run from here. */
|
|
25
|
+
export declare function readDenyPatternRun(pattern: string): {
|
|
26
|
+
readonly homeAnchored: boolean;
|
|
27
|
+
readonly run: string;
|
|
28
|
+
};
|
|
29
|
+
/** How many leading segments of a run are LITERAL — the run's head before the first segment carrying `*`. A home-anchored
|
|
30
|
+
* row's head names one location below the home, which a mount resolves to where it really leads
|
|
31
|
+
* ({@link compileReadDeny}'s `linked`); the segments after it are matched below that location as the run matches. */
|
|
32
|
+
export declare function readDenyRunHead(run: string): number;
|
|
15
33
|
/** The normalized (validated, defaulted) form of one entry. */
|
|
16
34
|
export interface NormalizedReadDenyEntry {
|
|
17
35
|
pattern: string;
|
|
@@ -26,6 +44,66 @@ export interface ReadDenyHit {
|
|
|
26
44
|
* sends the reader chasing a path the pattern does not match. */
|
|
27
45
|
matchedView?: string;
|
|
28
46
|
}
|
|
47
|
+
/** The two faces of the deny table: what a READ must not reach, and what a WRITE must not reach. */
|
|
48
|
+
export declare const DENY_FACES: readonly ["read", "write"];
|
|
49
|
+
export type DenyFace = (typeof DENY_FACES)[number];
|
|
50
|
+
/**
|
|
51
|
+
* A deny row in the rule DSL's PATH grammar — a `Read(<pattern>)` / `Edit(<pattern>)` deny rule, compiled by the
|
|
52
|
+
* rule compiler against its own bases (`createPermissionRulePolicy`). It joins the SAME table as the segment rows: one
|
|
53
|
+
* judge (`matchPath` / `matchTarget`), one refusal sentence, one classifier rendering — only the row's reach is the
|
|
54
|
+
* rule grammar's (the path it names and everything under a directory it names), not a segment run.
|
|
55
|
+
* · `rule` — the rule as written, the row's name wherever a refusal or the classifier names it;
|
|
56
|
+
* · `tool` / `pattern` — the rule's two parts, for a rendering that states the pattern inertly;
|
|
57
|
+
* · `faces` — which faces the row guards (`Read` ⇒ read and write: a path whose reads are denied is not written
|
|
58
|
+
* either; `Edit` ⇒ write);
|
|
59
|
+
* · `reaches` — the rule compiler's own reach over an ABSOLUTE path.
|
|
60
|
+
*/
|
|
61
|
+
export interface PathRuleDenyRow {
|
|
62
|
+
readonly rule: string;
|
|
63
|
+
readonly tool: string;
|
|
64
|
+
readonly pattern: string;
|
|
65
|
+
readonly faces: readonly DenyFace[];
|
|
66
|
+
readonly reaches: (absolutePath: string) => boolean;
|
|
67
|
+
/** The row's HEAD — the absolute location its reach starts at (the rule model's `pathRuleHeadOf`: the pattern's
|
|
68
|
+
* literal prefix under its base, a directory rule's directory), spelled so the execution environment can resolve it.
|
|
69
|
+
* A leg judges the row where that location LEADS too ({@link withRuleRowsWhereTheyLead}). Absent on a hand-built
|
|
70
|
+
* row, or one whose head cannot be read: it is judged as spelled. */
|
|
71
|
+
readonly head?: string;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* The rule rows as a leg judges them: each row as spelled, and — for every location on the way to the row's HEAD (each
|
|
75
|
+
* leading part of it: the anchor's children down to the head itself) that the environment resolves to another spelling
|
|
76
|
+
* (a link or firmlink: `/tmp` → `/private/tmp`, `/var` → `/private/var`, a project root, a home entry or a directory
|
|
77
|
+
* inside the rule reached through a symlink) — the same row at that spelling: a path at or below the resolved location
|
|
78
|
+
* is judged as its spelling at or below that part. Every read and write face judges the CANONICAL key of a target beside
|
|
79
|
+
* its spelled view, so a row whose head is spelled through a link would otherwise never meet a target named by its real
|
|
80
|
+
* location — whether the row was written with the short spelling and the call with the long one, or the reverse. Every
|
|
81
|
+
* part, not the head alone: a link inside the rule's literal prefix that is re-pointed after the rows were placed still
|
|
82
|
+
* meets the row through the parts above it (`<real root>/alias/**` holds when `alias` moves). One rule for every row
|
|
83
|
+
* form (absolute, home, root, working directory, directory): the reference compiles the same "physical twin" of every
|
|
84
|
+
* deny / ask rule's literal prefix (cli280.js `ei` 9766583, compiled in `mt` 9767083). `leadsTo` is the leg's resolution,
|
|
85
|
+
* the one the faces mint their keys with (`undefined` = could not resolve: no twin there); each location is resolved
|
|
86
|
+
* once, and a part that only extends its parent's resolution adds no row.
|
|
87
|
+
*/
|
|
88
|
+
export declare function withRuleRowsWhereTheyLead(rows: readonly PathRuleDenyRow[], leadsTo: (head: string) => Promise<string | undefined>): Promise<PathRuleDenyRow[]>;
|
|
89
|
+
/**
|
|
90
|
+
* The WRITE face of the deny table: the rule rows whose faces include `write` — the judge the shell station reads for a
|
|
91
|
+
* command's write targets (a redirection, `tee`, `sed -i`, `cp` / `mv` / `touch` / `mkdir` / `rm` operands). Same row
|
|
92
|
+
* type, same judgment (`ruleRowHit`) as the read face's rule rows; a hit names the row by its rule text.
|
|
93
|
+
*/
|
|
94
|
+
export interface WriteDenyMatcher {
|
|
95
|
+
/** Which face this judge is — the write tools' path resolution reads it to word and code the refusal as a write. */
|
|
96
|
+
readonly face: "write";
|
|
97
|
+
readonly ruleRows: readonly PathRuleDenyRow[];
|
|
98
|
+
matchPath(path: string): ReadDenyHit | null;
|
|
99
|
+
/** The target judgment the read face makes, on this face's rows: canonical key, lexical view, the leaf chain's hops. */
|
|
100
|
+
matchTarget(canonicalKey: string, lexicalView?: string, opts?: {
|
|
101
|
+
readonly aliasResolved?: boolean;
|
|
102
|
+
readonly hops?: readonly string[];
|
|
103
|
+
}): ReadDenyHit | null;
|
|
104
|
+
}
|
|
105
|
+
/** Compile the write face over the rule rows (the rows not on it are dropped here, once). */
|
|
106
|
+
export declare function compileWriteDeny(rules?: readonly PathRuleDenyRow[]): WriteDenyMatcher;
|
|
29
107
|
/** One compiled ripgrep glob flag for the traversal legs (`--iglob` = case-insensitive entry). */
|
|
30
108
|
export interface ReadDenyRgGlob {
|
|
31
109
|
flag: "--glob" | "--iglob";
|
|
@@ -40,12 +118,20 @@ export interface ReadDenyMatcher {
|
|
|
40
118
|
/** Normalized entries (built-ins first, then additions; exact duplicates folded). The disclosure /
|
|
41
119
|
* persistence face. */
|
|
42
120
|
readonly entries: readonly NormalizedReadDenyEntry[];
|
|
121
|
+
/** The rule-DSL rows on the READ face ({@link PathRuleDenyRow}), judged after the segment rows. Not persisted:
|
|
122
|
+
* they are the caller policy's, which a resume re-supplies. Absent on a matcher compiled without rules. */
|
|
123
|
+
readonly ruleRows?: readonly PathRuleDenyRow[];
|
|
43
124
|
/** Judge ONE path view (segment-window semantics). Returns the first matching entry, or null.
|
|
44
125
|
* `aliasResolved` = the key's MINTER reports the real filesystem already collapsed any win32
|
|
45
126
|
* component alias in it (canonicalizeTarget's realpath arm); unset ⇒ both readings are judged,
|
|
46
|
-
* which is the fail-closed default.
|
|
127
|
+
* which is the fail-closed default. `walked` = the path is a MEMBER a traversal enumerated below a
|
|
128
|
+
* root the command's text names, not a spelling of the command: the rule-DSL rows do not judge it (a
|
|
129
|
+
* `Read(<pattern>)` deny reaches what the command SPELLS — the reference implementation's best-effort
|
|
130
|
+
* reading of a rule against a command's arguments), while the segment rows (the built-in tiers, the
|
|
131
|
+
* deployment's patterns) reach every path a read can open. Unset ⇒ every row, the fail-closed default. */
|
|
47
132
|
matchPath(path: string, opts?: {
|
|
48
133
|
readonly aliasResolved?: boolean;
|
|
134
|
+
readonly walked?: boolean;
|
|
49
135
|
}): ReadDenyHit | null;
|
|
50
136
|
/** §3.3 target judgment: canonical view + lexical view + every hop of the leaf symlink chain (`hops`,
|
|
51
137
|
* each an unresolved spelling some link wrote); ANY hit decides. `aliasResolved` applies to the
|
|
@@ -55,17 +141,32 @@ export interface ReadDenyMatcher {
|
|
|
55
141
|
readonly hops?: readonly string[];
|
|
56
142
|
}): ReadDenyHit | null;
|
|
57
143
|
/** Traversal exclusions for the rg legs: `!`-polarity glob pairs — `!**\/<p>` plus `!**\/<p>/…`
|
|
58
|
-
* (the entry itself and its descendants).
|
|
144
|
+
* (the entry itself and its descendants). An entry placed at a home carries none (a glob is relative to the
|
|
145
|
+
* search root): `matchPath` judges it on the rg leg's output and in the walker. */
|
|
59
146
|
readonly rgExclusionGlobs: readonly ReadDenyRgGlob[];
|
|
60
147
|
/** Positive-polarity twins of the exclusions, for the bounded existence probe (first hit = entries
|
|
61
148
|
* matching the deny list exist under the scope and were excluded). */
|
|
62
149
|
readonly rgProbeGlobs: readonly ReadDenyRgGlob[];
|
|
150
|
+
/** Whether an entry that carries NO traversal glob (one placed at a home) can match a path under `scope` (an absolute
|
|
151
|
+
* directory): the rg leg cannot exclude such an entry, and a search that reads it and prunes afterwards answers
|
|
152
|
+
* differently by whether the entry's content matched — so a search over such a scope takes the walker, which prunes
|
|
153
|
+
* by path before reading. A scope that is not rooted answers true. */
|
|
154
|
+
unexcludableUnder?(scope: string): boolean;
|
|
155
|
+
/** A row that lies AT or BELOW `path` — the path is inside one of the row's matches, or a match would run on below the
|
|
156
|
+
* path from its own segments ({@link rowMeetsPath}) — so moving, copying or removing the path changes what the row
|
|
157
|
+
* covers. A rule row's reach below a path is not enumerable: any rule row answers. */
|
|
158
|
+
rowAtOrBelow?(path: string): ReadDenyHit | null;
|
|
63
159
|
}
|
|
64
160
|
/** The DENY HALF of a shell read boundary, in the shape `BashReadonlyRootBoundary` declares it (structural:
|
|
65
161
|
* this module is the leaf every face depends on). `denyMatch` judges one LEXICALLY RESOLVED operand,
|
|
66
162
|
* returning the matched pattern or null; PRESENT-IFF there is something to judge. */
|
|
67
163
|
export interface ReadDenySeat {
|
|
68
|
-
|
|
164
|
+
/** `walked` — the path is a traversal's enumerated member, not a spelling ({@link ReadDenyMatcher.matchPath}). */
|
|
165
|
+
denyMatch?: (resolvedPath: string, opts?: {
|
|
166
|
+
readonly walked?: boolean;
|
|
167
|
+
}) => string | null;
|
|
168
|
+
/** {@link ReadDenyMatcher.rowAtOrBelow} for one lexically resolved path; present exactly when `denyMatch` is. */
|
|
169
|
+
denyAtOrBelow?: (resolvedPath: string) => string | null;
|
|
69
170
|
denyEntries: readonly string[];
|
|
70
171
|
}
|
|
71
172
|
/** The ONE adapter from a compiled matcher to that seat, so every face mounts it the SAME way and the "wire
|
|
@@ -86,9 +187,11 @@ export declare function readDenySeat(matcher: ReadDenyMatcher | undefined): Read
|
|
|
86
187
|
* which is approvable and so states the finding without claiming a refusal. NOT composers, each stating
|
|
87
188
|
* its own fact: the compaction lane's withheld-attachment HTML attribute, and the traversal notes'
|
|
88
189
|
* withheld-COUNT lines, which name the table and a pattern LIST. */
|
|
89
|
-
export declare function sensitivePathDenyList(pattern: string | readonly string[]): string;
|
|
190
|
+
export declare function sensitivePathDenyList(pattern: string | readonly string[], face?: DenyFace): string;
|
|
90
191
|
/** The deny table's REACH: a deny row holds INSIDE the containment roots, so no root grant answers it. */
|
|
91
192
|
export declare const SENSITIVE_PATH_DENY_SCOPE = "This list guards credential-class paths and applies regardless of the containment roots.";
|
|
193
|
+
/** The write face's reach, in the same place of the sentence: a write deny row holds under every root and every mode. */
|
|
194
|
+
export declare const WRITE_DENY_SCOPE = "A write deny row holds regardless of the containment roots and of the permission mode.";
|
|
92
195
|
/**
|
|
93
196
|
* THE deny refusal sentence — five segments, one mint, every read face: the ACTION, the ABSOLUTE target as the
|
|
94
197
|
* caller spelled it (with its canonical target in parentheses when the two differ), the DISPOSITION (a deny row is
|
|
@@ -105,6 +208,8 @@ export declare function readDenySentence(finding: {
|
|
|
105
208
|
readonly canonical?: string;
|
|
106
209
|
readonly pattern: string;
|
|
107
210
|
readonly matchedView?: string;
|
|
211
|
+
/** The table face that refused (default `read`): a write-face hit names the write list and its reach, and writes. */
|
|
212
|
+
readonly face?: DenyFace;
|
|
108
213
|
}): string;
|
|
109
214
|
/** The per-part budget of the deny sentence — the gate's own card-account cap, not a second number. */
|
|
110
215
|
export declare const READ_DENY_SENTENCE_PART_MAX = 200;
|
|
@@ -135,15 +240,16 @@ export interface ReadDenyBuiltinRow {
|
|
|
135
240
|
/**
|
|
136
241
|
* READ_FACE_BUILTIN_DENY_TABLE — the FULL built-in table with tier metadata: the material basis of
|
|
137
242
|
* the deployment admin face, what an org administrator "sees" before selecting tiers / excluding
|
|
138
|
-
* rows. Activation is a SEPARATE fact
|
|
139
|
-
* deployment
|
|
140
|
-
* the active set. Rows stay curated regardless — the menu has to be worth switching on.
|
|
243
|
+
* rows. Activation is a SEPARATE fact: the tiers of {@link READ_DENY_DEFAULT_TIERS} are active unless the
|
|
244
|
+
* deployment selects otherwise, the rest are inert until a deployment names their tier — so this is the
|
|
245
|
+
* MENU, never the active set. Rows stay curated regardless — the menu has to be worth switching on.
|
|
141
246
|
* The dotenv family is deliberately NOT here — workspace `.env` files are working material for the
|
|
142
247
|
* tasks this engine runs; an `outside_workspace` tier is likewise deliberately absent.
|
|
143
248
|
*
|
|
144
|
-
* Each row states its tradeoff.
|
|
145
|
-
* fixture spelled `fixtures/.ssh/id_rsa` is refused too — deliberate: the refusal names the pattern,
|
|
146
|
-
* and a false refusal on a fixture is the cheap direction (deny errs strict).
|
|
249
|
+
* Each row states its tradeoff. An unanchored row is matched as a segment run anywhere in the path, so a
|
|
250
|
+
* repo fixture spelled `fixtures/.ssh/id_rsa` is refused too — deliberate: the refusal names the pattern,
|
|
251
|
+
* and a false refusal on a fixture is the cheap direction (deny errs strict). A home-anchored row
|
|
252
|
+
* (`~/…`, {@link READ_DENY_HOME_WORD}) is matched only below the home directory.
|
|
147
253
|
*
|
|
148
254
|
* NOT listed, deliberately: `.env`/`.env.*` (ruled out — workspace material; the WRITE guard still
|
|
149
255
|
* covers them; the ENGINE's own `.env` under its data root is a different file and IS listed, see the
|
|
@@ -153,27 +259,31 @@ export interface ReadDenyBuiltinRow {
|
|
|
153
259
|
* engine's own data root as a WHOLE (transcript integrity policy owns it with an ask on the write
|
|
154
260
|
* face, and the memory library the model is told to read and edit lives under it).
|
|
155
261
|
*
|
|
156
|
-
* agent-config tier — the agent's OWN configuration files whose `env` / server blocks carry API
|
|
157
|
-
* in cleartext (the shell's `settings.json` env block,
|
|
158
|
-
*
|
|
159
|
-
*
|
|
160
|
-
*
|
|
161
|
-
*
|
|
162
|
-
*
|
|
163
|
-
*
|
|
262
|
+
* agent-config tier — the agent's OWN user-level configuration files whose `env` / server blocks carry API
|
|
263
|
+
* keys in cleartext (the shell's `settings.json` env block, an MCP server env, the engine host's data-root
|
|
264
|
+
* `.env`). Unguarded, a structured read of one of these lands the key verbatim in the tool result and
|
|
265
|
+
* therefore in the session record. Every row is HOME-ANCHORED: the user's own files under the home
|
|
266
|
+
* directory are refused, while a PROJECT's file of the same name (`./.sema/settings.json`, `./.mcp.json`,
|
|
267
|
+
* `./.claude/settings.json`) is working material the person edits with the model and reads normally — its
|
|
268
|
+
* writes are the write faces' business. Rows name FILES, not the config DIRECTORIES: `.sema`/`.claude` also
|
|
269
|
+
* hold agents, skills, hooks and (under the shell's layout, `~/.sema/engine-data`) the engine data root
|
|
270
|
+
* with the memory library — material the model reads and edits as ordinary work, and a directory-wide row
|
|
271
|
+
* would need a data-root passthrough (one more rule) to keep it readable. Token
|
|
164
272
|
* STORES of the same tooling (`.credentials.json`, an agent CLI's `auth.json`, …) are credentials rows, not
|
|
165
273
|
* agent-config rows: a deployment that switches this tier off to debug its agent configuration must
|
|
166
|
-
* not also expose the OAuth stores beside it.
|
|
274
|
+
* not also expose the OAuth stores beside it. The rows' RUNS are ALSO the opt-in write deny set's
|
|
167
275
|
* agent-configuration block ({@link import("../../core/sensitive-path-policy.js").agentConfigDenyPatterns},
|
|
168
276
|
* which reads them from here; contract sentence there), so a row added here is judged by both faces.
|
|
169
277
|
*/
|
|
170
278
|
export declare const READ_FACE_BUILTIN_DENY_TABLE: readonly ReadDenyBuiltinRow[];
|
|
171
279
|
/**
|
|
172
|
-
* The DEFAULT tier selection
|
|
173
|
-
*
|
|
174
|
-
*
|
|
280
|
+
* The DEFAULT tier selection — what an unconfigured deployment activates: the `credentials` tier (key
|
|
281
|
+
* material, cloud and registry credential files, token stores) and the `agent-config` tier (the agent's own
|
|
282
|
+
* user-level configuration files, home-anchored). The `shell-history`, `browser` and `wallet` tiers are a
|
|
283
|
+
* deployment's opt-in. Activation is a deployment decision with a visible seat, and the leg's wiring
|
|
284
|
+
* manifest reports which tiers it got.
|
|
175
285
|
*
|
|
176
|
-
*
|
|
286
|
+
* The NAMED default rather than an inline literal: `[]` and absence stay separately spellable on the
|
|
177
287
|
* seat, and a deployment profile wanting a different default changes THIS expression, not call sites.
|
|
178
288
|
*/
|
|
179
289
|
export declare const READ_DENY_DEFAULT_TIERS: readonly ReadDenyBuiltinTier[];
|
|
@@ -182,8 +292,7 @@ export declare const READ_DENY_DEFAULT_TIERS: readonly ReadDenyBuiltinTier[];
|
|
|
182
292
|
* HandsToolkitOptions; deliberately NOT a TaskSpec key and NOT in the governed workflow whitelist,
|
|
183
293
|
* so no task or governed script can widen below its deployment).
|
|
184
294
|
* - `tiers` — EXACTLY-these tier selection: listed tiers are active, unlisted ones are not
|
|
185
|
-
* (`[]` = no built-in tiers at all — an explicit, legal posture; absent = {@link READ_DENY_DEFAULT_TIERS}
|
|
186
|
-
* which is itself empty, so absent and `[]` resolve alike today and stay separately spellable).
|
|
295
|
+
* (`[]` = no built-in tiers at all — an explicit, legal posture; absent = {@link READ_DENY_DEFAULT_TIERS}).
|
|
187
296
|
* - `exclude` — per-row removal by canonical pattern name, applied AFTER tier selection.
|
|
188
297
|
* Unknown tier names / unknown row names refuse loudly — never silently ignored.
|
|
189
298
|
*/
|
|
@@ -198,6 +307,17 @@ export interface ReadDenyBuiltinConfig {
|
|
|
198
307
|
* table under a candidate configuration with the engine's own rules.
|
|
199
308
|
*/
|
|
200
309
|
export declare function resolveReadDenyBuiltins(config?: ReadDenyBuiltinConfig): readonly ReadDenyBuiltinRow[];
|
|
310
|
+
/**
|
|
311
|
+
* The deployment's built-in configuration as ONE validated snapshot, read off the seats' OWN values: each seat is an
|
|
312
|
+
* array of names or absent — any other value (a string, the empty string included) refuses loudly, naming the seat,
|
|
313
|
+
* before anything is spread or copied (a spread string reads as its characters, or as no names at all). The names are
|
|
314
|
+
* then judged by {@link resolveReadDenyBuiltins}. The snapshot holds copies: a caller mutating its arrays afterwards
|
|
315
|
+
* moves nothing, so every compile of one mount reads the same selection.
|
|
316
|
+
*/
|
|
317
|
+
export declare function readDenyBuiltinConfigOf(seats: {
|
|
318
|
+
readonly tiers?: unknown;
|
|
319
|
+
readonly exclude?: unknown;
|
|
320
|
+
}): ReadDenyBuiltinConfig;
|
|
201
321
|
/**
|
|
202
322
|
* The tiers a deployment configuration ACTIVATES — the tier names of {@link resolveReadDenyBuiltins}'s
|
|
203
323
|
* surviving rows, in {@link READ_DENY_BUILTIN_TIERS} order (the closed set is the ordering source, so
|
|
@@ -210,10 +330,9 @@ export declare function resolveReadDenyBuiltins(config?: ReadDenyBuiltinConfig):
|
|
|
210
330
|
export declare function activeReadDenyBuiltinTiers(config?: ReadDenyBuiltinConfig): readonly ReadDenyBuiltinTier[];
|
|
211
331
|
/**
|
|
212
332
|
* READ_FACE_DEFAULT_DENY_ENTRIES — the DEFAULT-ACTIVE built-in set: {@link READ_FACE_BUILTIN_DENY_TABLE}
|
|
213
|
-
* under {@link READ_DENY_DEFAULT_TIERS}
|
|
214
|
-
*
|
|
215
|
-
*
|
|
216
|
-
* tier-annotated table is where a consumer renders "what COULD be switched on".
|
|
333
|
+
* under {@link READ_DENY_DEFAULT_TIERS} — the answer to "what does an unconfigured deployment refuse, from
|
|
334
|
+
* this table". Derived, so it follows the default selection; the tier-annotated table is where a consumer
|
|
335
|
+
* renders "what COULD be switched on".
|
|
217
336
|
*/
|
|
218
337
|
export declare const READ_FACE_DEFAULT_DENY_ENTRIES: readonly ReadDenyEntry[];
|
|
219
338
|
/** A compiled segment-run pattern (shared engine — the write guard delegates here too). */
|
|
@@ -222,6 +341,12 @@ export interface CompiledSegmentPattern {
|
|
|
222
341
|
segments: RegExp[];
|
|
223
342
|
/** Fold BOTH sides through {@link asciiLower} before testing (compile lowered the pattern side). */
|
|
224
343
|
foldAscii: boolean;
|
|
344
|
+
/** The segment lists of the directories the run is ANCHORED at (folded as the pattern folds): the run must start
|
|
345
|
+
* right after one of them. Absent = the run matches anywhere. */
|
|
346
|
+
anchors?: readonly (readonly string[])[];
|
|
347
|
+
/** Judged in a ROOTED view only: an extra location of a home-anchored row (where its literal head leads). A view that
|
|
348
|
+
* is not rooted cannot be placed and is covered by the row's own run, held anywhere. */
|
|
349
|
+
rootedOnly?: true;
|
|
225
350
|
}
|
|
226
351
|
/**
|
|
227
352
|
* Compile one segment-glob pattern (shared engine). `fold`:
|
|
@@ -235,9 +360,10 @@ export interface CompiledSegmentPattern {
|
|
|
235
360
|
export declare function compileSegmentPattern(raw: string, fold: "none" | "ascii" | "unicode"): CompiledSegmentPattern | null;
|
|
236
361
|
/**
|
|
237
362
|
* Does the path contain a compiled pattern as a CONTIGUOUS run of full path segments? (Shared engine:
|
|
238
|
-
* `.git/hooks` matches `/repo/.git/hooks/pre-commit`; `.ssh` matches any `.ssh` segment.)
|
|
239
|
-
*
|
|
240
|
-
*
|
|
363
|
+
* `.git/hooks` matches `/repo/.git/hooks/pre-commit`; `.ssh` matches any `.ssh` segment.) An ANCHORED pattern
|
|
364
|
+
* ({@link CompiledSegmentPattern.anchors}) matches only where the path's leading segments are one anchor's and
|
|
365
|
+
* the run starts right after them. Splits on BOTH separator families — win32 canonical keys are backslash-form.
|
|
366
|
+
* Returns the matched raw pattern or null.
|
|
241
367
|
*/
|
|
242
368
|
export declare function matchSegmentPatterns(path: string, compiled: readonly CompiledSegmentPattern[], opts?: {
|
|
243
369
|
readonly aliasResolved?: boolean;
|
|
@@ -251,6 +377,10 @@ export declare function matchSegmentPatterns(path: string, compiled: readonly Co
|
|
|
251
377
|
* refusal reason, or null when the entry is valid.
|
|
252
378
|
*/
|
|
253
379
|
export declare function persistedReadDenyEntryProblem(e: unknown): string | null;
|
|
380
|
+
/** The home spellings a home-anchored row is placed under, judged as given: an array of absolute path strings, loud
|
|
381
|
+
* otherwise and naming `readDenyHomes` — a relative or empty spelling places the row where no home is, and a bare
|
|
382
|
+
* string would be read as its characters. */
|
|
383
|
+
export declare function checkedReadDenyHomes(homes: unknown, seat?: string): readonly string[];
|
|
254
384
|
/**
|
|
255
385
|
* Compile the read-face deny judge: ACTIVE built-in rows ∪ additions. Additions are add-only at every
|
|
256
386
|
* task layer (`[]` additions ≡ absent, deliberately a no-op rather than a refusal: an array API
|
|
@@ -260,5 +390,17 @@ export declare function persistedReadDenyEntryProblem(e: unknown): string | null
|
|
|
260
390
|
* re-add single patterns through the additions seats — they then ride as ordinary additions.
|
|
261
391
|
* Bad entry shapes / zero-segment patterns / unknown tier or row names throw loudly, naming the
|
|
262
392
|
* layer or knob.
|
|
393
|
+
*
|
|
394
|
+
* `homes` — the spellings of the execution environment's home directory a home-anchored entry
|
|
395
|
+
* ({@link READ_DENY_HOME_WORD}) is placed under: the declared home and, where the environment's canonical
|
|
396
|
+
* spelling differs, that one too (the faces judge canonical keys beside the spelled views). Absolute
|
|
397
|
+
* spellings only — any other shape refuses loudly, naming `readDenyHomes`. EMPTY = the environment declares no
|
|
398
|
+
* home: a home-anchored entry then matches its run anywhere — a row that cannot be placed holds wider, never narrower.
|
|
399
|
+
*
|
|
400
|
+
* `linked` — where a home-anchored row's LITERAL HEAD ({@link readDenyRunHead}) really leads, keyed by the row's
|
|
401
|
+
* pattern: the canonical locations a mount resolved (`readDenyHomeLinksOf`, read-deny-home.ts) where one of the
|
|
402
|
+
* head's segments is a link (`~/.sema` linked into a dotfiles directory). The row is placed at each location too —
|
|
403
|
+
* the same row, the same name in every refusal — with the rest of its run matched below it. The read faces judge
|
|
404
|
+
* canonical keys, so every spelling that reaches the linked file meets the row there. Absolute locations only.
|
|
263
405
|
*/
|
|
264
|
-
export declare function compileReadDeny(additions?: readonly ReadDenyEntry[], layer?: string, builtin?: ReadDenyBuiltinConfig): ReadDenyMatcher;
|
|
406
|
+
export declare function compileReadDeny(additions?: readonly ReadDenyEntry[], layer?: string, builtin?: ReadDenyBuiltinConfig, rules?: readonly PathRuleDenyRow[], homes?: readonly string[], linked?: ReadonlyMap<string, readonly string[]>): ReadDenyMatcher;
|