@sema-agent/core 7.17.7 → 7.19.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 (201) hide show
  1. package/CHANGELOG.md +233 -13
  2. package/dist/agents/send-message-tool.js +1 -0
  3. package/dist/agents/subagent.d.ts +1 -0
  4. package/dist/agents/subagent.js +5 -0
  5. package/dist/agents/teacher.js +1 -0
  6. package/dist/agents/verify.js +3 -0
  7. package/dist/brain/anthropic.js +6 -3
  8. package/dist/brain/open-responses.js +10 -4
  9. package/dist/brain/openai.js +5 -2
  10. package/dist/brain/reasoning.d.ts +13 -13
  11. package/dist/brain/reasoning.js +2 -2
  12. package/dist/brain/route-adjudicator.d.ts +18 -0
  13. package/dist/brain/route-adjudicator.js +16 -0
  14. package/dist/brain/stream-engine.js +2 -1
  15. package/dist/brain/stream-shared.d.ts +3 -0
  16. package/dist/brain/stream-shared.js +1 -0
  17. package/dist/core/agent-definition.d.ts +10 -0
  18. package/dist/core/artifact-host.d.ts +41 -4
  19. package/dist/core/artifact-host.js +18 -2
  20. package/dist/core/ask-origin.d.ts +16 -0
  21. package/dist/core/ask-origin.js +8 -0
  22. package/dist/core/auto-mode-arming.d.ts +6 -0
  23. package/dist/core/auto-mode-arming.js +5 -5
  24. package/dist/core/auto-mode-defaults.d.ts +43 -15
  25. package/dist/core/auto-mode-defaults.js +4 -1
  26. package/dist/core/auto-mode.d.ts +184 -59
  27. package/dist/core/auto-mode.js +188 -13
  28. package/dist/core/checkpoint-store.d.ts +23 -2
  29. package/dist/core/checkpoint-store.js +2 -2
  30. package/dist/core/engine-notice.d.ts +77 -1
  31. package/dist/core/engine-notice.js +7 -0
  32. package/dist/core/gate-fold.js +5 -1
  33. package/dist/core/gate-lanes.js +9 -8
  34. package/dist/core/gate-outcome.d.ts +42 -2
  35. package/dist/core/gate-outcome.js +23 -0
  36. package/dist/core/governance-codes.d.ts +1 -1
  37. package/dist/core/governance-codes.js +14 -0
  38. package/dist/core/hook-seats.d.ts +66 -0
  39. package/dist/core/hook-seats.js +37 -0
  40. package/dist/core/hooks.d.ts +1 -1
  41. package/dist/core/read-root-candidate.d.ts +12 -0
  42. package/dist/core/read-root-candidate.js +24 -0
  43. package/dist/core/roles.d.ts +9 -1
  44. package/dist/core/roles.js +4 -0
  45. package/dist/core/runner/advertised-writable-dirs.d.ts +8 -3
  46. package/dist/core/runner/assemble-result.d.ts +2 -1
  47. package/dist/core/runner/assemble-result.js +1 -1
  48. package/dist/core/runner/classifier-ladder.d.ts +227 -0
  49. package/dist/core/runner/classifier-ladder.js +214 -0
  50. package/dist/core/runner/contracts.d.ts +71 -13
  51. package/dist/core/runner/decide-continuation.d.ts +1 -3
  52. package/dist/core/runner/decide-continuation.js +1 -7
  53. package/dist/core/runner/deferral-census.d.ts +52 -0
  54. package/dist/core/runner/deferral-census.js +44 -0
  55. package/dist/core/runner/denial-limit-arms.js +2 -2
  56. package/dist/core/runner/derived-route-fallback.d.ts +8 -4
  57. package/dist/core/runner/derived-route-fallback.js +8 -3
  58. package/dist/core/runner/gate-exit.d.ts +12 -0
  59. package/dist/core/runner/gate-exit.js +6 -3
  60. package/dist/core/runner/prepare-artifact.d.ts +10 -2
  61. package/dist/core/runner/prepare-artifact.js +5 -4
  62. package/dist/core/runner/prepare-ask-lane.js +1 -0
  63. package/dist/core/runner/prepare-caps-and-workflow.d.ts +4 -2
  64. package/dist/core/runner/prepare-caps-and-workflow.js +43 -55
  65. package/dist/core/runner/prepare-config-doors.d.ts +7 -1
  66. package/dist/core/runner/prepare-config-doors.js +13 -13
  67. package/dist/core/runner/prepare-defer-classify.d.ts +13 -19
  68. package/dist/core/runner/prepare-defer-classify.js +8 -3
  69. package/dist/core/runner/prepare-gate-stations.d.ts +26 -0
  70. package/dist/core/runner/prepare-gate-stations.js +1 -1
  71. package/dist/core/runner/prepare-hands-readface.d.ts +24 -7
  72. package/dist/core/runner/prepare-hands-readface.js +60 -24
  73. package/dist/core/runner/prepare-inherited-gate.d.ts +17 -4
  74. package/dist/core/runner/prepare-inherited-gate.js +10 -2
  75. package/dist/core/runner/prepare-lsp.d.ts +2 -2
  76. package/dist/core/runner/prepare-lsp.js +1 -1
  77. package/dist/core/runner/prepare-policy-chain.js +2 -0
  78. package/dist/core/runner/prepare-project-context.d.ts +5 -1
  79. package/dist/core/runner/prepare-project-context.js +7 -4
  80. package/dist/core/runner/prepare-prompt-inputs.d.ts +4 -2
  81. package/dist/core/runner/prepare-prompt-inputs.js +4 -1
  82. package/dist/core/runner/prepare-run-refs.d.ts +9 -1
  83. package/dist/core/runner/prepare-run-refs.js +3 -2
  84. package/dist/core/runner/prepare-suspend-saga.d.ts +5 -0
  85. package/dist/core/runner/prepare-suspend-saga.js +5 -2
  86. package/dist/core/runner/prepare-task.js +19 -14
  87. package/dist/core/runner/prepare-tool-disclosure-mount.d.ts +5 -0
  88. package/dist/core/runner/prepare-tool-disclosure-mount.js +10 -2
  89. package/dist/core/runner/prepare-turn-wiring.d.ts +8 -4
  90. package/dist/core/runner/prepare-turn-wiring.js +3 -2
  91. package/dist/core/runner/prepare-wiring-manifest.d.ts +5 -5
  92. package/dist/core/runner/prepare-wiring-manifest.js +5 -2
  93. package/dist/core/runner/resume-apply.js +3 -3
  94. package/dist/core/runner/resume-checkpoint-screen.js +7 -0
  95. package/dist/core/runner/run-harness-handlers.js +4 -1
  96. package/dist/core/runner/run-identity-wiring.js +6 -2
  97. package/dist/core/runner/run-leg.d.ts +2 -0
  98. package/dist/core/runner/run-leg.js +45 -45
  99. package/dist/core/runner/run-notification-lane.js +55 -27
  100. package/dist/core/runner/run-settle-and-teardown.js +1 -0
  101. package/dist/core/runner/run-terminal-adoption.js +16 -13
  102. package/dist/core/runner/run-turn-boundary.js +15 -5
  103. package/dist/core/runner/runtask.d.ts +6 -5
  104. package/dist/core/runner/runtask.js +26 -14
  105. package/dist/core/runner/skill-base-dir.d.ts +54 -0
  106. package/dist/core/runner/skill-base-dir.js +42 -0
  107. package/dist/core/runner/synthetic-tools.d.ts +83 -48
  108. package/dist/core/runner/synthetic-tools.js +69 -32
  109. package/dist/core/runner/tool-defer-gate.d.ts +22 -9
  110. package/dist/core/runner/tool-defer-gate.js +15 -5
  111. package/dist/core/runner/tool-disclosure.d.ts +5 -0
  112. package/dist/core/runner/tool-disclosure.js +2 -0
  113. package/dist/core/runner/tool-progress-lane.d.ts +42 -0
  114. package/dist/core/runner/tool-progress-lane.js +61 -0
  115. package/dist/core/runner/turn-attachments.d.ts +43 -12
  116. package/dist/core/runner/turn-attachments.js +58 -14
  117. package/dist/core/runner-deps.d.ts +48 -7
  118. package/dist/core/sensitive-path-policy.d.ts +58 -17
  119. package/dist/core/sensitive-path-policy.js +33 -5
  120. package/dist/core/skill-listing-budget.d.ts +112 -0
  121. package/dist/core/skill-listing-budget.js +58 -0
  122. package/dist/core/swappable-deps.d.ts +9 -7
  123. package/dist/core/swappable-deps.js +7 -3
  124. package/dist/core/task-event.d.ts +96 -29
  125. package/dist/core/task-notification.d.ts +142 -16
  126. package/dist/core/task-notification.js +106 -0
  127. package/dist/core/task-result.d.ts +9 -1
  128. package/dist/core/task-spec.d.ts +46 -3
  129. package/dist/core/tighten-task-spec.js +7 -5
  130. package/dist/core/tool-model-gate.d.ts +1 -1
  131. package/dist/core/tool-policy.d.ts +19 -4
  132. package/dist/core/tool-policy.js +6 -7
  133. package/dist/core/tool-search-setting.d.ts +78 -0
  134. package/dist/core/tool-search-setting.js +90 -0
  135. package/dist/core/tool-spec.d.ts +45 -4
  136. package/dist/core/types.d.ts +2 -2
  137. package/dist/core/types.js +1 -1
  138. package/dist/core/wiring-manifest.d.ts +46 -0
  139. package/dist/core/wiring-manifest.js +9 -1
  140. package/dist/core/write-protect.d.ts +8 -3
  141. package/dist/core/write-protect.js +8 -5
  142. package/dist/engine/execution-env/node-execution-env.js +10 -4
  143. package/dist/engine/harness/agent-harness.d.ts +42 -0
  144. package/dist/engine/harness/agent-harness.js +61 -29
  145. package/dist/engine/loop/agent-loop.d.ts +68 -3
  146. package/dist/engine/loop/agent-loop.js +109 -16
  147. package/dist/index.d.ts +13 -8
  148. package/dist/index.js +11 -7
  149. package/dist/internal/harness.d.ts +1 -1
  150. package/dist/orchestration/run-workflow-tool.d.ts +3 -0
  151. package/dist/orchestration/run-workflow-tool.js +6 -1
  152. package/dist/orchestration/workflow-governance.d.ts +7 -1
  153. package/dist/orchestration/workflow-governance.js +9 -5
  154. package/dist/orchestration/workflow-primitives.d.ts +3 -0
  155. package/dist/orchestration/workflow-primitives.js +6 -1
  156. package/dist/orchestration/workflow-types.d.ts +48 -13
  157. package/dist/orchestration/workflow-types.js +1 -1
  158. package/dist/orchestration/workflow.js +45 -17
  159. package/dist/prompt-assembly/epoch.js +2 -0
  160. package/dist/prompt-assembly/packs/sema-default.js +2 -2
  161. package/dist/prompt-assembly/section-usage.d.ts +26 -0
  162. package/dist/prompt-assembly/section-usage.js +10 -0
  163. package/dist/prompt-assembly/types.d.ts +8 -6
  164. package/dist/prompts/default.d.ts +13 -0
  165. package/dist/prompts/default.js +16 -8
  166. package/dist/tools/artifact/artifact-text.d.ts +10 -1
  167. package/dist/tools/artifact/artifact-text.js +6 -3
  168. package/dist/tools/artifact/artifact-tool.d.ts +12 -2
  169. package/dist/tools/artifact/artifact-tool.js +203 -70
  170. package/dist/tools/artifact/conformance.d.ts +56 -0
  171. package/dist/tools/artifact/conformance.js +349 -0
  172. package/dist/tools/artifact/local-stub.js +1 -1
  173. package/dist/tools/artifact/reject-breaker.d.ts +28 -0
  174. package/dist/tools/artifact/reject-breaker.js +42 -0
  175. package/dist/tools/fs/bash-lexer.d.ts +132 -0
  176. package/dist/tools/fs/bash-lexer.js +563 -0
  177. package/dist/tools/fs/bash-program-position.d.ts +142 -0
  178. package/dist/tools/fs/bash-program-position.js +125 -0
  179. package/dist/tools/fs/bash-readonly-classifier.d.ts +81 -16
  180. package/dist/tools/fs/bash-readonly-classifier.js +180 -558
  181. package/dist/tools/fs/containment-face.d.ts +89 -0
  182. package/dist/tools/fs/containment-face.js +81 -0
  183. package/dist/tools/fs/fs-bash.d.ts +46 -1
  184. package/dist/tools/fs/fs-bash.js +165 -25
  185. package/dist/tools/fs/fs-read.d.ts +1 -1
  186. package/dist/tools/fs/fs-search-tools.d.ts +3 -3
  187. package/dist/tools/fs/fs-write.d.ts +4 -3
  188. package/dist/tools/fs/fs-write.js +9 -9
  189. package/dist/tools/fs/index.d.ts +24 -5
  190. package/dist/tools/fs/index.js +18 -6
  191. package/dist/tools/fs/read-deny.d.ts +4 -3
  192. package/dist/tools/fs/repo-map.d.ts +2 -2
  193. package/dist/tools/fs/safety.d.ts +18 -3
  194. package/dist/tools/fs/safety.js +11 -4
  195. package/package.json +1 -1
  196. package/test/export-surface.snapshot.json +99 -13
  197. package/wire-changes/core@7.17.7.json +49 -0
  198. package/wire-changes/core@7.18.0.json +1273 -0
  199. package/wire-changes/core@7.19.0.json +457 -0
  200. package/dist/tools/fs/read-face.d.ts +0 -68
  201. package/dist/tools/fs/read-face.js +0 -47
