@sema-agent/core 7.46.0 → 7.47.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 +52 -0
- package/dist/core/allow-entry.d.ts +1 -1
- package/dist/core/allow-entry.js +3 -4
- package/dist/core/ask-origin.d.ts +35 -12
- package/dist/core/ask-origin.js +23 -6
- package/dist/core/engine-notice.d.ts +7 -4
- package/dist/core/gate-fold.js +2 -2
- package/dist/core/gate-lanes.js +2 -1
- package/dist/core/gate-outcome.d.ts +4 -2
- package/dist/core/gate-outcome.js +2 -2
- package/dist/core/permission-rule-model.d.ts +12 -6
- package/dist/core/permission-rule-model.js +10 -2
- package/dist/core/permission-rules.d.ts +4 -5
- package/dist/core/permission-rules.js +28 -15
- package/dist/core/runner/gate-exit.d.ts +4 -1
- package/dist/core/runner/gate-exit.js +5 -1
- package/dist/core/runner/prepare-hands-readface.js +5 -5
- package/dist/core/runner/prepare-policy-chain.js +2 -1
- package/dist/core/shell-wrapper-projection.js +1 -1
- package/dist/core/shell-wrapper-table.d.ts +6 -6
- package/dist/core/shell-wrapper-table.js +2 -2
- package/dist/core/tool-catalog-entries.js +3 -2
- package/dist/core/tool-face.d.ts +10 -1
- package/dist/core/tool-name-readings.d.ts +8 -0
- package/dist/core/tool-name-readings.js +2 -0
- package/dist/core/tool-policy.d.ts +10 -9
- package/dist/core/tool-policy.js +2 -2
- package/dist/core/tool-registry.d.ts +9 -0
- package/dist/core/tool-registry.js +10 -0
- package/dist/core/write-protect.d.ts +10 -3
- package/dist/core/write-protect.js +11 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/shell/lex.d.ts +15 -0
- package/dist/shell/lex.js +58 -13
- package/dist/tools/fs/bash-program-position.d.ts +9 -6
- package/dist/tools/fs/bash-program-position.js +13 -10
- package/dist/tools/fs/program-read-model.d.ts +24 -7
- package/dist/tools/fs/program-read-model.js +7 -0
- package/dist/tools/fs/program-read-model.rows.js +18 -7
- package/dist/tools/fs/removal-targets.js +14 -4
- package/dist/tools/fs/shell-read-argv.d.ts +5 -4
- package/dist/tools/fs/shell-read-argv.js +5 -3
- package/dist/tools/fs/shell-read-bindings.d.ts +46 -19
- package/dist/tools/fs/shell-read-bindings.js +38 -29
- package/dist/tools/fs/shell-read-establish.js +3 -3
- package/dist/tools/fs/shell-read-plan.d.ts +2 -1
- package/dist/tools/fs/shell-read-plan.js +32 -36
- package/dist/tools/fs/shell-read-word.d.ts +38 -7
- package/dist/tools/fs/shell-read-word.js +26 -8
- package/dist/tools/fs/shell-read-write-targets.js +4 -3
- package/dist/tools/fs/shell-reentry-text.d.ts +98 -0
- package/dist/tools/fs/shell-reentry-text.js +115 -2
- package/dist/tools/monitor.d.ts +3 -3
- package/package.json +1 -1
- package/test/export-surface.snapshot.json +9 -3
- package/wire-changes/core@7.47.0.json +412 -0
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
import { MCP_NAMESPACE, protocolOf } from "./protocol-table.js";
|
|
2
2
|
import { splitNamespacedName } from "./tool-name-aliases.js";
|
|
3
|
-
import { catalogRuleFaceOf, pathTargetOf } from "./tool-registry.js";
|
|
3
|
+
import { catalogRuleFaceOf, isShellFaceToolName, pathTargetOf } from "./tool-registry.js";
|
|
4
4
|
import { indexOfUnescaped, lastIndexOfUnescaped, parsePermissionRule } from "./permission-rule-syntax.js";
|
|
5
|
-
import { NAME_RULE_FACES, PATH_RULE_BASE_LABEL, PATH_RULE_FACES, callPathFaceOf, isUsablePathBase, nameRuleAdmits, nameRuleSubjectOf, parseRuleText, programRunReachOf, ruleBasesNeeded, ruleToolGrammarOf } from "./permission-rule-model.js";
|
|
5
|
+
import { COMMAND_RULE_TOOL, NAME_RULE_FACES, PATH_RULE_BASE_LABEL, PATH_RULE_FACES, callPathFaceOf, isUsablePathBase, laneToolNamesOf, nameRuleAdmits, nameRuleSubjectOf, parseRuleText, programRunReachOf, ruleBasesNeeded, ruleToolGrammarOf } from "./permission-rule-model.js";
|
|
6
6
|
import { pathFamilyOf } from "../tools/fs/safety.js";
|
|
7
7
|
import { callTargetReadingOf, callTargetViewsOf } from "./effective-path-target.js";
|
|
8
8
|
import { bindPathRules, pathRowsReach, policyAnchors, rowsWhereTheyLead } from "./path-rule-binding.js";
|
|
9
9
|
import { ruleDenialSentence } from "./rule-denial-sentence.js";
|
|
10
10
|
import { heldValues } from "./tool-name-readings.js";
|
|
11
11
|
export { parsePermissionRule };
|
|
12
|
-
export const BASH_GENERIC_PARAMS = new Set(catalogRuleFaceOf("Bash")?.params ?? []);
|
|
13
12
|
function describeBehavior(v) {
|
|
14
13
|
switch (typeof v) {
|
|
15
14
|
case "undefined": return "undefined";
|
|
@@ -110,13 +109,14 @@ function compile(rules, caps, primaryFieldGeneric, ruleFaces, pathBases = {}) {
|
|
|
110
109
|
return { face, judged: face !== undefined };
|
|
111
110
|
};
|
|
112
111
|
const byTool = new Map();
|
|
112
|
+
const command = { deny: [], ask: [] };
|
|
113
113
|
const namespacedCovering = { deny: [], ask: [] };
|
|
114
114
|
const issues = [];
|
|
115
115
|
const bad = (rule, code, message) => {
|
|
116
116
|
issues.push({ rule, code, message });
|
|
117
117
|
};
|
|
118
118
|
if (rules.length > caps.maxRules) {
|
|
119
|
-
return { byTool, namespacedCovering, issues: [{ rule: "", code: "invalid.cap_exceeded", message: `${rules.length} rules > maxRules ${caps.maxRules}` }] };
|
|
119
|
+
return { byTool, command, namespacedCovering, issues: [{ rule: "", code: "invalid.cap_exceeded", message: `${rules.length} rules > maxRules ${caps.maxRules}` }] };
|
|
120
120
|
}
|
|
121
121
|
for (const r of rules) {
|
|
122
122
|
const text = r.rule;
|
|
@@ -174,17 +174,24 @@ function compile(rules, caps, primaryFieldGeneric, ruleFaces, pathBases = {}) {
|
|
|
174
174
|
}
|
|
175
175
|
const content = parsed.ruleContent;
|
|
176
176
|
const colon = content.indexOf(":");
|
|
177
|
-
if (toolName
|
|
177
|
+
if (isShellFaceToolName(toolName)) {
|
|
178
178
|
const paramPart = colon > 0 ? content.slice(0, colon).trim() : "";
|
|
179
|
-
const
|
|
180
|
-
|
|
179
|
+
const declaresParam = (tool) => colon > 0 && /^[A-Za-z_][A-Za-z0-9_]*$/.test(paramPart) && (catalogRuleFaceOf(tool)?.params ?? []).includes(paramPart);
|
|
180
|
+
const looksLikeParamRule = declaresParam(toolName);
|
|
181
|
+
if (toolName !== COMMAND_RULE_TOOL && !looksLikeParamRule) {
|
|
182
|
+
const commandWordParam = declaresParam(COMMAND_RULE_TOOL) && !content.endsWith(":*");
|
|
183
|
+
bad(text, "invalid.command_rule", commandWordParam
|
|
184
|
+
? `"${text}" is a parameter rule on "${paramPart}", which ${toolName} does not declare (its rule parameters: ${(catalogRuleFaceOf(toolName)?.params ?? []).join(", ")}); a parameter rule reaches only the tool that declares the parameter, so ${COMMAND_RULE_TOOL}(${content}) is a rule on ${COMMAND_RULE_TOOL}'s own argument and does not reach ${toolName}. A shell command rule is written ${COMMAND_RULE_TOOL}(<command>) — it reaches ${toolName} and every other tool that runs a shell command by the command it runs.`
|
|
185
|
+
: `a shell command rule is written ${COMMAND_RULE_TOOL}(…) — it reaches ${toolName} and every other tool that runs a shell command by the command it runs; "${text}" names no rule upstream reads. Write ${COMMAND_RULE_TOOL}(${content}).`);
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
if (toolName === COMMAND_RULE_TOOL && (!looksLikeParamRule || content.endsWith(":*"))) {
|
|
181
189
|
const parsedCommand = parseRuleText(text, verb);
|
|
182
190
|
if ("reject" in parsedCommand) {
|
|
183
191
|
bad(text, "invalid.command_rule", `${parsedCommand.reject.code}: ${parsedCommand.reject.message}`);
|
|
184
192
|
continue;
|
|
185
193
|
}
|
|
186
|
-
|
|
187
|
-
byTool.set(toolName, entry);
|
|
194
|
+
command[verb].push({ parsed: parsedCommand.rule, ruleText: text, ...(r.source !== undefined ? { source: r.source } : {}) });
|
|
188
195
|
continue;
|
|
189
196
|
}
|
|
190
197
|
}
|
|
@@ -264,7 +271,7 @@ function compile(rules, caps, primaryFieldGeneric, ruleFaces, pathBases = {}) {
|
|
|
264
271
|
entry.param[verb].push({ param, pattern, behavior: verb, ruleText: text, source: r.source });
|
|
265
272
|
byTool.set(toolName, entry);
|
|
266
273
|
}
|
|
267
|
-
return { byTool, namespacedCovering, issues };
|
|
274
|
+
return { byTool, command, namespacedCovering, issues };
|
|
268
275
|
}
|
|
269
276
|
export function validatePermissionRules(rules, opts) {
|
|
270
277
|
const caps = { ...DEFAULT_CAPS, ...opts?.caps };
|
|
@@ -303,7 +310,7 @@ const NO_LEADS = new Map();
|
|
|
303
310
|
export function createPermissionRulePolicy(rules, opts) {
|
|
304
311
|
const caps = { ...DEFAULT_CAPS, ...opts?.caps };
|
|
305
312
|
const pathBases = opts?.pathBases ?? {};
|
|
306
|
-
const { byTool, namespacedCovering, issues } = compile(rules, caps, opts?.primaryFieldGeneric ?? "reject", opts?.ruleFaces, pathBases);
|
|
313
|
+
const { byTool, command, namespacedCovering, issues } = compile(rules, caps, opts?.primaryFieldGeneric ?? "reject", opts?.ruleFaces, pathBases);
|
|
307
314
|
const setLevelCap = issues.some((i) => i.code === "invalid.cap_exceeded" && i.rule === "");
|
|
308
315
|
if (issues.length > 0 && ((opts?.onInvalidRule ?? "throw") === "throw" || setLevelCap)) {
|
|
309
316
|
const e = new Error(`createPermissionRulePolicy: ${issues.length} invalid/unsupported rule(s):\n` +
|
|
@@ -335,6 +342,9 @@ export function createPermissionRulePolicy(rules, opts) {
|
|
|
335
342
|
if (entry.bare.ask !== undefined || entry.param.ask.length > 0 || entry.content.ask.length > 0)
|
|
336
343
|
nameSets.ask.push(toolName);
|
|
337
344
|
}
|
|
345
|
+
for (const behavior of ["deny", "ask"])
|
|
346
|
+
if (command[behavior].length > 0 && !nameSets[behavior].includes(COMMAND_RULE_TOOL))
|
|
347
|
+
nameSets[behavior].push(COMMAND_RULE_TOOL);
|
|
338
348
|
for (const behavior of ["deny", "ask"]) {
|
|
339
349
|
for (const r of namespacedCovering[behavior])
|
|
340
350
|
nameSets[behavior].push(r.ruleName);
|
|
@@ -345,11 +355,13 @@ export function createPermissionRulePolicy(rules, opts) {
|
|
|
345
355
|
deny: [...byTool.values()].flatMap((e) => e.content.deny.filter((r) => r.faces !== undefined)),
|
|
346
356
|
ask: [...byTool.values()].flatMap((e) => e.content.ask.filter((r) => r.faces !== undefined)),
|
|
347
357
|
};
|
|
348
|
-
const contentLaneOf = (behavior, entry, callFace) => [
|
|
358
|
+
const contentLaneOf = (behavior, entry, shellCall, callFace) => [
|
|
349
359
|
...(entry?.content[behavior].filter((r) => r.faces === undefined) ?? []),
|
|
360
|
+
...(shellCall ? command[behavior] : []),
|
|
350
361
|
...(callFace !== undefined ? faceRules[behavior].filter((r) => r.faces.includes(callFace)) : []),
|
|
351
362
|
];
|
|
352
|
-
const
|
|
363
|
+
const hasCommandRules = command.deny.length > 0 || command.ask.length > 0;
|
|
364
|
+
const commandParam = catalogRuleFaceOf(COMMAND_RULE_TOOL)?.primaryParams[0] ?? "command";
|
|
353
365
|
const pathRuleOf = new Map();
|
|
354
366
|
for (const e of byTool.values()) {
|
|
355
367
|
for (const behavior of ["deny", "ask"]) {
|
|
@@ -450,7 +462,8 @@ export function createPermissionRulePolicy(rules, opts) {
|
|
|
450
462
|
return { action: "deny", message: ruleDenial(req, coveringDeny.ruleText, coveringDeny.source) };
|
|
451
463
|
}
|
|
452
464
|
const callFace = faceRules.deny.length > 0 || faceRules.ask.length > 0 ? callPathFaceOf(req) : undefined;
|
|
453
|
-
const
|
|
465
|
+
const shellCall = hasCommandRules && laneToolNamesOf(req).includes(COMMAND_RULE_TOOL);
|
|
466
|
+
const denyLane = contentLaneOf("deny", entry, shellCall, callFace);
|
|
454
467
|
const contentDeny = denyLane.length > 0 ? contentHit(denyLane, req) : undefined;
|
|
455
468
|
if (contentDeny !== undefined && contentDeny.unreadable === undefined) {
|
|
456
469
|
return { action: "deny", message: ruleDenial(req, contentDeny.rule.ruleText, contentDeny.rule.source) };
|
|
@@ -473,7 +486,7 @@ export function createPermissionRulePolicy(rules, opts) {
|
|
|
473
486
|
if (coveringAsk)
|
|
474
487
|
return { action: "ask", message: ruleAskMessage(coveringAsk.ruleText, coveringAsk.source), matchedAskRule: coveringAsk.ruleText };
|
|
475
488
|
}
|
|
476
|
-
const askLane = contentLaneOf("ask", entry, callFace);
|
|
489
|
+
const askLane = contentLaneOf("ask", entry, shellCall, callFace);
|
|
477
490
|
const contentAsk = askLane.length > 0 ? contentHit(askLane, req) : undefined;
|
|
478
491
|
if (contentAsk !== undefined) {
|
|
479
492
|
if (contentAsk.unreadable !== undefined)
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
*/
|
|
24
24
|
import { type ClassifierDenyCause, type DeniedBy, type GateOutcome, type ModeRefusal, type ModeRelease, type Settlement } from "../gate-outcome.js";
|
|
25
25
|
import type { AutoModeClassifierRound } from "../auto-mode.js";
|
|
26
|
-
import { type Arbitration, type AskClassification, type AskOrigin, type AskOriginFacts } from "../ask-origin.js";
|
|
26
|
+
import { type Arbitration, type AskClassification, type AskFace, type AskOrigin, type AskOriginFacts } from "../ask-origin.js";
|
|
27
27
|
import { type PermissionResult, type ResolvedAsk, type ToolCallRequest } from "../tool-policy.js";
|
|
28
28
|
import type { SafeNotifier } from "../safe-notify.js";
|
|
29
29
|
import type { HookSeatOutcome, PermissionDeniedPayload, PreToolUseResult, ToolGateInput, ToolGateResult } from "../hooks.js";
|
|
@@ -143,6 +143,9 @@ export interface PreToolUseScreening {
|
|
|
143
143
|
/** The model-facing text for an answer the decision receipt refused (an ENGINE fail-closed, not the face's deny). */
|
|
144
144
|
refusedReason(answer: PreToolUseResult): string;
|
|
145
145
|
}
|
|
146
|
+
/** The FACE a pass's questions are put on ({@link askFaceOf} of the call's wire name) — the one reading of it the
|
|
147
|
+
* write-protection tighten (the fold) and every class derivation (the lanes) make. */
|
|
148
|
+
export declare function passAskFace(pass: Pick<GatePass, "req">): AskFace;
|
|
146
149
|
/**
|
|
147
150
|
* ONE gate pass — the record the station groups (`runGateFold`, `runGateLanes`) and the exits read and write,
|
|
148
151
|
* built once per call by `runToolGate` before the screening pass. What used to be the closure-captured `let`
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { screenGateOutcome, SETTLEMENT_IS_REFUSAL } from "../gate-outcome.js";
|
|
2
|
-
import { classifyAsk, headlessApprovalRemedy } from "../ask-origin.js";
|
|
2
|
+
import { askFaceOf, classifyAsk, headlessApprovalRemedy } from "../ask-origin.js";
|
|
3
|
+
import { wireOf } from "../tool-name-readings.js";
|
|
3
4
|
import { isProtectedWrite } from "../tool-registry.js";
|
|
4
5
|
import { askUnresolvableCauseOf, classifierDenyCauseOf, classifierRoundOf, decisionText, engineSettlementOf } from "../tool-policy.js";
|
|
5
6
|
import { askUnresolvableRemedySeat } from "../ask-unresolvable-notice.js";
|
|
@@ -97,6 +98,9 @@ function withParkFailureCause(reason, parkFailed) {
|
|
|
97
98
|
`(${inlineUntrusted(parkFailed, PARK_FAILURE_CAUSE_MAX)}), so the refusal above is what the fallback had ` +
|
|
98
99
|
`left to say, not the reason the call stopped.`);
|
|
99
100
|
}
|
|
101
|
+
export function passAskFace(pass) {
|
|
102
|
+
return askFaceOf(wireOf("gate.askFace", pass.req));
|
|
103
|
+
}
|
|
100
104
|
export async function engineFailClosedExit(pass, reason) {
|
|
101
105
|
const { input, toolName, toolCallId, notifyPermissionDenied } = pass;
|
|
102
106
|
const gate = directDeny("hook");
|
|
@@ -8,7 +8,7 @@ import { placeReadDenyAtHome } from "../../tools/fs/read-deny-home.js";
|
|
|
8
8
|
import { createShellOverflowSpoolFence } from "../../tools/fs/fs-shared.js";
|
|
9
9
|
import { createShellReadStation } from "../../tools/fs/shell-read-check.js";
|
|
10
10
|
import { composeReadChecks, createFileReadStation } from "../../tools/fs/file-read-check.js";
|
|
11
|
-
import { readTargetToolNames } from "../tool-registry.js";
|
|
11
|
+
import { isShellFaceToolName, predictMountedToolNames, readTargetToolNames } from "../tool-registry.js";
|
|
12
12
|
import { normalizeFileText } from "../../tools/fs/encoding.js";
|
|
13
13
|
import { wholeFileRecordsFromTranscript } from "./session-file-state-replay.js";
|
|
14
14
|
import { deliverEngineNotice } from "../types.js";
|
|
@@ -203,6 +203,7 @@ export async function prepareHandsMount(input) {
|
|
|
203
203
|
const handsIncludeShell = !(executionEnv instanceof StubExecutionEnv);
|
|
204
204
|
const handsReadOnly = spec.handsReadOnly === true;
|
|
205
205
|
backgroundTaskToolsActive = handsIncludeShell && !handsReadOnly && hasBackgroundShell(executionEnv);
|
|
206
|
+
const shellFaceHere = [...predictMountedToolNames(new Set(backgroundTaskToolsActive ? ["hands", "backgroundTasks"] : ["hands"]))].filter(isShellFaceToolName);
|
|
206
207
|
wsSnapshot = resume !== undefined || !(handsIncludeShell && !handsReadOnly) ? undefined : await session.getWorkspaceState().catch(() => undefined);
|
|
207
208
|
rebaseWsPath = (p) => (wsSnapshot === undefined || wsSnapshot.taskRoot === rootCanonical ? p : rebaseWorkspacePath(p, wsSnapshot.taskRoot, rootCanonical));
|
|
208
209
|
if (handsIncludeShell && !handsReadOnly) {
|
|
@@ -307,7 +308,7 @@ export async function prepareHandsMount(input) {
|
|
|
307
308
|
if (handsIncludeShell) {
|
|
308
309
|
const spoolFence = handsReadOnly ? createShellOverflowSpoolFence(executionEnv) : undefined;
|
|
309
310
|
const readonlyBoundary = () => spoolFence !== undefined ? { roots: [rootCanonical, ...additionalRootsCanonical, ...additionalReadRootsCanonical], cwd: rootCanonical, ...denySeat, ...writeDenySeat, alsoInside: spoolFence } : undefined;
|
|
310
|
-
shellReadTools = new Set(
|
|
311
|
+
shellReadTools = new Set(shellFaceHere);
|
|
311
312
|
shellReadStation = createShellReadStation({
|
|
312
313
|
env: executionEnv,
|
|
313
314
|
tools: shellReadTools,
|
|
@@ -400,9 +401,8 @@ export async function prepareHandsMount(input) {
|
|
|
400
401
|
if (seatOwned)
|
|
401
402
|
shellGatedTools.add(name);
|
|
402
403
|
};
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
installShellSeat("Monitor");
|
|
404
|
+
for (const name of shellFaceHere)
|
|
405
|
+
installShellSeat(name);
|
|
406
406
|
}
|
|
407
407
|
}
|
|
408
408
|
return {
|
|
@@ -14,7 +14,7 @@ import { emitTrace } from "../trace.js";
|
|
|
14
14
|
import { skillGrantBindingMembers } from "../skill-grant.js";
|
|
15
15
|
import { SKILL_TOOL_NAME } from "./synthetic-tools.js";
|
|
16
16
|
import { autoStrippedRule, cardDisclosureOf } from "../allow-entry.js";
|
|
17
|
-
import { arbitrateByMode, askClassMembersOf, askMandateMember } from "../ask-origin.js";
|
|
17
|
+
import { arbitrateByMode, askClassMembersOf, askFaceOf, askMandateMember } from "../ask-origin.js";
|
|
18
18
|
import { createModeGatePolicy, mountedToolDeclaresReadOnly } from "../mode-policy.js";
|
|
19
19
|
const ancestorFacts = (ask) => ({ ...(ask ?? {}), ancestorResolved: true });
|
|
20
20
|
import { headlessDenyAtFold, headlessDenyAtRecheck, INHERITED_STATION_FACTS, inheritedAskCarry, inheritedAskFallbackMessage, judgeInheritedClassifier, lateAskSettlementObserver, resolveInheritedAsk, settleDenialLimitFallback } from "./denial-limit-arms.js";
|
|
@@ -313,6 +313,7 @@ export async function preparePolicyChain(input) {
|
|
|
313
313
|
...(seat.sources.size > 0 ? { toolMarkSources: seat.sources } : {}),
|
|
314
314
|
...(deps.a2a?.isolatePeers === true ? { a2aIsolatePeers: true } : {}),
|
|
315
315
|
...(face !== undefined ? { readFace: face } : {}),
|
|
316
|
+
askFace: askFaceOf(wireOf("inherited.askFace", call)),
|
|
316
317
|
};
|
|
317
318
|
};
|
|
318
319
|
const inheritedAskEvidence = inheritedAskRuleEvidence(deps.permissionRuleStore);
|
|
@@ -30,7 +30,7 @@ const runsCommand = (spec) => spec.effect === "executes" || spec.value?.kind ===
|
|
|
30
30
|
function projectRow(row) {
|
|
31
31
|
const sorted = (xs) => [...new Set(xs)].sort();
|
|
32
32
|
const all = spellings(row);
|
|
33
|
-
const viaShell = row.reentry?.kind === "shell" || row.launcher?.shellText
|
|
33
|
+
const viaShell = row.reentry?.kind === "shell" || row.launcher?.shellText !== undefined || row.body === "first-operand" || row.body === "assignment-values" || (row.operands?.rest?.kind === "program-text" && row.operands.rest.language === "arithmetic");
|
|
34
34
|
if (row.launcher === undefined) {
|
|
35
35
|
if (viaShell)
|
|
36
36
|
return { valueFlags: [], flags: [], commandString: [], viaShell: true };
|
|
@@ -276,15 +276,15 @@ export declare const SHELL_WRAPPER_TABLE: {
|
|
|
276
276
|
readonly commandString: readonly [];
|
|
277
277
|
};
|
|
278
278
|
readonly su: {
|
|
279
|
-
readonly valueFlags: readonly [];
|
|
280
|
-
readonly flags: readonly [];
|
|
281
|
-
readonly commandString: readonly [];
|
|
279
|
+
readonly valueFlags: readonly ["--group", "--shell", "--supp-group", "--whitelist-environment", "-G", "-g", "-s", "-w"];
|
|
280
|
+
readonly flags: readonly ["--fast", "--help", "--login", "--no-pty", "--preserve-environment", "--pty", "--version", "-P", "-T", "-V", "-f", "-h", "-l", "-m", "-p"];
|
|
281
|
+
readonly commandString: readonly ["--command", "--session-command", "-c"];
|
|
282
282
|
readonly viaShell: true;
|
|
283
283
|
};
|
|
284
284
|
readonly runuser: {
|
|
285
|
-
readonly valueFlags: readonly [];
|
|
286
|
-
readonly flags: readonly [];
|
|
287
|
-
readonly commandString: readonly [];
|
|
285
|
+
readonly valueFlags: readonly ["--group", "--shell", "--supp-group", "--user", "--whitelist-environment", "-G", "-g", "-s", "-u", "-w"];
|
|
286
|
+
readonly flags: readonly ["--fast", "--help", "--login", "--no-pty", "--preserve-environment", "--pty", "--version", "-P", "-T", "-V", "-f", "-h", "-l", "-m", "-p"];
|
|
287
|
+
readonly commandString: readonly ["--command", "--session-command", "-c"];
|
|
288
288
|
readonly viaShell: true;
|
|
289
289
|
};
|
|
290
290
|
readonly chroot: {
|
|
@@ -43,8 +43,8 @@ export const SHELL_WRAPPER_TABLE = {
|
|
|
43
43
|
compgen: { valueFlags: [], flags: [], commandString: [], executes: ["-C", "-F"] },
|
|
44
44
|
complete: { valueFlags: [], flags: [], commandString: [], executes: ["-C", "-F"] },
|
|
45
45
|
busybox: { valueFlags: [], flags: [], commandString: [] },
|
|
46
|
-
su: { valueFlags: [], flags: [], commandString: [], viaShell: true },
|
|
47
|
-
runuser: { valueFlags: [], flags: [], commandString: [], viaShell: true },
|
|
46
|
+
su: { valueFlags: ["--group", "--shell", "--supp-group", "--whitelist-environment", "-G", "-g", "-s", "-w"], flags: ["--fast", "--help", "--login", "--no-pty", "--preserve-environment", "--pty", "--version", "-P", "-T", "-V", "-f", "-h", "-l", "-m", "-p"], commandString: ["--command", "--session-command", "-c"], viaShell: true },
|
|
47
|
+
runuser: { valueFlags: ["--group", "--shell", "--supp-group", "--user", "--whitelist-environment", "-G", "-g", "-s", "-u", "-w"], flags: ["--fast", "--help", "--login", "--no-pty", "--preserve-environment", "--pty", "--version", "-P", "-T", "-V", "-f", "-h", "-l", "-m", "-p"], commandString: ["--command", "--session-command", "-c"], viaShell: true },
|
|
48
48
|
chroot: { valueFlags: [], flags: [], commandString: [], positional: /^/ },
|
|
49
49
|
setpriv: { valueFlags: [], flags: [], commandString: [] },
|
|
50
50
|
numactl: { valueFlags: [], flags: [], commandString: [] },
|
|
@@ -8,7 +8,8 @@ export const TASK_LIST_TOOL_NAME = "TaskList";
|
|
|
8
8
|
export const TODO_WRITE_TOOL_NAME = "TodoWrite";
|
|
9
9
|
export const REFRESH_MCP_TOOLS_TOOL_NAME = "RefreshMcpTools";
|
|
10
10
|
export const ARTIFACT_TOOL_NAME = "Artifact";
|
|
11
|
-
const
|
|
11
|
+
const shellRuleFace = (params) => ({ grammar: "command", primaryParams: ["command"], params });
|
|
12
|
+
const SHELL_RULE_FACE = shellRuleFace(["command", "timeout", "description", "run_in_background"]);
|
|
12
13
|
const c = (contractId, implementationRevision = "1") => ({ contractId, implementationRevision });
|
|
13
14
|
export const TOOL_CATALOG_ENTRIES = [
|
|
14
15
|
{
|
|
@@ -108,7 +109,7 @@ export const TOOL_CATALOG_ENTRIES = [
|
|
|
108
109
|
{ id: "task-stop-hands", name: "TaskStop", source: "builtin", definedIn: "src/tools/fs/fs-bash.ts", mountedBy: ["hands"], cards: [], face: { family: "delegate", effect: "write", contract: TASK_STOP_CONTRACT } },
|
|
109
110
|
{ id: "task-output", name: "TaskOutput", source: "builtin", definedIn: "src/core/task-registry.ts", mountedBy: ["delegation"], cards: ["task-output"], face: { family: "delegate", effect: "read", contract: TASK_OUTPUT_CONTRACT } },
|
|
110
111
|
{ id: "task-stop", name: "TaskStop", source: "builtin", definedIn: "src/core/task-registry.ts", mountedBy: ["delegation"], cards: ["task-stop"], face: { family: "delegate", effect: "write", contract: TASK_STOP_CONTRACT } },
|
|
111
|
-
{ id: "monitor", name: "Monitor", source: "builtin", definedIn: "src/tools/monitor.ts", mountedBy: ["backgroundTasks"], cards: ["monitor-start", "readonly_out_of_root", "read_unestablished"], face: { family: "other", effect: "write", contract: c("core.monitor@1"), offloadThresholdChars: 10_000 } },
|
|
112
|
+
{ id: "monitor", name: "Monitor", source: "builtin", definedIn: "src/tools/monitor.ts", mountedBy: ["backgroundTasks"], cards: ["monitor-start", "readonly_out_of_root", "read_unestablished"], face: { family: "other", effect: "write", contract: c("core.monitor@1"), ruleFace: shellRuleFace(["command", "description", "timeout_ms", "persistent"]), offloadThresholdChars: 10_000 } },
|
|
112
113
|
{ id: "send-message", name: "SendMessage", source: "builtin", definedIn: "src/agents/send-message-tool.ts", mountedBy: ["delegation", "hostInternals", "peerLane"], cards: ["send-message"], face: { family: "protocol", effect: "write", contract: c("core.send_message@1") } },
|
|
113
114
|
{ id: "agent-transcript", name: "AgentTranscript", source: "builtin", definedIn: "src/agents/agent-transcript-tool.ts", mountedBy: ["delegation"], cards: ["agent-transcript"], face: { family: "delegate", effect: "read", contract: c("core.agent_transcript@1") } },
|
|
114
115
|
{ id: "list-agents", name: "ListAgents", source: "builtin", definedIn: "src/agents/list-agents-tool.ts", mountedBy: ["delegation", "peerLane"], cards: ["list-agents"], face: { family: "protocol", effect: "read", aliases: ["ListPeers"], contract: c("core.list_agents@1") } },
|
package/dist/core/tool-face.d.ts
CHANGED
|
@@ -201,7 +201,16 @@ export interface ToolFaceDeclaration {
|
|
|
201
201
|
};
|
|
202
202
|
aliases?: string[];
|
|
203
203
|
pathTarget?: ToolPathTarget;
|
|
204
|
-
|
|
204
|
+
/**
|
|
205
|
+
* The rule face ({@link ToolRuleFace}) and, for a definition whose primary parameter is a shell command line, the grammar
|
|
206
|
+
* that judges it — `grammar: "command"`, the SHELL FACE. Every `Bash(<command>)` rule (the command grammar's one spelling,
|
|
207
|
+
* deny / ask / allow, from every source) is judged on a call that answers to the name of such a definition, against that
|
|
208
|
+
* parameter; a bare `Bash` rule stays a rule on the tool of that name. Only a catalog row declares it (a caller's
|
|
209
|
+
* `ToolSpec.ruleFace` is a {@link ToolRuleFace}): the static catalog is the one place the shell face is read from.
|
|
210
|
+
*/
|
|
211
|
+
ruleFace?: ToolRuleFace & {
|
|
212
|
+
readonly grammar?: "command";
|
|
213
|
+
};
|
|
205
214
|
renderHints?: ToolRenderHints;
|
|
206
215
|
offload?: boolean;
|
|
207
216
|
offloadThresholdChars?: number;
|
|
@@ -69,6 +69,10 @@ export declare const NAME_READINGS: {
|
|
|
69
69
|
readonly kind: "wire";
|
|
70
70
|
readonly file: string;
|
|
71
71
|
};
|
|
72
|
+
readonly "gate.askFace": {
|
|
73
|
+
readonly kind: "wire";
|
|
74
|
+
readonly file: string;
|
|
75
|
+
};
|
|
72
76
|
readonly "fold.readStation": {
|
|
73
77
|
readonly kind: "seat";
|
|
74
78
|
readonly rank: "member";
|
|
@@ -251,6 +255,10 @@ export declare const NAME_READINGS: {
|
|
|
251
255
|
readonly kind: "wire";
|
|
252
256
|
readonly file: string;
|
|
253
257
|
};
|
|
258
|
+
readonly "inherited.askFace": {
|
|
259
|
+
readonly kind: "wire";
|
|
260
|
+
readonly file: string;
|
|
261
|
+
};
|
|
254
262
|
readonly "park.contentKindProjection": {
|
|
255
263
|
readonly kind: "wire";
|
|
256
264
|
readonly file: string;
|
|
@@ -19,6 +19,7 @@ export const NAME_READINGS = {
|
|
|
19
19
|
"gate.sandboxBoundary.egress": any(CHAIN),
|
|
20
20
|
"gate.sandboxBoundary.hand": wire(CHAIN),
|
|
21
21
|
"gate.preToolUse": wire("src/core/hooks.ts"),
|
|
22
|
+
"gate.askFace": wire("src/core/runner/gate-exit.ts"),
|
|
22
23
|
"fold.readStation": seat("member", "src/tools/fs/shell-read-check.ts"),
|
|
23
24
|
"lanes.contentQuestionClass": any(LANES),
|
|
24
25
|
"lanes.readOnlyShellAllow": wire(LANES),
|
|
@@ -63,6 +64,7 @@ export const NAME_READINGS = {
|
|
|
63
64
|
"activity.sink": spelling(HANDLERS),
|
|
64
65
|
"reconcile.orphanEffect": spelling("src/core/session-reconcile.ts"),
|
|
65
66
|
"inherited.questionFloat": wire(CHAIN),
|
|
67
|
+
"inherited.askFace": wire(CHAIN),
|
|
66
68
|
"park.contentKindProjection": wire("src/core/checkpoint-store.ts"),
|
|
67
69
|
"resume.contentAskRow": wire("src/core/runner/decide-continuation.ts"),
|
|
68
70
|
"resume.editRecheckQuestionSkip": wire("src/core/runner/resume-apply.ts"),
|
|
@@ -977,13 +977,14 @@ export declare function createApprovalPolicy(opts: {
|
|
|
977
977
|
*/
|
|
978
978
|
export declare function combinePolicies(...policies: ToolPolicy[]): ToolPolicy;
|
|
979
979
|
/**
|
|
980
|
-
* The shell tools whose `{ command }` arg a coarse command-name policy parses; other tools are out of scope.
|
|
981
|
-
*
|
|
982
|
-
*
|
|
983
|
-
*
|
|
984
|
-
*
|
|
980
|
+
* The shell tools whose `{ command }` arg a coarse command-name policy parses; other tools are out of scope. It is the
|
|
981
|
+
* catalog's shell face ({@link shellFaceToolNames} — every definition whose `command` the command grammar judges, `Monitor`
|
|
982
|
+
* among them: it runs an arbitrary shell `command` through the same env seam as Bash), spelled out for the wire; the
|
|
983
|
+
* catalog's shell face is the truth, and a lockstep pin keeps the two one (a catalog row added or dropped turns it red):
|
|
984
|
+
* the always-on parity gates keyed on it, the `Bash(...)` rules and the shell seat reach the same tools. EXPORTED: a
|
|
985
|
+
* downstream governance layer reads it so its per-command ask-list gates the same tools core does.
|
|
985
986
|
*/
|
|
986
|
-
export declare const COARSE_SHELL_TOOLS:
|
|
987
|
+
export declare const COARSE_SHELL_TOOLS: readonly ["Bash", "Monitor"];
|
|
987
988
|
/**
|
|
988
989
|
* design/center §10 — a COARSE allow/deny gate keyed on the LEADING command NAME (`argv[0]`) of a single
|
|
989
990
|
* simple shell command. It reuses the ONE shared simple-command parser
|
|
@@ -1033,7 +1034,7 @@ export declare function createCoarseCommandNamePolicy(opts: {
|
|
|
1033
1034
|
deny?: string[];
|
|
1034
1035
|
/** What to do for a command not matched by `allow` (or un-parseable): `"ask"` (default) or `"deny"`. */
|
|
1035
1036
|
defaultAction?: "ask" | "deny";
|
|
1036
|
-
/** Override the shell tool names this gate parses (default
|
|
1037
|
+
/** Override the shell tool names this gate parses (default {@link COARSE_SHELL_TOOLS}, the shell face). */
|
|
1037
1038
|
tools?: string[];
|
|
1038
1039
|
}): ToolPolicy;
|
|
1039
1040
|
export declare function findUnverifiableRecursiveDelete(command: string, safeVars?: ReadonlySet<string>): string | undefined;
|
|
@@ -1112,7 +1113,7 @@ export declare function createUnverifiableDeletePolicy(opts?: {
|
|
|
1112
1113
|
* the default was `TMPDIR` alone, reading STRICTER than the gate really is; `HOME`/`PWD` are a
|
|
1113
1114
|
* registered sema divergence from CC's temp-only carve-out, see the RB-340 note there). */
|
|
1114
1115
|
safeVars?: readonly string[];
|
|
1115
|
-
/** Override the shell tool names this gate parses (default
|
|
1116
|
+
/** Override the shell tool names this gate parses (default {@link COARSE_SHELL_TOOLS}, the shell face). */
|
|
1116
1117
|
tools?: string[];
|
|
1117
1118
|
removalSeat?: RemovalGuardSeat;
|
|
1118
1119
|
}): ToolPolicy;
|
|
@@ -1121,7 +1122,7 @@ export declare function createTranscriptIntegrityPolicy(opts?: {
|
|
|
1121
1122
|
sessionsDirs?: readonly string[];
|
|
1122
1123
|
/** argv[0] allowlist for read-only access to the transcript dir (default {@link BASH_READONLY_DEFAULT_ALLOW}). */
|
|
1123
1124
|
readAllow?: readonly string[];
|
|
1124
|
-
/** Override the shell tool names this gate parses (default
|
|
1125
|
+
/** Override the shell tool names this gate parses (default {@link COARSE_SHELL_TOOLS}, the shell face). */
|
|
1125
1126
|
tools?: string[];
|
|
1126
1127
|
}): ToolPolicy;
|
|
1127
1128
|
/**
|
package/dist/core/tool-policy.js
CHANGED
|
@@ -10,7 +10,7 @@ export { policyCannotAnswer, rowDemandsAPerson } from "./real-approval.js";
|
|
|
10
10
|
import { sanitizeAutoModeArmingRecipe } from "./auto-mode-arming.js";
|
|
11
11
|
import { join, normalize as normalizePath, sep } from "node:path";
|
|
12
12
|
import { BASH_READONLY_DEFAULT_ALLOW, parseLeadingCommandName } from "../tools/fs/index.js";
|
|
13
|
-
import { protectivePathTargetOf } from "./tool-registry.js";
|
|
13
|
+
import { protectivePathTargetOf, shellFaceToolNames } from "./tool-registry.js";
|
|
14
14
|
import { heldBy } from "./tool-name-readings.js";
|
|
15
15
|
import { boundInputHashOf } from "./canonical-json.js";
|
|
16
16
|
import { delimitUntrusted, inlineUntrusted, REVIEWER_NOTE_MAX_BODY } from "./untrusted-text.js";
|
|
@@ -642,7 +642,7 @@ export function combinePolicies(...policies) {
|
|
|
642
642
|
},
|
|
643
643
|
};
|
|
644
644
|
}
|
|
645
|
-
export const COARSE_SHELL_TOOLS = ["Bash", "Monitor"];
|
|
645
|
+
export const COARSE_SHELL_TOOLS = Object.freeze(["Bash", "Monitor"]);
|
|
646
646
|
function canonicalToolNameSet(tools) {
|
|
647
647
|
return new Set(tools ?? COARSE_SHELL_TOOLS);
|
|
648
648
|
}
|
|
@@ -25,6 +25,15 @@ export declare function catalogToolNames(): ReadonlySet<string>;
|
|
|
25
25
|
export declare function pathConfinableWriteToolNames(): ReadonlySet<string>;
|
|
26
26
|
/** A-8 — every definition that names a path target at all (read or write): the file-tool glob lane. */
|
|
27
27
|
export declare function pathTargetingToolNames(): ReadonlySet<string>;
|
|
28
|
+
/**
|
|
29
|
+
* The SHELL FACE: every definition whose `command` argument is a shell command line the command grammar judges (today the two
|
|
30
|
+
* `Bash` definitions and `Monitor`). A `Bash(<command>)` rule reaches a call that answers to any of these names; the coarse
|
|
31
|
+
* command-name gate's default list, the shell read station and the shell seat are this set. Derived from the rows' rule faces,
|
|
32
|
+
* never a list of its own.
|
|
33
|
+
*/
|
|
34
|
+
export declare function shellFaceToolNames(): ReadonlySet<string>;
|
|
35
|
+
/** {@link shellFaceToolNames}' membership for one name — any definition under the name on the shell face. */
|
|
36
|
+
export declare function isShellFaceToolName(name: string): boolean;
|
|
28
37
|
/** The tools whose declared path target is a READ (a file read, a search root): the file read station's membership. */
|
|
29
38
|
export declare function readTargetToolNames(): ReadonlySet<string>;
|
|
30
39
|
/** A-10 — the micro-compaction whitelist (`compactable: true`). */
|
|
@@ -29,6 +29,16 @@ export function pathConfinableWriteToolNames() {
|
|
|
29
29
|
export function pathTargetingToolNames() {
|
|
30
30
|
return names((e) => e.face.pathTarget !== undefined);
|
|
31
31
|
}
|
|
32
|
+
const onShellFace = (e) => e.face.ruleFace?.grammar === "command";
|
|
33
|
+
export function shellFaceToolNames() {
|
|
34
|
+
return names(onShellFace);
|
|
35
|
+
}
|
|
36
|
+
export function isShellFaceToolName(name) {
|
|
37
|
+
for (const e of TOOL_CATALOG_ENTRIES)
|
|
38
|
+
if (e.name === name && onShellFace(e))
|
|
39
|
+
return true;
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
32
42
|
export function readTargetToolNames() {
|
|
33
43
|
return names((e) => e.face.pathTarget?.access === "read");
|
|
34
44
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ExecutionEnv } from "../internal/harness-types.js";
|
|
2
2
|
import type { PermissionResult } from "./tool-policy.js";
|
|
3
|
+
import { type AskFace } from "./ask-origin.js";
|
|
3
4
|
import { type ToolCallLike } from "./tool-registry.js";
|
|
4
5
|
/**
|
|
5
6
|
* How one table row matches the judged path:
|
|
@@ -209,11 +210,17 @@ export declare function compileWriteProtection(entries?: readonly WriteProtected
|
|
|
209
210
|
* message (table names are deployment-validated vocabulary); the model-authored target path deliberately
|
|
210
211
|
* does not — `message` is the classifier's `askMessage`, and the approval card already shows the args. The
|
|
211
212
|
* spelling form's sentence is the historical one byte for byte; the target form says the target RESOLVED.
|
|
212
|
-
* · `unresolvable` ⇒
|
|
213
|
-
*
|
|
213
|
+
* · `unresolvable` ⇒ read by the call's FACE (`face`, {@link releasableAskClassesFor}): where no permission mode releases
|
|
214
|
+
* `write_protection_unresolved` (the file face) — `deny` naming the cause: an unjudged target is never vouched (the rule
|
|
215
|
+
* the canonicalizing deny set follows for the same reason), so there a standing allow is refused while a standing ask
|
|
216
|
+
* carrying the record stays a person's question in the modes that ask (dontAsk and bypassPermissions refuse it); where
|
|
217
|
+
* a mode may release the class (the shell face) — `ask` (`safety`)
|
|
218
|
+
* naming the cause, which the mode arbitration answers exactly as it answers that record over a standing ask
|
|
219
|
+
* (bypassPermissions releases it, default puts it to a person, dontAsk refuses): on that face alone a standing allow
|
|
220
|
+
* reads as a standing ask.
|
|
214
221
|
* · `clear` ⇒ `undefined`: the surviving decision stands.
|
|
215
222
|
*/
|
|
216
|
-
export declare function writeProtectionTightenDecision(toolName: string, verdict: WriteProtectionVerdict): PermissionResult | undefined;
|
|
223
|
+
export declare function writeProtectionTightenDecision(toolName: string, verdict: WriteProtectionVerdict, face: AskFace): PermissionResult | undefined;
|
|
217
224
|
/**
|
|
218
225
|
* @contract write_protection.verdict_is_the_mandate — the judge's two non-clear verdicts are the two facts
|
|
219
226
|
* a standing allow rule may not retire, and `clear` is neither: a MATCHED row is the mandate a live decider
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { releasableAskClassesFor } from "./ask-origin.js";
|
|
1
2
|
import { canonicalizeTarget, isWinFormPath, writeTargetPath, isUncPath, isAbsoluteForFamily, joinForFamily, pathFamilyOf } from "../tools/fs/safety.js";
|
|
2
3
|
import { protectivePathTargetOf } from "./tool-registry.js";
|
|
3
4
|
import { pathTargetBaseOf } from "./effective-path-target.js";
|
|
@@ -346,7 +347,7 @@ export function compileWriteProtection(entries, opts) {
|
|
|
346
347
|
},
|
|
347
348
|
};
|
|
348
349
|
}
|
|
349
|
-
export function writeProtectionTightenDecision(toolName, verdict) {
|
|
350
|
+
export function writeProtectionTightenDecision(toolName, verdict, face) {
|
|
350
351
|
if (verdict.kind === "hit") {
|
|
351
352
|
return {
|
|
352
353
|
action: "ask",
|
|
@@ -359,6 +360,15 @@ export function writeProtectionTightenDecision(toolName, verdict) {
|
|
|
359
360
|
};
|
|
360
361
|
}
|
|
361
362
|
if (verdict.kind === "unresolvable") {
|
|
363
|
+
if (releasableAskClassesFor(face).includes("write_protection_unresolved")) {
|
|
364
|
+
return {
|
|
365
|
+
action: "ask",
|
|
366
|
+
message: verdict.unresolvedSymlink
|
|
367
|
+
? `tool "${toolName}" writes to a symlink whose real target could not be resolved, so the write-protection judge cannot tell whether it lands on a protected path (${verdict.cause}) — explicit approval required`
|
|
368
|
+
: `tool "${toolName}" writes to a target that could not be resolved on this tree, so the write-protection judge cannot tell whether it lands on a protected path (${verdict.cause}) — explicit approval required`,
|
|
369
|
+
decisionReason: "safety",
|
|
370
|
+
};
|
|
371
|
+
}
|
|
362
372
|
return {
|
|
363
373
|
action: "deny",
|
|
364
374
|
message: verdict.unresolvedSymlink
|
package/dist/index.d.ts
CHANGED
|
@@ -197,7 +197,7 @@ export { createAllowDenyPolicy, createApprovalPolicy,
|
|
|
197
197
|
type ApprovalSeatOutcome, COARSE_SHELL_TOOLS, createCoarseCommandNamePolicy, createTranscriptIntegrityPolicy, createUnverifiableDeletePolicy, findUnverifiableRecursiveDelete, combinePolicies, abortedSeatDeny, receiveHookResult, isEngineRefusal, wrapToolPolicy, decisionText, resolveAsk, toolPolicyNameSets, toolPolicyPathRules, type ToolPolicyNameSets, type NamedToolPolicy, type ToolPolicyProjection, type ToolPolicyProjectionComponent, type ConstraintChainEntry, checkToolPolicyProjection, constraintChainEntryOf, constraintChainDigest, type ToolPolicy, type ToolCallRequest, type PermissionResult, type DecisionReason, screenApproverAttribution, APPROVER_ATTRIBUTION_MAX_CHARS, type OnAsk, type AskOutcome, type ResolvedAsk, type AskRequest, type AskDelegationProvenance, type AskRuleEvidence, type AskEvidenceAbsence, ASK_EVIDENCE_ABSENCE_VALUES, } from "./core/tool-policy.js";
|
|
198
198
|
export { type ReadRootGrantScreen } from "./core/read-root-grant-screen.js";
|
|
199
199
|
export { type AskOrigin, ASK_ORIGINS, isAskOrigin, ORIGIN_IMPLIES_REAL_APPROVAL, PERSISTED_RULE_MANDATES, type PersistedRuleMandate, isPersistedRuleMandate, screenAskClassMembers } from "./core/ask-origin.js";
|
|
200
|
-
export { PERMISSION_MODES, type PermissionMode, isPermissionMode, PERMISSION_MODE_RANK, clampPermissionMode, ASK_CLASSES, type AskClass, isAskClass, MODE_ARBITRATION, type ModeArbitrationRow,
|
|
200
|
+
export { PERMISSION_MODES, type PermissionMode, isPermissionMode, PERMISSION_MODE_RANK, clampPermissionMode, ASK_CLASSES, type AskClass, isAskClass, MODE_ARBITRATION, type ModeArbitrationRow, ASK_FACES, type AskFace, askFaceOf, releasableAskClassesFor, MODE_RELEASE_REASONS, type ModeReleaseReason, isModeReleaseReason, RRA_EXPLAINERS, classifyAsk, foldArbitrationRow, type AskClassification, type AskClassFacts, type ToolMarkSource, arbitrateByMode, type Arbitration, fileCommandOptionsSentence } from "./core/ask-origin.js";
|
|
201
201
|
export { modePolicyOf, type ModePolicy, PERMISSION_MODE_FOLD_REASONS, type PermissionModeFoldReason, type PermissionModeReceipt, ACCEPT_EDITS_FILE_COMMANDS, modeAcceptsFileCommand } from "./core/mode-policy.js";
|
|
202
202
|
export type { PermissionSeatInputs, PermissionSeatReading } from "./core/mode-policy.js";
|
|
203
203
|
export type { SkillGateSeats, SkillGrantHit, SkillGrantLane, SkillGrantOrigin, SkillGrantRecord } from "./core/skill-grant.js";
|
package/dist/index.js
CHANGED
|
@@ -162,7 +162,7 @@ export { tightenTaskSpec, TaskSpecTightenError } from "./core/tighten-task-spec.
|
|
|
162
162
|
export { createAllowDenyPolicy, createApprovalPolicy, COARSE_SHELL_TOOLS, createCoarseCommandNamePolicy, createTranscriptIntegrityPolicy, createUnverifiableDeletePolicy, findUnverifiableRecursiveDelete, combinePolicies, abortedSeatDeny, receiveHookResult, isEngineRefusal, wrapToolPolicy, decisionText, resolveAsk, toolPolicyNameSets, toolPolicyPathRules, checkToolPolicyProjection, constraintChainEntryOf, constraintChainDigest, screenApproverAttribution, APPROVER_ATTRIBUTION_MAX_CHARS, ASK_EVIDENCE_ABSENCE_VALUES, } from "./core/tool-policy.js";
|
|
163
163
|
export {} from "./core/read-root-grant-screen.js";
|
|
164
164
|
export { ASK_ORIGINS, isAskOrigin, ORIGIN_IMPLIES_REAL_APPROVAL, PERSISTED_RULE_MANDATES, isPersistedRuleMandate, screenAskClassMembers } from "./core/ask-origin.js";
|
|
165
|
-
export { PERMISSION_MODES, isPermissionMode, PERMISSION_MODE_RANK, clampPermissionMode, ASK_CLASSES, isAskClass, MODE_ARBITRATION,
|
|
165
|
+
export { PERMISSION_MODES, isPermissionMode, PERMISSION_MODE_RANK, clampPermissionMode, ASK_CLASSES, isAskClass, MODE_ARBITRATION, ASK_FACES, askFaceOf, releasableAskClassesFor, MODE_RELEASE_REASONS, isModeReleaseReason, RRA_EXPLAINERS, classifyAsk, foldArbitrationRow, arbitrateByMode, fileCommandOptionsSentence } from "./core/ask-origin.js";
|
|
166
166
|
export { modePolicyOf, PERMISSION_MODE_FOLD_REASONS, ACCEPT_EDITS_FILE_COMMANDS, modeAcceptsFileCommand } from "./core/mode-policy.js";
|
|
167
167
|
export { SKILL_GRANT_ORIGINS } from "./core/skill-grant.js";
|
|
168
168
|
export { ALLOW_ENTRY_INERT_REASONS, compileAllowEntry, compileAllowList, renderListEntry } from "./core/allow-entry.js";
|
package/dist/shell/lex.d.ts
CHANGED
|
@@ -103,6 +103,17 @@ export interface WordExpansion {
|
|
|
103
103
|
readonly body?: string;
|
|
104
104
|
/** The parameter name, where the expansion names one. */
|
|
105
105
|
readonly name?: string;
|
|
106
|
+
/** A glob's pattern — absent where an expansion stands inside it (its text is not the pattern): `folded`, what the
|
|
107
|
+
* program receives where it matches nothing — quotes removed, the spelling {@link LexedWord.folded} carries at its place
|
|
108
|
+
* (`[^.]`); `written`, as written, quoting kept (`[^\.]`, `["!"a]`) — the text a shell re-reading it matches by;
|
|
109
|
+
* `judged`, the spelling a reader judges the names it matches by — `folded`, save a class the judge reads as any one
|
|
110
|
+
* character: one holding a quoted or escaped character (its quote-removed text reads another class: `["!"a]` is not
|
|
111
|
+
* the negation `[!a]`) or a `[:…:]` / `[=…=]` / `[.….]` unit — spelled `?`, a superset of its members. */
|
|
112
|
+
readonly pattern?: {
|
|
113
|
+
readonly folded: string;
|
|
114
|
+
readonly written: string;
|
|
115
|
+
readonly judged: string;
|
|
116
|
+
};
|
|
106
117
|
}
|
|
107
118
|
export interface QuotedSpan {
|
|
108
119
|
/** Offsets into {@link LexedWord.raw}, `[start, end)`; the quote characters belong to their span. */
|
|
@@ -143,6 +154,10 @@ export interface LexedWord {
|
|
|
143
154
|
readonly kind: "word";
|
|
144
155
|
readonly raw: string;
|
|
145
156
|
readonly folded: string;
|
|
157
|
+
/** The spelling a reader judges the word's names by, where it is not `folded`: each bracket class holding a quoted or
|
|
158
|
+
* escaped character or a `[:…:]` / `[=…=]` / `[.….]` unit spelled `?` (`["!"a]*`, `[[:alpha:]]*` ⇒ `?*`,
|
|
159
|
+
* {@link WordExpansion.pattern}). */
|
|
160
|
+
readonly judged?: string;
|
|
146
161
|
readonly pieces: readonly WordPiece[];
|
|
147
162
|
readonly quotedSpans: readonly QuotedSpan[];
|
|
148
163
|
/** The pieces that carry an expansion, in order. */
|