@sema-agent/core 7.45.0 → 7.46.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +246 -1
- package/dist/agents/extra-tool-names.d.ts +65 -0
- package/dist/agents/extra-tool-names.js +124 -0
- package/dist/agents/subagent.d.ts +17 -5
- package/dist/agents/subagent.js +33 -25
- package/dist/agents/tool-filter.d.ts +17 -1
- package/dist/agents/tool-filter.js +7 -0
- package/dist/core/agent-config-dir.d.ts +20 -0
- package/dist/core/agent-config-dir.js +49 -0
- package/dist/core/allow-entry.d.ts +5 -3
- package/dist/core/allow-entry.js +3 -1
- package/dist/core/ask-origin.js +1 -1
- package/dist/core/checkpoint-store.d.ts +39 -2
- package/dist/core/effective-path-target.d.ts +136 -4
- package/dist/core/effective-path-target.js +110 -2
- package/dist/core/engine-notice.d.ts +5 -0
- package/dist/core/gate-fold.js +3 -0
- package/dist/core/gate-lanes.js +9 -3
- package/dist/core/governance-codes.d.ts +3 -1
- package/dist/core/governance-codes.js +3 -0
- package/dist/core/hooks.d.ts +3 -0
- package/dist/core/path-rule-binding.d.ts +65 -0
- package/dist/core/path-rule-binding.js +99 -0
- package/dist/core/permission-rule-model.d.ts +47 -18
- package/dist/core/permission-rule-model.js +38 -42
- package/dist/core/permission-rule-provider.d.ts +7 -0
- package/dist/core/permission-rule-provider.js +10 -3
- package/dist/core/permission-rule-store.d.ts +8 -0
- package/dist/core/permission-rule-store.js +6 -2
- package/dist/core/permission-rule-sync.js +2 -2
- package/dist/core/permission-rules.d.ts +22 -40
- package/dist/core/permission-rules.js +50 -43
- package/dist/core/runner/child-tree.d.ts +97 -0
- package/dist/core/runner/child-tree.js +104 -0
- package/dist/core/runner/construction-doors.d.ts +5 -0
- package/dist/core/runner/construction-doors.js +6 -0
- package/dist/core/runner/contracts.d.ts +46 -0
- package/dist/core/runner/permission-rule-lanes.d.ts +34 -25
- package/dist/core/runner/permission-rule-lanes.js +18 -12
- package/dist/core/runner/persisted-path-rows.d.ts +31 -7
- package/dist/core/runner/persisted-path-rows.js +31 -12
- package/dist/core/runner/prepare-ask-lane.d.ts +4 -0
- package/dist/core/runner/prepare-ask-lane.js +4 -2
- package/dist/core/runner/prepare-execution-env.d.ts +5 -2
- package/dist/core/runner/prepare-execution-env.js +10 -3
- package/dist/core/runner/prepare-gate-stations.d.ts +3 -0
- package/dist/core/runner/prepare-gate-stations.js +2 -1
- package/dist/core/runner/prepare-hands-readface.d.ts +8 -4
- package/dist/core/runner/prepare-hands-readface.js +6 -6
- package/dist/core/runner/prepare-inherited-gate.d.ts +8 -1
- package/dist/core/runner/prepare-inherited-gate.js +8 -2
- package/dist/core/runner/prepare-park-ask.d.ts +3 -0
- package/dist/core/runner/prepare-park-ask.js +5 -3
- package/dist/core/runner/prepare-policy-chain.d.ts +14 -2
- package/dist/core/runner/prepare-policy-chain.js +11 -4
- package/dist/core/runner/prepare-suspend-saga.d.ts +10 -0
- package/dist/core/runner/prepare-suspend-saga.js +7 -1
- package/dist/core/runner/prepare-task.js +37 -18
- package/dist/core/runner/prepare-turn-wiring.d.ts +2 -0
- package/dist/core/runner/prepare-turn-wiring.js +2 -1
- package/dist/core/runner/prepare-workspace-restore.d.ts +3 -0
- package/dist/core/runner/prepare-workspace-restore.js +1 -0
- package/dist/core/runner/resume-apply.js +17 -3
- package/dist/core/runner/resume-checkpoint-screen.js +13 -0
- package/dist/core/runner/run-leg.js +5 -1
- package/dist/core/runner/runtask.js +2 -2
- package/dist/core/runner/sub-agent-deny-lists.d.ts +1 -1
- package/dist/core/runner/write-protection-station.d.ts +3 -2
- package/dist/core/runner/write-protection-station.js +1 -0
- package/dist/core/runner-deps.d.ts +14 -0
- package/dist/core/sensitive-path-policy.d.ts +8 -0
- package/dist/core/sensitive-path-policy.js +18 -2
- package/dist/core/shell-wrapper-projection.d.ts +11 -0
- package/dist/core/shell-wrapper-projection.js +56 -0
- package/dist/core/shell-wrapper-table.d.ts +144 -34
- package/dist/core/shell-wrapper-table.js +40 -20
- package/dist/core/synthetic-read-verdict.d.ts +3 -0
- package/dist/core/synthetic-read-verdict.js +3 -1
- package/dist/core/tool-catalog-entries.d.ts +21 -0
- package/dist/core/tool-catalog-entries.js +9 -0
- package/dist/core/tool-policy.d.ts +30 -8
- package/dist/core/tool-policy.js +9 -7
- package/dist/core/tool-spec.d.ts +3 -2
- package/dist/core/write-protect.d.ts +4 -0
- package/dist/core/write-protect.js +30 -9
- package/dist/index.d.ts +5 -3
- package/dist/index.js +4 -2
- package/dist/orchestration/run-spec.js +8 -7
- package/dist/orchestration/workflow.js +1 -1
- package/dist/shell/lex.d.ts +7 -0
- package/dist/shell/lex.js +17 -0
- package/dist/tools/fs/bash-program-position.d.ts +8 -87
- package/dist/tools/fs/bash-program-position.js +33 -69
- package/dist/tools/fs/bash-readonly-classifier.d.ts +17 -444
- package/dist/tools/fs/bash-readonly-classifier.js +169 -1225
- package/dist/tools/fs/index.d.ts +3 -2
- package/dist/tools/fs/program-read-model.d.ts +694 -0
- package/dist/tools/fs/program-read-model.js +1252 -0
- package/dist/tools/fs/program-read-model.rows.d.ts +18 -0
- package/dist/tools/fs/program-read-model.rows.js +613 -0
- package/dist/tools/fs/read-deny-home.d.ts +19 -1
- package/dist/tools/fs/read-deny-home.js +62 -24
- package/dist/tools/fs/read-deny.d.ts +54 -14
- package/dist/tools/fs/read-deny.js +104 -50
- package/dist/tools/fs/removal-targets.d.ts +7 -6
- package/dist/tools/fs/removal-targets.js +22 -22
- package/dist/tools/fs/safety.d.ts +3 -0
- package/dist/tools/fs/safety.js +10 -3
- package/dist/tools/fs/shell-read-argv.d.ts +85 -0
- package/dist/tools/fs/shell-read-argv.js +386 -0
- package/dist/tools/fs/shell-read-bindings.d.ts +273 -0
- package/dist/tools/fs/shell-read-bindings.js +746 -0
- package/dist/tools/fs/shell-read-check.js +23 -15
- package/dist/tools/fs/shell-read-establish.js +79 -5
- package/dist/tools/fs/shell-read-judge.d.ts +11 -1
- package/dist/tools/fs/shell-read-judge.js +9 -1
- package/dist/tools/fs/shell-read-plan.d.ts +31 -7
- package/dist/tools/fs/shell-read-plan.js +299 -393
- package/dist/tools/fs/shell-read-word.d.ts +29 -26
- package/dist/tools/fs/shell-read-word.js +30 -38
- package/dist/tools/fs/shell-read-write-targets.d.ts +15 -64
- package/dist/tools/fs/shell-read-write-targets.js +83 -136
- package/dist/tools/fs/shell-reentry-text.d.ts +38 -6
- package/dist/tools/fs/shell-reentry-text.js +90 -53
- package/package.json +1 -1
- package/test/export-surface.snapshot.json +17 -3
- package/wire-changes/core@7.46.0.json +1522 -0
|
@@ -47,3 +47,24 @@ export declare const ENGINE_CARDS: Readonly<Record<string, string>>;
|
|
|
47
47
|
export declare function toolFace(id: string): ToolFaceDeclaration;
|
|
48
48
|
/** Definition-id → face, for the modules that spread one (`...TOOL_FACES.read`). Frozen: a face is data. */
|
|
49
49
|
export declare const TOOL_FACES: Readonly<Record<string, ToolFaceDeclaration>>;
|
|
50
|
+
/**
|
|
51
|
+
* The engine's own tool names, once each: every catalog entry the engine mounts itself (`source: "builtin"` — the hands,
|
|
52
|
+
* the scaffolds, the memory tools, the background-task tools), whatever face or contract it is mounted under. A host that
|
|
53
|
+
* must hand the engine a tool face with NO built-ins (`TaskSpec.excludeTools`, a shell's `--tools ""`) reads this set
|
|
54
|
+
* instead of keeping a list of its own: the catalog is the one source, and a name the engine adds later lands here with it.
|
|
55
|
+
*/
|
|
56
|
+
export declare const ENGINE_BUILTIN_TOOL_NAMES: readonly string[];
|
|
57
|
+
/**
|
|
58
|
+
* The engine's HAND tools, once each, with the other spellings they answer to: the catalog rows mounted by `hands` — what a run
|
|
59
|
+
* mounts for itself over an execution environment (the file and shell band, the worktree pair, the band's background-shell
|
|
60
|
+
* pair). A delegated child's tool-name lists govern these beside the tools the delegation tool hands it, so the spawn reads
|
|
61
|
+
* this set rather than a list of its own.
|
|
62
|
+
*/
|
|
63
|
+
export declare const HAND_TOOLS: ReadonlyArray<{
|
|
64
|
+
readonly name: string;
|
|
65
|
+
readonly aliases?: readonly string[];
|
|
66
|
+
}>;
|
|
67
|
+
/** The wire names of {@link HAND_TOOLS}. */
|
|
68
|
+
export declare const HAND_TOOL_NAMES: readonly string[];
|
|
69
|
+
/** Is this name one the engine mounts itself ({@link ENGINE_BUILTIN_TOOL_NAMES})? A caller tool, an MCP tool or a skill tool is not. */
|
|
70
|
+
export declare function isEngineBuiltinToolName(name: string): boolean;
|
|
@@ -180,3 +180,12 @@ function deepFreeze(value) {
|
|
|
180
180
|
}
|
|
181
181
|
deepFreeze(TOOL_CATALOG_ENTRIES);
|
|
182
182
|
export const TOOL_FACES = Object.freeze(Object.fromEntries(TOOL_CATALOG_ENTRIES.map((e) => [e.id, e.face])));
|
|
183
|
+
export const ENGINE_BUILTIN_TOOL_NAMES = Object.freeze([...new Set(TOOL_CATALOG_ENTRIES.filter((e) => e.source === "builtin").map((e) => e.name))]);
|
|
184
|
+
export const HAND_TOOLS = Object.freeze([...new Set(TOOL_CATALOG_ENTRIES.filter((e) => e.mountedBy.includes("hands")).map((e) => e.name))].map((name) => {
|
|
185
|
+
const aliases = [...new Set(TOOL_CATALOG_ENTRIES.filter((e) => e.name === name && e.mountedBy.includes("hands")).flatMap((e) => e.face.aliases ?? []))];
|
|
186
|
+
return Object.freeze(aliases.length > 0 ? { name, aliases: Object.freeze(aliases) } : { name });
|
|
187
|
+
}));
|
|
188
|
+
export const HAND_TOOL_NAMES = Object.freeze(HAND_TOOLS.map((t) => t.name));
|
|
189
|
+
export function isEngineBuiltinToolName(name) {
|
|
190
|
+
return ENGINE_BUILTIN_TOOL_NAMES.includes(name);
|
|
191
|
+
}
|
|
@@ -79,6 +79,17 @@ export interface ToolCallRequest {
|
|
|
79
79
|
* Read-only, like `cwd`.
|
|
80
80
|
*/
|
|
81
81
|
root?: string;
|
|
82
|
+
/** The call's target reading (`readCallTarget`: lexical / canonical / hop views per reading of the path slot), stamped by the Runner on a
|
|
83
|
+
* leg whose lanes can hold a path rule; it describes only the `args` object it was taken for (`callTargetReadingOf`). Read-only. */
|
|
84
|
+
targetReading?: import("./effective-path-target.js").CallTargetReading;
|
|
85
|
+
/**
|
|
86
|
+
* An ENGINE-INTERNAL seat, not a fact of the call: the anchors of the policy now judging this request — the leg its
|
|
87
|
+
* path rules were installed on (`installPolicyAnchors`). Present only on the copy an installed policy hands its own
|
|
88
|
+
* inner `check` (where `createPermissionRulePolicy` reads it; an inner installation's anchors win over an outer one's);
|
|
89
|
+
* the request the gate holds, an `AskRequest`, a hook's input and a parked row never carry it. A host neither sets nor
|
|
90
|
+
* reads it.
|
|
91
|
+
*/
|
|
92
|
+
pathRuleAnchors?: import("./permission-rule-model.js").PathRuleBases;
|
|
82
93
|
/**
|
|
83
94
|
* The CANONICAL directories THIS RUN'S PROMPT ADVERTISES AS WRITABLE — the admitted list of the root
|
|
84
95
|
* fence's advertised-writable-directory seat (the memory engine's write home, the deployment's
|
|
@@ -537,11 +548,14 @@ export interface ToolPolicy {
|
|
|
537
548
|
* describes the policy must read that bit, not the field's presence.
|
|
538
549
|
*/
|
|
539
550
|
readonly projection?: ToolPolicyProjection;
|
|
540
|
-
/** The policy's
|
|
541
|
-
*
|
|
542
|
-
*
|
|
543
|
-
*
|
|
544
|
-
|
|
551
|
+
/** The policy's PATH rules as a function of the ANCHORS of the leg they are installed on (`createPermissionRulePolicy`
|
|
552
|
+
* sets it; present exactly when the policy judges a path target): given the leg's root and home, every path rule
|
|
553
|
+
* bound there — the face-keyed deny rows the prepared leg joins to its read and write tables (so the shell's reads and
|
|
554
|
+
* writes and the traversals meet the rule, not only the tool it names), and the rows its own `check` judges at the
|
|
555
|
+
* gate. A policy the Runner already installed answers with its own installation's rows whatever it is given. Carried
|
|
556
|
+
* through {@link combinePolicies}; read with {@link toolPolicyPathRules}. Its presence also tells the Runner to read
|
|
557
|
+
* each call's target ({@link ToolCallRequest.targetReading}). */
|
|
558
|
+
readonly pathRules?: (anchors: import("./permission-rule-model.js").PathRuleBases) => readonly import("./path-rule-binding.js").BoundPathRule[];
|
|
545
559
|
/** Does this policy guard a write to this ABSOLUTE target — would it refuse the write if a write tool made it? The
|
|
546
560
|
* policy's answer for a writer it cannot see as a call (the shell's file commands), read by the shell station's
|
|
547
561
|
* `writesPlaced` fact alone: a guarded target is not placed, so no permission mode answers the command and its ask
|
|
@@ -558,9 +572,15 @@ export interface ToolPolicy {
|
|
|
558
572
|
* each by name off the wrapped policy, {@link combinePolicies} folds each. The fence makes a new {@link ToolPolicy} member
|
|
559
573
|
* a compile error until the table names it.
|
|
560
574
|
*/
|
|
561
|
-
declare const TOOL_POLICY_CARRIED_MEMBERS: readonly ["projection", "
|
|
575
|
+
declare const TOOL_POLICY_CARRIED_MEMBERS: readonly ["projection", "pathRules", "writeTargetGuard", "nameSets"];
|
|
562
576
|
type ToolPolicyCarriedMember = (typeof TOOL_POLICY_CARRIED_MEMBERS)[number];
|
|
563
577
|
export type ToolPolicyCarriesEveryMember = AssertAllKeysHandled<Exclude<Exclude<keyof ToolPolicy, "check">, ToolPolicyCarriedMember>>;
|
|
578
|
+
/** The carried members that are set on `p`, each read by name — an accessor or a non-enumerable member included. */
|
|
579
|
+
export declare function carriedMembersOf(p: {
|
|
580
|
+
readonly [K in ToolPolicyCarriedMember]?: ToolPolicy[K];
|
|
581
|
+
}): {
|
|
582
|
+
-readonly [K in ToolPolicyCarriedMember]?: ToolPolicy[K];
|
|
583
|
+
};
|
|
564
584
|
/**
|
|
565
585
|
* The ONE way to wrap a policy: the wrapper decides through `check`; every member {@link ToolPolicy} declares beside it
|
|
566
586
|
* (projection, name sets, path deny rows, write-target guard — the carried-member table) rides as `inner` holds it, read
|
|
@@ -570,8 +590,10 @@ export type ToolPolicyCarriesEveryMember = AssertAllKeysHandled<Exclude<Exclude<
|
|
|
570
590
|
* The ask-class brand is not carried: it attests the replaced `check`.
|
|
571
591
|
*/
|
|
572
592
|
export declare function wrapToolPolicy<P extends ToolPolicy>(inner: P, check: ToolPolicy["check"]): Pick<P, ToolPolicyCarriedMember> & ToolPolicy;
|
|
573
|
-
/** Every path
|
|
574
|
-
|
|
593
|
+
/** Every path rule the given policies carry (combined policies included), in order, bound at `anchors` — the anchors of
|
|
594
|
+
* the leg the policies are installed on (a policy already installed answers with its own installation's rows). The deny
|
|
595
|
+
* tables take the rows that guard a face (`faces` non-empty) of a deny rule. */
|
|
596
|
+
export declare function toolPolicyPathRules(policies: readonly (ToolPolicy | undefined)[], anchors: import("./permission-rule-model.js").PathRuleBases): readonly import("./path-rule-binding.js").BoundPathRule[];
|
|
575
597
|
/** The one write-target guard the given policies carry together (combined policies included): `true` when any member
|
|
576
598
|
* guards the target, a member that throws counting as guarding it. `undefined` when none carries one. */
|
|
577
599
|
export declare function toolPolicyWriteTargetGuard(policies: readonly (ToolPolicy | undefined)[]): ToolPolicy["writeTargetGuard"];
|
package/dist/core/tool-policy.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { homedir } from "node:os";
|
|
3
|
+
import { withArgsReading } from "./effective-path-target.js";
|
|
3
4
|
import { brandPolicyAskClass } from "./ask-class.js";
|
|
4
5
|
import { decisionAskClassesOf, headlessDenialMessage } from "./ask-origin.js";
|
|
5
6
|
import { ownReadRootCandidate, readRootGrantSeat } from "./read-root-grant-screen.js";
|
|
@@ -137,8 +138,8 @@ export const ASK_EVIDENCE_ABSENCE_VALUES = ["not_wired", "not_adjudicated", "una
|
|
|
137
138
|
export function decisionText(d) {
|
|
138
139
|
return d.message;
|
|
139
140
|
}
|
|
140
|
-
const TOOL_POLICY_CARRIED_MEMBERS = ["projection", "
|
|
141
|
-
function carriedMembersOf(p) {
|
|
141
|
+
const TOOL_POLICY_CARRIED_MEMBERS = ["projection", "pathRules", "writeTargetGuard", "nameSets"];
|
|
142
|
+
export function carriedMembersOf(p) {
|
|
142
143
|
const out = {};
|
|
143
144
|
const carry = (k) => {
|
|
144
145
|
const v = p[k];
|
|
@@ -152,8 +153,8 @@ function carriedMembersOf(p) {
|
|
|
152
153
|
export function wrapToolPolicy(inner, check) {
|
|
153
154
|
return { ...carriedMembersOf(inner), check };
|
|
154
155
|
}
|
|
155
|
-
export function
|
|
156
|
-
return policies.flatMap((p) => (
|
|
156
|
+
export function toolPolicyPathRules(policies, anchors) {
|
|
157
|
+
return policies.flatMap((p) => (typeof p?.pathRules === "function" ? p.pathRules(anchors) : []));
|
|
157
158
|
}
|
|
158
159
|
export function toolPolicyWriteTargetGuard(policies) {
|
|
159
160
|
const guards = policies.flatMap((p) => (typeof p?.writeTargetGuard === "function" ? [p.writeTargetGuard] : []));
|
|
@@ -514,7 +515,7 @@ export function createApprovalPolicy(opts) {
|
|
|
514
515
|
}
|
|
515
516
|
export function combinePolicies(...policies) {
|
|
516
517
|
const nameSets = policies.flatMap(toolPolicyNameSets);
|
|
517
|
-
const
|
|
518
|
+
const pathRuled = policies.filter((p) => typeof p?.pathRules === "function");
|
|
518
519
|
const writeTargetGuard = toolPolicyWriteTargetGuard(policies);
|
|
519
520
|
const childProjections = policies.map((p) => p.projection);
|
|
520
521
|
const projectedComponents = [];
|
|
@@ -536,7 +537,7 @@ export function combinePolicies(...policies) {
|
|
|
536
537
|
const folded = {
|
|
537
538
|
projection: combinedProjection,
|
|
538
539
|
nameSets: nameSets.length > 0 ? nameSets : undefined,
|
|
539
|
-
|
|
540
|
+
pathRules: pathRuled.length > 0 ? (anchors) => toolPolicyPathRules(pathRuled, anchors) : undefined,
|
|
540
541
|
writeTargetGuard,
|
|
541
542
|
};
|
|
542
543
|
return {
|
|
@@ -576,7 +577,8 @@ export function combinePolicies(...policies) {
|
|
|
576
577
|
continue;
|
|
577
578
|
}
|
|
578
579
|
if (d.updatedInput !== undefined) {
|
|
579
|
-
|
|
580
|
+
const reread = withArgsReading(current, d.updatedInput, signal);
|
|
581
|
+
current = reread !== undefined ? await reread : { ...current, args: d.updatedInput };
|
|
580
582
|
rewrite = d;
|
|
581
583
|
stated = d.action === "allow" ? d.decisionReason : undefined;
|
|
582
584
|
}
|
package/dist/core/tool-spec.d.ts
CHANGED
|
@@ -212,8 +212,9 @@ export interface ToolSpec<TParams extends TSchema = TSchema> {
|
|
|
212
212
|
* carries its own tool set, and a parent that mounts nothing but the delegation tool can still hand
|
|
213
213
|
* its children a network tool, so the parent's roster is the wrong thing to look at.
|
|
214
214
|
*
|
|
215
|
-
* ABSENT ⇔ the pool is not statically knowable — a spawn-time tool factory
|
|
216
|
-
* identity does not exist until the call
|
|
215
|
+
* ABSENT ⇔ the pool is not statically knowable — a spawn-time tool factory with no declared roster
|
|
216
|
+
* (`SubagentToolOptions.extraToolNames`) can inject tools whose identity does not exist until the call
|
|
217
|
+
* happens — and the reader fails closed on that. A declared roster's tools are rows of the pool.
|
|
217
218
|
*/
|
|
218
219
|
agentToolPool?: ReadonlyArray<{
|
|
219
220
|
name: string;
|
|
@@ -173,6 +173,10 @@ export interface WriteProtectionCompileOptions {
|
|
|
173
173
|
* path-family source when the call carries no cwd. Omitted ⇒ relative spellings resolve against the
|
|
174
174
|
* cwd, then the environment's own working directory. */
|
|
175
175
|
readonly rootPath?: string;
|
|
176
|
+
/** The host's configuration root when it keeps the agent's configuration directory somewhere other than the home's
|
|
177
|
+
* `.sema` (`RunnerDeps.agentConfigDir`, absolute, already screened): every row that names the `.sema` directory also
|
|
178
|
+
* names the same tail below this root, and a hit there cites the row. Omitted ⇒ the table is judged as it stands. */
|
|
179
|
+
readonly agentConfigDir?: string;
|
|
176
180
|
/**
|
|
177
181
|
* Called at most ONCE per judgment, at the moment the ENVIRONMENT half is found unable to contribute
|
|
178
182
|
* ({@link WriteProtectionViewAbsence}) — before the judgment returns, and on the hit arm as well as the
|
|
@@ -4,6 +4,7 @@ import { pathTargetBaseOf } from "./effective-path-target.js";
|
|
|
4
4
|
import { canonicalDataRootKeys, dataRootSpellings, relativeUnderRoot } from "./sensitive-path-policy.js";
|
|
5
5
|
import { symlinkChainHops } from "./path-identity.js";
|
|
6
6
|
import { readDenyPatternRun } from "../tools/fs/read-deny.js";
|
|
7
|
+
import { agentConfigDirSegments, agentConfigDirTail } from "./agent-config-dir.js";
|
|
7
8
|
import { thrownText } from "./thrown-value.js";
|
|
8
9
|
const freezeTable = (rows) => Object.freeze(rows.map((r) => Object.freeze(r)));
|
|
9
10
|
export const WRITE_PROTECTED_DEFAULT_TABLE = freezeTable([
|
|
@@ -196,6 +197,7 @@ export function compileWriteProtection(entries, opts) {
|
|
|
196
197
|
const basenames = new Map();
|
|
197
198
|
const segments = new Map();
|
|
198
199
|
const runs = [];
|
|
200
|
+
const configTails = [];
|
|
199
201
|
for (const row of rows) {
|
|
200
202
|
const folded = foldWriteProtectName(row.name);
|
|
201
203
|
if (row.kind === "basename") {
|
|
@@ -209,8 +211,23 @@ export function compileWriteProtection(entries, opts) {
|
|
|
209
211
|
else {
|
|
210
212
|
runs.push({ name: row.name, parts: folded.split("/").filter((s) => s.length > 0) });
|
|
211
213
|
}
|
|
214
|
+
const tail = row.kind === "basename" ? undefined : agentConfigDirTail(row.name);
|
|
215
|
+
if (tail !== undefined && opts?.agentConfigDir !== undefined)
|
|
216
|
+
configTails.push({ hit: { name: row.name, kind: row.kind }, tail: foldWriteProtectName(tail).split("/").filter((s) => s.length > 0) });
|
|
212
217
|
}
|
|
213
|
-
const
|
|
218
|
+
const configRootSpelled = opts?.agentConfigDir === undefined ? [] : [agentConfigDirSegments(opts.agentConfigDir)];
|
|
219
|
+
const judgeAtConfigRoots = (segs, roots) => {
|
|
220
|
+
for (const root of roots) {
|
|
221
|
+
const at = root.map(foldWriteProtectSegment);
|
|
222
|
+
if (segs.length < at.length || !at.every((a, i) => segs[i] === a))
|
|
223
|
+
continue;
|
|
224
|
+
for (const { hit, tail } of configTails)
|
|
225
|
+
if (segs.length >= at.length + tail.length && tail.every((t, i) => segs[at.length + i] === t))
|
|
226
|
+
return hit;
|
|
227
|
+
}
|
|
228
|
+
return null;
|
|
229
|
+
};
|
|
230
|
+
const judgeSegments = (raw, configRoots) => {
|
|
214
231
|
if (raw.length === 0)
|
|
215
232
|
return null;
|
|
216
233
|
const segs = raw.map(foldWriteProtectSegment);
|
|
@@ -229,23 +246,26 @@ export function compileWriteProtection(entries, opts) {
|
|
|
229
246
|
return { name: run.name, kind: "segment-run" };
|
|
230
247
|
}
|
|
231
248
|
}
|
|
232
|
-
return null;
|
|
249
|
+
return configTails.length === 0 ? null : judgeAtConfigRoots(segs, configRoots);
|
|
233
250
|
};
|
|
234
|
-
const judgeViews = (spelling) => {
|
|
235
|
-
const posix = judgeSegments(pathViewSegments(spelling, "literal", POSIX_SEPARATOR_FAMILY, uncAnchorSegments(spelling)));
|
|
251
|
+
const judgeViews = (spelling, configRoots = configRootSpelled) => {
|
|
252
|
+
const posix = judgeSegments(pathViewSegments(spelling, "literal", POSIX_SEPARATOR_FAMILY, uncAnchorSegments(spelling)), configRoots);
|
|
236
253
|
if (posix !== null)
|
|
237
254
|
return posix;
|
|
238
|
-
const bothSeparators = judgeSegments(pathViewSegments(spelling, "literal"));
|
|
255
|
+
const bothSeparators = judgeSegments(pathViewSegments(spelling, "literal"), configRoots);
|
|
239
256
|
if (bothSeparators !== null)
|
|
240
257
|
return bothSeparators;
|
|
241
258
|
for (const normalization of WIN32_NORMALIZATIONS) {
|
|
242
|
-
const hit = judgeSegments(pathViewSegments(spelling, normalization));
|
|
259
|
+
const hit = judgeSegments(pathViewSegments(spelling, normalization), configRoots);
|
|
243
260
|
if (hit !== null)
|
|
244
261
|
return hit;
|
|
245
262
|
}
|
|
246
263
|
return null;
|
|
247
264
|
};
|
|
248
|
-
const judgeSpelled = (spelled) =>
|
|
265
|
+
const judgeSpelled = (spelled) => {
|
|
266
|
+
const remainder = relativeUnderDataRoot(spelled, rootViews);
|
|
267
|
+
return remainder === null ? judgeViews(spelled) : judgeViews(remainder, []);
|
|
268
|
+
};
|
|
249
269
|
const judgeSpelling = (path, cwd) => {
|
|
250
270
|
const base = typeof cwd === "string" && cwd.length > 0 ? cwd : undefined;
|
|
251
271
|
const family = pathFamilyOf({ cwd: base });
|
|
@@ -291,7 +311,8 @@ export function compileWriteProtection(entries, opts) {
|
|
|
291
311
|
}
|
|
292
312
|
try {
|
|
293
313
|
if (canonKey !== undefined) {
|
|
294
|
-
const
|
|
314
|
+
const canonicalRoots = configTails.length === 0 || opts?.agentConfigDir === undefined ? [] : (await canonicalDataRootKeys(env, [opts.agentConfigDir], ctx.signal)).map(agentConfigDirSegments);
|
|
315
|
+
const byTarget = judgeViews(canonKey, [...configRootSpelled, ...canonicalRoots]);
|
|
295
316
|
if (byTarget !== null) {
|
|
296
317
|
for (const rootKey of await canonicalDataRootKeys(env, dataRootPaths, ctx.signal)) {
|
|
297
318
|
const relative = relativeUnderRoot(canonKey, rootKey);
|
|
@@ -299,7 +320,7 @@ export function compileWriteProtection(entries, opts) {
|
|
|
299
320
|
continue;
|
|
300
321
|
if (relative === "")
|
|
301
322
|
return { kind: "clear" };
|
|
302
|
-
const remainder = judgeViews(relative);
|
|
323
|
+
const remainder = judgeViews(relative, []);
|
|
303
324
|
return remainder === null ? { kind: "clear" } : { kind: "hit", hit: remainder, via: "target" };
|
|
304
325
|
}
|
|
305
326
|
return { kind: "hit", hit: byTarget, via: "target" };
|
package/dist/index.d.ts
CHANGED
|
@@ -151,7 +151,8 @@ export { TOOL_FAMILIES, TOOL_MOUNT_TAGS, TOOL_APPROVAL_CARDS, TOOL_PATH_ACCESSES
|
|
|
151
151
|
export { describeToolCatalog, catalogEntriesNamed, catalogToolNames, catalogRuleFaceOf, pathTargetOf, pathTargetValue, protectivePathTargetOf, declaredPathTargetOf, BARE_REQUEST_CODE, isProtectedWrite, isDeclaredWrite, fileOpAccessOf, type ToolCallLike, pathConfinableWriteToolNames, pathTargetingToolNames, compactableToolNames, budgetExemptToolNames, handBandEffects, engineCardTypes, predictMountedToolNames, HANDS_BAND_TAGS, MOUNT_TAG_IN_HANDS_BAND, } from "./core/tool-registry.js";
|
|
152
152
|
export { ToolRosterEntry, ToolRoster, ToolRosterDelta, ToolRosterRenderHints, ROSTER_SOURCES, type RosterSource, type ToolIdentity, toolIdentityOf, mintCapabilityId, diffToolRoster, judgeParkedToolIdentity, ruleFacesFromRoster, toolCallFaceOf, rosterToolName, ruleNamesOf, inputKeysOf, ruleFaceOfInstance, rosterMemberBoundProblem, type RosterBoundMembers, type RosterBoundCode, ToolRosterDeltaSeat, } from "./core/tool-roster.js";
|
|
153
153
|
export { type ToolCallFace } from "./core/tool-policy.js";
|
|
154
|
-
export { effectivePathTargetOf, pathTargetBaseOf, type ToolPathBases, type PathTargetReading } from "./core/effective-path-target.js";
|
|
154
|
+
export { effectivePathTargetOf, pathTargetBaseOf, type ToolPathBases, type PathTargetReading, type CallTargetReading, type CallTargetView } from "./core/effective-path-target.js";
|
|
155
|
+
export { type BoundPathRule, type PathRuleSpec } from "./core/path-rule-binding.js";
|
|
155
156
|
export { pathFamilyOf, isAbsoluteForFamily, pathIdentityOf, win32NamespaceScreen, WIN32_NAMESPACE_REFUSALS, type PathFamily, type PathIdentity, type PathIdentityReading, type Win32NamespaceRefusal } from "./tools/fs/safety.js";
|
|
156
157
|
export { judgeShellRead, type ShellReadJudgeInput, type ShellReadJudgment, type ShellReadVerdict, type ShellReadStationSeat, type ShellReadAdmission, } from "./tools/fs/shell-read-check.js";
|
|
157
158
|
export { SHELL_READ_FINDINGS, SHELL_READ_SEATS, SHELL_READ_DECISION, SHELL_READ_FINDING_RANK, type ShellReadFinding, type ShellReadSeat, type ShellReadDisposition, type ShellReadBoundary, type ReadEstablishment, type EstablishedRead, type ReadFinding, } from "./tools/fs/shell-read-judge.js";
|
|
@@ -193,7 +194,7 @@ export { createAllowDenyPolicy, createApprovalPolicy,
|
|
|
193
194
|
* members that seat has no lane for (`"unavailable"`, a routing answer; `updatedInput`, an operator edit a
|
|
194
195
|
* policy verdict cannot get re-adjudicated). Exported so a host can NAME it instead of deriving it through
|
|
195
196
|
* `Parameters<typeof createApprovalPolicy>[0]["approve"]`. */
|
|
196
|
-
type ApprovalSeatOutcome, COARSE_SHELL_TOOLS, createCoarseCommandNamePolicy, createTranscriptIntegrityPolicy, createUnverifiableDeletePolicy, findUnverifiableRecursiveDelete, combinePolicies, abortedSeatDeny, receiveHookResult, isEngineRefusal, wrapToolPolicy, decisionText, resolveAsk, toolPolicyNameSets,
|
|
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";
|
|
197
198
|
export { type ReadRootGrantScreen } from "./core/read-root-grant-screen.js";
|
|
198
199
|
export { type AskOrigin, ASK_ORIGINS, isAskOrigin, ORIGIN_IMPLIES_REAL_APPROVAL, PERSISTED_RULE_MANDATES, type PersistedRuleMandate, isPersistedRuleMandate, screenAskClassMembers } from "./core/ask-origin.js";
|
|
199
200
|
export { PERMISSION_MODES, type PermissionMode, isPermissionMode, PERMISSION_MODE_RANK, clampPermissionMode, ASK_CLASSES, type AskClass, isAskClass, MODE_ARBITRATION, type ModeArbitrationRow, RELEASABLE_ASK_CLASSES, 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";
|
|
@@ -202,12 +203,13 @@ export type { PermissionSeatInputs, PermissionSeatReading } from "./core/mode-po
|
|
|
202
203
|
export type { SkillGateSeats, SkillGrantHit, SkillGrantLane, SkillGrantOrigin, SkillGrantRecord } from "./core/skill-grant.js";
|
|
203
204
|
export { SKILL_GRANT_ORIGINS } from "./core/skill-grant.js";
|
|
204
205
|
export type { BoundSkillGrants } from "./core/skill-grant-binding.js";
|
|
205
|
-
export { ALLOW_ENTRY_INERT_REASONS, compileAllowEntry, compileAllowList, type AllowEntryInertReason, type CompiledAllowEntry, type AllowEntryGateSeats, type AllowEntryLane, type AllowEntryHit } from "./core/allow-entry.js";
|
|
206
|
+
export { ALLOW_ENTRY_INERT_REASONS, compileAllowEntry, compileAllowList, renderListEntry, type AllowEntryInertReason, type CompiledAllowEntry, type AllowEntryGateSeats, type AllowEntryLane, type AllowEntryHit } from "./core/allow-entry.js";
|
|
206
207
|
export { ALLOW_RULE_MEMBERS, type AllowRuleMember, type AllowRuleShadow } from "./core/allow-rule-shadow.js";
|
|
207
208
|
export { mintNamespacePrefix } from "./core/protocol-naming.js";
|
|
208
209
|
export { readDenyClassifierRules, renderClassifierDenyRules, type DerivedDenyRule } from "./core/auto-mode-prompt.js";
|
|
209
210
|
export { AUTO_MODE_DENY_SOURCES, isAutoModeDenySource, type AutoModeDenySource } from "./core/runtime-caps.js";
|
|
210
211
|
export { SETTLEMENT_KINDS, type SettlementKind, isSettlementKind, type Settlement, SETTLEMENT_PARTIES, type SettlementParty, isSettlementParty, HOST_DECIDERS, type HostDecider, isHostDecider, SETTLEMENT_IS_REFUSAL, DENIED_BY_VALUES, type DeniedBy, isDeniedBy, DENIED_BY_MAY_VETO, CLASSIFIER_DENY_CAUSES, type ClassifierDenyCause, isClassifierDenyCause, DENIED_BY_MAY_CARRY_CAUSE, type GateDisposition, type GateOutcome, type ModeRelease, type ModeRefusal, screenGateOutcome } from "./core/gate-outcome.js";
|
|
212
|
+
export { ENGINE_BUILTIN_TOOL_NAMES, isEngineBuiltinToolName } from "./core/tool-catalog-entries.js";
|
|
211
213
|
export { policyCannotAnswer, type HeldQuestion } from "./core/real-approval.js";
|
|
212
214
|
export { type AskCarry } from "./core/hooks.js";
|
|
213
215
|
export { SWAPPABLE_DEP_SEATS, SWAP_SEAT_NOTICE, isSwappableDepSeat, screenSwappableDeps, type SwappableDepSeat, type SwappableDeps } from "./core/swappable-deps.js";
|
package/dist/index.js
CHANGED
|
@@ -124,6 +124,7 @@ export { describeToolCatalog, catalogEntriesNamed, catalogToolNames, catalogRule
|
|
|
124
124
|
export { ToolRosterEntry, ToolRoster, ToolRosterDelta, ToolRosterRenderHints, ROSTER_SOURCES, toolIdentityOf, mintCapabilityId, diffToolRoster, judgeParkedToolIdentity, ruleFacesFromRoster, toolCallFaceOf, rosterToolName, ruleNamesOf, inputKeysOf, ruleFaceOfInstance, rosterMemberBoundProblem, ToolRosterDeltaSeat, } from "./core/tool-roster.js";
|
|
125
125
|
export {} from "./core/tool-policy.js";
|
|
126
126
|
export { effectivePathTargetOf, pathTargetBaseOf } from "./core/effective-path-target.js";
|
|
127
|
+
export {} from "./core/path-rule-binding.js";
|
|
127
128
|
export { pathFamilyOf, isAbsoluteForFamily, pathIdentityOf, win32NamespaceScreen, WIN32_NAMESPACE_REFUSALS } from "./tools/fs/safety.js";
|
|
128
129
|
export { judgeShellRead, } from "./tools/fs/shell-read-check.js";
|
|
129
130
|
export { SHELL_READ_FINDINGS, SHELL_READ_SEATS, SHELL_READ_DECISION, SHELL_READ_FINDING_RANK, } from "./tools/fs/shell-read-judge.js";
|
|
@@ -158,18 +159,19 @@ export { hasScheduler, isValidCronExpr, SchedulerError } from "./core/scheduler.
|
|
|
158
159
|
export { createSchedulerTools, SCHEDULE_WAKEUP_TOOL_NAME, AUTONOMOUS_LOOP_SENTINEL, AUTONOMOUS_LOOP_DYNAMIC_SENTINEL, } from "./tools/scheduler-tools.js";
|
|
159
160
|
export { resolveAutonomousLoopPrompt, AUTONOMOUS_LOOP_PREAMBLE, AUTONOMOUS_LOOP_PREAMBLE_PERSISTENT, } from "./tools/loop-tick.js";
|
|
160
161
|
export { tightenTaskSpec, TaskSpecTightenError } from "./core/tighten-task-spec.js";
|
|
161
|
-
export { createAllowDenyPolicy, createApprovalPolicy, COARSE_SHELL_TOOLS, createCoarseCommandNamePolicy, createTranscriptIntegrityPolicy, createUnverifiableDeletePolicy, findUnverifiableRecursiveDelete, combinePolicies, abortedSeatDeny, receiveHookResult, isEngineRefusal, wrapToolPolicy, decisionText, resolveAsk, toolPolicyNameSets,
|
|
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";
|
|
162
163
|
export {} from "./core/read-root-grant-screen.js";
|
|
163
164
|
export { ASK_ORIGINS, isAskOrigin, ORIGIN_IMPLIES_REAL_APPROVAL, PERSISTED_RULE_MANDATES, isPersistedRuleMandate, screenAskClassMembers } from "./core/ask-origin.js";
|
|
164
165
|
export { PERMISSION_MODES, isPermissionMode, PERMISSION_MODE_RANK, clampPermissionMode, ASK_CLASSES, isAskClass, MODE_ARBITRATION, RELEASABLE_ASK_CLASSES, MODE_RELEASE_REASONS, isModeReleaseReason, RRA_EXPLAINERS, classifyAsk, foldArbitrationRow, arbitrateByMode, fileCommandOptionsSentence } from "./core/ask-origin.js";
|
|
165
166
|
export { modePolicyOf, PERMISSION_MODE_FOLD_REASONS, ACCEPT_EDITS_FILE_COMMANDS, modeAcceptsFileCommand } from "./core/mode-policy.js";
|
|
166
167
|
export { SKILL_GRANT_ORIGINS } from "./core/skill-grant.js";
|
|
167
|
-
export { ALLOW_ENTRY_INERT_REASONS, compileAllowEntry, compileAllowList } from "./core/allow-entry.js";
|
|
168
|
+
export { ALLOW_ENTRY_INERT_REASONS, compileAllowEntry, compileAllowList, renderListEntry } from "./core/allow-entry.js";
|
|
168
169
|
export { ALLOW_RULE_MEMBERS } from "./core/allow-rule-shadow.js";
|
|
169
170
|
export { mintNamespacePrefix } from "./core/protocol-naming.js";
|
|
170
171
|
export { readDenyClassifierRules, renderClassifierDenyRules } from "./core/auto-mode-prompt.js";
|
|
171
172
|
export { AUTO_MODE_DENY_SOURCES, isAutoModeDenySource } from "./core/runtime-caps.js";
|
|
172
173
|
export { SETTLEMENT_KINDS, isSettlementKind, SETTLEMENT_PARTIES, isSettlementParty, HOST_DECIDERS, isHostDecider, SETTLEMENT_IS_REFUSAL, DENIED_BY_VALUES, isDeniedBy, DENIED_BY_MAY_VETO, CLASSIFIER_DENY_CAUSES, isClassifierDenyCause, DENIED_BY_MAY_CARRY_CAUSE, screenGateOutcome } from "./core/gate-outcome.js";
|
|
174
|
+
export { ENGINE_BUILTIN_TOOL_NAMES, isEngineBuiltinToolName } from "./core/tool-catalog-entries.js";
|
|
173
175
|
export { policyCannotAnswer } from "./core/real-approval.js";
|
|
174
176
|
export {} from "./core/hooks.js";
|
|
175
177
|
export { SWAPPABLE_DEP_SEATS, SWAP_SEAT_NOTICE, isSwappableDepSeat, screenSwappableDeps } from "./core/swappable-deps.js";
|
|
@@ -5,7 +5,8 @@ import { combinePolicies } from "../core/tool-policy.js";
|
|
|
5
5
|
import { delimitUntrusted } from "../core/untrusted-text.js";
|
|
6
6
|
import { isDurablePause } from "../agents/suspend-guard.js";
|
|
7
7
|
import { terminalProjection } from "../core/runner/terminal-projection.js";
|
|
8
|
-
import {
|
|
8
|
+
import { writeTargetPath } from "../tools/fs/safety.js";
|
|
9
|
+
import { callTargetViewsOf } from "../core/effective-path-target.js";
|
|
9
10
|
import { captureBaseline, resolveFrozenPaths, restoreFrozenPaths, runOracle, snapshotFrozenPaths, specError, } from "../core/spec-contract.js";
|
|
10
11
|
import { isProtectedWrite } from "../core/tool-registry.js";
|
|
11
12
|
const CASE_INSENSITIVE_FS = process.platform === "darwin" || process.platform === "win32";
|
|
@@ -21,13 +22,13 @@ export function frozenDenyPolicy(rootDir, frozenResolved) {
|
|
|
21
22
|
check(req) {
|
|
22
23
|
if (!isProtectedWrite(req))
|
|
23
24
|
return { action: "allow" };
|
|
24
|
-
const
|
|
25
|
-
if (
|
|
25
|
+
const views = callTargetViewsOf(req, "tighten", { root: rootDir });
|
|
26
|
+
if (views === undefined || views.spellings.length === 0)
|
|
26
27
|
return { action: "allow" };
|
|
27
|
-
const
|
|
28
|
-
const
|
|
29
|
-
if (
|
|
30
|
-
return { action: "deny", message: `${raw} is part of the frozen specification surface (read-only).` };
|
|
28
|
+
const raw = writeTargetPath(req);
|
|
29
|
+
const hit = views.spellings.some((v) => frozen.has(pathKey(v)));
|
|
30
|
+
if (hit) {
|
|
31
|
+
return { action: "deny", message: `${typeof raw === "string" && raw.length > 0 ? raw : views.spellings[0]} is part of the frozen specification surface (read-only).` };
|
|
31
32
|
}
|
|
32
33
|
return { action: "allow" };
|
|
33
34
|
},
|
|
@@ -1579,7 +1579,7 @@ export function startWorkflow(runner, fn, opts = {}, internals) {
|
|
|
1579
1579
|
opts.onForwardEvent(e.type === "task_progress" ? { ...e, workflowRunId: runId, workflowAgentLabel: label } : e);
|
|
1580
1580
|
}
|
|
1581
1581
|
: undefined;
|
|
1582
|
-
const baseInternals = { ...(agentOpts.isolation ? { isolation: agentOpts.isolation } : {}), ...(opts.parentCwd !== undefined ? { parentCwd: opts.parentCwd } : {}), ...spawnAttribution, ...(await captureFloorSeatsNow()), ...(enrichedForward !== undefined ? { onForwardEvent: enrichedForward } : {}), delegationTaskType: "workflow", agentName: label, onWorkspaceResolved: createWorkspaceObserver(rec) };
|
|
1582
|
+
const baseInternals = { ...(agentOpts.isolation ? { isolation: agentOpts.isolation } : {}), ...(opts.parentCwd !== undefined ? { parentCwd: opts.parentCwd, callerRoot: opts.parentCwd } : {}), ...spawnAttribution, ...(await captureFloorSeatsNow()), ...(enrichedForward !== undefined ? { onForwardEvent: enrichedForward } : {}), delegationTaskType: "workflow", agentName: label, onWorkspaceResolved: createWorkspaceObserver(rec) };
|
|
1583
1583
|
let attempts = 0;
|
|
1584
1584
|
let throttleRetried = false;
|
|
1585
1585
|
let lastAttemptReason;
|
package/dist/shell/lex.d.ts
CHANGED
|
@@ -248,6 +248,13 @@ export declare function substitutionsInHeredocBody(body: string): {
|
|
|
248
248
|
readonly body: string;
|
|
249
249
|
readonly kind: "command" | "arithmetic";
|
|
250
250
|
}[] | undefined;
|
|
251
|
+
/** The text a word's pieces hand the program when every piece is literal; `undefined` when one is an expansion or
|
|
252
|
+
* unresolved. */
|
|
253
|
+
export declare function literalTextOf(pieces: readonly WordPiece[]): string | undefined;
|
|
254
|
+
/** The text an UNQUOTED here-document body delivers when the shell expands nothing in it: a backslash before `$`, a
|
|
255
|
+
* backquote or a backslash is removed (a line continuation was joined when the body was read), every other character
|
|
256
|
+
* stands. `undefined` when an unescaped `$` or backquote stands in it: the program reads what the expansion makes. */
|
|
257
|
+
export declare function literalHeredocBody(body: string): string | undefined;
|
|
251
258
|
/** Lex a command: its segments, or the one reason bash (or this lexer) cannot delimit them. */
|
|
252
259
|
export declare function lexShell(source: string): ShellLex;
|
|
253
260
|
/**
|
package/dist/shell/lex.js
CHANGED
|
@@ -319,6 +319,23 @@ export function substitutionsInHeredocBody(body) {
|
|
|
319
319
|
}
|
|
320
320
|
return out;
|
|
321
321
|
}
|
|
322
|
+
export function literalTextOf(pieces) {
|
|
323
|
+
return pieces.every((q) => q.literal !== undefined) ? pieces.map((q) => q.literal).join("") : undefined;
|
|
324
|
+
}
|
|
325
|
+
export function literalHeredocBody(body) {
|
|
326
|
+
let out = "";
|
|
327
|
+
for (let i = 0; i < body.length; i++) {
|
|
328
|
+
const ch = body[i];
|
|
329
|
+
if (ch === "$" || ch === "`")
|
|
330
|
+
return undefined;
|
|
331
|
+
if (ch === "\\" && i + 1 < body.length && "$`\\".includes(body[i + 1])) {
|
|
332
|
+
out += body[++i];
|
|
333
|
+
continue;
|
|
334
|
+
}
|
|
335
|
+
out += ch;
|
|
336
|
+
}
|
|
337
|
+
return out;
|
|
338
|
+
}
|
|
322
339
|
const BLANK = /[ \t]/;
|
|
323
340
|
const fail = (reason, at) => ({ parseFailure: { reason, at } });
|
|
324
341
|
const publicPiece = (p) => ({
|
|
@@ -31,8 +31,6 @@ export declare function writtenAssignmentPrefix(token: string): {
|
|
|
31
31
|
readonly settlesWholeName: boolean;
|
|
32
32
|
} | undefined;
|
|
33
33
|
export { SHELL_CONTROL_PREFIXES };
|
|
34
|
-
/** Shell block words that name no command of their own (a loop head, a block close, a case head). */
|
|
35
|
-
export declare const SHELL_BLOCK_WORDS: ReadonlySet<string>;
|
|
36
34
|
/** A control word is a keyword only when bash sees it BARE: `'if'` / `"do"` / `\\!` are command names (a program
|
|
37
35
|
* looked up on PATH, whose failure changes nothing), so a quoted or escaped spelling never counts as one. */
|
|
38
36
|
export declare function isBareControlWord(words: readonly LexedWord[], i: number, set: ReadonlySet<string>): boolean;
|
|
@@ -43,88 +41,6 @@ export declare function isBareControlWord(words: readonly LexedWord[], i: number
|
|
|
43
41
|
* over control words and earlier assignments re-traces). One fact, two readers that must agree.
|
|
44
42
|
*/
|
|
45
43
|
export declare function isLeadingAssignmentWord(words: readonly LexedWord[], i: number): boolean;
|
|
46
|
-
/**
|
|
47
|
-
* Programs that turn their STANDARD INPUT into the OPERANDS of another program, and EXEC that program directly.
|
|
48
|
-
*
|
|
49
|
-
* The same class as the shell re-entry programs, one level down: there the unseen text is COMMANDS; here the
|
|
50
|
-
* command's name IS visible and its argv is not — `echo <path> | xargs cat` opens a file no token of this walk
|
|
51
|
-
* ever named, because the operand arrives on a stream. The membership criterion is that behaviour and only that
|
|
52
|
-
* behaviour; a program that consumes stdin as DATA (`sort`, `wc`, a reader the deployment never listed) is NOT a
|
|
53
|
-
* member, because its stdin never becomes a filename. Spelled as a pattern — these are program names, never tool
|
|
54
|
-
* names.
|
|
55
|
-
*
|
|
56
|
-
* "EXECS DIRECTLY" is what separates this class from the shell re-entry class, and it is why GNU `parallel` is
|
|
57
|
-
* NOT a member: parallel BUILDS A COMMAND LINE and hands it to a shell, and with no command word at all it runs
|
|
58
|
-
* each input line as a command — so parallel belongs to the shell re-entry class and is refused whole with no
|
|
59
|
-
* exemption at all. A spelling this file has no name for (an aliased or wrapped `xargs`) is judged as any other
|
|
60
|
-
* unlisted program is — the name-table floor the re-entry class records for itself.
|
|
61
|
-
*/
|
|
62
|
-
export declare const ARGV_REENTRY_PROGRAM_RE: RegExp;
|
|
63
|
-
/** The program an {@link ARGV_REENTRY_PROGRAM_RE} launcher runs when the chain names none: `xargs` defaults to
|
|
64
|
-
* `echo`, which is why a bare one reads nothing. */
|
|
65
|
-
export declare const ARGV_REENTRY_DEFAULT_PROGRAM = "echo";
|
|
66
|
-
/**
|
|
67
|
-
* What a launcher declares about its OWN options: each exact spelling with its ARITY — `0` for a switch, `1` for an
|
|
68
|
-
* option whose value is the next token. Every spelling not in the map falls to the refusal (the conservative side,
|
|
69
|
-
* and the verdict those spellings always had); a `1` with no token left to take is not readable either.
|
|
70
|
-
*
|
|
71
|
-
* THREE SPELLINGS OF ONE DECLARATION, because getopt has three: an arity-1 entry is honoured separated (`-k 5`),
|
|
72
|
-
* fused to its long name (`--signal=TERM`) and fused to its short letter (`-k5`), and an arity-0 short entry inside
|
|
73
|
-
* a cluster of arity-0 letters (`-pv`). Reading only the exact spelling made one program two.
|
|
74
|
-
*
|
|
75
|
-
* WHAT A ROW MAY NOT GET WRONG: declaring an arity-1 option as arity 0 steps onto its VALUE and reads the value as
|
|
76
|
-
* the program — the one direction that turns a refusal into a wrong answer rather than a lost one. A row carries
|
|
77
|
-
* only options whose arity every supported implementation agrees on; a flag no row declares still refuses.
|
|
78
|
-
*/
|
|
79
|
-
export interface LauncherRow {
|
|
80
|
-
/** Consulted ONLY for the launcher the command line itself names — a bare keyword in front of the command
|
|
81
|
-
* (`time -p …`) or the written command word (`"time" -p …`, `X=1 time -p …`, `/usr/bin/time -p …`). A launcher
|
|
82
|
-
* nested behind another (`env time -p …`) is that launcher's operand, and its options refuse: resolving past
|
|
83
|
-
* them would turn a whole refusal into an ordinary walked segment — a widening, where every other read of this
|
|
84
|
-
* table only ever finds MORE program to judge. */
|
|
85
|
-
readonly optionArity: ReadonlyMap<string, 0 | 1>;
|
|
86
|
-
/** A bare NEGATIVE integer in this launcher's option section is its OWN argument (`nice -5 <cmd>`), not a flag
|
|
87
|
-
* the table cannot read. Per row: elsewhere a dash-leading token is an option, and reading one as a number
|
|
88
|
-
* would step over a flag whose meaning this table does not know. */
|
|
89
|
-
readonly negativeIntegerIsOwnArgument?: true;
|
|
90
|
-
/** Positional operands the launcher takes BEFORE the program, consumed whatever they spell (`timeout DURATION
|
|
91
|
-
* COMMAND`); a spelling-based reading under-accepted, and every spelling it missed became the program word. */
|
|
92
|
-
readonly requiredOperands?: number;
|
|
93
|
-
/** The launcher reads NAME=VALUE operands with ITS OWN name grammar (`env 1=x …`), wider than the shell's. */
|
|
94
|
-
readonly assignmentOperands?: true;
|
|
95
|
-
/** The launcher hands its operands to a SHELL as text (`watch "cat /x"` runs `sh -c`): no program word to read. */
|
|
96
|
-
readonly shellText?: true;
|
|
97
|
-
/** With no program word the launcher runs a SHELL: piped text would reach it, so an empty program position refuses. */
|
|
98
|
-
readonly whenNoProgram?: "shell";
|
|
99
|
-
/** Options whose value is a WHOLE COMMAND LINE the launcher splits and runs (`env -S "cmd arg"`,
|
|
100
|
-
* `--split-string=…`). Not an arity this reader steps over — the program is inside the value, so the
|
|
101
|
-
* program-position walk still refuses here — but the candidate extraction reads the value as nested shell
|
|
102
|
-
* text instead of as one opaque word. */
|
|
103
|
-
readonly commandString?: readonly string[];
|
|
104
|
-
}
|
|
105
|
-
/**
|
|
106
|
-
* The ONE launcher truth: programs that RUN ANOTHER PROGRAM named in ordinary argv tokens, so a declared reader can
|
|
107
|
-
* sit behind one (`env cat <path>`, `sudo cat <path>`, `timeout 5 cat <path>`), each with the option grammar this
|
|
108
|
-
* file reads for it.
|
|
109
|
-
*
|
|
110
|
-
* Membership is the key set. A row with no declared options means every option-shaped word after that launcher
|
|
111
|
-
* refuses ("which program runs is not readable without the launcher's option table") — the answer every launcher
|
|
112
|
-
* but `time` has always had, now stated per row rather than by omission. Declaring a row's options is a behaviour
|
|
113
|
-
* decision with its own criteria; adding a launcher is a row.
|
|
114
|
-
*
|
|
115
|
-
* `time` carries both spellings a deployment can meet: the shell builtin takes `-p` alone, and the standalone
|
|
116
|
-
* program adds a format, an output file, and three switches.
|
|
117
|
-
*
|
|
118
|
-
* A deliberate SUBSET of the rule lane's own `BARE_INTERPRETER_NAMES`, mirrored rather than imported because that
|
|
119
|
-
* module imports the classifier — the dependency runs that way, and a cycle is a worse problem than a mirror whose
|
|
120
|
-
* subset relation is pinned by test. GNU `parallel` is absent for the reason the shells are absent: it does not run
|
|
121
|
-
* a program named in token-visible argv — it BUILDS a command line and hands it to a shell — so it is a shell
|
|
122
|
-
* re-entry member instead. Language interpreters and shells are excluded as a scope statement: their program
|
|
123
|
-
* argument is a script path or a quoted code string, not a token-visible program with token-visible operands.
|
|
124
|
-
*/
|
|
125
|
-
export declare const LAUNCHER_TABLE: ReadonlyMap<string, LauncherRow>;
|
|
126
|
-
/** The launcher names — derived from the table, never kept beside it. */
|
|
127
|
-
export declare const COMMAND_LAUNCHERS: ReadonlySet<string>;
|
|
128
44
|
export declare const PROGRAM_POSITION_REFUSAL_KINDS: readonly ["option_at_program_position", "control_syntax_at_program_position"];
|
|
129
45
|
export type ProgramPositionRefusalKind = (typeof PROGRAM_POSITION_REFUSAL_KINDS)[number];
|
|
130
46
|
/** Why the program could not be read — a closed set, each member minted into one sentence. `owner` is the word
|
|
@@ -140,6 +56,9 @@ export type ProgramPositionRefusal = {
|
|
|
140
56
|
/** Not a DECLINE ({@link programPositionRunsShell}): the row WAS read and a SHELL stands at the program
|
|
141
57
|
* position. Absent ⇒ genuine control syntax, which IS one. */
|
|
142
58
|
readonly runsShell?: true;
|
|
59
|
+
/** The launcher hands the shell its operands as text (`watch "S"`); absent beside `runsShell`, the shell is handed
|
|
60
|
+
* nothing (`sudo -s`, `chroot DIR`) and runs its standard input. */
|
|
61
|
+
readonly handsText?: true;
|
|
143
62
|
};
|
|
144
63
|
export interface ProgramPosition {
|
|
145
64
|
/** Indices of the bare control words stripped in front (`!`, `if`, `time`, …), in order; their declared options
|
|
@@ -159,6 +78,8 @@ export interface ProgramPosition {
|
|
|
159
78
|
readonly argvFromStdin: boolean;
|
|
160
79
|
/** The chain ends ON the re-entry program (nothing follows it), so it runs its default program. */
|
|
161
80
|
readonly runsDefaultProgram: boolean;
|
|
81
|
+
/** The program the stream-fed chain member runs when the chain names none (`xargs` runs `echo`). */
|
|
82
|
+
readonly defaultProgram?: string;
|
|
162
83
|
/** The program bash RUNS: the first word past the chain. `name` is the folded word as written, `basename` what
|
|
163
84
|
* bash resolves (`/usr/bin/cat` is `cat`). `undefined` when the chain names none or the read was refused. */
|
|
164
85
|
readonly program: {
|
|
@@ -184,7 +105,7 @@ export declare function readProgramPosition(words: readonly LexedWord[]): Progra
|
|
|
184
105
|
/** Does this program position hand a program operands the walk cannot see? True when the chain re-enters through
|
|
185
106
|
* stdin AND the program it runs is not one the caller accepts as a stream target. ONE reading, shared by every
|
|
186
107
|
* consumer; WHICH targets are accepted is the caller's declaration, not this reader's. */
|
|
187
|
-
export declare function argvReentryUnjudged(pos: ProgramPosition,
|
|
108
|
+
export declare function argvReentryUnjudged(pos: ProgramPosition, acceptsTarget: (basename: string) => boolean): boolean;
|
|
188
109
|
/**
|
|
189
110
|
* The two ways this reader DECLINES rather than judges — a CLOSED set (`docs/CLOSED-SETS.md`), because what a
|
|
190
111
|
* seat does with a declined segment is a ROW of the shell read decision table (`shell-read-judge.ts`, the
|
|
@@ -197,9 +118,9 @@ export type ShellScanDecline = (typeof SHELL_SCAN_DECLINES)[number];
|
|
|
197
118
|
/** Does a SHELL stand where this segment's program would be — as the program itself (`sh -c`), or as a launcher
|
|
198
119
|
* ROW handing its operands to one (`watch "…"`)? ONE predicate for the re-entry class, so the two spellings
|
|
199
120
|
* cannot be answered differently; which names are shells is the caller's declaration. Not a decline. */
|
|
200
|
-
export declare function programPositionRunsShell(pos: ProgramPosition,
|
|
121
|
+
export declare function programPositionRunsShell(pos: ProgramPosition, runsShell: (basename: string) => boolean): boolean;
|
|
201
122
|
/** Which decline, if any, this program position raises — ONE reading of the two facts above, so no consumer
|
|
202
123
|
* re-derives either. A refusal that says a SHELL runs is NOT one: it names what runs. */
|
|
203
|
-
export declare function shellScanDeclineOf(pos: ProgramPosition,
|
|
124
|
+
export declare function shellScanDeclineOf(pos: ProgramPosition, acceptsStreamTarget: (basename: string) => boolean): ShellScanDecline | undefined;
|
|
204
125
|
/** ONE sentence per refusal kind, minted here and nowhere else. */
|
|
205
126
|
export declare function programPositionRefusalSentence(refusal: ProgramPositionRefusal): string;
|