@molecule/api-ai-tools 1.1.0 → 1.2.1
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 +40 -1
- package/dist/backends/sandbox.d.ts.map +1 -1
- package/dist/backends/sandbox.js +9 -1
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +52 -3
- package/dist/utilities.d.ts +23 -0
- package/dist/utilities.d.ts.map +1 -1
- package/dist/utilities.js +36 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@ AUTO-GENERATED — DO NOT EDIT THIS FILE.
|
|
|
3
3
|
Generated by `mlcl sync-docs` from the package's src/index.ts JSDoc + mlcl/registry.json.
|
|
4
4
|
Edits here are overwritten on the next commit (molecule's pre-commit hook regenerates).
|
|
5
5
|
To change this document, edit the module-level JSDoc in src/index.ts.
|
|
6
|
-
Generated: 2026-09-
|
|
6
|
+
Generated: 2026-09-22T21:57:22.227Z
|
|
7
7
|
-->
|
|
8
8
|
|
|
9
9
|
# @molecule/api-ai-tools
|
|
@@ -581,6 +581,37 @@ function outputIsWithheldUntilExit(command: string): boolean
|
|
|
581
581
|
|
|
582
582
|
**Returns:** True when a kill would return no useful output.
|
|
583
583
|
|
|
584
|
+
#### `parseCheckCommand(path)`
|
|
585
|
+
|
|
586
|
+
The shell command that parses one file and exits non-zero with the error
|
|
587
|
+
when it does not parse: `node --check` for JavaScript, `JSON.parse` for
|
|
588
|
+
JSON, esbuild's transform (present wherever Vite is) for TypeScript.
|
|
589
|
+
|
|
590
|
+
```typescript
|
|
591
|
+
function parseCheckCommand(path: string): string | null
|
|
592
|
+
```
|
|
593
|
+
|
|
594
|
+
- `path` — The file's absolute path.
|
|
595
|
+
|
|
596
|
+
**Returns:** The command, or null when the file type has no cheap parser.
|
|
597
|
+
|
|
598
|
+
#### `parseCheckProblem(result)`
|
|
599
|
+
|
|
600
|
+
What a failed parse check says to the executor, or null when the check
|
|
601
|
+
passed or could not run (no parser available is not a syntax error).
|
|
602
|
+
|
|
603
|
+
```typescript
|
|
604
|
+
function parseCheckProblem(result: {
|
|
605
|
+
stdout: string
|
|
606
|
+
stderr: string
|
|
607
|
+
exitCode: number
|
|
608
|
+
}): string | null
|
|
609
|
+
```
|
|
610
|
+
|
|
611
|
+
- `result` — The check command's result.
|
|
612
|
+
|
|
613
|
+
**Returns:** The message for the tool result, or null.
|
|
614
|
+
|
|
584
615
|
#### `pathArgError(path, tool)`
|
|
585
616
|
|
|
586
617
|
Validate a file tool's `path` argument is a non-empty string. A weak model
|
|
@@ -881,6 +912,14 @@ Max content size for write_file (10MB).
|
|
|
881
912
|
const MAX_WRITE_SIZE: number
|
|
882
913
|
```
|
|
883
914
|
|
|
915
|
+
#### `PARSE_CHECKABLE`
|
|
916
|
+
|
|
917
|
+
Files a parse check can vouch for cheaply right after a write.
|
|
918
|
+
|
|
919
|
+
```typescript
|
|
920
|
+
const PARSE_CHECKABLE: RegExp
|
|
921
|
+
```
|
|
922
|
+
|
|
884
923
|
#### `TOOL_SCHEMAS`
|
|
885
924
|
|
|
886
925
|
Canonical tool schemas shared by the agent runtime and documentation.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sandbox.d.ts","sourceRoot":"","sources":["../../src/backends/sandbox.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAGnD,sFAAsF;AACtF,UAAU,WAAW;IACnB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;IACvC,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACvD,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACvC,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAAA;KAAE,CAAC,CAAC,CAAA;IACnF,mEAAmE;IAEnE,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;CACnB;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,WAAW,EACpB,WAAW,SAAe,GACzB,gBAAgB,
|
|
1
|
+
{"version":3,"file":"sandbox.d.ts","sourceRoot":"","sources":["../../src/backends/sandbox.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAGnD,sFAAsF;AACtF,UAAU,WAAW;IACnB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;IACvC,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACvD,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACvC,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAAA;KAAE,CAAC,CAAC,CAAA;IACnF,mEAAmE;IAEnE,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;CACnB;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,WAAW,EACpB,WAAW,SAAe,GACzB,gBAAgB,CAmDlB"}
|
package/dist/backends/sandbox.js
CHANGED
|
@@ -42,9 +42,17 @@ export function createSandboxBackend(sandbox, projectRoot = '/workspace') {
|
|
|
42
42
|
// printed still comes back, with exit code 124. Wrapping only the inner
|
|
43
43
|
// command would run it in a child shell that never sees unexported variables
|
|
44
44
|
// the consumer's sourcing set.
|
|
45
|
+
// Its output goes to files that are printed once the shell exits, never to
|
|
46
|
+
// the exec's own pipe: `cd app && server > log &` backgrounds a SUBSHELL whose
|
|
47
|
+
// stdout is still that pipe (the redirect covers the server only), and the
|
|
48
|
+
// exec waits for every holder of the pipe — past the budget, which kills the
|
|
49
|
+
// shell but not the detached subshell, until the caller's outer timeout
|
|
50
|
+
// throws the output away. Measured: three 300 s calls in two runs (X0 x31,
|
|
51
|
+
// x33). A background child now holds a file, and the call returns when the
|
|
52
|
+
// command does.
|
|
45
53
|
const budgetSeconds = opts?.budgetMs ? Math.max(1, Math.round(opts.budgetMs / 1000)) : 0;
|
|
46
54
|
const fullCommand = budgetSeconds
|
|
47
|
-
? `timeout -k 5 ${budgetSeconds} bash -c ${shellQuote(anchored)}`
|
|
55
|
+
? `o=$(mktemp); e=$(mktemp); timeout -k 5 ${budgetSeconds} bash -c ${shellQuote(anchored)} > "$o" 2> "$e" < /dev/null; c=$?; cat "$o"; cat "$e" >&2; rm -f "$o" "$e"; exit $c`
|
|
48
56
|
: anchored;
|
|
49
57
|
// sandbox.exec is the Sandbox interface method — runs inside Docker, inherently sandboxed
|
|
50
58
|
const result = await sandbox.exec(fullCommand, { timeout: opts?.timeout });
|
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;AAY9C,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,YAAY,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;AAY9C,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AA+BnE;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,gBAAgB,EAAE,MAAM,CAAC,EAAE,eAAe,GAAG,MAAM,EAAE,CAmvCxF"}
|
package/dist/tools.js
CHANGED
|
@@ -15,7 +15,7 @@ import { TOOL_SCHEMAS } from './schemas.js';
|
|
|
15
15
|
const stuckAfterMs = () => Number(process.env.MOL_AI_BG_STUCK_AFTER_MS) || 5 * 60 * 1000;
|
|
16
16
|
/** Cumulative wait_for_task time per task id, this process. */
|
|
17
17
|
const waitedByTask = new Map();
|
|
18
|
-
import { checkBlockedCommand, declaredBudgetSeconds, DEFAULT_SEARCH_EXCLUDED_DIRS, directoryReadHint, isEnvFilePath, isValidGlob, MAX_BATCH_READ_BYTES, MAX_BATCH_READ_FILES, MAX_FIND_RESULTS, MAX_OUTPUT_SIZE, MAX_READ_RETURN_CHARS, MAX_READ_SIZE, MAX_SEARCH_CONTEXT_LINES, MAX_SEARCH_RESULTS, MAX_WRITE_SIZE, outputIsWithheldUntilExit, pathArgError, redactSecrets, redactSecretsInCode, resolvePath, shellQuote, sliceLines, stripControlChars, truncateMiddle, whitespaceTolerantReplace, } from './utilities.js';
|
|
18
|
+
import { checkBlockedCommand, declaredBudgetSeconds, DEFAULT_SEARCH_EXCLUDED_DIRS, directoryReadHint, isEnvFilePath, isValidGlob, MAX_BATCH_READ_BYTES, MAX_BATCH_READ_FILES, MAX_FIND_RESULTS, MAX_OUTPUT_SIZE, MAX_READ_RETURN_CHARS, MAX_READ_SIZE, MAX_SEARCH_CONTEXT_LINES, MAX_SEARCH_RESULTS, MAX_WRITE_SIZE, outputIsWithheldUntilExit, parseCheckCommand, parseCheckProblem, pathArgError, redactSecrets, redactSecretsInCode, resolvePath, shellQuote, sliceLines, stripControlChars, truncateMiddle, whitespaceTolerantReplace, } from './utilities.js';
|
|
19
19
|
/**
|
|
20
20
|
* Build a complete set of AI agent tools bound to an execution backend.
|
|
21
21
|
*
|
|
@@ -51,6 +51,29 @@ export function buildTools(backend, config) {
|
|
|
51
51
|
const grepExcludeArgs = excludedDirs.map((d) => `--exclude-dir=${shellQuote(d)}`).join(' ');
|
|
52
52
|
const findExcludeArgs = excludedDirs.map((d) => `-not -path ${shellQuote(`*/${d}/*`)}`).join(' ');
|
|
53
53
|
// ── Path helpers ───────────────────────────────────────────────
|
|
54
|
+
/**
|
|
55
|
+
* After a write or edit, parse the file and hand the error back IN THE SAME
|
|
56
|
+
* RESULT. An edit that leaves a file unparsable used to surface only at the
|
|
57
|
+
* next build or type-check, minutes and many calls later — x13 (2026-09-22)
|
|
58
|
+
* mangled a template literal in `scripts/prerender.mjs`, the site stopped
|
|
59
|
+
* building, and the fix round spent 500 calls on the fallout. `node --check`
|
|
60
|
+
* is ~50 ms; the executor sees the exact line while the edit is still in
|
|
61
|
+
* front of it.
|
|
62
|
+
*/
|
|
63
|
+
async function syntaxErrorAfterWrite(path) {
|
|
64
|
+
const command = parseCheckCommand(path);
|
|
65
|
+
if (!command)
|
|
66
|
+
return {};
|
|
67
|
+
const result = await backend
|
|
68
|
+
.run(command, { cwd: path.slice(0, path.lastIndexOf('/')) || '/', timeout: 15_000 })
|
|
69
|
+
.catch(() => null);
|
|
70
|
+
const problem = result ? parseCheckProblem(result) : null;
|
|
71
|
+
return problem
|
|
72
|
+
? {
|
|
73
|
+
syntaxError: `The file no longer parses after this change — fix it before anything else, every build and test fails until it does:\n${problem}`,
|
|
74
|
+
}
|
|
75
|
+
: {};
|
|
76
|
+
}
|
|
54
77
|
/**
|
|
55
78
|
* Resolve a workspace-relative path using optional path guards.
|
|
56
79
|
*
|
|
@@ -464,7 +487,7 @@ export function buildTools(backend, config) {
|
|
|
464
487
|
const diff = computeDiff(oldContent, finalContent);
|
|
465
488
|
if (onFileChange)
|
|
466
489
|
onFileChange({ type: oldContent === null ? 'created' : 'modified', path });
|
|
467
|
-
return { path, ok: true, diff };
|
|
490
|
+
return { path, ok: true, diff, ...(await syntaxErrorAfterWrite(path)) };
|
|
468
491
|
}
|
|
469
492
|
catch (e) {
|
|
470
493
|
return { error: `Failed to write ${path}: ${e.message}` };
|
|
@@ -504,6 +527,7 @@ export function buildTools(backend, config) {
|
|
|
504
527
|
content = verdict.content;
|
|
505
528
|
}
|
|
506
529
|
const oldContent = content;
|
|
530
|
+
let alreadyApplied = 0;
|
|
507
531
|
for (const { old_string: oldString, new_string: newString } of replacements) {
|
|
508
532
|
// Validate each replacement is well-formed BEFORE touching content. A
|
|
509
533
|
// weak model sometimes emits a batch element missing a field or with a
|
|
@@ -516,6 +540,19 @@ export function buildTools(backend, config) {
|
|
|
516
540
|
};
|
|
517
541
|
}
|
|
518
542
|
const count = content.split(oldString).length - 1;
|
|
543
|
+
// The edit is ALREADY in the file: old_string is gone and a distinctive
|
|
544
|
+
// new_string is there exactly once. The executor re-sends an edit it
|
|
545
|
+
// made a few calls ago (it works from its memory of the file), and the
|
|
546
|
+
// "not found" error cost it a read_file plus a retry each time — 6 to 11
|
|
547
|
+
// times a build in X0 x30/x34. Report it as applied instead. Short or
|
|
548
|
+
// repeated new_strings still error: they could be there by coincidence.
|
|
549
|
+
if (count === 0 &&
|
|
550
|
+
newString !== oldString &&
|
|
551
|
+
newString.trim().length >= 20 &&
|
|
552
|
+
content.split(newString).length - 1 === 1) {
|
|
553
|
+
alreadyApplied++;
|
|
554
|
+
continue;
|
|
555
|
+
}
|
|
519
556
|
if (count === 0) {
|
|
520
557
|
// A bare "old_string not found" gives the model nothing to correct, so
|
|
521
558
|
// it retries blindly (the #1 edit_file failure mode). Diagnose WHY the
|
|
@@ -625,6 +662,12 @@ export function buildTools(backend, config) {
|
|
|
625
662
|
const at = content.indexOf(oldString);
|
|
626
663
|
content = content.slice(0, at) + newString + content.slice(at + oldString.length);
|
|
627
664
|
}
|
|
665
|
+
const alreadyNote = alreadyApplied > 0
|
|
666
|
+
? `${alreadyApplied} of these edits ${alreadyApplied === 1 ? 'was' : 'were'} already in the file (its new_string is there and its old_string is not) — nothing to do for ${alreadyApplied === 1 ? 'it' : 'them'}.`
|
|
667
|
+
: null;
|
|
668
|
+
if (content === oldContent) {
|
|
669
|
+
return { path, ok: true, replacementsApplied: 0, alreadyApplied, note: alreadyNote };
|
|
670
|
+
}
|
|
628
671
|
if (onFileDiff)
|
|
629
672
|
onFileDiff({ path, oldContent, newContent: content });
|
|
630
673
|
await backend.writeFile(path, content);
|
|
@@ -632,7 +675,13 @@ export function buildTools(backend, config) {
|
|
|
632
675
|
await onAfterWrite(path).catch(() => { });
|
|
633
676
|
if (onFileChange)
|
|
634
677
|
onFileChange({ type: 'modified', path });
|
|
635
|
-
return {
|
|
678
|
+
return {
|
|
679
|
+
path,
|
|
680
|
+
ok: true,
|
|
681
|
+
replacementsApplied: replacements.length - alreadyApplied,
|
|
682
|
+
...(alreadyApplied > 0 ? { alreadyApplied, note: alreadyNote } : {}),
|
|
683
|
+
...(await syntaxErrorAfterWrite(path)),
|
|
684
|
+
};
|
|
636
685
|
}
|
|
637
686
|
catch (e) {
|
|
638
687
|
return { error: `Failed to edit ${path}: ${e.message}` };
|
package/dist/utilities.d.ts
CHANGED
|
@@ -263,4 +263,27 @@ export declare function truncateMiddle(s: string, maxLength: number): string;
|
|
|
263
263
|
* @returns The new content if a unique fuzzy run matched, else null.
|
|
264
264
|
*/
|
|
265
265
|
export declare function whitespaceTolerantReplace(content: string, oldString: string, newString: string): string | null;
|
|
266
|
+
/** Files a parse check can vouch for cheaply right after a write. */
|
|
267
|
+
export declare const PARSE_CHECKABLE: RegExp;
|
|
268
|
+
/**
|
|
269
|
+
* The shell command that parses one file and exits non-zero with the error
|
|
270
|
+
* when it does not parse: `node --check` for JavaScript, `JSON.parse` for
|
|
271
|
+
* JSON, esbuild's transform (present wherever Vite is) for TypeScript.
|
|
272
|
+
*
|
|
273
|
+
* @param path - The file's absolute path.
|
|
274
|
+
* @returns The command, or null when the file type has no cheap parser.
|
|
275
|
+
*/
|
|
276
|
+
export declare function parseCheckCommand(path: string): string | null;
|
|
277
|
+
/**
|
|
278
|
+
* What a failed parse check says to the executor, or null when the check
|
|
279
|
+
* passed or could not run (no parser available is not a syntax error).
|
|
280
|
+
*
|
|
281
|
+
* @param result - The check command's result.
|
|
282
|
+
* @returns The message for the tool result, or null.
|
|
283
|
+
*/
|
|
284
|
+
export declare function parseCheckProblem(result: {
|
|
285
|
+
stdout: string;
|
|
286
|
+
stderr: string;
|
|
287
|
+
exitCode: number;
|
|
288
|
+
}): string | null;
|
|
266
289
|
//# sourceMappingURL=utilities.d.ts.map
|
package/dist/utilities.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utilities.d.ts","sourceRoot":"","sources":["../src/utilities.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"utilities.d.ts","sourceRoot":"","sources":["../src/utilities.ts"],"names":[],"mappings":"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"}
|
package/dist/utilities.js
CHANGED
|
@@ -521,3 +521,39 @@ export function whitespaceTolerantReplace(content, oldString, newString) {
|
|
|
521
521
|
const start = matches[0];
|
|
522
522
|
return [...fileLines.slice(0, start), newString, ...fileLines.slice(start + normOld.length)].join('\n');
|
|
523
523
|
}
|
|
524
|
+
/** Files a parse check can vouch for cheaply right after a write. */
|
|
525
|
+
export const PARSE_CHECKABLE = /\.(m?js|cjs|jsx|ts|tsx|mts|cts|json)$/;
|
|
526
|
+
/**
|
|
527
|
+
* The shell command that parses one file and exits non-zero with the error
|
|
528
|
+
* when it does not parse: `node --check` for JavaScript, `JSON.parse` for
|
|
529
|
+
* JSON, esbuild's transform (present wherever Vite is) for TypeScript.
|
|
530
|
+
*
|
|
531
|
+
* @param path - The file's absolute path.
|
|
532
|
+
* @returns The command, or null when the file type has no cheap parser.
|
|
533
|
+
*/
|
|
534
|
+
export function parseCheckCommand(path) {
|
|
535
|
+
if (!PARSE_CHECKABLE.test(path))
|
|
536
|
+
return null;
|
|
537
|
+
const q = shellQuote(path);
|
|
538
|
+
if (/\.(m?js|cjs)$/.test(path))
|
|
539
|
+
return `node --check ${q}`;
|
|
540
|
+
if (/\.json$/.test(path))
|
|
541
|
+
return `node -e "JSON.parse(require('fs').readFileSync(process.argv[1],'utf8'))" ${q}`;
|
|
542
|
+
return (`node -e "const p=process.argv[1];const src=require('fs').readFileSync(p,'utf8');` +
|
|
543
|
+
`require('esbuild').transformSync(src,{loader:p.endsWith('x')?'tsx':'ts',logLevel:'silent'})" ${q}`);
|
|
544
|
+
}
|
|
545
|
+
/**
|
|
546
|
+
* What a failed parse check says to the executor, or null when the check
|
|
547
|
+
* passed or could not run (no parser available is not a syntax error).
|
|
548
|
+
*
|
|
549
|
+
* @param result - The check command's result.
|
|
550
|
+
* @returns The message for the tool result, or null.
|
|
551
|
+
*/
|
|
552
|
+
export function parseCheckProblem(result) {
|
|
553
|
+
if (result.exitCode === 0)
|
|
554
|
+
return null;
|
|
555
|
+
const text = (result.stderr || result.stdout).trim();
|
|
556
|
+
if (/Cannot find module|not found/i.test(text) && !/SyntaxError/.test(text))
|
|
557
|
+
return null;
|
|
558
|
+
return text.slice(0, 1200);
|
|
559
|
+
}
|