@molecule/api-ai-tools 1.6.5 → 1.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +600 -28
- package/dist/command-position.d.ts +26 -0
- package/dist/command-position.d.ts.map +1 -1
- package/dist/command-position.js +54 -7
- package/dist/cut-run.d.ts +124 -0
- package/dist/cut-run.d.ts.map +1 -0
- package/dist/cut-run.js +267 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/tools.d.ts +20 -0
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +330 -82
- package/dist/trace-artifacts.d.ts +147 -0
- package/dist/trace-artifacts.d.ts.map +1 -0
- package/dist/trace-artifacts.js +467 -0
- package/dist/utilities.d.ts +152 -18
- package/dist/utilities.d.ts.map +1 -1
- package/dist/utilities.js +207 -26
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -3,15 +3,11 @@ 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-
|
|
6
|
+
Generated: 2026-10-17T00:30:38.264Z
|
|
7
7
|
-->
|
|
8
8
|
|
|
9
9
|
# @molecule/api-ai-tools
|
|
10
10
|
|
|
11
|
-
> **Auto-generated, AI-first package reference** for the [molecule.dev](https://molecule.dev) ecosystem.
|
|
12
|
-
> It is written to be read by coding agents as much as by people, and is generated from this
|
|
13
|
-
> package's source — edit `src/index.ts` JSDoc, not this file.
|
|
14
|
-
|
|
15
11
|
Shared AI agent tool set for molecule.dev — filesystem, search, and shell
|
|
16
12
|
tools an LLM agent can call, with a swappable execution backend.
|
|
17
13
|
|
|
@@ -24,6 +20,10 @@ WHERE they act: `createLocalBackend(projectRoot)` (host filesystem) or
|
|
|
24
20
|
container). `buildAgentPrompt(ctx)` composes a matching system prompt
|
|
25
21
|
(identity, tool listing, project docs, discovered skills).
|
|
26
22
|
|
|
23
|
+
> **Auto-generated, AI-first package reference** for the [molecule.dev](https://molecule.dev) ecosystem.
|
|
24
|
+
> It is written to be read by coding agents as much as by people, and is generated from this
|
|
25
|
+
> package's source — edit `src/index.ts` JSDoc, not this file.
|
|
26
|
+
|
|
27
27
|
## Quick Start
|
|
28
28
|
|
|
29
29
|
```typescript
|
|
@@ -73,6 +73,37 @@ interface CommandSegment {
|
|
|
73
73
|
args: readonly string[]
|
|
74
74
|
/** The program and its arguments as one line — what a runner pattern is matched against. */
|
|
75
75
|
text: string
|
|
76
|
+
/**
|
|
77
|
+
* The seconds the segment's own `timeout` wrapper allows the command it runs
|
|
78
|
+
* (`timeout 280 hurl --test …` → 280), or null when it carries none.
|
|
79
|
+
*/
|
|
80
|
+
timeoutSeconds: number | null
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
#### `CutRun`
|
|
85
|
+
|
|
86
|
+
A run a budget stopped, as its result reports it.
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
interface CutRun {
|
|
90
|
+
/** The bound the run was allowed, in seconds. */
|
|
91
|
+
seconds: number
|
|
92
|
+
/** Whether this tool's budget stopped it or the `timeout` the command carried. */
|
|
93
|
+
stoppedBy: CutBy
|
|
94
|
+
/**
|
|
95
|
+
* The exit code the run's own process reported, or null when the code in the result belongs to
|
|
96
|
+
* the pipeline's filter (`… | tail`) rather than to the run — the case that read as success.
|
|
97
|
+
*/
|
|
98
|
+
exitCode: number | null
|
|
99
|
+
/** How many files or tests the runner's own output shows finishing, or null when it shows none. */
|
|
100
|
+
ran: number | null
|
|
101
|
+
/** How many the run was walking, when the runner printed it (Playwright's `[n/K]`), or null. */
|
|
102
|
+
total: number | null
|
|
103
|
+
/** The noun the runner counts in. */
|
|
104
|
+
noun: 'files' | 'tests'
|
|
105
|
+
/** The last thing the run printed — the line a reader looks at first. */
|
|
106
|
+
doing: string | null
|
|
76
107
|
}
|
|
77
108
|
```
|
|
78
109
|
|
|
@@ -155,6 +186,51 @@ interface SuiteRunInFlight {
|
|
|
155
186
|
}
|
|
156
187
|
```
|
|
157
188
|
|
|
189
|
+
#### `TraceRequest`
|
|
190
|
+
|
|
191
|
+
One request a traced page made, as `*.network` records it.
|
|
192
|
+
|
|
193
|
+
```typescript
|
|
194
|
+
interface TraceRequest {
|
|
195
|
+
/** The HTTP method (`POST`). */
|
|
196
|
+
method: string
|
|
197
|
+
/** The request's path (`/api/articles/x/favorite`), or the whole URL when it is cross-origin. */
|
|
198
|
+
path: string
|
|
199
|
+
/** The response status, or 0 when the request never got a response. */
|
|
200
|
+
status: number
|
|
201
|
+
}
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
#### `TraceArtifact`
|
|
205
|
+
|
|
206
|
+
What one trace says: the test it belongs to, the requests it made, and its console errors.
|
|
207
|
+
|
|
208
|
+
```typescript
|
|
209
|
+
interface TraceArtifact {
|
|
210
|
+
/** The failure's spec file, relative to the run's directory, when the trace names one. */
|
|
211
|
+
spec: string | null
|
|
212
|
+
/** The line of the test's last traced step, 0 when the trace names no spec. */
|
|
213
|
+
line: number
|
|
214
|
+
/** The requests the page made, the interesting ones first ({@link traceArtifactsScript}). */
|
|
215
|
+
requests: TraceRequest[]
|
|
216
|
+
/** The console errors and page errors the page raised. */
|
|
217
|
+
console: string[]
|
|
218
|
+
}
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
#### `DiagnosisTraceRewrite`
|
|
222
|
+
|
|
223
|
+
A command with the trace flag this platform decided to add, and the sentence to report it in.
|
|
224
|
+
|
|
225
|
+
```typescript
|
|
226
|
+
interface DiagnosisTraceRewrite {
|
|
227
|
+
/** The command to run. */
|
|
228
|
+
command: string
|
|
229
|
+
/** One sentence naming the flag and why, or null when nothing was added. */
|
|
230
|
+
change: string | null
|
|
231
|
+
}
|
|
232
|
+
```
|
|
233
|
+
|
|
158
234
|
#### `ExecutionBackend`
|
|
159
235
|
|
|
160
236
|
Abstraction over the execution environment.
|
|
@@ -443,6 +519,22 @@ interface FailureCause {
|
|
|
443
519
|
|
|
444
520
|
### Types
|
|
445
521
|
|
|
522
|
+
#### `CommandRunState`
|
|
523
|
+
|
|
524
|
+
What a command's result records: a run that ended, or one a budget stopped.
|
|
525
|
+
|
|
526
|
+
```typescript
|
|
527
|
+
type CommandRunState = 'ran' | typeof CUT_AT_BUDGET
|
|
528
|
+
```
|
|
529
|
+
|
|
530
|
+
#### `CutBy`
|
|
531
|
+
|
|
532
|
+
What stopped a run.
|
|
533
|
+
|
|
534
|
+
```typescript
|
|
535
|
+
type CutBy = 'tool-budget' | 'command-timeout'
|
|
536
|
+
```
|
|
537
|
+
|
|
446
538
|
#### `DiscoveredSkill` _(deprecated)_
|
|
447
539
|
|
|
448
540
|
Re-export SkillEntry as DiscoveredSkill for backwards compatibility.
|
|
@@ -491,6 +583,18 @@ function createSandboxBackend(sandbox: SandboxLike, projectRoot?: string): Execu
|
|
|
491
583
|
|
|
492
584
|
**Returns:** A backend that proxies all filesystem calls into the sandbox.
|
|
493
585
|
|
|
586
|
+
#### `durationSeconds(word)`
|
|
587
|
+
|
|
588
|
+
The seconds a duration word stands for.
|
|
589
|
+
|
|
590
|
+
```typescript
|
|
591
|
+
function durationSeconds(word: string): number | null
|
|
592
|
+
```
|
|
593
|
+
|
|
594
|
+
- `word` — A duration (`280`, `20s`, `1m`).
|
|
595
|
+
|
|
596
|
+
**Returns:** The seconds, or null when the word is not a duration.
|
|
597
|
+
|
|
494
598
|
#### `commandSegments(command)`
|
|
495
599
|
|
|
496
600
|
The segments of a command, each with its program and arguments — the answer every classifier
|
|
@@ -504,6 +608,25 @@ function commandSegments(command: string): CommandSegment[]
|
|
|
504
608
|
|
|
505
609
|
**Returns:** One entry per statement or pipeline stage that runs something, in order.
|
|
506
610
|
|
|
611
|
+
#### `commandTimeoutSeconds(command)`
|
|
612
|
+
|
|
613
|
+
The bound a command puts on its own run: the largest `timeout <N>` any of its segments runs
|
|
614
|
+
under (`timeout 280 hurl --test …` → 280), or null when it carries none.
|
|
615
|
+
|
|
616
|
+
This is the bound `man timeout` enforces by KILLING the command — so a run that used it did not
|
|
617
|
+
finish, whatever the pipeline's own exit status says. It is read here, from the same parse every
|
|
618
|
+
other command reader uses, rather than matched as text: `timeout` in an argument
|
|
619
|
+
(`grep -n 'timeout 280' notes.md`) bounds nothing, and `-k 5` is the grace before the kill, not
|
|
620
|
+
the bound ({@link WRAPPER_VALUE_FLAGS}).
|
|
621
|
+
|
|
622
|
+
```typescript
|
|
623
|
+
function commandTimeoutSeconds(command: string): number | null
|
|
624
|
+
```
|
|
625
|
+
|
|
626
|
+
- `command` — The shell command.
|
|
627
|
+
|
|
628
|
+
**Returns:** The seconds the command allows itself, or null.
|
|
629
|
+
|
|
507
630
|
#### `runsInCommandPosition(command, pattern)`
|
|
508
631
|
|
|
509
632
|
Whether any segment of the command runs a program whose command-position text matches
|
|
@@ -522,6 +645,102 @@ function runsInCommandPosition(command: string, pattern: RegExp): boolean
|
|
|
522
645
|
|
|
523
646
|
**Returns:** True when a segment in command position matches.
|
|
524
647
|
|
|
648
|
+
#### `commandRunState(result)`
|
|
649
|
+
|
|
650
|
+
The state a command's result records — THE reader every verdict about a run goes through.
|
|
651
|
+
|
|
652
|
+
A result that carries no state is a run that ended: the state is written where the run is
|
|
653
|
+
stopped ({@link commandCutRun}), so a result without it was never stopped by a budget this
|
|
654
|
+
platform set or can see. Results produced before this state existed read the same way, which is
|
|
655
|
+
what keeps a past turn's tally readable.
|
|
656
|
+
|
|
657
|
+
```typescript
|
|
658
|
+
function commandRunState(result: unknown): CommandRunState
|
|
659
|
+
```
|
|
660
|
+
|
|
661
|
+
- `result` — The exec_command / wait_for_task result.
|
|
662
|
+
|
|
663
|
+
**Returns:** The state the result records.
|
|
664
|
+
|
|
665
|
+
#### `suiteTally(text)`
|
|
666
|
+
|
|
667
|
+
Whether text carries a runner's closing summary ({@link SUITE_TALLY}).
|
|
668
|
+
|
|
669
|
+
```typescript
|
|
670
|
+
function suiteTally(text: string): boolean
|
|
671
|
+
```
|
|
672
|
+
|
|
673
|
+
- `text` — The run's stdout and stderr.
|
|
674
|
+
|
|
675
|
+
**Returns:** True when the runner's own end-of-run summary is there.
|
|
676
|
+
|
|
677
|
+
#### `commandCutRun(command, result, elapsedMs, budgetSeconds)`
|
|
678
|
+
|
|
679
|
+
The run a command's result records as stopped by a budget, or null when the result is a run that
|
|
680
|
+
ended (or says nothing about a run).
|
|
681
|
+
|
|
682
|
+
Evidence, in order — never a guess:
|
|
683
|
+
|
|
684
|
+
- `timeout`'s own 124 (the code the backend's wrapper returns whenever the TOOL's budget fired,
|
|
685
|
+
whatever the pipeline ended with) — and, when the clock says the command's own smaller bound
|
|
686
|
+
ran out first and less than the budget, that bound instead.
|
|
687
|
+
- the command carried its own `timeout <N>`, N is inside this tool's budget (a larger one never
|
|
688
|
+
fires — the tool's does), the run used N, and no runner printed a closing summary: the run was
|
|
689
|
+
killed at its own bound. Its exit code, when the pipeline handed one back, is the filter's.
|
|
690
|
+
- the run used this tool's whole budget behind a filter that withheld its output, and printed no
|
|
691
|
+
closing summary: the budget stopped it, and no code in the result is the run's.
|
|
692
|
+
|
|
693
|
+
```typescript
|
|
694
|
+
function commandCutRun(
|
|
695
|
+
command: string,
|
|
696
|
+
result: unknown,
|
|
697
|
+
elapsedMs: number,
|
|
698
|
+
budgetSeconds?: number,
|
|
699
|
+
): CutRun | null
|
|
700
|
+
```
|
|
701
|
+
|
|
702
|
+
- `command` — The command the executor sent.
|
|
703
|
+
- `result` — Its result.
|
|
704
|
+
- `elapsedMs` — How long the command ran, in ms.
|
|
705
|
+
- `budgetSeconds` — This tool's foreground budget, in seconds (0 when it has none).
|
|
706
|
+
|
|
707
|
+
**Returns:** The stopped run, or null.
|
|
708
|
+
|
|
709
|
+
#### `cutRunFacts(result)`
|
|
710
|
+
|
|
711
|
+
The facts a result records about the run a budget stopped it in — what a reader needs to say
|
|
712
|
+
how far the run got WITHOUT re-reading its log or re-deriving the counts.
|
|
713
|
+
|
|
714
|
+
The numbers are read from the runner's own output where it printed them (Hurl's per-file lines,
|
|
715
|
+
Playwright's `[n/K]`), so a caller that can also see the suite itself — the file count of the
|
|
716
|
+
`*.hurl` directory the run walked — fills in the `total` this could not know, and both then say
|
|
717
|
+
the same sentence ({@link cutRunSentence}).
|
|
718
|
+
|
|
719
|
+
```typescript
|
|
720
|
+
function cutRunFacts(result: unknown): CutRun | null
|
|
721
|
+
```
|
|
722
|
+
|
|
723
|
+
- `result` — The exec_command / wait_for_task result.
|
|
724
|
+
|
|
725
|
+
**Returns:** The facts the result carries, or null when it records no stopped run.
|
|
726
|
+
|
|
727
|
+
#### `cutRunSentence(run)`
|
|
728
|
+
|
|
729
|
+
The first line of a stopped run's result: how long it was allowed, what stopped it, how far it
|
|
730
|
+
got, and the last thing it printed.
|
|
731
|
+
|
|
732
|
+
Written to be quotable by the model in one sentence and to leave no room for "nearly passing":
|
|
733
|
+
the counts come from the runner's own output ({@link runProgress}), and a `total` the caller
|
|
734
|
+
read elsewhere (the suite's own file count) fills in the "M of K" it can then state.
|
|
735
|
+
|
|
736
|
+
```typescript
|
|
737
|
+
function cutRunSentence(run: CutRun): string
|
|
738
|
+
```
|
|
739
|
+
|
|
740
|
+
- `run` — The stopped run.
|
|
741
|
+
|
|
742
|
+
**Returns:** The sentence.
|
|
743
|
+
|
|
525
744
|
#### `normalizeToolInput(schema, raw)`
|
|
526
745
|
|
|
527
746
|
Move known aliases onto the declared parameter names and report what is still
|
|
@@ -851,6 +1070,107 @@ function buildTools(backend: ExecutionBackend, config?: ToolBuildConfig): AITool
|
|
|
851
1070
|
|
|
852
1071
|
**Returns:** Array of AITool objects ready to pass to an AI provider
|
|
853
1072
|
|
|
1073
|
+
#### `traceArtifactsScript()`
|
|
1074
|
+
|
|
1075
|
+
The Node program the sandbox runs to read a failed run's traces: `node <script> <dir>` prints one
|
|
1076
|
+
`TRACE <json>` line per `trace.zip` under `<dir>` (newest first, {@link MAX_TRACE_ARTIFACTS} of
|
|
1077
|
+
them), each with the test it belongs to, the requests its page made and its console errors.
|
|
1078
|
+
|
|
1079
|
+
It is a standalone program, not an import, for the reason `@molecule/api-code-sandbox` runs it:
|
|
1080
|
+
Playwright's own zip reader is not reachable from a project's `node_modules`, and a dependency
|
|
1081
|
+
on `unzip` would make the read fail on every sandbox that has none. The zip is read with the
|
|
1082
|
+
central directory and `zlib.inflateRawSync` — about forty lines, and the only thing that has to
|
|
1083
|
+
be true of a `trace.zip` is that it IS one.
|
|
1084
|
+
|
|
1085
|
+
A request is kept over a static asset when it failed (`status >= 400`) or never answered
|
|
1086
|
+
(`status === 0`), and over anything else when it is the page talking to the app
|
|
1087
|
+
(`fetch`/`xhr`/`document`): a failing test's trace holds the whole page load, and the answer is
|
|
1088
|
+
always at the end of it.
|
|
1089
|
+
|
|
1090
|
+
```typescript
|
|
1091
|
+
function traceArtifactsScript(): string
|
|
1092
|
+
```
|
|
1093
|
+
|
|
1094
|
+
**Returns:** The program's source, to write to a file and run.
|
|
1095
|
+
|
|
1096
|
+
#### `parseTraceArtifacts(stdout)`
|
|
1097
|
+
|
|
1098
|
+
The traces {@link traceArtifactsScript} printed, parsed.
|
|
1099
|
+
|
|
1100
|
+
A line that is not a complete `TRACE <json>` (a truncated read, a program that died mid-write) is
|
|
1101
|
+
dropped: a partial trace is not evidence about the test it came from.
|
|
1102
|
+
|
|
1103
|
+
```typescript
|
|
1104
|
+
function parseTraceArtifacts(stdout: string): TraceArtifact[]
|
|
1105
|
+
```
|
|
1106
|
+
|
|
1107
|
+
- `stdout` — The program's output.
|
|
1108
|
+
|
|
1109
|
+
**Returns:** One entry per trace it printed.
|
|
1110
|
+
|
|
1111
|
+
#### `failureTraceNote(items, target)`
|
|
1112
|
+
|
|
1113
|
+
The note a failed suite run's result carries: what each failing test's page actually sent, and
|
|
1114
|
+
what came back.
|
|
1115
|
+
|
|
1116
|
+
This is the answer to the question a swallowed error hides — a click that changed nothing is
|
|
1117
|
+
usually a request that failed — so the note leads with the failing requests and says what to do
|
|
1118
|
+
with them: read the handler the route reaches, not the selector. It is BOUNDED to
|
|
1119
|
+
{@link MAX_TRACE_NOTE_TESTS} tests with {@link MAX_TRACE_NOTE_REQUESTS} requests each, because it
|
|
1120
|
+
rides in a result the model pays for.
|
|
1121
|
+
|
|
1122
|
+
```typescript
|
|
1123
|
+
function failureTraceNote(items: readonly TraceArtifact[], target: string): string | null
|
|
1124
|
+
```
|
|
1125
|
+
|
|
1126
|
+
- `items` — The traces {@link parseTraceArtifacts} read.
|
|
1127
|
+
- `target` — Where the run went — the deployed host, as the note names it.
|
|
1128
|
+
|
|
1129
|
+
**Returns:** The note, or null when no trace held a request or a console error.
|
|
1130
|
+
|
|
1131
|
+
#### `suiteRecordsTraces(configText)`
|
|
1132
|
+
|
|
1133
|
+
Whether a Playwright config records traces for the tests that fail.
|
|
1134
|
+
|
|
1135
|
+
Only `on` and `retain-on-failure` keep a `trace.zip` for a failure whatever the retries do: the
|
|
1136
|
+
two retry modes need a retry to happen at all, and a diagnosis run is given `--retries=0`
|
|
1137
|
+
(`streamingBackgroundCommand`), so on X1 xr-molecule-flash-m100 the run's own
|
|
1138
|
+
`trace: 'on-first-retry'` recorded nothing — which is what left the failing tests' network log
|
|
1139
|
+
unreadable. A config that sets no `trace:` at all records nothing either (Playwright's default
|
|
1140
|
+
is off); so does one that cannot be read.
|
|
1141
|
+
|
|
1142
|
+
```typescript
|
|
1143
|
+
function suiteRecordsTraces(configText: string): boolean
|
|
1144
|
+
```
|
|
1145
|
+
|
|
1146
|
+
- `configText` — The config's source, or '' when none could be read.
|
|
1147
|
+
|
|
1148
|
+
**Returns:** True when the config records traces for failures.
|
|
1149
|
+
|
|
1150
|
+
#### `diagnosisTraceCommand(args)`
|
|
1151
|
+
|
|
1152
|
+
The command a DIAGNOSIS run needs to leave a readable trace behind, when the suite's config
|
|
1153
|
+
would record none.
|
|
1154
|
+
|
|
1155
|
+
The failing tests' requests are only readable if the run wrote them down (see the module doc),
|
|
1156
|
+
and the config is where the decision about that lives — the platform does not edit the suite's
|
|
1157
|
+
config, so it passes the flag instead, and only where it can do no harm: on a run narrowed to a
|
|
1158
|
+
part of the suite or capped ({@link isDiagnosisRun}), never on the whole-suite proof run, and
|
|
1159
|
+
never over a `--trace` the executor wrote itself. A capped walk is exactly the run whose
|
|
1160
|
+
failures someone is about to work on; the proof run is graded as it is.
|
|
1161
|
+
|
|
1162
|
+
```typescript
|
|
1163
|
+
function diagnosisTraceCommand(args: {
|
|
1164
|
+
command: string
|
|
1165
|
+
cwd: string
|
|
1166
|
+
readFile: (path: string) => Promise<string>
|
|
1167
|
+
}): Promise<DiagnosisTraceRewrite>
|
|
1168
|
+
```
|
|
1169
|
+
|
|
1170
|
+
- `args` — The command, the directory it starts in, and the backend's file read.
|
|
1171
|
+
|
|
1172
|
+
**Returns:** The command to run and the sentence to report, or the command unchanged.
|
|
1173
|
+
|
|
854
1174
|
#### `shellQuote(s)`
|
|
855
1175
|
|
|
856
1176
|
Shell-safe quoting using single quotes. Unlike JSON.stringify (double quotes),
|
|
@@ -1199,6 +1519,105 @@ function findPatternArgs(pattern: string): string
|
|
|
1199
1519
|
|
|
1200
1520
|
**Returns:** A `find` predicate: `-name '…'` for a bare name, else one or more `-path '…'` alternatives, ORed inside parens when there is more than one.
|
|
1201
1521
|
|
|
1522
|
+
#### `findPathArgs(pattern)`
|
|
1523
|
+
|
|
1524
|
+
The `-path` alternatives of {@link findPatternArgs}, for a pattern whose
|
|
1525
|
+
directory the caller has already rooted the search at.
|
|
1526
|
+
|
|
1527
|
+
`find_files` narrows the search root itself when the pattern spells its own
|
|
1528
|
+
directory out (see {@link literalDirPrefix}) — `node_modules` plus
|
|
1529
|
+
`@molecule/api-resource-user/dist/*.d.ts` searched that one directory. The
|
|
1530
|
+
remainder (`*.d.ts`) must then keep the `-path` form rather than collapsing to
|
|
1531
|
+
`-name`: `*` crosses `/` in an fnmatch glob, so the `-path` form still finds a
|
|
1532
|
+
declaration file in a `dist/utilities/` subdirectory, which `-name '*.d.ts'`
|
|
1533
|
+
would drop (a silent partial answer, which is worse than the timeout it
|
|
1534
|
+
replaced).
|
|
1535
|
+
|
|
1536
|
+
```typescript
|
|
1537
|
+
function findPathArgs(pattern: string): string
|
|
1538
|
+
```
|
|
1539
|
+
|
|
1540
|
+
- `pattern` — The pattern's remainder, with a `/` in it or not.
|
|
1541
|
+
|
|
1542
|
+
**Returns:** One or more `-path '…'` alternatives, ORed inside parens when there is more than one.
|
|
1543
|
+
|
|
1544
|
+
#### `literalDirPrefix(pattern)`
|
|
1545
|
+
|
|
1546
|
+
The directory a GLOB pattern spells out, when it spells one out: everything
|
|
1547
|
+
before the pattern's first glob character, trimmed back to the last `/`.
|
|
1548
|
+
|
|
1549
|
+
A pattern is matched against the whole path with a leading "any parent"
|
|
1550
|
+
anchor ({@link findPatternArgs}), so `find` visits every entry below the
|
|
1551
|
+
search root. Handed `/workspace/node_modules` and
|
|
1552
|
+
`@molecule/api-resource-user/dist/*.d.ts` — a directory named outright —
|
|
1553
|
+
`find` walked the sandbox's entire dependency tree and hit the tool's 10 s
|
|
1554
|
+
deadline without printing a result (xr-molecule-flash-m94: the one directory
|
|
1555
|
+
the model meant answered a `list_files` in 0 s). The literal prefix is that
|
|
1556
|
+
directory, and rooting the search at it is what keeps the call instant.
|
|
1557
|
+
|
|
1558
|
+
A BARE first segment (`helpers/*`, `bonds/*`, `e2e/*.spec.ts`) is NOT one:
|
|
1559
|
+
`helpers/*` means "a `helpers/` directory anywhere below the root", which the
|
|
1560
|
+
tool documents and a live run relied on (xr-molecule-flash-m77 — a path
|
|
1561
|
+
pattern is not anchored to one depth). Rooting those at
|
|
1562
|
+
`<root>/helpers` would answer with a SUBSET, silently: the nested
|
|
1563
|
+
`api/src/helpers/` matches would disappear, and a missing file reads as
|
|
1564
|
+
"no such file". So a prefix is returned only when it names a path of two or
|
|
1565
|
+
more segments (`@molecule/api-resource-user/dist`), which is the shape that
|
|
1566
|
+
is a directory rather than a directory name.
|
|
1567
|
+
|
|
1568
|
+
```typescript
|
|
1569
|
+
function literalDirPrefix(pattern: string): string | null
|
|
1570
|
+
```
|
|
1571
|
+
|
|
1572
|
+
- `pattern` — A pattern already accepted by {@link isValidGlob}.
|
|
1573
|
+
|
|
1574
|
+
**Returns:** The literal directory prefix, without a trailing slash, or null when the pattern has no glob character, no `/`, or only a bare name before one.
|
|
1575
|
+
|
|
1576
|
+
#### `isCommandTimeout(error)`
|
|
1577
|
+
|
|
1578
|
+
Whether a backend error is the sandbox's own command deadline.
|
|
1579
|
+
|
|
1580
|
+
The E2B SDK answers a timed-out `exec` with its generic text —
|
|
1581
|
+
`[deadline_exceeded] the operation timed out: … It can be modified by passing
|
|
1582
|
+
'timeoutMs' when making the request. Use '0' to disable the timeout.` — which
|
|
1583
|
+
names an option the executor cannot set and says nothing about the search
|
|
1584
|
+
that died. Handing that sentence to the model is what xr-molecule-flash-m94
|
|
1585
|
+
read after `find_files` spent 10 s walking `node_modules`; the tool answers
|
|
1586
|
+
the deadline in its own words instead (see {@link searchTimeoutError}).
|
|
1587
|
+
|
|
1588
|
+
```typescript
|
|
1589
|
+
function isCommandTimeout(error: unknown): boolean
|
|
1590
|
+
```
|
|
1591
|
+
|
|
1592
|
+
- `error` — The error the backend threw.
|
|
1593
|
+
|
|
1594
|
+
**Returns:** True when the error reads as a timeout or a deadline.
|
|
1595
|
+
|
|
1596
|
+
#### `searchTimeoutError(tool, searched, pattern, seconds, hint)`
|
|
1597
|
+
|
|
1598
|
+
The answer a search tool gives when the backend's deadline fires: what was
|
|
1599
|
+
searched, for what, how long the tool allowed, and the two ways to get an
|
|
1600
|
+
answer — a narrower `path`, or a listing. Never the vendor's `timeoutMs`
|
|
1601
|
+
sentence (see {@link isCommandTimeout}).
|
|
1602
|
+
|
|
1603
|
+
```typescript
|
|
1604
|
+
function searchTimeoutError(
|
|
1605
|
+
tool: string,
|
|
1606
|
+
searched: string,
|
|
1607
|
+
pattern: string,
|
|
1608
|
+
seconds: number,
|
|
1609
|
+
hint: string,
|
|
1610
|
+
): string
|
|
1611
|
+
```
|
|
1612
|
+
|
|
1613
|
+
- `tool` — The tool that timed out (`find_files`, `search_files`).
|
|
1614
|
+
- `searched` — The directory the search actually covered.
|
|
1615
|
+
- `pattern` — The pattern it was searching for.
|
|
1616
|
+
- `seconds` — The tool's own limit, in seconds.
|
|
1617
|
+
- `hint` — What to do differently — the narrower target or the listing tool.
|
|
1618
|
+
|
|
1619
|
+
**Returns:** The message's text.
|
|
1620
|
+
|
|
1202
1621
|
#### `pathArgError(path, tool)`
|
|
1203
1622
|
|
|
1204
1623
|
Validate a file tool's `path` argument is a non-empty string. A weak model
|
|
@@ -1388,10 +1807,12 @@ function failureBlocks(s: string): string[]
|
|
|
1388
1807
|
The shell command whose stdout is the interesting part of a log that is still being written:
|
|
1389
1808
|
the failure lines so far (capped at {@link RUNNING_FAILURE_LINES}), then the end of the log.
|
|
1390
1809
|
|
|
1391
|
-
A Playwright `list` run prints one line per test as it finishes
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1810
|
+
A Playwright `list` run prints one line per test as it finishes, so the LAST bytes of a running
|
|
1811
|
+
log are its least informative part — in X1 flash-m61 the last 3 000 bytes of a 139-test walk
|
|
1812
|
+
were the skip list, and all 61 failures were above them, which cost the run the grep it had to
|
|
1813
|
+
make anyway. A background run also carries each failure's own error as it fails
|
|
1814
|
+
({@link PLAYWRIGHT_FAILURES_INLINE_ENV}), and {@link RUNNING_FAILURE_LINE_RE} already matches
|
|
1815
|
+
those blocks' headers and their `Error:` lines, so this slice shows the causes too.
|
|
1395
1816
|
|
|
1396
1817
|
```typescript
|
|
1397
1818
|
function runningOutputSliceCommand(log: string, own?: string): string
|
|
@@ -1427,9 +1848,11 @@ function runningCauseCommand(log: string, own?: string): string
|
|
|
1427
1848
|
The cause repeating among a running suite's failures, or null.
|
|
1428
1849
|
|
|
1429
1850
|
Playwright's `list` reporter prints one line per test as it finishes — its mark, its title
|
|
1430
|
-
path and how long it ran — and
|
|
1431
|
-
|
|
1432
|
-
|
|
1851
|
+
path and how long it ran — and, on a run the platform started in the background, each failure's
|
|
1852
|
+
own error with it ({@link PLAYWRIGHT_FAILURES_INLINE_ENV}); a `list` run with no error bodies in
|
|
1853
|
+
its log (an older Playwright, a log the bodies have been cut from) carries only the duration as
|
|
1854
|
+
a signature. Where the log carries the failure bodies (either inline, or in a finished run's
|
|
1855
|
+
summary) the signature is the
|
|
1433
1856
|
error itself — {@link failureMessage}: the error class plus the locator or the values that
|
|
1434
1857
|
differ — which is stronger (it needs no assumption about how long a failure should take), so
|
|
1435
1858
|
the message is looked for first and the duration is then left with the tests whose bodies the
|
|
@@ -1442,8 +1865,8 @@ each was the test timeout and one missing `navigate('/')` was the cause of all o
|
|
|
1442
1865
|
|
|
1443
1866
|
The quorum is of DISTINCT tests — {@link REPEATING_FAILURE_MIN} of them — because a log that
|
|
1444
1867
|
prints each failure twice (a second reporter, a retry) had X1 flash-m83 told that four of its
|
|
1445
|
-
|
|
1446
|
-
|
|
1868
|
+
|
|
1869
|
+
_(description truncated — read the full text in `node_modules/@molecule/api-ai-tools/dist/utilities.d.ts`.)_
|
|
1447
1870
|
|
|
1448
1871
|
```typescript
|
|
1449
1872
|
function repeatingFailure(log: string): RepeatingFailure | null
|
|
@@ -1478,6 +1901,43 @@ function universalCause(log: string, found: RepeatingFailure): UniversalCause |
|
|
|
1478
1901
|
|
|
1479
1902
|
**Returns:** The cause's count and files, or null when the run is not failing whole.
|
|
1480
1903
|
|
|
1904
|
+
#### `earlyReturnCause(found, universal)`
|
|
1905
|
+
|
|
1906
|
+
The ONE finding that ends a wait early, and the only one a note may hand a stop command over
|
|
1907
|
+
for: a PROVEN cause that is failing the whole run.
|
|
1908
|
+
|
|
1909
|
+
Both halves are load-bearing, each of them learned from a run that lost minutes to the other.
|
|
1910
|
+
|
|
1911
|
+
- **Failing the WHOLE run** is {@link universalCause}: every failure the log names, across
|
|
1912
|
+
{@link UNIVERSAL_CAUSE_FILES} spec files or more, with nothing passing since the first — the
|
|
1913
|
+
dont-stop-runs rule. X1 flash-m83 (2026-10-16) stopped eight local runs at their first
|
|
1914
|
+
repeating cause in 14.5 minutes and never finished one, and a run that still has other causes
|
|
1915
|
+
left to name is the only thing that will name them.
|
|
1916
|
+
- **A PROVEN cause** is an error repeated VERBATIM. A shared DURATION is a LEAD, never a cause:
|
|
1917
|
+
a suite whose config sets a timeout spends that timeout on every failure that waits for
|
|
1918
|
+
anything, so three unrelated tests timing out together look exactly like one broken run
|
|
1919
|
+
(`~7 s` is a property of the CONFIG, not of the cause — {@link normalizedSignature}). X1
|
|
1920
|
+
xr-molecule-flash-m99 (2026-10-16) was told `status: 'repeating_failure'` twice on that
|
|
1921
|
+
shape — three `url-navigation.spec.ts` tests at ~16.2 s, one `timeout: 15_000` plus teardown
|
|
1922
|
+
— read the early return as "stop waiting, edit now", and voided a run that was still walking;
|
|
1923
|
+
the second cost a 224 s wait that ended with no result at all.
|
|
1924
|
+
|
|
1925
|
+
Neither half alone is enough, and {@link repeatingFailure} finding a signature is not either:
|
|
1926
|
+
|
|
1927
|
+
_(description truncated — read the full text in `node_modules/@molecule/api-ai-tools/dist/utilities.d.ts`.)_
|
|
1928
|
+
|
|
1929
|
+
```typescript
|
|
1930
|
+
function earlyReturnCause(
|
|
1931
|
+
found: RepeatingFailure,
|
|
1932
|
+
universal: UniversalCause | null | undefined,
|
|
1933
|
+
): UniversalCause | null
|
|
1934
|
+
```
|
|
1935
|
+
|
|
1936
|
+
- `found` — What {@link repeatingFailure} found mid-run.
|
|
1937
|
+
- `universal` — What {@link universalCause} found for it, if anything.
|
|
1938
|
+
|
|
1939
|
+
**Returns:** The cause to stop the run over, or null — in which case nothing may be stopped, the wait keeps going, and the finding rides the run's result as a note.
|
|
1940
|
+
|
|
1481
1941
|
#### `failuresByCause(log)`
|
|
1482
1942
|
|
|
1483
1943
|
Every failure a run names, grouped by the cause behind it — one group per cause, with the
|
|
@@ -1488,11 +1948,11 @@ after another with no statement of which of them share a cause, so a run of 19 f
|
|
|
1488
1948
|
bodies to read; X1 flash-m83 (2026-10-16) spent three deploys (6.7 minutes) each fixing a
|
|
1489
1949
|
single cause, two of them found by runs stopped before they walked the rest.
|
|
1490
1950
|
|
|
1491
|
-
The signature is the cause itself where the log carries the failures' bodies (a finished run,
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
why a duration group is labelled as a duration and not as a
|
|
1495
|
-
printed twice, or failed twice, is one test.
|
|
1951
|
+
The signature is the cause itself where the log carries the failures' bodies (a finished run, a
|
|
1952
|
+
background `list` run — whose failures print as they fail — or a `line` reporter) —
|
|
1953
|
+
{@link failureMessage}: the error class plus the locator or the values that differ — and how long
|
|
1954
|
+
they took where it does not, which is why a duration group is labelled as a duration and not as a
|
|
1955
|
+
proven cause. A test the log printed twice, or failed twice, is one test.
|
|
1496
1956
|
|
|
1497
1957
|
```typescript
|
|
1498
1958
|
function failuresByCause(log: string): FailureCause[]
|
|
@@ -1553,18 +2013,19 @@ what that function could actually see in the log — including the count, which
|
|
|
1553
2013
|
tests, so the note never names one or two tests as a cause.
|
|
1554
2014
|
|
|
1555
2015
|
The note names the cause early so the executor can START FIXING while the run keeps walking,
|
|
1556
|
-
and it
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
2016
|
+
and it hands a stop command over only when {@link earlyReturnCause} says so — the SAME
|
|
2017
|
+
predicate the wait uses to decide whether to return early, so a finding the wait keeps waiting
|
|
2018
|
+
on is a finding this note never asks to stop for. X1 flash-m76 (2026-10-16) spent 4.0 minutes
|
|
2019
|
+
walking 15 tests whose one cause was certain at the third, 47 s in, and the note answers
|
|
2020
|
+
exactly that; X1 flash-m83 then stopped eight runs at their first repeating cause in 14.5
|
|
2021
|
+
minutes and never finished one, so the note must not say to stop when the run still has other
|
|
2022
|
+
causes to name. X1 xr-molecule-flash-m99 (2026-10-16) is the third shape: a shared duration is
|
|
2023
|
+
not a cause at all, so a lead reaches the executor as a note on a result it is still waiting
|
|
2024
|
+
for — never as a verdict with a stop command in it.
|
|
1561
2025
|
|
|
1562
2026
|
The run is STILL WALKING while this note is written — both callers produce it mid-run — so a
|
|
1563
2027
|
re-run of one of its tests is a second run against the same app and database, and the platform
|
|
1564
2028
|
refuses that ({@link suiteRunInTheWay}, the same rule its overlap hold answers with):
|
|
1565
|
-
xr-molecule-flash-m89 (2026-10-16) read _"Start on them now, while the run keeps walking.
|
|
1566
|
-
`null-fields.spec.ts:53` alone reproduces the first one."_ and sent exactly that re-run 23 s
|
|
1567
|
-
later, where the hold answered *"A playwright suite is already running against this app and
|
|
1568
2029
|
|
|
1569
2030
|
_(description truncated — read the full text in `node_modules/@molecule/api-ai-tools/dist/utilities.d.ts`.)_
|
|
1570
2031
|
|
|
@@ -1582,7 +2043,7 @@ function repeatingFailureNote(
|
|
|
1582
2043
|
```
|
|
1583
2044
|
|
|
1584
2045
|
- `found` — What {@link repeatingFailure} found.
|
|
1585
|
-
- `o` — The run's facts: its `taskId`, its `log` path, `stop` — the shell command that stops it, printed only for
|
|
2046
|
+
- `o` — The run's facts: its `taskId`, its `log` path, `stop` — the shell command that stops it, printed only for an {@link earlyReturnCause} — `universal`, what {@link universalCause} found (absent means the run is not failing whole), and `run`, how the run was started ({@link rerunOfRunningSuite}) when the caller knows it.
|
|
1586
2047
|
|
|
1587
2048
|
**Returns:** The note.
|
|
1588
2049
|
|
|
@@ -1664,6 +2125,15 @@ function fusedLine(content: string, newString: string): { line: number; text: st
|
|
|
1664
2125
|
|
|
1665
2126
|
### Constants
|
|
1666
2127
|
|
|
2128
|
+
#### `CUT_AT_BUDGET`
|
|
2129
|
+
|
|
2130
|
+
The state a result carries when a budget stopped its command: a run that did not finish is not a
|
|
2131
|
+
run that passed, and no reader may treat it as one.
|
|
2132
|
+
|
|
2133
|
+
```typescript
|
|
2134
|
+
const CUT_AT_BUDGET: 'cut_at_budget'
|
|
2135
|
+
```
|
|
2136
|
+
|
|
1667
2137
|
#### `TOOL_SCHEMAS`
|
|
1668
2138
|
|
|
1669
2139
|
Canonical tool schemas shared by the agent runtime and documentation.
|
|
@@ -1672,6 +2142,108 @@ Canonical tool schemas shared by the agent runtime and documentation.
|
|
|
1672
2142
|
const TOOL_SCHEMAS: Record<string, ToolSchema>
|
|
1673
2143
|
```
|
|
1674
2144
|
|
|
2145
|
+
#### `SPLIT_WORK_ADVICE`
|
|
2146
|
+
|
|
2147
|
+
The closing advice on an over-budget message for a command that is not a test
|
|
2148
|
+
suite: the work it was chaining is what to split.
|
|
2149
|
+
|
|
2150
|
+
```typescript
|
|
2151
|
+
const SPLIT_WORK_ADVICE: string
|
|
2152
|
+
```
|
|
2153
|
+
|
|
2154
|
+
#### `SUITE_OVER_BUDGET_ADVICE`
|
|
2155
|
+
|
|
2156
|
+
The closing advice on an over-budget message for a command that IS a suite run
|
|
2157
|
+
(Playwright, Vitest, a `hurl` run — {@link isSuiteRunCommand}): its files are not
|
|
2158
|
+
a bigger unit to break up.
|
|
2159
|
+
|
|
2160
|
+
X1 flash-m36 asked for this half and the message kept saying "run a smaller unit"
|
|
2161
|
+
for two months. It is the wrong direction for a suite, and measurably: a run
|
|
2162
|
+
stopped here reports NO tally, so nothing in the result says which test or request
|
|
2163
|
+
hung — the run has to be sent again in the background (off this budget, its output
|
|
2164
|
+
written to a log that can be read mid-run) or, when a consumer knows the runner's
|
|
2165
|
+
per-request limit, with each request bounded. xr-molecule-flash-m93 (+06:22,
|
|
2166
|
+
2026-10-16) lost 4.8 minutes to a Hurl run whose ONE unanswered request had hurl's
|
|
2167
|
+
300 s default per request holding the file, ten seconds past this tool's budget.
|
|
2168
|
+
|
|
2169
|
+
```typescript
|
|
2170
|
+
const SUITE_OVER_BUDGET_ADVICE: string
|
|
2171
|
+
```
|
|
2172
|
+
|
|
2173
|
+
#### `MAX_TRACE_ARTIFACTS`
|
|
2174
|
+
|
|
2175
|
+
How many traces one read reports, newest first.
|
|
2176
|
+
|
|
2177
|
+
```typescript
|
|
2178
|
+
const MAX_TRACE_ARTIFACTS: 12
|
|
2179
|
+
```
|
|
2180
|
+
|
|
2181
|
+
#### `MAX_TRACED_REQUESTS`
|
|
2182
|
+
|
|
2183
|
+
How many requests one test's trace contributes.
|
|
2184
|
+
|
|
2185
|
+
```typescript
|
|
2186
|
+
const MAX_TRACED_REQUESTS: 6
|
|
2187
|
+
```
|
|
2188
|
+
|
|
2189
|
+
#### `MAX_TRACED_CONSOLE`
|
|
2190
|
+
|
|
2191
|
+
How many console errors one test's trace contributes.
|
|
2192
|
+
|
|
2193
|
+
```typescript
|
|
2194
|
+
const MAX_TRACED_CONSOLE: 4
|
|
2195
|
+
```
|
|
2196
|
+
|
|
2197
|
+
#### `MAX_TRACE_NOTE_TESTS`
|
|
2198
|
+
|
|
2199
|
+
How many tests the note in a run's result names.
|
|
2200
|
+
|
|
2201
|
+
```typescript
|
|
2202
|
+
const MAX_TRACE_NOTE_TESTS: 5
|
|
2203
|
+
```
|
|
2204
|
+
|
|
2205
|
+
#### `MAX_TRACE_NOTE_REQUESTS`
|
|
2206
|
+
|
|
2207
|
+
How many requests the note names per test.
|
|
2208
|
+
|
|
2209
|
+
```typescript
|
|
2210
|
+
const MAX_TRACE_NOTE_REQUESTS: 4
|
|
2211
|
+
```
|
|
2212
|
+
|
|
2213
|
+
#### `TRACE_NOTE_URL_CHARS`
|
|
2214
|
+
|
|
2215
|
+
How long a URL the note prints, before the middle is cut.
|
|
2216
|
+
|
|
2217
|
+
```typescript
|
|
2218
|
+
const TRACE_NOTE_URL_CHARS: 96
|
|
2219
|
+
```
|
|
2220
|
+
|
|
2221
|
+
#### `DIAGNOSIS_TRACE_FLAG`
|
|
2222
|
+
|
|
2223
|
+
The `--trace` flag this platform adds, and the only value it adds it with.
|
|
2224
|
+
|
|
2225
|
+
```typescript
|
|
2226
|
+
const DIAGNOSIS_TRACE_FLAG: '--trace=retain-on-failure'
|
|
2227
|
+
```
|
|
2228
|
+
|
|
2229
|
+
#### `PLAYWRIGHT_FAILURES_INLINE_ENV`
|
|
2230
|
+
|
|
2231
|
+
Playwright's switch for printing each failure's own error as that test fails, instead of only in
|
|
2232
|
+
the summary at the end: `list` reads it in its constructor (`ListReporterOptions.printFailuresInline`,
|
|
2233
|
+
`playwright/lib/reporters/list.js` — 1.63, the version the toolchain pins), and when it prints
|
|
2234
|
+
inline the end-of-run repeat is suppressed with it (`epilogue(!printFailuresInline)`), so one
|
|
2235
|
+
reporter still prints each failure exactly once.
|
|
2236
|
+
|
|
2237
|
+
A background suite is put on `list` (see {@link streamingBackgroundCommand}) and this with it, so
|
|
2238
|
+
the log names its causes WHILE it walks. Without it the log's only failure lines are `✘` rows:
|
|
2239
|
+
X1 xr-molecule-flash-m99 (+19:27 → +21:00, 2026-10-16) was told to `grep -n -A 20 '<test name>'`
|
|
2240
|
+
the log, got nothing back through four greps, inferred causes from the spec prose instead, and
|
|
2241
|
+
edited `app/src` while the run was still walking — which voids the run.
|
|
2242
|
+
|
|
2243
|
+
```typescript
|
|
2244
|
+
const PLAYWRIGHT_FAILURES_INLINE_ENV: 'PLAYWRIGHT_LIST_PRINT_FAILURES_INLINE'
|
|
2245
|
+
```
|
|
2246
|
+
|
|
1675
2247
|
#### `MAX_READ_SIZE`
|
|
1676
2248
|
|
|
1677
2249
|
Max file size for read_file (5MB).
|