@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.
Files changed (127) hide show
  1. package/CHANGELOG.md +246 -1
  2. package/dist/agents/extra-tool-names.d.ts +65 -0
  3. package/dist/agents/extra-tool-names.js +124 -0
  4. package/dist/agents/subagent.d.ts +17 -5
  5. package/dist/agents/subagent.js +33 -25
  6. package/dist/agents/tool-filter.d.ts +17 -1
  7. package/dist/agents/tool-filter.js +7 -0
  8. package/dist/core/agent-config-dir.d.ts +20 -0
  9. package/dist/core/agent-config-dir.js +49 -0
  10. package/dist/core/allow-entry.d.ts +5 -3
  11. package/dist/core/allow-entry.js +3 -1
  12. package/dist/core/ask-origin.js +1 -1
  13. package/dist/core/checkpoint-store.d.ts +39 -2
  14. package/dist/core/effective-path-target.d.ts +136 -4
  15. package/dist/core/effective-path-target.js +110 -2
  16. package/dist/core/engine-notice.d.ts +5 -0
  17. package/dist/core/gate-fold.js +3 -0
  18. package/dist/core/gate-lanes.js +9 -3
  19. package/dist/core/governance-codes.d.ts +3 -1
  20. package/dist/core/governance-codes.js +3 -0
  21. package/dist/core/hooks.d.ts +3 -0
  22. package/dist/core/path-rule-binding.d.ts +65 -0
  23. package/dist/core/path-rule-binding.js +99 -0
  24. package/dist/core/permission-rule-model.d.ts +47 -18
  25. package/dist/core/permission-rule-model.js +38 -42
  26. package/dist/core/permission-rule-provider.d.ts +7 -0
  27. package/dist/core/permission-rule-provider.js +10 -3
  28. package/dist/core/permission-rule-store.d.ts +8 -0
  29. package/dist/core/permission-rule-store.js +6 -2
  30. package/dist/core/permission-rule-sync.js +2 -2
  31. package/dist/core/permission-rules.d.ts +22 -40
  32. package/dist/core/permission-rules.js +50 -43
  33. package/dist/core/runner/child-tree.d.ts +97 -0
  34. package/dist/core/runner/child-tree.js +104 -0
  35. package/dist/core/runner/construction-doors.d.ts +5 -0
  36. package/dist/core/runner/construction-doors.js +6 -0
  37. package/dist/core/runner/contracts.d.ts +46 -0
  38. package/dist/core/runner/permission-rule-lanes.d.ts +34 -25
  39. package/dist/core/runner/permission-rule-lanes.js +18 -12
  40. package/dist/core/runner/persisted-path-rows.d.ts +31 -7
  41. package/dist/core/runner/persisted-path-rows.js +31 -12
  42. package/dist/core/runner/prepare-ask-lane.d.ts +4 -0
  43. package/dist/core/runner/prepare-ask-lane.js +4 -2
  44. package/dist/core/runner/prepare-execution-env.d.ts +5 -2
  45. package/dist/core/runner/prepare-execution-env.js +10 -3
  46. package/dist/core/runner/prepare-gate-stations.d.ts +3 -0
  47. package/dist/core/runner/prepare-gate-stations.js +2 -1
  48. package/dist/core/runner/prepare-hands-readface.d.ts +8 -4
  49. package/dist/core/runner/prepare-hands-readface.js +6 -6
  50. package/dist/core/runner/prepare-inherited-gate.d.ts +8 -1
  51. package/dist/core/runner/prepare-inherited-gate.js +8 -2
  52. package/dist/core/runner/prepare-park-ask.d.ts +3 -0
  53. package/dist/core/runner/prepare-park-ask.js +5 -3
  54. package/dist/core/runner/prepare-policy-chain.d.ts +14 -2
  55. package/dist/core/runner/prepare-policy-chain.js +11 -4
  56. package/dist/core/runner/prepare-suspend-saga.d.ts +10 -0
  57. package/dist/core/runner/prepare-suspend-saga.js +7 -1
  58. package/dist/core/runner/prepare-task.js +37 -18
  59. package/dist/core/runner/prepare-turn-wiring.d.ts +2 -0
  60. package/dist/core/runner/prepare-turn-wiring.js +2 -1
  61. package/dist/core/runner/prepare-workspace-restore.d.ts +3 -0
  62. package/dist/core/runner/prepare-workspace-restore.js +1 -0
  63. package/dist/core/runner/resume-apply.js +17 -3
  64. package/dist/core/runner/resume-checkpoint-screen.js +13 -0
  65. package/dist/core/runner/run-leg.js +5 -1
  66. package/dist/core/runner/runtask.js +2 -2
  67. package/dist/core/runner/sub-agent-deny-lists.d.ts +1 -1
  68. package/dist/core/runner/write-protection-station.d.ts +3 -2
  69. package/dist/core/runner/write-protection-station.js +1 -0
  70. package/dist/core/runner-deps.d.ts +14 -0
  71. package/dist/core/sensitive-path-policy.d.ts +8 -0
  72. package/dist/core/sensitive-path-policy.js +18 -2
  73. package/dist/core/shell-wrapper-projection.d.ts +11 -0
  74. package/dist/core/shell-wrapper-projection.js +56 -0
  75. package/dist/core/shell-wrapper-table.d.ts +144 -34
  76. package/dist/core/shell-wrapper-table.js +40 -20
  77. package/dist/core/synthetic-read-verdict.d.ts +3 -0
  78. package/dist/core/synthetic-read-verdict.js +3 -1
  79. package/dist/core/tool-catalog-entries.d.ts +21 -0
  80. package/dist/core/tool-catalog-entries.js +9 -0
  81. package/dist/core/tool-policy.d.ts +30 -8
  82. package/dist/core/tool-policy.js +9 -7
  83. package/dist/core/tool-spec.d.ts +3 -2
  84. package/dist/core/write-protect.d.ts +4 -0
  85. package/dist/core/write-protect.js +30 -9
  86. package/dist/index.d.ts +5 -3
  87. package/dist/index.js +4 -2
  88. package/dist/orchestration/run-spec.js +8 -7
  89. package/dist/orchestration/workflow.js +1 -1
  90. package/dist/shell/lex.d.ts +7 -0
  91. package/dist/shell/lex.js +17 -0
  92. package/dist/tools/fs/bash-program-position.d.ts +8 -87
  93. package/dist/tools/fs/bash-program-position.js +33 -69
  94. package/dist/tools/fs/bash-readonly-classifier.d.ts +17 -444
  95. package/dist/tools/fs/bash-readonly-classifier.js +169 -1225
  96. package/dist/tools/fs/index.d.ts +3 -2
  97. package/dist/tools/fs/program-read-model.d.ts +694 -0
  98. package/dist/tools/fs/program-read-model.js +1252 -0
  99. package/dist/tools/fs/program-read-model.rows.d.ts +18 -0
  100. package/dist/tools/fs/program-read-model.rows.js +613 -0
  101. package/dist/tools/fs/read-deny-home.d.ts +19 -1
  102. package/dist/tools/fs/read-deny-home.js +62 -24
  103. package/dist/tools/fs/read-deny.d.ts +54 -14
  104. package/dist/tools/fs/read-deny.js +104 -50
  105. package/dist/tools/fs/removal-targets.d.ts +7 -6
  106. package/dist/tools/fs/removal-targets.js +22 -22
  107. package/dist/tools/fs/safety.d.ts +3 -0
  108. package/dist/tools/fs/safety.js +10 -3
  109. package/dist/tools/fs/shell-read-argv.d.ts +85 -0
  110. package/dist/tools/fs/shell-read-argv.js +386 -0
  111. package/dist/tools/fs/shell-read-bindings.d.ts +273 -0
  112. package/dist/tools/fs/shell-read-bindings.js +746 -0
  113. package/dist/tools/fs/shell-read-check.js +23 -15
  114. package/dist/tools/fs/shell-read-establish.js +79 -5
  115. package/dist/tools/fs/shell-read-judge.d.ts +11 -1
  116. package/dist/tools/fs/shell-read-judge.js +9 -1
  117. package/dist/tools/fs/shell-read-plan.d.ts +31 -7
  118. package/dist/tools/fs/shell-read-plan.js +299 -393
  119. package/dist/tools/fs/shell-read-word.d.ts +29 -26
  120. package/dist/tools/fs/shell-read-word.js +30 -38
  121. package/dist/tools/fs/shell-read-write-targets.d.ts +15 -64
  122. package/dist/tools/fs/shell-read-write-targets.js +83 -136
  123. package/dist/tools/fs/shell-reentry-text.d.ts +38 -6
  124. package/dist/tools/fs/shell-reentry-text.js +90 -53
  125. package/package.json +1 -1
  126. package/test/export-surface.snapshot.json +17 -3
  127. package/wire-changes/core@7.46.0.json +1522 -0
