@molecule/api-ai-tools 1.5.0 → 1.5.2

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":"schemas.d.ts","sourceRoot":"","sources":["../src/schemas.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAElD;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,EAAE,MAAM,CAAA;IACnB,UAAU,EAAE,UAAU,CAAA;CACvB;AAED;;GAEG;AACH,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CA+T1C,CAAA"}
1
+ {"version":3,"file":"schemas.d.ts","sourceRoot":"","sources":["../src/schemas.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAElD;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,EAAE,MAAM,CAAA;IACnB,UAAU,EAAE,UAAU,CAAA;CACvB;AAED;;GAEG;AACH,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAgU1C,CAAA"}
package/dist/schemas.js CHANGED
@@ -121,7 +121,7 @@ export const TOOL_SCHEMAS = {
121
121
  properties: {
122
122
  pattern: {
123
123
  type: 'string',
124
- description: 'Text or regex pattern to search for.',
124
+ description: 'Extended regex (`a|b`, `(x)+`) or plain text to search for; a pattern that is not a valid regex is searched as plain text.',
125
125
  },
126
126
  path: {
127
127
  type: 'string',
@@ -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;AAoB9C,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAgCnE;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,gBAAgB,EAAE,MAAM,CAAC,EAAE,eAAe,GAAG,MAAM,EAAE,CAu3CxF"}
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;AA2B9C,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAgCnE;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,gBAAgB,EAAE,MAAM,CAAC,EAAE,eAAe,GAAG,MAAM,EAAE,CAs6CxF"}
package/dist/tools.js CHANGED
@@ -16,6 +16,13 @@ import { TOOL_SCHEMAS } from './schemas.js';
16
16
  const stuckAfterMs = () => Number(process.env.MOL_AI_BG_STUCK_AFTER_MS) || 5 * 60 * 1000;
17
17
  /** Cumulative wait_for_task time per task id, this process. */
18
18
  const waitedByTask = new Map();
19
+ /**
20
+ * Each waited task's log size at the end of its last wait. A command whose log
21
+ * keeps growing is slow, not stuck: a serial 139-test suite was called stuck at
22
+ * 560 s and the executor was told to kill it while its log still grew (X1 run
23
+ * m21, 2026-10-15).
24
+ */
25
+ const logBytesByTask = new Map();
19
26
  import { checkBlockedCommand, declaredBudgetSeconds, DEFAULT_SEARCH_EXCLUDED_DIRS, directoryReadHint, isEnvFilePath, isOutsideRoot, 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';
20
27
  /**
21
28
  * Build a complete set of AI agent tools bound to an execution backend.
@@ -827,7 +834,13 @@ export function buildTools(backend, config) {
827
834
  // `file:line:content` for matches, plus `--` group separators. Both
828
835
  // shapes are parsed below; without context the output is unchanged.
829
836
  const contextArg = contextLines > 0 ? `-C ${contextLines}` : '';
830
- const result = await backend.run(`grep -rn ${globArg} ${contextArg} ${grepExcludeArgs(path)} --max-count=${MAX_SEARCH_RESULTS} -- ${shellQuote(pattern)} ${shellQuote(path)} 2>/dev/null || true`, { timeout: 10000 });
837
+ // Extended regex, the dialect a pattern like `useTransitions|BrowserRouter`
838
+ // is written in: as a basic regex its `|` was a literal character and the
839
+ // search found nothing (X1 run sol-m07). A pattern that is not a valid
840
+ // extended regex (`foo(` — an unbalanced paren) makes grep exit 2; it is
841
+ // then searched as the plain text it was meant as.
842
+ const grepArgs = `-rn ${globArg} ${contextArg} ${grepExcludeArgs(path)} --max-count=${MAX_SEARCH_RESULTS} -- ${shellQuote(pattern)} ${shellQuote(path)}`;
843
+ const result = await backend.run(`{ grep -E ${grepArgs}; s=$?; if [ "$s" -eq 2 ]; then grep -F ${grepArgs}; fi; } 2>/dev/null || true`, { timeout: 10000 });
831
844
  // grep emits `<file>:<line>:<content>`, so redaction runs PER MATCH on the
832
845
  // content alone: the file prefix would otherwise hide an env assignment from
833
846
  // the line-anchored env pattern, and knowing each match's own path is what
@@ -1152,7 +1165,33 @@ export function buildTools(backend, config) {
1152
1165
  // exiting is not going to: on one run the executor waited five times,
1153
1166
  // 120 s each, on an e2e suite that could never start (no browser page
1154
1167
  // attached). Say so once, name the pid, and refuse to wait on it again.
1155
- const waitedBefore = waitedByTask.get(taskId) ?? 0;
1168
+ let waitedBefore = waitedByTask.get(taskId) ?? 0;
1169
+ // Output since the last wait means the command is working: the stuck
1170
+ // clock starts again from here.
1171
+ const logBytes = async () => {
1172
+ if (!/^mol-bg-[a-z0-9]+-[a-z0-9]+$/.test(taskId))
1173
+ return null;
1174
+ let r;
1175
+ try {
1176
+ r = await backend.run(`wc -c < /tmp/${taskId}.log 2>/dev/null`, { timeout: 5_000 });
1177
+ }
1178
+ catch (_error) {
1179
+ // No answer about the log: treat the size as unknown, which keeps the
1180
+ // stuck verdict exactly as before.
1181
+ r = null;
1182
+ }
1183
+ const n = Number.parseInt(r?.stdout?.trim() ?? '', 10);
1184
+ return Number.isFinite(n) ? n : null;
1185
+ };
1186
+ if (taskId && waitedBefore >= stuckAfterMs()) {
1187
+ const now = await logBytes();
1188
+ const before = logBytesByTask.get(taskId);
1189
+ if (now !== null && before !== undefined && now > before) {
1190
+ logBytesByTask.set(taskId, now);
1191
+ waitedByTask.set(taskId, 0);
1192
+ waitedBefore = 0;
1193
+ }
1194
+ }
1156
1195
  if (taskId && waitedBefore >= stuckAfterMs()) {
1157
1196
  return {
1158
1197
  taskId,
@@ -1198,6 +1237,22 @@ export function buildTools(backend, config) {
1198
1237
  const waitedMs = Date.now() - startedAt;
1199
1238
  waitedByTask.set(taskId, waitedBefore + waitedMs);
1200
1239
  if (exitCode === null) {
1240
+ const grownTo = await logBytes();
1241
+ const lastSize = logBytesByTask.get(taskId);
1242
+ if (grownTo !== null)
1243
+ logBytesByTask.set(taskId, grownTo);
1244
+ const stillWriting = grownTo !== null && (lastSize === undefined ? grownTo > 0 : grownTo > lastSize);
1245
+ if (stillWriting && waitedBefore + waitedMs >= stuckAfterMs()) {
1246
+ waitedByTask.set(taskId, 0);
1247
+ return {
1248
+ taskId,
1249
+ status: 'running',
1250
+ waitedMs,
1251
+ note: `Still running after ${Math.round((waitedBefore + waitedMs) / 1000)}s of waiting, and its ` +
1252
+ `output is still growing (${grownTo} bytes) — slow, not stuck. Call wait_for_task again to ` +
1253
+ `keep waiting; peek with exec_command: tail ${log}.`,
1254
+ };
1255
+ }
1201
1256
  if (waitedBefore + waitedMs >= stuckAfterMs()) {
1202
1257
  return {
1203
1258
  taskId,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@molecule/api-ai-tools",
3
- "version": "1.5.0",
3
+ "version": "1.5.2",
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",