@molecule/api-ai-tools 1.9.16 → 1.9.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -3,7 +3,7 @@ AUTO-GENERATED — DO NOT EDIT THIS FILE.
3
3
  Generated by `mlcl sync-docs` from the package's src/index.ts JSDoc + mlcl/registry.json.
4
4
  Edits here are overwritten on the next commit (molecule's pre-commit hook regenerates).
5
5
  To change this document, edit the module-level JSDoc in src/index.ts.
6
- Generated: 2026-10-18T12:49:51.606Z
6
+ Generated: 2026-10-18T20:46:28.562Z
7
7
  -->
8
8
 
9
9
  # @molecule/api-ai-tools
@@ -66,7 +66,9 @@ One statement or pipeline stage of a shell command: what it runs, and with what.
66
66
  interface CommandSegment {
67
67
  /**
68
68
  * The word in command position, its directory removed (`/usr/bin/hurl` → `hurl`): the program
69
- * the shell would run once whatever wrappers stood in front of it are taken away.
69
+ * the shell would run once whatever wrappers stood in front of it are taken away. When a
70
+ * wrapper runs a tool's own CLI by file, this is the tool's name — `node
71
+ * <path>/node_modules/.bin/playwright test` is a `playwright` invocation, not a `node` one.
70
72
  */
71
73
  program: string
72
74
  /** The words after it, as written — quotes kept, so a quoted `"a|b"` is one word. */
@@ -284,9 +286,13 @@ A cutting stage that follows the runner, and the command with the whole pipe rem
284
286
 
285
287
  ```typescript
286
288
  interface RunnerOutputFilter {
287
- /** The stage exactly as the runner's command wrote it — `tail -6`. */
289
+ /**
290
+ * The stage exactly as the runner's command wrote it — `tail -6`. When several cutting filters
291
+ * follow the runner, this is the whole trailing chain as written, pipes included:
292
+ * `grep -E "Failure|actual" | head -40`.
293
+ */
288
294
  stage: string
289
- /** The pipe as a sentence names it — `| tail -6`. */
295
+ /** The pipe as a sentence names it — `| tail -6`, or the whole chain `| grep … | head -40`. */
290
296
  pipe: string
291
297
  /**
292
298
  * The command with the pipe and the stage removed — `hurl --variable uid=… --test /tmp/t.hurl
@@ -309,6 +315,17 @@ interface ToolSchema {
309
315
  }
310
316
  ```
311
317
 
318
+ #### `ForegroundServer`
319
+
320
+ A foreground command that starts a server, as the refusal names it.
321
+
322
+ ```typescript
323
+ interface ForegroundServer {
324
+ /** The program (and script, when there is one) that serves. */
325
+ readonly program: string
326
+ }
327
+ ```
328
+
312
329
  #### `LeadingSleep`
313
330
 
314
331
  The leading `sleep` a command opens with, and the command that follows it.
@@ -345,6 +362,20 @@ interface SuiteRunInFlight {
345
362
  }
346
363
  ```
347
364
 
365
+ #### `DiagnosisRunContext`
366
+
367
+ What a caller knows about the run being judged beyond its command text.
368
+
369
+ ```typescript
370
+ interface DiagnosisRunContext {
371
+ /**
372
+ * The test titles the person's request excludes by name — the same reading molecule-dev's
373
+ * `excludedTestTitles` gives the rest of the platform.
374
+ */
375
+ approvedExclusions?: readonly string[]
376
+ }
377
+ ```
378
+
348
379
  #### `InstalledPackageFacts`
349
380
 
350
381
  What the filesystem says about a write target under `node_modules` — the half
@@ -486,6 +517,17 @@ interface TraceAction {
486
517
  pageId?: string
487
518
  /** The frame the action ran in, from the same snapshot. */
488
519
  frameId?: string
520
+ /**
521
+ * The selector a `fill`/`type`/`selectOption` acted on (`textarea[name="bio"]`), from the
522
+ * trace's own params. Kept so the note can compare what the test typed with what the page
523
+ * submitted (m173: the field it typed into was sent empty).
524
+ */
525
+ selector?: string
526
+ /**
527
+ * The value a `fill`/`type`/`selectOption` typed or chose, from the trace's own params —
528
+ * absent when the action carried none (a `selectOption` by label only).
529
+ */
530
+ value?: string
489
531
  }
490
532
  ```
491
533
 
@@ -512,6 +554,30 @@ interface TraceContextEvidence {
512
554
  }
513
555
  ```
514
556
 
557
+ #### `TraceMissingText`
558
+
559
+ The text a failing assertion waited for that a same-page API response carried and the page never
560
+ rendered.
561
+
562
+ The assertion's own error names the text (a `has-text(…)` / `getByText(…)` / `toContainText(…)`
563
+ argument), and the trace's network log holds the response that contained it. That pair is the
564
+ whole diagnosis: the API returned the value and the page dropped it before rendering, so the cut
565
+ is in the component that maps the response onto the list — not in the API, the route or the
566
+ selector. xr-molecule-flash-m169 (+13:09) spent its fix rounds on the wrong component and on a
567
+ markup detail while `GET /api/tags` was returning the missing "trending" and `PopularTags`
568
+ sliced the response to its first 20 entries (the m99 top-N class, in the frontend).
569
+
570
+ ```typescript
571
+ interface TraceMissingText {
572
+ /** The text the assertion waited for (`trending`). */
573
+ text: string
574
+ /** The method of the response that carried it (`GET`). */
575
+ method: string
576
+ /** The path of the response that carried it (`/api/tags`). */
577
+ path: string
578
+ }
579
+ ```
580
+
515
581
  #### `TraceArtifact`
516
582
 
517
583
  What one trace says: the test it belongs to, the requests it made, and its console lines.
@@ -581,6 +647,17 @@ interface TraceArtifact {
581
647
  * and on traces read before this field existed.
582
648
  */
583
649
  otherContexts?: TraceContextEvidence[]
650
+ /**
651
+ * The text the failing assertion waited for and a same-page API response carried, when the trace
652
+ * recorded both. The note leads with it: the value reached the browser and the page did not
653
+ * render it, so the next read is where that response is mapped — never the API or the selector
654
+ * (xr-molecule-flash-m169).
655
+ *
656
+ * Absent when the assertion waited for no text (a text-free selector, a strict-mode violation),
657
+ * when no page-scoped `fetch`/`xhr` answered 2xx/3xx with the text in its body, and on traces
658
+ * read before this field existed.
659
+ */
660
+ missingText?: TraceMissingText
584
661
  }
585
662
  ```
586
663
 
@@ -627,6 +704,14 @@ interface TraceRunReport {
627
704
  * contradicts it.
628
705
  */
629
706
  productionBuildAdvised?: boolean
707
+ /**
708
+ * The endpoints the app's own auth config names for a credential exchange — a login, a
709
+ * registration, a token endpoint (`loginEndpoint: '/users/login'`, resolved from the app's
710
+ * source). A 401/403 there answers credentials the app was ASKED to check, never a token the
711
+ * client failed to attach, so the note says so even when the path is mounted somewhere unusual.
712
+ * Empty when the caller could not read the app's config.
713
+ */
714
+ authEndpoints?: readonly string[]
630
715
  }
631
716
  ```
632
717
 
@@ -695,6 +780,33 @@ interface ExecutionBackend {
695
780
  }
696
781
  ```
697
782
 
783
+ #### `SuiteBudgetPin`
784
+
785
+ The supplied suite's own settings, put back on a run whose runner config changes them.
786
+
787
+ A graded suite is walked by the grader under its own per-test budget, worker count and
788
+ timeouts. An executor's run that reads a config setting different numbers measures a run the
789
+ grader never sees: a shorter budget fails tests the graded walk passes, a longer one passes
790
+ tests the graded walk times out on. The platform that knows the suite supplies these values
791
+ before the run ({@link ToolBuildConfig.suiteBudget}); the shared tool puts the flags on the
792
+ runner's own command line, where Playwright lets them win over every config file.
793
+
794
+ ```typescript
795
+ interface SuiteBudgetPin {
796
+ /**
797
+ * The Playwright flags that carry the suite's own settings onto the command
798
+ * (`--timeout=15000`, `--workers=1`), as the consumer's own scan reads them. Empty when the
799
+ * difference is in settings no flag carries — the pin is then the sentence alone.
800
+ */
801
+ flags: string[]
802
+ /**
803
+ * One sentence for the run's result: this run uses the suite's own budget, and which settings
804
+ * a config keeps because Playwright has no flag for them.
805
+ */
806
+ note: string
807
+ }
808
+ ```
809
+
698
810
  #### `ToolBuildConfig`
699
811
 
700
812
  Configuration for building the tool set.
@@ -802,6 +914,23 @@ interface ToolBuildConfig {
802
914
  */
803
915
  excludedTestTitles?: () => readonly string[]
804
916
 
917
+ /**
918
+ * The supplied suite's own budget, when the Playwright config a command would load sets
919
+ * different settings, asked BEFORE the run executes. The consumer knows where the suite and its
920
+ * base config live; this shared package does not, so the reading is the consumer's and the
921
+ * mechanics — putting the flags on the runner's own arguments, before the pipeline and outside
922
+ * any redirect or group closer — are this tool's.
923
+ *
924
+ * Called for a Playwright suite command only, and only the LAST suite step is touched. A run
925
+ * that already keeps the suite's settings, or a sandbox with no supplied suite, answers `null`.
926
+ * Unset (or a hook that throws) changes nothing: the command runs as the model wrote it.
927
+ *
928
+ * @param command - The shell command, as the model sent it.
929
+ * @param cwd - The resolved working directory it will run in.
930
+ * @returns The flags and the sentence, or null when nothing is put back.
931
+ */
932
+ suiteBudget?: (command: string, cwd: string) => Promise<SuiteBudgetPin | null | undefined>
933
+
805
934
  /** Post-write hook (e.g. auto-format via Prettier/ESLint). Called after every write_file/edit_file. */
806
935
  onAfterWrite?: (path: string) => Promise<void>
807
936
 
@@ -1583,24 +1712,29 @@ function commandRunsNoTests(command: string): boolean
1583
1712
 
1584
1713
  **Returns:** True when the command invokes a runner only to print.
1585
1714
 
1586
- #### `outputFilterAfterRunner(command, runs)`
1715
+ #### `outputFilterAfterRunner(command, runs, occurrence)`
1587
1716
 
1588
1717
  The output filter that follows the runner in `command`, or null when there is none.
1589
1718
 
1590
- Only a stage of the runner's OWN pipeline counts (its separator is `|`), and only when it is the
1591
- LAST stage of that pipeline: a `| grep … | wc -l` chain would leave a dangling pipe if the
1592
- middle stage were cut out, so a chain is not answered here at all. A filter before the runner
1593
- (`cat run.log | tail -20 && hurl …`) reads something else's output and is not this.
1719
+ Only stages of the runner's OWN pipeline count (their separator is `|`), and they are read as
1720
+ one trailing CHAIN: `| grep … | head -40` is two cuts, and removing only the first would leave
1721
+ the run piped into the second. Every stage after the runner must be a cutting filter and the
1722
+ chain must run to the pipeline's end — a pipeline that continues into `wc -l` is not answered
1723
+ here, because removing the cutting stages alone would still leave the failure hidden. A filter
1724
+ before the runner (`cat run.log | tail -20 && hurl …`) reads something else's output and is not
1725
+ this.
1594
1726
 
1595
1727
  ```typescript
1596
1728
  function outputFilterAfterRunner(
1597
1729
  command: string,
1598
1730
  runs: (segment: CommandSegment) => boolean,
1731
+ occurrence?: number,
1599
1732
  ): RunnerOutputFilter | null
1600
1733
  ```
1601
1734
 
1602
1735
  - `command` — The command as the executor sent it.
1603
1736
  - `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).
