@molecule/api-ai-tools 1.9.16 → 1.9.18

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-18T15:48:52.035Z
7
7
  -->
8
8
 
9
9
  # @molecule/api-ai-tools
@@ -309,6 +309,17 @@ interface ToolSchema {
309
309
  }
310
310
  ```
311
311
 
312
+ #### `ForegroundServer`
313
+
314
+ A foreground command that starts a server, as the refusal names it.
315
+
316
+ ```typescript
317
+ interface ForegroundServer {
318
+ /** The program (and script, when there is one) that serves. */
319
+ readonly program: string
320
+ }
321
+ ```
322
+
312
323
  #### `LeadingSleep`
313
324
 
314
325
  The leading `sleep` a command opens with, and the command that follows it.
@@ -345,6 +356,20 @@ interface SuiteRunInFlight {
345
356
  }
346
357
  ```
347
358
 
359
+ #### `DiagnosisRunContext`
360
+
361
+ What a caller knows about the run being judged beyond its command text.
362
+
363
+ ```typescript
364
+ interface DiagnosisRunContext {
365
+ /**
366
+ * The test titles the person's request excludes by name — the same reading molecule-dev's
367
+ * `excludedTestTitles` gives the rest of the platform.
368
+ */
369
+ approvedExclusions?: readonly string[]
370
+ }
371
+ ```
372
+
348
373
  #### `InstalledPackageFacts`
349
374
 
350
375
  What the filesystem says about a write target under `node_modules` — the half
@@ -512,6 +537,30 @@ interface TraceContextEvidence {
512
537
  }
513
538
  ```
514
539
 
540
+ #### `TraceMissingText`
541
+
542
+ The text a failing assertion waited for that a same-page API response carried and the page never
543
+ rendered.
544
+
545
+ The assertion's own error names the text (a `has-text(…)` / `getByText(…)` / `toContainText(…)`
546
+ argument), and the trace's network log holds the response that contained it. That pair is the
547
+ whole diagnosis: the API returned the value and the page dropped it before rendering, so the cut
548
+ is in the component that maps the response onto the list — not in the API, the route or the
549
+ selector. xr-molecule-flash-m169 (+13:09) spent its fix rounds on the wrong component and on a
550
+ markup detail while `GET /api/tags` was returning the missing "trending" and `PopularTags`
551
+ sliced the response to its first 20 entries (the m99 top-N class, in the frontend).
552
+
553
+ ```typescript
554
+ interface TraceMissingText {
555
+ /** The text the assertion waited for (`trending`). */
556
+ text: string
557
+ /** The method of the response that carried it (`GET`). */
558
+ method: string
559
+ /** The path of the response that carried it (`/api/tags`). */
560
+ path: string
561
+ }
562
+ ```
563
+
515
564
  #### `TraceArtifact`
516
565
 
517
566
  What one trace says: the test it belongs to, the requests it made, and its console lines.
@@ -581,6 +630,17 @@ interface TraceArtifact {
581
630
  * and on traces read before this field existed.
582
631
  */
583
632
  otherContexts?: TraceContextEvidence[]
633
+ /**
634
+ * The text the failing assertion waited for and a same-page API response carried, when the trace
635
+ * recorded both. The note leads with it: the value reached the browser and the page did not
636
+ * render it, so the next read is where that response is mapped — never the API or the selector
637
+ * (xr-molecule-flash-m169).
638
+ *
639
+ * Absent when the assertion waited for no text (a text-free selector, a strict-mode violation),
640
+ * when no page-scoped `fetch`/`xhr` answered 2xx/3xx with the text in its body, and on traces
641
+ * read before this field existed.
642
+ */
643
+ missingText?: TraceMissingText
584
644
  }
585
645
  ```
586
646
 
@@ -627,6 +687,14 @@ interface TraceRunReport {
627
687
  * contradicts it.
628
688
  */
629
689
  productionBuildAdvised?: boolean
690
+ /**
691
+ * The endpoints the app's own auth config names for a credential exchange — a login, a
692
+ * registration, a token endpoint (`loginEndpoint: '/users/login'`, resolved from the app's
693
+ * source). A 401/403 there answers credentials the app was ASKED to check, never a token the
694
+ * client failed to attach, so the note says so even when the path is mounted somewhere unusual.
695
+ * Empty when the caller could not read the app's config.
696
+ */
697
+ authEndpoints?: readonly string[]
630
698
  }
