@molecule/api-ai-tools 1.5.18 → 1.5.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -3,7 +3,7 @@ AUTO-GENERATED — DO NOT EDIT THIS FILE.
3
3
  Generated by `mlcl sync-docs` from the package's src/index.ts JSDoc + mlcl/registry.json.
4
4
  Edits here are overwritten on the next commit (molecule's pre-commit hook regenerates).
5
5
  To change this document, edit the module-level JSDoc in src/index.ts.
6
- Generated: 2026-10-16T09:30:45.054Z
6
+ Generated: 2026-10-16T12:22:32.984Z
7
7
  -->
8
8
 
9
9
  # @molecule/api-ai-tools
@@ -242,6 +242,23 @@ interface ToolBuildConfig {
242
242
  */
243
243
  searchExcludedDirs?: string[]
244
244
 
245
+ /**
246
+ * Whether a LARGE HTML document should come back as its OUTLINE — the head's
247
+ * tags and the tags that open each body section, one line each, plus a note
248
+ * naming the offset/limit that returns the whole document — instead of its
249
+ * whole text. Over {@link MAX_HTML_INLINE_CHARS}, and only when the read named
250
+ * no line window.
251
+ *
252
+ * A getter, not a boolean, because the answer belongs to the TURN, not to the
253
+ * tool set: a host that plans first and builds after wants an outline while the
254
+ * executor is researching (where a read it cannot act on is pure context cost)
255
+ * and the document itself while it is writing the page. Unset means every big
256
+ * HTML read returns its text, which is what a host with no such phases wants.
257
+ *
258
+ * @returns True when a large HTML read should be outlined rather than returned.
259
+ */
260
+ outlineLargeHtml?: () => boolean
261
+
245
262
  /** Post-write hook (e.g. auto-format via Prettier/ESLint). Called after every write_file/edit_file. */
246
263
  onAfterWrite?: (path: string) => Promise<void>
247
264
 
@@ -320,6 +337,38 @@ interface PromptContext {
320
337
  }
321
338
  ```
322
339
 
340
+ #### `FailedTest`
341
+
342
+ One failed test, as a running suite's log names it.
343
+
344
+ ```typescript
345
+ interface FailedTest {
346
+ /** The location the log writes (`specs/e2e/articles.spec.ts:32`), or '' when it writes none. */
347
+ where: string
348
+ /** The test's own title — the last segment of its title path. */
349
+ title: string
350
+ /** How long it ran before failing, in seconds, or null when the log does not say. */
351
+ seconds: number | null
352
+ }
353
+ ```
354
+
355
+ #### `RepeatingFailure`
356
+
357
+ A cause repeating among a running suite's failures.
358
+
359
+ ```typescript
360
+ interface RepeatingFailure {
361
+ /** How many failures share the signature. */
362
+ count: number
363
+ /** The shared error line, when the log carries the failures' bodies (`line` reporter). */
364
+ message: string | null
365
+ /** The shared duration in seconds, when the failures line up by how long they ran. */
366
+ seconds: number | null
367
+ /** The failures that share it, in log order. */
368
+ tests: FailedTest[]
369
+ }
370
+ ```
371
+
323
372
  ### Types
324
373
 
325
374
  #### `DiscoveredSkill` _(deprecated)_
@@ -689,7 +738,12 @@ killed (X1 run flash-m15 lost 20 of its 60 minutes this way, 2026-10-15):
689
738
  `[n/N] <title>` with no ✓/✘ mark and no duration, so on X1 flash-m62 an
690
739
  8.3-minute walk of one suite could never trip the fail-fast note.
691
740
 
692
- Only the command's last step is touched, and only its reporter.
741
+ A run being ITERATED on (capped, narrowed, or by file) gets `list,line` instead
742
+ of `list` alone. `list` holds every failure's error for the summary at the end,
743
+ so mid-run its log says only how long each failure took; `line` writes the body
744
+ as the failure happens, and a look at a running suite (`wait_for_task`'s
745
+
746
+ _(description truncated — read the full text in `node_modules/@molecule/api-ai-tools/dist/utilities.d.ts`.)_
693
747
 
694
748
  ```typescript
695
749
  function streamingBackgroundCommand(command: string): { command: string; changes: string[] }
@@ -699,6 +753,52 @@ function streamingBackgroundCommand(command: string): { command: string; changes
699
753
 
700
754
  **Returns:** The command to run and one sentence per change made (empty when none).
701
755
 
756
+ #### `isSuiteRunCommand(command)`
757
+
758
+ Whether a command runs a whole test suite — a Playwright, Vitest or Hurl run
759
+ — rather than a build, an install or a one-off script.
760
+
761
+ ```typescript
762
+ function isSuiteRunCommand(command: string): boolean
763
+ ```
764
+
765
+ - `command` — The shell command.
766
+
767
+ **Returns:** True when the command starts one of those runners.
768
+
769
+ #### `suiteRunPriorityLaunch(command, launch)`
770
+
771
+ Start a background suite run at lower CPU and IO priority, so the sandbox it
772
+ shares with everything else stays responsive to what the executor does next.
773
+
774
+ A whole-suite Playwright run saturates the one container the app's Vite
775
+ server, its API process and every other command also live in: on X1 flash-m75
776
+ the model backgrounded a 139-test run at +36:23 and the two-word shell
777
+ command it sent next — tick its plan file — returned nothing for 2 min 40 s,
778
+ measured on the chat payload and on the live-stream follower independently.
779
+ The backgrounding note is right that the executor should go do other work;
780
+ what it cannot promise is that the sandbox will let it.
781
+
782
+ `nice -n 10` lowers the run's CPU share and `ionice -c3` its IO class. Both
783
+ are PROBED in the sandbox by RUNNING them, not with `command -v`: a syscall a
784
+ container's seccomp policy refuses leaves `-v` passing while the real run
785
+ fails. Each is applied only if its probe passed, so a sandbox with neither,
786
+ or with only one, runs the suite exactly as it did before. Playwright's own
787
+ workers are children of this shell and inherit both (niceness and IO class
788
+ are inherited across fork/exec).
789
+
790
+ Only a command the caller has already detached reaches this: foreground
791
+ commands are never touched.
792
+
793
+ ```typescript
794
+ function suiteRunPriorityLaunch(command: string, launch: string): { launch: string; note: string }
795
+ ```
796
+
797
+ - `command` — The command as the executor sent it; the suite detection reads this.
798
+ - `launch` — The shell line that would start it, with no prefix of its own.
799
+
800
+ **Returns:** The line to run, and one sentence for the tool note ('' when unchanged).
801
+
702
802
  #### `checkBlockedCommand(command)`
703
803
 
704
804
  Check if a command is blocked for security reasons. Returns error message or null if allowed.
@@ -766,6 +866,32 @@ function isValidGlob(pattern: string): boolean
766
866
 
767
867
  **Returns:** `true` when the pattern contains only allowed characters.
768
868
 
869
+ #### `findPatternArgs(pattern)`
870
+
871
+ The `find` predicate for a `find_files` pattern.
872
+
873
+ `find -name` compares a pattern against a file's BASENAME, so a pattern
874
+ containing a slash (`helpers/*`, `e2e/*.spec.ts`, `src` + `**` + `/App.tsx`)
875
+ can never match and the tool answers `[]` (xr-molecule-flash-m77, on two
876
+ calls in one run). Such a pattern is matched with `-path` instead: an fnmatch
877
+ glob over the whole printed path, whose `*` crosses `/` — the rule
878
+ `.gitignore` uses, and the reason a bare `helpers/*` means "a `helpers/`
879
+ directory anywhere below the root". The pattern is anchored with a leading
880
+ `*` then `/` ("any parent") so the search root — an absolute
881
+ `/workspace/<app>` in a sandbox — need not be spelled out.
882
+
883
+ A doubled-star directory segment keeps its usual meaning of ZERO or more
884
+ directories; `-path` alone would demand at least one, so the pattern is also
885
+ emitted with each such segment removed.
886
+
887
+ ```typescript
888
+ function findPatternArgs(pattern: string): string
889
+ ```
890
+
891
+ - `pattern` — A pattern already accepted by {@link isValidGlob}.
892
+
893
+ **Returns:** A `find` predicate: `-name '…'` for a bare name, else one or more `-path '…'` alternatives, ORed inside parens when there is more than one.
894
+
769
895
  #### `pathArgError(path, tool)`
770
896
 
771
897
  Validate a file tool's `path` argument is a non-empty string. A weak model
@@ -821,6 +947,36 @@ function sliceLines(
821
947
 
822
948
  **Returns:** The window plus its position, or null when neither was given.
823
949
 
950
+ #### `looksLikeHtml(text)`
951
+
952
+ Whether `text` is an HTML document (a doctype or an `<html` root tag).
953
+
954
+ ```typescript
955
+ function looksLikeHtml(text: string): boolean
956
+ ```
957
+
958
+ #### `htmlOutline(text, label)`
959
+
960
+ An OUTLINE of an HTML document: its head's tags and the tags that open each
961
+ body section, one per line, with line numbers.
962
+
963
+ Line-based on purpose — a parser would be a dependency and an attack surface
964
+ for a read that only has to be _navigable_. A line is shown when it starts with
965
+ `<` after trimming, which is what keeps the inside of a `<style>` block, a
966
+ `<script>` body or a prerendered text section out of the outline while keeping
967
+ every tag the document is built from. Long lines (a minified tag, a data URI)
968
+ are cut, and the whole outline is capped, so a pathological document cannot
969
+ turn one read into another wall of text.
970
+
971
+ ```typescript
972
+ function htmlOutline(text: string, label?: string): string | null
973
+ ```
974
+
975
+ - `text` — The document's full text.
976
+ - `label` — What to call the document in the outline's header (its path).
977
+
978
+ **Returns:** The outline — or null when `text` is not an HTML document.
979
+
824
980
  #### `truncate(s, maxLength)`
825
981
 
826
982
  Truncate a string to a max length with a truncation notice.
@@ -887,6 +1043,70 @@ function runningOutputSliceCommand(log: string, own?: string): string
887
1043
 
888
1044
  **Returns:** The shell command, safe to hand to the execution backend as-is.
889
1045
 
1046
+ #### `runningCauseCommand(log, own)`
1047
+
1048
+ The shell command whose stdout is enough of a running log to tell whether one cause is
1049
+ repeating: its tail, {@link REPEATING_CAUSE_TAIL_BYTES} of it.
1050
+
1051
+ {@link runningOutputSliceCommand} is the wrong read for this: it keeps only the FIRST 40
1052
+ failure lines plus 1 000 bytes, so on a run whose failures print a body each — which is what
1053
+ a `line` reporter does, and the only form that names the cause as an error message rather than
1054
+ a duration — three bodies do not fit, and the repeat is invisible. This read is not shown to
1055
+ anyone; it is parsed.
1056
+
1057
+ ```typescript
1058
+ function runningCauseCommand(log: string, own?: string): string
1059
+ ```
1060
+
1061
+ - `log` — The task log path.
1062
+ - `own` — A file the command redirects its own output to, if any.
1063
+
1064
+ **Returns:** The shell command, safe to hand to the execution backend as-is.
1065
+
1066
+ #### `repeatingFailure(log)`
1067
+
1068
+ The cause repeating among a running suite's failures, or null.
1069
+
1070
+ Playwright's `list` reporter prints one line per test as it finishes — its mark, its title
1071
+ path and how long it ran — and defers each failure's error to the summary at the end, so
1072
+ mid-run the only signature those lines carry is the duration. A `line` reporter prints the
1073
+ failure bodies as they happen, and there the repeated error message itself is the signature,
1074
+ which is stronger (it needs no assumption about how long a failure should take), so the
1075
+ message is looked for first.
1076
+
1077
+ Either way the point is the same: a suite whose failures repeat one cause spends its whole
1078
+ runtime proving that one cause, and the executor watching it can be told so at the third
1079
+ failure instead of at the last. X1 flash-m76 walked 15 tests in 4.0 minutes where 15.6 s
1080
+ each was the test timeout and one missing `navigate('/')` was the cause of all of them.
1081
+
1082
+ ```typescript
1083
+ function repeatingFailure(log: string): RepeatingFailure | null
1084
+ ```
1085
+
1086
+ - `log` — The log so far.
1087
+
1088
+ **Returns:** The repeating cause, or null when no signature has {@link REPEATING_FAILURE_MIN} failures behind it.
1089
+
1090
+ #### `repeatingFailureNote(found, o)`
1091
+
1092
+ The sentence that names a cause repeating among a running suite's failures, and the command
1093
+ that stops the run so the cause can be fixed first.
1094
+
1095
+ Kept here, beside {@link repeatingFailure}, so the two stay in step: every word below is about
1096
+ what that function could actually see in the log.
1097
+
1098
+ ```typescript
1099
+ function repeatingFailureNote(
1100
+ found: RepeatingFailure,
1101
+ o: { taskId: string; log: string; stop: string },
1102
+ ): string
1103
+ ```
1104
+
1105
+ - `found` — What {@link repeatingFailure} found.
1106
+ - `o` — The run's facts: its `taskId`, its `log` path, and `stop` — the shell command that stops it (the pids the platform's own pid file leads to).
1107
+
1108
+ **Returns:** The note.
1109
+
890
1110
  #### `whitespaceTolerantReplace(content, oldString, newString)`
891
1111
 
892
1112
  Attempt a whitespace-tolerant replacement when an exact `old_string` match
@@ -1053,6 +1273,38 @@ file under it "fits in ONE call", and a smaller bound here broke that promise (X
1053
1273
  const SMALL_FILE_WHOLE_CHARS: number
1054
1274
  ```
1055
1275
 
1276
+ #### `MAX_HTML_INLINE_CHARS`
1277
+
1278
+ How large an HTML document must be before a whole-file read is replaced by its
1279
+ outLINE — much smaller than {@link MAX_READ_RETURN_CHARS}, because the cost of
1280
+ an HTML read is almost never worth it.
1281
+
1282
+ An entry document is boilerplate the reader rarely needs in full: meta tags,
1283
+ stylesheet and preload links, a `<style>` block, and a handful of `<script>`
1284
+ tags. The parts an executor acts on — the body's structure, where a script is
1285
+ loaded — are all visible in an outline of one line per tag. X0 flash-m77 paid
1286
+ 63,735 chars for one such read: ~60 KB of it was the IDE's own preview bridges,
1287
+ and the read pushed the transcript past its context cap, so the e2e specs the
1288
+ executor had just read were trimmed to summaries and it read the whole suite
1289
+ again — twice (findings, +01:23 and +02:56). The scaffold now keeps those
1290
+ bridges in separate files, but a template's own head is still ~5-12 KB, and a
1291
+ static-site or SSR entry can be far more.
1292
+
1293
+ The outline is not a truncation: the note beside it names the exact
1294
+ offset/limit that returns the whole document in one call.
1295
+
1296
+ ```typescript
1297
+ const MAX_HTML_INLINE_CHARS: number
1298
+ ```
1299
+
1300
+ #### `MAX_HTML_OUTLINE_LINES`
1301
+
1302
+ Most lines an HTML outline prints before it stops and says so.
1303
+
1304
+ ```typescript
1305
+ const MAX_HTML_OUTLINE_LINES: 200
1306
+ ```
1307
+
1056
1308
  #### `MAX_SEARCH_RESULTS`
1057
1309
 
1058
1310
  Max search results.
@@ -1118,6 +1370,30 @@ numbered block header, and a runner's own error lines. Matched with `grep -E`.
1118
1370
  const RUNNING_FAILURE_LINE_RE: '^[[:space:]]*(✘|✗|×|✖|✕|[0-9]+\\)|[0-9]+ (passed|failed|flaky|skipped|did not run)|FAIL|FAILED|ERROR|Error:|error:|Traceback|AssertionError)'
1119
1371
  ```
1120
1372
 
1373
+ #### `REPEATING_CAUSE_TAIL_BYTES`
1374
+
1375
+ Bytes of a running log a repeating-cause look reads back.
1376
+
1377
+ ```typescript
1378
+ const REPEATING_CAUSE_TAIL_BYTES: 40000
1379
+ ```
1380
+
1381
+ #### `REPEATING_FAILURE_MIN`
1382
+
1383
+ Failures that must share one signature before a running suite's repeating cause is named.
1384
+
1385
+ ```typescript
1386
+ const REPEATING_FAILURE_MIN: 3
1387
+ ```
1388
+
1389
+ #### `REPEATING_FAILURE_LISTED`
1390
+
1391
+ How many of a repeating cause's failures the note lists before it says "and N more".
1392
+
1393
+ ```typescript
1394
+ const REPEATING_FAILURE_LISTED: 5
1395
+ ```
1396
+
1121
1397
  #### `PARSE_CHECKABLE`
1122
1398
 
1123
1399
  Files a parse check can vouch for cheaply right after a write.
@@ -1 +1 @@
1
- {"version":3,"file":"schemas.d.ts","sourceRoot":"","sources":["../src/schemas.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAElD;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,EAAE,MAAM,CAAA;IACnB,UAAU,EAAE,UAAU,CAAA;CACvB;AAED;;GAEG;AACH,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAgU1C,CAAA"}
1
+ {"version":3,"file":"schemas.d.ts","sourceRoot":"","sources":["../src/schemas.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAElD;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,EAAE,MAAM,CAAA;IACnB,UAAU,EAAE,UAAU,CAAA;CACvB;AAED;;GAEG;AACH,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAmU1C,CAAA"}
package/dist/schemas.js CHANGED
@@ -143,13 +143,13 @@ export const TOOL_SCHEMAS = {
143
143
  },
144
144
  find_files: {
145
145
  name: 'find_files',
146
- description: "Find files by name or glob pattern recursively. Excludes node_modules and .git. Use for discovering files by name (e.g. '*.tsx', 'Dashboard*', 'index.*').",
146
+ description: "Find files by name or glob pattern recursively. Excludes node_modules and .git. Use for discovering files by name (e.g. '*.tsx', 'Dashboard*', 'index.*'). A pattern containing '/' matches the path instead: 'e2e/*.spec.ts', 'src/**/App.tsx', '**/SELECTORS.md'.",
147
147
  parameters: {
148
148
  type: 'object',
149
149
  properties: {
150
150
  pattern: {
151
151
  type: 'string',
152
- description: "File name or glob pattern (e.g. '*.tsx', 'Dashboard*').",
152
+ description: "File name or glob pattern (e.g. '*.tsx', 'Dashboard*'). A pattern containing '/' is matched against the file's path, so 'src/**/App.tsx' and 'e2e/*.spec.ts' work.",
153
153
  },
154
154
  path: {
155
155
  type: 'string',
@@ -247,7 +247,9 @@ export const TOOL_SCHEMAS = {
247
247
  'code. Blocks until it finishes or the time budget runs out, then returns — one call, ' +
248
248
  'no polling. Use it the moment you need that result to continue; never `sleep` and ' +
249
249
  'read the log by hand. If it is still running when the budget ends, the result says so ' +
250
- 'and you can call it again.',
250
+ 'and you can call it again. A suite whose failures keep repeating one cause comes back ' +
251
+ 'EARLY with that cause named, the tests it hit and the command that stops the run — act ' +
252
+ 'on it instead of waiting for the rest.',
251
253
  parameters: {
252
254
  type: 'object',
253
255
  properties: {
@@ -15,7 +15,7 @@ const TOOL_DESCRIPTIONS = {
15
15
  write_file: 'Create or overwrite files (full content)',
16
16
  edit_file: 'Make targeted search-and-replace edits (preferred for small changes)',
17
17
  search_files: 'Search for text patterns across files (grep)',
18
- find_files: 'Find files by name/glob pattern recursively',
18
+ find_files: 'Find files by name/glob pattern recursively (a pattern with "/" matches the path)',
19
19
  create_directory: 'Create directories (with parents)',
20
20
  rename_file: 'Rename or move files and directories',
21
21
  delete_file: 'Remove files',
@@ -1 +1 @@
1
- {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AA0H9C,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AA6CnE;;;;;;;;;;;GAWG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAUhE;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,gBAAgB,EAAE,MAAM,CAAC,EAAE,eAAe,GAAG,MAAM,EAAE,CAutDxF"}
1
+ {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AAkL9C,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAoDnE;;;;;;;;;;;GAWG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAUhE;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,gBAAgB,EAAE,MAAM,CAAC,EAAE,eAAe,GAAG,MAAM,EAAE,CAuzDxF"}
package/dist/tools.js CHANGED
@@ -32,6 +32,58 @@ const outputFileByTask = new Map();
32
32
  * would re-read the task's script on every single wait.
33
33
  */
34
34
  const outputFileProbed = new Set();
35
+ /**
36
+ * How often a wait looks at a running task's log for a cause repeating among its failures.
37
+ * Env-tunable so a test can exercise it.
38
+ */
39
+ const repeatingCauseLookMs = () => Number(process.env.MOL_AI_BG_CAUSE_LOOK_MS) || 10_000;
40
+ /**
41
+ * Task ids a wait has already named a repeating cause for. The note is news once; a second wait
42
+ * on the same run has nothing to add to it, and re-sending it would spend the executor's turns
43
+ * telling it what it has already been told.
44
+ */
45
+ const repeatingCauseNoted = new Set();
46
+ /**
47
+ * The shell command that stops a background task by pid: the pid the platform wrote when it
48
+ * detached the run, plus every process under it.
49
+ *
50
+ * Both halves matter and neither is a pattern. Killing only the pid file's entry can leave the
51
+ * runner (`sh -c` wraps the command here) orphaned and still driving the app, and a
52
+ * `pkill -f <something>` is the shape that matches the shell running the kill itself — the
53
+ * platform holds that command once with the processes it would hit named
54
+ * (`molecule-dev/api/src/ai/pattern-kill-guard.ts`). `pgrep -P` walks to a real pid list, which
55
+ * is what the guard's own note sends the executor to.
56
+ *
57
+ * @param taskId - The task's `mol-bg-…` handle (format already checked by the caller).
58
+ * @returns The pids, space-separated, or '' when the pid file is gone.
59
+ */
60
+ const taskPidsCommand = (taskId) => `R=$(cat /tmp/${taskId}.pid 2>/dev/null); ` +
61
+ 'all() { echo "$1"; for c in $(pgrep -P "$1" 2>/dev/null); do all "$c"; done; }; ' +
62
+ 'all "$R" | tr "\\n" " "';
63
+ /**
64
+ * The command that stops a running background task, read from the sandbox's own pid files.
65
+ *
66
+ * @param backend - The execution backend.
67
+ * @param taskId - The task's `mol-bg-…` handle.
68
+ * @returns The command to hand the executor — `kill <pids>` — falling back to the task's own pid
69
+ * file when the walk comes back empty (a sandbox without `pgrep`, or a pid file already gone,
70
+ * in which case the pid-file form is still the one the platform's guard names).
71
+ */
72
+ async function taskStopCommand(backend, taskId) {
73
+ const fallback = `kill $(cat /tmp/${taskId}.pid)`;
74
+ try {
75
+ const r = await backend.run(taskPidsCommand(taskId), { timeout: 10_000 });
76
+ const pids = String(r.stdout ?? '')
77
+ .split(/\s+/)
78
+ .filter((p) => /^\d+$/.test(p));
79
+ return pids.length ? `kill ${pids.join(' ')}` : fallback;
80
+ }
81
+ catch (_error) {
82
+ // The sandbox would not answer the pid walk. The pid-file form below is what the platform
83
+ // tells the executor to use anyway, so there is nothing to report and nothing to lose.
84
+ return fallback;
85
+ }
86
+ }
35
87
  /** A millisecond span the way a model reads it: seconds under 90, else minutes. */
36
88
  function durationText(ms) {
37
89
  const s = Math.max(0, Math.round(ms / 1000));
@@ -109,7 +161,7 @@ function waitProgressNote(o) {
109
161
  const sentence = parts.join('; ');
110
162
  return ` ${sentence[0].toUpperCase()}${sentence.slice(1)}.`;
111
163
  }
112
- import { checkBlockedCommand, declaredBudgetSeconds, DEFAULT_SEARCH_EXCLUDED_DIRS, directoryReadHint, fusedLine, isEnvFilePath, isOutsideRoot, isValidGlob, MAX_BATCH_READ_BYTES, MAX_BATCH_READ_FILES, MAX_FIND_RESULTS, MAX_OUTPUT_SIZE, MAX_READ_RETURN_CHARS, MAX_READ_SIZE, MAX_SEARCH_CONTEXT_LINES, MAX_SEARCH_RESULTS, MAX_WRITE_SIZE, outputIsWithheldUntilExit, outputRedirectTarget, parseCheckCommand, parseCheckProblem, pathArgError, printsOnlySource, redactSecrets, redactSecretsInCode, resolvePath, RUNNING_FAILURE_LINES, runningOutputSliceCommand, shellQuote, sliceLines, SMALL_FILE_WHOLE_CHARS, streamingBackgroundCommand, stripControlChars, truncateMiddle, whitespaceTolerantReplace, } from './utilities.js';
164
+ import { checkBlockedCommand, declaredBudgetSeconds, DEFAULT_SEARCH_EXCLUDED_DIRS, directoryReadHint, findPatternArgs, fusedLine, htmlOutline, isEnvFilePath, isOutsideRoot, isValidGlob, MAX_BATCH_READ_BYTES, MAX_BATCH_READ_FILES, MAX_FIND_RESULTS, MAX_HTML_INLINE_CHARS, MAX_OUTPUT_SIZE, MAX_READ_RETURN_CHARS, MAX_READ_SIZE, MAX_SEARCH_CONTEXT_LINES, MAX_SEARCH_RESULTS, MAX_WRITE_SIZE, outputIsWithheldUntilExit, outputRedirectTarget, parseCheckCommand, parseCheckProblem, pathArgError, printsOnlySource, redactSecrets, redactSecretsInCode, repeatingFailure, repeatingFailureNote, resolvePath, RUNNING_FAILURE_LINES, runningCauseCommand, runningOutputSliceCommand, shellQuote, sliceLines, SMALL_FILE_WHOLE_CHARS, streamingBackgroundCommand, stripControlChars, suiteRunPriorityLaunch, truncateMiddle, whitespaceTolerantReplace, } from './utilities.js';
113
165
  /**
114
166
  * How long exec_command waits for a command it backgrounded only because the
115
167
  * command asked for more than the tool's ceiling, before handing back a task id.
@@ -145,7 +197,7 @@ export function moleculeDistReadNote(path) {
145
197
  * @returns Array of AITool objects ready to pass to an AI provider
146
198
  */
147
199
  export function buildTools(backend, config) {
148
- const { include, exclude, pathGuards = true, symlinkGuards = false, redactSecrets: doRedact = true, blockDangerousCommands = false, blockCommand, execTimeoutMs = 120_000, commandBudgetMs, overCeilingGraceMs = OVER_CEILING_GRACE_MS, searchExcludedDirs, onAfterWrite, onFileDiff, onFileChange, } = config ?? {};
200
+ const { include, exclude, pathGuards = true, symlinkGuards = false, redactSecrets: doRedact = true, blockDangerousCommands = false, blockCommand, execTimeoutMs = 120_000, commandBudgetMs, overCeilingGraceMs = OVER_CEILING_GRACE_MS, searchExcludedDirs, outlineLargeHtml, onAfterWrite, onFileDiff, onFileChange, } = config ?? {};
149
201
  const root = backend.projectRoot;
150
202
  // A host-filesystem backend with escape guards off is the combination the
151
203
  // audit flagged: `pathGuards` off lets `..` paths out of the workspace,
@@ -499,6 +551,41 @@ export function buildTools(backend, config) {
499
551
  return {
500
552
  error: `File too large (${Math.round(content.length / 1024)}KB). Maximum is ${MAX_READ_SIZE / 1024 / 1024}MB.`,
501
553
  };
554
+ // A LARGE HTML document comes back as its OUTLINE, not its text, when the
555
+ // host asks for that (a plan-mode turn: see `outlineLargeHtml`). X0
556
+ // flash-m77 paid 63,735 chars for one `read_file` of `app/index.html` —
557
+ // ~60 KB of it the IDE's own preview bridges — and that read alone took the
558
+ // transcript past its context cap, so the e2e specs it had just read were
559
+ // trimmed to summaries and it read the whole suite again, twice (+01:23,
560
+ // +02:56). An outline is not a truncation: the note names the exact
561
+ // offset/limit that returns the whole document, and any line range works.
562
+ if (!sliceLines(content, window) &&
563
+ content.length > MAX_HTML_INLINE_CHARS &&
564
+ outlineLargeHtml?.()) {
565
+ const outline = htmlOutline(content, path);
566
+ if (outline) {
567
+ const totalLines = content.split('\n').length;
568
+ return noted({
569
+ path,
570
+ // Same redaction every other read goes through: an outline is made of
571
+ // the document's own lines, and a meta tag can carry a key.
572
+ content: sanitizeFileContent(outline, path),
573
+ outline: true,
574
+ bytes: content.length,
575
+ totalLines,
576
+ note: `Outlined, not returned whole: a ${Math.round(content.length / 1024)} KB HTML document is ` +
577
+ `its tags plus a lot of plumbing, and the tags are what matter. ` +
578
+ `The whole file is ONE call — read_file {path, offset: 1, limit: ${totalLines}} — ` +
579
+ `and any line range works the same way.` +
580
+ // The IDE's own preview scripts are in this document but are not the
581
+ // app's behaviour, and an executor that reads them pays 60 KB for
582
+ // something it must never edit (flash-m77). Named only when present.
583
+ (/<!--\s*mol:[a-z0-9-]+-(?:bridge|shim)/.test(content)
584
+ ? ' The `mol:*-bridge` scripts the IDE injects belong to the platform, not to the app: leave them as they are.'
585
+ : ''),
586
+ });
587
+ }
588
+ }
502
589
  // No window asked for and the file is bigger than one call should hand
503
590
  // back: return its first window, sized to the ceiling, and say so. The
504
591
  // model can read on with offset/limit; it cannot un-send 300k tokens.
@@ -1162,9 +1249,18 @@ export function buildTools(backend, config) {
1162
1249
  if (symlinkErr)
1163
1250
  return { error: symlinkErr };
1164
1251
  try {
1165
- const result = await backend.run(`find ${shellQuote(path)} ${findPruneArgs(path)} -name ${shellQuote(pattern)} -print 2>/dev/null | head -${MAX_FIND_RESULTS}`, { timeout: 10000 });
1252
+ // `findPatternArgs`, not `-name`: a pattern with a `/` in it
1253
+ // (`helpers/*`, `e2e/*.spec.ts`) matched nothing at all, because `-name`
1254
+ // compares it against the basename (xr-molecule-flash-m77).
1255
+ const result = await backend.run(`find ${shellQuote(path)} ${findPruneArgs(path)} ${findPatternArgs(pattern)} -print 2>/dev/null | head -${MAX_FIND_RESULTS}`, { timeout: 10000 });
1166
1256
  const files = result.stdout.trim().split('\n').filter(Boolean);
1167
- return { pattern, path, files };
1257
+ // A slash changes what the pattern means — the whole path, not the file
1258
+ // name — and an empty result does not say so, so the model reads it as
1259
+ // "no such file" and goes looking elsewhere. Name the rule on the miss.
1260
+ const note = files.length === 0 && pattern.includes('/')
1261
+ ? `"${pattern}" contains a "/", so it was matched against each file's PATH below ${path} (a leading "*/" is implicit), not against the file name — no path matched it. list_files shows the real entries.`
1262
+ : undefined;
1263
+ return { pattern, path, files, ...(note ? { note } : {}) };
1168
1264
  }
1169
1265
  catch (e) {
1170
1266
  return { error: `Find failed: ${e.message}` };
@@ -1294,6 +1390,11 @@ export function buildTools(backend, config) {
1294
1390
  // Keep the log growing while it runs (see streamingBackgroundCommand):
1295
1391
  // an empty log is what wait_for_task reads as stuck.
1296
1392
  const streaming = streamingBackgroundCommand(command);
1393
+ // A whole-suite run starves the sandbox it shares with the app and with
1394
+ // every command the executor sends next, so it starts at lower CPU and
1395
+ // IO priority (see suiteRunPriorityLaunch). Foreground runs are never
1396
+ // touched, and a sandbox without the tools runs it unchanged.
1397
+ const prioritized = suiteRunPriorityLaunch(streaming.command, `sh ${script} > ${log} 2>&1; echo $? > ${exitFile}`);
1297
1398
  const ownTarget = outputRedirectTarget(streaming.command);
1298
1399
  const ownFile = ownTarget
1299
1400
  ? ownTarget.startsWith('/')
@@ -1303,7 +1404,7 @@ export function buildTools(backend, config) {
1303
1404
  if (ownFile)
1304
1405
  outputFileByTask.set(id, ownFile);
1305
1406
  await backend.writeFile(script, `${streaming.command}\n`);
1306
- await backend.run(`nohup sh -c ${shellQuote(`sh ${script} > ${log} 2>&1; echo $? > ${exitFile}`)} ` +
1407
+ await backend.run(`nohup sh -c ${shellQuote(prioritized.launch)} ` +
1307
1408
  `> /dev/null 2>&1 & echo $! > /tmp/${id}.pid`, { cwd, timeout: 15_000 });
1308
1409
  // A command backgrounded only for the budget it ASKED for often ends in
1309
1410
  // seconds (a Hurl suite: 37 ms). Handing it back as a task cost a
@@ -1360,6 +1461,7 @@ export function buildTools(backend, config) {
1360
1461
  `check back. If you need this result before you can continue, call wait_for_task ` +
1361
1462
  `with this taskId — never sleep and read the log by hand.` +
1362
1463
  (streaming.changes.length ? ` ${streaming.changes.join(' ')}` : '') +
1464
+ (prioritized.note ? ` ${prioritized.note}` : '') +
1363
1465
  (ownFile
1364
1466
  ? ` This command writes its own output to ${ownFile}, so the task log stays empty: peek with exec_command: tail ${ownFile}.`
1365
1467
  : ''),
@@ -1588,10 +1690,53 @@ export function buildTools(backend, config) {
1588
1690
  return null;
1589
1691
  }
1590
1692
  };
1693
+ // A running SUITE whose failures repeat one cause is named at the third of them. The run
1694
+ // would otherwise spend its whole clock proving that cause, and the executor only learns it
1695
+ // from the finished log: X1 flash-m76 waited out 4.0 minutes of 15 tests where every
1696
+ // failure was the same `TimeoutError: page.waitForURL` from one missing `navigate('/')`,
1697
+ // and then fixed it in 51 s. The task is NOT stopped — the executor is handed the cause and
1698
+ // its own pid command and decides.
1699
+ let lastCauseLookAt = startedAt - repeatingCauseLookMs();
1591
1700
  let exitCode = await readExit();
1592
1701
  while (exitCode === null && Date.now() - startedAt < budgetMs) {
1593
1702
  await new Promise((r) => setTimeout(r, Math.min(2_000, budgetMs - (Date.now() - startedAt))));
1594
1703
  exitCode = await readExit();
1704
+ // The exit file is read first, so a run that finishes between two looks still returns
1705
+ // its real result instead of a note about it.
1706
+ if (exitCode !== null)
1707
+ break;
1708
+ if (repeatingCauseNoted.has(taskId))
1709
+ continue;
1710
+ if (Date.now() - lastCauseLookAt < repeatingCauseLookMs())
1711
+ continue;
1712
+ lastCauseLookAt = Date.now();
1713
+ let causeLog;
1714
+ try {
1715
+ const r = await backend.run(runningCauseCommand(log, outputFileByTask.get(taskId)), {
1716
+ timeout: 10_000,
1717
+ });
1718
+ causeLog = r.stdout ?? '';
1719
+ }
1720
+ catch (_error) {
1721
+ // No look at the log this tick; the next one asks again.
1722
+ continue;
1723
+ }
1724
+ const found = repeatingFailure(causeLog);
1725
+ if (!found)
1726
+ continue;
1727
+ repeatingCauseNoted.add(taskId);
1728
+ const earlyMs = Date.now() - startedAt;
1729
+ waitedByTask.set(taskId, waitedBefore + earlyMs);
1730
+ return {
1731
+ taskId,
1732
+ status: 'repeating_failure',
1733
+ waitedMs: earlyMs,
1734
+ note: repeatingFailureNote(found, {
1735
+ taskId,
1736
+ log,
1737
+ stop: await taskStopCommand(backend, taskId),
1738
+ }),
1739
+ };
1595
1740
  }
1596
1741
  const waitedMs = Date.now() - startedAt;
1597
1742
  waitedByTask.set(taskId, waitedBefore + waitedMs);
package/dist/types.d.ts CHANGED
@@ -112,6 +112,22 @@ export interface ToolBuildConfig {
112
112
  * setting so every search surface shares ONE synchronized set.
113
113
  */
114
114
  searchExcludedDirs?: string[];
115
+ /**
116
+ * Whether a LARGE HTML document should come back as its OUTLINE — the head's
117
+ * tags and the tags that open each body section, one line each, plus a note
118
+ * naming the offset/limit that returns the whole document — instead of its
119
+ * whole text. Over {@link MAX_HTML_INLINE_CHARS}, and only when the read named
120
+ * no line window.
121
+ *
122
+ * A getter, not a boolean, because the answer belongs to the TURN, not to the
123
+ * tool set: a host that plans first and builds after wants an outline while the
124
+ * executor is researching (where a read it cannot act on is pure context cost)
125
+ * and the document itself while it is writing the page. Unset means every big
126
+ * HTML read returns its text, which is what a host with no such phases wants.
127
+ *
128
+ * @returns True when a large HTML read should be outlined rather than returned.
129
+ */
130
+ outlineLargeHtml?: () => boolean;
115
131
  /** Post-write hook (e.g. auto-format via Prettier/ESLint). Called after every write_file/edit_file. */
116
132
  onAfterWrite?: (path: string) => Promise<void>;
117
133
  /** Diff tracking callback. Called before writes with old/new content. */
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}