@@ -0,0 +1,563 @@
1
+ export const SUBSTITUTION_PLACEHOLDER = " subst";
2
+ function quoteStep(open, ch) {
3
+ if (open === undefined)
4
+ return ch === '"' || ch === "'" ? ch : undefined;
5
+ return open === ch ? undefined : open;
6
+ }
7
+ export function quoteMask(s) {
8
+ const quoted = new Array(s.length).fill(false);
9
+ let open;
10
+ for (let i = 0; i < s.length; i++) {
11
+ const before = open;
12
+ open = quoteStep(open, s[i]);
13
+ quoted[i] = before !== undefined || open !== undefined;
14
+ }
15
+ return { quoted, balanced: open === undefined };
16
+ }
17
+ export function literalTildeSpelling(word) {
18
+ return word.startsWith("~") ? `./${word}` : word;
19
+ }
20
+ function tildeIsLiteral(raw) {
21
+ if (!raw.startsWith("~"))
22
+ return true;
23
+ for (let i = 1; i < raw.length; i++) {
24
+ const ch = raw[i];
25
+ if (ch === '"' || ch === "'")
26
+ return true;
27
+ if (ch === "/")
28
+ return false;
29
+ }
30
+ return false;
31
+ }
32
+ export function wordsOf(segment) {
33
+ const folded = [];
34
+ const raw = [];
35
+ for (const t of segment.tokens) {
36
+ if (t.kind !== "word")
37
+ continue;
38
+ folded.push(t.folded);
39
+ raw.push(t.raw);
40
+ }
41
+ return { folded, raw };
42
+ }
43
+ function past(text, j) {
44
+ let k = j;
45
+ while (text[k] === "\\" && text[k + 1] === "\n")
46
+ k += 2;
47
+ return k;
48
+ }
49
+ function parenBodyEnd(text, open) {
50
+ let depth = 0;
51
+ let quoted;
52
+ let wordStart = true;
53
+ let word = "";
54
+ for (let j = open; j < text.length; j++) {
55
+ const c = text[j];
56
+ if (quoted !== undefined) {
57
+ if (c === "\\" && quoted === '"')
58
+ j++;
59
+ else if (c === quoted)
60
+ quoted = undefined;
61
+ wordStart = false;
62
+ continue;
63
+ }
64
+ if (c === "\\") {
65
+ if (text[j + 1] === "\n") {
66
+ j++;
67
+ continue;
68
+ }
69
+ j++;
70
+ wordStart = false;
71
+ word = "";
72
+ continue;
73
+ }
74
+ if (c === "#" && wordStart) {
75
+ const nl = text.indexOf("\n", j);
76
+ if (nl < 0)
77
+ return undefined;
78
+ j = nl;
79
+ wordStart = true;
80
+ word = "";
81
+ continue;
82
+ }
83
+ if (c === "'" || c === '"') {
84
+ quoted = c;
85
+ wordStart = false;
86
+ word = "";
87
+ continue;
88
+ }
89
+ if (c === "$" && text[past(text, j + 1)] === "{") {
90
+ const open2 = past(text, j + 1);
91
+ const close = braceExpansionEnd(text, open2);
92
+ if (close === undefined)
93
+ return undefined;
94
+ if (UNREADABLE_EXPANSION_TEXT.test(text.slice(open2 + 1, close - 1)))
95
+ return "unreadable";
96
+ j = close - 1;
97
+ wordStart = false;
98
+ word = "";
99
+ continue;
100
+ }
101
+ if (c === "<" && text[past(text, j + 1)] === "<" && text[past(text, past(text, j + 1) + 1)] !== "<")
102
+ return "unreadable";
103
+ if (c === "(")
104
+ depth++;
105
+ else if (c === ")") {
106
+ depth--;
107
+ if (depth === 0)
108
+ return j + 1;
109
+ }
110
+ const boundary = /[\s;|&()]/.test(c);
111
+ if (boundary && word === "case")
112
+ return "unreadable";
113
+ word = boundary ? "" : word + c;
114
+ wordStart = boundary;
115
+ }
116
+ return undefined;
117
+ }
118
+ function braceExpansionEnd(text, open) {
119
+ let quoted;
120
+ for (let j = open + 1; j < text.length; j++) {
121
+ const c = text[j];
122
+ if (quoted !== undefined) {
123
+ if (c === "\\" && quoted === '"')
124
+ j++;
125
+ else if (c === quoted)
126
+ quoted = undefined;
127
+ continue;
128
+ }
129
+ if (c === "\\") {
130
+ j++;
131
+ continue;
132
+ }
133
+ if (c === "'" || c === '"') {
134
+ quoted = c;
135
+ continue;
136
+ }
137
+ if (c === "}")
138
+ return j + 1;
139
+ }
140
+ return undefined;
141
+ }
142
+ const UNREADABLE_EXPANSION_TEXT = /[$`()<>{\\]/;
143
+ function backtickBody(source, open) {
144
+ let body = "";
145
+ for (let j = open + 1; j < source.length; j++) {
146
+ const c = source[j];
147
+ if (c === "\\" && j + 1 < source.length) {
148
+ const next = source[j + 1];
149
+ if (next === "\n") {
150
+ j++;
151
+ continue;
152
+ }
153
+ body += next === "$" || next === "`" || next === "\\" ? next : c + next;
154
+ j++;
155
+ continue;
156
+ }
157
+ if (c === "`")
158
+ return { body, close: j };
159
+ body += c;
160
+ }
161
+ return undefined;
162
+ }
163
+ export function substitutionsInHeredocBody(body) {
164
+ const out = [];
165
+ for (let i = 0; i < body.length; i++) {
166
+ const ch = body[i];
167
+ if (ch === "\\") {
168
+ i++;
169
+ continue;
170
+ }
171
+ if (ch === "`") {
172
+ const tick = backtickBody(body, i);
173
+ if (tick === undefined)
174
+ return undefined;
175
+ out.push(tick.body);
176
+ i = tick.close;
177
+ continue;
178
+ }
179
+ if (ch === "$" && body[i + 1] === "(") {
180
+ const end = parenBodyEnd(body, i + 1);
181
+ if (typeof end !== "number")
182
+ return undefined;
183
+ out.push(body.slice(i + 2, end - 1));
184
+ i = end - 1;
185
+ }
186
+ }
187
+ return out;
188
+ }
189
+ const UNDELIMITED_SUBSTITUTION = "a command substitution carries a construct this lexer cannot delimit";
190
+ const WHITESPACE = /\s/;
191
+ const BLANK = /[ \t]/;
192
+ const UNTERMINATED_HEREDOC = "a here-document body never terminates";
193
+ function scan(source, opts, quotesLiteral) {
194
+ const lenient = opts.mode === "lenient";
195
+ const split = opts.connectors !== "text";
196
+ const segments = [{ start: 0, end: source.length, connector: "start", tokens: [], substitutions: [] }];
197
+ const current = () => segments[segments.length - 1];
198
+ const pendingHeredocs = [];
199
+ let pendingRedirect;
200
+ let pendingHeredoc;
201
+ let inSingle = false;
202
+ let inDouble = false;
203
+ let raw = "";
204
+ let folded = "";
205
+ let started = false;
206
+ let foldOpen;
207
+ const next = (j) => (lenient ? past(source, j + 1) : j + 1);
208
+ const foldChar = (ch) => {
209
+ if (ch === '"' || ch === "'") {
210
+ if (foldOpen === undefined) {
211
+ foldOpen = ch;
212
+ return;
213
+ }
214
+ if (foldOpen === ch) {
215
+ foldOpen = undefined;
216
+ return;
217
+ }
218
+ }
219
+ folded += ch;
220
+ };
221
+ const take = (ch) => { raw += ch; foldChar(ch); started = true; };
222
+ const endWord = () => {
223
+ if (!started)
224
+ return undefined;
225
+ const word = { raw, folded, tilde: tildeIsLiteral(raw) ? literalTildeSpelling(folded) : folded };
226
+ raw = "";
227
+ folded = "";
228
+ started = false;
229
+ foldOpen = undefined;
230
+ if (pendingHeredoc !== undefined) {
231
+ const header = pendingHeredoc;
232
+ pendingHeredoc = undefined;
233
+ if (word.folded === "" || /[$`]/.test(word.raw))
234
+ return "a here-document delimiter is not a plain word";
235
+ const token = { kind: "heredoc", delimiter: word.folded, quoted: /["'\\]/.test(word.raw), body: "" };
236
+ current().tokens.push(token);
237
+ pendingHeredocs.push({ delimiter: word.folded, quoted: token.quoted, dash: header.dash, token });
238
+ return undefined;
239
+ }
240
+ if (pendingRedirect !== undefined) {
241
+ const op = pendingRedirect;
242
+ pendingRedirect = undefined;
243
+ if (op === ">&" || op === "<&") {
244
+ if (word.raw === word.folded && /^(?:\d+-?|-)$/.test(word.folded)) {
245
+ current().tokens.push({ kind: "redirect", op });
246
+ return undefined;
247
+ }
248
+ current().tokens.push({ kind: "redirect", op: op === ">&" ? "&>" : "<", target: { kind: "word", raw: word.raw, folded: word.tilde } });
249
+ return undefined;
250
+ }
251
+ current().tokens.push({ kind: "redirect", op, target: { kind: "word", raw: word.raw, folded: word.tilde } });
252
+ return undefined;
253
+ }
254
+ current().tokens.push({ kind: "word", raw: word.raw, folded: word.tilde });
255
+ return undefined;
256
+ };
257
+ const operandOwed = () => pendingRedirect !== undefined ? "a redirection names no target" : pendingHeredoc !== undefined ? "a here-document operator names no delimiter" : undefined;
258
+ const closeSegment = (connector, at, from) => {
259
+ const refusal = endWord() ?? operandOwed();
260
+ if (refusal !== undefined)
261
+ return refusal;
262
+ current().end = at;
263
+ segments.push({ start: from, end: source.length, connector, tokens: [], substitutions: [] });
264
+ return undefined;
265
+ };
266
+ const substitution = (from, to, body) => {
267
+ raw += source.slice(from, to);
268
+ folded += SUBSTITUTION_PLACEHOLDER;
269
+ started = true;
270
+ current().substitutions.push(body);
271
+ };
272
+ const readHeredocBodies = (from) => {
273
+ let p = from;
274
+ for (const h of pendingHeredocs) {
275
+ const lines = [];
276
+ for (;;) {
277
+ if (p > source.length)
278
+ return { reject: UNTERMINATED_HEREDOC };
279
+ let nl = source.indexOf("\n", p);
280
+ let line = nl < 0 ? source.slice(p) : source.slice(p, nl);
281
+ if (!h.quoted) {
282
+ while (nl >= 0 && /(?<!\\)(?:\\\\)*\\$/.test(line)) {
283
+ const after = source.indexOf("\n", nl + 1);
284
+ line = line.slice(0, -1) + (after < 0 ? source.slice(nl + 1) : source.slice(nl + 1, after));
285
+ nl = after;
286
+ }
287
+ }
288
+ if ((h.dash ? line.replace(/^\t+/, "") : line) === h.delimiter) {
289
+ p = nl < 0 ? source.length + 1 : nl + 1;
290
+ break;
291
+ }
292
+ if (nl < 0)
293
+ return { reject: UNTERMINATED_HEREDOC };
294
+ lines.push(line);
295
+ p = nl + 1;
296
+ }
297
+ h.token.body = lines.join("\n");
298
+ }
299
+ pendingHeredocs.length = 0;
300
+ return Math.min(p, source.length);
301
+ };
302
+ for (let i = 0; i < source.length; i++) {
303
+ if (lenient && !inSingle) {
304
+ const j = past(source, i);
305
+ if (j >= source.length)
306
+ break;
307
+ i = j;
308
+ }
309
+ const ch = source[i];
310
+ if (!quotesLiteral) {
311
+ if (ch === "'" && !inDouble) {
312
+ inSingle = !inSingle;
313
+ take(ch);
314
+ continue;
315
+ }
316
+ if (ch === '"' && !inSingle) {
317
+ inDouble = !inDouble;
318
+ take(ch);
319
+ continue;
320
+ }
321
+ }
322
+ if (inSingle) {
323
+ take(ch);
324
+ continue;
325
+ }
326
+ if (lenient) {
327
+ const n1 = next(i);
328
+ if (ch === "\\") {
329
+ const escaped = source[i + 1];
330
+ if (escaped === undefined) {
331
+ take(ch);
332
+ continue;
333
+ }
334
+ raw += ch + escaped;
335
+ folded += inDouble && !"$`\"\\".includes(escaped) ? ch + escaped : escaped;
336
+ started = true;
337
+ i++;
338
+ continue;
339
+ }
340
+ if (ch === "`") {
341
+ const tick = backtickBody(source, i);
342
+ if (tick === undefined)
343
+ return { reject: "a command substitution never terminates" };
344
+ substitution(i, tick.close + 1, tick.body);
345
+ i = tick.close;
346
+ continue;
347
+ }
348
+ if (ch === "$" && source[n1] === "(") {
349
+ const end = parenBodyEnd(source, n1);
350
+ if (end === undefined)
351
+ return { reject: "a command substitution never terminates" };
352
+ if (end === "unreadable")
353
+ return { reject: UNDELIMITED_SUBSTITUTION };
354
+ substitution(i, end, source.slice(n1 + 1, end - 1));
355
+ i = end - 1;
356
+ continue;
357
+ }
358
+ if (ch === "$" && source[n1] === "{") {
359
+ const end = braceExpansionEnd(source, n1);
360
+ if (end === undefined)
361
+ return { reject: "a parameter expansion never terminates" };
362
+ if (UNREADABLE_EXPANSION_TEXT.test(source.slice(n1 + 1, end - 1)))
363
+ return { reject: "a parameter expansion carries text this lexer does not read" };
364
+ const text = source.slice(i, end);
365
+ raw += text;
366
+ folded += text;
367
+ started = true;
368
+ i = end - 1;
369
+ continue;
370
+ }
371
+ if (inDouble) {
372
+ take(ch);
373
+ continue;
374
+ }
375
+ if ((ch === "<" || ch === ">") && source[n1] === "(") {
376
+ const end = parenBodyEnd(source, n1);
377
+ if (end === undefined)
378
+ return { reject: "a process substitution never terminates" };
379
+ if (end === "unreadable")
380
+ return { reject: UNDELIMITED_SUBSTITUTION };
381
+ substitution(i, end, source.slice(n1 + 1, end - 1));
382
+ i = end - 1;
383
+ continue;
384
+ }
385
+ if (ch === "#" && !started) {
386
+ const nl = source.indexOf("\n", i);
387
+ i = nl < 0 ? source.length : nl - 1;
388
+ continue;
389
+ }
390
+ if (ch === "\n") {
391
+ if (!split) {
392
+ const r = endWord();
393
+ if (r !== undefined)
394
+ return { reject: r };
395
+ continue;
396
+ }
397
+ const refusal = closeSegment("\n", i, i + 1);
398
+ if (refusal !== undefined)
399
+ return { reject: refusal };
400
+ if (pendingHeredocs.length > 0) {
401
+ const resume = readHeredocBodies(i + 1);
402
+ if (typeof resume !== "number")
403
+ return resume;
404
+ current().start = resume;
405
+ i = resume - 1;
406
+ }
407
+ continue;
408
+ }
409
+ if (ch === "(" || ch === ")") {
410
+ const r = endWord() ?? operandOwed();
411
+ if (r !== undefined)
412
+ return { reject: r };
413
+ current().tokens.push({ kind: "group", op: ch });
414
+ continue;
415
+ }
416
+ if (ch === "<" || ch === ">" || (ch === "&" && source[n1] === ">")) {
417
+ if (ch !== "&" && started && raw === folded && /^\d+$/.test(raw)) {
418
+ raw = "";
419
+ folded = "";
420
+ started = false;
421
+ }
422
+ else {
423
+ const r = endWord() ?? operandOwed();
424
+ if (r !== undefined)
425
+ return { reject: r };
426
+ }
427
+ const n2 = next(n1);
428
+ if (ch === "&") {
429
+ if (source[n2] === ">") {
430
+ pendingRedirect = "&>>";
431
+ i = n2;
432
+ }
433
+ else {
434
+ pendingRedirect = "&>";
435
+ i = n1;
436
+ }
437
+ continue;
438
+ }
439
+ if (ch === "<" && source[n1] === "<") {
440
+ if (source[n2] === "<") {
441
+ pendingRedirect = "<<<";
442
+ i = n2;
443
+ continue;
444
+ }
445
+ const dash = source[n2] === "-";
446
+ pendingHeredoc = { dash };
447
+ i = dash ? n2 : n1;
448
+ continue;
449
+ }
450
+ if (source[n1] === "&") {
451
+ pendingRedirect = ch === ">" ? ">&" : "<&";
452
+ i = n1;
453
+ continue;
454
+ }
455
+ if (ch === ">" && (source[n1] === ">" || source[n1] === "|")) {
456
+ pendingRedirect = source[n1] === ">" ? ">>" : ">";
457
+ i = n1;
458
+ continue;
459
+ }
460
+ pendingRedirect = ch;
461
+ continue;
462
+ }
463
+ if (split) {
464
+ let refusal;
465
+ if (ch === ";")
466
+ refusal = closeSegment(";", i, i + 1);
467
+ else if (ch === "&") {
468
+ if (source[n1] === "&") {
469
+ refusal = closeSegment("&&", i, n1 + 1);
470
+ i = n1;
471
+ }
472
+ else
473
+ refusal = closeSegment("&", i, i + 1);
474
+ }
475
+ else if (ch === "|") {
476
+ if (source[n1] === "|" || source[n1] === "&") {
477
+ refusal = closeSegment(source[n1] === "|" ? "||" : "|", i, n1 + 1);
478
+ i = n1;
479
+ }
480
+ else
481
+ refusal = closeSegment("|", i, i + 1);
482
+ }
483
+ else if (BLANK.test(ch)) {
484
+ refusal = endWord();
485
+ }
486
+ else {
487
+ take(ch);
488
+ continue;
489
+ }
490
+ if (refusal !== undefined)
491
+ return { reject: refusal };
492
+ continue;
493
+ }
494
+ if (BLANK.test(ch)) {
495
+ const r = endWord();
496
+ if (r !== undefined)
497
+ return { reject: r };
498
+ continue;
499
+ }
500
+ take(ch);
501
+ continue;
502
+ }
503
+ if (!inDouble && split) {
504
+ if (ch === "&") {
505
+ if (source[i + 1] === "&") {
506
+ closeSegment("&&", i, i + 2);
507
+ i++;
508
+ continue;
509
+ }
510
+ return { reject: "`&` backgrounding is not allowed — a backgrounded process outlives the command" };
511
+ }
512
+ if (ch === "|") {
513
+ if (source[i + 1] === "|") {
514
+ closeSegment("||", i, i + 2);
515
+ i++;
516
+ }
517
+ else
518
+ closeSegment("|", i, i + 1);
519
+ continue;
520
+ }
521
+ if (ch === ";") {
522
+ closeSegment(";", i, i + 1);
523
+ continue;
524
+ }
525
+ }
526
+ if (!inDouble && WHITESPACE.test(ch)) {
527
+ endWord();
528
+ continue;
529
+ }
530
+ take(ch);
531
+ }
532
+ const tail = endWord() ?? operandOwed();
533
+ if (tail !== undefined)
534
+ return { reject: tail };
535
+ if (pendingHeredocs.length > 0)
536
+ return { reject: UNTERMINATED_HEREDOC };
537
+ const out = segments.map((s) => ({
538
+ text: source.slice(s.start, s.end),
539
+ connector: s.connector,
540
+ tokens: s.tokens,
541
+ substitutions: s.substitutions,
542
+ }));
543
+ return { balanced: !inSingle && !inDouble, command: { segments: out, substitutions: out.flatMap((s) => s.substitutions) } };
544
+ }
545
+ export function lexShellText(source, opts) {
546
+ const honoured = scan(source, opts, false);
547
+ if ("reject" in honoured)
548
+ return honoured;
549
+ if (honoured.balanced)
550
+ return honoured.command;
551
+ const blind = scan(source, opts, true);
552
+ if ("reject" in blind)
553
+ return blind;
554
+ if (opts.mode === "lenient" || opts.connectors === "text")
555
+ return blind.command;
556
+ const segments = blind.command.segments.map((segment) => {
557
+ const own = scan(segment.text, { mode: "strict", connectors: "text" }, false);
558
+ if ("reject" in own || !own.balanced)
559
+ return segment;
560
+ return { ...segment, tokens: own.command.segments[0].tokens };
561
+ });
562
+ return { segments, substitutions: [] };
563
+ }