631
699
  ```
632
700
 
@@ -695,6 +763,33 @@ interface ExecutionBackend {
695
763
  }
696
764
  ```
697
765
 
766
+ #### `SuiteBudgetPin`
767
+
768
+ The supplied suite's own settings, put back on a run whose runner config changes them.
769
+
770
+ A graded suite is walked by the grader under its own per-test budget, worker count and
771
+ timeouts. An executor's run that reads a config setting different numbers measures a run the
772
+ grader never sees: a shorter budget fails tests the graded walk passes, a longer one passes
773
+ tests the graded walk times out on. The platform that knows the suite supplies these values
774
+ before the run ({@link ToolBuildConfig.suiteBudget}); the shared tool puts the flags on the
775
+ runner's own command line, where Playwright lets them win over every config file.
776
+
777
+ ```typescript
778
+ interface SuiteBudgetPin {
779
+ /**
780
+ * The Playwright flags that carry the suite's own settings onto the command
781
+ * (`--timeout=15000`, `--workers=1`), as the consumer's own scan reads them. Empty when the
782
+ * difference is in settings no flag carries — the pin is then the sentence alone.
783
+ */
784
+ flags: string[]
785
+ /**
786
+ * One sentence for the run's result: this run uses the suite's own budget, and which settings
787
+ * a config keeps because Playwright has no flag for them.
788
+ */
789
+ note: string
790
+ }
791
+ ```
792
+
698
793
  #### `ToolBuildConfig`
699
794
 
700
795
  Configuration for building the tool set.
@@ -802,6 +897,23 @@ interface ToolBuildConfig {
802
897
  */
803
898
  excludedTestTitles?: () => readonly string[]
804
899
 
900
+ /**
901
+ * The supplied suite's own budget, when the Playwright config a command would load sets
902
+ * different settings, asked BEFORE the run executes. The consumer knows where the suite and its
903
+ * base config live; this shared package does not, so the reading is the consumer's and the
904
+ * mechanics — putting the flags on the runner's own arguments, before the pipeline and outside
905
+ * any redirect or group closer — are this tool's.
906
+ *
907
+ * Called for a Playwright suite command only, and only the LAST suite step is touched. A run
908
+ * that already keeps the suite's settings, or a sandbox with no supplied suite, answers `null`.
909
+ * Unset (or a hook that throws) changes nothing: the command runs as the model wrote it.
910
+ *
911
+ * @param command - The shell command, as the model sent it.
912
+ * @param cwd - The resolved working directory it will run in.
913
+ * @returns The flags and the sentence, or null when nothing is put back.
914
+ */
915
+ suiteBudget?: (command: string, cwd: string) => Promise<SuiteBudgetPin | null | undefined>
916
+
805
917
  /** Post-write hook (e.g. auto-format via Prettier/ESLint). Called after every write_file/edit_file. */
806
918
  onAfterWrite?: (path: string) => Promise<void>
807
919
 
@@ -1583,7 +1695,7 @@ function commandRunsNoTests(command: string): boolean
1583
1695
 
1584
1696
  **Returns:** True when the command invokes a runner only to print.
1585
1697
 
1586
- #### `outputFilterAfterRunner(command, runs)`
1698
+ #### `outputFilterAfterRunner(command, runs, occurrence)`
1587
1699
 
1588
1700
  The output filter that follows the runner in `command`, or null when there is none.
1589
1701
 
@@ -1596,11 +1708,13 @@ middle stage were cut out, so a chain is not answered here at all. A filter befo
1596
1708
  function outputFilterAfterRunner(
1597
1709
  command: string,
1598
1710
  runs: (segment: CommandSegment) => boolean,
1711
+ occurrence?: number,
1599
1712
  ): RunnerOutputFilter | null
1600
1713
  ```
1601
1714
 
1602
1715
  - `command` — The command as the executor sent it.
1603
1716
  - `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).
