@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
@@ -0,0 +1,746 @@
1
+ import { argvOf, lexShell, literalTildeSpelling, substitutionsInHeredocBody } from "../../shell/lex.js";
2
+ import { readProgramPosition } from "./bash-program-position.js";
3
+ import { declaredAttributes, programRow, readArgRoles } from "./program-read-model.js";
4
+ import { codeBodies } from "./shell-reentry-text.js";
5
+ export const SHELL_INTEGER_VARIABLES = new Set([
6
+ "RANDOM", "SECONDS", "LINENO", "OPTIND", "MAILCHECK", "HISTCMD", "SRANDOM", "EPOCHSECONDS", "EPOCHREALTIME", "BASHPID", "BASH_MONOSECONDS",
7
+ "BASH_TRAPSIG", "COLUMNS", "LINES", "SHLVL", "ERRNO", "TMOUT", "HISTSIZE", "SAVEHIST", "TRY_BLOCK_ERROR", "TRY_BLOCK_INTERRUPT", "KEYTIMEOUT",
8
+ "LISTMAX", "LOGCHECK", "PERIOD", "FUNCNEST", "UID", "EUID", "GID", "EGID", "REPORTTIME", "REPORTMEMORY", "DIRSTACKSIZE", "BAUD",
9
+ "ZLE_RPROMPT_INDENT", "MBEGIN", "MEND", "PPID", "ARGC", "ZSH_SUBSHELL", "TTYIDLE", "status",
10
+ ]);
11
+ const SHELL_SET_VARIABLES = new Set(["PWD", "OLDPWD", "REPLY", "OPTARG", "MAPFILE", "BASH_REMATCH", "PIPESTATUS", "FUNCNAME", "BASH_COMMAND", "BASH_SUBSHELL", "COPROC", "GROUPS", "SHELLOPTS", "BASHOPTS", "BASH_VERSINFO"]);
12
+ export function shellKeepsName(name) {
13
+ return SHELL_INTEGER_VARIABLES.has(name) || SHELL_SET_VARIABLES.has(name);
14
+ }
15
+ const OPENING_WORDS = new Set(["if", "case", "for", "while", "until", "select", "{"]);
16
+ const CLOSING_WORDS = new Set(["fi", "esac", "done", "}"]);
17
+ const SWITCHING_WORDS = new Set(["else", "elif"]);
18
+ const SPECIAL_BUILTINS = new Set([":", ".", "break", "continue", "eval", "exec", "exit", "export", "readonly", "return", "set", "shift", "source", "times", "trap", "unset"]);
19
+ const IDENTIFIER = /^[A-Za-z_][A-Za-z0-9_]*$/;
20
+ const ATTRIBUTE_KINDS = { integer: "integer", transformed: "transformed", frozen: "frozen" };
21
+ const EVERY_ATTRIBUTE = Object.values(ATTRIBUTE_KINDS).map((kind) => ({ kind }));
22
+ export function projectSegment(segment) {
23
+ const tokens = [...segment.tokens];
24
+ let grouped = tokens.some((t) => t.kind === "group");
25
+ const definesFunction = dropDefinitionHeads(tokens).some((n) => n > 0);
26
+ dropCasePattern(tokens);
27
+ const words = tokens.filter((t) => {
28
+ if (t.kind !== "word")
29
+ return false;
30
+ if (t.raw === t.folded && (t.folded === "{" || t.folded === "}")) {
31
+ grouped = true;
32
+ return false;
33
+ }
34
+ return true;
35
+ });
36
+ return { argv: words, redirects: tokens.filter((t) => t.kind === "redirect"), grouped, definesFunction };
37
+ }
38
+ function isDefinitionName(word) {
39
+ return IDENTIFIER.test(word.folded) || (word.atCommandPosition && word.expansions.length === 0 && !/[?*+@!]$/.test(word.raw));
40
+ }
41
+ export function dropDefinitionHeads(tokens) {
42
+ const heads = new Array(tokens.length + 1).fill(0);
43
+ const drop = (k, count) => {
44
+ tokens.splice(k, count);
45
+ heads.splice(k, count);
46
+ heads[k]++;
47
+ };
48
+ for (let k = 0; k + 1 < tokens.length; k++) {
49
+ const [a, b, c, d] = [tokens[k], tokens[k + 1], tokens[k + 2], tokens[k + 3]];
50
+ if (a.kind === "word" && a.raw === "function" && a.atCommandPosition && b.kind === "word") {
51
+ drop(k--, c?.kind === "group" && c.op === "(" && d?.kind === "group" && d.op === ")" ? 4 : 2);
52
+ }
53
+ }
54
+ for (let k = 0; k + 2 < tokens.length; k++) {
55
+ const [a, b, c] = [tokens[k], tokens[k + 1], tokens[k + 2]];
56
+ if (a.kind === "word" && b.kind === "group" && b.op === "(" && c.kind === "group" && c.op === ")" && isDefinitionName(a))
57
+ drop(k, 3);
58
+ }
59
+ return heads;
60
+ }
61
+ function dropCasePattern(tokens) {
62
+ const end = casePatternEnd(tokens);
63
+ if (end >= 0)
64
+ tokens.splice(0, end + 1);
65
+ }
66
+ function casePatternEnd(tokens) {
67
+ const open = [];
68
+ let end = -1;
69
+ for (let k = 0; k < tokens.length; k++) {
70
+ const t = tokens[k];
71
+ if (t.kind === "word" && t.raw === "[[")
72
+ return -1;
73
+ if (t.kind !== "group")
74
+ continue;
75
+ if (t.op === "(") {
76
+ open.push(k);
77
+ continue;
78
+ }
79
+ const from = open.pop();
80
+ if (from !== undefined && extglobParen(tokens, from))
81
+ continue;
82
+ if (tokens[k + 1]?.kind === "word")
83
+ end = k;
84
+ }
85
+ return end;
86
+ }
87
+ function extglobParen(tokens, at) {
88
+ const before = tokens[at - 1];
89
+ return before?.kind === "word" && /[?*+@!]$/.test(before.raw);
90
+ }
91
+ function blockStructure(segments) {
92
+ let depth = 0;
93
+ let bodies = 0;
94
+ const frames = [];
95
+ const deeper = () => {
96
+ depth++;
97
+ for (const f of frames)
98
+ if (!f.opened && depth > f.body.base)
99
+ f.opened = true;
100
+ };
101
+ const shallower = () => {
102
+ depth--;
103
+ while (frames.length > 0 && frames[frames.length - 1].opened && depth <= frames[frames.length - 1].body.base)
104
+ frames.pop();
105
+ };
106
+ return segments.map((segment) => {
107
+ const tokens = [...segment.tokens];
108
+ const heads = dropDefinitionHeads(tokens);
109
+ const startBodies = (k) => {
110
+ for (let n = heads[k]; n > 0; n--)
111
+ frames.push({ body: { id: bodies++, base: depth }, opened: false });
112
+ };
113
+ const arithmetic = isArithmeticCommand(segment);
114
+ const patternEnd = casePatternEnd(tokens);
115
+ const skipped = new Set();
116
+ const open = [];
117
+ let lowest = depth;
118
+ let words;
119
+ let body;
120
+ const switches = [];
121
+ const innermost = () => [...frames].reverse().find((f) => f.opened)?.body;
122
+ const visit = (t, k) => {
123
+ if (t.kind === "group") {
124
+ if (arithmetic || skipped.has(k))
125
+ return;
126
+ if (t.op === "(") {
127
+ if (extglobParen(tokens, k)) {
128
+ skipped.add(k);
129
+ open.push(-1);
130
+ return;
131
+ }
132
+ open.push(k);
133
+ if (k <= patternEnd)
134
+ return;
135
+ deeper();
136
+ return;
137
+ }
138
+ const from = open.pop();
139
+ if (from === -1)
140
+ return;
141
+ if (k <= patternEnd) {
142
+ if (k === patternEnd)
143
+ switches.push(depth);
144
+ return;
145
+ }
146
+ shallower();
147
+ lowest = Math.min(lowest, depth);
148
+ return;
149
+ }
150
+ if (t.kind !== "word")
151
+ return;
152
+ const keyword = t.atCommandPosition && t.raw === t.folded ? t.folded : undefined;
153
+ if (keyword !== undefined && OPENING_WORDS.has(keyword))
154
+ deeper();
155
+ else if (keyword !== undefined && CLOSING_WORDS.has(keyword)) {
156
+ shallower();
157
+ lowest = Math.min(lowest, depth);
158
+ }
159
+ else if (keyword !== undefined && SWITCHING_WORDS.has(keyword))
160
+ switches.push(depth);
161
+ else if (k > patternEnd) {
162
+ words ??= depth;
163
+ body = innermost();
164
+ }
165
+ };
166
+ tokens.forEach((t, k) => {
167
+ startBodies(k);
168
+ visit(t, k);
169
+ let settled = false;
170
+ for (let i = frames.length - 1; i >= 0 && !frames[i].opened; i--) {
171
+ const f = frames[i];
172
+ f.opened = true;
173
+ settled = true;
174
+ if (t.kind === "word" && t.raw === "[[" && t.atCommandPosition)
175
+ f.conditional = true;
176
+ else if (t.kind === "group" && t.op === "(" && arithmetic)
177
+ f.parens = 0;
178
+ else
179
+ f.flat = true;
180
+ }
181
+ if (settled && t.kind === "word")
182
+ body = innermost();
183
+ const top = frames[frames.length - 1];
184
+ if (top?.parens !== undefined && t.kind === "group") {
185
+ top.parens += t.op === "(" ? 1 : -1;
186
+ if (top.parens <= 0)
187
+ frames.pop();
188
+ }
189
+ if (t.kind === "word" && t.raw === "]]")
190
+ while (frames.length > 0 && frames[frames.length - 1].conditional === true)
191
+ frames.pop();
192
+ });
193
+ while (frames.length > 0 && frames[frames.length - 1].flat === true)
194
+ frames.pop();
195
+ startBodies(tokens.length);
196
+ return { words: words ?? depth, after: depth, lowest, switches, ...(body !== undefined ? { body } : {}) };
197
+ });
198
+ }
199
+ const ONE_WORD = /^[A-Za-z0-9_./+@:%=,-]+$/;
200
+ const WORD_VALUES_MAX = 64;
201
+ export function wordValues(word, table, quoted, overlay = new Map()) {
202
+ const pieces = word.pieces;
203
+ if (!pieces.some((p) => p.expansion?.kind === "parameter" || p.expansion?.kind === "tilde"))
204
+ return undefined;
205
+ if (table === undefined)
206
+ return "unknown";
207
+ const start = pieces[0]?.span[0] ?? 0;
208
+ const raw = word.raw;
209
+ let combos = [{ text: "", glob: false }];
210
+ const stateOf = (name) => overlay.get(name) ?? table.stateOf(name);
211
+ for (const p of pieces) {
212
+ const e = p.expansion;
213
+ let options;
214
+ if (e === undefined) {
215
+ if (p.literal === undefined)
216
+ return "unknown";
217
+ options = [{ spelled: p.literal }];
218
+ }
219
+ else if (e.kind === "glob") {
220
+ const written = raw === undefined ? undefined : raw.slice(p.span[0] - start, p.span[1] - start);
221
+ if (written === undefined)
222
+ return "unknown";
223
+ options = [{ spelled: written, glob: true }];
224
+ }
225
+ else if (e.kind === "tilde") {
226
+ if (e.body !== "~")
227
+ return "unknown";
228
+ const home = stateOf("HOME");
229
+ if (home === "unknown")
230
+ return "unknown";
231
+ options = home;
232
+ }
233
+ else if (e.kind === "parameter" && e.name !== undefined && (e.body === undefined || e.body === e.name)) {
234
+ const state = stateOf(e.name);
235
+ if (state === "unknown")
236
+ return "unknown";
237
+ if (p.quotedBy === undefined && !quoted && (table.ifsTouched || state.some((v) => v.glob === true || !ONE_WORD.test(v.spelled))))
238
+ return "unknown";
239
+ options = state;
240
+ }
241
+ else
242
+ return "unknown";
243
+ const next = [];
244
+ for (const c of combos)
245
+ for (const o of options)
246
+ next.push({ text: c.text + o.spelled, glob: c.glob || o.glob === true });
247
+ if (next.length > WORD_VALUES_MAX)
248
+ return "unknown";
249
+ combos = next;
250
+ }
251
+ const out = [];
252
+ for (const c of combos)
253
+ if (!out.some((v) => v.spelled === c.text && (v.glob === true) === c.glob))
254
+ out.push(c.glob ? { spelled: c.text, glob: true } : { spelled: c.text });
255
+ return out;
256
+ }
257
+ export function wordFloorValues(word, table) {
258
+ if (table === undefined)
259
+ return [];
260
+ let combos = [{ spelled: "" }];
261
+ for (const p of word.pieces) {
262
+ const e = p.expansion;
263
+ let options;
264
+ if (e === undefined) {
265
+ if (p.literal === undefined)
266
+ return [];
267
+ options = [{ spelled: p.literal }];
268
+ }
269
+ else if (e.kind === "parameter" && e.name !== undefined) {
270
+ const state = table.stateOf(e.name);
271
+ options = state !== "unknown" && (e.body === undefined || e.body === e.name) ? state : table.floorOf(e.name);
272
+ }
273
+ else
274
+ return [];
275
+ if (options.length === 0)
276
+ return [];
277
+ const next = [];
278
+ for (const c of combos)
279
+ for (const o of options)
280
+ next.push(c.glob === true || o.glob === true ? { spelled: c.spelled + o.spelled, glob: true } : { spelled: c.spelled + o.spelled });
281
+ if (next.length > WORD_VALUES_MAX)
282
+ return [];
283
+ combos = next;
284
+ }
285
+ return combos;
286
+ }
287
+ export function valueOf(word, table, quoted, overlay = new Map()) {
288
+ const values = wordValues(word, table, quoted, overlay);
289
+ if (values !== undefined)
290
+ return values;
291
+ if (word.pieces.every((p) => p.expansion === undefined && p.literal !== undefined))
292
+ return [{ spelled: word.pieces.map((p) => p.literal).join("") }];
293
+ const folded = word.folded;
294
+ if (folded !== undefined && word.pieces.every((p) => p.expansion === undefined ? p.literal !== undefined : p.expansion.kind === "glob"))
295
+ return [{ spelled: folded, glob: true }];
296
+ return "unknown";
297
+ }
298
+ export function bindingEffectOf(segment, argv, shape, readValue) {
299
+ const bindings = [];
300
+ let all = shape.definesFunction ? "reset" : undefined;
301
+ const unknownAll = () => void (all = "unknown");
302
+ const overlay = new Map();
303
+ let scope;
304
+ const bind = (b) => {
305
+ bindings.push(scope !== undefined ? { ...b, scope } : b);
306
+ if (b.prefix !== true)
307
+ overlay.set(b.name, b.state);
308
+ };
309
+ const kill = (name) => bind({ name, state: "unknown" });
310
+ const nameOf = (w) => {
311
+ if (w.pieces.some((p) => p.expansion !== undefined || p.literal === undefined))
312
+ return "computed";
313
+ return nameIn(w.pieces.map((p) => p.literal).join(""));
314
+ };
315
+ const killNamed = (name) => {
316
+ if (name === "computed")
317
+ unknownAll();
318
+ else if (name !== undefined)
319
+ kill(name);
320
+ };
321
+ const wordsOfSegment = segment.tokens.flatMap((t) => (t.kind === "word" ? [t] : t.kind === "redirect" && t.target !== undefined ? [t.target] : []));
322
+ for (const w of wordsOfSegment)
323
+ for (const name of assignedByPieces(w.pieces))
324
+ kill(name);
325
+ for (const t of segment.tokens) {
326
+ if (t.kind !== "heredoc" || t.quoted)
327
+ continue;
328
+ if (t.body.includes("${"))
329
+ unknownAll();
330
+ for (const s of substitutionsInHeredocBody(t.body) ?? [])
331
+ if (s.kind === "arithmetic")
332
+ for (const name of arithmeticAssignedNames(s.body))
333
+ kill(name);
334
+ }
335
+ segment.tokens.forEach((t, i) => {
336
+ const next = segment.tokens[i + 1];
337
+ const m = t.kind === "word" && next?.kind === "redirect" ? /^\{([A-Za-z_][A-Za-z0-9_]*)\}$/.exec(t.raw) : null;
338
+ if (m !== null)
339
+ kill(m[1]);
340
+ });
341
+ const tokens = segment.tokens;
342
+ if (isArithmeticCommand(segment)) {
343
+ for (const name of arithmeticAssignedNames(argv.map((w) => w.raw).join(" ")))
344
+ kill(name);
345
+ return { bindings, all };
346
+ }
347
+ const head = argv[0];
348
+ const headWord = head !== undefined && head.raw === head.folded ? head.folded : undefined;
349
+ if (headWord === "for" || headWord === "select") {
350
+ if (tokens.some((t) => t.kind === "group"))
351
+ unknownAll();
352
+ const name = argv[1] !== undefined ? nameOf(argv[1]) : undefined;
353
+ if (name === undefined || name === "computed") {
354
+ unknownAll();
355
+ return { bindings, all };
356
+ }
357
+ const inAt = argv.findIndex((w, i) => i >= 2 && w.raw === "in");
358
+ let state = "unknown";
359
+ if (headWord === "for" && inAt === 2 && argv.length > 3) {
360
+ const values = [];
361
+ for (const w of argv.slice(3)) {
362
+ const v = readValue(w, false, overlay);
363
+ if (v === "unknown") {
364
+ values.length = 0;
365
+ break;
366
+ }
367
+ values.push(...v);
368
+ }
369
+ if (values.length > 0)
370
+ state = values;
371
+ }
372
+ bind({ name, state, loopVar: true });
373
+ return { bindings, all };
374
+ }
375
+ if (headWord === "coproc" && argv[1] !== undefined)
376
+ killNamed(nameOf(argv[1]));
377
+ const pos = readProgramPosition(argv);
378
+ const program = pos.program;
379
+ const row = program === undefined ? undefined : programRow(program.basename);
380
+ const statement = program === undefined && pos.launchers.length === 0 && pos.refusal === undefined;
381
+ for (const k of pos.assignments) {
382
+ const a = argv[k].assignment;
383
+ if (a === undefined)
384
+ continue;
385
+ if (statement)
386
+ bind({ name: a.name, state: assignedState(a, readValue, overlay), ...(a.form === "compound" ? { floor: compoundFloor(a.value.raw, readValue, overlay) } : {}) });
387
+ else if (program !== undefined && SPECIAL_BUILTINS.has(program.basename))
388
+ bind({ name: a.name, state: assignedState(a, readValue, overlay), prefix: true });
389
+ }
390
+ const programWord = program === undefined ? undefined : argv[program.index];
391
+ if (programWord !== undefined && nameOf(programWord) === "computed" && !programWord.pieces.some((p) => p.literal?.includes("/") === true)) {
392
+ unknownAll();
393
+ return { bindings, all, attributes: EVERY_ATTRIBUTE, references: true };
394
+ }
395
+ if (program === undefined || row === undefined)
396
+ return { bindings, all };
397
+ const args = argv.slice(program.index + 1);
398
+ const folded = args.map((w) => w.folded);
399
+ if (row.reentry?.kind === "shell" && (row.reentry.text === "words" || row.reentry.text === "file-operand"))
400
+ unknownAll();
401
+ for (const body of codeBodies(program.basename, folded)) {
402
+ const inner = namesBoundIn(body);
403
+ if (inner === "all")
404
+ unknownAll();
405
+ else
406
+ for (const name of inner)
407
+ kill(name);
408
+ }
409
+ if (row.builtin === "declaration") {
410
+ scope = args.some((w) => w.folded.startsWith("-") || w.folded.startsWith("+")) ? "unread" : program.basename === "local" ? "local" : undefined;
411
+ const assigns = declaredAttributes(row, folded);
412
+ const kinds = [...assigns].filter((k) => k !== "reference" && k !== "unsettled");
413
+ const storesText = [...assigns].every((k) => k === "frozen");
414
+ let references = assigns.has("reference");
415
+ const attributes = [];
416
+ const expanded = new Map(overlay);
417
+ for (const w of args) {
418
+ const a = w.assignment;
419
+ const name = a?.name ?? nameOf(w);
420
+ if (name === "computed") {
421
+ unknownAll();
422
+ attributes.push(...EVERY_ATTRIBUTE);
423
+ references = true;
424
+ continue;
425
+ }
426
+ if (name === undefined)
427
+ continue;
428
+ const refused = attributes.some((x) => x.name === undefined || x.name === name);
429
+ if (a?.form === "compound")
430
+ bind({ name, state: "unknown", floor: compoundFloor(a.value.raw, readValue, expanded) });
431
+ else if (a === undefined || !storesText || refused)
432
+ kill(name);
433
+ else
434
+ bind({ name, state: assignedState(a, readValue, expanded) });
435
+ attributes.push(...kinds.map((kind) => ({ name, kind })));
436
+ }
437
+ const inFunction = program.basename === "local" ? { inFunction: true } : {};
438
+ if (references) {
439
+ unknownAll();
440
+ return { bindings, all, attributes, references: true, ...inFunction };
441
+ }
442
+ return { bindings, all, attributes, ...inFunction };
443
+ }
444
+ for (const e of readArgRoles(row, folded)) {
445
+ const role = e.kind === "value" || e.kind === "operand" ? e.role : e.kind === "option" && typeof e.reading === "object" ? e.reading.role : undefined;
446
+ if (role?.kind === "program-text" && role.language === "arithmetic" && e.kind !== "end-of-options" && e.kind !== "dash-word-operand") {
447
+ for (const name of arithmeticAssignedNames(e.kind === "option" && typeof e.reading === "object" ? e.reading.attached : folded[e.at]))
448
+ kill(name);
449
+ continue;
450
+ }
451
+ if (role?.kind !== "binds-name")
452
+ continue;
453
+ if (e.kind === "option" && typeof e.reading === "object") {
454
+ killNamed(nameOf(args[e.at]) === "computed" ? "computed" : nameIn(e.reading.attached));
455
+ continue;
456
+ }
457
+ killNamed(nameOf(args[e.at]));
458
+ }
459
+ const nameLetters = new Set(row.grammars.flatMap((g) => Object.entries(g.short).flatMap(([letter, s]) => (s.value?.kind === "binds-name" ? [letter] : []))));
460
+ if (nameLetters.size > 0 && readArgRoles(row, folded).some((e) => e.kind === "option" && e.reading === "speculative" && [...folded[e.at].slice(1)].some((ch) => nameLetters.has(ch))))
461
+ unknownAll();
462
+ return { bindings, all };
463
+ }
464
+ function nameIn(text) {
465
+ return /^([A-Za-z_][A-Za-z0-9_]*)(?:\[.*\])?$/s.exec(text)?.[1];
466
+ }
467
+ function compoundElementWords(raw) {
468
+ const inner = /^\(([\s\S]*)\)$/.exec(raw)?.[1];
469
+ const l = inner === undefined ? undefined : lexShell(inner);
470
+ if (l === undefined || "parseFailure" in l)
471
+ return [];
472
+ return l.segments.flatMap((sg) => argvOf(sg)).flatMap((element) => {
473
+ const prefix = /^\[[^\]]*\]\+?=/.exec(element.raw)?.[0];
474
+ const assigned = prefix === undefined ? undefined : lexShell(`V=${element.raw.slice(prefix.length)}`);
475
+ const value = assigned === undefined || "parseFailure" in assigned ? undefined : argvOf(assigned.segments[0])[0]?.assignment?.value;
476
+ const folded = /^\[[^\]]*\]\+?=/.exec(element.folded)?.[0];
477
+ return [prefix === undefined ? element : value, ...(folded !== undefined ? [{ pieces: element.pieces, folded: element.folded.slice(folded.length), tail: true }] : [])].filter((w) => w !== undefined);
478
+ });
479
+ }
480
+ function compoundFloor(raw, readValue, overlay) {
481
+ return compoundElementWords(raw).flatMap((w) => {
482
+ const v = w.tail === true ? "unknown" : readValue(w, true, overlay);
483
+ if (v !== "unknown")
484
+ return [...v];
485
+ const text = w.pieces.some((q) => q.expansion?.kind === "tilde") ? w.folded : literalTildeSpelling(w.folded);
486
+ return [w.pieces.some((q) => q.expansion?.kind === "glob") ? { spelled: text, glob: true } : { spelled: text }];
487
+ });
488
+ }
489
+ function assignedState(a, readValue, overlay) {
490
+ if (a.op !== "=" || a.subscript !== undefined || a.form !== "scalar" || a.value.unresolved !== undefined)
491
+ return "unknown";
492
+ return readValue({ pieces: a.value.pieces }, true, overlay);
493
+ }
494
+ function assignedByPieces(pieces) {
495
+ const out = [];
496
+ for (const p of pieces) {
497
+ const e = p.expansion;
498
+ if (e?.kind === "parameter" && e.name !== undefined && e.body !== undefined && new RegExp(`^${e.name}:?=`).test(e.body))
499
+ out.push(e.name);
500
+ if (e?.kind === "arithmetic" && e.body !== undefined)
501
+ out.push(...arithmeticAssignedNames(e.body));
502
+ }
503
+ return out;
504
+ }
505
+ export function arithmeticAssignedNames(text) {
506
+ const assigns = /(?:^|[^=!<>])=(?!=)|\+\+|--/.test(text);
507
+ return assigns ? [...new Set(text.match(/[A-Za-z_][A-Za-z0-9_]*/g) ?? [])] : [];
508
+ }
509
+ export function namesBoundIn(text, depth = 0) {
510
+ if (depth > 4)
511
+ return "all";
512
+ const lexed = lexShell(text);
513
+ if ("parseFailure" in lexed)
514
+ return "all";
515
+ const names = new Set();
516
+ const nothing = () => "unknown";
517
+ for (const segment of lexed.segments) {
518
+ const p = projectSegment(segment);
519
+ const effect = bindingEffectOf(segment, p.argv, p, nothing);
520
+ if (effect.all === "unknown")
521
+ return "all";
522
+ for (const b of effect.bindings)
523
+ names.add(b.name);
524
+ }
525
+ return names;
526
+ }
527
+ export function textBindsHome(text) {
528
+ const names = namesBoundIn(text);
529
+ return names === "all" || names.has("HOME");
530
+ }
531
+ const NOTHING_INHERITED = { attributes: [], references: false };
532
+ export class BindingThread {
533
+ segments;
534
+ named;
535
+ initial;
536
+ boundAnywhere;
537
+ loops;
538
+ homeStartsTouched;
539
+ entries = [];
540
+ attributesAt;
541
+ unreadable;
542
+ blocks;
543
+ detached;
544
+ constructor(segments, named, initial, boundAnywhere, loops, homeStartsTouched = false, inherited = NOTHING_INHERITED) {
545
+ this.segments = segments;
546
+ this.named = named;
547
+ this.initial = initial;
548
+ this.boundAnywhere = boundAnywhere;
549
+ this.loops = loops;
550
+ this.homeStartsTouched = homeStartsTouched;
551
+ this.blocks = blockStructure(segments);
552
+ this.detached = segments.map((s, i) => s.connector === "|" || segments[i + 1]?.connector === "|" || segments[i + 1]?.connector === "&");
553
+ this.unreadable = [...(inherited.references ? [{ at: -1 }] : []), ...named.flatMap((effect, at) => this.unreadableBy(effect, at))];
554
+ this.attributesAt = [...inherited.attributes.map((a) => ({ ...a, at: -1 })), ...named.flatMap((effect, at) => (effect.attributes ?? []).map((a) => ({ ...a, at })))];
555
+ }
556
+ inheritedAt(j) {
557
+ const holds = (at) => j === undefined || this.mayRunBefore(at, j);
558
+ return {
559
+ attributes: this.attributesAt.filter((x) => holds(x.at)).map(({ name, kind }) => (name !== undefined ? { name, kind } : { kind })),
560
+ references: this.unreadable.some((u) => u.name === undefined && u.body === undefined && holds(u.at)),
561
+ };
562
+ }
563
+ unreadableBy(effect, at) {
564
+ const body = this.blocks[at].body;
565
+ const out = [];
566
+ if (body !== undefined) {
567
+ for (const b of effect.bindings)
568
+ if (b.scope !== "local" && (b.scope === "unread" || !this.declaredLocal(b.name, at, body)))
569
+ out.push({ at, name: b.name, body });
570
+ if (effect.all === "unknown")
571
+ out.push({ at, body });
572
+ }
573
+ if (effect.references === true)
574
+ out.push({ at });
575
+ return out;
576
+ }
577
+ mayRunBefore(x, e) {
578
+ if (x < e)
579
+ return true;
580
+ return this.blocks[e].body !== undefined || [...this.loops].some(([head, end]) => head <= e && x < end);
581
+ }
582
+ declaredLocal(name, at, body) {
583
+ for (let k = 0; k < at; k++) {
584
+ const connector = this.segments[k].connector;
585
+ if (this.blocks[k].body?.id !== body.id || this.detached[k] === true || connector === "&&" || connector === "||")
586
+ continue;
587
+ if (this.named[k].bindings.some((b) => b.scope === "local" && b.name === name) && this.heldThrough(k, at))
588
+ return true;
589
+ }
590
+ return false;
591
+ }
592
+ heldThrough(at, j) {
593
+ const depth = this.blocks[at].words;
594
+ if (this.blocks[at].after < depth)
595
+ return false;
596
+ for (let k = at + 1; k <= j; k++)
597
+ if (this.blocks[k].lowest < depth || this.blocks[k].switches.includes(depth))
598
+ return false;
599
+ return true;
600
+ }
601
+ apply(at, effect, andRun, loopEnd) {
602
+ const detached = this.detached[at] === true;
603
+ const refused = effect.inFunction === true && this.blocks[at].body === undefined;
604
+ if (effect.all !== undefined)
605
+ this.entries.push({ at, state: effect.all === "reset" ? "reset" : "unknown", detached: false, andRun: false });
606
+ for (const b of effect.bindings) {
607
+ this.entries.push({
608
+ at, name: b.name, state: refused ? "unknown" : b.state, detached: detached || b.prefix === true, andRun,
609
+ ...(b.loopVar === true && loopEnd !== undefined ? { certainUntil: loopEnd } : {}),
610
+ ...(b.floor !== undefined && b.floor.length > 0 ? { floor: b.floor } : {}),
611
+ });
612
+ }
613
+ }
614
+ leftBody(at, body, j) {
615
+ if (this.blocks[at].after <= body.base)
616
+ return true;
617
+ for (let k = at + 1; k <= j; k++)
618
+ if (this.blocks[k].lowest <= body.base)
619
+ return true;
620
+ return false;
621
+ }
622
+ kill(at, names) {
623
+ if (names === "all") {
624
+ this.entries.push({ at, state: "unknown", detached: false, andRun: false });
625
+ return;
626
+ }
627
+ for (const name of names)
628
+ this.entries.push({ at, name, state: "unknown", detached: false, andRun: false });
629
+ }
630
+ tableAt(j, andRunStart) {
631
+ const certain = (e) => {
632
+ if (e.certainUntil !== undefined && j < e.certainUntil)
633
+ return true;
634
+ if (e.detached || !this.heldThrough(e.at, j))
635
+ return false;
636
+ return !e.andRun || e.at >= andRunStart;
637
+ };
638
+ const readerBody = this.blocks[j].body;
639
+ const lost = (u) => {
640
+ if (u.body === undefined)
641
+ return this.mayRunBefore(u.at, j);
642
+ if (readerBody !== undefined && readerBody.id !== u.body.id)
643
+ return true;
644
+ return u.at < j && this.leftBody(u.at, u.body, j);
645
+ };
646
+ const before = (e) => e.at < j || (e.at === j && e.state === "reset");
647
+ const given = (name, at, integer) => this.attributesAt.some((x) => (x.name === undefined || x.name === name) && (integer !== true || x.kind === "integer") && x.at !== at && this.mayRunBefore(x.at, at));
648
+ const stateOf = (name) => {
649
+ if (shellKeepsName(name))
650
+ return "unknown";
651
+ if (this.unreadable.some((u) => (u.name === undefined || u.name === name) && lost(u)))
652
+ return "unknown";
653
+ const values = [];
654
+ const add = (vs) => { for (const v of vs)
655
+ if (!values.some((x) => x.spelled === v.spelled && x.glob === v.glob))
656
+ values.push(v); };
657
+ for (let i = this.entries.length - 1; i >= 0; i--) {
658
+ const e = this.entries[i];
659
+ if (!before(e) || (e.name !== undefined && e.name !== name))
660
+ continue;
661
+ const state = e.state === "reset" ? (name === "HOME" && this.boundAnywhere("HOME") ? "unknown" : this.initial(name)) : e.state;
662
+ if (state === "unknown")
663
+ return "unknown";
664
+ if (e.name !== undefined && given(e.name, e.at))
665
+ return "unknown";
666
+ add(state);
667
+ if (certain(e))
668
+ return values;
669
+ }
670
+ const start = this.initial(name);
671
+ if (start === "unknown")
672
+ return "unknown";
673
+ add(start);
674
+ return values;
675
+ };
676
+ const touched = (name) => this.entries.some((e) => before(e) && (e.name === undefined || e.name === name));
677
+ return {
678
+ stateOf,
679
+ isInteger: (name) => SHELL_INTEGER_VARIABLES.has(name) || given(name, j, true),
680
+ ifsTouched: touched("IFS"),
681
+ homeTouched: this.homeStartsTouched || touched("HOME"),
682
+ floorOf: (name) => this.entries.filter((e) => e.at < j && e.name === name).flatMap((e) => e.floor ?? []),
683
+ inherited: this.inheritedAt(j),
684
+ };
685
+ }
686
+ }
687
+ export function isArithmeticCommand(segment) {
688
+ const [a, b] = segment.tokens;
689
+ return a?.kind === "group" && a.op === "(" && b?.kind === "group" && b.op === "(";
690
+ }
691
+ export const ARITHMETIC_TEST_OPERATORS = new Set(["-eq", "-ne", "-lt", "-le", "-gt", "-ge"]);
692
+ export function parameterBodySubstitutions(name, body, table) {
693
+ const rest = body.slice(name.length);
694
+ const out = arithmeticSubstitutions(rest.startsWith("[") ? rest.slice(0, rest.indexOf("]") + 1 || undefined) : "", table);
695
+ const offset = /^(?:\[[^\]]*\])?:(?![-=+?])(.*)$/s.exec(rest)?.[1];
696
+ if (offset !== undefined)
697
+ out.push(...substitutionBodies(`${offset}`, true), ...arithmeticSubstitutions(offset, table));
698
+ return [...new Set(out)];
699
+ }
700
+ export function arithmeticSubstitutions(text, table, chain = new Set(), budget = { left: ARITHMETIC_VALUES_MAX }) {
701
+ if (--budget.left < 0)
702
+ return [text];
703
+ const out = [];
704
+ for (const subscript of subscriptsOf(text))
705
+ out.push(...substitutionBodies(subscript));
706
+ if (table !== undefined) {
707
+ for (const name of new Set(text.match(/[A-Za-z_][A-Za-z0-9_]*/g) ?? [])) {
708
+ if (chain.has(name))
709
+ continue;
710
+ const state = table.stateOf(name);
711
+ const values = state === "unknown" ? table.floorOf(name) : state;
712
+ for (const v of values)
713
+ out.push(...arithmeticSubstitutions(v.spelled, table, new Set([...chain, name]), budget));
714
+ }
715
+ }
716
+ return [...new Set(out)];
717
+ }
718
+ const ARITHMETIC_VALUES_MAX = 256;
719
+ export function subscriptsOf(text) {
720
+ const out = [];
721
+ for (let i = 0; i < text.length; i++) {
722
+ if (text[i] !== "[")
723
+ continue;
724
+ let depth = 0;
725
+ let j = i;
726
+ for (; j < text.length; j++) {
727
+ if (text[j] === "[")
728
+ depth++;
729
+ else if (text[j] === "]" && --depth === 0)
730
+ break;
731
+ }
732
+ out.push(text.slice(i + 1, j));
733
+ i = j;
734
+ }
735
+ return out;
736
+ }
737
+ function substitutionBodies(subscript, keepQuotes = false) {
738
+ if (!/\$\(|`|[<>]\(/.test(subscript))
739
+ return [];
740
+ const unquoted = keepQuotes ? subscript : subscript.replace(/["']/g, "");
741
+ const lexed = lexShell(unquoted);
742
+ if ("parseFailure" in lexed)
743
+ return [unquoted];
744
+ const bodies = lexed.segments.flatMap((s) => s.substitutions);
745
+ return bodies.length > 0 ? bodies : [unquoted];
746
+ }