1737
+ - `occurrence` — Which runner invocation the output belongs to, 1-based. A command can run the same runner more than once (xr-molecule-flash-m170: the suite bare, then with `HOST=…`), and each invocation carries its OWN pipe; a reader judging the second walk must name the second walk's cut, never the first's.
1604
1738
 
1605
1739
  **Returns:** The filter and the unfiltered command, or null.
1606
1740
 
@@ -1628,6 +1762,56 @@ function pipelineExitCodeNote(command: string, exitCode: number, output: string)
1628
1762
 
1629
1763
  **Returns:** One sentence naming the stage the code came from, or null.
1630
1764
 
1765
+ #### `startsServerProgram(command)`
1766
+
1767
+ Whether a command, read from its TEXT alone, starts a server that stays up in the foreground.
1768
+
1769
+ The over-budget reader's shape: at that point the command has already run, and only its text is
1770
+ left to say whether "run a smaller unit" was ever the right advice.
1771
+
1772
+ ```typescript
1773
+ function startsServerProgram(command: string): boolean
1774
+ ```
1775
+
1776
+ - `command` — The command as the executor sent it.
1777
+
1778
+ **Returns:** True when a foreground statement of the command starts a server.
1779
+
1780
+ #### `foregroundServerCommand(command, readScript)`
1781
+
1782
+ A FOREGROUND command that starts a server, including one that only names a script whose SOURCE
1783
+ imports the app's server and calls its `create()`/`listen()`.
1784
+
1785
+ The script read is best-effort: a path the backend will not answer for is a command this reading
1786
+ cannot classify, never a refusal. A backgrounded launch, a test runner and a test file are all
1787
+ left alone.
1788
+
1789
+ ```typescript
1790
+ function foregroundServerCommand(
1791
+ command: string,
1792
+ readScript: (path: string) => Promise<string>,
1793
+ ): Promise<ForegroundServer | null>
1794
+ ```
1795
+
1796
+ - `command` — The command as the executor sent it.
1797
+ - `readScript` — Reads a script file the command names (`backend.readFile`).
1798
+
1799
+ **Returns:** The serving program, or null when the command starts no server in the foreground.
1800
+
1801
+ #### `serverCommandRefusal(server, budgetSeconds)`
1802
+
1803
+ The refusal for a foreground command that starts a server: why it would never return, and the
1804
+ two shapes that do work — a background launch, or a probe of the API already running.
1805
+
1806
+ ```typescript
1807
+ function serverCommandRefusal(server: ForegroundServer, budgetSeconds: number): string
1808
+ ```
1809
+
1810
+ - `server` — The serving program, from {@link foregroundServerCommand}.
1811
+ - `budgetSeconds` — The tool's budget, which a foreground server would consume whole.
1812
+
1813
+ **Returns:** The refusal sentence.
1814
+
1631
1815
  #### `leadingSleepPeek(command)`