1717
+ - `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
1718
 
1605
1719
  **Returns:** The filter and the unfiltered command, or null.
1606
1720
 
@@ -1628,6 +1742,56 @@ function pipelineExitCodeNote(command: string, exitCode: number, output: string)
1628
1742
 
1629
1743
  **Returns:** One sentence naming the stage the code came from, or null.
1630
1744
 
1745
+ #### `startsServerProgram(command)`
1746
+
1747
+ Whether a command, read from its TEXT alone, starts a server that stays up in the foreground.
1748
+
1749
+ The over-budget reader's shape: at that point the command has already run, and only its text is
1750
+ left to say whether "run a smaller unit" was ever the right advice.
1751
+
1752
+ ```typescript
1753
+ function startsServerProgram(command: string): boolean
1754
+ ```
1755
+
1756
+ - `command` — The command as the executor sent it.
1757
+
1758
+ **Returns:** True when a foreground statement of the command starts a server.
1759
+
1760
+ #### `foregroundServerCommand(command, readScript)`
1761
+
1762
+ A FOREGROUND command that starts a server, including one that only names a script whose SOURCE
1763
+ imports the app's server and calls its `create()`/`listen()`.
1764
+
1765
+ The script read is best-effort: a path the backend will not answer for is a command this reading
1766
+ cannot classify, never a refusal. A backgrounded launch, a test runner and a test file are all
1767
+ left alone.
1768
+
1769
+ ```typescript
1770
+ function foregroundServerCommand(
1771
+ command: string,
1772
+ readScript: (path: string) => Promise<string>,
1773
+ ): Promise<ForegroundServer | null>
1774
+ ```
1775
+
1776
+ - `command` — The command as the executor sent it.
1777
+ - `readScript` — Reads a script file the command names (`backend.readFile`).
1778
+
1779
+ **Returns:** The serving program, or null when the command starts no server in the foreground.
1780
+
1781
+ #### `serverCommandRefusal(server, budgetSeconds)`
1782
+
1783
+ The refusal for a foreground command that starts a server: why it would never return, and the
1784
+ two shapes that do work — a background launch, or a probe of the API already running.
1785
+
1786
+ ```typescript
1787
+ function serverCommandRefusal(server: ForegroundServer, budgetSeconds: number): string
1788
+ ```
1789
+
1790
+ - `server` — The serving program, from {@link foregroundServerCommand}.
1791
+ - `budgetSeconds` — The tool's budget, which a foreground server would consume whole.
1792
+
1793
+ **Returns:** The refusal sentence.
1794
+
1631
1795
  #### `leadingSleepPeek(command)`
1632
1796
 
1633
1797
  The leading `sleep` of a command, when the first statement is one and it asks for at least
@@ -1765,7 +1929,7 @@ function suiteRunInTheWay(candidate: { target: string }, inFlight: readonly T[])
1765
1929
 
1766
1930
  **Returns:** The first run in the way, or null.
1767
1931
 
1768
- #### `isDiagnosisRun(command)`
1932
+ #### `isDiagnosisRun(command, context)`
1769
1933
 
1770
1934
  Whether a command walks a PART of a suite instead of all of it: a cap that stops it at a failure
1771
1935
  count, the failures the last run printed (`--last-failed`, `--only-changed`), a title filter, or
@@ -1794,10 +1958,11 @@ absorb it).
1794
1958
  _(description truncated — read the full text in `node_modules/@molecule/api-ai-tools/dist/suite-overlap.d.ts`.)_
1795
1959
 
1796
1960
  ```typescript
