@molecule/api-ai-tools 1.9.7 → 1.9.9
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 +121 -2
- package/dist/command-position.d.ts +24 -0
- package/dist/command-position.d.ts.map +1 -1
- package/dist/command-position.js +81 -0
- package/dist/cut-run.d.ts +8 -0
- package/dist/cut-run.d.ts.map +1 -1
- package/dist/cut-run.js +20 -2
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/line-diff.d.ts +34 -0
- package/dist/line-diff.d.ts.map +1 -0
- package/dist/line-diff.js +150 -0
- package/dist/output-filter.d.ts +48 -0
- package/dist/output-filter.d.ts.map +1 -0
- package/dist/output-filter.js +64 -0
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +42 -12
- package/dist/trace-artifacts.d.ts +11 -1
- package/dist/trace-artifacts.d.ts.map +1 -1
- package/dist/trace-artifacts.js +121 -4
- package/dist/utilities.d.ts +16 -0
- package/dist/utilities.d.ts.map +1 -1
- package/dist/utilities.js +16 -1
- package/package.json +1 -1
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-
|
|
6
|
+
Generated: 2026-10-18T04:56:57.682Z
|
|
7
7
|
-->
|
|
8
8
|
|
|
9
9
|
# @molecule/api-ai-tools
|
|
@@ -187,6 +187,14 @@ interface CutRun {
|
|
|
187
187
|
seconds: number
|
|
188
188
|
/** Whether this tool's budget stopped it or the `timeout` the command carried. */
|
|
189
189
|
stoppedBy: CutBy
|
|
190
|
+
/**
|
|
191
|
+
* Whether this was a detached background task, read with no budget of this tool's own
|
|
192
|
+
* ({@link commandCutRun}'s `budgetSeconds` is 0). A background run is bounded by what its own
|
|
193
|
+
* command carries — so a `command-timeout` cut there is that prefix killing the walk, and the
|
|
194
|
+
* executor must be told to drop the prefix, never to start the run in the background it is
|
|
195
|
+
* already in (X1 xr-molecule-flash-m143 +24:45).
|
|
196
|
+
*/
|
|
197
|
+
background: boolean
|
|
190
198
|
/**
|
|
191
199
|
* The exit code the run's own process reported, or null when the code in the result belongs to
|
|
192
200
|
* the pipeline's filter (`… | tail`) rather than to the run — the case that read as success.
|
|
@@ -270,6 +278,25 @@ interface GuardOptions {
|
|
|
270
278
|
}
|
|
271
279
|
```
|
|
272
280
|
|
|
281
|
+
#### `RunnerOutputFilter`
|
|
282
|
+
|
|
283
|
+
A cutting stage that follows the runner, and the command with the whole pipe removed.
|
|
284
|
+
|
|
285
|
+
```typescript
|
|
286
|
+
interface RunnerOutputFilter {
|
|
287
|
+
/** The stage exactly as the runner's command wrote it — `tail -6`. */
|
|
288
|
+
stage: string
|
|
289
|
+
/** The pipe as a sentence names it — `| tail -6`. */
|
|
290
|
+
pipe: string
|
|
291
|
+
/**
|
|
292
|
+
* The command with the pipe and the stage removed — `hurl --variable uid=… --test /tmp/t.hurl
|
|
293
|
+
* 2>&1`. This is a command to send again as written, not a re-typing of it: it is a slice of the
|
|
294
|
+
* original with the filter's own range cut out, so every other word, quote and redirect stays.
|
|
295
|
+
*/
|
|
296
|
+
command: string
|
|
297
|
+
}
|
|
298
|
+
```
|
|
299
|
+
|
|
273
300
|
#### `ToolSchema`
|
|
274
301
|
|
|
275
302
|
JSON-schema-backed definition for a single agent tool.
|
|
@@ -1020,6 +1047,35 @@ function commandTimeoutSeconds(command: string): number | null
|
|
|
1020
1047
|
|
|
1021
1048
|
**Returns:** The seconds the command allows itself, or null.
|
|
1022
1049
|
|
|
1050
|
+
#### `withoutTimeoutWrapper(command, which)`
|
|
1051
|
+
|
|
1052
|
+
The command with the `timeout <N>` wrapper lifted off the statements `which` selects, and the
|
|
1053
|
+
largest bound that was lifted.
|
|
1054
|
+
|
|
1055
|
+
The platform moves a run to the background precisely because its own bound cannot fit this
|
|
1056
|
+
tool's ceiling, and a background task has no ceiling — its wait is bounded by `wait_for_task`
|
|
1057
|
+
and the turn's deadline. The `timeout N` an executor wrote to fit the ceiling therefore keeps
|
|
1058
|
+
killing the run at N in the background, where nothing needed it: X1 xr-molecule-flash-m143
|
|
1059
|
+
(+24:45) wrote `timeout 280` for a 31-test walk, the tool backgrounded it, and the prefix stopped
|
|
1060
|
+
the walk with no tally — then the cut sentence told the executor to "run it in the background",
|
|
1061
|
+
which is where it already was.
|
|
1062
|
+
|
|
1063
|
+
The wrapper is found by the same parse every other command reader uses ({@link commandStart}):
|
|
1064
|
+
`timeout` in an argument bounds nothing, and the wrapper's own flags (`-k`/`--kill-after`,
|
|
1065
|
+
`-s`/`--signal`) are stepped over, so `timeout -k 5 280 …` is `280`.
|
|
1066
|
+
|
|
1067
|
+
```typescript
|
|
1068
|
+
function withoutTimeoutWrapper(
|
|
1069
|
+
command: string,
|
|
1070
|
+
which: (segment: CommandSegment) => boolean,
|
|
1071
|
+
): { command: string; seconds: number | null }
|
|
1072
|
+
```
|
|
1073
|
+
|
|
1074
|
+
- `command` — The shell command.
|
|
1075
|
+
- `which` — The statements to lift the wrapper from (a suite run, for the background path).
|
|
1076
|
+
|
|
1077
|
+
**Returns:** The rewritten command, and the largest lifted bound (null when nothing was lifted).
|
|
1078
|
+
|
|
1023
1079
|
#### `runsInCommandPosition(command, pattern)`
|
|
1024
1080
|
|
|
1025
1081
|
Whether any segment of the command runs a program whose command-position text matches
|
|
@@ -1296,6 +1352,28 @@ function guardAITool(tool: T, options?: GuardOptions): T
|
|
|
1296
1352
|
|
|
1297
1353
|
**Returns:** A copy with a guarded `execute`.
|
|
1298
1354
|
|
|
1355
|
+
#### `diffLineCount(oldLines, newLines)`
|
|
1356
|
+
|
|
1357
|
+
Count truly added/removed lines between two line arrays using LCS.
|
|
1358
|
+
|
|
1359
|
+
Bounded by construction, so a whole-file edit cannot allocate a quadratic
|
|
1360
|
+
table here: the longest common prefix and suffix are trimmed first (exact —
|
|
1361
|
+
those lines belong to every optimal diff, and on the usual small edit they
|
|
1362
|
+
shrink the table to the edited span), and what is left is capped (see
|
|
1363
|
+
{@link lcsLength}).
|
|
1364
|
+
|
|
1365
|
+
```typescript
|
|
1366
|
+
function diffLineCount(
|
|
1367
|
+
oldLines: readonly string[],
|
|
1368
|
+
newLines: readonly string[],
|
|
1369
|
+
): { added: number; removed: number }
|
|
1370
|
+
```
|
|
1371
|
+
|
|
1372
|
+
- `oldLines` — The original lines array.
|
|
1373
|
+
- `newLines` — The modified lines array.
|
|
1374
|
+
|
|
1375
|
+
**Returns:** An object with the number of added and removed lines.
|
|
1376
|
+
|
|
1299
1377
|
#### `commandRunsNoTests(command)`
|
|
1300
1378
|
|
|
1301
1379
|
Whether a shell command invokes a test runner and EVERY such invocation only prints — so the
|
|
@@ -1323,6 +1401,27 @@ function commandRunsNoTests(command: string): boolean
|
|
|
1323
1401
|
|
|
1324
1402
|
**Returns:** True when the command invokes a runner only to print.
|
|
1325
1403
|
|
|
1404
|
+
#### `outputFilterAfterRunner(command, runs)`
|
|
1405
|
+
|
|
1406
|
+
The output filter that follows the runner in `command`, or null when there is none.
|
|
1407
|
+
|
|
1408
|
+
Only a stage of the runner's OWN pipeline counts (its separator is `|`), and only when it is the
|
|
1409
|
+
LAST stage of that pipeline: a `| grep … | wc -l` chain would leave a dangling pipe if the
|
|
1410
|
+
middle stage were cut out, so a chain is not answered here at all. A filter before the runner
|
|
1411
|
+
(`cat run.log | tail -20 && hurl …`) reads something else's output and is not this.
|
|
1412
|
+
|
|
1413
|
+
```typescript
|
|
1414
|
+
function outputFilterAfterRunner(
|
|
1415
|
+
command: string,
|
|
1416
|
+
runs: (segment: CommandSegment) => boolean,
|
|
1417
|
+
): RunnerOutputFilter | null
|
|
1418
|
+
```
|
|
1419
|
+
|
|
1420
|
+
- `command` — The command as the executor sent it.
|
|
1421
|
+
- `runs` — Whether a segment is the runner this run is about (the caller knows its own kind: {@link CommandSegment.program} is the program, once wrapper words like `timeout 280` and `npx` are taken away).
|
|
1422
|
+
|
|
1423
|
+
**Returns:** The filter and the unfiltered command, or null.
|
|
1424
|
+
|
|
1326
1425
|
#### `pipelineExitCodeNote(command, exitCode, output)`
|
|
1327
1426
|
|
|
1328
1427
|
The sentence an `exec_command` result carries beside `exitCode` when the code belongs to a
|
|
@@ -1780,7 +1879,7 @@ function stalledDocumentSubresource(request: TraceRequest): boolean
|
|
|
1780
1879
|
|
|
1781
1880
|
**Returns:** True when the request is a document subresource that never finished.
|
|
1782
1881
|
|
|
1783
|
-
#### `failureTraceNote(items, target, earlierFailures)`
|
|
1882
|
+
#### `failureTraceNote(items, target, earlierFailures, apiPrefixes)`
|
|
1784
1883
|
|
|
1785
1884
|
The note a failed suite run's result carries: what each failing test's page actually sent, and
|
|
1786
1885
|
what came back.
|
|
@@ -1811,12 +1910,14 @@ function failureTraceNote(
|
|
|
1811
1910
|
items: readonly TraceArtifact[],
|
|
1812
1911
|
target: string,
|
|
1813
1912
|
earlierFailures?: readonly string[],
|
|
1913
|
+
apiPrefixes?: readonly string[],
|
|
1814
1914
|
): string | null
|
|
1815
1915
|
```
|
|
1816
1916
|
|
|
1817
1917
|
- `items` — The traces {@link parseTraceArtifacts} read.
|
|
1818
1918
|
- `target` — Where the run went — the deployed host, as the note names it.
|
|
1819
1919
|
- `earlierFailures` — The failing `file:line`s earlier walks of this suite left, so a repeat is ranked above a one-off. Empty when the caller knows of no earlier walk.
|
|
1920
|
+
- `apiPrefixes` — The API path prefixes the app's client config names (`['/api']`), read from the app by the caller. Empty when the caller could not read one; the traces' own requests may still supply it. Never a hard-coded literal.
|
|
1820
1921
|
|
|
1821
1922
|
**Returns:** The note, or null when no trace held a recorded response, a real failure or a console error.
|
|
1822
1923
|
|
|
@@ -2075,6 +2176,24 @@ function isSuiteRunCommand(command: string): boolean
|
|
|
2075
2176
|
|
|
2076
2177
|
**Returns:** True when the command starts one of those runners.
|
|
2077
2178
|
|
|
2179
|
+
#### `withoutSuiteTimeout(command)`
|
|
2180
|
+
|
|
2181
|
+
The command with the `timeout <N>` wrapper lifted off the statement that runs the suite, and the
|
|
2182
|
+
bound that was lifted.
|
|
2183
|
+
|
|
2184
|
+
Used where the platform moves a run to the background: the executor wrote the bound to fit this
|
|
2185
|
+
tool's foreground ceiling, and in the background that ceiling no longer applies — the task is
|
|
2186
|
+
bounded by `wait_for_task` and the turn's deadline — so the prefix only kills the walk
|
|
2187
|
+
({@link withoutTimeoutWrapper}; X1 xr-molecule-flash-m143 +24:45).
|
|
2188
|
+
|
|
2189
|
+
```typescript
|
|
2190
|
+
function withoutSuiteTimeout(command: string): { command: string; seconds: number | null }
|
|
2191
|
+
```
|
|
2192
|
+
|
|
2193
|
+
- `command` — The shell command.
|
|
2194
|
+
|
|
2195
|
+
**Returns:** The rewritten command, and the largest lifted bound (null when nothing was lifted).
|
|
2196
|
+
|
|
2078
2197
|
#### `isPlaywrightRunCommand(command)`
|
|
2079
2198
|
|
|
2080
2199
|
Whether a command starts a Playwright run — a `playwright test` in command position, in a form
|
|
@@ -172,6 +172,30 @@ export declare function statementArgumentEnd(command: string, start: number | nu
|
|
|
172
172
|
* @returns The seconds the command allows itself, or null.
|
|
173
173
|
*/
|
|
174
174
|
export declare function commandTimeoutSeconds(command: string): number | null;
|
|
175
|
+
/**
|
|
176
|
+
* The command with the `timeout <N>` wrapper lifted off the statements `which` selects, and the
|
|
177
|
+
* largest bound that was lifted.
|
|
178
|
+
*
|
|
179
|
+
* The platform moves a run to the background precisely because its own bound cannot fit this
|
|
180
|
+
* tool's ceiling, and a background task has no ceiling — its wait is bounded by `wait_for_task`
|
|
181
|
+
* and the turn's deadline. The `timeout N` an executor wrote to fit the ceiling therefore keeps
|
|
182
|
+
* killing the run at N in the background, where nothing needed it: X1 xr-molecule-flash-m143
|
|
183
|
+
* (+24:45) wrote `timeout 280` for a 31-test walk, the tool backgrounded it, and the prefix stopped
|
|
184
|
+
* the walk with no tally — then the cut sentence told the executor to "run it in the background",
|
|
185
|
+
* which is where it already was.
|
|
186
|
+
*
|
|
187
|
+
* The wrapper is found by the same parse every other command reader uses ({@link commandStart}):
|
|
188
|
+
* `timeout` in an argument bounds nothing, and the wrapper's own flags (`-k`/`--kill-after`,
|
|
189
|
+
* `-s`/`--signal`) are stepped over, so `timeout -k 5 280 …` is `280`.
|
|
190
|
+
*
|
|
191
|
+
* @param command - The shell command.
|
|
192
|
+
* @param which - The statements to lift the wrapper from (a suite run, for the background path).
|
|
193
|
+
* @returns The rewritten command, and the largest lifted bound (null when nothing was lifted).
|
|
194
|
+
*/
|
|
195
|
+
export declare function withoutTimeoutWrapper(command: string, which: (segment: CommandSegment) => boolean): {
|
|
196
|
+
command: string;
|
|
197
|
+
seconds: number | null;
|
|
198
|
+
};
|
|
175
199
|
/**
|
|
176
200
|
* Whether any segment of the command runs a program whose command-position text matches
|
|
177
201
|
* `pattern` — the one question a runner-kind table asks.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"command-position.d.ts","sourceRoot":"","sources":["../src/command-position.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,uFAAuF;AACvF,MAAM,WAAW,cAAc;IAC7B;;;OAGG;IACH,OAAO,EAAE,MAAM,CAAA;IACf,qFAAqF;IACrF,IAAI,EAAE,SAAS,MAAM,EAAE,CAAA;IACvB,4FAA4F;IAC5F,IAAI,EAAE,MAAM,CAAA;IACZ;;;OAGG;IACH,cAAc,EAAE,MAAM,GAAG,IAAI,CAAA;CAC9B;AAqND;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAM3D;AAmjBD;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,cAAc,EAAE,CAIjE;AAmCD;;;;;;;;;GASG;AACH,MAAM,WAAW,qBAAqB;IACpC;;;;OAIG;IACH,OAAO,EAAE,cAAc,GAAG,IAAI,CAAA;IAC9B,0FAA0F;IAC1F,WAAW,EAAE,SAAS,MAAM,EAAE,CAAA;IAC9B;;;;OAIG;IACH,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB;;;;;OAKG;IACH,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAA;IAC/B;;;OAGG;IACH,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;IAClB;;;;OAIG;IACH,aAAa,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B;;;;;OAKG;IACH,SAAS,EAAE,MAAM,CAAA;IACjB;;;OAGG;IACH,UAAU,EAAE,MAAM,CAAA;IAClB;;;;;OAKG;IACH,cAAc,EAAE,MAAM,GAAG,IAAI,CAAA;CAC9B;AAED;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,GAAG,qBAAqB,EAAE,CAE/E;AAmDD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,GAAG,IAAI,EACpB,GAAG,EAAE,MAAM,GAAG,IAAI,GACjB,MAAM,GAAG,IAAI,CAyDf;AAwFD;;;;;;;;;;;;GAYG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAOpE;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAK/E"}
|
|
1
|
+
{"version":3,"file":"command-position.d.ts","sourceRoot":"","sources":["../src/command-position.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,uFAAuF;AACvF,MAAM,WAAW,cAAc;IAC7B;;;OAGG;IACH,OAAO,EAAE,MAAM,CAAA;IACf,qFAAqF;IACrF,IAAI,EAAE,SAAS,MAAM,EAAE,CAAA;IACvB,4FAA4F;IAC5F,IAAI,EAAE,MAAM,CAAA;IACZ;;;OAGG;IACH,cAAc,EAAE,MAAM,GAAG,IAAI,CAAA;CAC9B;AAqND;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAM3D;AAmjBD;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,cAAc,EAAE,CAIjE;AAmCD;;;;;;;;;GASG;AACH,MAAM,WAAW,qBAAqB;IACpC;;;;OAIG;IACH,OAAO,EAAE,cAAc,GAAG,IAAI,CAAA;IAC9B,0FAA0F;IAC1F,WAAW,EAAE,SAAS,MAAM,EAAE,CAAA;IAC9B;;;;OAIG;IACH,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB;;;;;OAKG;IACH,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAA;IAC/B;;;OAGG;IACH,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;IAClB;;;;OAIG;IACH,aAAa,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B;;;;;OAKG;IACH,SAAS,EAAE,MAAM,CAAA;IACjB;;;OAGG;IACH,UAAU,EAAE,MAAM,CAAA;IAClB;;;;;OAKG;IACH,cAAc,EAAE,MAAM,GAAG,IAAI,CAAA;CAC9B;AAED;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,GAAG,qBAAqB,EAAE,CAE/E;AAmDD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,GAAG,IAAI,EACpB,GAAG,EAAE,MAAM,GAAG,IAAI,GACjB,MAAM,GAAG,IAAI,CAyDf;AAwFD;;;;;;;;;;;;GAYG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAOpE;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,OAAO,GAC1C;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAoD7C;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAK/E"}
|
package/dist/command-position.js
CHANGED
|
@@ -1076,6 +1076,87 @@ export function commandTimeoutSeconds(command) {
|
|
|
1076
1076
|
}
|
|
1077
1077
|
return bound;
|
|
1078
1078
|
}
|
|
1079
|
+
/**
|
|
1080
|
+
* The command with the `timeout <N>` wrapper lifted off the statements `which` selects, and the
|
|
1081
|
+
* largest bound that was lifted.
|
|
1082
|
+
*
|
|
1083
|
+
* The platform moves a run to the background precisely because its own bound cannot fit this
|
|
1084
|
+
* tool's ceiling, and a background task has no ceiling — its wait is bounded by `wait_for_task`
|
|
1085
|
+
* and the turn's deadline. The `timeout N` an executor wrote to fit the ceiling therefore keeps
|
|
1086
|
+
* killing the run at N in the background, where nothing needed it: X1 xr-molecule-flash-m143
|
|
1087
|
+
* (+24:45) wrote `timeout 280` for a 31-test walk, the tool backgrounded it, and the prefix stopped
|
|
1088
|
+
* the walk with no tally — then the cut sentence told the executor to "run it in the background",
|
|
1089
|
+
* which is where it already was.
|
|
1090
|
+
*
|
|
1091
|
+
* The wrapper is found by the same parse every other command reader uses ({@link commandStart}):
|
|
1092
|
+
* `timeout` in an argument bounds nothing, and the wrapper's own flags (`-k`/`--kill-after`,
|
|
1093
|
+
* `-s`/`--signal`) are stepped over, so `timeout -k 5 280 …` is `280`.
|
|
1094
|
+
*
|
|
1095
|
+
* @param command - The shell command.
|
|
1096
|
+
* @param which - The statements to lift the wrapper from (a suite run, for the background path).
|
|
1097
|
+
* @returns The rewritten command, and the largest lifted bound (null when nothing was lifted).
|
|
1098
|
+
*/
|
|
1099
|
+
export function withoutTimeoutWrapper(command, which) {
|
|
1100
|
+
if (typeof command !== 'string' || !command.trim())
|
|
1101
|
+
return { command, seconds: null };
|
|
1102
|
+
/** The spans to remove, as offsets in the command — applied back to front. */
|
|
1103
|
+
const removals = [];
|
|
1104
|
+
let seconds = null;
|
|
1105
|
+
for (const located of locatedSegmentsOf(command, 0)) {
|
|
1106
|
+
const segment = located.segment;
|
|
1107
|
+
if (segment === null || segment.timeoutSeconds === null)
|
|
1108
|
+
continue;
|
|
1109
|
+
// A statement inside a nested `sh -c '…'` has no offsets here: its positions are positions in
|
|
1110
|
+
// that string, so nothing may be spliced at them.
|
|
1111
|
+
if (located.start === null || located.programOffset === null)
|
|
1112
|
+
continue;
|
|
1113
|
+
if (!which(segment))
|
|
1114
|
+
continue;
|
|
1115
|
+
// Every word in front of the program: the assignments and the wrappers. The duration belongs
|
|
1116
|
+
// to the wrapper, so the walk has to see the words, not just the parse's total bound.
|
|
1117
|
+
const prefix = command.slice(located.start, located.programOffset);
|
|
1118
|
+
const words = [];
|
|
1119
|
+
const at = new RegExp(WORD.source, 'g');
|
|
1120
|
+
for (let match = at.exec(prefix); match !== null; match = at.exec(prefix)) {
|
|
1121
|
+
words.push({
|
|
1122
|
+
text: match[0],
|
|
1123
|
+
start: located.start + match.index,
|
|
1124
|
+
end: located.start + match.index + match[0].length,
|
|
1125
|
+
});
|
|
1126
|
+
}
|
|
1127
|
+
for (let i = 0; i < words.length; i += 1) {
|
|
1128
|
+
const word = words[i];
|
|
1129
|
+
if (word === undefined || programName(word.text) !== 'timeout')
|
|
1130
|
+
continue;
|
|
1131
|
+
// The wrapper's own flags, then the duration — the same walk {@link commandStart} makes.
|
|
1132
|
+
const withValue = WRAPPER_VALUE_FLAGS.timeout ?? [];
|
|
1133
|
+
let next = i + 1;
|
|
1134
|
+
while (next < words.length && (words[next]?.text ?? '').startsWith('-')) {
|
|
1135
|
+
const flag = unquoted(words[next]?.text ?? '');
|
|
1136
|
+
next += !flag.includes('=') && withValue.includes(flag) ? 2 : 1;
|
|
1137
|
+
}
|
|
1138
|
+
const duration = words[next];
|
|
1139
|
+
const bound = duration === undefined ? null : durationSeconds(unquoted(duration.text));
|
|
1140
|
+
if (duration === undefined || bound === null)
|
|
1141
|
+
continue;
|
|
1142
|
+
seconds = seconds === null ? bound : Math.max(seconds, bound);
|
|
1143
|
+
// The whitespace between the duration and the program goes too, so the command does not
|
|
1144
|
+
// carry a double space where the wrapper stood.
|
|
1145
|
+
let end = duration.end;
|
|
1146
|
+
while (end < command.length && /\s/.test(command[end] ?? ''))
|
|
1147
|
+
end += 1;
|
|
1148
|
+
removals.push({ start: word.start, end });
|
|
1149
|
+
i = next;
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
if (removals.length === 0)
|
|
1153
|
+
return { command, seconds: null };
|
|
1154
|
+
let out = command;
|
|
1155
|
+
for (const removal of removals.sort((a, b) => b.start - a.start)) {
|
|
1156
|
+
out = out.slice(0, removal.start) + out.slice(removal.end);
|
|
1157
|
+
}
|
|
1158
|
+
return { command: out, seconds };
|
|
1159
|
+
}
|
|
1079
1160
|
/**
|
|
1080
1161
|
* Whether any segment of the command runs a program whose command-position text matches
|
|
1081
1162
|
* `pattern` — the one question a runner-kind table asks.
|
package/dist/cut-run.d.ts
CHANGED
|
@@ -134,6 +134,14 @@ export interface CutRun {
|
|
|
134
134
|
seconds: number;
|
|
135
135
|
/** Whether this tool's budget stopped it or the `timeout` the command carried. */
|
|
136
136
|
stoppedBy: CutBy;
|
|
137
|
+
/**
|
|
138
|
+
* Whether this was a detached background task, read with no budget of this tool's own
|
|
139
|
+
* ({@link commandCutRun}'s `budgetSeconds` is 0). A background run is bounded by what its own
|
|
140
|
+
* command carries — so a `command-timeout` cut there is that prefix killing the walk, and the
|
|
141
|
+
* executor must be told to drop the prefix, never to start the run in the background it is
|
|
142
|
+
* already in (X1 xr-molecule-flash-m143 +24:45).
|
|
143
|
+
*/
|
|
144
|
+
background: boolean;
|
|
137
145
|
/**
|
|
138
146
|
* The exit code the run's own process reported, or null when the code in the result belongs to
|
|
139
147
|
* the pipeline's filter (`… | tail`) rather than to the run — the case that read as success.
|
package/dist/cut-run.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cut-run.d.ts","sourceRoot":"","sources":["../src/cut-run.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAKH;;;GAGG;AACH,eAAO,MAAM,aAAa,kBAAkB,CAAA;AAE5C,kFAAkF;AAClF,MAAM,MAAM,eAAe,GAAG,KAAK,GAAG,OAAO,aAAa,CAAA;AAE1D;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,OAAO,GAAG,eAAe,CAGhE;AAiBD;;;;;;;;GAQG;AACH,MAAM,WAAW,YAAY;IAC3B;;;OAGG;IACH,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB;;;;;OAKG;IACH,SAAS,EAAE,MAAM,CAAA;IACjB,wEAAwE;IACxE,QAAQ,EAAE,OAAO,CAAA;CAClB;AAkDD;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CA4B9D;AAED;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEhD;AAKD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,GAAG,IAAI,EACvB,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,OAAO,GACb,MAAM,GAAG,IAAI,CAIf;AAED,0BAA0B;AAC1B,MAAM,MAAM,KAAK;AACf,sEAAsE;AACpE,aAAa;AACf,oDAAoD;GAClD,iBAAiB;AACnB;;;;GAIG;GACD,cAAc,CAAA;AAElB,wDAAwD;AACxD,MAAM,WAAW,MAAM;IACrB,iDAAiD;IACjD,OAAO,EAAE,MAAM,CAAA;IACf,kFAAkF;IAClF,SAAS,EAAE,KAAK,CAAA;IAChB;;;OAGG;IACH,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB;;;OAGG;IACH,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;IAClB;;;OAGG;IACH,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,qCAAqC;IACrC,IAAI,EAAE,OAAO,GAAG,OAAO,CAAA;IACvB,yEAAyE;IACzE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CACrB;AAsDD;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,OAAO,EACf,SAAS,EAAE,MAAM,EACjB,aAAa,SAAI,EACjB,SAAS,CAAC,EAAE,MAAM,GACjB,MAAM,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"cut-run.d.ts","sourceRoot":"","sources":["../src/cut-run.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAKH;;;GAGG;AACH,eAAO,MAAM,aAAa,kBAAkB,CAAA;AAE5C,kFAAkF;AAClF,MAAM,MAAM,eAAe,GAAG,KAAK,GAAG,OAAO,aAAa,CAAA;AAE1D;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,OAAO,GAAG,eAAe,CAGhE;AAiBD;;;;;;;;GAQG;AACH,MAAM,WAAW,YAAY;IAC3B;;;OAGG;IACH,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB;;;;;OAKG;IACH,SAAS,EAAE,MAAM,CAAA;IACjB,wEAAwE;IACxE,QAAQ,EAAE,OAAO,CAAA;CAClB;AAkDD;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CA4B9D;AAED;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEhD;AAKD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,GAAG,IAAI,EACvB,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,OAAO,GACb,MAAM,GAAG,IAAI,CAIf;AAED,0BAA0B;AAC1B,MAAM,MAAM,KAAK;AACf,sEAAsE;AACpE,aAAa;AACf,oDAAoD;GAClD,iBAAiB;AACnB;;;;GAIG;GACD,cAAc,CAAA;AAElB,wDAAwD;AACxD,MAAM,WAAW,MAAM;IACrB,iDAAiD;IACjD,OAAO,EAAE,MAAM,CAAA;IACf,kFAAkF;IAClF,SAAS,EAAE,KAAK,CAAA;IAChB;;;;;;OAMG;IACH,UAAU,EAAE,OAAO,CAAA;IACnB;;;OAGG;IACH,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB;;;OAGG;IACH,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;IAClB;;;OAGG;IACH,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,qCAAqC;IACrC,IAAI,EAAE,OAAO,GAAG,OAAO,CAAA;IACvB,yEAAyE;IACzE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CACrB;AAsDD;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,OAAO,EACf,SAAS,EAAE,MAAM,EACjB,aAAa,SAAI,EACjB,SAAS,CAAC,EAAE,MAAM,GACjB,MAAM,GAAG,IAAI,CAuFf;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAiB1D;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAiClD"}
|
package/dist/cut-run.js
CHANGED
|
@@ -296,6 +296,9 @@ export function commandCutRun(command, result, elapsedMs, budgetSeconds = 0, raw
|
|
|
296
296
|
// tool budget at all (a reader re-reading a background run) the command's own bound is the only
|
|
297
297
|
// one there is.
|
|
298
298
|
const ownBoundStops = budgetSeconds === 0 || (declared !== null && declared < budgetSeconds);
|
|
299
|
+
// No budget of this tool's in force is what a background reader is: the task was detached, and
|
|
300
|
+
// nothing bounded it but what its own command carries.
|
|
301
|
+
const background = budgetSeconds === 0;
|
|
299
302
|
// `timeout`'s own 124 — the code the backend's wrapper returns when the TOOL's budget fires,
|
|
300
303
|
// whatever the pipeline ended with (`backends/sandbox.ts`). Which of the two bounds fired is the
|
|
301
304
|
// clock's to say: a run killed at the tool's budget reports at least the budget, and one killed
|
|
@@ -308,6 +311,7 @@ export function commandCutRun(command, result, elapsedMs, budgetSeconds = 0, raw
|
|
|
308
311
|
return {
|
|
309
312
|
seconds: ownFired ? declared : budgetSeconds,
|
|
310
313
|
stoppedBy: ownFired ? 'command-timeout' : 'tool-budget',
|
|
314
|
+
background,
|
|
311
315
|
exitCode,
|
|
312
316
|
...progress,
|
|
313
317
|
doing,
|
|
@@ -317,6 +321,7 @@ export function commandCutRun(command, result, elapsedMs, budgetSeconds = 0, raw
|
|
|
317
321
|
return {
|
|
318
322
|
seconds: declared,
|
|
319
323
|
stoppedBy: 'command-timeout',
|
|
324
|
+
background,
|
|
320
325
|
// A code the filter produced is not the run's: `… | tail` handed back 0 forever (m93).
|
|
321
326
|
exitCode: withheld || exitCode === 0 ? null : exitCode,
|
|
322
327
|
...progress,
|
|
@@ -329,6 +334,7 @@ export function commandCutRun(command, result, elapsedMs, budgetSeconds = 0, raw
|
|
|
329
334
|
return {
|
|
330
335
|
seconds: budgetSeconds,
|
|
331
336
|
stoppedBy: 'tool-budget',
|
|
337
|
+
background,
|
|
332
338
|
exitCode: exitCode === 0 ? null : exitCode,
|
|
333
339
|
...progress,
|
|
334
340
|
doing,
|
|
@@ -344,6 +350,7 @@ export function commandCutRun(command, result, elapsedMs, budgetSeconds = 0, raw
|
|
|
344
350
|
return {
|
|
345
351
|
seconds: usedBudget ? budgetSeconds : Math.max(1, Math.round(elapsedMs / 1000)),
|
|
346
352
|
stoppedBy: usedBudget ? 'tool-budget' : 'process-stop',
|
|
353
|
+
background,
|
|
347
354
|
exitCode: withheld || exitCode === 0 ? null : exitCode,
|
|
348
355
|
...progress,
|
|
349
356
|
doing,
|
|
@@ -377,6 +384,7 @@ export function cutRunFacts(result) {
|
|
|
377
384
|
return {
|
|
378
385
|
seconds: c.seconds,
|
|
379
386
|
stoppedBy: c.stoppedBy,
|
|
387
|
+
background: c.background === true,
|
|
380
388
|
exitCode: typeof c.exitCode === 'number' ? c.exitCode : null,
|
|
381
389
|
ran: typeof c.ran === 'number' ? c.ran : null,
|
|
382
390
|
total: typeof c.total === 'number' ? c.total : null,
|
|
@@ -401,9 +409,19 @@ export function cutRunSentence(run) {
|
|
|
401
409
|
const by = run.stoppedBy === 'tool-budget'
|
|
402
410
|
? "this tool's limit"
|
|
403
411
|
: run.stoppedBy === 'command-timeout'
|
|
404
|
-
?
|
|
412
|
+
? run.background
|
|
413
|
+
? `the command's own \`timeout ${run.seconds}\`, which killed it at ${run.seconds} s`
|
|
414
|
+
: `its own \`timeout ${run.seconds}\``
|
|
405
415
|
: 'a process kill outside this tool (no `timeout` of its own and no limit of this tool had fired)';
|
|
406
|
-
|
|
416
|
+
// What the reader must do next. A run that was ALREADY a background task has nothing to gain
|
|
417
|
+
// from being sent to the background again — when its own `timeout` is what stopped it, that
|
|
418
|
+
// prefix is the thing to drop (X1 xr-molecule-flash-m143 +24:45: three cut sentences in one run
|
|
419
|
+
// told a background walk to "run it in the background", which is where it already was).
|
|
420
|
+
const background = run.background
|
|
421
|
+
? run.stoppedBy === 'command-timeout'
|
|
422
|
+
? `drop the \`timeout\` prefix and send it again — in the background a run is bounded by \`wait_for_task\` and the turn's deadline, not by this tool's ceiling, so the bound you wrote is what killed it`
|
|
423
|
+
: 'send it again when you are ready'
|
|
424
|
+
: 'run it in the background (run_in_background + wait_for_task)';
|
|
407
425
|
const missing = run.ran !== null && run.total !== null ? run.total - run.ran : null;
|
|
408
426
|
const lead = missing !== null
|
|
409
427
|
? `Ran ${run.ran} of ${run.total} ${run.noun} in ${run.seconds} s and was stopped by ${by} — the ${missing} it never reached ${missing === 1 ? 'has' : 'have'} not run; ${background}`
|
package/dist/index.d.ts
CHANGED
|
@@ -136,7 +136,9 @@ export * from './command-position.js';
|
|
|
136
136
|
export * from './cut-run.js';
|
|
137
137
|
export * from './failure-kind.js';
|
|
138
138
|
export * from './input-normalizer.js';
|
|
139
|
+
export * from './line-diff.js';
|
|
139
140
|
export * from './no-test-run.js';
|
|
141
|
+
export * from './output-filter.js';
|
|
140
142
|
export * from './pipeline-exit-note.js';
|
|
141
143
|
export * from './schemas.js';
|
|
142
144
|
export * from './sleep-peek.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkIG;AAEH,cAAc,qBAAqB,CAAA;AACnC,cAAc,uBAAuB,CAAA;AACrC,cAAc,oBAAoB,CAAA;AAClC,cAAc,uBAAuB,CAAA;AACrC,cAAc,cAAc,CAAA;AAC5B,cAAc,mBAAmB,CAAA;AACjC,cAAc,uBAAuB,CAAA;AACrC,cAAc,kBAAkB,CAAA;AAChC,cAAc,yBAAyB,CAAA;AACvC,cAAc,cAAc,CAAA;AAC5B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,oBAAoB,CAAA;AAClC,cAAc,oBAAoB,CAAA;AAClC,cAAc,YAAY,CAAA;AAC1B,cAAc,sBAAsB,CAAA;AACpC,cAAc,YAAY,CAAA;AAC1B,cAAc,gBAAgB,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkIG;AAEH,cAAc,qBAAqB,CAAA;AACnC,cAAc,uBAAuB,CAAA;AACrC,cAAc,oBAAoB,CAAA;AAClC,cAAc,uBAAuB,CAAA;AACrC,cAAc,cAAc,CAAA;AAC5B,cAAc,mBAAmB,CAAA;AACjC,cAAc,uBAAuB,CAAA;AACrC,cAAc,gBAAgB,CAAA;AAC9B,cAAc,kBAAkB,CAAA;AAChC,cAAc,oBAAoB,CAAA;AAClC,cAAc,yBAAyB,CAAA;AACvC,cAAc,cAAc,CAAA;AAC5B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,oBAAoB,CAAA;AAClC,cAAc,oBAAoB,CAAA;AAClC,cAAc,YAAY,CAAA;AAC1B,cAAc,sBAAsB,CAAA;AACpC,cAAc,YAAY,CAAA;AAC1B,cAAc,gBAAgB,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -136,7 +136,9 @@ export * from './command-position.js';
|
|
|
136
136
|
export * from './cut-run.js';
|
|
137
137
|
export * from './failure-kind.js';
|
|
138
138
|
export * from './input-normalizer.js';
|
|
139
|
+
export * from './line-diff.js';
|
|
139
140
|
export * from './no-test-run.js';
|
|
141
|
+
export * from './output-filter.js';
|
|
140
142
|
export * from './pipeline-exit-note.js';
|
|
141
143
|
export * from './schemas.js';
|
|
142
144
|
export * from './sleep-peek.js';
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Real added/removed LINE counts for the `diff` a file-writing tool reports.
|
|
3
|
+
*
|
|
4
|
+
* The count is a diff, not an arithmetic delta: replacing one line with another
|
|
5
|
+
* leaves the line count unchanged, so `new.length - old.length` is 0 for the
|
|
6
|
+
* edit that changed the file — a result that reads to the model as "your write
|
|
7
|
+
* did nothing" (X0 xr-molecule-flash-m141 +09:56: `write_file` rewrote
|
|
8
|
+
* `ArticlePreview.tsx`'s favorite label and reported
|
|
9
|
+
* `"linesAdded":0,"linesRemoved":0`). The lines an edit actually changed are
|
|
10
|
+
* found with the same longest-common-subsequence line diff the IDE's tool cards
|
|
11
|
+
* use (`@molecule/app-ide-react`'s `diffLineCount`); it is mirrored here because
|
|
12
|
+
* the API and App halves of the workspace cannot import each other (AGENTS.md
|
|
13
|
+
* Rule 11, enforced by ESLint). Keep the two in step if either changes.
|
|
14
|
+
*
|
|
15
|
+
* @module
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* Count truly added/removed lines between two line arrays using LCS.
|
|
19
|
+
*
|
|
20
|
+
* Bounded by construction, so a whole-file edit cannot allocate a quadratic
|
|
21
|
+
* table here: the longest common prefix and suffix are trimmed first (exact —
|
|
22
|
+
* those lines belong to every optimal diff, and on the usual small edit they
|
|
23
|
+
* shrink the table to the edited span), and what is left is capped (see
|
|
24
|
+
* {@link lcsLength}).
|
|
25
|
+
*
|
|
26
|
+
* @param oldLines - The original lines array.
|
|
27
|
+
* @param newLines - The modified lines array.
|
|
28
|
+
* @returns An object with the number of added and removed lines.
|
|
29
|
+
*/
|
|
30
|
+
export declare function diffLineCount(oldLines: readonly string[], newLines: readonly string[]): {
|
|
31
|
+
added: number;
|
|
32
|
+
removed: number;
|
|
33
|
+
};
|
|
34
|
+
//# sourceMappingURL=line-diff.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"line-diff.d.ts","sourceRoot":"","sources":["../src/line-diff.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAmGH;;;;;;;;;;;;GAYG;AACH,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,SAAS,MAAM,EAAE,EAC3B,QAAQ,EAAE,SAAS,MAAM,EAAE,GAC1B;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAuBpC"}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Real added/removed LINE counts for the `diff` a file-writing tool reports.
|
|
3
|
+
*
|
|
4
|
+
* The count is a diff, not an arithmetic delta: replacing one line with another
|
|
5
|
+
* leaves the line count unchanged, so `new.length - old.length` is 0 for the
|
|
6
|
+
* edit that changed the file — a result that reads to the model as "your write
|
|
7
|
+
* did nothing" (X0 xr-molecule-flash-m141 +09:56: `write_file` rewrote
|
|
8
|
+
* `ArticlePreview.tsx`'s favorite label and reported
|
|
9
|
+
* `"linesAdded":0,"linesRemoved":0`). The lines an edit actually changed are
|
|
10
|
+
* found with the same longest-common-subsequence line diff the IDE's tool cards
|
|
11
|
+
* use (`@molecule/app-ide-react`'s `diffLineCount`); it is mirrored here because
|
|
12
|
+
* the API and App halves of the workspace cannot import each other (AGENTS.md
|
|
13
|
+
* Rule 11, enforced by ESLint). Keep the two in step if either changes.
|
|
14
|
+
*
|
|
15
|
+
* @module
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* Largest LCS table the exact diff may build, in cells (old × new). A whole-file
|
|
19
|
+
* edit can be thousands of lines per side, i.e. tens of millions of cells —
|
|
20
|
+
* gigabytes of heap for a summary that only needs two numbers. Above this cap
|
|
21
|
+
* the answer comes from {@link orderedCommonLines}, a provable, order-respecting
|
|
22
|
+
* common subsequence in O(n log n); below it the result is exact. 1 << 22 cells
|
|
23
|
+
* is a few milliseconds, and the two-row table keeps memory at O(min(old, new)).
|
|
24
|
+
*/
|
|
25
|
+
const MAX_DIFF_CELLS = 1 << 22;
|
|
26
|
+
/**
|
|
27
|
+
* Longest common subsequence length of two line arrays, in bounded time and
|
|
28
|
+
* memory: two rows of the recurrence instead of the whole table, and — once the
|
|
29
|
+
* table would exceed {@link MAX_DIFF_CELLS} — the order-respecting lower bound
|
|
30
|
+
* from {@link orderedCommonLines}.
|
|
31
|
+
*
|
|
32
|
+
* @param a - The first line array.
|
|
33
|
+
* @param b - The second line array.
|
|
34
|
+
* @returns The common subsequence length (a lower bound for the capped inputs).
|
|
35
|
+
*/
|
|
36
|
+
function lcsLength(a, b) {
|
|
37
|
+
if (a.length === 0 || b.length === 0)
|
|
38
|
+
return 0;
|
|
39
|
+
if (a.length * b.length > MAX_DIFF_CELLS)
|
|
40
|
+
return orderedCommonLines(a, b);
|
|
41
|
+
// The recurrence needs only the previous row, and a row is `cols` wide — so
|
|
42
|
+
// the SHORTER array belongs on the `cols` axis, which bounds every row by
|
|
43
|
+
// min(a, b) and is what the memory claim above rests on. The recurrence is
|
|
44
|
+
// symmetric, so the length it returns is the same either way.
|
|
45
|
+
let rows = a;
|
|
46
|
+
let cols = b;
|
|
47
|
+
if (cols.length > rows.length) {
|
|
48
|
+
rows = b;
|
|
49
|
+
cols = a;
|
|
50
|
+
}
|
|
51
|
+
let previous = new Array(cols.length + 1).fill(0);
|
|
52
|
+
let current = new Array(cols.length + 1).fill(0);
|
|
53
|
+
for (let i = 1; i <= rows.length; i++) {
|
|
54
|
+
const line = rows[i - 1];
|
|
55
|
+
for (let j = 1; j <= cols.length; j++) {
|
|
56
|
+
if (line === cols[j - 1]) {
|
|
57
|
+
current[j] = previous[j - 1] + 1;
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
const above = previous[j];
|
|
61
|
+
const left = current[j - 1];
|
|
62
|
+
current[j] = above >= left ? above : left;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
const swap = previous;
|
|
66
|
+
previous = current;
|
|
67
|
+
current = swap;
|
|
68
|
+
}
|
|
69
|
+
return previous[cols.length];
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* How many lines the two arrays share IN ORDER, for inputs too large for the
|
|
73
|
+
* exact table: the longest run of lines that appear in the same relative order
|
|
74
|
+
* on both sides, found among the lines that occur exactly once in `a` (their
|
|
75
|
+
* position is unambiguous) by reducing their `b` positions to a longest
|
|
76
|
+
* strictly increasing run — the standard patience LIS, O(n log n).
|
|
77
|
+
*
|
|
78
|
+
* Order is the whole point. Counting the lines as a multiset (order thrown
|
|
79
|
+
* away) is only an UPPER bound on the LCS, so it under-reports changes: a
|
|
80
|
+
* 3000-line file whose two halves were swapped shares every line, and the
|
|
81
|
+
* multiset count would report the replacement as `+0 −0` — a result claiming a
|
|
82
|
+
* real edit changed nothing. Every line counted here is a real in-order match,
|
|
83
|
+
* so the answer is a LOWER bound on the LCS and the change count it feeds can
|
|
84
|
+
* only ever be over-stated, never denied.
|
|
85
|
+
*
|
|
86
|
+
* @param a - The first line array.
|
|
87
|
+
* @param b - The second line array.
|
|
88
|
+
* @returns A common subsequence length.
|
|
89
|
+
*/
|
|
90
|
+
function orderedCommonLines(a, b) {
|
|
91
|
+
const occurrences = new Map();
|
|
92
|
+
for (const line of a)
|
|
93
|
+
occurrences.set(line, (occurrences.get(line) ?? 0) + 1);
|
|
94
|
+
const position = new Map();
|
|
95
|
+
a.forEach((line, index) => {
|
|
96
|
+
if (occurrences.get(line) === 1)
|
|
97
|
+
position.set(line, index);
|
|
98
|
+
});
|
|
99
|
+
// Patience: `tails[i]` is the smallest `b`-position that ends an increasing
|
|
100
|
+
// run of length `i + 1`.
|
|
101
|
+
const tails = [];
|
|
102
|
+
for (const line of b) {
|
|
103
|
+
const at = position.get(line);
|
|
104
|
+
if (at === undefined)
|
|
105
|
+
continue;
|
|
106
|
+
let lo = 0;
|
|
107
|
+
let hi = tails.length;
|
|
108
|
+
while (lo < hi) {
|
|
109
|
+
const mid = (lo + hi) >> 1;
|
|
110
|
+
if (tails[mid] < at)
|
|
111
|
+
lo = mid + 1;
|
|
112
|
+
else
|
|
113
|
+
hi = mid;
|
|
114
|
+
}
|
|
115
|
+
tails[lo] = at;
|
|
116
|
+
}
|
|
117
|
+
return tails.length;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Count truly added/removed lines between two line arrays using LCS.
|
|
121
|
+
*
|
|
122
|
+
* Bounded by construction, so a whole-file edit cannot allocate a quadratic
|
|
123
|
+
* table here: the longest common prefix and suffix are trimmed first (exact —
|
|
124
|
+
* those lines belong to every optimal diff, and on the usual small edit they
|
|
125
|
+
* shrink the table to the edited span), and what is left is capped (see
|
|
126
|
+
* {@link lcsLength}).
|
|
127
|
+
*
|
|
128
|
+
* @param oldLines - The original lines array.
|
|
129
|
+
* @param newLines - The modified lines array.
|
|
130
|
+
* @returns An object with the number of added and removed lines.
|
|
131
|
+
*/
|
|
132
|
+
export function diffLineCount(oldLines, newLines) {
|
|
133
|
+
const oldLength = oldLines.length;
|
|
134
|
+
const newLength = newLines.length;
|
|
135
|
+
const shared = Math.min(oldLength, newLength);
|
|
136
|
+
// Longest common prefix / suffix. `suffix` is bounded by `shared - prefix`, so
|
|
137
|
+
// the two trimmed runs can never overlap each other.
|
|
138
|
+
let prefix = 0;
|
|
139
|
+
while (prefix < shared && oldLines[prefix] === newLines[prefix])
|
|
140
|
+
prefix++;
|
|
141
|
+
let suffix = 0;
|
|
142
|
+
while (suffix < shared - prefix &&
|
|
143
|
+
oldLines[oldLength - 1 - suffix] === newLines[newLength - 1 - suffix]) {
|
|
144
|
+
suffix++;
|
|
145
|
+
}
|
|
146
|
+
const common = prefix +
|
|
147
|
+
suffix +
|
|
148
|
+
lcsLength(oldLines.slice(prefix, oldLength - suffix), newLines.slice(prefix, newLength - suffix));
|
|
149
|
+
return { added: newLength - common, removed: oldLength - common };
|
|
150
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A run whose own command piped its output through `tail`/`head`/`grep`/`sed`/`awk`/`cut`, so the
|
|
3
|
+
* failure the runner printed is not in what came back.
|
|
4
|
+
*
|
|
5
|
+
* xr-molecule-flash-m147 (+10:12) sent `hurl --variable uid=… --test /tmp/t.hurl 2>&1 | tail -6`.
|
|
6
|
+
* The six lines that survived are Hurl's closing tally; the failed assertion was above the cut.
|
|
7
|
+
* The platform, seeing a red run with no assertion block, said the cause was "the transport or the
|
|
8
|
+
* run itself" — a sentence that sends the reader at the network for a status assertion it never
|
|
9
|
+
* saw. The command was in the platform's hands the whole time: the pipe is the reader's own filter,
|
|
10
|
+
* not a fact about the run.
|
|
11
|
+
*
|
|
12
|
+
* This module answers the one question that lets a caller say so: is an output-slicing stage of
|
|
13
|
+
* THIS command's pipeline sitting after the runner, and what is the command without it? The parse
|
|
14
|
+
* is the package's own shell reader ({@link locatedCommandSegments}), so a `|` inside quotes or a
|
|
15
|
+
* heredoc is not a stage, and only a stage of the runner's own pipeline is considered.
|
|
16
|
+
*
|
|
17
|
+
* @module
|
|
18
|
+
*/
|
|
19
|
+
import { type CommandSegment } from './command-position.js';
|
|
20
|
+
/** A cutting stage that follows the runner, and the command with the whole pipe removed. */
|
|
21
|
+
export interface RunnerOutputFilter {
|
|
22
|
+
/** The stage exactly as the runner's command wrote it — `tail -6`. */
|
|
23
|
+
stage: string;
|
|
24
|
+
/** The pipe as a sentence names it — `| tail -6`. */
|
|
25
|
+
pipe: string;
|
|
26
|
+
/**
|
|
27
|
+
* The command with the pipe and the stage removed — `hurl --variable uid=… --test /tmp/t.hurl
|
|
28
|
+
* 2>&1`. This is a command to send again as written, not a re-typing of it: it is a slice of the
|
|
29
|
+
* original with the filter's own range cut out, so every other word, quote and redirect stays.
|
|
30
|
+
*/
|
|
31
|
+
command: string;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* The output filter that follows the runner in `command`, or null when there is none.
|
|
35
|
+
*
|
|
36
|
+
* Only a stage of the runner's OWN pipeline counts (its separator is `|`), and only when it is the
|
|
37
|
+
* LAST stage of that pipeline: a `| grep … | wc -l` chain would leave a dangling pipe if the
|
|
38
|
+
* middle stage were cut out, so a chain is not answered here at all. A filter before the runner
|
|
39
|
+
* (`cat run.log | tail -20 && hurl …`) reads something else's output and is not this.
|
|
40
|
+
*
|
|
41
|
+
* @param command - The command as the executor sent it.
|
|
42
|
+
* @param runs - Whether a segment is the runner this run is about (the caller knows its own kind:
|
|
43
|
+
* {@link CommandSegment.program} is the program, once wrapper words like `timeout 280` and
|
|
44
|
+
* `npx` are taken away).
|
|
45
|
+
* @returns The filter and the unfiltered command, or null.
|
|
46
|
+
*/
|
|
47
|
+
export declare function outputFilterAfterRunner(command: string, runs: (segment: CommandSegment) => boolean): RunnerOutputFilter | null;
|
|
48
|
+
//# sourceMappingURL=output-filter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"output-filter.d.ts","sourceRoot":"","sources":["../src/output-filter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAE,KAAK,cAAc,EAA0B,MAAM,uBAAuB,CAAA;AASnF,4FAA4F;AAC5F,MAAM,WAAW,kBAAkB;IACjC,sEAAsE;IACtE,KAAK,EAAE,MAAM,CAAA;IACb,qDAAqD;IACrD,IAAI,EAAE,MAAM,CAAA;IACZ;;;;OAIG;IACH,OAAO,EAAE,MAAM,CAAA;CAChB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,OAAO,GACzC,kBAAkB,GAAG,IAAI,CAkB3B"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A run whose own command piped its output through `tail`/`head`/`grep`/`sed`/`awk`/`cut`, so the
|
|
3
|
+
* failure the runner printed is not in what came back.
|
|
4
|
+
*
|
|
5
|
+
* xr-molecule-flash-m147 (+10:12) sent `hurl --variable uid=… --test /tmp/t.hurl 2>&1 | tail -6`.
|
|
6
|
+
* The six lines that survived are Hurl's closing tally; the failed assertion was above the cut.
|
|
7
|
+
* The platform, seeing a red run with no assertion block, said the cause was "the transport or the
|
|
8
|
+
* run itself" — a sentence that sends the reader at the network for a status assertion it never
|
|
9
|
+
* saw. The command was in the platform's hands the whole time: the pipe is the reader's own filter,
|
|
10
|
+
* not a fact about the run.
|
|
11
|
+
*
|
|
12
|
+
* This module answers the one question that lets a caller say so: is an output-slicing stage of
|
|
13
|
+
* THIS command's pipeline sitting after the runner, and what is the command without it? The parse
|
|
14
|
+
* is the package's own shell reader ({@link locatedCommandSegments}), so a `|` inside quotes or a
|
|
15
|
+
* heredoc is not a stage, and only a stage of the runner's own pipeline is considered.
|
|
16
|
+
*
|
|
17
|
+
* @module
|
|
18
|
+
*/
|
|
19
|
+
import { locatedCommandSegments } from './command-position.js';
|
|
20
|
+
/**
|
|
21
|
+
* The programs whose job is to re-shape what an earlier stage printed and that can DROP lines —
|
|
22
|
+
* the reader's own cut, never part of the run. `tee`, `sort` and `wc` are output plumbing too, but
|
|
23
|
+
* they do not hide a failure, so they are not read as the cause of a missing one.
|
|
24
|
+
*/
|
|
25
|
+
const CUTTING_FILTERS = new Set(['awk', 'cut', 'grep', 'head', 'sed', 'tail']);
|
|
26
|
+
/**
|
|
27
|
+
* The output filter that follows the runner in `command`, or null when there is none.
|
|
28
|
+
*
|
|
29
|
+
* Only a stage of the runner's OWN pipeline counts (its separator is `|`), and only when it is the
|
|
30
|
+
* LAST stage of that pipeline: a `| grep … | wc -l` chain would leave a dangling pipe if the
|
|
31
|
+
* middle stage were cut out, so a chain is not answered here at all. A filter before the runner
|
|
32
|
+
* (`cat run.log | tail -20 && hurl …`) reads something else's output and is not this.
|
|
33
|
+
*
|
|
34
|
+
* @param command - The command as the executor sent it.
|
|
35
|
+
* @param runs - Whether a segment is the runner this run is about (the caller knows its own kind:
|
|
36
|
+
* {@link CommandSegment.program} is the program, once wrapper words like `timeout 280` and
|
|
37
|
+
* `npx` are taken away).
|
|
38
|
+
* @returns The filter and the unfiltered command, or null.
|
|
39
|
+
*/
|
|
40
|
+
export function outputFilterAfterRunner(command, runs) {
|
|
41
|
+
if (typeof command !== 'string' || command.trim() === '')
|
|
42
|
+
return null;
|
|
43
|
+
const entries = locatedCommandSegments(command);
|
|
44
|
+
const runnerAt = entries.findIndex((entry) => entry.segment !== null && runs(entry.segment));
|
|
45
|
+
if (runnerAt < 0 || runnerAt + 1 >= entries.length)
|
|
46
|
+
return null;
|
|
47
|
+
const runner = entries[runnerAt];
|
|
48
|
+
const filter = entries[runnerAt + 1];
|
|
49
|
+
// The stage must be fed by the runner's own pipe, and nothing may be piped out of the filter:
|
|
50
|
+
// those are the two positions where cutting it out leaves a command that still runs the run.
|
|
51
|
+
if (!runner || !filter || filter.separator.trim() !== '|')
|
|
52
|
+
return null;
|
|
53
|
+
if (!filter.segment || !CUTTING_FILTERS.has(filter.segment.program))
|
|
54
|
+
return null;
|
|
55
|
+
if (filter.terminator.trim() === '|')
|
|
56
|
+
return null;
|
|
57
|
+
if (runner.end === null || filter.start === null || filter.end === null)
|
|
58
|
+
return null;
|
|
59
|
+
return {
|
|
60
|
+
stage: command.slice(filter.start, filter.end),
|
|
61
|
+
pipe: `| ${command.slice(filter.start, filter.end)}`,
|
|
62
|
+
command: `${command.slice(0, runner.end)}${command.slice(filter.end)}`,
|
|
63
|
+
};
|
|
64
|
+
}
|
package/dist/tools.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;
|
|
1
|
+
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AAkJ9C;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,eAAe,CAAC,OAAO,EAAE,gBAAgB,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAahG;AAkOD,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAqEnE;;;GAGG;AACH,eAAO,MAAM,iBAAiB,QAEA,CAAA;AAE9B;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,wBAAwB,QAI6C,CAAA;AAmBlF;;;;;;;;;;;GAWG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAUhE;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,qBAAqB;IACpC,iFAAiF;IACjF,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;IACzB,8FAA8F;IAC9F,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAA;CAC5B;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,qBAAqB,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,qBAAqB,CAAC,CAAA;AAoBpF;;;;;;;GAOG;AACH,wBAAgB,0BAA0B,CACxC,GAAG,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC;IAAE,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,GAAG,SAAS,CAAC,GACxE,qBAAqB,CAkBvB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,wBAAsB,4BAA4B,CAChD,IAAI,EAAE,MAAM,EACZ,KAAK,CAAC,EAAE,qBAAqB,GAC5B,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAiCxB;AA8ED;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,gBAAgB,EAAE,MAAM,CAAC,EAAE,eAAe,GAAG,MAAM,EAAE,CAk9ExF"}
|
package/dist/tools.js
CHANGED
|
@@ -10,6 +10,7 @@ import { posix } from 'path';
|
|
|
10
10
|
import { commandCutRun, CUT_AT_BUDGET, cutRunSentence, suiteRunExitCode } from './cut-run.js';
|
|
11
11
|
import { addChangeSpan, changedSpan, countLines, renderEditExcerpt, renderSmallFile, } from './edit-excerpt.js';
|
|
12
12
|
import { guardToolExecute, missingParamError, ToolInputError } from './input-normalizer.js';
|
|
13
|
+
import { diffLineCount } from './line-diff.js';
|
|
13
14
|
import { pipelineExitCodeNote } from './pipeline-exit-note.js';
|
|
14
15
|
import { TOOL_SCHEMAS } from './schemas.js';
|
|
15
16
|
import { leadingSleepPeek } from './sleep-peek.js';
|
|
@@ -362,7 +363,7 @@ async function backgroundTaskState(backend, command, seconds, ceilingMs) {
|
|
|
362
363
|
}
|
|
363
364
|
import { suiteCommandTarget } from './suite-overlap.js';
|
|
364
365
|
import { diagnosisTraceCommand } from './trace-artifacts.js';
|
|
365
|
-
import { checkBlockedCommand, declaredBudgetSeconds, DEFAULT_SEARCH_EXCLUDED_DIRS, directoryReadHint, earlyReturnCause, emptyCallError, failureCausesNote, findPathArgs, findPatternArgs, fusedLine, htmlOutline, isCommandTimeout, isEnvFilePath, isOutsideRoot, isPlaywrightRunCommand, isSuiteRunCommand, isValidGlob, linesWithinSerialized, literalDirPrefix, MAX_BATCH_READ_FILES, MAX_FIND_RESULTS, MAX_HTML_INLINE_CHARS, MAX_OUTPUT_SIZE, MAX_READ_CALL_CHARS, MAX_READ_RETURN_CHARS, MAX_READ_SIZE, MAX_SEARCH_CONTEXT_LINES, MAX_SEARCH_RESULTS, MAX_WRITE_SIZE, outputIsWithheldUntilExit, outputRedirectTarget, parseCheckCommand, parseCheckProblem, pathArgError, printsOnlySource, READ_RESULT_ENVELOPE_CHARS, redactSecrets, redactSecretsInCode, repeatingFailure, repeatingFailureNote, resolvePath, RUNNING_FAILURE_LINES, runningCauseCommand, runningOutputSliceCommand, searchTimeoutError, serializedChars, shellQuote, sliceLines, SMALL_FILE_WHOLE_CHARS, streamingBackgroundCommand, stripControlChars, suiteRunPipelinePlacement, suiteRunPriorityLaunch, truncateMiddle, universalCause, whitespaceTolerantReplace, } from './utilities.js';
|
|
366
|
+
import { checkBlockedCommand, declaredBudgetSeconds, DEFAULT_SEARCH_EXCLUDED_DIRS, directoryReadHint, earlyReturnCause, emptyCallError, failureCausesNote, findPathArgs, findPatternArgs, fusedLine, htmlOutline, isCommandTimeout, isEnvFilePath, isOutsideRoot, isPlaywrightRunCommand, isSuiteRunCommand, isValidGlob, linesWithinSerialized, literalDirPrefix, MAX_BATCH_READ_FILES, MAX_FIND_RESULTS, MAX_HTML_INLINE_CHARS, MAX_OUTPUT_SIZE, MAX_READ_CALL_CHARS, MAX_READ_RETURN_CHARS, MAX_READ_SIZE, MAX_SEARCH_CONTEXT_LINES, MAX_SEARCH_RESULTS, MAX_WRITE_SIZE, outputIsWithheldUntilExit, outputRedirectTarget, parseCheckCommand, parseCheckProblem, pathArgError, printsOnlySource, READ_RESULT_ENVELOPE_CHARS, redactSecrets, redactSecretsInCode, repeatingFailure, repeatingFailureNote, resolvePath, RUNNING_FAILURE_LINES, runningCauseCommand, runningOutputSliceCommand, searchTimeoutError, serializedChars, shellQuote, sliceLines, SMALL_FILE_WHOLE_CHARS, streamingBackgroundCommand, stripControlChars, suiteRunPipelinePlacement, suiteRunPriorityLaunch, truncateMiddle, universalCause, whitespaceTolerantReplace, withoutSuiteTimeout, } from './utilities.js';
|
|
366
367
|
/**
|
|
367
368
|
* How long exec_command waits for a command it backgrounded only because the
|
|
368
369
|
* command asked for more than the tool's ceiling, before handing back a task id.
|
|
@@ -921,21 +922,27 @@ export function buildTools(backend, config) {
|
|
|
921
922
|
/**
|
|
922
923
|
* Compute a lightweight diff summary for telemetry and UI badges.
|
|
923
924
|
*
|
|
925
|
+
* The counts are a real line diff ({@link diffLineCount}), not the change in
|
|
926
|
+
* line count: replacing one line with another leaves the file the same
|
|
927
|
+
* length, and a length delta reported that edit as `0/0` — reading to the
|
|
928
|
+
* model as "your write changed nothing" (xr-molecule-flash-m141 +09:56, a
|
|
929
|
+
* one-line rewrite of `ArticlePreview.tsx`). A write that leaves the content
|
|
930
|
+
* identical is `unchanged` rather than a `modified` with `0/0`, so the two
|
|
931
|
+
* are distinguishable.
|
|
932
|
+
*
|
|
924
933
|
* @param oldContent - Previous file contents, or `null` when creating a file.
|
|
925
934
|
* @param newContent - Replacement file contents after a write/edit.
|
|
926
|
-
* @returns Counts describing whether the file was created or
|
|
935
|
+
* @returns Counts describing whether the file was created, modified or left unchanged.
|
|
927
936
|
*/
|
|
928
937
|
function computeDiff(oldContent, newContent) {
|
|
929
938
|
if (oldContent === null) {
|
|
930
|
-
return { type: 'created', linesAdded: newContent
|
|
939
|
+
return { type: 'created', linesAdded: countLines(newContent), linesRemoved: 0 };
|
|
931
940
|
}
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
linesRemoved: Math.max(0, oldLines.length - newLines.length),
|
|
938
|
-
};
|
|
941
|
+
if (oldContent === newContent) {
|
|
942
|
+
return { type: 'unchanged', linesAdded: 0, linesRemoved: 0 };
|
|
943
|
+
}
|
|
944
|
+
const { added, removed } = diffLineCount(oldContent.split('\n'), newContent.split('\n'));
|
|
945
|
+
return { type: 'modified', linesAdded: added, linesRemoved: removed };
|
|
939
946
|
}
|
|
940
947
|
// ── Tool implementations ───────────────────────────────────────
|
|
941
948
|
/**
|
|
@@ -2137,6 +2144,16 @@ export function buildTools(backend, config) {
|
|
|
2137
2144
|
// an executor's commands routinely carry quotes, heredocs and
|
|
2138
2145
|
// newlines, and re-quoting them into a detached `sh -c` is how a
|
|
2139
2146
|
// background runner corrupts the very thing it is running.
|
|
2147
|
+
// The platform moved this run to the background because its own bound could not fit
|
|
2148
|
+
// this tool's ceiling — and in the background the ceiling no longer applies, so the
|
|
2149
|
+
// `timeout N` the executor wrote to fit it is the only bound left, and it kills the
|
|
2150
|
+
// walk mid-run with no tally (X1 xr-molecule-flash-m143 +24:45). Lift it from the
|
|
2151
|
+
// statement that runs the suite. A run the EXECUTOR detached keeps the bound it chose.
|
|
2152
|
+
const lifted = overCeiling && input.run_in_background !== true
|
|
2153
|
+
? withoutSuiteTimeout(command)
|
|
2154
|
+
: { command, seconds: null };
|
|
2155
|
+
if (lifted.seconds !== null)
|
|
2156
|
+
command = lifted.command;
|
|
2140
2157
|
// Keep the log growing while it runs (see streamingBackgroundCommand):
|
|
2141
2158
|
// an empty log is what wait_for_task reads as stuck.
|
|
2142
2159
|
const streaming = streamingBackgroundCommand(command);
|
|
@@ -2144,6 +2161,12 @@ export function buildTools(backend, config) {
|
|
|
2144
2161
|
// its changes are what this branch reports the run's own settings in.
|
|
2145
2162
|
if (traced.change)
|
|
2146
2163
|
streaming.changes.unshift(traced.change);
|
|
2164
|
+
// Said once, in the result that also hands back the task id or the run's output.
|
|
2165
|
+
if (lifted.seconds !== null) {
|
|
2166
|
+
streaming.changes.unshift(`Dropped your \`timeout ${lifted.seconds}\` from the suite run: in the background it ` +
|
|
2167
|
+
'is bounded by `wait_for_task` and the turn’s deadline, not by this tool’s ' +
|
|
2168
|
+
'ceiling, so the bound you wrote would only have killed the walk.');
|
|
2169
|
+
}
|
|
2147
2170
|
// A whole-suite run starves the sandbox it shares with the app and with
|
|
2148
2171
|
// every command the executor sends next, so it starts at lower CPU and
|
|
2149
2172
|
// IO priority (see suiteRunPriorityLaunch). Foreground runs are never
|
|
@@ -2210,8 +2233,15 @@ export function buildTools(backend, config) {
|
|
|
2210
2233
|
? { truncated: true, fullOutput: log }
|
|
2211
2234
|
: {}),
|
|
2212
2235
|
error: (sleepNote ? `${sleepNote} ` : '') +
|
|
2213
|
-
|
|
2214
|
-
|
|
2236
|
+
cutRunSentence(cut) +
|
|
2237
|
+
// A suite stopped in the background is never sent to the background again:
|
|
2238
|
+
// the sentence says what to do, and the advice's reason (it “takes the run
|
|
2239
|
+
// off this budget and writes its output to a log”) has already happened.
|
|
2240
|
+
(cut.background
|
|
2241
|
+
? ''
|
|
2242
|
+
: ` ${isSuiteRunCommand(command)
|
|
2243
|
+
? SUITE_OVER_BUDGET_ADVICE
|
|
2244
|
+
: SPLIT_WORK_ADVICE}`),
|
|
2215
2245
|
};
|
|
2216
2246
|
}
|
|
2217
2247
|
// The exit file holds the PIPELINE's code: when a stage this rewrite could not
|
|
@@ -250,14 +250,24 @@ export declare function stalledDocumentSubresource(request: TraceRequest): boole
|
|
|
250
250
|
* fetch stalled for 27 s (xr-molecule-flash-m135 +34:10). The page shell's incidental requests
|
|
251
251
|
* (`/sw.js`, the manifest, a favicon, a font) stay out, for m125's reason.
|
|
252
252
|
*
|
|
253
|
+
* A 404 whose path lacks the API prefix the app's own calls carry is called out with that prefix
|
|
254
|
+
* ({@link missingApiPrefixRequests}): xr-molecule-flash-m145 posted `/users` to `vite preview`,
|
|
255
|
+
* whose 404 was answered by the static server because the HTTP client's `baseURL` was never set —
|
|
256
|
+
* and the note printed "read the handler this route reaches in your own source", which presumes a
|
|
257
|
+
* request that never reached the API. The prefix is READ (the caller passes what the app's client
|
|
258
|
+
* config names; the traces add what their own requests show) and never assumed to be `/api`.
|
|
259
|
+
*
|
|
253
260
|
* @param items - The traces {@link parseTraceArtifacts} read.
|
|
254
261
|
* @param target - Where the run went — the deployed host, as the note names it.
|
|
255
262
|
* @param earlierFailures - The failing `file:line`s earlier walks of this suite left, so a repeat
|
|
256
263
|
* is ranked above a one-off. Empty when the caller knows of no earlier walk.
|
|
264
|
+
* @param apiPrefixes - The API path prefixes the app's client config names (`['/api']`), read from
|
|
265
|
+
* the app by the caller. Empty when the caller could not read one; the traces' own requests may
|
|
266
|
+
* still supply it. Never a hard-coded literal.
|
|
257
267
|
* @returns The note, or null when no trace held a recorded response, a real failure or a console
|
|
258
268
|
* error.
|
|
259
269
|
*/
|
|
260
|
-
export declare function failureTraceNote(items: readonly TraceArtifact[], target: string, earlierFailures?: readonly string[]): string | null;
|
|
270
|
+
export declare function failureTraceNote(items: readonly TraceArtifact[], target: string, earlierFailures?: readonly string[], apiPrefixes?: readonly string[]): string | null;
|
|
261
271
|
/**
|
|
262
272
|
* Whether a Playwright config records traces for the tests that fail.
|
|
263
273
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"trace-artifacts.d.ts","sourceRoot":"","sources":["../src/trace-artifacts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAMH,iEAAiE;AACjE,MAAM,WAAW,YAAY;IAC3B,gCAAgC;IAChC,MAAM,EAAE,MAAM,CAAA;IACd,iGAAiG;IACjG,IAAI,EAAE,MAAM,CAAA;IACZ;;;;OAIG;IACH,MAAM,EAAE,MAAM,CAAA;IACd;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB;;;;;OAKG;IACH,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,oFAAoF;IACpF,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAED,gFAAgF;AAChF,MAAM,WAAW,WAAW;IAC1B,yFAAyF;IACzF,GAAG,EAAE,MAAM,CAAA;IACX,wEAAwE;IACxE,KAAK,EAAE,MAAM,CAAA;IACb,kEAAkE;IAClE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;CACnB;AAED,iGAAiG;AACjG,MAAM,WAAW,aAAa;IAC5B,0FAA0F;IAC1F,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IACnB,+EAA+E;IAC/E,IAAI,EAAE,MAAM,CAAA;IACZ,6FAA6F;IAC7F,QAAQ,EAAE,YAAY,EAAE,CAAA;IACxB;;;;;;;;OAQG;IACH,IAAI,CAAC,EAAE,YAAY,CAAA;IACnB,0DAA0D;IAC1D,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB;;;;OAIG;IACH,OAAO,EAAE,WAAW,EAAE,CAAA;CACvB;AAED,sDAAsD;AACtD,eAAO,MAAM,mBAAmB,KAAK,CAAA;AAErC,sDAAsD;AACtD,eAAO,MAAM,mBAAmB,IAAI,CAAA;AAEpC,4DAA4D;AAC5D,eAAO,MAAM,kBAAkB,IAAI,CAAA;AAEnC,sFAAsF;AACtF,eAAO,MAAM,kBAAkB,KAAK,CAAA;AAEpC,yFAAyF;AACzF,eAAO,MAAM,qBAAqB,MAAM,CAAA;AAExC,uDAAuD;AACvD,eAAO,MAAM,oBAAoB,IAAI,CAAA;AAErC,iDAAiD;AACjD,eAAO,MAAM,uBAAuB,IAAI,CAAA;AAExC,gEAAgE;AAChE,eAAO,MAAM,oBAAoB,KAAK,CAAA;AAEtC,wFAAwF;AACxF,eAAO,MAAM,qBAAqB,MAAM,CAAA;AAExC,kFAAkF;AAClF,eAAO,MAAM,oBAAoB,MAAM,CAAA;AAEvC;;;;GAIG;AACH,eAAO,MAAM,yBAAyB,MAAM,CAAA;AAK5C;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,eAAO,MAAM,oBAAoB,QAAO,MA0VvC,CAAA;AAsBD;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,aAAa,EAAE,CAgCnE;AA+HD;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAKnE;AAED;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAG5D;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAGhE;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAMzE;
|
|
1
|
+
{"version":3,"file":"trace-artifacts.d.ts","sourceRoot":"","sources":["../src/trace-artifacts.ts"],"names":[],"mappings":"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"}
|
package/dist/trace-artifacts.js
CHANGED
|
@@ -810,6 +810,94 @@ function transportFailure(items) {
|
|
|
810
810
|
}
|
|
811
811
|
return null;
|
|
812
812
|
}
|
|
813
|
+
/**
|
|
814
|
+
* A request's path when the trace recorded a same-origin path — `/users` — or null for an
|
|
815
|
+
* absolute URL (another origin's path is no evidence about this app's API).
|
|
816
|
+
*
|
|
817
|
+
* @param request - One request a trace recorded.
|
|
818
|
+
* @returns The path without a query or trailing slash, or null.
|
|
819
|
+
*/
|
|
820
|
+
function sameOriginPath(request) {
|
|
821
|
+
const raw = String(request.path ?? '').split(/[?#]/)[0] ?? '';
|
|
822
|
+
if (!raw.startsWith('/') || raw === '/')
|
|
823
|
+
return null;
|
|
824
|
+
return raw.replace(/\/+$/, '');
|
|
825
|
+
}
|
|
826
|
+
/**
|
|
827
|
+
* The API path prefixes the traced requests themselves show.
|
|
828
|
+
*
|
|
829
|
+
* A request whose path ends with another traced request's path carries that other path's prefix
|
|
830
|
+
* (`/api/users` beside `/users` → `/api`) — the trace's own evidence, with no literal in the code.
|
|
831
|
+
*
|
|
832
|
+
* @param items - The traces the note is about.
|
|
833
|
+
* @returns The one-segment prefixes (`/api`), sorted; empty when the traces show none.
|
|
834
|
+
*/
|
|
835
|
+
function tracedApiPrefixes(items) {
|
|
836
|
+
const paths = items.flatMap((item) => item.requests.map(sameOriginPath).filter((path) => path !== null));
|
|
837
|
+
const prefixes = new Set();
|
|
838
|
+
for (const longer of paths) {
|
|
839
|
+
for (const shorter of paths) {
|
|
840
|
+
if (longer === shorter || shorter.length < 2 || !longer.endsWith(shorter))
|
|
841
|
+
continue;
|
|
842
|
+
const prefix = longer.slice(0, longer.length - shorter.length);
|
|
843
|
+
if (/^\/[^/]+$/.test(prefix))
|
|
844
|
+
prefixes.add(prefix);
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
return [...prefixes].sort();
|
|
848
|
+
}
|
|
849
|
+
/**
|
|
850
|
+
* The failed 404s whose path carries none of the known API prefixes.
|
|
851
|
+
*
|
|
852
|
+
* Only a run where the prefix is KNOWABLE diagnoses: the caller reads it from the app's client
|
|
853
|
+
* config, and the traces' own requests add what they show ({@link tracedApiPrefixes}). With no
|
|
854
|
+
* known prefix, an unprefixed path is just a path — assuming `/api` is how a correct request gets
|
|
855
|
+
* a wrong note.
|
|
856
|
+
*
|
|
857
|
+
* @param items - The traces the note is about.
|
|
858
|
+
* @param apiPrefixes - Prefixes the app's client config names (`['/api']`), never a literal.
|
|
859
|
+
* @returns One entry per distinct failed 404, in trace order.
|
|
860
|
+
*/
|
|
861
|
+
function missingApiPrefixRequests(items, apiPrefixes) {
|
|
862
|
+
const prefixes = new Set([...apiPrefixes, ...tracedApiPrefixes(items)]
|
|
863
|
+
.map((prefix) => `/${String(prefix).replace(/^\/+|\/+$/g, '')}`)
|
|
864
|
+
.filter((prefix) => prefix !== '/'));
|
|
865
|
+
if (prefixes.size === 0)
|
|
866
|
+
return [];
|
|
867
|
+
const known = [...prefixes];
|
|
868
|
+
const out = [];
|
|
869
|
+
const seen = new Set();
|
|
870
|
+
for (const item of items) {
|
|
871
|
+
for (const request of item.requests) {
|
|
872
|
+
// A document navigation is a page route, never an API call — a 404 on one says nothing
|
|
873
|
+
// about the client's base URL.
|
|
874
|
+
if (request.status !== 404 || request.type === 'document' || !failedRequest(request))
|
|
875
|
+
continue;
|
|
876
|
+
const path = sameOriginPath(request);
|
|
877
|
+
if (path === null)
|
|
878
|
+
continue;
|
|
879
|
+
if (known.some((prefix) => path === prefix || path.startsWith(`${prefix}/`)))
|
|
880
|
+
continue;
|
|
881
|
+
const key = `${request.method} ${path}`;
|
|
882
|
+
if (seen.has(key))
|
|
883
|
+
continue;
|
|
884
|
+
seen.add(key);
|
|
885
|
+
out.push({ method: request.method, path, prefix: known[0] ?? '' });
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
return out;
|
|
889
|
+
}
|
|
890
|
+
/**
|
|
891
|
+
* The sentence naming a request that reached the page origin without the app's API prefix.
|
|
892
|
+
*
|
|
893
|
+
* @param request - The failed request and the prefix its sibling calls carry.
|
|
894
|
+
* @returns The sentence the note appends.
|
|
895
|
+
*/
|
|
896
|
+
function missingApiPrefixSentence(request) {
|
|
897
|
+
return (`\`${request.method} ${request.path}\` → 404: this request went to \`${request.path}\` ` +
|
|
898
|
+
`without the \`${request.prefix}\` prefix — the HTTP client's base URL is not set: is ` +
|
|
899
|
+
'`setupProviders()` (which runs the HTTP bond setup) called in `main.tsx`?');
|
|
900
|
+
}
|
|
813
901
|
/**
|
|
814
902
|
* The note a failed suite run's result carries: what each failing test's page actually sent, and
|
|
815
903
|
* what came back.
|
|
@@ -854,14 +942,24 @@ function transportFailure(items) {
|
|
|
854
942
|
* fetch stalled for 27 s (xr-molecule-flash-m135 +34:10). The page shell's incidental requests
|
|
855
943
|
* (`/sw.js`, the manifest, a favicon, a font) stay out, for m125's reason.
|
|
856
944
|
*
|
|
945
|
+
* A 404 whose path lacks the API prefix the app's own calls carry is called out with that prefix
|
|
946
|
+
* ({@link missingApiPrefixRequests}): xr-molecule-flash-m145 posted `/users` to `vite preview`,
|
|
947
|
+
* whose 404 was answered by the static server because the HTTP client's `baseURL` was never set —
|
|
948
|
+
* and the note printed "read the handler this route reaches in your own source", which presumes a
|
|
949
|
+
* request that never reached the API. The prefix is READ (the caller passes what the app's client
|
|
950
|
+
* config names; the traces add what their own requests show) and never assumed to be `/api`.
|
|
951
|
+
*
|
|
857
952
|
* @param items - The traces {@link parseTraceArtifacts} read.
|
|
858
953
|
* @param target - Where the run went — the deployed host, as the note names it.
|
|
859
954
|
* @param earlierFailures - The failing `file:line`s earlier walks of this suite left, so a repeat
|
|
860
955
|
* is ranked above a one-off. Empty when the caller knows of no earlier walk.
|
|
956
|
+
* @param apiPrefixes - The API path prefixes the app's client config names (`['/api']`), read from
|
|
957
|
+
* the app by the caller. Empty when the caller could not read one; the traces' own requests may
|
|
958
|
+
* still supply it. Never a hard-coded literal.
|
|
861
959
|
* @returns The note, or null when no trace held a recorded response, a real failure or a console
|
|
862
960
|
* error.
|
|
863
961
|
*/
|
|
864
|
-
export function failureTraceNote(items, target, earlierFailures = []) {
|
|
962
|
+
export function failureTraceNote(items, target, earlierFailures = [], apiPrefixes = []) {
|
|
865
963
|
const candidates = items.filter(traceRowEvidence);
|
|
866
964
|
if (candidates.length === 0)
|
|
867
965
|
return null;
|
|
@@ -885,6 +983,22 @@ export function failureTraceNote(items, target, earlierFailures = []) {
|
|
|
885
983
|
const wrapped = traced.some((item) => item.requests.some((request) => request.sent !== undefined && wrappedInData(request.sent)));
|
|
886
984
|
// The "one of these requests failing" answer is only honest when a listed request really failed.
|
|
887
985
|
const anyFailed = traced.some((item) => item.requests.some(failedRequest));
|
|
986
|
+
// m145's shape: the app's HTTP client lost its base URL, so every API call reached the page
|
|
987
|
+
// origin without the prefix the app mounts its routes under. The prefix is READ — the caller's
|
|
988
|
+
// client config plus what the traces show — never assumed to be `/api`.
|
|
989
|
+
const missingPrefix = missingApiPrefixRequests(traced, apiPrefixes);
|
|
990
|
+
const failedRequests = traced.flatMap((item) => item.requests.filter(failedRequest));
|
|
991
|
+
const missingKeys = new Set(missingPrefix.map((request) => `${request.method} ${request.path}`));
|
|
992
|
+
const everyFailureLacksPrefix = failedRequests.length > 0 &&
|
|
993
|
+
failedRequests.every((request) => {
|
|
994
|
+
const path = sameOriginPath(request);
|
|
995
|
+
return path !== null && missingKeys.has(`${request.method} ${path}`);
|
|
996
|
+
});
|
|
997
|
+
// The route advice goes when every failure is one of these: a request that never reached the
|
|
998
|
+
// API has no handler to read (m145's note sent the run to the PWA and then to the handler).
|
|
999
|
+
const prefixAdvice = missingPrefix.length > 0
|
|
1000
|
+
? ` ${missingPrefix.slice(0, 2).map(missingApiPrefixSentence).join(' ')}`
|
|
1001
|
+
: '';
|
|
888
1002
|
// The document subresources that never finished anywhere in this read — the assets a timed-out
|
|
889
1003
|
// navigation was waiting on, when there is no failed request to explain the timeout.
|
|
890
1004
|
const stalledAssets = [
|
|
@@ -994,9 +1108,11 @@ export function failureTraceNote(items, target, earlierFailures = []) {
|
|
|
994
1108
|
`requests that failed, the ones that got no response, and its last answered ones):\n` +
|
|
995
1109
|
`${rows.join('\n')}${more}\n` +
|
|
996
1110
|
(anyFailed
|
|
997
|
-
?
|
|
998
|
-
|
|
999
|
-
'
|
|
1111
|
+
? everyFailureLacksPrefix
|
|
1112
|
+
? ''
|
|
1113
|
+
: 'A click that changed nothing is usually one of these requests failing, not the selector: ' +
|
|
1114
|
+
'read the handler this route reaches in your own source, and stop `catch`-ing the request of a ' +
|
|
1115
|
+
'user action silently — log it.'
|
|
1000
1116
|
: stalledAssets.length > 0
|
|
1001
1117
|
? 'No listed request failed — none got a 4xx/5xx or a network error — but the document did ' +
|
|
1002
1118
|
'not finish loading because its own subresource never answered. Do not read this as a ' +
|
|
@@ -1007,6 +1123,7 @@ export function failureTraceNote(items, target, earlierFailures = []) {
|
|
|
1007
1123
|
'row): if it is not the request the test meant — an unfollow sent where the test clicked ' +
|
|
1008
1124
|
'follow — the state the click read was stale before it. Otherwise read the state the ' +
|
|
1009
1125
|
'failing test waited on.') +
|
|
1126
|
+
prefixAdvice +
|
|
1010
1127
|
(wrapped
|
|
1011
1128
|
? ' The API received a body wrapped in `data`: app-http’s `post(url, data, config)` / ' +
|
|
1012
1129
|
'`put(url, data, config)` / `patch(url, data, config)` take the body as their SECOND ' +
|
package/dist/utilities.d.ts
CHANGED
|
@@ -193,6 +193,22 @@ export declare function streamingBackgroundCommand(command: string): {
|
|
|
193
193
|
* @returns True when the command starts one of those runners.
|
|
194
194
|
*/
|
|
195
195
|
export declare function isSuiteRunCommand(command: string): boolean;
|
|
196
|
+
/**
|
|
197
|
+
* The command with the `timeout <N>` wrapper lifted off the statement that runs the suite, and the
|
|
198
|
+
* bound that was lifted.
|
|
199
|
+
*
|
|
200
|
+
* Used where the platform moves a run to the background: the executor wrote the bound to fit this
|
|
201
|
+
* tool's foreground ceiling, and in the background that ceiling no longer applies — the task is
|
|
202
|
+
* bounded by `wait_for_task` and the turn's deadline — so the prefix only kills the walk
|
|
203
|
+
* ({@link withoutTimeoutWrapper}; X1 xr-molecule-flash-m143 +24:45).
|
|
204
|
+
*
|
|
205
|
+
* @param command - The shell command.
|
|
206
|
+
* @returns The rewritten command, and the largest lifted bound (null when nothing was lifted).
|
|
207
|
+
*/
|
|
208
|
+
export declare function withoutSuiteTimeout(command: string): {
|
|
209
|
+
command: string;
|
|
210
|
+
seconds: number | null;
|
|
211
|
+
};
|
|
196
212
|
/**
|
|
197
213
|
* Whether a command starts a Playwright run — a `playwright test` in command position, in a form
|
|
198
214
|
* that runs something.
|
package/dist/utilities.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utilities.d.ts","sourceRoot":"","sources":["../src/utilities.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;
|
|
1
|
+
{"version":3,"file":"utilities.d.ts","sourceRoot":"","sources":["../src/utilities.ts"],"names":[],"mappings":"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"}
|
package/dist/utilities.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* @module
|
|
6
6
|
*/
|
|
7
7
|
import { posix } from 'path';
|
|
8
|
-
import { commandSegments, locatedCommandSegments, statementArgumentEnd, } from './command-position.js';
|
|
8
|
+
import { commandSegments, locatedCommandSegments, statementArgumentEnd, withoutTimeoutWrapper, } from './command-position.js';
|
|
9
9
|
import { classifyRunFailure } from './failure-kind.js';
|
|
10
10
|
import { commandRunsNoTests } from './no-test-run.js';
|
|
11
11
|
import { isDiagnosisRun, scopedRerunCommand, suiteCommandTarget, suiteRunInTheWay, withoutHeredocs, withSuiteConfig, } from './suite-overlap.js';
|
|
@@ -590,6 +590,21 @@ export function isSuiteRunCommand(command) {
|
|
|
590
590
|
return false;
|
|
591
591
|
return commandSegments(command).some((segment) => SUITE_RUN_COMMANDS.some((re) => re.test(segment.text)));
|
|
592
592
|
}
|
|
593
|
+
/**
|
|
594
|
+
* The command with the `timeout <N>` wrapper lifted off the statement that runs the suite, and the
|
|
595
|
+
* bound that was lifted.
|
|
596
|
+
*
|
|
597
|
+
* Used where the platform moves a run to the background: the executor wrote the bound to fit this
|
|
598
|
+
* tool's foreground ceiling, and in the background that ceiling no longer applies — the task is
|
|
599
|
+
* bounded by `wait_for_task` and the turn's deadline — so the prefix only kills the walk
|
|
600
|
+
* ({@link withoutTimeoutWrapper}; X1 xr-molecule-flash-m143 +24:45).
|
|
601
|
+
*
|
|
602
|
+
* @param command - The shell command.
|
|
603
|
+
* @returns The rewritten command, and the largest lifted bound (null when nothing was lifted).
|
|
604
|
+
*/
|
|
605
|
+
export function withoutSuiteTimeout(command) {
|
|
606
|
+
return withoutTimeoutWrapper(command, (segment) => isSuiteRunCommand(segment.text));
|
|
607
|
+
}
|
|
593
608
|
/**
|
|
594
609
|
* Whether a command starts a Playwright run — a `playwright test` in command position, in a form
|
|
595
610
|
* that runs something.
|