1632
1816
 
1633
1817
  The leading `sleep` of a command, when the first statement is one and it asks for at least
@@ -1724,6 +1908,10 @@ invocation — same directory, same settings — with the named `file:line`s in
1724
1908
  suite, `--last-failed` dropped (it re-runs the tests that never ran too, X1 flash-m60) and
1725
1909
  `--retries=0` set (a re-run of a known failure does not need to pay for retries).
1726
1910
 
1911
+ The re-run keeps the run's `> <file>` redirect only in the sense of keeping its OUTPUT there:
1912
+ the target is rewritten to a fresh sibling ({@link redirectToFreshFile}), so a re-run never
1913
+ overwrites the log the run itself is the evidence for (xr-molecule-flash-m173).
1914
+
1727
1915
  `config` is the config the run was measured under ({@link withSuiteConfig}): the narrowed re-run
1728
1916
  carries it whenever the command itself does not, so a run that dropped `--config` cannot be
1729
1917
  handed back a command with the same defect (xr-molecule-flash-m113: every failing test was
@@ -1765,7 +1953,7 @@ function suiteRunInTheWay(candidate: { target: string }, inFlight: readonly T[])
1765
1953
 
1766
1954
  **Returns:** The first run in the way, or null.
1767
1955
 
1768
- #### `isDiagnosisRun(command)`
1956
+ #### `isDiagnosisRun(command, context)`
1769
1957
 
1770
1958
  Whether a command walks a PART of a suite instead of all of it: a cap that stops it at a failure
1771
1959
  count, the failures the last run printed (`--last-failed`, `--only-changed`), a title filter, or
@@ -1794,10 +1982,11 @@ absorb it).
1794
1982
  _(description truncated — read the full text in `node_modules/@molecule/api-ai-tools/dist/suite-overlap.d.ts`.)_