1797
- function isDiagnosisRun(command: string): boolean
1961
+ function isDiagnosisRun(command: string, context?: DiagnosisRunContext): boolean
1798
1962
  ```
1799
1963
 
1800
1964
  - `command` — The command as the executor sent it.
1965
+ - `context` — The request's approved exclusions, when the caller knows them.
1801
1966
 
1802
1967
  **Returns:** True when the run is a diagnosis or a re-run, not the whole suite.
1803
1968
 
@@ -2126,15 +2291,21 @@ function suiteRecordsTraces(configText: string): boolean
2126
2291
 
2127
2292
  #### `diagnosisTraceCommand(args)`
2128
2293
 
2129
- The command a DIAGNOSIS run needs to leave a readable trace behind, when the suite's config
2294
+ The command a Playwright run needs to leave a readable trace behind, when the suite's config
2130
2295
  would record none.
2131
2296
 
2132
2297
  The failing tests' requests are only readable if the run wrote them down (see the module doc),
2133
2298
  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.
2299
+ config, so it passes the flag instead, on every Playwright run that starts tests and does not
2300
+ already decide its own `--trace`. That includes the whole-suite proof run, which an earlier
2301
+ version of this function skipped on the reasoning that its flags are graded: the flag changes
2302
+ what is RETAINED, not what passes. `retain-on-failure` keeps the trace only of a test that
2303
+ fails — Playwright records it and deletes it when the test passes (playwright's
2304
+ `_shouldAbandonTrace` unlinks the temporary trace files of a passing test) — so a green proof
2305
+ run keeps nothing extra. A failing one keeps the network log its failures need.
2306
+ xr-molecule-flash-m163 (+24:58) is why the proof run is no longer skipped: its one deployed
2307
+ failure left only an `error-context.md`, the executor spent the next calls hunting a `trace.zip`
2308
+ that could not exist, and then shipped a guessed fix.
2138
2309
 
2139
2310
  ```typescript
