@molecule/api-ai-tools 1.6.4 → 1.6.6

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/dist/index.js CHANGED
@@ -51,6 +51,18 @@
51
51
  * `newlineNote` hint beside an unrelated-looking parse error, which an executor
52
52
  * repairs by rewriting the fused line's text and fuses the next one. The
53
53
  * `newlineNote` alone is still returned when the file parses.
54
+ * - **A replacement that only changes a line break at its boundary is refused,
55
+ * not applied.** When `new_string` is `old_string` with its trailing newline
56
+ * added or removed and nothing else, the edit can only move text across lines —
57
+ * joining the next line onto the replaced text, or pushing the rest of the line
58
+ * down — which is what a weak model does by accident when it repeats a line it
59
+ * means to keep (flash-m28, m55, m62, m89, five times in the m89 run alone). The
60
+ * replacement is skipped, the rest of the batch is applied, and the result
61
+ * carries `skippedNote`: one line naming the replacement, what applying it would
62
+ * have done, and how to say it on purpose (include the next line in `old_string`).
63
+ * The file's own next character decides: text on the same line is a join or a
64
+ * split and is refused; a blank line after it, or the end of the file, means no
65
+ * text crosses lines and the edit is applied as written.
54
66
  * - **A parameter under another name is not a broken tool.** Every tool's input
55
67
  * is normalized first (`cmd` → `command`, `file` → `path`, camelCase ⇄
56
68
  * snake_case), and a handler that throws returns an error naming the missing
@@ -121,9 +133,11 @@ export * from './backends/local.js';
121
133
  export * from './backends/sandbox.js';
122
134
  export * from './browser-guard.js';
123
135
  export * from './command-position.js';
136
+ export * from './cut-run.js';
124
137
  export * from './input-normalizer.js';
125
138
  export * from './no-test-run.js';
126
139
  export * from './schemas.js';
140
+ export * from './suite-overlap.js';
127
141
  export * from './system-prompt.js';
128
142
  export * from './tools.js';
129
143
  export * from './types.js';