1795
1983
 
1796
1984
  ```typescript
1797
- function isDiagnosisRun(command: string): boolean
1985
+ function isDiagnosisRun(command: string, context?: DiagnosisRunContext): boolean
1798
1986
  ```
1799
1987
 
1800
1988
  - `command` — The command as the executor sent it.
1989
+ - `context` — The request's approved exclusions, when the caller knows them.
1801
1990
 
1802
1991
  **Returns:** True when the run is a diagnosis or a re-run, not the whole suite.
1803
1992
 
@@ -2061,6 +2250,25 @@ function stalledDocumentSubresource(request: TraceRequest): boolean
2061
2250
 
2062
2251
  **Returns:** True when the request is a document subresource that never finished.
2063
2252
 
2253
+ #### `isCredentialExchangePath(path, authEndpoints)`
2254
+
2255
+ Whether a request PATH asks the app to mint a credential — a login, a registration, a token
2256
+ endpoint — as opposed to a protected resource that presents one.
2257
+
2258
+ This is the path half of {@link isCredentialExchange}, exported so a caller holding only a
2259
+ `METHOD path` reads the same classification: a Hurl failing entry says
2260
+ `POST /api/users/login` and nothing about the body, and the whole-suite note must not explain
2261
+ a wrong-password login that answered 200 as a cookie-jar effect (xr-molecule-flash-m176).
2262
+
2263
+ ```typescript
2264
+ function isCredentialExchangePath(path: string, authEndpoints?: readonly string[]): boolean
2265
+ ```
2266
+
2267
+ - `path` — The request path (`/api/users/login`); a query or fragment is ignored.
2268
+ - `authEndpoints` — The endpoints the app's auth config names (`/users/login`).
2269
+
2270
+ **Returns:** True when the path names a credential exchange.
2271
+
2064
2272
  #### `failureTraceNote(items, target, earlierFailures, apiPrefixes, report)`