2140
2311
  function diagnosisTraceCommand(args: {
@@ -2310,7 +2481,7 @@ function outputRedirectTarget(command: string): string | null
2310
2481
 
2311
2482
  **Returns:** The target, or null (`/dev/null` and `&1`/`&2` are not files).
2312
2483
 
2313
- #### `streamingBackgroundCommand(command)`
2484
+ #### `streamingBackgroundCommand(command, context)`
2314
2485
 
2315
2486
  Rewrite a command about to run in the background so its log grows while it
2316
2487
  runs. Two shapes left the log empty until exit, and an empty log is what
@@ -2336,14 +2507,14 @@ into the log twice, which on X1 flash-m83 (2026-10-16) made a `--reporter=list,l
2336
2507
  _(description truncated — read the full text in `node_modules/@molecule/api-ai-tools/dist/utilities.d.ts`.)_
2337
2508
 
2338
2509
  ```typescript
2339
- function streamingBackgroundCommand(command: string): {
2340
- command: string
2341
- changes: string[]
2342
- error?: string
2343
- }
2510
+ function streamingBackgroundCommand(
2511
+ command: string,
2512
+ context?: DiagnosisRunContext,
2513
+ ): { command: string; changes: string[]; error?: string }
2344
2514
  ```
2345
2515
 
2346
2516
  - `command` — The command as the executor sent it.
2517
+ - `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
2518
 
2348
2519
  **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
2520
 
@@ -2381,6 +2552,61 @@ function isSuiteRunCommand(command: string): boolean
2381
2552
 
2382
2553
  **Returns:** True when the command starts one of those runners.
2383
2554
 
2555
+ #### `repeatedSuiteRunError(command)`
2556
+
2557
+ The refusal for a command that starts the suite MORE THAN ONCE — the ONE reading every launch
2558
+ site asks, foreground and background alike.
2559
+
2560
+ Each statement is a whole walk of the same suite against the same app, and only the last of them
2561
+ is rewritten, so the earlier ones measure nothing the first did. X1 xr-molecule-flash-m157
2562
+ (+14:02) chained two whole walks of one 138-test suite with `;` (4.5 minutes each) and the
2563
+ background launcher refused it; X1 xr-molecule-flash-m170 (+06:02) chained two invocations of the
2564
+ suite's own runner script in the foreground, where nothing counted them, and the note reported
2565
+ the passing second walk as the first walk's 13 failures.
2566
+
2567
+ The one run handed back is the LAST suite statement, rewritten exactly as
2568
+ {@link streamingBackgroundCommand} rewrites a run (a filter or `| tail` after the runner is
2569
+ dropped, and a Playwright run gets the reporter that prints failures). Its own count is one —
2570
+ guarded, so this cannot recurse.
2571
+
2572
+ ```typescript
2573
+ function repeatedSuiteRunError(command: string): string | null
2574
+ ```
2575
+
2576
+ - `command` — The shell command as the executor sent it.
2577
+
2578
+ **Returns:** The refusal sentence, or null when the command starts the suite zero or one time.
2579
+
2580
+ #### `suiteRunBudgetCommand(command, flags)`
2581
+
2582
+ A command with the supplied suite's own settings written back onto its runner as Playwright
2583
+ flags, and the flags that could not be placed.
2584
+
2585
+ The grader walks the suite under its OWN budget, so a run whose runner config sets a different
2586
+ timeout or worker count measures a run the grader never sees (xr-molecule-flash-m169: a 60 s
2587
+ wrapper config produced a green local proof the official 15 s grader then failed). Playwright
2588
+ lets a command-line flag win over every config file, so the suite's values are put on the
2589
+ runner's own arguments — written where {@link statementArgumentEnd} places them, never after a
2590
+ pipe, redirect or group closer. Only the command's LAST suite step is touched: that is the run
2591
+ every launch site measures.
2592
+
2593
+ A flag the command already carries is REWRITTEN in place (last occurrence wins, as it does for
2594
+ Playwright); a flag it does not carry is inserted at the end of the runner's arguments. A
2595
+ command whose runner cannot be placed is returned exactly as written, with every flag reported
2596
+ as unplaced, so the caller states the fact instead of claiming a pin it did not make.
2597
+
2598
+ ```typescript
2599
+ function suiteRunBudgetCommand(
2600
+ command: string,
2601
+ flags: readonly string[],
2602
+ ): { command: string; placed: string[]; unplaced: string[] }
2603
+ ```
2604
+
2605
+ - `command` — The shell command about to run.
2606
+ - `flags` — The flags to put on it, each as `--name=value`.
2607
+
2608
+ **Returns:** The command to run, the flags placed on it, and the ones it could not carry.
2609
+
2384
2610
  #### `withoutSuiteTimeout(command)`
2385
2611
 
2386
2612
  The command with the `timeout <N>` wrapper lifted off the statement that runs the suite, and the
@@ -3323,6 +3549,35 @@ Canonical tool schemas shared by the agent runtime and documentation.
3323
3549
  const TOOL_SCHEMAS: Record<string, ToolSchema>
3324
3550
  ```
3325
3551
 
3552
+ #### `BACKGROUNDED`
3553
+
3554
+ A background launch: a single `&` (not `&&`, not a `2>&1`) or an explicit detacher. A
3555
+ backgrounded launch never holds the tool, so every reading here stops at it.
3556
+
3557
+ ```typescript
3558
+ const BACKGROUNDED: RegExp
3559
+ ```
3560
+
3561
+ #### `SERVER_PROGRAM`
3562
+
3563
+ A program that SERVES: the scaffold's entry (`server.ts`/`server.js`), a package manager's
3564
+ `start`/`dev`/`serve`/`preview` script, a Vite dev or preview server, `next start|dev`, or a
3565
+ Python WSGI server. Matched against a command segment's own text, and against a script's SOURCE
3566
+ when the command only names the script file.
3567
+
3568
+ ```typescript
3569
+ const SERVER_PROGRAM: RegExp
3570
+ ```
3571
+
3572
+ #### `SERVER_OVER_BUDGET_ADVICE`
3573
+
3574
+ The closing advice on an over-budget message for a command recognised as a server start: there
3575
+ is no tally to split, and the run has to leave the foreground budget to keep serving.
3576
+
3577
+ ```typescript
3578
+ const SERVER_OVER_BUDGET_ADVICE: string
3579
+ ```
3580
+
3326
3581
  #### `MIN_PEEK_SLEEP_SECONDS`
3327
3582
 
3328
3583
  The shortest leading sleep the platform drops: below it the wall clock it costs is noise, and a
package/dist/index.d.ts CHANGED
@@ -141,6 +141,7 @@ export * from './no-test-run.js';
141
141
  export * from './output-filter.js';
142
142
  export * from './pipeline-exit-note.js';
143
143
  export * from './schemas.js';
144
+ export * from './server-command.js';
144
145
  export * from './sleep-peek.js';
145
146
  export * from './suite-overlap.js';
146
147
  export * from './system-prompt.js';
@@ -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,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"}
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,qBAAqB,CAAA;AACnC,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
@@ -141,6 +141,7 @@ export * from './no-test-run.js';
141
141
  export * from './output-filter.js';
142
142
  export * from './pipeline-exit-note.js';
143
143
  export * from './schemas.js';
144
+ export * from './server-command.js';
144
145
  export * from './sleep-peek.js';
145
146
  export * from './suite-overlap.js';
146
147
  export * from './system-prompt.js';
@@ -42,7 +42,11 @@ export interface RunnerOutputFilter {
42
42
  * @param runs - Whether a segment is the runner this run is about (the caller knows its own kind:
43
43
  * {@link CommandSegment.program} is the program, once wrapper words like `timeout 280` and
44
44
  * `npx` are taken away).
45
+ * @param occurrence - Which runner invocation the output belongs to, 1-based. A command can run
46
+ * the same runner more than once (xr-molecule-flash-m170: the suite bare, then with `HOST=…`),
47
+ * and each invocation carries its OWN pipe; a reader judging the second walk must name the
48
+ * second walk's cut, never the first's.
45
49
  * @returns The filter and the unfiltered command, or null.
46
50
  */
47
- export declare function outputFilterAfterRunner(command: string, runs: (segment: CommandSegment) => boolean): RunnerOutputFilter | null;
51
+ export declare function outputFilterAfterRunner(command: string, runs: (segment: CommandSegment) => boolean, occurrence?: number): RunnerOutputFilter | null;
48
52
  //# sourceMappingURL=output-filter.d.ts.map
@@ -1 +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"}
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;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,OAAO,EAC1C,UAAU,SAAI,GACb,kBAAkB,GAAG,IAAI,CAsB3B"}
@@ -35,14 +35,23 @@ const CUTTING_FILTERS = new Set(['awk', 'cut', 'grep', 'head', 'sed', 'tail']);
35
35
  * @param runs - Whether a segment is the runner this run is about (the caller knows its own kind:
36
36
  * {@link CommandSegment.program} is the program, once wrapper words like `timeout 280` and
37
37
  * `npx` are taken away).
38
+ * @param occurrence - Which runner invocation the output belongs to, 1-based. A command can run
39
+ * the same runner more than once (xr-molecule-flash-m170: the suite bare, then with `HOST=…`),
40
+ * and each invocation carries its OWN pipe; a reader judging the second walk must name the
41
+ * second walk's cut, never the first's.
38
42
  * @returns The filter and the unfiltered command, or null.
39
43
  */
40
- export function outputFilterAfterRunner(command, runs) {
44
+ export function outputFilterAfterRunner(command, runs, occurrence = 1) {
41
45
  if (typeof command !== 'string' || command.trim() === '')
42
46
  return null;
43
47
  const entries = locatedCommandSegments(command);
44
- const runnerAt = entries.findIndex((entry) => entry.segment !== null && runs(entry.segment));
45
- if (runnerAt < 0 || runnerAt + 1 >= entries.length)
48
+ const runners = [];
49
+ entries.forEach((entry, index) => {
50
+ if (entry.segment !== null && runs(entry.segment))
51
+ runners.push(index);
52
+ });
53
+ const runnerAt = runners[occurrence - 1];
54
+ if (runnerAt === undefined || runnerAt + 1 >= entries.length)
46
55
  return null;
47
56
  const runner = entries[runnerAt];
48
57
  const filter = entries[runnerAt + 1];
@@ -0,0 +1,73 @@
1
+ /**
2
+ * "This command starts a server, and a server never exits" — ONE reading of a server launch,
3
+ * shared by the `exec_command` refusal, the over-budget advice, and the platform's own-server note
4
+ * (`molecule-dev/api/src/ai/own-server-note.ts` imports {@link SERVER_PROGRAM} and
5
+ * {@link BACKGROUNDED} from here), so every surface that names a server names the same commands.
6
+ *
7
+ * X1 xr-molecule-flash-m167 (+19:16 → +24:04) wrote `/tmp/probe.mts` whose body imports the app's
8
+ * `api/src/server.ts` and calls its `create(0)`, then ran it in the FOREGROUND:
9
+ * `npx tsx --env-file=.env /tmp/probe.mts 2>&1 | tail -15`. The scaffold's `create()` calls
10
+ * `server.listen(port)` and returns the server, so the process never exits — and behind `| tail`
11
+ * it printed nothing until it did. The tool waited its whole 290 s budget and answered with "run
12
+ * a smaller unit", advice about the wrong axis for a process that was never going to return.
13
+ *
14
+ * A server started in the background is a different thing: it is the launch the platform records
15
+ * and hands a task id for, and the over-budget advice below keeps it off the foreground budget.
16
+ *
17
+ * @module
18
+ */
19
+ /**
20
+ * A background launch: a single `&` (not `&&`, not a `2>&1`) or an explicit detacher. A
21
+ * backgrounded launch never holds the tool, so every reading here stops at it.
22
+ */
23
+ export declare const BACKGROUNDED: RegExp;
24
+ /**
25
+ * A program that SERVES: the scaffold's entry (`server.ts`/`server.js`), a package manager's
26
+ * `start`/`dev`/`serve`/`preview` script, a Vite dev or preview server, `next start|dev`, or a
27
+ * Python WSGI server. Matched against a command segment's own text, and against a script's SOURCE
28
+ * when the command only names the script file.
29
+ */
30
+ export declare const SERVER_PROGRAM: RegExp;
31
+ /**
32
+ * Whether a command, read from its TEXT alone, starts a server that stays up in the foreground.
33
+ *
34
+ * The over-budget reader's shape: at that point the command has already run, and only its text is
35
+ * left to say whether "run a smaller unit" was ever the right advice.
36
+ *
37
+ * @param command - The command as the executor sent it.
38
+ * @returns True when a foreground statement of the command starts a server.
39
+ */
40
+ export declare function startsServerProgram(command: string): boolean;
41
+ /** A foreground command that starts a server, as the refusal names it. */
42
+ export interface ForegroundServer {
43
+ /** The program (and script, when there is one) that serves. */
44
+ readonly program: string;
45
+ }
46
+ /**
47
+ * A FOREGROUND command that starts a server, including one that only names a script whose SOURCE
48
+ * imports the app's server and calls its `create()`/`listen()`.
49
+ *
50
+ * The script read is best-effort: a path the backend will not answer for is a command this reading
51
+ * cannot classify, never a refusal. A backgrounded launch, a test runner and a test file are all
52
+ * left alone.
53
+ *
54
+ * @param command - The command as the executor sent it.
55
+ * @param readScript - Reads a script file the command names (`backend.readFile`).
56
+ * @returns The serving program, or null when the command starts no server in the foreground.
57
+ */
58
+ export declare function foregroundServerCommand(command: string, readScript: (path: string) => Promise<string>): Promise<ForegroundServer | null>;
59
+ /**
60
+ * The refusal for a foreground command that starts a server: why it would never return, and the
61
+ * two shapes that do work — a background launch, or a probe of the API already running.
62
+ *
63
+ * @param server - The serving program, from {@link foregroundServerCommand}.
64
+ * @param budgetSeconds - The tool's budget, which a foreground server would consume whole.
65
+ * @returns The refusal sentence.
66
+ */
67
+ export declare function serverCommandRefusal(server: ForegroundServer, budgetSeconds: number): string;
68
+ /**
69
+ * The closing advice on an over-budget message for a command recognised as a server start: there
70
+ * is no tally to split, and the run has to leave the foreground budget to keep serving.
71
+ */
72
+ export declare const SERVER_OVER_BUDGET_ADVICE: string;
73
+ //# sourceMappingURL=server-command.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server-command.d.ts","sourceRoot":"","sources":["../src/server-command.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAMH;;;GAGG;AACH,eAAO,MAAM,YAAY,QAA6C,CAAA;AAEtE;;;;;GAKG;AACH,eAAO,MAAM,cAAc,QACmP,CAAA;AAmF9Q;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAI5D;AAED,0EAA0E;AAC1E,MAAM,WAAW,gBAAgB;IAC/B,+DAA+D;IAC/D,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CACzB;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,GAC5C,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAmBlC;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,GAAG,MAAM,CAO5F;AAED;;;GAGG;AACH,eAAO,MAAM,yBAAyB,QAG0B,CAAA"}