@@ -0,0 +1,132 @@
1
+ /**
2
+ * The ONE rule that decides whether a suite run may start while another is already walking, and
3
+ * the command readings both sides of it need.
4
+ *
5
+ * Two sites ask "may a second run start now?" about the same pair of runs — the mid-run note that
6
+ * tells the executor what to do next while the suite it is waiting on is still going
7
+ * ({@link ./utilities.js} `repeatingFailureNote`), and the hold that refuses the run when it
8
+ * arrives (molecule-dev's `suite-overlap-hold.ts`) — and until xr-molecule-flash-m89 they answered
9
+ * it separately. That run's +19:59 `wait_for_task` handed over a repeating cause with "the test
10
+ * alone reproduces the first one … start on them now, while the run keeps walking"; 23 s later the
11
+ * executor sent exactly that re-run, and the overlap hold refused it — "A playwright suite is
12
+ * already running against this app and database … Wait for it." Two orders, one run, and the round
13
+ * trip between them was the whole cost.
14
+ *
15
+ * So the rule lives here ({@link suiteRunInTheWay}) and both of them call it: a run may start only
16
+ * when nothing in flight is already measuring the same app and database. What "the same app and
17
+ * database" MEANS lives here too ({@link suiteCommandTarget}) — the note and the hold have to key
18
+ * a command the same way before they can agree on whether two of them collide, and the re-run a
19
+ * note hands over is built from the same reading of the running command
20
+ * ({@link scopedRerunCommand}).
21
+ *
22
+ * The same treatment for the other pair that answered one question twice: whether a run walks the
23
+ * WHOLE suite or a part of it. The rewriter that decides whether a background Playwright run gets
24
+ * `--retries=0` ({@link ./utilities.js} `streamingBackgroundCommand`) and the platform's own notes
25
+ * (molecule-dev's `suite-cli-override.ts`) asked it separately — a regex there, a predicate here —
26
+ * until xr-molecule-flash-m89 showed the two answers were never the same question
27
+ * ({@link isDiagnosisRun}).
28
+ *
29
+ * @module
30
+ */
31
+ /**
32
+ * A command with its heredoc bodies removed: the runs, kills and hosts it names in a file it
33
+ * WRITES are not what the command itself does.
34
+ *
35
+ * The header is kept (`cat > run.sh <<'EOF' …EOF` leaves `cat > run.sh`), which is what the
36
+ * readers here want: everything before the body is still readable as the command's own words.
37
+ *
38
+ * @param command - The shell command.
39
+ * @returns The command without its heredoc bodies.
40
+ */
41
+ export declare function withoutHeredocs(command: string): string;
42
+ /**
43
+ * The server a suite command points at, as a comparable key: the host it names (`HOST=`,
44
+ * `BASE_URL=`, `PLAYWRIGHT_BASE_URL=`, `--variable host=`, or a bare URL), or `local` for a run
45
+ * that names none — a suite run in the sandbox goes to the sandbox's own app unless the command
46
+ * says otherwise. Every localhost spelling collapses to `local` (one sandbox, one app, one
47
+ * database).
48
+ *
49
+ * @param command - The suite command.
50
+ * @returns The target key.
51
+ */
52
+ export declare function suiteCommandTarget(command: string): string;
53
+ /**
54
+ * The whole-suite command made scoped to the tests it should re-run instead: the same
55
+ * invocation — same directory, same settings — with the named `file:line`s in place of the whole
56
+ * suite, `--last-failed` dropped (it re-runs the tests that never ran too, X1 flash-m60) and
57
+ * `--retries=0` set (a re-run of a known failure does not need to pay for retries).
58
+ *
59
+ * @param command - The whole-suite run's command.
60
+ * @param locations - The failing `file:line`s to re-run, as the run's own log printed them — or,
61
+ * for a run that started outside the suite, the absolute form Playwright matches each file
62
+ * argument against.
63
+ * @returns The scoped command, or the command unchanged when it runs no Playwright suite.
64
+ */
65
+ export declare function scopedRerunCommand(command: string, locations: readonly string[]): string;
66
+ /**
67
+ * One suite run, as the rule that decides whether a second may start beside it reads it.
68
+ *
69
+ * Both callers describe the runs in flight the same way and ask the same question of them; what
70
+ * differs is only which runs each one knows about — the hold reads its ledger of background runs,
71
+ * the note reads the one run it is waiting on.
72
+ */
73
+ export interface SuiteRunInFlight {
74
+ /** Whether the run invokes a suite at all: a `vitest`/`jest` run is not one, and cannot be. */
75
+ runsSuite: boolean;
76
+ /** The app and database the run measures, as {@link suiteCommandTarget} keys it. */
77
+ target: string;
78
+ /**
79
+ * Whether the command being judged STOPS this run before starting its own — a kill comes first,
80
+ * so a run the command itself stops is not in the way (xr-molecule-flash-m87's
81
+ * `kill 19408 … ; npx playwright test …`).
82
+ */
83
+ stoppedByCommand?: boolean;
84
+ }
85
+ /**
86
+ * The run in flight that makes a second run a hazard, or null when nothing is in the way.
87
+ *
88
+ * This is the ONE rule the mid-run note and the suite-overlap hold both answer with: a suite run
89
+ * may start only when this returns null, because two suites against one app and database share its
90
+ * rows — the failures one causes the other cannot be told from real ones. A run that invokes no
91
+ * suite is never in the way, and neither is one the candidate command stops first.
92
+ *
93
+ * @param candidate - The run about to start, by the app and database it would measure
94
+ * ({@link suiteCommandTarget}).
95
+ * @param inFlight - The suite runs already going, in the order the caller found them.
96
+ * @returns The first run in the way, or null.
97
+ */
98
+ export declare function suiteRunInTheWay<T extends SuiteRunInFlight>(candidate: {
99
+ target: string;
100
+ }, inFlight: readonly T[]): T | null;
101
+ /**
102
+ * Whether a command walks a PART of a suite instead of all of it: a cap that stops it at a failure
103
+ * count, the failures the last run printed (`--last-failed`, `--only-changed`), a title filter, or
104
+ * named files/lines.
105
+ *
106
+ * THE one reading of that question, in one place. Two sites act on the answer and must never
107
+ * disagree:
108
+ *
109
+ * - the command rewriter that adds `--retries=0` to a background Playwright run
110
+ * ({@link ./utilities.js} `streamingBackgroundCommand`) — a diagnosis walks each failure once,
111
+ * while the run the suite is judged by keeps the suite's own retries; and
112
+ * - the platform's own notes (molecule-dev's `suite-cli-override.ts`), which must not tell the
113
+ * executor to drop the `--retries=0` the platform itself has just added — the contradiction X1
114
+ * flash-m74 and m75 filed, and m76 filed a third time.
115
+ *
116
+ * Until xr-molecule-flash-m89 they were a regex and a predicate that only LOOKED alike, and the
117
+ * gap was exactly the run the platform had asked for: its `testRunNote` says "run the whole suite
118
+ * once with `--max-failures=0`", and the rewriter read that cap flag as a capped diagnosis and
119
+ * stripped the suite's retries from the uncapped proof run that follows a deploy — the one run the
120
+ * graded run is compared against (+25:03, +31:39, +36:40 and +49:57, the last two against the
121
+ * deployed URL, where a network blip is likeliest and the retries it dropped are the ones that
122
+ * absorb it).
123
+ *
124
+ * `--max-failures=0` is Playwright's own "no cap" ({@link boundsFailures}), so it is not a
125
+ * diagnosis. A command that starts no Playwright run is not one either (false): the question is
126
+ * about a run already read as a Playwright suite.
127
+ *
128
+ * @param command - The command as the executor sent it.
129
+ * @returns True when the run is a diagnosis or a re-run, not the whole suite.
130
+ */
131
+ export declare function isDiagnosisRun(command: string): boolean;
132
+ //# sourceMappingURL=suite-overlap.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"suite-overlap.d.ts","sourceRoot":"","sources":["../src/suite-overlap.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAKH;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAoB1D;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAexF;AAED;;;;;;GAMG;AACH,MAAM,WAAW,gBAAgB;IAC/B,+FAA+F;IAC/F,SAAS,EAAE,OAAO,CAAA;IAClB,oFAAoF;IACpF,MAAM,EAAE,MAAM,CAAA;IACd;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAA;CAC3B;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,SAAS,gBAAgB,EACzD,SAAS,EAAE;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,EAC7B,QAAQ,EAAE,SAAS,CAAC,EAAE,GACrB,CAAC,GAAG,IAAI,CAMV;AAqDD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAQvD"}
@@ -0,0 +1,208 @@
1
+ /**
2
+ * The ONE rule that decides whether a suite run may start while another is already walking, and
3
+ * the command readings both sides of it need.
4
+ *
5
+ * Two sites ask "may a second run start now?" about the same pair of runs — the mid-run note that
6
+ * tells the executor what to do next while the suite it is waiting on is still going
7
+ * ({@link ./utilities.js} `repeatingFailureNote`), and the hold that refuses the run when it
8
+ * arrives (molecule-dev's `suite-overlap-hold.ts`) — and until xr-molecule-flash-m89 they answered
9
+ * it separately. That run's +19:59 `wait_for_task` handed over a repeating cause with "the test
10
+ * alone reproduces the first one … start on them now, while the run keeps walking"; 23 s later the
11
+ * executor sent exactly that re-run, and the overlap hold refused it — "A playwright suite is
12
+ * already running against this app and database … Wait for it." Two orders, one run, and the round
13
+ * trip between them was the whole cost.
14
+ *
15
+ * So the rule lives here ({@link suiteRunInTheWay}) and both of them call it: a run may start only
16
+ * when nothing in flight is already measuring the same app and database. What "the same app and
17
+ * database" MEANS lives here too ({@link suiteCommandTarget}) — the note and the hold have to key
18
+ * a command the same way before they can agree on whether two of them collide, and the re-run a
19
+ * note hands over is built from the same reading of the running command
20
+ * ({@link scopedRerunCommand}).
21
+ *
22
+ * The same treatment for the other pair that answered one question twice: whether a run walks the
23
+ * WHOLE suite or a part of it. The rewriter that decides whether a background Playwright run gets
24
+ * `--retries=0` ({@link ./utilities.js} `streamingBackgroundCommand`) and the platform's own notes
25
+ * (molecule-dev's `suite-cli-override.ts`) asked it separately — a regex there, a predicate here —
26
+ * until xr-molecule-flash-m89 showed the two answers were never the same question
27
+ * ({@link isDiagnosisRun}).
28
+ *
29
+ * @module
30
+ */
31
+ import { commandSegments } from './command-position.js';
32
+ import { commandRunsNoTests } from './no-test-run.js';
33
+ /**
34
+ * A command with its heredoc bodies removed: the runs, kills and hosts it names in a file it
35
+ * WRITES are not what the command itself does.
36
+ *
37
+ * The header is kept (`cat > run.sh <<'EOF' …EOF` leaves `cat > run.sh`), which is what the
38
+ * readers here want: everything before the body is still readable as the command's own words.
39
+ *
40
+ * @param command - The shell command.
41
+ * @returns The command without its heredoc bodies.
42
+ */
43
+ export function withoutHeredocs(command) {
44
+ return command.replace(/<<-?\s*(['"]?)(\w+)\1[\s\S]*?\n\s*\2\b/g, ' ');
45
+ }
46
+ /**
47
+ * The server a suite command points at, as a comparable key: the host it names (`HOST=`,
48
+ * `BASE_URL=`, `PLAYWRIGHT_BASE_URL=`, `--variable host=`, or a bare URL), or `local` for a run
49
+ * that names none — a suite run in the sandbox goes to the sandbox's own app unless the command
50
+ * says otherwise. Every localhost spelling collapses to `local` (one sandbox, one app, one
51
+ * database).
52
+ *
53
+ * @param command - The suite command.
54
+ * @returns The target key.
55
+ */
56
+ export function suiteCommandTarget(command) {
57
+ const text = withoutHeredocs(command);
58
+ const named = /(?:\b(?:HOST|BASE_URL|PLAYWRIGHT_BASE_URL|E2E_BASE_URL)|--variable\s+host)\s*=\s*['"]?([^\s'"|;&]+)/i.exec(text)?.[1] ?? /https?:\/\/[^\s'"|;&]+/i.exec(text)?.[0];
59
+ if (!named)
60
+ return 'local';
61
+ const value = named.trim();
62
+ let host;
63
+ try {
64
+ const url = new URL(/^[a-z][a-z0-9+.-]*:\/\//i.test(value) ? value : `http://${value}`);
65
+ host = url.hostname.toLowerCase();
66
+ if (!/^(?:localhost|127\.0\.0\.1|0\.0\.0\.0|::1|\[::1\])$/.test(host))
67
+ return url.port ? `${host}:${url.port}` : host;
68
+ }
69
+ catch (_error) {
70
+ // Not a URL at all (a shell variable the command does not expand): compare
71
+ // the token itself, so the same spelling still reads as the same target.
72
+ return value.toLowerCase();
73
+ }
74
+ return 'local';
75
+ }
76
+ /**
77
+ * The whole-suite command made scoped to the tests it should re-run instead: the same
78
+ * invocation — same directory, same settings — with the named `file:line`s in place of the whole
79
+ * suite, `--last-failed` dropped (it re-runs the tests that never ran too, X1 flash-m60) and
80
+ * `--retries=0` set (a re-run of a known failure does not need to pay for retries).
81
+ *
82
+ * @param command - The whole-suite run's command.
83
+ * @param locations - The failing `file:line`s to re-run, as the run's own log printed them — or,
84
+ * for a run that started outside the suite, the absolute form Playwright matches each file
85
+ * argument against.
86
+ * @returns The scoped command, or the command unchanged when it runs no Playwright suite.
87
+ */
88
+ export function scopedRerunCommand(command, locations) {
89
+ const at = command.search(/\bplaywright\s+test\b/);
90
+ if (at === -1)
91
+ return command;
92
+ const head = command.slice(0, at);
93
+ const rest = command.slice(at);
94
+ // Only the first statement runs the suite; the rest (`| tail -50`, `&& echo done`)
95
+ // is left exactly as it was.
96
+ const end = rest.search(/&&|\|\||;|\|/);
97
+ const statement = end === -1 ? rest : rest.slice(0, end);
98
+ const tail = end === -1 ? '' : rest.slice(end);
99
+ const scoped = statement
100
+ .replace(/\s*--last-failed\b/g, '')
101
+ .replace(/\s*--retries(?:[= ]\d+)?/g, '')
102
+ .replace(/^(playwright\s+test)\b/, `$1 ${locations.join(' ')} --retries=0`);
103
+ return `${head}${scoped}${tail}`;
104
+ }
105
+ /**
106
+ * The run in flight that makes a second run a hazard, or null when nothing is in the way.
107
+ *
108
+ * This is the ONE rule the mid-run note and the suite-overlap hold both answer with: a suite run
109
+ * may start only when this returns null, because two suites against one app and database share its
110
+ * rows — the failures one causes the other cannot be told from real ones. A run that invokes no
111
+ * suite is never in the way, and neither is one the candidate command stops first.
112
+ *
113
+ * @param candidate - The run about to start, by the app and database it would measure
114
+ * ({@link suiteCommandTarget}).
115
+ * @param inFlight - The suite runs already going, in the order the caller found them.
116
+ * @returns The first run in the way, or null.
117
+ */
118
+ export function suiteRunInTheWay(candidate, inFlight) {
119
+ return (inFlight.find((run) => run.runsSuite && run.target === candidate.target && !run.stoppedByCommand) ?? null);
120
+ }
121
+ /**
122
+ * The Playwright invocation of a command, as its own command-position text (`playwright test …`),
123
+ * or null when the command starts no Playwright run.
124
+ *
125
+ * The runner's own word has to END where the sub-command starts (`(?=\s|$)`, not a `\b`): a
126
+ * segment can be a fragment beginning at a `playwright test` printed INSIDE a string
127
+ * (`echo "then npx playwright test" && npx playwright test`), and a match that ends against the
128
+ * closing quote is that echo. A version-pinned `playwright@1.44.0 test` is the same run.
129
+ */
130
+ const PLAYWRIGHT_RUN = /^(?:@playwright\/test|playwright)(?:@\S+)?\s+test(?=\s|$)/;
131
+ /**
132
+ * What narrows a Playwright run to a part of the suite: a named file or line, a title filter, or
133
+ * the tests the run before it printed (`--last-failed`, `--only-changed`).
134
+ */
135
+ const NARROWED_RUN = /\.(?:spec|test)\.[cm]?[jt]sx?\b|\.[cm]?[jt]sx?:\d+|(?:^|\s)(?:-g|--grep(?:-invert)?|--last-failed|--only-changed)\b/;
136
+ /**
137
+ * The Playwright invocation's text when the command starts one, or null.
138
+ *
139
+ * @param command - The command as the executor sent it.
140
+ * @returns The invocation's command-position text, or null.
141
+ */
142
+ function playwrightRun(command) {
143
+ return (commandSegments(withoutHeredocs(command)).find((segment) => PLAYWRIGHT_RUN.test(segment.text))
144
+ ?.text ?? null);
145
+ }
146
+ /**
147
+ * Whether a `--max-failures` BOUNDS the walk: the flag is given a count above zero.
148
+ *
149
+ * Playwright's own spelling of "no cap" is zero — `maxFailures: 0` "disables the feature" — and
150
+ * that is the flag the platform's `testRunNote` asks for on the uncapped whole-suite run ("run the
151
+ * whole suite once with `--max-failures=0`"), which is why this is not a plain flag test.
152
+ *
153
+ * @param command - The command as the executor sent it.
154
+ * @returns True when a failure cap would stop the walk.
155
+ */
156
+ function boundsFailures(command) {
157
+ const flag = /(?:^|[\s;&|(])--max-failures(?![-\w])/.exec(command);
158
+ if (!flag)
159
+ return false;
160
+ const value = /(?:^|[\s;&|(])--max-failures(?:=|\s+)(['"]?)(\d[\d_]*)/.exec(command)?.[2];
161
+ // A `--max-failures` with no count at all is not the uncapped spelling of anything: it is read
162
+ // as a cap, the way Playwright's own default reading of the flag is a count of failures.
163
+ if (value === undefined)
164
+ return true;
165
+ return Number(value.replace(/_/g, '')) !== 0;
166
+ }
167
+ /**
168
+ * Whether a command walks a PART of a suite instead of all of it: a cap that stops it at a failure
169
+ * count, the failures the last run printed (`--last-failed`, `--only-changed`), a title filter, or
170
+ * named files/lines.
171
+ *
172
+ * THE one reading of that question, in one place. Two sites act on the answer and must never
173
+ * disagree:
174
+ *
175
+ * - the command rewriter that adds `--retries=0` to a background Playwright run
176
+ * ({@link ./utilities.js} `streamingBackgroundCommand`) — a diagnosis walks each failure once,
177
+ * while the run the suite is judged by keeps the suite's own retries; and
178
+ * - the platform's own notes (molecule-dev's `suite-cli-override.ts`), which must not tell the
179
+ * executor to drop the `--retries=0` the platform itself has just added — the contradiction X1
180
+ * flash-m74 and m75 filed, and m76 filed a third time.
181
+ *
182
+ * Until xr-molecule-flash-m89 they were a regex and a predicate that only LOOKED alike, and the
183
+ * gap was exactly the run the platform had asked for: its `testRunNote` says "run the whole suite
184
+ * once with `--max-failures=0`", and the rewriter read that cap flag as a capped diagnosis and
185
+ * stripped the suite's retries from the uncapped proof run that follows a deploy — the one run the
186
+ * graded run is compared against (+25:03, +31:39, +36:40 and +49:57, the last two against the
187
+ * deployed URL, where a network blip is likeliest and the retries it dropped are the ones that
188
+ * absorb it).
189
+ *
190
+ * `--max-failures=0` is Playwright's own "no cap" ({@link boundsFailures}), so it is not a
191
+ * diagnosis. A command that starts no Playwright run is not one either (false): the question is
192
+ * about a run already read as a Playwright suite.
193
+ *
194
+ * @param command - The command as the executor sent it.
195
+ * @returns True when the run is a diagnosis or a re-run, not the whole suite.
196
+ */
197
+ export function isDiagnosisRun(command) {
198
+ if (boundsFailures(command))
199
+ return true;
200
+ const run = playwrightRun(command);
201
+ if (run === null)
202
+ return false;
203
+ // A run that only collects and prints (`playwright test --list`, `--version`) walks no part of
204
+ // the suite either, and is not the whole-suite run the retries belong on.
205
+ if (commandRunsNoTests(run))
206
+ return true;
207
+ return NARROWED_RUN.test(run);
208
+ }
package/dist/tools.d.ts CHANGED
@@ -26,6 +26,26 @@ import type { AITool } from '@molecule/api-ai';
26
26
  */
27
27
  export declare function taskStopCommand(backend: ExecutionBackend, taskId: string): Promise<string>;
28
28
  import type { ExecutionBackend, ToolBuildConfig } from './types.js';
29
+ /**
30
+ * The closing advice on an over-budget message for a command that is not a test
31
+ * suite: the work it was chaining is what to split.
32
+ */
33
+ export declare const SPLIT_WORK_ADVICE: string;
34
+ /**
35
+ * The closing advice on an over-budget message for a command that IS a suite run
36
+ * (Playwright, Vitest, a `hurl` run — {@link isSuiteRunCommand}): its files are not
37
+ * a bigger unit to break up.
38
+ *
39
+ * X1 flash-m36 asked for this half and the message kept saying "run a smaller unit"
40
+ * for two months. It is the wrong direction for a suite, and measurably: a run
41
+ * stopped here reports NO tally, so nothing in the result says which test or request
42
+ * hung — the run has to be sent again in the background (off this budget, its output
43
+ * written to a log that can be read mid-run) or, when a consumer knows the runner's
44
+ * per-request limit, with each request bounded. xr-molecule-flash-m93 (+06:22,
45
+ * 2026-10-16) lost 4.8 minutes to a Hurl run whose ONE unanswered request had hurl's
46
+ * 300 s default per request holding the file, ten seconds past this tool's budget.
47
+ */
48
+ export declare const SUITE_OVER_BUDGET_ADVICE: string;
29
49
  /**
30
50
  * The steer attached to a SUCCESSFUL read of a package's built output. The ENOENT
31
51
  * redirect below fires only when the read FAILS, so a model that knows the hoisted
@@ -39,6 +59,31 @@ import type { ExecutionBackend, ToolBuildConfig } from './types.js';
39
59
  * @returns The note, or null when the path is not inside a package's `dist`.
40
60
  */
41
61
  export declare function moleculeDistReadNote(path: string): string | null;
62
+ /**
63
+ * The refusal for a write whose target is inside an installed package, or null
64
+ * when the path is not under a `node_modules` directory.
65
+ *
66
+ * `node_modules` is not the project's code: nothing written there survives the
67
+ * next install, no deploy carries it, and a sandbox whose dependency tree is
68
+ * shared (the pre-installed superset) breaks every other run through it. X0
69
+ * xr-molecule-flash-m91 (+04:43) called `write_file` on
70
+ * `node_modules/@molecule/api-resource-comment/dist/numbers.d.ts` while reading
71
+ * the package's declarations and clobbered its runtime module a call later
72
+ * (`module.exports = {}` over the ESM handler its own code imports) — the write
73
+ * answered `ok: true`, and 18 s went to noticing, `npm pack`-ing the package and
74
+ * putting the file back. Reading these files stays allowed (that is how the
75
+ * declarations above are read at all).
76
+ *
77
+ * The restore route is named because the model that has ALREADY clobbered a file
78
+ * (this refusal is too late for it) is the one who needs it, and because the
79
+ * obvious repair — `npm install <pkg>` — is itself refused in the pre-installed
80
+ * sandbox. `npm pack` + a `tar` extraction over the package directory is what
81
+ * m91 used, and what both guards leave alone.
82
+ *
83
+ * @param path - An absolute path a tool is about to write.
84
+ * @returns The one-line refusal naming the package, or null when nothing is installed there.
85
+ */
86
+ export declare function installedPackageWriteRefusal(path: string): string | null;
42
87
  /**
43
88
  * Build a complete set of AI agent tools bound to an execution backend.
44
89
  *
@@ -1 +1 @@
1
- {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AAsE9C;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,eAAe,CAAC,OAAO,EAAE,gBAAgB,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAahG;AAwHD,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAoEnE;;;;;;;;;;;GAWG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAUhE;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,gBAAgB,EAAE,MAAM,CAAC,EAAE,eAAe,GAAG,MAAM,EAAE,CA65DxF"}
1
+ {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AAkF9C;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,eAAe,CAAC,OAAO,EAAE,gBAAgB,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAahG;AAwHD,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAgEnE;;;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;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,4BAA4B,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAsBxE;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,gBAAgB,EAAE,MAAM,CAAC,EAAE,eAAe,GAAG,MAAM,EAAE,CAipExF"}