2065
2273
 
2066
2274
  The note a failed suite run's result carries: what each failing test's page actually sent, and
@@ -2126,15 +2334,21 @@ function suiteRecordsTraces(configText: string): boolean
2126
2334
 
2127
2335
  #### `diagnosisTraceCommand(args)`
2128
2336
 
2129
- The command a DIAGNOSIS run needs to leave a readable trace behind, when the suite's config
2337
+ The command a Playwright run needs to leave a readable trace behind, when the suite's config
2130
2338
  would record none.
2131
2339
 
2132
2340
  The failing tests' requests are only readable if the run wrote them down (see the module doc),
2133
2341
  and the config is where the decision about that lives — the platform does not edit the suite's
2134
- config, so it passes the flag instead, and only where it can do no harm: on a run narrowed to a
2135
- part of the suite or capped ({@link isDiagnosisRun}), never on the whole-suite proof run, and
2136
- never over a `--trace` the executor wrote itself. A capped walk is exactly the run whose
2137
- failures someone is about to work on; the proof run is graded as it is.
2342
+ config, so it passes the flag instead, on every Playwright run that starts tests and does not
2343
+ already decide its own `--trace`. That includes the whole-suite proof run, which an earlier
2344
+ version of this function skipped on the reasoning that its flags are graded: the flag changes
2345
+ what is RETAINED, not what passes. `retain-on-failure` keeps the trace only of a test that
2346
+ fails — Playwright records it and deletes it when the test passes (playwright's
2347
+ `_shouldAbandonTrace` unlinks the temporary trace files of a passing test) — so a green proof
2348
+ run keeps nothing extra. A failing one keeps the network log its failures need.
2349
+ xr-molecule-flash-m163 (+24:58) is why the proof run is no longer skipped: its one deployed
2350
+ failure left only an `error-context.md`, the executor spent the next calls hunting a `trace.zip`
2351
+ that could not exist, and then shipped a guessed fix.
2138
2352
 
2139
2353
  ```typescript
2140
2354
  function diagnosisTraceCommand(args: {
@@ -2310,7 +2524,7 @@ function outputRedirectTarget(command: string): string | null
2310
2524
 
2311
2525
  **Returns:** The target, or null (`/dev/null` and `&1`/`&2` are not files).
2312
2526
 
2313
- #### `streamingBackgroundCommand(command)`
2527
+ #### `streamingBackgroundCommand(command, context)`
2314
2528
 
2315
2529
  Rewrite a command about to run in the background so its log grows while it
2316
2530
  runs. Two shapes left the log empty until exit, and an empty log is what
@@ -2336,14 +2550,14 @@ into the log twice, which on X1 flash-m83 (2026-10-16) made a `--reporter=list,l
2336
2550
  _(description truncated — read the full text in `node_modules/@molecule/api-ai-tools/dist/utilities.d.ts`.)_
2337
2551
 
2338
2552
  ```typescript
2339
- function streamingBackgroundCommand(command: string): {
2340
- command: string
2341
- changes: string[]
2342
- error?: string
2343
- }
2553
+ function streamingBackgroundCommand(
2554
+ command: string,
2555
+ context?: DiagnosisRunContext,
2556
+ ): { command: string; changes: string[]; error?: string }
2344
2557
  ```
