@molecule/api-ai-tools 1.6.5 → 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/README.md +311 -1
- package/dist/command-position.d.ts +26 -0
- package/dist/command-position.d.ts.map +1 -1
- package/dist/command-position.js +54 -7
- package/dist/cut-run.d.ts +124 -0
- package/dist/cut-run.d.ts.map +1 -0
- package/dist/cut-run.js +267 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/tools.d.ts +20 -0
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +118 -13
- package/dist/utilities.d.ts +75 -0
- package/dist/utilities.d.ts.map +1 -1
- package/dist/utilities.js +95 -0
- package/package.json +1 -1
package/dist/cut-run.js
ADDED
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A command that a budget stopped before it finished, and the state its result carries so no
|
|
3
|
+
* reader can mistake it for a run that ended.
|
|
4
|
+
*
|
|
5
|
+
* X1 xr-molecule-flash-m93 (2026-10-16, +12:37) sent the supplied RealWorld suite as
|
|
6
|
+
* `timeout 280 hurl --test --continue-on-error --jobs 1 … hurl/*.hurl 2>&1 | tail -60` and got
|
|
7
|
+
* back `exitCode: 0`, four finished files' lines and nothing else. Two lies in one result: the
|
|
8
|
+
* `timeout 280` killed the run at 280 s, and the `| tail` made the pipeline's status the status —
|
|
9
|
+
* so the executor read a suite that stopped at its fifth file as a suite that ran and nearly
|
|
10
|
+
* passed, fixed one message, and immediately sent the same 280-second command again (the run's
|
|
11
|
+
* third, ~14 minutes of a 60-minute cap across three of them).
|
|
12
|
+
*
|
|
13
|
+
* So the tool never reports a stopped run as `exitCode: 0`. Such a result carries
|
|
14
|
+
* `status: 'cut_at_budget'` and an exit code that is `null` whenever the number the pipeline
|
|
15
|
+
* reported belongs to the filter rather than to the run, and every reader of a run's verdict reads
|
|
16
|
+
* the state through {@link commandRunState} — a cut run is NOT RUN, never a pass.
|
|
17
|
+
*
|
|
18
|
+
* Two things stop a run, and both are read from the command and the result, never inferred:
|
|
19
|
+
*
|
|
20
|
+
* 1. **Its own bound** — a `timeout <N>` the command carries ({@link commandTimeoutSeconds}). The
|
|
21
|
+
* run used it, so the run was killed at it; with no closing tally in the output, the run did
|
|
22
|
+
* not finish. This is m93's shape.
|
|
23
|
+
* 2. **This tool's budget** — the backend runs the command under `timeout` (see
|
|
24
|
+
* `backends/sandbox.ts`), which reports 124 whatever the pipeline did, and returns everything
|
|
25
|
+
* printed until then. The budget is the tool's own, so its number belongs to the tool.
|
|
26
|
+
*
|
|
27
|
+
* @module
|
|
28
|
+
*/
|
|
29
|
+
import { commandTimeoutSeconds } from './command-position.js';
|
|
30
|
+
import { isSuiteRunCommand } from './utilities.js';
|
|
31
|
+
/**
|
|
32
|
+
* The state a result carries when a budget stopped its command: a run that did not finish is not a
|
|
33
|
+
* run that passed, and no reader may treat it as one.
|
|
34
|
+
*/
|
|
35
|
+
export const CUT_AT_BUDGET = 'cut_at_budget';
|
|
36
|
+
/**
|
|
37
|
+
* The state a command's result records — THE reader every verdict about a run goes through.
|
|
38
|
+
*
|
|
39
|
+
* A result that carries no state is a run that ended: the state is written where the run is
|
|
40
|
+
* stopped ({@link commandCutRun}), so a result without it was never stopped by a budget this
|
|
41
|
+
* platform set or can see. Results produced before this state existed read the same way, which is
|
|
42
|
+
* what keeps a past turn's tally readable.
|
|
43
|
+
*
|
|
44
|
+
* @param result - The exec_command / wait_for_task result.
|
|
45
|
+
* @returns The state the result records.
|
|
46
|
+
*/
|
|
47
|
+
export function commandRunState(result) {
|
|
48
|
+
if (result === null || typeof result !== 'object')
|
|
49
|
+
return 'ran';
|
|
50
|
+
return result.status === CUT_AT_BUDGET ? CUT_AT_BUDGET : 'ran';
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* The gap between a bound and the elapsed time a run killed at it reports. Zero, and deliberately
|
|
54
|
+
* not a tolerance: a command killed at its bound reports AT LEAST the bound (the shell's own
|
|
55
|
+
* bookkeeping runs after the kill, and this tool's round trip is on top), so any slack would only
|
|
56
|
+
* make a SHORT bound fire on a run that finished inside it (`timeout 5 curl …` answering in 3 s).
|
|
57
|
+
* A run that finished near its bound carried its runner's closing summary — {@link suiteTally}.
|
|
58
|
+
*
|
|
59
|
+
* @param boundSeconds - The bound the run was allowed.
|
|
60
|
+
* @returns The elapsed ms a run stopped at that bound reports at least.
|
|
61
|
+
*/
|
|
62
|
+
function atBoundMs(boundSeconds) {
|
|
63
|
+
return boundSeconds * 1000;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* A runner's closing summary, printed only by a run that walked its whole suite: Hurl's
|
|
67
|
+
* `Executed files:` block and the `N passed` / `N failed` / `N flaky` line Playwright and Vitest
|
|
68
|
+
* print when they finish.
|
|
69
|
+
*
|
|
70
|
+
* This is what separates "stopped at the bound" from "used the bound and finished": a suite that
|
|
71
|
+
* printed its summary ran to its end, and is read as the run it was.
|
|
72
|
+
*/
|
|
73
|
+
const SUITE_TALLY = /\bExecuted files:\s*\d+|\b\d+\s+(?:passed|failed|flaky|skipped)\b/;
|
|
74
|
+
/**
|
|
75
|
+
* Whether text carries a runner's closing summary ({@link SUITE_TALLY}).
|
|
76
|
+
*
|
|
77
|
+
* @param text - The run's stdout and stderr.
|
|
78
|
+
* @returns True when the runner's own end-of-run summary is there.
|
|
79
|
+
*/
|
|
80
|
+
export function suiteTally(text) {
|
|
81
|
+
return SUITE_TALLY.test(text);
|
|
82
|
+
}
|
|
83
|
+
/** The last thing a run printed: its last non-blank line, as one line and short enough to quote. */
|
|
84
|
+
function lastPrinted(text) {
|
|
85
|
+
const lines = text.split('\n');
|
|
86
|
+
for (let i = lines.length - 1; i >= 0; i -= 1) {
|
|
87
|
+
const line = (lines[i] ?? '').trim();
|
|
88
|
+
if (line)
|
|
89
|
+
return line.length > 180 ? `${line.slice(0, 177)}…` : line;
|
|
90
|
+
}
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
/** Hurl's per-file line: `Success hurl/articles.hurl (17 request(s) in 318 ms)`. */
|
|
94
|
+
const HURL_FILE_LINE = /^(?:Success|Failure)\s+(\S+)/gm;
|
|
95
|
+
/** Playwright's progress line — `[5/13] specs/auth.spec.ts:10:5 › signs up`. */
|
|
96
|
+
const PROGRESS_LINE = /^\s*\[(\d+)\/(\d+)\]/gm;
|
|
97
|
+
/**
|
|
98
|
+
* What the runner's own output says the run got through.
|
|
99
|
+
*
|
|
100
|
+
* Hurl prints a line per finished file and no total unless it finished; Playwright's line reporter
|
|
101
|
+
* prints `[n/K]` with both numbers. Neither is inferred from the command — a run that printed
|
|
102
|
+
* nothing says nothing, which is what `null` means here.
|
|
103
|
+
*
|
|
104
|
+
* @param text - The run's stdout and stderr.
|
|
105
|
+
* @param suite - Whether the command runs a suite.
|
|
106
|
+
* @returns How many finished, how many the run was walking, and the noun.
|
|
107
|
+
*/
|
|
108
|
+
function runProgress(text, suite) {
|
|
109
|
+
const files = new Set();
|
|
110
|
+
for (const m of text.matchAll(HURL_FILE_LINE))
|
|
111
|
+
files.add(m[1] ?? '');
|
|
112
|
+
if (files.size > 0)
|
|
113
|
+
return { ran: files.size, total: null, noun: 'files' };
|
|
114
|
+
let ran = null;
|
|
115
|
+
let total = null;
|
|
116
|
+
for (const m of text.matchAll(PROGRESS_LINE)) {
|
|
117
|
+
ran = Number(m[1]);
|
|
118
|
+
total = Number(m[2]);
|
|
119
|
+
}
|
|
120
|
+
if (ran !== null)
|
|
121
|
+
return { ran, total, noun: 'tests' };
|
|
122
|
+
return { ran: null, total: null, noun: suite ? 'tests' : 'files' };
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* The run a command's result records as stopped by a budget, or null when the result is a run that
|
|
126
|
+
* ended (or says nothing about a run).
|
|
127
|
+
*
|
|
128
|
+
* Evidence, in order — never a guess:
|
|
129
|
+
*
|
|
130
|
+
* - `timeout`'s own 124 (the code the backend's wrapper returns whenever the TOOL's budget fired,
|
|
131
|
+
* whatever the pipeline ended with) — and, when the clock says the command's own smaller bound
|
|
132
|
+
* ran out first and less than the budget, that bound instead.
|
|
133
|
+
* - the command carried its own `timeout <N>`, N is inside this tool's budget (a larger one never
|
|
134
|
+
* fires — the tool's does), the run used N, and no runner printed a closing summary: the run was
|
|
135
|
+
* killed at its own bound. Its exit code, when the pipeline handed one back, is the filter's.
|
|
136
|
+
* - the run used this tool's whole budget behind a filter that withheld its output, and printed no
|
|
137
|
+
* closing summary: the budget stopped it, and no code in the result is the run's.
|
|
138
|
+
*
|
|
139
|
+
* @param command - The command the executor sent.
|
|
140
|
+
* @param result - Its result.
|
|
141
|
+
* @param elapsedMs - How long the command ran, in ms.
|
|
142
|
+
* @param budgetSeconds - This tool's foreground budget, in seconds (0 when it has none).
|
|
143
|
+
* @returns The stopped run, or null.
|
|
144
|
+
*/
|
|
145
|
+
export function commandCutRun(command, result, elapsedMs, budgetSeconds = 0) {
|
|
146
|
+
if (typeof command !== 'string' || !command.trim())
|
|
147
|
+
return null;
|
|
148
|
+
if (result === null || typeof result !== 'object')
|
|
149
|
+
return null;
|
|
150
|
+
if (!Number.isFinite(elapsedMs))
|
|
151
|
+
return null;
|
|
152
|
+
const r = result;
|
|
153
|
+
const exitCode = typeof r.exitCode === 'number' ? r.exitCode : null;
|
|
154
|
+
const text = `${String(r.stdout ?? '')}\n${String(r.stderr ?? '')}`;
|
|
155
|
+
// A suite that printed its summary ran to its end, whatever it was wrapped in.
|
|
156
|
+
if (suiteTally(text))
|
|
157
|
+
return null;
|
|
158
|
+
const suite = isSuiteRunCommand(command);
|
|
159
|
+
const withheld = /\|\s*(?:tail|head|grep|egrep|fgrep|sort|uniq|wc|jq)\b/.test(command);
|
|
160
|
+
const progress = runProgress(text, suite);
|
|
161
|
+
const doing = lastPrinted(text);
|
|
162
|
+
const declared = commandTimeoutSeconds(command);
|
|
163
|
+
// A command whose own bound is inside this tool's budget is stopped by ITS bound: a larger one
|
|
164
|
+
// never gets the chance — the backend's `timeout` fires first (see the module doc). With no
|
|
165
|
+
// tool budget at all (a reader re-reading a background run) the command's own bound is the only
|
|
166
|
+
// one there is.
|
|
167
|
+
const ownBoundStops = budgetSeconds === 0 || (declared !== null && declared < budgetSeconds);
|
|
168
|
+
// `timeout`'s own 124 — the code the backend's wrapper returns when the TOOL's budget fires,
|
|
169
|
+
// whatever the pipeline ended with (`backends/sandbox.ts`). Which of the two bounds fired is the
|
|
170
|
+
// clock's to say: a run killed at the tool's budget reports at least the budget, and one killed
|
|
171
|
+
// at its own bound reports at least that bound and less than the budget.
|
|
172
|
+
if (budgetSeconds > 0 && exitCode === 124) {
|
|
173
|
+
const ownFired = declared !== null &&
|
|
174
|
+
ownBoundStops &&
|
|
175
|
+
elapsedMs >= atBoundMs(declared) &&
|
|
176
|
+
elapsedMs < atBoundMs(budgetSeconds);
|
|
177
|
+
return {
|
|
178
|
+
seconds: ownFired ? declared : budgetSeconds,
|
|
179
|
+
stoppedBy: ownFired ? 'command-timeout' : 'tool-budget',
|
|
180
|
+
exitCode,
|
|
181
|
+
...progress,
|
|
182
|
+
doing,
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
if (declared !== null && ownBoundStops && elapsedMs >= atBoundMs(declared)) {
|
|
186
|
+
return {
|
|
187
|
+
seconds: declared,
|
|
188
|
+
stoppedBy: 'command-timeout',
|
|
189
|
+
// A code the filter produced is not the run's: `… | tail` handed back 0 forever (m93).
|
|
190
|
+
exitCode: withheld || exitCode === 0 ? null : exitCode,
|
|
191
|
+
...progress,
|
|
192
|
+
doing,
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
// The tool's budget with its code masked: the run used the whole budget and the pipeline left
|
|
196
|
+
// nothing readable of it — no code of its own, and no runner's closing summary.
|
|
197
|
+
if (budgetSeconds > 0 && withheld && elapsedMs >= atBoundMs(budgetSeconds)) {
|
|
198
|
+
return {
|
|
199
|
+
seconds: budgetSeconds,
|
|
200
|
+
stoppedBy: 'tool-budget',
|
|
201
|
+
exitCode: exitCode === 0 ? null : exitCode,
|
|
202
|
+
...progress,
|
|
203
|
+
doing,
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
return null;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* The facts a result records about the run a budget stopped it in — what a reader needs to say
|
|
210
|
+
* how far the run got WITHOUT re-reading its log or re-deriving the counts.
|
|
211
|
+
*
|
|
212
|
+
* The numbers are read from the runner's own output where it printed them (Hurl's per-file lines,
|
|
213
|
+
* Playwright's `[n/K]`), so a caller that can also see the suite itself — the file count of the
|
|
214
|
+
* `*.hurl` directory the run walked — fills in the `total` this could not know, and both then say
|
|
215
|
+
* the same sentence ({@link cutRunSentence}).
|
|
216
|
+
*
|
|
217
|
+
* @param result - The exec_command / wait_for_task result.
|
|
218
|
+
* @returns The facts the result carries, or null when it records no stopped run.
|
|
219
|
+
*/
|
|
220
|
+
export function cutRunFacts(result) {
|
|
221
|
+
if (result === null || typeof result !== 'object')
|
|
222
|
+
return null;
|
|
223
|
+
if (commandRunState(result) !== CUT_AT_BUDGET)
|
|
224
|
+
return null;
|
|
225
|
+
const cut = result.cutRun;
|
|
226
|
+
if (cut === null || typeof cut !== 'object')
|
|
227
|
+
return null;
|
|
228
|
+
const c = cut;
|
|
229
|
+
if (typeof c.seconds !== 'number' || typeof c.stoppedBy !== 'string')
|
|
230
|
+
return null;
|
|
231
|
+
return {
|
|
232
|
+
seconds: c.seconds,
|
|
233
|
+
stoppedBy: c.stoppedBy,
|
|
234
|
+
exitCode: typeof c.exitCode === 'number' ? c.exitCode : null,
|
|
235
|
+
ran: typeof c.ran === 'number' ? c.ran : null,
|
|
236
|
+
total: typeof c.total === 'number' ? c.total : null,
|
|
237
|
+
noun: c.noun === 'tests' ? 'tests' : 'files',
|
|
238
|
+
doing: typeof c.doing === 'string' && c.doing ? c.doing : null,
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* The first line of a stopped run's result: how long it was allowed, what stopped it, how far it
|
|
243
|
+
* got, and the last thing it printed.
|
|
244
|
+
*
|
|
245
|
+
* Written to be quotable by the model in one sentence and to leave no room for "nearly passing":
|
|
246
|
+
* the counts come from the runner's own output ({@link runProgress}), and a `total` the caller
|
|
247
|
+
* read elsewhere (the suite's own file count) fills in the "M of K" it can then state.
|
|
248
|
+
*
|
|
249
|
+
* @param run - The stopped run.
|
|
250
|
+
* @returns The sentence.
|
|
251
|
+
*/
|
|
252
|
+
export function cutRunSentence(run) {
|
|
253
|
+
const by = run.stoppedBy === 'tool-budget' ? "this tool's limit" : `its own \`timeout ${run.seconds}\``;
|
|
254
|
+
const lead = `This run was stopped after ${run.seconds}s (${by})`;
|
|
255
|
+
const count = run.ran !== null && run.total !== null
|
|
256
|
+
? `: ${run.ran} of ${run.total} ${run.noun} ran, the rest did not run`
|
|
257
|
+
: run.ran !== null
|
|
258
|
+
? `: ${run.ran} ${run.noun} ran and the rest did not run`
|
|
259
|
+
: ' before it finished';
|
|
260
|
+
const doing = run.doing ? `; the last thing it was doing: ${run.doing}` : '';
|
|
261
|
+
// Why the result carries no code: without this the model reads the missing field as an
|
|
262
|
+
// omission, and the number it used to read there was the pipeline's own 0.
|
|
263
|
+
const code = run.exitCode === null
|
|
264
|
+
? ' It has no exit code of its own to report — the one this pipeline produced belongs to the filter, not to the run.'
|
|
265
|
+
: '';
|
|
266
|
+
return `${lead}${count}${doing}.${code}`;
|
|
267
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -133,6 +133,7 @@ export * from './backends/local.js';
|
|
|
133
133
|
export * from './backends/sandbox.js';
|
|
134
134
|
export * from './browser-guard.js';
|
|
135
135
|
export * from './command-position.js';
|
|
136
|
+
export * from './cut-run.js';
|
|
136
137
|
export * from './input-normalizer.js';
|
|
137
138
|
export * from './no-test-run.js';
|
|
138
139
|
export * from './schemas.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkIG;AAEH,cAAc,qBAAqB,CAAA;AACnC,cAAc,uBAAuB,CAAA;AACrC,cAAc,oBAAoB,CAAA;AAClC,cAAc,uBAAuB,CAAA;AACrC,cAAc,uBAAuB,CAAA;AACrC,cAAc,kBAAkB,CAAA;AAChC,cAAc,cAAc,CAAA;AAC5B,cAAc,oBAAoB,CAAA;AAClC,cAAc,oBAAoB,CAAA;AAClC,cAAc,YAAY,CAAA;AAC1B,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,uBAAuB,CAAA;AACrC,cAAc,kBAAkB,CAAA;AAChC,cAAc,cAAc,CAAA;AAC5B,cAAc,oBAAoB,CAAA;AAClC,cAAc,oBAAoB,CAAA;AAClC,cAAc,YAAY,CAAA;AAC1B,cAAc,YAAY,CAAA;AAC1B,cAAc,gBAAgB,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -133,6 +133,7 @@ export * from './backends/local.js';
|
|
|
133
133
|
export * from './backends/sandbox.js';
|
|
134
134
|
export * from './browser-guard.js';
|
|
135
135
|
export * from './command-position.js';
|
|
136
|
+
export * from './cut-run.js';
|
|
136
137
|
export * from './input-normalizer.js';
|
|
137
138
|
export * from './no-test-run.js';
|
|
138
139
|
export * from './schemas.js';
|
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
|
package/dist/tools.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;
|
|
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"}
|
package/dist/tools.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
* @module
|
|
8
8
|
*/
|
|
9
|
+
import { commandCutRun, CUT_AT_BUDGET, cutRunSentence } from './cut-run.js';
|
|
9
10
|
import { addChangeSpan, changedSpan, countLines, renderEditExcerpt, renderSmallFile, } from './edit-excerpt.js';
|
|
10
11
|
import { guardToolExecute, missingParamError, ToolInputError } from './input-normalizer.js';
|
|
11
12
|
import { TOOL_SCHEMAS } from './schemas.js';
|
|
@@ -208,12 +209,43 @@ function runningNote(clauses) {
|
|
|
208
209
|
const joined = clauses.join('; ');
|
|
209
210
|
return `${joined[0].toUpperCase()}${joined.slice(1)}.`;
|
|
210
211
|
}
|
|
211
|
-
import { checkBlockedCommand, declaredBudgetSeconds, DEFAULT_SEARCH_EXCLUDED_DIRS, directoryReadHint, emptyCallError, failureCausesNote, findPatternArgs, fusedLine, htmlOutline, isEnvFilePath, isOutsideRoot, isPlaywrightRunCommand, isValidGlob, linesWithinSerialized, MAX_BATCH_READ_FILES, MAX_FIND_RESULTS, MAX_HTML_INLINE_CHARS, MAX_OUTPUT_SIZE, MAX_READ_CALL_CHARS, MAX_READ_RETURN_CHARS, MAX_READ_SIZE, MAX_SEARCH_CONTEXT_LINES, MAX_SEARCH_RESULTS, MAX_WRITE_SIZE, outputIsWithheldUntilExit, outputRedirectTarget, parseCheckCommand, parseCheckProblem, pathArgError, printsOnlySource, READ_RESULT_ENVELOPE_CHARS, redactSecrets, redactSecretsInCode, repeatingFailure, repeatingFailureNote, resolvePath, RUNNING_FAILURE_LINES, runningCauseCommand, runningOutputSliceCommand, serializedChars, shellQuote, sliceLines, SMALL_FILE_WHOLE_CHARS, streamingBackgroundCommand, stripControlChars, suiteRunPriorityLaunch, truncateMiddle, universalCause, whitespaceTolerantReplace, } from './utilities.js';
|
|
212
|
+
import { checkBlockedCommand, declaredBudgetSeconds, DEFAULT_SEARCH_EXCLUDED_DIRS, directoryReadHint, emptyCallError, failureCausesNote, findPathArgs, findPatternArgs, fusedLine, htmlOutline, isCommandTimeout, isEnvFilePath, isOutsideRoot, isPlaywrightRunCommand, isSuiteRunCommand, isValidGlob, linesWithinSerialized, literalDirPrefix, MAX_BATCH_READ_FILES, MAX_FIND_RESULTS, MAX_HTML_INLINE_CHARS, MAX_OUTPUT_SIZE, MAX_READ_CALL_CHARS, MAX_READ_RETURN_CHARS, MAX_READ_SIZE, MAX_SEARCH_CONTEXT_LINES, MAX_SEARCH_RESULTS, MAX_WRITE_SIZE, outputIsWithheldUntilExit, outputRedirectTarget, parseCheckCommand, parseCheckProblem, pathArgError, printsOnlySource, READ_RESULT_ENVELOPE_CHARS, redactSecrets, redactSecretsInCode, repeatingFailure, repeatingFailureNote, resolvePath, RUNNING_FAILURE_LINES, runningCauseCommand, runningOutputSliceCommand, searchTimeoutError, serializedChars, shellQuote, sliceLines, SMALL_FILE_WHOLE_CHARS, streamingBackgroundCommand, stripControlChars, suiteRunPriorityLaunch, truncateMiddle, universalCause, whitespaceTolerantReplace, } from './utilities.js';
|
|
212
213
|
/**
|
|
213
214
|
* How long exec_command waits for a command it backgrounded only because the
|
|
214
215
|
* command asked for more than the tool's ceiling, before handing back a task id.
|
|
215
216
|
*/
|
|
216
217
|
const OVER_CEILING_GRACE_MS = 45_000;
|
|
218
|
+
/**
|
|
219
|
+
* The closing advice on an over-budget message for a command that is not a test
|
|
220
|
+
* suite: the work it was chaining is what to split.
|
|
221
|
+
*/
|
|
222
|
+
export const SPLIT_WORK_ADVICE = 'Run a smaller unit per command (one test file, one build step) instead of chaining ' +
|
|
223
|
+
'a build and a whole suite.';
|
|
224
|
+
/**
|
|
225
|
+
* The closing advice on an over-budget message for a command that IS a suite run
|
|
226
|
+
* (Playwright, Vitest, a `hurl` run — {@link isSuiteRunCommand}): its files are not
|
|
227
|
+
* a bigger unit to break up.
|
|
228
|
+
*
|
|
229
|
+
* X1 flash-m36 asked for this half and the message kept saying "run a smaller unit"
|
|
230
|
+
* for two months. It is the wrong direction for a suite, and measurably: a run
|
|
231
|
+
* stopped here reports NO tally, so nothing in the result says which test or request
|
|
232
|
+
* hung — the run has to be sent again in the background (off this budget, its output
|
|
233
|
+
* written to a log that can be read mid-run) or, when a consumer knows the runner's
|
|
234
|
+
* per-request limit, with each request bounded. xr-molecule-flash-m93 (+06:22,
|
|
235
|
+
* 2026-10-16) lost 4.8 minutes to a Hurl run whose ONE unanswered request had hurl's
|
|
236
|
+
* 300 s default per request holding the file, ten seconds past this tool's budget.
|
|
237
|
+
*/
|
|
238
|
+
export const SUITE_OVER_BUDGET_ADVICE = 'A test suite is not a bigger unit to split: stopped here it reports no tally at all, so nothing ' +
|
|
239
|
+
'in this result says which test or request never finished — run it in the background ' +
|
|
240
|
+
'(`run_in_background`, then `wait_for_task`), which takes it off this budget and writes its output ' +
|
|
241
|
+
"to a log, and read that log (or the app's own log) for what it was waiting on.";
|
|
242
|
+
/**
|
|
243
|
+
* How long a tree walk (`find_files`, `search_files`) may run before the backend's
|
|
244
|
+
* own deadline stops it. Both tools answer that deadline in their own words
|
|
245
|
+
* (see searchTimeoutError) rather than forwarding the sandbox SDK's `timeoutMs`
|
|
246
|
+
* sentence, which names an option the executor cannot set.
|
|
247
|
+
*/
|
|
248
|
+
const SEARCH_TIMEOUT_MS = 10_000;
|
|
217
249
|
/**
|
|
218
250
|
* The share of its budget a command must use before the "you asked for more than
|
|
219
251
|
* this tool's ceiling" note is worth attaching. Below it the clamp changed nothing
|
|
@@ -1484,7 +1516,7 @@ export function buildTools(backend, config) {
|
|
|
1484
1516
|
// -H: a search scoped to one file printed `line:content` with no file, and every match
|
|
1485
1517
|
// came back `{file: "", line: 0}` (X1 flash-m62).
|
|
1486
1518
|
const grepArgs = `-rnH ${globArg} ${contextArg} ${grepExcludeArgs(path)} --max-count=${MAX_SEARCH_RESULTS} -- ${shellQuote(pattern)} ${shellQuote(path)}`;
|
|
1487
|
-
const result = await backend.run(`{ grep -E ${grepArgs}; s=$?; if [ "$s" -eq 2 ]; then grep -F ${grepArgs}; fi; } 2>/dev/null || true`, { timeout:
|
|
1519
|
+
const result = await backend.run(`{ grep -E ${grepArgs}; s=$?; if [ "$s" -eq 2 ]; then grep -F ${grepArgs}; fi; } 2>/dev/null || true`, { timeout: SEARCH_TIMEOUT_MS });
|
|
1488
1520
|
// grep emits `<file>:<line>:<content>`, so redaction runs PER MATCH on the
|
|
1489
1521
|
// content alone: the file prefix would otherwise hide an env assignment from
|
|
1490
1522
|
// the line-anchored env pattern, and knowing each match's own path is what
|
|
@@ -1521,6 +1553,11 @@ export function buildTools(backend, config) {
|
|
|
1521
1553
|
return { pattern, path, matches };
|
|
1522
1554
|
}
|
|
1523
1555
|
catch (e) {
|
|
1556
|
+
if (isCommandTimeout(e))
|
|
1557
|
+
return {
|
|
1558
|
+
error: searchTimeoutError('search_files', path, pattern, SEARCH_TIMEOUT_MS / 1000, `Give \`path\` the subdirectory you mean and narrow with \`include\` ` +
|
|
1559
|
+
`(e.g. "*.ts"), or use find_files with the directory in the pattern.`),
|
|
1560
|
+
};
|
|
1524
1561
|
return { error: `Search failed: ${e.message}` };
|
|
1525
1562
|
}
|
|
1526
1563
|
},
|
|
@@ -1542,21 +1579,54 @@ export function buildTools(backend, config) {
|
|
|
1542
1579
|
const symlinkErr = await checkSymlink(path);
|
|
1543
1580
|
if (symlinkErr)
|
|
1544
1581
|
return { error: symlinkErr };
|
|
1582
|
+
// The directory the PATTERN itself names (`node_modules` +
|
|
1583
|
+
// `@molecule/api-resource-user/dist/*.d.ts`) becomes the search root. Matched
|
|
1584
|
+
// against the whole path with an implicit "any parent" anchor, the pattern
|
|
1585
|
+
// made `find` visit every entry below `path` — the whole dependency tree,
|
|
1586
|
+
// which hit the 10 s deadline with no result at all while the one directory
|
|
1587
|
+
// the model meant answered a `list_files` instantly (xr-molecule-flash-m94).
|
|
1588
|
+
// A bare first segment (`helpers/*`) is left alone on purpose: it means "any
|
|
1589
|
+
// `helpers/` below the root" (xr-molecule-flash-m77, see literalDirPrefix).
|
|
1590
|
+
const prefixDir = literalDirPrefix(pattern);
|
|
1591
|
+
let findRoot = path;
|
|
1592
|
+
let findArgs = findPatternArgs(pattern);
|
|
1593
|
+
let missingPrefix;
|
|
1545
1594
|
try {
|
|
1595
|
+
if (prefixDir) {
|
|
1596
|
+
const candidate = `${path.replace(/\/+$/, '')}/${prefixDir}`;
|
|
1597
|
+
const probe = await backend.run(`test -d ${shellQuote(candidate)}`, {
|
|
1598
|
+
timeout: SEARCH_TIMEOUT_MS,
|
|
1599
|
+
});
|
|
1600
|
+
if (probe.exitCode === 0) {
|
|
1601
|
+
findRoot = candidate;
|
|
1602
|
+
// The remainder keeps the `-path` form: `*` crosses `/`, so a `.d.ts`
|
|
1603
|
+
// in `dist/utilities/` is still found (see findPathArgs).
|
|
1604
|
+
findArgs = findPathArgs(pattern.replace(/^(?:\.?\/)+/, '').slice(prefixDir.length + 1));
|
|
1605
|
+
}
|
|
1606
|
+
else {
|
|
1607
|
+
missingPrefix = `"${candidate}" does not exist; searched all of ${path}.`;
|
|
1608
|
+
}
|
|
1609
|
+
}
|
|
1546
1610
|
// `findPatternArgs`, not `-name`: a pattern with a `/` in it
|
|
1547
1611
|
// (`helpers/*`, `e2e/*.spec.ts`) matched nothing at all, because `-name`
|
|
1548
1612
|
// compares it against the basename (xr-molecule-flash-m77).
|
|
1549
|
-
const result = await backend.run(`find ${shellQuote(
|
|
1613
|
+
const result = await backend.run(`find ${shellQuote(findRoot)} ${findPruneArgs(findRoot)} ${findArgs} -print 2>/dev/null | head -${MAX_FIND_RESULTS}`, { timeout: SEARCH_TIMEOUT_MS });
|
|
1550
1614
|
const files = result.stdout.trim().split('\n').filter(Boolean);
|
|
1551
1615
|
// A slash changes what the pattern means — the whole path, not the file
|
|
1552
1616
|
// name — and an empty result does not say so, so the model reads it as
|
|
1553
1617
|
// "no such file" and goes looking elsewhere. Name the rule on the miss.
|
|
1554
|
-
const
|
|
1618
|
+
const missNote = files.length === 0 && pattern.includes('/')
|
|
1555
1619
|
? `"${pattern}" contains a "/", so it was matched against each file's PATH below ${path} (a leading "*/" is implicit), not against the file name — no path matched it. list_files shows the real entries.`
|
|
1556
1620
|
: undefined;
|
|
1557
|
-
|
|
1621
|
+
const notes = [missingPrefix, missNote].filter((n) => Boolean(n));
|
|
1622
|
+
return { pattern, path, files, ...(notes.length ? { note: notes.join(' ') } : {}) };
|
|
1558
1623
|
}
|
|
1559
1624
|
catch (e) {
|
|
1625
|
+
if (isCommandTimeout(e))
|
|
1626
|
+
return {
|
|
1627
|
+
error: searchTimeoutError('find_files', findRoot === path ? path : `${findRoot} (under ${path})`, pattern, SEARCH_TIMEOUT_MS / 1000, `Give \`path\` the directory you mean (a package's own folder rather than all of ` +
|
|
1628
|
+
`${path}) or use list_files on it, and keep the pattern narrow.`),
|
|
1629
|
+
};
|
|
1560
1630
|
return { error: `Find failed: ${e.message}` };
|
|
1561
1631
|
}
|
|
1562
1632
|
},
|
|
@@ -1825,18 +1895,31 @@ export function buildTools(backend, config) {
|
|
|
1825
1895
|
: sanitizeOutput;
|
|
1826
1896
|
const stdout = grade(truncateMiddle(result.stdout, MAX_OUTPUT_SIZE));
|
|
1827
1897
|
const stderr = sanitizeOutput(truncateMiddle(result.stderr, MAX_OUTPUT_SIZE));
|
|
1828
|
-
|
|
1898
|
+
// A run a budget stopped is NOT a run that ended: it reports `status: 'cut_at_budget'`,
|
|
1899
|
+
// never a 0 the pipeline produced, and names how far it got. Read from the command, the
|
|
1900
|
+
// output and the clock in one place (cut-run.ts) so every reader of a run's verdict reads
|
|
1901
|
+
// the same state (X1 xr-molecule-flash-m93: `timeout 280 hurl … | tail -60` came back
|
|
1902
|
+
// `exitCode: 0` after 284 s and four of thirteen files, and the executor re-sent it).
|
|
1903
|
+
const cut = commandCutRun(command, { exitCode: result.exitCode, stdout, stderr }, elapsedMs, budgetSeconds);
|
|
1904
|
+
if (cut) {
|
|
1905
|
+
const cutLead = cutRunSentence(cut);
|
|
1829
1906
|
return {
|
|
1830
1907
|
stdout,
|
|
1831
1908
|
stderr,
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1909
|
+
status: CUT_AT_BUDGET,
|
|
1910
|
+
exitCode: cut.exitCode,
|
|
1911
|
+
// The facts, so a reader that can also see the suite itself (its own `.hurl` file
|
|
1912
|
+
// count) says the same sentence with the total the output alone cannot give.
|
|
1913
|
+
cutRun: cut,
|
|
1914
|
+
error: `${cutLead} ` +
|
|
1915
|
+
// The tool's own budget stopping a run that printed nothing: keep the sentence that
|
|
1916
|
+
// says why the output is empty, since nothing else in the result can say it.
|
|
1917
|
+
(cut.stoppedBy === 'tool-budget'
|
|
1918
|
+
? `The output above is everything it printed until then${/\|\s*(tail|head)\b/.test(command)
|
|
1919
|
+
? ' — and a pipe through tail/head holds everything back until the command ends, so it printed nothing: run it without the pipe (use a line reporter and let the output stream)'
|
|
1920
|
+
: ''}. `
|
|
1837
1921
|
: '') +
|
|
1838
|
-
|
|
1839
|
-
'a build and a whole suite.',
|
|
1922
|
+
(isSuiteRunCommand(command) ? SUITE_OVER_BUDGET_ADVICE : SPLIT_WORK_ADVICE),
|
|
1840
1923
|
};
|
|
1841
1924
|
}
|
|
1842
1925
|
return {
|
|
@@ -2175,6 +2258,28 @@ export function buildTools(backend, config) {
|
|
|
2175
2258
|
// the second and third were found by runs stopped before they walked the rest. Null for
|
|
2176
2259
|
// anything that is not a run with failures in it.
|
|
2177
2260
|
const causes = failureCausesNote(output);
|
|
2261
|
+
// A backgrounded run is under no tool budget, and the exit file hands back the PIPELINE's
|
|
2262
|
+
// code — so a suite killed at a `timeout 280` the command carried behind a `| tail` reports
|
|
2263
|
+
// 0 here too. The run's start is in its own id ({@link taskStartedAt}), so the bound it used
|
|
2264
|
+
// is readable without new state: say stopped, and never hand the pipeline's 0 back as the
|
|
2265
|
+
// run's (X1 xr-molecule-flash-m93 re-sent that run three times).
|
|
2266
|
+
const run = runByTask.get(taskId);
|
|
2267
|
+
const startedAtMs = taskStartedAt(taskId);
|
|
2268
|
+
const cut = run && startedAtMs !== null
|
|
2269
|
+
? commandCutRun(run.command, { exitCode, stdout: output, stderr: '' }, Date.now() - startedAtMs, 0)
|
|
2270
|
+
: null;
|
|
2271
|
+
if (cut) {
|
|
2272
|
+
return {
|
|
2273
|
+
taskId,
|
|
2274
|
+
status: CUT_AT_BUDGET,
|
|
2275
|
+
exitCode: cut.exitCode,
|
|
2276
|
+
waitedMs,
|
|
2277
|
+
cutRun: cut,
|
|
2278
|
+
note: [cutRunSentence(cut), causes].filter((n) => Boolean(n)).join(' '),
|
|
2279
|
+
stdout: sanitizeOutput(truncateMiddle(output, MAX_OUTPUT_SIZE)),
|
|
2280
|
+
...(output.length > MAX_OUTPUT_SIZE ? { truncated: true, fullOutput: log } : {}),
|
|
2281
|
+
};
|
|
2282
|
+
}
|
|
2178
2283
|
// Same shaping as a foreground exec result, so the executor reads both
|
|
2179
2284
|
// the same way.
|
|
2180
2285
|
return {
|
package/dist/utilities.d.ts
CHANGED
|
@@ -274,6 +274,81 @@ export declare function isValidGlob(pattern: string): boolean;
|
|
|
274
274
|
* `-path '…'` alternatives, ORed inside parens when there is more than one.
|
|
275
275
|
*/
|
|
276
276
|
export declare function findPatternArgs(pattern: string): string;
|
|
277
|
+
/**
|
|
278
|
+
* The `-path` alternatives of {@link findPatternArgs}, for a pattern whose
|
|
279
|
+
* directory the caller has already rooted the search at.
|
|
280
|
+
*
|
|
281
|
+
* `find_files` narrows the search root itself when the pattern spells its own
|
|
282
|
+
* directory out (see {@link literalDirPrefix}) — `node_modules` plus
|
|
283
|
+
* `@molecule/api-resource-user/dist/*.d.ts` searched that one directory. The
|
|
284
|
+
* remainder (`*.d.ts`) must then keep the `-path` form rather than collapsing to
|
|
285
|
+
* `-name`: `*` crosses `/` in an fnmatch glob, so the `-path` form still finds a
|
|
286
|
+
* declaration file in a `dist/utilities/` subdirectory, which `-name '*.d.ts'`
|
|
287
|
+
* would drop (a silent partial answer, which is worse than the timeout it
|
|
288
|
+
* replaced).
|
|
289
|
+
*
|
|
290
|
+
* @param pattern - The pattern's remainder, with a `/` in it or not.
|
|
291
|
+
* @returns One or more `-path '…'` alternatives, ORed inside parens when there
|
|
292
|
+
* is more than one.
|
|
293
|
+
*/
|
|
294
|
+
export declare function findPathArgs(pattern: string): string;
|
|
295
|
+
/**
|
|
296
|
+
* The directory a GLOB pattern spells out, when it spells one out: everything
|
|
297
|
+
* before the pattern's first glob character, trimmed back to the last `/`.
|
|
298
|
+
*
|
|
299
|
+
* A pattern is matched against the whole path with a leading "any parent"
|
|
300
|
+
* anchor ({@link findPatternArgs}), so `find` visits every entry below the
|
|
301
|
+
* search root. Handed `/workspace/node_modules` and
|
|
302
|
+
* `@molecule/api-resource-user/dist/*.d.ts` — a directory named outright —
|
|
303
|
+
* `find` walked the sandbox's entire dependency tree and hit the tool's 10 s
|
|
304
|
+
* deadline without printing a result (xr-molecule-flash-m94: the one directory
|
|
305
|
+
* the model meant answered a `list_files` in 0 s). The literal prefix is that
|
|
306
|
+
* directory, and rooting the search at it is what keeps the call instant.
|
|
307
|
+
*
|
|
308
|
+
* A BARE first segment (`helpers/*`, `bonds/*`, `e2e/*.spec.ts`) is NOT one:
|
|
309
|
+
* `helpers/*` means "a `helpers/` directory anywhere below the root", which the
|
|
310
|
+
* tool documents and a live run relied on (xr-molecule-flash-m77 — a path
|
|
311
|
+
* pattern is not anchored to one depth). Rooting those at
|
|
312
|
+
* `<root>/helpers` would answer with a SUBSET, silently: the nested
|
|
313
|
+
* `api/src/helpers/` matches would disappear, and a missing file reads as
|
|
314
|
+
* "no such file". So a prefix is returned only when it names a path of two or
|
|
315
|
+
* more segments (`@molecule/api-resource-user/dist`), which is the shape that
|
|
316
|
+
* is a directory rather than a directory name.
|
|
317
|
+
*
|
|
318
|
+
* @param pattern - A pattern already accepted by {@link isValidGlob}.
|
|
319
|
+
* @returns The literal directory prefix, without a trailing slash, or null when
|
|
320
|
+
* the pattern has no glob character, no `/`, or only a bare name before one.
|
|
321
|
+
*/
|
|
322
|
+
export declare function literalDirPrefix(pattern: string): string | null;
|
|
323
|
+
/**
|
|
324
|
+
* Whether a backend error is the sandbox's own command deadline.
|
|
325
|
+
*
|
|
326
|
+
* The E2B SDK answers a timed-out `exec` with its generic text —
|
|
327
|
+
* `[deadline_exceeded] the operation timed out: … It can be modified by passing
|
|
328
|
+
* 'timeoutMs' when making the request. Use '0' to disable the timeout.` — which
|
|
329
|
+
* names an option the executor cannot set and says nothing about the search
|
|
330
|
+
* that died. Handing that sentence to the model is what xr-molecule-flash-m94
|
|
331
|
+
* read after `find_files` spent 10 s walking `node_modules`; the tool answers
|
|
332
|
+
* the deadline in its own words instead (see {@link searchTimeoutError}).
|
|
333
|
+
*
|
|
334
|
+
* @param error - The error the backend threw.
|
|
335
|
+
* @returns True when the error reads as a timeout or a deadline.
|
|
336
|
+
*/
|
|
337
|
+
export declare function isCommandTimeout(error: unknown): boolean;
|
|
338
|
+
/**
|
|
339
|
+
* The answer a search tool gives when the backend's deadline fires: what was
|
|
340
|
+
* searched, for what, how long the tool allowed, and the two ways to get an
|
|
341
|
+
* answer — a narrower `path`, or a listing. Never the vendor's `timeoutMs`
|
|
342
|
+
* sentence (see {@link isCommandTimeout}).
|
|
343
|
+
*
|
|
344
|
+
* @param tool - The tool that timed out (`find_files`, `search_files`).
|
|
345
|
+
* @param searched - The directory the search actually covered.
|
|
346
|
+
* @param pattern - The pattern it was searching for.
|
|
347
|
+
* @param seconds - The tool's own limit, in seconds.
|
|
348
|
+
* @param hint - What to do differently — the narrower target or the listing tool.
|
|
349
|
+
* @returns The message's text.
|
|
350
|
+
*/
|
|
351
|
+
export declare function searchTimeoutError(tool: string, searched: string, pattern: string, seconds: number, hint: string): string;
|
|
277
352
|
/**
|
|
278
353
|
* Validate a file tool's `path` argument is a non-empty string. A weak model
|
|
279
354
|
* sometimes omits it or passes a non-string, which would otherwise crash
|
package/dist/utilities.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utilities.d.ts","sourceRoot":"","sources":["../src/utilities.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAcH;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAS5C;AAED;;;;;;GAMG;AACH,eAAO,MAAM,iBAAiB,GAAI,GAAG,MAAM,KAAG,MAEE,CAAA;AAuGhD;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAK/C;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAErD;AAKD;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAsCzD;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAGnD;AAED;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,OAAO,GAAG,MAAM,CAUpF;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAElE;AAED;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAWnE;AAuDD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,MAAM,GAAG;IAC3D,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,EAAE,CAAA;CAClB,CA6FA;AAsBD;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAK1D;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAG/D;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,GACb;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAalC;AA4BD;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAuBlE;AAID;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAQrE;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAOxE;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAQpD;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"utilities.d.ts","sourceRoot":"","sources":["../src/utilities.ts"],"names":[],"mappings":"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"}
|