@@ -129,6 +129,9 @@ export interface FsViolation {
129
129
  * lands it outside every POSIX/drive root, i.e. a location the working directory does not contain.
130
130
  */
131
131
  export declare function isAbsolutePathForm(p: string): boolean;
132
+ /** Does this absolute spelling name the opening process's STANDARD INPUT (`/dev/stdin`, `/dev/fd/0`, `/proc/<pid>/fd/0`),
133
+ * folded lexically first (`/dev/./stdin`, `/dev//fd/0`)? */
134
+ export declare function namesStandardInput(path: string): boolean;
132
135
  /**
133
136
  * Windows-FAMILY spelling of a path (extracted here so {@link canonicalizeTarget} judges
134
137
  * the UNC family by the same rule): drive-rooted (`C:\…`/`C:/…`), UNC-backslash (`\\host\share`), or a
@@ -3,6 +3,7 @@ import { readDenySentence } from "./read-deny.js";
3
3
  import { homeViewsOf } from "./read-deny-home.js";
4
4
  import { sliceHeadSafe } from "../../core/surrogate-safe-slice.js";
5
5
  import { pathTargetValue, protectivePathTargetOf } from "../../core/tool-registry.js";
6
+ import { readPathViews } from "../../core/effective-path-target.js";
6
7
  import { withGateTimeout, DEFAULT_GATE_AWAIT_TIMEOUT_MS } from "../../core/gate-bounded-await.js";
7
8
  import { thrownText } from "../../core/thrown-value.js";
8
9
  export function fileArgPath(args) {
@@ -41,6 +42,10 @@ const BLOCKED_DEVICE_PATHS = new Set([
41
42
  function isProcStdioFd(key) {
42
43
  return key.startsWith("/proc/") && (key.endsWith("/fd/0") || key.endsWith("/fd/1") || key.endsWith("/fd/2"));
43
44
  }
45
+ export function namesStandardInput(path) {
46
+ const k = path.startsWith("/") ? normalizeAbsPathLexically(path) : "";
47
+ return k === "/dev/stdin" || k === "/dev/fd/0" || (k.startsWith("/proc/") && k.endsWith("/fd/0"));
48
+ }
44
49
  const PROC_SENSITIVE_SUFFIXES = ["/environ", "/cmdline", "/auxv", "/maps", "/mem", "/stat"];
45
50
  function isProcSensitiveFile(key) {
46
51
  return key.startsWith("/proc/") && PROC_SENSITIVE_SUFFIXES.some((suf) => key.endsWith(suf));
@@ -457,7 +462,8 @@ function within(rootCanonical, p) {
457
462
  return pN.identity.startsWith(rootN.identity.endsWith(SEP) ? rootN.identity : rootN.identity + SEP);
458
463
  }
459
464
  export async function resolveKey(env, rootCanonical, path, signal, baseCwd, additionalRoots, exactFileReadExemption, readDeny, containmentFace) {
460
- const canon = await canonicalizeTarget(env, path, signal, pathFamilyOf({ root: rootCanonical }), baseCwd);
465
+ const views = await readPathViews(env, path, baseCwd ?? rootCanonical, { root: rootCanonical, ...(baseCwd !== undefined ? { cwd: baseCwd } : {}) }, signal, { hops: readDeny !== undefined });
466
+ const canon = views.canonical ?? (await canonicalizeTarget(env, path, signal, pathFamilyOf({ root: rootCanonical }), baseCwd));
461
467
  if (!canon.ok)
462
468
  return { ok: false, violation: { code: "invalid", message: canon.message } };
463
469
  const key = canon.key;
@@ -466,8 +472,9 @@ export async function resolveKey(env, rootCanonical, path, signal, baseCwd, addi
466
472
  return { ok: false, violation: { code: "invalid", message: `path "${path}" is a blocked device/special file (reading it can hang the process); refused.` } };
467
473
  }
468
474
  if (readDeny !== undefined) {
469
- const judged = { aliasResolved: canon.aliasResolved === true };
470
- let hit = readDeny.matchTarget(key, lexicalViewOf(path, baseCwd ?? rootCanonical), judged);
475
+ const judged = { aliasResolved: canon.aliasResolved === true, hops: views.hops };
476
+ const lexical = views.lexical?.kind === "path" ? spellingOfIdentity(views.lexical.identity, pathFamilyOf({ root: rootCanonical })) : undefined;
477
+ let hit = readDeny.matchTarget(key, lexical, judged);
471
478
  if (hit === null) {
472
479
  const homeView = await homeViewsOf(env, readDeny, signal).of(key);
473
480
  if (homeView !== undefined)
@@ -0,0 +1,85 @@
1
+ /**
2
+ * The candidates ONE program's words hand the read station — the program read model's rows read word by word: a
3
+ * listed reader's operands and option-value slots, an unlisted program's words (its own row read where it has one,
4
+ * read the speculative way where it has none), a subcommand's words by the subcommand's row, a module's program text,
5
+ * an assignment word's value. `shell-read-plan.ts` threads the segments and their bases; this module is what one
6
+ * segment's argv offers.
7
+ */
8
+ import { type LexedWord } from "../../shell/lex.js";
9
+ import { type Read } from "./shell-read-word.js";
10
+ import { type BindingTable, type BoundValue } from "./shell-read-bindings.js";
11
+ import { subcommandOf, type ArgRole, type ProgramRow } from "./program-read-model.js";
12
+ import type { ReadCandidate, ReadCandidateProvenance } from "./shell-read-plan.js";
13
+ /** A candidate before the threading fills its bases. `alternatives`: the other spellings its word may expand to (a name
14
+ * bound to several values) — one candidate each once the segment's reads are read. */
15
+ export type Bare = Omit<ReadCandidate, "bases"> & {
16
+ readonly alternatives?: readonly string[];
17
+ readonly floors?: readonly BoundValue[];
18
+ };
19
+ /** What reading a segment's words found beside its candidates. */
20
+ export interface WordContext {
21
+ /** The names at this segment. */
22
+ readonly table: BindingTable;
23
+ /** Shell text the arithmetic evaluator runs (a substitution in a subscript it evaluates): the segment re-enters it. */
24
+ readonly reentered: string[];
25
+ /** Program text the station does not read — inline text in a language that may open files, or a program read from
26
+ * standard input: why the segment's reads are not established. */
27
+ programText?: string;
28
+ /** Some program text was found in a slot (whether or not it may open files). */
29
+ programTextSeen: boolean;
30
+ }
31
+ /** The subcommand whose row says how ITS words name paths (operands or a traversal of its own), or `undefined`: a
32
+ * subcommand whose row states only its grammar — its message slots, read by {@link messageWords} — leaves its words to
33
+ * the program's own reading. */
34
+ export declare function pathSubcommandOf(row: ProgramRow | undefined, args: readonly string[]): ReturnType<typeof subcommandOf>;
35
+ /** The sentence of inline program text the station does not read. */
36
+ export declare const programTextSentence: (language: string) => string;
37
+ /** One candidate without its bases (filled by the threading pass): the word's spelling — or `spelled`, a caller's
38
+ * reading of PART of it (an option payload, a `key=value` value) — with the word's own expansion facts. A word whose
39
+ * names may hold several values offers each as its whole spelling; a PART of such a word is not followed through them
40
+ * (it is unresolvable). */
41
+ export declare function bare(r: Read, provenance: ReadCandidateProvenance, spelled?: string): Bare;
42
+ /** The reads one value of a declared role hands over: a candidate per file it names, or — a value the program expands
43
+ * past the listed bound — the word itself, unresolvable. A mount source is a read the daemon makes of the whole tree
44
+ * under it (a read and a traversal root); an object path
45
+ * `<rev>:<path>` offers the word as a path and the `<path>` it spells to the deny judge; program text is never a path
46
+ * ({@link programTextReads}). `alsoOperand`: the walk read the option past the first operand, where the program may
47
+ * have stopped reading options — a path role is still offered (more candidates), but no program text is read there:
48
+ * the word is the operands' (the script's own argument), as the program's delivery reading says. */
49
+ export declare function roleReads(r: Read, provenance: ReadCandidateProvenance, value: string, role: ArgRole, globs: "expanded" | "literal", ctx: WordContext, alsoOperand?: boolean): Bare[];
50
+ /**
51
+ * The candidates a `NAME=value` word hands the judge, both facts about its VALUE (the word is a path to nobody):
52
+ * what path it NAMES, and what the text cannot SETTLE — placed as an operand is wherever an environment value
53
+ * names ONE path (a leading tilde against the declared home; a `:` joins a second). Where the text runs nothing able
54
+ * to open a file, {@link planShellReads} drops them: the value names a path to nobody.
55
+ *
56
+ * Bash applies its assignment-word expansion to such a word even when it is an ARGUMENT to a simple command, so
57
+ * the program receives `NAME=<expanded value>`, and a program that reads its value as a path (`dd if=…`, a build
58
+ * or tool variable, a launcher's environment word) opens what the value names. Two facts of the lexer's
59
+ * {@link AssignmentFact} are read: a tilde the shell EXPANDS in the value (at its start and after each `:`, where
60
+ * unquoted — `value.expansions`) leaves the path unsettled, and the append form (`NAME+=value`) names `<whatever
61
+ * NAME held>` + value, so the whole value is not in the text — but the text it appends IS, and an unset or empty
62
+ * NAME makes it the whole value, so it is a candidate too. A value that is a substitution is the nested plan
63
+ * its body already is, never a candidate of its own text (its placeholder is not a path).
64
+ */
65
+ export declare function pushAssignment(reads: Bare[], w: LexedWord, provenance: ReadCandidateProvenance): void;
66
+ /** Every word of a segment whose program's grammar this parser does not model, from `from` onward: option
67
+ * payloads and path-shaped option words included, the launcher chain's own words excluded, a program declared
68
+ * to open no path operand handing only the environment words in front of it. A program with NO row is read the
69
+ * speculative way: every word is still handed over, but the filesystem decides it
70
+ * ({@link ReadCandidate.speculative}). */
71
+ export declare function unlistedWords(reads: Bare[], toks: readonly Read[], from: number, launcherIndices: ReadonlySet<number>, provenance: ReadCandidateProvenance, ctx: WordContext, program?: {
72
+ readonly index: number;
73
+ readonly basename: string;
74
+ }): void;
75
+ /** The positional operands and declared option-value slots of a listed reader — the program read model's argv walk,
76
+ * one candidate per word the program may open, plus the traversal roots of a recursive form. A subcommand's words
77
+ * are read by its own row; the words a module takes as its program text are program text. */
78
+ export declare function listedVerbCandidates(name: string, argReads: readonly Read[], reads: Bare[], ctx: WordContext): void;
79
+ /** The subscript of a variable-name word (`a[…]`), as the arithmetic text that is evaluated; the empty text for a plain
80
+ * name. */
81
+ export declare function subscriptOnly(word: string): string;
82
+ /** An assignment word's arithmetic: its own subscript (`a[$(…)]=1`, quoted or not — not for a builtin that takes the
83
+ * name `asWritten`), and its value where the name carries the integer attribute (`declare -i n; n='a[$(…)]'`, bash's
84
+ * own `OPTIND` / `RANDOM` / …). */
85
+ export declare function assignmentArithmetic(w: LexedWord, integer: boolean, ctx: WordContext, asWritten?: boolean): void;
@@ -0,0 +1,386 @@
1
+ import { literalTildeSpelling } from "../../shell/lex.js";
2
+ import { expandsOf, readOfText, rewritten } from "./shell-read-word.js";
3
+ import { arithmeticSubstitutions } from "./shell-read-bindings.js";
4
+ import { argvProgramOf, attachedOptionPayloads, isPathShapedToken } from "./bash-readonly-classifier.js";
5
+ import { assignmentArgumentsAreNotPaths, evaluatesOperands, messageWords, moduleTailOf, objectPathsOf, opensNoOperand, programRow, programTextMayOpen, programTextOpensOf, readArgs, selectsContentsTraversal, slotValueCandidates, subcommandOf, valuesAreLiteral, walksCwdWithoutOperand, wordsReadBySomeImplementations, } from "./program-read-model.js";
6
+ export function pathSubcommandOf(row, args) {
7
+ const sub = subcommandOf(row, args);
8
+ return sub !== undefined && (sub.row.operands !== undefined || sub.row.traversal !== undefined) ? sub : undefined;
9
+ }
10
+ export const programTextSentence = (language) => `runs inline ${language} program text, which may open files this station does not read`;
11
+ export function bare(r, provenance, spelled = r.folded) {
12
+ const whole = spelled === r.folded;
13
+ if (!whole && r.values !== undefined)
14
+ return { spelled, raw: r.raw, provenance, expands: "unresolvable" };
15
+ return {
16
+ spelled,
17
+ raw: r.raw,
18
+ provenance,
19
+ expands: r.expands,
20
+ ...(whole && r.values !== undefined ? { alternatives: r.values.slice(1) } : {}),
21
+ ...(whole && r.homeFloor !== undefined ? { floorSpelled: r.homeFloor } : {}),
22
+ ...(whole && r.floorValues !== undefined ? { floors: r.floorValues } : {}),
23
+ };
24
+ }
25
+ export function roleReads(r, provenance, value, role, globs, ctx, alsoOperand = false) {
26
+ if (role.kind === "program-text")
27
+ return alsoOperand ? [] : programTextReads(r, provenance, value, role.language, ctx);
28
+ if (role.kind === "object-path")
29
+ return [bare(r, provenance, value), ...objectPathsOf(value).map((c) => ({ ...bare(r, "object_path", c), denyOnly: true }))];
30
+ const named = slotValueCandidates(value, role, globs);
31
+ if (named === undefined)
32
+ return [{ ...bare(r, provenance), expands: "unresolvable" }];
33
+ if (role.kind === "mount-source")
34
+ return named.flatMap((c) => [bare(r, "mount_source", c), bare(r, "traversal_root", c)]);
35
+ return named.map((c) => bare(r, provenance, c));
36
+ }
37
+ function programTextReads(r, provenance, text, language, ctx) {
38
+ ctx.programTextSeen = true;
39
+ const opens = programTextOpensOf(language);
40
+ if (opens === "arithmetic") {
41
+ ctx.reentered.push(...arithmeticSubstitutions(text, ctx.table));
42
+ return [];
43
+ }
44
+ if (opens === "parsed" || opens === "never")
45
+ return [];
46
+ if (programTextMayOpen(language, text))
47
+ ctx.programText ??= programTextSentence(language);
48
+ return [{ ...bare(r, provenance, text), denyOnly: true }];
49
+ }
50
+ export function pushAssignment(reads, w, provenance) {
51
+ const a = w.assignment;
52
+ if (a === undefined || a.form === "compound")
53
+ return;
54
+ const expandsTilde = a.value.expansions.some((p) => p.expansion?.kind === "tilde");
55
+ const value = expandsTilde ? a.value.folded : literalTildeSpelling(a.value.folded);
56
+ const settled = a.op === "=" && !expandsTilde;
57
+ const named = a.value.folded === "" || (a.op === "+=" ? expandsTilde : !settled) ? undefined : value;
58
+ if (a.value.folded === "" && settled)
59
+ return;
60
+ const v = { spelled: value, raw: a.bare ? a.value.raw : w.raw, provenance };
61
+ const unsettled = provenance !== "env_assignment" ? { ...v, expands: "unresolvable" } : v.spelled.startsWith("~") && !v.spelled.includes(":") ? { ...v, expands: expandsOf(a.value) } : undefined;
62
+ if (!settled && unsettled !== undefined)
63
+ reads.push(unsettled);
64
+ const substitutes = a.value.expansions.some((p) => p.expansion?.kind === "command" || p.expansion?.kind === "process" || p.expansion?.kind === "arithmetic");
65
+ if (named !== undefined && !substitutes)
66
+ reads.push({ spelled: named, raw: w.raw, provenance, expands: expandsOf(w) });
67
+ if (a.value.folded.includes(":"))
68
+ for (const m of a.value.folded.split(":"))
69
+ if (m !== "") {
70
+ const r = readOfText(m);
71
+ reads.push({ spelled: r.folded, raw: r.raw, provenance, expands: r.expands, denyOnly: true });
72
+ }
73
+ }
74
+ export function unlistedWords(reads, toks, from, launcherIndices, provenance, ctx, program) {
75
+ const argv = program !== undefined ? { program: program.basename, from: program.index } : argvProgramOf(toks.map((r) => r.word));
76
+ const argvStart = argv?.from ?? toks.length;
77
+ const slotVerb = argv?.program;
78
+ const slotRow = slotVerb === undefined ? undefined : programRow(slotVerb);
79
+ const noRow = slotVerb !== undefined && slotRow === undefined;
80
+ const speculate = (start, how) => {
81
+ if (!noRow)
82
+ return;
83
+ for (let i = start; i < reads.length; i++) {
84
+ const c = reads[i];
85
+ if (c.expands === "unresolvable" || c.speculative !== undefined || c.denyOnly === true || c.provenance === "program_path" || c.provenance === "env_assignment")
86
+ continue;
87
+ reads[i] = { ...c, speculative: how };
88
+ }
89
+ };
90
+ const message = slotVerb === undefined || argv === undefined ? new Set() : messageWords(slotVerb, toks, argvStart);
91
+ let declaredNotPaths = assignmentArgumentsAreNotPaths(slotVerb ?? "");
92
+ const noPathOperands = slotVerb !== undefined && opensNoOperand(slotVerb);
93
+ const expanding = new Set();
94
+ for (let j = from; j < toks.length; j++) {
95
+ if (launcherIndices.has(j))
96
+ continue;
97
+ if (noPathOperands && j > argvStart && !evaluatesOperands(slotVerb))
98
+ break;
99
+ if (toks[j].expands !== "unresolvable")
100
+ continue;
101
+ expanding.add(j);
102
+ reads.push(bare(toks[j], j === argvStart ? "program_path" : provenance));
103
+ if (j < argvStart)
104
+ pushAssignment(reads, toks[j].word, "env_assignment");
105
+ else if (j > argvStart && !declaredNotPaths)
106
+ pushAssignment(reads, toks[j].word, provenance);
107
+ }
108
+ const positional = [];
109
+ const pastOperand = new Set();
110
+ let stdinOperand = false;
111
+ let globs = "expanded";
112
+ let walkRow = slotRow;
113
+ let walkFrom = argvStart + 1;
114
+ let relocated = false;
115
+ if (argvStart >= toks.length) {
116
+ let endOfOptions = false;
117
+ for (let j = from; j < toks.length; j++) {
118
+ if (launcherIndices.has(j) || expanding.has(j) || message.has(j))
119
+ continue;
120
+ const r = toks[j];
121
+ const word = r.folded;
122
+ if (j > from && !endOfOptions && word === "--") {
123
+ endOfOptions = true;
124
+ continue;
125
+ }
126
+ if (!endOfOptions && word.startsWith("-") && word !== "-") {
127
+ for (const payload of attachedOptionPayloads(word))
128
+ reads.push(bare(r, provenance, payload));
129
+ if (isPathShapedToken(word))
130
+ reads.push(bare(r, provenance));
131
+ continue;
132
+ }
133
+ reads.push(bare(r, provenance));
134
+ pushAssignment(reads, r.word, "env_assignment");
135
+ }
136
+ }
137
+ else if (!noPathOperands) {
138
+ const programWord = toks[argvStart];
139
+ if (!expanding.has(argvStart) && !launcherIndices.has(argvStart) && !message.has(argvStart)) {
140
+ const word = programWord.folded;
141
+ if (isPathShapedToken(word)) {
142
+ if (word.startsWith("-") && word !== "-") {
143
+ for (const payload of attachedOptionPayloads(word))
144
+ reads.push(bare(programWord, provenance, payload));
145
+ reads.push(bare(programWord, provenance));
146
+ }
147
+ else {
148
+ reads.push(bare(programWord, "program_path"));
149
+ if (!declaredNotPaths)
150
+ pushAssignment(reads, programWord.word, provenance);
151
+ }
152
+ }
153
+ }
154
+ const args = toks.slice(argvStart + 1).map((r) => r.folded);
155
+ const sub = pathSubcommandOf(slotRow, args);
156
+ const tail = moduleTailOf(slotRow, args);
157
+ const skipped = (j) => launcherIndices.has(j) || expanding.has(j) || message.has(j) || (tail !== undefined && j >= argvStart + 1 + tail.from);
158
+ const walk = (row, first, last, slotKind) => {
159
+ const at = (k) => first + k;
160
+ const words = toks.slice(first, last).map((r, k) => ({ folded: r.folded, skip: skipped(at(k)) }));
161
+ globs = valuesAreLiteral(row, words.map((w) => w.folded)) ? "literal" : "expanded";
162
+ const someRead = wordsReadBySomeImplementations(row, words.map((w) => w.folded));
163
+ for (const e of readArgs(row, words, { firstOperandSlot: slotKind })) {
164
+ const start = reads.length;
165
+ handleUnlistedEvent(e, at(e.at));
166
+ if (someRead.has(e.at))
167
+ for (let i = start; i < reads.length; i++)
168
+ if (reads[i].expands !== "unresolvable" && reads[i].denyOnly !== true)
169
+ reads[i] = { ...reads[i], speculative: "containment" };
170
+ }
171
+ };
172
+ if (sub !== undefined) {
173
+ walk(slotRow, argvStart + 1, argvStart + 1 + sub.at, false);
174
+ declaredNotPaths = sub.row.operands?.assignmentArguments === "not-paths";
175
+ walkRow = sub.row;
176
+ walkFrom = argvStart + 2 + sub.at;
177
+ relocated = sub.relocated;
178
+ positional.length = 0;
179
+ stdinOperand = false;
180
+ walk(sub.row, walkFrom, toks.length, true);
181
+ }
182
+ else
183
+ walk(slotRow, argvStart + 1, toks.length, "program-text");
184
+ if (tail !== undefined)
185
+ for (let j = argvStart + 1 + tail.from; j < toks.length; j++)
186
+ if (!expanding.has(j))
187
+ reads.push(...roleReads(toks[j], "operand", toks[j].folded, tail.role, globs, ctx));
188
+ }
189
+ function handleUnlistedEvent(e, j) {
190
+ const r = toks[j];
191
+ const start = reads.length;
192
+ switch (e.kind) {
193
+ case "end-of-options":
194
+ return;
195
+ case "dash-word-operand":
196
+ pastOperand.add(j);
197
+ reads.push(bare(r, provenance));
198
+ speculate(start, "whole");
199
+ return;
200
+ case "value": {
201
+ if (!e.consumed) {
202
+ if (r.expands !== "unresolvable")
203
+ reads.push(...roleReads(r, "option_value", r.folded, e.role, globs, ctx, true));
204
+ return;
205
+ }
206
+ if (expanding.has(j))
207
+ return;
208
+ const named = roleReads(r, "option_value", r.folded, e.role, globs, ctx);
209
+ reads.push(...named);
210
+ if (named.some((c) => c.denyOnly !== true))
211
+ positional.push(r);
212
+ return;
213
+ }
214
+ case "option":
215
+ if (e.reading === "no-value")
216
+ return;
217
+ if (e.reading === "speculative") {
218
+ for (const payload of attachedOptionPayloads(r.folded))
219
+ reads.push(bare(r, provenance, payload));
220
+ speculate(start, "containment");
221
+ const whole = reads.length;
222
+ if (isPathShapedToken(r.folded))
223
+ reads.push(bare(r, provenance));
224
+ speculate(whole, "whole");
225
+ return;
226
+ }
227
+ reads.push(...roleReads(r, "option_value", literalTildeSpelling(e.reading.attached), e.reading.role, globs, ctx, pastOperand.has(j)));
228
+ return;
229
+ case "operand": {
230
+ const role = e.dash === undefined ? (e.slot ?? e.role) : undefined;
231
+ const named = role !== undefined ? roleReads(r, provenance, r.folded, role, globs, ctx) : [bare(r, provenance)];
232
+ if (e.dash === "stdin")
233
+ stdinOperand = true;
234
+ else if (named.some((c) => c.denyOnly !== true))
235
+ positional.push(r);
236
+ reads.push(...named);
237
+ speculate(start, r.word.assignment !== undefined ? "whole" : "containment");
238
+ const values = reads.length;
239
+ if (!declaredNotPaths && e.slot === undefined)
240
+ pushAssignment(reads, r.word, provenance);
241
+ speculate(values, "containment");
242
+ return;
243
+ }
244
+ }
245
+ }
246
+ if (walkRow !== undefined && argv !== undefined && selectsContentsTraversal(walkRow, toks.slice(walkFrom).map((x) => x.folded))) {
247
+ const roots = positional.length > 0 ? positional : stdinOperand || !walksCwdWithoutOperand(walkRow) ? [] : [readOfText(".")];
248
+ for (const root of roots)
249
+ reads.push(relocated ? { ...bare(root, "traversal_root"), expands: "unresolvable" } : bare(root, "traversal_root"));
250
+ }
251
+ for (let j = from; j < toks.length; j++) {
252
+ if (launcherIndices.has(j) || j === argvStart)
253
+ continue;
254
+ if (noPathOperands && j > argvStart)
255
+ break;
256
+ const start = reads.length;
257
+ if (toks[j].expands === "glob")
258
+ reads.push(bare(toks[j], provenance));
259
+ speculate(start, "containment");
260
+ }
261
+ }
262
+ export function listedVerbCandidates(name, argReads, reads, ctx) {
263
+ if (opensNoOperand(name))
264
+ return;
265
+ const row = programRow(name);
266
+ const args = argReads.map((r) => r.folded);
267
+ const expanding = new Set();
268
+ argReads.forEach((r, k) => {
269
+ if (!rewritten(r))
270
+ return;
271
+ expanding.add(k);
272
+ reads.push({ ...bare(r, "operand"), expands: "unresolvable" });
273
+ });
274
+ const sub = pathSubcommandOf(row, args);
275
+ const tail = moduleTailOf(row, args);
276
+ if (sub !== undefined) {
277
+ walkListed(row, argReads, 0, sub.at, expanding, reads, ctx, false);
278
+ walkListed(sub.row, argReads, sub.at + 1, argReads.length, expanding, reads, ctx, sub.relocated);
279
+ }
280
+ else
281
+ walkListed(row, argReads, 0, tail !== undefined ? tail.from : argReads.length, expanding, reads, ctx, false);
282
+ if (tail !== undefined)
283
+ for (let k = tail.from; k < argReads.length; k++)
284
+ if (!expanding.has(k))
285
+ reads.push(...roleReads(argReads[k], "operand", args[k], tail.role, "expanded", ctx));
286
+ for (let k = 0; k < args.length; k++) {
287
+ if (expanding.has(k))
288
+ continue;
289
+ if (argReads[k].expands === "glob")
290
+ reads.push(bare(argReads[k], "operand"));
291
+ }
292
+ }
293
+ function walkListed(row, argReads, first, last, expanding, reads, ctx, relocated) {
294
+ const slice = argReads.slice(first, last);
295
+ const args = slice.map((r) => r.folded);
296
+ const positional = [];
297
+ let stdinOperand = false;
298
+ const globs = valuesAreLiteral(row, args) ? "literal" : "expanded";
299
+ const someRead = wordsReadBySomeImplementations(row, args);
300
+ const pastOperand = new Set();
301
+ const offer = (k, value, role, provenance, alsoOperand = false) => {
302
+ const named = roleReads(slice[k], provenance, value, role, globs, ctx, alsoOperand);
303
+ reads.push(...named);
304
+ return named.filter((c) => c.denyOnly !== true).length;
305
+ };
306
+ for (const e of readArgs(row, args.map((folded, k) => ({ folded, skip: expanding.has(first + k) })), { firstOperandSlot: true })) {
307
+ const t = args[e.at];
308
+ const start = reads.length;
309
+ switch (e.kind) {
310
+ case "end-of-options":
311
+ break;
312
+ case "dash-word-operand":
313
+ pastOperand.add(e.at);
314
+ reads.push(bare(slice[e.at], "operand"));
315
+ break;
316
+ case "value": {
317
+ if (expanding.has(first + e.at))
318
+ break;
319
+ if (offer(e.at, t, e.role, "option_value", !e.consumed) > 0 && e.consumed)
320
+ positional.push(slice[e.at]);
321
+ break;
322
+ }
323
+ case "option":
324
+ if (e.reading === "no-value")
325
+ break;
326
+ if (e.reading === "speculative") {
327
+ for (const payload of attachedOptionPayloads(t))
328
+ reads.push(bare(slice[e.at], "option_value", payload));
329
+ break;
330
+ }
331
+ offer(e.at, literalTildeSpelling(e.reading.attached), e.reading.role, "option_value", pastOperand.has(e.at));
332
+ break;
333
+ case "operand": {
334
+ if (e.dash !== undefined) {
335
+ if (e.slot !== undefined)
336
+ break;
337
+ if (e.dash === "stdin")
338
+ stdinOperand = true;
339
+ else
340
+ positional.push(slice[e.at]);
341
+ break;
342
+ }
343
+ if (e.slot !== undefined) {
344
+ if (offer(e.at, t, e.slot, "operand") > 0)
345
+ positional.push(slice[e.at]);
346
+ break;
347
+ }
348
+ let named = 1;
349
+ if (e.role !== undefined)
350
+ named = offer(e.at, t, e.role, "operand");
351
+ else
352
+ reads.push(bare(slice[e.at], "operand"));
353
+ if (named > 0)
354
+ positional.push(slice[e.at]);
355
+ if (row?.operands?.assignmentArguments !== "not-paths")
356
+ pushAssignment(reads, slice[e.at].word, "operand");
357
+ break;
358
+ }
359
+ }
360
+ if (someRead.has(e.at))
361
+ for (let i = start; i < reads.length; i++)
362
+ if (reads[i].expands !== "unresolvable" && reads[i].denyOnly !== true)
363
+ reads[i] = { ...reads[i], speculative: "containment" };
364
+ }
365
+ if (selectsContentsTraversal(row, args)) {
366
+ const roots = positional.length > 0 ? positional : stdinOperand || !walksCwdWithoutOperand(row) ? [] : [readOfText(".")];
367
+ for (const root of roots)
368
+ reads.push(relocated ? { ...bare(root, "traversal_root"), expands: "unresolvable" } : bare(root, "traversal_root"));
369
+ }
370
+ }
371
+ export function subscriptOnly(word) {
372
+ const at = word.indexOf("[");
373
+ return at < 0 ? "" : word.slice(at);
374
+ }
375
+ export function assignmentArithmetic(w, integer, ctx, asWritten = false) {
376
+ const a = w.assignment;
377
+ if (a === undefined) {
378
+ if (!asWritten)
379
+ ctx.reentered.push(...arithmeticSubstitutions(subscriptOnly(w.folded), ctx.table));
380
+ return;
381
+ }
382
+ if (a.subscript !== undefined && !asWritten)
383
+ ctx.reentered.push(...arithmeticSubstitutions(`[${a.subscript.folded}]`, ctx.table));
384
+ if (integer)
385
+ ctx.reentered.push(...arithmeticSubstitutions(a.value.folded, ctx.table));
386
+ }