2345
2558
 
2346
2559
  - `command` — The command as the executor sent it.
2560
+ - `context` — What the caller knows beyond the command: the tests the person's request excludes by name ({@link DiagnosisRunContext}). A `--grep-invert` of exactly those is the request's own scope, not a diagnosis, so it keeps the suite's retries on a deployed proof (xr-molecule-flash-m170 +33:16).
2347
2561
 
2348
2562
  **Returns:** The command to run and one sentence per change made (empty when none), or `error` when the command starts the suite more than once.
2349
2563
 
@@ -2381,6 +2595,61 @@ function isSuiteRunCommand(command: string): boolean
2381
2595
 
2382
2596
  **Returns:** True when the command starts one of those runners.
2383
2597
 
2598
+ #### `repeatedSuiteRunError(command)`
2599
+
2600
+ The refusal for a command that starts the suite MORE THAN ONCE — the ONE reading every launch
2601
+ site asks, foreground and background alike.
2602
+
2603
+ Each statement is a whole walk of the same suite against the same app, and only the last of them
2604
+ is rewritten, so the earlier ones measure nothing the first did. X1 xr-molecule-flash-m157
2605
+ (+14:02) chained two whole walks of one 138-test suite with `;` (4.5 minutes each) and the
2606
+ background launcher refused it; X1 xr-molecule-flash-m170 (+06:02) chained two invocations of the
2607
+ suite's own runner script in the foreground, where nothing counted them, and the note reported
2608
+ the passing second walk as the first walk's 13 failures.
2609
+
2610
+ The one run handed back is the LAST suite statement, rewritten exactly as
2611
+ {@link streamingBackgroundCommand} rewrites a run (a filter or `| tail` after the runner is
2612
+ dropped, and a Playwright run gets the reporter that prints failures). Its own count is one —
2613
+ guarded, so this cannot recurse.
2614
+
2615
+ ```typescript
2616
+ function repeatedSuiteRunError(command: string): string | null
2617
+ ```
2618
+
2619
+ - `command` — The shell command as the executor sent it.
2620
+
2621
+ **Returns:** The refusal sentence, or null when the command starts the suite zero or one time.
2622
+
2623
+ #### `suiteRunBudgetCommand(command, flags)`
2624
+
2625
+ A command with the supplied suite's own settings written back onto its runner as Playwright
2626
+ flags, and the flags that could not be placed.
2627
+
2628
+ The grader walks the suite under its OWN budget, so a run whose runner config sets a different
2629
+ timeout or worker count measures a run the grader never sees (xr-molecule-flash-m169: a 60 s
2630
+ wrapper config produced a green local proof the official 15 s grader then failed). Playwright
2631
+ lets a command-line flag win over every config file, so the suite's values are put on the
2632
+ runner's own arguments — written where {@link statementArgumentEnd} places them, never after a
2633
+ pipe, redirect or group closer. Only the command's LAST suite step is touched: that is the run
2634
+ every launch site measures.
2635
+
2636
+ A flag the command already carries is REWRITTEN in place (last occurrence wins, as it does for
2637
+ Playwright); a flag it does not carry is inserted at the end of the runner's arguments. A
2638
+ command whose runner cannot be placed is returned exactly as written, with every flag reported
2639
+ as unplaced, so the caller states the fact instead of claiming a pin it did not make.
2640
+
2641
+ ```typescript
2642
+ function suiteRunBudgetCommand(
2643
+ command: string,
2644
+ flags: readonly string[],
2645
+ ): { command: string; placed: string[]; unplaced: string[] }
2646
+ ```
2647
+
2648
+ - `command` — The shell command about to run.
2649
+ - `flags` — The flags to put on it, each as `--name=value`.
2650
+
2651
+ **Returns:** The command to run, the flags placed on it, and the ones it could not carry.
2652
+
2384
2653
  #### `withoutSuiteTimeout(command)`
2385
2654
 
2386
2655
  The command with the `timeout <N>` wrapper lifted off the statement that runs the suite, and the
@@ -3323,6 +3592,35 @@ Canonical tool schemas shared by the agent runtime and documentation.
3323
3592
  const TOOL_SCHEMAS: Record<string, ToolSchema>
