@molecule/api-ai-tools 1.2.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.
@@ -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,CA2ClB"}
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"}
@@ -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 });
@@ -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;AA+BnE;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,gBAAgB,EAAE,MAAM,CAAC,EAAE,eAAe,GAAG,MAAM,EAAE,CA0tCxF"}
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
@@ -527,6 +527,7 @@ export function buildTools(backend, config) {
527
527
  content = verdict.content;
528
528
  }
529
529
  const oldContent = content;
530
+ let alreadyApplied = 0;
530
531
  for (const { old_string: oldString, new_string: newString } of replacements) {
531
532
  // Validate each replacement is well-formed BEFORE touching content. A
532
533
  // weak model sometimes emits a batch element missing a field or with a
@@ -539,6 +540,19 @@ export function buildTools(backend, config) {
539
540
  };
540
541
  }
541
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
+ }
542
556
  if (count === 0) {
543
557
  // A bare "old_string not found" gives the model nothing to correct, so
544
558
  // it retries blindly (the #1 edit_file failure mode). Diagnose WHY the
@@ -648,6 +662,12 @@ export function buildTools(backend, config) {
648
662
  const at = content.indexOf(oldString);
649
663
  content = content.slice(0, at) + newString + content.slice(at + oldString.length);
650
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
+ }
651
671
  if (onFileDiff)
652
672
  onFileDiff({ path, oldContent, newContent: content });
653
673
  await backend.writeFile(path, content);
@@ -658,7 +678,8 @@ export function buildTools(backend, config) {
658
678
  return {
659
679
  path,
660
680
  ok: true,
661
- replacementsApplied: replacements.length,
681
+ replacementsApplied: replacements.length - alreadyApplied,
682
+ ...(alreadyApplied > 0 ? { alreadyApplied, note: alreadyNote } : {}),
662
683
  ...(await syntaxErrorAfterWrite(path)),
663
684
  };
664
685
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@molecule/api-ai-tools",
3
- "version": "1.2.0",
3
+ "version": "1.2.1",
4
4
  "description": "Shared AI agent tools with backend abstraction for sandbox and local execution",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",