3324
3593
  ```
3325
3594
 
3595
+ #### `BACKGROUNDED`
3596
+
3597
+ A background launch: a single `&` (not `&&`, not a `2>&1`) or an explicit detacher. A
3598
+ backgrounded launch never holds the tool, so every reading here stops at it.
3599
+
3600
+ ```typescript
3601
+ const BACKGROUNDED: RegExp
3602
+ ```
3603
+
3604
+ #### `SERVER_PROGRAM`
3605
+
3606
+ A program that SERVES: the scaffold's entry (`server.ts`/`server.js`), a package manager's
3607
+ `start`/`dev`/`serve`/`preview` script, a Vite dev or preview server, `next start|dev`, or a
3608
+ Python WSGI server. Matched against a command segment's own text, and against a script's SOURCE
3609
+ when the command only names the script file.
3610
+
3611
+ ```typescript
3612
+ const SERVER_PROGRAM: RegExp
3613
+ ```
3614
+
3615
+ #### `SERVER_OVER_BUDGET_ADVICE`
3616
+
3617
+ The closing advice on an over-budget message for a command recognised as a server start: there
3618
+ is no tally to split, and the run has to leave the foreground budget to keep serving.
3619
+
3620
+ ```typescript
3621
+ const SERVER_OVER_BUDGET_ADVICE: string
3622
+ ```
3623
+
3326
3624
  #### `MIN_PEEK_SLEEP_SECONDS`
3327
3625
 
3328
3626
  The shortest leading sleep the platform drops: below it the wall clock it costs is noise, and a
@@ -3771,11 +4069,22 @@ const UNIVERSAL_CAUSE_FILES: 3
3771
4069
  #### `EMPTY_PAGE_CAUSE_TESTS`
3772
4070
 
3773
4071
  How many tests a run must have walked — with nothing passing since its first failure — before
3774
- "the page renders nothing" can be its one cause. A handful of failures is a handful of bugs;
3775
- a page that never painted fails every spec that reaches it, and m141's walk had 36 by +20:30.
3776
-
3777
- ```typescript
3778
- const EMPTY_PAGE_CAUSE_TESTS: 15
4072
+ "the page renders nothing" can be its one cause.
4073
+
4074
+ THREE, and the number is the cost of the alternative. The old floor of 15 was chosen when the
4075
+ reading was only offered at a completed run; once it STOPS the walk, every test waited for is
4076
+ a test whose result is thrown away, and a blank page costs its per-test budget each time
4077
+ (m177: 18 tests, ~4.2 minutes, on a page whose thrown error was visible in the first
4078
+ `pageerror`). Three is the smallest count that is not one page's own bug: the cause additionally
4079
+ needs three DISTINCT spec files (`UNIVERSAL_CAUSE_FILES`, or the files a narrowed run selected),
4080
+ each spec waiting for its OWN first element, and no test passing after the first failure. A page
4081
+ that renders misses elements, but three unrelated specs in three files failing at three
4082
+ different first locators with nothing passing in between is the page, not three bugs. At the
4083
+ suite's own 5 s action timeout that verdict lands ~20 s into the walk, so the early stop costs
4084
+ about one test's worth of time instead of fifteen.
4085
+
4086
+ ```typescript
4087
+ const EMPTY_PAGE_CAUSE_TESTS: 3
3779
4088
  ```
3780
4089
 
3781
4090
  #### `CONNECTION_CAUSE_TESTS`
@@ -36,7 +36,9 @@
36
36
  export interface CommandSegment {
37
37
  /**
38
38
  * The word in command position, its directory removed (`/usr/bin/hurl` → `hurl`): the program
39
- * the shell would run once whatever wrappers stood in front of it are taken away.
39
+ * the shell would run once whatever wrappers stood in front of it are taken away. When a
40
+ * wrapper runs a tool's own CLI by file, this is the tool's name — `node
41
+ * <path>/node_modules/.bin/playwright test` is a `playwright` invocation, not a `node` one.
40
42
  */
41
43
  program: string;
42
44
  /** The words after it, as written — quotes kept, so a quoted `"a|b"` is one word. */
@@ -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;;;;;;;;;;;;;;;;;;;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"}
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;;;;;OAKG;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;AA8RD;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAM3D;AAwkBD;;;;;;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;AA2FD;;;;;;;;;;;;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"}