@morphllm/morphsdk 0.2.146 → 0.2.147

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.
Files changed (205) hide show
  1. package/dist/{chunk-J2HIK4GB.js → chunk-334JOOAC.js} +2 -2
  2. package/dist/{chunk-QRSWXP4K.js → chunk-3GXWZ7TI.js} +2 -2
  3. package/dist/{chunk-BDHKL3MT.js → chunk-4LZK67MS.js} +2 -2
  4. package/dist/{chunk-UVNENJ6H.js → chunk-4XNHMFVU.js} +3 -3
  5. package/dist/{chunk-HZOTLGJH.js → chunk-4Y2NM6JD.js} +42 -2
  6. package/dist/chunk-4Y2NM6JD.js.map +1 -0
  7. package/dist/{chunk-V73GO5AJ.js → chunk-7HKJUVAK.js} +2 -2
  8. package/dist/{chunk-NF2QWJDY.js → chunk-B3AKP3RA.js} +31 -2
  9. package/dist/chunk-B3AKP3RA.js.map +1 -0
  10. package/dist/{chunk-VZ6VYRQB.js → chunk-BXQYZR7O.js} +2 -2
  11. package/dist/{chunk-GJUB3ECP.js → chunk-CJD4ZFOX.js} +2 -2
  12. package/dist/chunk-CMSHXALI.js +60 -0
  13. package/dist/chunk-CMSHXALI.js.map +1 -0
  14. package/dist/{chunk-KYKRRF7E.js → chunk-DYCRGTRN.js} +2 -2
  15. package/dist/{chunk-EU7OLX4Z.js → chunk-ETKDGHOB.js} +2 -2
  16. package/dist/{chunk-SJYAKVSS.js → chunk-G4TRYATX.js} +2 -2
  17. package/dist/{chunk-SJYAKVSS.js.map → chunk-G4TRYATX.js.map} +1 -1
  18. package/dist/chunk-GK56GCLU.js +401 -0
  19. package/dist/chunk-GK56GCLU.js.map +1 -0
  20. package/dist/{chunk-I7SFRYTX.js → chunk-HWED34T3.js} +2 -2
  21. package/dist/{chunk-FIVYDIHX.js → chunk-HYRHI2UL.js} +1 -1
  22. package/dist/{chunk-DKODF3YG.js → chunk-I3J46TSB.js} +5 -4
  23. package/dist/chunk-I3J46TSB.js.map +1 -0
  24. package/dist/{chunk-E4YKEKGW.js → chunk-KV2CC4N7.js} +2 -2
  25. package/dist/{chunk-BIQ7234U.js → chunk-LSPEWVCD.js} +2 -2
  26. package/dist/{chunk-OV57JBMB.js → chunk-N7XLC7BK.js} +2 -2
  27. package/dist/{chunk-FBOJJ3UY.js → chunk-O356YR2N.js} +17 -17
  28. package/dist/{chunk-JSWNBCGS.js → chunk-PIHW2GSK.js} +2 -2
  29. package/dist/{chunk-UYPWKQKV.js → chunk-RGNP6FNH.js} +2 -2
  30. package/dist/{chunk-YIETFYCL.js → chunk-T7HF2TDQ.js} +75 -48
  31. package/dist/chunk-T7HF2TDQ.js.map +1 -0
  32. package/dist/{chunk-NKUSUSVI.js → chunk-T7VCGKXB.js} +3 -3
  33. package/dist/{chunk-Q36MNOFA.js → chunk-UZ2QUWJD.js} +2 -2
  34. package/dist/{chunk-FYO46OT6.js → chunk-VTTU7MJB.js} +2 -2
  35. package/dist/{chunk-4PBUB77N.js → chunk-XADFYB6B.js} +2 -2
  36. package/dist/{chunk-T564HFSH.js → chunk-XS4PO4FC.js} +1 -1
  37. package/dist/{chunk-E45FW5EK.js → chunk-YH3XIVF2.js} +2 -2
  38. package/dist/{chunk-MMBQKN4G.js → chunk-ZZW4B4GG.js} +2 -2
  39. package/dist/client.cjs +439 -446
  40. package/dist/client.cjs.map +1 -1
  41. package/dist/client.js +26 -27
  42. package/dist/edge.cjs +1 -1
  43. package/dist/edge.cjs.map +1 -1
  44. package/dist/edge.js +4 -4
  45. package/dist/{finish-DBKuo8yj.d.ts → finish-Ddj1MPGt.d.ts} +1 -1
  46. package/dist/index.cjs +458 -446
  47. package/dist/index.cjs.map +1 -1
  48. package/dist/index.js +29 -29
  49. package/dist/modelrouter/core.cjs +1 -1
  50. package/dist/modelrouter/core.cjs.map +1 -1
  51. package/dist/modelrouter/core.js +3 -3
  52. package/dist/modelrouter/index.cjs +1 -1
  53. package/dist/modelrouter/index.cjs.map +1 -1
  54. package/dist/modelrouter/index.js +3 -3
  55. package/dist/subagents/anthropic.cjs +435 -442
  56. package/dist/subagents/anthropic.cjs.map +1 -1
  57. package/dist/subagents/anthropic.js +8 -9
  58. package/dist/subagents/vercel.cjs +435 -442
  59. package/dist/subagents/vercel.cjs.map +1 -1
  60. package/dist/subagents/vercel.js +8 -9
  61. package/dist/tools/browser/anthropic.cjs +1 -1
  62. package/dist/tools/browser/anthropic.cjs.map +1 -1
  63. package/dist/tools/browser/anthropic.js +5 -5
  64. package/dist/tools/browser/core.cjs +1 -1
  65. package/dist/tools/browser/core.cjs.map +1 -1
  66. package/dist/tools/browser/core.js +4 -4
  67. package/dist/tools/browser/index.cjs +1 -1
  68. package/dist/tools/browser/index.cjs.map +1 -1
  69. package/dist/tools/browser/index.js +7 -7
  70. package/dist/tools/browser/openai.cjs +1 -1
  71. package/dist/tools/browser/openai.cjs.map +1 -1
  72. package/dist/tools/browser/openai.js +5 -5
  73. package/dist/tools/browser/profiles/core.cjs +1 -1
  74. package/dist/tools/browser/profiles/core.cjs.map +1 -1
  75. package/dist/tools/browser/profiles/core.js +3 -3
  76. package/dist/tools/browser/profiles/index.cjs +1 -1
  77. package/dist/tools/browser/profiles/index.cjs.map +1 -1
  78. package/dist/tools/browser/profiles/index.js +3 -3
  79. package/dist/tools/browser/vercel.cjs +1 -1
  80. package/dist/tools/browser/vercel.cjs.map +1 -1
  81. package/dist/tools/browser/vercel.js +5 -5
  82. package/dist/tools/codebase_search/anthropic.cjs +1 -1
  83. package/dist/tools/codebase_search/anthropic.cjs.map +1 -1
  84. package/dist/tools/codebase_search/anthropic.js +4 -4
  85. package/dist/tools/codebase_search/core.cjs +1 -1
  86. package/dist/tools/codebase_search/core.cjs.map +1 -1
  87. package/dist/tools/codebase_search/core.js +3 -3
  88. package/dist/tools/codebase_search/index.cjs +1 -1
  89. package/dist/tools/codebase_search/index.cjs.map +1 -1
  90. package/dist/tools/codebase_search/index.js +6 -6
  91. package/dist/tools/codebase_search/openai.cjs +1 -1
  92. package/dist/tools/codebase_search/openai.cjs.map +1 -1
  93. package/dist/tools/codebase_search/openai.js +4 -4
  94. package/dist/tools/codebase_search/vercel.cjs +1 -1
  95. package/dist/tools/codebase_search/vercel.cjs.map +1 -1
  96. package/dist/tools/codebase_search/vercel.js +4 -4
  97. package/dist/tools/fastapply/anthropic.cjs +1 -1
  98. package/dist/tools/fastapply/anthropic.cjs.map +1 -1
  99. package/dist/tools/fastapply/anthropic.js +4 -4
  100. package/dist/tools/fastapply/apply.cjs +1 -1
  101. package/dist/tools/fastapply/apply.cjs.map +1 -1
  102. package/dist/tools/fastapply/apply.js +2 -2
  103. package/dist/tools/fastapply/core.cjs +1 -1
  104. package/dist/tools/fastapply/core.cjs.map +1 -1
  105. package/dist/tools/fastapply/core.js +3 -3
  106. package/dist/tools/fastapply/index.cjs +1 -1
  107. package/dist/tools/fastapply/index.cjs.map +1 -1
  108. package/dist/tools/fastapply/index.js +6 -6
  109. package/dist/tools/fastapply/openai.cjs +1 -1
  110. package/dist/tools/fastapply/openai.cjs.map +1 -1
  111. package/dist/tools/fastapply/openai.js +4 -4
  112. package/dist/tools/fastapply/vercel.cjs +1 -1
  113. package/dist/tools/fastapply/vercel.cjs.map +1 -1
  114. package/dist/tools/fastapply/vercel.js +4 -4
  115. package/dist/tools/index.cjs +1 -1
  116. package/dist/tools/index.cjs.map +1 -1
  117. package/dist/tools/index.js +6 -6
  118. package/dist/tools/utils/resilience.cjs +1 -1
  119. package/dist/tools/utils/resilience.cjs.map +1 -1
  120. package/dist/tools/utils/resilience.js +2 -2
  121. package/dist/tools/warp_grep/agent/config.cjs +4 -3
  122. package/dist/tools/warp_grep/agent/config.cjs.map +1 -1
  123. package/dist/tools/warp_grep/agent/config.d.ts +2 -1
  124. package/dist/tools/warp_grep/agent/config.js +1 -1
  125. package/dist/tools/warp_grep/agent/parser.cjs +52 -121
  126. package/dist/tools/warp_grep/agent/parser.cjs.map +1 -1
  127. package/dist/tools/warp_grep/agent/parser.d.ts +12 -5
  128. package/dist/tools/warp_grep/agent/parser.js +7 -3
  129. package/dist/tools/warp_grep/agent/runner.cjs +348 -424
  130. package/dist/tools/warp_grep/agent/runner.cjs.map +1 -1
  131. package/dist/tools/warp_grep/agent/runner.d.ts +6 -3
  132. package/dist/tools/warp_grep/agent/runner.js +5 -6
  133. package/dist/tools/warp_grep/agent/types.cjs.map +1 -1
  134. package/dist/tools/warp_grep/agent/types.d.ts +22 -3
  135. package/dist/tools/warp_grep/anthropic.cjs +435 -442
  136. package/dist/tools/warp_grep/anthropic.cjs.map +1 -1
  137. package/dist/tools/warp_grep/anthropic.js +8 -9
  138. package/dist/tools/warp_grep/client.cjs +435 -442
  139. package/dist/tools/warp_grep/client.cjs.map +1 -1
  140. package/dist/tools/warp_grep/client.js +7 -8
  141. package/dist/tools/warp_grep/gemini.cjs +435 -442
  142. package/dist/tools/warp_grep/gemini.cjs.map +1 -1
  143. package/dist/tools/warp_grep/gemini.js +7 -8
  144. package/dist/tools/warp_grep/gemini.js.map +1 -1
  145. package/dist/tools/warp_grep/harness.cjs +168 -180
  146. package/dist/tools/warp_grep/harness.cjs.map +1 -1
  147. package/dist/tools/warp_grep/harness.d.ts +17 -38
  148. package/dist/tools/warp_grep/harness.js +15 -14
  149. package/dist/tools/warp_grep/harness.js.map +1 -1
  150. package/dist/tools/warp_grep/index.cjs +454 -442
  151. package/dist/tools/warp_grep/index.cjs.map +1 -1
  152. package/dist/tools/warp_grep/index.d.ts +1 -1
  153. package/dist/tools/warp_grep/index.js +10 -10
  154. package/dist/tools/warp_grep/openai.cjs +435 -442
  155. package/dist/tools/warp_grep/openai.cjs.map +1 -1
  156. package/dist/tools/warp_grep/openai.js +8 -9
  157. package/dist/tools/warp_grep/providers/local.cjs +43 -2
  158. package/dist/tools/warp_grep/providers/local.cjs.map +1 -1
  159. package/dist/tools/warp_grep/providers/local.d.ts +5 -1
  160. package/dist/tools/warp_grep/providers/local.js +2 -2
  161. package/dist/tools/warp_grep/providers/remote.cjs +32 -2
  162. package/dist/tools/warp_grep/providers/remote.cjs.map +1 -1
  163. package/dist/tools/warp_grep/providers/remote.d.ts +9 -1
  164. package/dist/tools/warp_grep/providers/remote.js +2 -2
  165. package/dist/tools/warp_grep/providers/types.cjs.map +1 -1
  166. package/dist/tools/warp_grep/providers/types.d.ts +14 -1
  167. package/dist/tools/warp_grep/vercel.cjs +435 -442
  168. package/dist/tools/warp_grep/vercel.cjs.map +1 -1
  169. package/dist/tools/warp_grep/vercel.js +8 -9
  170. package/dist/version.cjs +1 -1
  171. package/dist/version.cjs.map +1 -1
  172. package/dist/version.js +1 -1
  173. package/package.json +1 -1
  174. package/dist/chunk-DKODF3YG.js.map +0 -1
  175. package/dist/chunk-EUHNJMWL.js +0 -409
  176. package/dist/chunk-EUHNJMWL.js.map +0 -1
  177. package/dist/chunk-HZOTLGJH.js.map +0 -1
  178. package/dist/chunk-NF2QWJDY.js.map +0 -1
  179. package/dist/chunk-VCKJ22DX.js +0 -131
  180. package/dist/chunk-VCKJ22DX.js.map +0 -1
  181. package/dist/chunk-YIETFYCL.js.map +0 -1
  182. /package/dist/{chunk-J2HIK4GB.js.map → chunk-334JOOAC.js.map} +0 -0
  183. /package/dist/{chunk-QRSWXP4K.js.map → chunk-3GXWZ7TI.js.map} +0 -0
  184. /package/dist/{chunk-BDHKL3MT.js.map → chunk-4LZK67MS.js.map} +0 -0
  185. /package/dist/{chunk-UVNENJ6H.js.map → chunk-4XNHMFVU.js.map} +0 -0
  186. /package/dist/{chunk-V73GO5AJ.js.map → chunk-7HKJUVAK.js.map} +0 -0
  187. /package/dist/{chunk-VZ6VYRQB.js.map → chunk-BXQYZR7O.js.map} +0 -0
  188. /package/dist/{chunk-GJUB3ECP.js.map → chunk-CJD4ZFOX.js.map} +0 -0
  189. /package/dist/{chunk-KYKRRF7E.js.map → chunk-DYCRGTRN.js.map} +0 -0
  190. /package/dist/{chunk-EU7OLX4Z.js.map → chunk-ETKDGHOB.js.map} +0 -0
  191. /package/dist/{chunk-I7SFRYTX.js.map → chunk-HWED34T3.js.map} +0 -0
  192. /package/dist/{chunk-FIVYDIHX.js.map → chunk-HYRHI2UL.js.map} +0 -0
  193. /package/dist/{chunk-E4YKEKGW.js.map → chunk-KV2CC4N7.js.map} +0 -0
  194. /package/dist/{chunk-BIQ7234U.js.map → chunk-LSPEWVCD.js.map} +0 -0
  195. /package/dist/{chunk-OV57JBMB.js.map → chunk-N7XLC7BK.js.map} +0 -0
  196. /package/dist/{chunk-FBOJJ3UY.js.map → chunk-O356YR2N.js.map} +0 -0
  197. /package/dist/{chunk-JSWNBCGS.js.map → chunk-PIHW2GSK.js.map} +0 -0
  198. /package/dist/{chunk-UYPWKQKV.js.map → chunk-RGNP6FNH.js.map} +0 -0
  199. /package/dist/{chunk-NKUSUSVI.js.map → chunk-T7VCGKXB.js.map} +0 -0
  200. /package/dist/{chunk-Q36MNOFA.js.map → chunk-UZ2QUWJD.js.map} +0 -0
  201. /package/dist/{chunk-FYO46OT6.js.map → chunk-VTTU7MJB.js.map} +0 -0
  202. /package/dist/{chunk-4PBUB77N.js.map → chunk-XADFYB6B.js.map} +0 -0
  203. /package/dist/{chunk-T564HFSH.js.map → chunk-XS4PO4FC.js.map} +0 -0
  204. /package/dist/{chunk-E45FW5EK.js.map → chunk-YH3XIVF2.js.map} +0 -0
  205. /package/dist/{chunk-MMBQKN4G.js.map → chunk-ZZW4B4GG.js.map} +0 -0
@@ -41,11 +41,12 @@ var init_config = __esm({
41
41
  return isNaN(parsed) || parsed <= 0 ? defaultMs : parsed;
42
42
  };
43
43
  AGENT_CONFIG = {
44
- MAX_TURNS: 4,
44
+ MAX_TURNS: 6,
45
45
  /** Default timeout for model calls. Can be overridden via MORPH_WARP_GREP_TIMEOUT env var (in ms) */
46
46
  TIMEOUT_MS: parseEnvTimeout(process.env.MORPH_WARP_GREP_TIMEOUT, 3e4),
47
- MAX_CONTEXT_CHARS: 54e4,
47
+ MAX_CONTEXT_CHARS: 321600,
48
48
  MAX_OUTPUT_LINES: 200,
49
+ MAX_LIST_RESULTS: 500,
49
50
  MAX_READ_LINES: 800,
50
51
  MAX_LIST_DEPTH: 3,
51
52
  LIST_TIMEOUT_MS: 2e3
@@ -130,7 +131,7 @@ var init_config = __esm({
130
131
  ".*"
131
132
  ];
132
133
  DEFAULT_EXCLUDES = (process.env.MORPH_WARP_GREP_EXCLUDE || "").split(",").map((s) => s.trim()).filter(Boolean).concat(BUILTIN_EXCLUDES);
133
- DEFAULT_MODEL = "morph-warp-grep-v2";
134
+ DEFAULT_MODEL = "morph-warp-grep-v2.1";
134
135
  }
135
136
  });
136
137
 
@@ -203,19 +204,19 @@ var init_ripgrep = __esm({
203
204
 
204
205
  // tools/warp_grep/utils/paths.ts
205
206
  function resolveUnderRepo(repoRoot, targetPath) {
206
- const absRoot = import_path3.default.resolve(repoRoot);
207
- const resolved = import_path3.default.resolve(absRoot, targetPath);
207
+ const absRoot = import_path4.default.resolve(repoRoot);
208
+ const resolved = import_path4.default.resolve(absRoot, targetPath);
208
209
  ensureWithinRepo(absRoot, resolved);
209
210
  return resolved;
210
211
  }
211
212
  function ensureWithinRepo(repoRoot, absTarget) {
212
- const rel = import_path3.default.relative(import_path3.default.resolve(repoRoot), import_path3.default.resolve(absTarget));
213
- if (rel.startsWith("..") || import_path3.default.isAbsolute(rel)) {
213
+ const rel = import_path4.default.relative(import_path4.default.resolve(repoRoot), import_path4.default.resolve(absTarget));
214
+ if (rel.startsWith("..") || import_path4.default.isAbsolute(rel)) {
214
215
  throw new Error(`Path outside repository root: ${absTarget}`);
215
216
  }
216
217
  }
217
218
  function toRepoRelative(repoRoot, absPath) {
218
- return import_path3.default.relative(import_path3.default.resolve(repoRoot), import_path3.default.resolve(absPath));
219
+ return import_path4.default.relative(import_path4.default.resolve(repoRoot), import_path4.default.resolve(absPath));
219
220
  }
220
221
  function isSymlink(p) {
221
222
  try {
@@ -226,7 +227,7 @@ function isSymlink(p) {
226
227
  }
227
228
  }
228
229
  function fixPathRepetition(fullPath) {
229
- const segments = fullPath.split(import_path3.default.sep).filter(Boolean);
230
+ const segments = fullPath.split(import_path4.default.sep).filter(Boolean);
230
231
  if (segments.length < 2) return null;
231
232
  for (let len = Math.floor(segments.length / 2); len >= 1; len--) {
232
233
  for (let i = 0; i <= segments.length - 2 * len; i++) {
@@ -234,7 +235,7 @@ function fixPathRepetition(fullPath) {
234
235
  const second = segments.slice(i + len, i + 2 * len);
235
236
  if (first.every((seg, idx) => seg === second[idx])) {
236
237
  const fixed = [...segments.slice(0, i), ...segments.slice(i + len)];
237
- return import_path3.default.sep + fixed.join(import_path3.default.sep);
238
+ return import_path4.default.sep + fixed.join(import_path4.default.sep);
238
239
  }
239
240
  }
240
241
  }
@@ -258,12 +259,12 @@ function isTextualFile(filePath, maxBytes = 2e6) {
258
259
  return false;
259
260
  }
260
261
  }
261
- var import_fs, import_path3;
262
+ var import_fs, import_path4;
262
263
  var init_paths = __esm({
263
264
  "tools/warp_grep/utils/paths.ts"() {
264
265
  "use strict";
265
266
  import_fs = __toESM(require("fs"), 1);
266
- import_path3 = __toESM(require("path"), 1);
267
+ import_path4 = __toESM(require("path"), 1);
267
268
  }
268
269
  });
269
270
 
@@ -294,12 +295,12 @@ function shouldSkip2(name, allowNames) {
294
295
  }
295
296
  return false;
296
297
  }
297
- var import_promises2, import_path4, SKIP_NAMES2, SKIP_EXTENSIONS2, LocalRipgrepProvider;
298
+ var import_promises2, import_path5, SKIP_NAMES2, SKIP_EXTENSIONS2, LocalRipgrepProvider;
298
299
  var init_local = __esm({
299
300
  "tools/warp_grep/providers/local.ts"() {
300
301
  "use strict";
301
302
  import_promises2 = __toESM(require("fs/promises"), 1);
302
- import_path4 = __toESM(require("path"), 1);
303
+ import_path5 = __toESM(require("path"), 1);
303
304
  init_ripgrep();
304
305
  init_paths();
305
306
  init_files();
@@ -401,7 +402,7 @@ var init_local = __esm({
401
402
  }
402
403
  const stat = await import_promises2.default.stat(abs).catch(() => null);
403
404
  if (!stat) return { lines: [] };
404
- const targetArg = abs === import_path4.default.resolve(this.repoRoot) ? "." : toRepoRelative(this.repoRoot, abs);
405
+ const targetArg = abs === import_path5.default.resolve(this.repoRoot) ? "." : toRepoRelative(this.repoRoot, abs);
405
406
  const contextLines = params.context_lines !== void 0 ? String(params.context_lines) : "1";
406
407
  const args = [
407
408
  "--no-config",
@@ -556,7 +557,7 @@ Details: ${res.stderr}` : ""}`
556
557
  if (timedOut || results.length >= maxResults) break;
557
558
  if (shouldSkip2(entry.name, allowNames)) continue;
558
559
  if (regex && !regex.test(entry.name)) continue;
559
- const full = import_path4.default.join(dir, entry.name);
560
+ const full = import_path5.default.join(dir, entry.name);
560
561
  const isDir = entry.isDirectory();
561
562
  results.push({
562
563
  name: entry.name,
@@ -572,6 +573,46 @@ Details: ${res.stderr}` : ""}`
572
573
  await walk(abs, 0);
573
574
  return results;
574
575
  }
576
+ async glob(params) {
577
+ let abs;
578
+ try {
579
+ abs = params.path ? resolveUnderRepo(this.repoRoot, params.path) : this.repoRoot;
580
+ } catch (err) {
581
+ return { files: [], searchDir: this.repoRoot, totalFound: 0, error: `[PATH ERROR] ${err instanceof Error ? err.message : String(err)}` };
582
+ }
583
+ const stat = await import_promises2.default.stat(abs).catch(() => null);
584
+ if (!stat || !stat.isDirectory()) {
585
+ return { files: [], searchDir: abs, totalFound: 0, error: `[PATH ERROR] Directory not found: ${params.path || "."}` };
586
+ }
587
+ const targetArg = abs === import_path5.default.resolve(this.repoRoot) ? "." : toRepoRelative(this.repoRoot, abs);
588
+ const args = [
589
+ "--no-config",
590
+ "--files",
591
+ "--color=never",
592
+ "-g",
593
+ params.pattern,
594
+ ...this.excludes.filter((e) => !this.allowNames?.has(e)).flatMap((e) => ["-g", `!${e}`]),
595
+ targetArg || "."
596
+ ];
597
+ const res = await runRipgrep(args, { cwd: this.repoRoot });
598
+ if (res.exitCode === -1) {
599
+ return { files: [], searchDir: abs, totalFound: 0, error: `[RIPGREP NOT AVAILABLE] ripgrep (rg) is required for glob search.` };
600
+ }
601
+ if (res.exitCode !== 0 && res.exitCode !== 1) {
602
+ return { files: [], searchDir: abs, totalFound: 0, error: `[GLOB ERROR] glob failed with exit code ${res.exitCode}${res.stderr ? `: ${res.stderr}` : ""}` };
603
+ }
604
+ const absRoot = import_path5.default.resolve(this.repoRoot);
605
+ const relFiles = (res.stdout || "").trim().split(/\r?\n/).filter((l) => l.length > 0);
606
+ const absFiles = relFiles.map((f) => import_path5.default.resolve(absRoot, f));
607
+ const withMtime = [];
608
+ for (const f of absFiles) {
609
+ const s = await import_promises2.default.stat(f).catch(() => null);
610
+ withMtime.push({ file: f, mtime: s?.mtimeMs ?? 0 });
611
+ }
612
+ withMtime.sort((a, b) => b.mtime - a.mtime);
613
+ const totalFound = withMtime.length;
614
+ return { files: withMtime.slice(0, 100).map((f) => f.file), searchDir: abs, totalFound };
615
+ }
575
616
  };
576
617
  }
577
618
  });
@@ -613,131 +654,58 @@ var import_zod = require("zod");
613
654
  init_config();
614
655
 
615
656
  // tools/warp_grep/agent/parser.ts
616
- var VALID_COMMANDS = ["list_directory", "ripgrep", "read", "finish"];
617
- function isValidCommand(name) {
618
- return VALID_COMMANDS.includes(name);
657
+ function parseReadLines(linesStr) {
658
+ const ranges = [];
659
+ for (const rangeStr of linesStr.split(",")) {
660
+ const trimmed = rangeStr.trim();
661
+ if (!trimmed) continue;
662
+ const parts = trimmed.split("-").map((v) => parseInt(v.trim(), 10));
663
+ if (parts.length >= 2 && Number.isFinite(parts[0]) && Number.isFinite(parts[1])) {
664
+ ranges.push([parts[0], parts[1]]);
665
+ } else if (Number.isFinite(parts[0])) {
666
+ ranges.push([parts[0], parts[0]]);
667
+ }
668
+ }
669
+ if (ranges.length === 1) return { start: ranges[0][0], end: ranges[0][1] };
670
+ if (ranges.length > 1) return { lines: ranges };
671
+ return {};
619
672
  }
620
- function parseQwen3ToolCalls(text) {
621
- const tools = [];
622
- const toolCallRegex = /<tool_call>\s*<function=([a-z_][a-z0-9_]*)>([\s\S]*?)<\/function>\s*<\/tool_call>/gi;
623
- let match;
624
- while ((match = toolCallRegex.exec(text)) !== null) {
625
- const funcName = match[1].toLowerCase();
626
- const body = match[2];
627
- if (!isValidCommand(funcName)) continue;
628
- const params = {};
629
- const paramRegex = /<parameter=([a-z_][a-z0-9_]*)>([\s\S]*?)<\/parameter>/gi;
630
- let paramMatch;
631
- while ((paramMatch = paramRegex.exec(body)) !== null) {
632
- params[paramMatch[1].toLowerCase()] = paramMatch[2].trim();
673
+ function parseFinishFiles(filesStr) {
674
+ const files = [];
675
+ for (const line of filesStr.trim().split(/\s+/)) {
676
+ const trimmed = line.trim();
677
+ if (!trimmed) continue;
678
+ const colonIdx = trimmed.indexOf(":");
679
+ if (colonIdx === -1) {
680
+ files.push({ path: trimmed, lines: "*" });
681
+ continue;
633
682
  }
634
- if (funcName === "ripgrep") {
635
- const pattern = params.pattern;
636
- if (!pattern) continue;
637
- const args = {
638
- pattern,
639
- path: params.path || ".",
640
- ...params.glob && { glob: params.glob },
641
- ...params.context_lines && { context_lines: parseInt(params.context_lines, 10) },
642
- ...params.case_sensitive && { case_sensitive: params.case_sensitive === "true" }
643
- };
644
- tools.push({ name: "grep", arguments: args });
645
- } else if (funcName === "list_directory") {
646
- const command = params.command;
647
- const directPath = params.path;
648
- let dirPath = directPath || ".";
649
- if (!directPath && command) {
650
- const tokens = command.trim().split(/\s+/);
651
- const pathTokens = tokens.slice(1).filter((t) => !t.startsWith("-") && !t.startsWith("|") && !t.startsWith("\\("));
652
- if (pathTokens.length > 0) {
653
- dirPath = pathTokens[0];
654
- }
655
- }
656
- tools.push({ name: "list_directory", arguments: { path: dirPath, pattern: params.pattern || null } });
657
- } else if (funcName === "read") {
658
- const filePath = params.path;
659
- if (!filePath) continue;
660
- const args = { path: filePath };
661
- const linesStr = params.lines;
662
- if (linesStr) {
663
- const ranges = [];
664
- for (const rangeStr of linesStr.split(",")) {
665
- const trimmed = rangeStr.trim();
666
- if (!trimmed) continue;
667
- const [s, e] = trimmed.split("-").map((v) => parseInt(v.trim(), 10));
668
- if (Number.isFinite(s) && Number.isFinite(e)) {
669
- ranges.push([s, e]);
670
- } else if (Number.isFinite(s)) {
671
- ranges.push([s, s]);
672
- }
673
- }
674
- if (ranges.length === 1) {
675
- args.start = ranges[0][0];
676
- args.end = ranges[0][1];
677
- } else if (ranges.length > 1) {
678
- args.lines = ranges;
679
- }
680
- }
681
- tools.push({ name: "read", arguments: args });
682
- } else if (funcName === "finish") {
683
- if (params.result && !params.files) {
684
- tools.push({ name: "finish", arguments: { files: [], textResult: params.result } });
685
- continue;
686
- }
687
- const filesStr = params.files;
688
- if (!filesStr) {
689
- tools.push({ name: "finish", arguments: { files: [], textResult: "No relevant code found." } });
690
- continue;
691
- }
692
- const files = [];
693
- for (const line of filesStr.split("\n")) {
694
- const trimmed = line.trim();
695
- if (!trimmed) continue;
696
- const colonIdx = trimmed.indexOf(":");
697
- if (colonIdx === -1) {
698
- files.push({ path: trimmed, lines: "*" });
699
- } else {
700
- const filePath = trimmed.slice(0, colonIdx);
701
- const rangesPart = trimmed.slice(colonIdx + 1);
702
- const ranges = [];
703
- for (const rangeStr of rangesPart.split(",")) {
704
- const rt = rangeStr.trim();
705
- if (!rt || rt === "*") {
706
- files.push({ path: filePath, lines: "*" });
707
- break;
708
- }
709
- const [s, e] = rt.split("-").map((v) => parseInt(v.trim(), 10));
710
- if (Number.isFinite(s) && Number.isFinite(e)) {
711
- ranges.push([s, e]);
712
- } else if (Number.isFinite(s)) {
713
- ranges.push([s, s]);
714
- }
715
- }
716
- if (ranges.length > 0) {
717
- files.push({ path: filePath, lines: ranges });
718
- } else if (!files.some((f) => f.path === filePath)) {
719
- files.push({ path: filePath, lines: "*" });
720
- }
721
- }
722
- }
723
- if (files.length > 0) {
724
- tools.push({ name: "finish", arguments: { files } });
725
- } else {
726
- tools.push({ name: "finish", arguments: { files: [], textResult: filesStr } });
683
+ const filePath = trimmed.slice(0, colonIdx);
684
+ const rangesPart = trimmed.slice(colonIdx + 1);
685
+ if (!rangesPart.trim() || rangesPart.trim() === "*") {
686
+ files.push({ path: filePath, lines: "*" });
687
+ continue;
688
+ }
689
+ const ranges = [];
690
+ for (const rangeStr of rangesPart.split(",")) {
691
+ const rt = rangeStr.trim();
692
+ if (!rt) continue;
693
+ const parts = rt.split("-").map((v) => parseInt(v.trim(), 10));
694
+ if (parts.length >= 2 && Number.isFinite(parts[0]) && Number.isFinite(parts[1])) {
695
+ ranges.push([parts[0], parts[1]]);
696
+ } else if (Number.isFinite(parts[0])) {
697
+ ranges.push([parts[0], parts[0]]);
727
698
  }
728
699
  }
700
+ files.push({ path: filePath, lines: ranges.length > 0 ? ranges : "*" });
729
701
  }
730
- return tools;
702
+ return files;
703
+ }
704
+ function extractPathFromCommand(command) {
705
+ const tokens = command.trim().split(/\s+/);
706
+ const pathTokens = tokens.slice(1).filter((t) => !t.startsWith("-") && !t.startsWith("|") && !t.startsWith("\\("));
707
+ return pathTokens[0] || ".";
731
708
  }
732
- var LLMResponseParser = class {
733
- parse(text) {
734
- if (typeof text !== "string") {
735
- throw new TypeError("Command text must be a string.");
736
- }
737
- const withoutThink = text.replace(/<think>[\s\S]*?<\/think>/gi, "");
738
- return parseQwen3ToolCalls(withoutThink);
739
- }
740
- };
741
709
 
742
710
  // tools/warp_grep/agent/tools/grep.ts
743
711
  async function toolGrep(provider, args) {
@@ -788,29 +756,42 @@ async function toolRead(provider, args) {
788
756
 
789
757
  // tools/warp_grep/agent/tools/list_directory.ts
790
758
  init_config();
791
- async function toolListDirectory(provider, args) {
792
- const maxResults = args.maxResults ?? AGENT_CONFIG.MAX_OUTPUT_LINES;
793
- const initialDepth = args.maxDepth ?? AGENT_CONFIG.MAX_LIST_DEPTH;
794
- async function getListRecursive(currentDepth) {
795
- const entries = await provider.listDirectory({
796
- path: args.path,
797
- pattern: args.pattern ?? null,
798
- maxResults,
799
- maxDepth: currentDepth
800
- });
801
- if (entries.length >= maxResults && currentDepth > 0) {
802
- return getListRecursive(currentDepth - 1);
803
- }
804
- return { entries };
805
- }
806
- const { entries: list } = await getListRecursive(initialDepth);
807
- if (!list.length) return "empty";
808
- const tree = list.map((e) => {
809
- const indent = " ".repeat(e.depth);
810
- const name = e.type === "dir" ? `${e.name}/` : e.name;
811
- return `${indent}${name}`;
812
- }).join("\n");
813
- return tree;
759
+ var import_path = __toESM(require("path"), 1);
760
+ async function toolListDirectory(provider, args, repoRoot) {
761
+ const maxResults = args.maxResults ?? AGENT_CONFIG.MAX_LIST_RESULTS;
762
+ const maxDepth = args.maxDepth ?? AGENT_CONFIG.MAX_LIST_DEPTH;
763
+ const entries = await provider.listDirectory({
764
+ path: args.path,
765
+ pattern: args.pattern ?? null,
766
+ maxResults,
767
+ maxDepth
768
+ });
769
+ if (!entries.length) return "empty";
770
+ if (repoRoot) {
771
+ const absRoot = import_path.default.resolve(repoRoot);
772
+ const lines = entries.map((e) => import_path.default.join(absRoot, e.path));
773
+ return lines.join("\n");
774
+ }
775
+ return entries.map((e) => e.path).join("\n");
776
+ }
777
+
778
+ // tools/warp_grep/agent/tools/glob.ts
779
+ async function toolGlob(provider, args) {
780
+ const res = await provider.glob(args);
781
+ if (res.error) {
782
+ return res.error;
783
+ }
784
+ if (!res.files.length) {
785
+ return "no matches";
786
+ }
787
+ const header = `Found ${res.totalFound} file(s) matching "${args.pattern}" within ${res.searchDir}, sorted by modification time (newest first):`;
788
+ const body = res.files.join("\n");
789
+ const truncated = res.totalFound > res.files.length ? `
790
+ [${res.totalFound - res.files.length} files truncated]` : "";
791
+ return `${header}
792
+ ---
793
+ ${body}
794
+ ---${truncated}`;
814
795
  }
815
796
 
816
797
  // tools/warp_grep/agent/tools/finish.ts
@@ -879,32 +860,21 @@ function mergeRanges(ranges) {
879
860
  return merged;
880
861
  }
881
862
 
882
- // tools/warp_grep/agent/formatter.ts
883
- var ToolOutputFormatter = class {
884
- format(toolName, _args, output, options = {}) {
885
- const name = (toolName ?? "").trim();
886
- if (!name) {
887
- return "";
888
- }
889
- const payload = output?.toString?.()?.trim?.() ?? "";
890
- const isError = Boolean(options.isError);
891
- if (!payload && !isError) {
892
- return "";
893
- }
894
- return `<tool_response>
895
- ${payload}
896
- </tool_response>`;
897
- }
898
- };
899
- var sharedFormatter = new ToolOutputFormatter();
900
- function formatAgentToolOutput(toolName, args, output, options = {}) {
901
- return sharedFormatter.format(toolName, args, output, options);
902
- }
903
-
904
863
  // tools/warp_grep/agent/helpers.ts
905
- var import_path = __toESM(require("path"), 1);
864
+ var import_path2 = __toESM(require("path"), 1);
906
865
  init_config();
907
866
  var TRUNCATED_MARKER = "[truncated for context limit]";
867
+ function getMessageSize(m) {
868
+ if (m.role === "tool") return m.content.length;
869
+ if (m.role === "assistant") {
870
+ let size = typeof m.content === "string" ? m.content.length : 0;
871
+ if (m.tool_calls) {
872
+ size += m.tool_calls.reduce((s, tc) => s + tc.function.name.length + tc.function.arguments.length, 0);
873
+ }
874
+ return size;
875
+ }
876
+ return m.content.length;
877
+ }
908
878
  function formatTurnMessage(turnsUsed, maxTurns) {
909
879
  const turnsRemaining = maxTurns - turnsUsed;
910
880
  if (turnsRemaining === 1) {
@@ -915,33 +885,30 @@ You have used ${turnsUsed} turns, you only have 1 turn remaining. You have run o
915
885
  You have used ${turnsUsed} turn${turnsUsed === 1 ? "" : "s"} and have ${turnsRemaining} remaining`;
916
886
  }
917
887
  function calculateContextBudget(messages) {
918
- const totalChars = messages.reduce((sum, m) => sum + m.content.length, 0);
888
+ const totalChars = messages.reduce((sum, m) => sum + getMessageSize(m), 0);
919
889
  const maxChars = AGENT_CONFIG.MAX_CONTEXT_CHARS;
920
- const percent = Math.round(totalChars / maxChars * 100);
921
- const usedK = Math.round(totalChars / 1e3);
922
- const maxK = Math.round(maxChars / 1e3);
923
- return `<context_budget>${percent}% (${usedK}K/${maxK}K chars used)</context_budget>`;
890
+ const percent = Math.floor(totalChars / maxChars * 100);
891
+ const usedK = Math.floor(totalChars / 1e3);
892
+ const maxK = Math.floor(maxChars / 1e3);
893
+ return `<context_budget>${percent}% (${usedK}K/${maxK}K chars)</context_budget>`;
924
894
  }
925
895
  async function buildInitialState(repoRoot, searchTerm, provider, options) {
926
896
  const budget = calculateContextBudget([]);
927
- const turnTag = `Turn 0/${AGENT_CONFIG.MAX_TURNS}`;
897
+ const turnTag = `You have used 0 turns and have ${AGENT_CONFIG.MAX_TURNS} remaining`;
928
898
  const treeDepth = options?.search_type === "node_modules" ? 1 : 2;
899
+ const absRoot = import_path2.default.resolve(repoRoot);
929
900
  try {
930
901
  const entries = await provider.listDirectory({
931
902
  path: ".",
932
903
  maxResults: AGENT_CONFIG.MAX_OUTPUT_LINES,
933
904
  maxDepth: treeDepth
934
905
  });
935
- const treeLines = entries.map((e) => {
936
- const indent = " ".repeat(e.depth);
937
- const name = e.type === "dir" ? `${e.name}/` : e.name;
938
- return `${indent}${name}`;
939
- });
940
- const repoName = import_path.default.basename(repoRoot);
941
- const treeOutput = treeLines.length > 0 ? `${repoName}/
942
- ${treeLines.join("\n")}` : `${repoName}/`;
906
+ const lines = [absRoot];
907
+ for (const e of entries) {
908
+ lines.push(import_path2.default.join(absRoot, e.path));
909
+ }
943
910
  return `<repo_structure>
944
- ${treeOutput}
911
+ ${lines.join("\n")}
945
912
  </repo_structure>
946
913
 
947
914
  <search_string>
@@ -950,9 +917,8 @@ ${searchTerm}
950
917
  ${budget}
951
918
  ${turnTag}`;
952
919
  } catch {
953
- const repoName = import_path.default.basename(repoRoot);
954
920
  return `<repo_structure>
955
- ${repoName}/
921
+ ${absRoot}
956
922
  </repo_structure>
957
923
 
958
924
  <search_string>
@@ -963,26 +929,32 @@ ${turnTag}`;
963
929
  }
964
930
  }
965
931
  function enforceContextLimit(messages, maxChars = AGENT_CONFIG.MAX_CONTEXT_CHARS) {
966
- const getTotalChars = () => messages.reduce((sum, m) => sum + m.content.length, 0);
932
+ const getTotalChars = () => messages.reduce((sum, m) => sum + getMessageSize(m), 0);
967
933
  if (getTotalChars() <= maxChars) {
968
934
  return messages;
969
935
  }
970
- const userIndices = [];
936
+ const truncatableIndices = [];
971
937
  let firstUserSkipped = false;
972
938
  for (let i = 0; i < messages.length; i++) {
973
- if (messages[i].role === "user") {
939
+ const m = messages[i];
940
+ if (m.role === "tool") {
941
+ truncatableIndices.push(i);
942
+ } else if (m.role === "user") {
974
943
  if (!firstUserSkipped) {
975
944
  firstUserSkipped = true;
976
945
  continue;
977
946
  }
978
- userIndices.push(i);
947
+ truncatableIndices.push(i);
979
948
  }
980
949
  }
981
- for (const idx of userIndices) {
950
+ for (const idx of truncatableIndices) {
982
951
  if (getTotalChars() <= maxChars) {
983
952
  break;
984
953
  }
985
- if (messages[idx].content !== TRUNCATED_MARKER) {
954
+ const m = messages[idx];
955
+ if (m.role === "tool" && m.content !== TRUNCATED_MARKER) {
956
+ messages[idx] = { role: "tool", tool_call_id: m.tool_call_id, content: TRUNCATED_MARKER };
957
+ } else if (m.role === "user" && m.content !== TRUNCATED_MARKER) {
986
958
  messages[idx] = { role: "user", content: TRUNCATED_MARKER };
987
959
  }
988
960
  }
@@ -995,7 +967,7 @@ var import_openai = __toESM(require("openai"), 1);
995
967
  // package.json
996
968
  var package_default = {
997
969
  name: "@morphllm/morphsdk",
998
- version: "0.2.146",
970
+ version: "0.2.147",
999
971
  description: "TypeScript SDK and CLI for Morph Fast Apply integration",
1000
972
  type: "module",
1001
973
  main: "./dist/index.cjs",
@@ -1234,9 +1206,115 @@ var package_default = {
1234
1206
  var SDK_VERSION = package_default.version;
1235
1207
 
1236
1208
  // tools/warp_grep/agent/runner.ts
1237
- var import_path2 = __toESM(require("path"), 1);
1238
- var parser = new LLMResponseParser();
1209
+ var import_path3 = __toESM(require("path"), 1);
1239
1210
  var DEFAULT_API_URL = "https://api.morphllm.com";
1211
+ var TOOL_SPECS = [
1212
+ {
1213
+ type: "function",
1214
+ function: {
1215
+ name: "list_directory",
1216
+ description: "Execute ls or find commands to explore directory structure. Max 500 results. Common junk directories are excluded automatically.",
1217
+ parameters: {
1218
+ type: "object",
1219
+ properties: {
1220
+ command: {
1221
+ type: "string",
1222
+ description: "Full ls or find command (e.g. ls -la src/, find . -maxdepth 2 -type f -name '*.py', find . -type d, ls -d */)."
1223
+ }
1224
+ },
1225
+ required: ["command"]
1226
+ }
1227
+ }
1228
+ },
1229
+ {
1230
+ type: "function",
1231
+ function: {
1232
+ name: "grep_search",
1233
+ description: "Search for a regex pattern in file contents. Returns matching lines with file paths and line numbers. Case-insensitive. Respects .gitignore.",
1234
+ parameters: {
1235
+ type: "object",
1236
+ properties: {
1237
+ pattern: {
1238
+ type: "string",
1239
+ description: "Regex pattern to search for in file contents (e.g. 'class\\s+\\w+Error', 'import|require|from', 'def (get|set|update)_user')."
1240
+ },
1241
+ path: {
1242
+ type: "string",
1243
+ description: "File or directory to search in. Defaults to current working directory."
1244
+ },
1245
+ glob: {
1246
+ type: "string",
1247
+ description: "Glob pattern to filter files (e.g. '*.py', '*.{ts,tsx,js,jsx,py,go}', 'src/**/*.go', '!*.test.*')."
1248
+ },
1249
+ limit: {
1250
+ type: "integer",
1251
+ description: "Limit output to first N matching lines. Shows all matches if not specified."
1252
+ }
1253
+ },
1254
+ required: ["pattern"]
1255
+ }
1256
+ }
1257
+ },
1258
+ {
1259
+ type: "function",
1260
+ function: {
1261
+ name: "glob",
1262
+ description: "Find files by name/extension using glob patterns. Returns absolute paths sorted by modification time (newest first). Respects .gitignore. Max 100 results.",
1263
+ parameters: {
1264
+ type: "object",
1265
+ properties: {
1266
+ pattern: {
1267
+ type: "string",
1268
+ description: "Glob pattern to match files (e.g. '*.py', 'src/**/*.js', '*.{ts,tsx}', 'test_*.py')."
1269
+ },
1270
+ path: {
1271
+ type: "string",
1272
+ description: "Directory to search in. Defaults to repository root."
1273
+ }
1274
+ },
1275
+ required: ["pattern"]
1276
+ }
1277
+ }
1278
+ },
1279
+ {
1280
+ type: "function",
1281
+ function: {
1282
+ name: "read",
1283
+ description: "Read entire files or specific line ranges using absolute paths.",
1284
+ parameters: {
1285
+ type: "object",
1286
+ properties: {
1287
+ path: {
1288
+ type: "string",
1289
+ description: "File path to read, using absolute path (e.g. '/home/ubuntu/repo/src/main.py' or windows path)."
1290
+ },
1291
+ lines: {
1292
+ type: "string",
1293
+ description: "Optional line range (e.g. '1-50' or '1-20,45-80'). Omit to read entire file."
1294
+ }
1295
+ },
1296
+ required: ["path"]
1297
+ }
1298
+ }
1299
+ },
1300
+ {
1301
+ type: "function",
1302
+ function: {
1303
+ name: "finish",
1304
+ description: "Submit final answer with all relevant code locations. Include imports and over-include rather than miss context.",
1305
+ parameters: {
1306
+ type: "object",
1307
+ properties: {
1308
+ files: {
1309
+ type: "string",
1310
+ description: "One file per line as path:lines (e.g. 'src/auth.py:1-15,25-50\\nsrc/user.py'). Omit line range to include entire file."
1311
+ }
1312
+ },
1313
+ required: ["files"]
1314
+ }
1315
+ }
1316
+ }
1317
+ ];
1240
1318
  async function callModel(messages, model, options = {}) {
1241
1319
  const baseUrl = options.morphApiUrl || DEFAULT_API_URL;
1242
1320
  const apiKey = options.morphApiKey || process.env.MORPH_API_KEY || "";
@@ -1257,8 +1335,9 @@ async function callModel(messages, model, options = {}) {
1257
1335
  data = await client.chat.completions.create({
1258
1336
  model,
1259
1337
  temperature: 0,
1260
- max_tokens: 1024,
1338
+ max_tokens: 2048,
1261
1339
  messages,
1340
+ tools: TOOL_SPECS,
1262
1341
  ...options.search_type ? { search_type: options.search_type } : {}
1263
1342
  });
1264
1343
  } catch (error) {
@@ -1270,187 +1349,87 @@ async function callModel(messages, model, options = {}) {
1270
1349
  throw error;
1271
1350
  }
1272
1351
  const choice = data?.choices?.[0];
1273
- const content = choice?.message?.content;
1274
- if (content && typeof content === "string") {
1275
- return content;
1352
+ const message = choice?.message;
1353
+ if (!message) {
1354
+ if (attempt === MAX_EMPTY_RETRIES) {
1355
+ throw new Error("Invalid response from model: no message in response");
1356
+ }
1357
+ await new Promise((resolve) => setTimeout(resolve, 200));
1358
+ continue;
1359
+ }
1360
+ const toolCalls = (message.tool_calls || []).map((tc) => ({
1361
+ id: tc.id,
1362
+ type: "function",
1363
+ function: { name: tc.function.name, arguments: tc.function.arguments }
1364
+ }));
1365
+ if (message.content || toolCalls.length > 0) {
1366
+ return { content: message.content ?? null, tool_calls: toolCalls };
1276
1367
  }
1277
1368
  if (attempt === MAX_EMPTY_RETRIES) {
1278
1369
  const finishReason = choice?.finish_reason ?? "unknown";
1279
- const hasToolCalls = Array.isArray(choice?.message?.tool_calls) && choice.message.tool_calls.length > 0;
1280
- const choicesLen = data?.choices?.length ?? 0;
1281
- const contentType = content === null ? "null" : content === void 0 ? "undefined" : typeof content;
1282
1370
  throw new Error(
1283
- `Invalid response from model: content=${contentType}, finish_reason=${finishReason}, has_tool_calls=${hasToolCalls}, choices_length=${choicesLen}`
1371
+ `Invalid response from model: no content and no tool_calls, finish_reason=${finishReason}`
1284
1372
  );
1285
1373
  }
1286
1374
  await new Promise((resolve) => setTimeout(resolve, 200));
1287
1375
  }
1288
1376
  throw new Error("Invalid response from model");
1289
1377
  }
1290
- async function runWarpGrep(config) {
1291
- const totalStart = Date.now();
1292
- const timeoutMs = config.timeout ?? AGENT_CONFIG.TIMEOUT_MS;
1293
- const timings = { turns: [], timeout_ms: timeoutMs };
1294
- const repoRoot = import_path2.default.resolve(config.repoRoot || process.cwd());
1295
- const model = config.model || DEFAULT_MODEL;
1296
- const messages = [];
1297
- const maxTurns = AGENT_CONFIG.MAX_TURNS;
1298
- const initialStateStart = Date.now();
1299
- const initialState = await buildInitialState(repoRoot, config.searchTerm, config.provider, { search_type: config.search_type });
1300
- timings.initial_state_ms = Date.now() - initialStateStart;
1301
- messages.push({ role: "user", content: initialState });
1302
- const provider = config.provider;
1303
- const errors = [];
1304
- let finishMeta;
1305
- let terminationReason = "terminated";
1306
- for (let turn = 1; turn <= maxTurns; turn += 1) {
1307
- const turnMetrics = { turn, morph_api_ms: 0, local_tools_ms: 0 };
1308
- enforceContextLimit(messages);
1309
- const modelCallStart = Date.now();
1310
- const assistantContent = await callModel(messages, model, {
1311
- morphApiKey: config.morphApiKey,
1312
- morphApiUrl: config.morphApiUrl,
1313
- retryConfig: config.retryConfig,
1314
- timeout: timeoutMs,
1315
- search_type: config.search_type
1316
- }).catch((e) => {
1317
- const errMsg = e instanceof Error ? e.message : String(e);
1318
- console.error(`[warp_grep] Morph API call failed on turn ${turn}:`, errMsg);
1319
- errors.push({ message: errMsg });
1320
- return "";
1321
- });
1322
- turnMetrics.morph_api_ms = Date.now() - modelCallStart;
1323
- if (!assistantContent) {
1324
- console.error(`[warp_grep] Empty response from Morph API on turn ${turn}. Errors so far:`, errors);
1325
- timings.turns.push(turnMetrics);
1326
- break;
1327
- }
1328
- messages.push({ role: "assistant", content: assistantContent });
1329
- const toolCalls = parser.parse(assistantContent);
1330
- if (toolCalls.length === 0) {
1331
- console.error(`[warp_grep] No tool calls parsed on turn ${turn}. Assistant content (first 500 chars):`, assistantContent.slice(0, 500));
1332
- errors.push({ message: "No tool calls produced by the model. Your MCP is likely out of date! Update it by running: rm -rf ~/.npm/_npx && npm cache clean --force && npx -y @morphllm/morphmcp@latest" });
1333
- terminationReason = "terminated";
1334
- timings.turns.push(turnMetrics);
1335
- break;
1336
- }
1337
- const finishCalls = toolCalls.filter((c) => c.name === "finish");
1338
- const grepCalls = toolCalls.filter((c) => c.name === "grep");
1339
- const listDirCalls = toolCalls.filter((c) => c.name === "list_directory");
1340
- const readCalls = toolCalls.filter((c) => c.name === "read");
1341
- const skipCalls = toolCalls.filter((c) => c.name === "_skip");
1342
- const formatted = [];
1343
- for (const c of skipCalls) {
1344
- const msg = c.arguments?.message || "Command skipped due to parsing error";
1345
- formatted.push(msg);
1346
- }
1347
- const allPromises = [];
1348
- for (const c of grepCalls) {
1349
- const args = c.arguments ?? {};
1350
- allPromises.push(
1351
- toolGrep(provider, args).then(
1352
- ({ output }) => formatAgentToolOutput("grep", args, output),
1353
- (err) => formatAgentToolOutput("grep", args, String(err), { isError: true })
1354
- )
1355
- );
1356
- }
1357
- for (const c of listDirCalls) {
1358
- const args = c.arguments ?? {};
1359
- allPromises.push(
1360
- toolListDirectory(provider, args).then(
1361
- (p) => formatAgentToolOutput("list_directory", args, p),
1362
- (err) => formatAgentToolOutput("list_directory", args, String(err), { isError: true })
1363
- )
1364
- );
1365
- }
1366
- for (const c of readCalls) {
1367
- const args = c.arguments ?? {};
1368
- allPromises.push(
1369
- toolRead(provider, args).then(
1370
- (p) => formatAgentToolOutput("read", args, p),
1371
- (err) => formatAgentToolOutput("read", args, String(err), { isError: true })
1372
- )
1373
- );
1378
+ function safeParseJSON(s) {
1379
+ try {
1380
+ return JSON.parse(s);
1381
+ } catch {
1382
+ return {};
1383
+ }
1384
+ }
1385
+ async function executeTool(provider, name, args, repoRoot) {
1386
+ switch (name) {
1387
+ case "grep_search": {
1388
+ const grepArgs = {
1389
+ pattern: args.pattern,
1390
+ path: args.path || "."
1391
+ };
1392
+ if (args.glob) grepArgs.glob = args.glob;
1393
+ if (args.case_sensitive !== void 0) grepArgs.case_sensitive = args.case_sensitive;
1394
+ const result = await toolGrep(provider, grepArgs);
1395
+ let output = result.output;
1396
+ if (args.limit && typeof args.limit === "number") {
1397
+ const lines = output.split("\n");
1398
+ if (lines.length > args.limit) {
1399
+ output = lines.slice(0, args.limit).join("\n") + `
1400
+ ... (truncated at ${args.limit} lines)`;
1401
+ }
1402
+ }
1403
+ return output;
1374
1404
  }
1375
- const toolExecStart = Date.now();
1376
- const allResults = await Promise.all(allPromises);
1377
- turnMetrics.local_tools_ms = Date.now() - toolExecStart;
1378
- for (const result of allResults) {
1379
- formatted.push(result);
1405
+ case "glob": {
1406
+ return toolGlob(provider, {
1407
+ pattern: args.pattern,
1408
+ path: args.path
1409
+ });
1380
1410
  }
1381
- if (formatted.length > 0) {
1382
- const turnMessage = formatTurnMessage(turn, maxTurns);
1383
- const contextBudget = calculateContextBudget(messages);
1384
- messages.push({ role: "user", content: formatted.join("\n") + turnMessage + "\n" + contextBudget });
1411
+ case "list_directory": {
1412
+ const dirPath = extractPathFromCommand(args.command || ".");
1413
+ return toolListDirectory(provider, { path: dirPath }, repoRoot);
1385
1414
  }
1386
- timings.turns.push(turnMetrics);
1387
- if (finishCalls.length) {
1388
- const fc = finishCalls[0];
1389
- const files = fc.arguments?.files ?? [];
1390
- const textResult = fc.arguments?.textResult;
1391
- finishMeta = { files };
1392
- terminationReason = "completed";
1393
- if (files.length === 0) {
1394
- const payload2 = textResult || "No relevant code found.";
1395
- timings.turns.push(turnMetrics);
1396
- timings.total_ms = Date.now() - totalStart;
1397
- return {
1398
- terminationReason: "completed",
1399
- messages,
1400
- finish: { payload: payload2, metadata: finishMeta },
1401
- timings
1402
- };
1403
- }
1404
- break;
1405
- }
1406
- }
1407
- if (terminationReason !== "completed" || !finishMeta) {
1408
- timings.total_ms = Date.now() - totalStart;
1409
- return { terminationReason, messages, errors, timings };
1410
- }
1411
- const parts = ["Relevant context found:"];
1412
- for (const f of finishMeta.files) {
1413
- const ranges = f.lines === "*" ? "*" : Array.isArray(f.lines) ? f.lines.map(([s, e]) => `${s}-${e}`).join(", ") : "*";
1414
- parts.push(`- ${f.path}: ${ranges}`);
1415
- }
1416
- const payload = parts.join("\n");
1417
- const finishResolutionStart = Date.now();
1418
- const fileReadErrors = [];
1419
- const resolved = await readFinishFiles(
1420
- repoRoot,
1421
- finishMeta.files,
1422
- async (p, s, e) => {
1423
- try {
1424
- const rr = await provider.read({ path: p, start: s, end: e });
1425
- return rr.lines.map((l) => {
1426
- const idx = l.indexOf("|");
1427
- return idx >= 0 ? l.slice(idx + 1) : l;
1428
- });
1429
- } catch (err) {
1430
- const errorMsg = err instanceof Error ? err.message : String(err);
1431
- fileReadErrors.push({ path: p, error: errorMsg });
1432
- console.error(`[warp_grep] Failed to read file: ${p} - ${errorMsg}`);
1433
- return [`[couldn't find: ${p}]`];
1415
+ case "read": {
1416
+ const readArgs = {
1417
+ path: args.path
1418
+ };
1419
+ if (args.lines && typeof args.lines === "string") {
1420
+ Object.assign(readArgs, parseReadLines(args.lines));
1434
1421
  }
1422
+ return toolRead(provider, readArgs);
1435
1423
  }
1436
- );
1437
- timings.finish_resolution_ms = Date.now() - finishResolutionStart;
1438
- if (fileReadErrors.length > 0) {
1439
- errors.push(...fileReadErrors.map((e) => ({ message: `File read error: ${e.path} - ${e.error}` })));
1424
+ default:
1425
+ return `Unknown tool: ${name}`;
1440
1426
  }
1441
- timings.total_ms = Date.now() - totalStart;
1442
- return {
1443
- terminationReason: "completed",
1444
- messages,
1445
- finish: { payload, metadata: finishMeta, resolved },
1446
- timings
1447
- };
1448
1427
  }
1449
1428
  async function* runWarpGrepStreaming(config) {
1450
1429
  const totalStart = Date.now();
1451
1430
  const timeoutMs = config.timeout ?? AGENT_CONFIG.TIMEOUT_MS;
1452
1431
  const timings = { turns: [], timeout_ms: timeoutMs };
1453
- const repoRoot = import_path2.default.resolve(config.repoRoot || process.cwd());
1432
+ const repoRoot = import_path3.default.resolve(config.repoRoot || process.cwd());
1454
1433
  const model = config.model || DEFAULT_MODEL;
1455
1434
  const messages = [];
1456
1435
  const maxTurns = AGENT_CONFIG.MAX_TURNS;
@@ -1466,7 +1445,7 @@ async function* runWarpGrepStreaming(config) {
1466
1445
  const turnMetrics = { turn, morph_api_ms: 0, local_tools_ms: 0 };
1467
1446
  enforceContextLimit(messages);
1468
1447
  const modelCallStart = Date.now();
1469
- const assistantContent = await callModel(messages, model, {
1448
+ const response = await callModel(messages, model, {
1470
1449
  morphApiKey: config.morphApiKey,
1471
1450
  morphApiUrl: config.morphApiUrl,
1472
1451
  retryConfig: config.retryConfig,
@@ -1474,90 +1453,45 @@ async function* runWarpGrepStreaming(config) {
1474
1453
  search_type: config.search_type
1475
1454
  }).catch((e) => {
1476
1455
  const errMsg = e instanceof Error ? e.message : String(e);
1477
- console.error(`[warp_grep:stream] Morph API call failed on turn ${turn}:`, errMsg);
1456
+ console.error(`[warp_grep] Morph API call failed on turn ${turn}:`, errMsg);
1478
1457
  errors.push({ message: errMsg });
1479
- return "";
1458
+ return null;
1480
1459
  });
1481
1460
  turnMetrics.morph_api_ms = Date.now() - modelCallStart;
1482
- if (!assistantContent) {
1483
- console.error(`[warp_grep:stream] Empty response from Morph API on turn ${turn}. Errors so far:`, errors);
1461
+ if (!response) {
1484
1462
  timings.turns.push(turnMetrics);
1485
1463
  break;
1486
1464
  }
1487
- messages.push({ role: "assistant", content: assistantContent });
1488
- const toolCalls = parser.parse(assistantContent);
1465
+ const toolCalls = response.tool_calls;
1466
+ messages.push({
1467
+ role: "assistant",
1468
+ content: response.content,
1469
+ ...toolCalls.length > 0 ? { tool_calls: toolCalls } : {}
1470
+ });
1489
1471
  if (toolCalls.length === 0) {
1490
- console.error(`[warp_grep:stream] No tool calls parsed on turn ${turn}. Assistant content (first 500 chars):`, assistantContent.slice(0, 500));
1491
- errors.push({ message: "No tool calls produced by the model. Your MCP is likely out of date! Update it by running: rm -rf ~/.npm/_npx && npm cache clean --force && npx -y @morphllm/morphmcp@latest" });
1472
+ console.error(`[warp_grep] No tool calls on turn ${turn}. Content: ${(response.content || "").slice(0, 500)}`);
1473
+ errors.push({ message: "No tool calls produced by the model." });
1492
1474
  terminationReason = "terminated";
1493
1475
  timings.turns.push(turnMetrics);
1494
1476
  break;
1495
1477
  }
1496
1478
  yield {
1497
1479
  turn,
1498
- toolCalls: toolCalls.map((c) => ({
1499
- name: c.name,
1500
- arguments: c.arguments ?? {}
1480
+ toolCalls: toolCalls.map((tc) => ({
1481
+ name: tc.function.name,
1482
+ arguments: safeParseJSON(tc.function.arguments)
1501
1483
  }))
1502
1484
  };
1503
- const finishCalls = toolCalls.filter((c) => c.name === "finish");
1504
- const grepCalls = toolCalls.filter((c) => c.name === "grep");
1505
- const listDirCalls = toolCalls.filter((c) => c.name === "list_directory");
1506
- const readCalls = toolCalls.filter((c) => c.name === "read");
1507
- const skipCalls = toolCalls.filter((c) => c.name === "_skip");
1508
- const formatted = [];
1509
- for (const c of skipCalls) {
1510
- const msg = c.arguments?.message || "Command skipped due to parsing error";
1511
- formatted.push(msg);
1512
- }
1513
- const allPromises = [];
1514
- for (const c of grepCalls) {
1515
- const args = c.arguments ?? {};
1516
- allPromises.push(
1517
- toolGrep(provider, args).then(
1518
- ({ output }) => formatAgentToolOutput("grep", args, output),
1519
- (err) => formatAgentToolOutput("grep", args, String(err), { isError: true })
1520
- )
1521
- );
1522
- }
1523
- for (const c of listDirCalls) {
1524
- const args = c.arguments ?? {};
1525
- allPromises.push(
1526
- toolListDirectory(provider, args).then(
1527
- (p) => formatAgentToolOutput("list_directory", args, p),
1528
- (err) => formatAgentToolOutput("list_directory", args, String(err), { isError: true })
1529
- )
1530
- );
1531
- }
1532
- for (const c of readCalls) {
1533
- const args = c.arguments ?? {};
1534
- allPromises.push(
1535
- toolRead(provider, args).then(
1536
- (p) => formatAgentToolOutput("read", args, p),
1537
- (err) => formatAgentToolOutput("read", args, String(err), { isError: true })
1538
- )
1539
- );
1540
- }
1541
- const toolExecStart = Date.now();
1542
- const allResults = await Promise.all(allPromises);
1543
- turnMetrics.local_tools_ms = Date.now() - toolExecStart;
1544
- for (const result of allResults) {
1545
- formatted.push(result);
1546
- }
1547
- if (formatted.length > 0) {
1548
- const turnMessage = formatTurnMessage(turn, maxTurns);
1549
- const contextBudget = calculateContextBudget(messages);
1550
- messages.push({ role: "user", content: formatted.join("\n") + turnMessage + "\n" + contextBudget });
1551
- }
1552
- timings.turns.push(turnMetrics);
1553
- if (finishCalls.length) {
1554
- const fc = finishCalls[0];
1555
- const files = fc.arguments?.files ?? [];
1556
- const textResult = fc.arguments?.textResult;
1485
+ const finishCall = toolCalls.find((tc) => tc.function.name === "finish");
1486
+ if (finishCall) {
1487
+ const args = safeParseJSON(finishCall.function.arguments);
1488
+ const filesStr = args.files || "";
1489
+ const files = parseFinishFiles(filesStr);
1557
1490
  finishMeta = { files };
1558
1491
  terminationReason = "completed";
1559
1492
  if (files.length === 0) {
1560
- const payload2 = textResult || "No relevant code found.";
1493
+ const payload2 = filesStr || "No relevant code found.";
1494
+ timings.turns.push(turnMetrics);
1561
1495
  timings.total_ms = Date.now() - totalStart;
1562
1496
  return {
1563
1497
  terminationReason: "completed",
@@ -1566,8 +1500,25 @@ async function* runWarpGrepStreaming(config) {
1566
1500
  timings
1567
1501
  };
1568
1502
  }
1503
+ timings.turns.push(turnMetrics);
1569
1504
  break;
1570
1505
  }
1506
+ const toolExecStart = Date.now();
1507
+ const results = await Promise.all(
1508
+ toolCalls.map(async (tc) => {
1509
+ const args = safeParseJSON(tc.function.arguments);
1510
+ const output = await executeTool(provider, tc.function.name, args, repoRoot).catch((err) => String(err));
1511
+ return { tool_call_id: tc.id, content: output };
1512
+ })
1513
+ );
1514
+ turnMetrics.local_tools_ms = Date.now() - toolExecStart;
1515
+ for (const result of results) {
1516
+ messages.push({ role: "tool", tool_call_id: result.tool_call_id, content: result.content });
1517
+ }
1518
+ const turnMsg = formatTurnMessage(turn, maxTurns);
1519
+ const budget = calculateContextBudget(messages);
1520
+ messages.push({ role: "user", content: turnMsg + "\n" + budget });
1521
+ timings.turns.push(turnMetrics);
1571
1522
  }
1572
1523
  if (terminationReason !== "completed" || !finishMeta) {
1573
1524
  timings.total_ms = Date.now() - totalStart;
@@ -1585,17 +1536,22 @@ async function* runWarpGrepStreaming(config) {
1585
1536
  repoRoot,
1586
1537
  finishMeta.files,
1587
1538
  async (p, s, e) => {
1539
+ let resolvedPath = p;
1540
+ if (!p.startsWith(repoRoot)) {
1541
+ const relative = p.startsWith("/") ? p.slice(1) : p;
1542
+ resolvedPath = import_path3.default.join(repoRoot, relative);
1543
+ }
1588
1544
  try {
1589
- const rr = await provider.read({ path: p, start: s, end: e });
1545
+ const rr = await provider.read({ path: resolvedPath, start: s, end: e });
1590
1546
  return rr.lines.map((l) => {
1591
1547
  const idx = l.indexOf("|");
1592
1548
  return idx >= 0 ? l.slice(idx + 1) : l;
1593
1549
  });
1594
1550
  } catch (err) {
1595
1551
  const errorMsg = err instanceof Error ? err.message : String(err);
1596
- fileReadErrors.push({ path: p, error: errorMsg });
1597
- console.error(`[warp_grep] Failed to read file: ${p} - ${errorMsg}`);
1598
- return [`[couldn't find: ${p}]`];
1552
+ fileReadErrors.push({ path: resolvedPath, error: errorMsg });
1553
+ console.error(`[warp_grep] Failed to read file: ${resolvedPath} - ${errorMsg}`);
1554
+ return [`[couldn't find: ${resolvedPath}]`];
1599
1555
  }
1600
1556
  }
1601
1557
  );
@@ -1611,6 +1567,14 @@ async function* runWarpGrepStreaming(config) {
1611
1567
  timings
1612
1568
  };
1613
1569
  }
1570
+ async function runWarpGrep(config) {
1571
+ const gen = runWarpGrepStreaming(config);
1572
+ let result = await gen.next();
1573
+ while (!result.done) {
1574
+ result = await gen.next();
1575
+ }
1576
+ return result.value;
1577
+ }
1614
1578
 
1615
1579
  // tools/warp_grep/providers/remote.ts
1616
1580
  init_config();
@@ -1790,6 +1754,35 @@ var RemoteCommandsProvider = class {
1790
1754
  return [];
1791
1755
  }
1792
1756
  }
1757
+ /**
1758
+ * Glob search - finds files matching a pattern.
1759
+ * Falls back to a grep --files approach via the listDir command.
1760
+ */
1761
+ async glob(params) {
1762
+ const searchPath = params.path || this.repoRoot;
1763
+ try {
1764
+ const stdout = await this.commands.listDir(searchPath, 10);
1765
+ const allPaths = (stdout || "").trim().split(/\r?\n/).filter((p) => p.length > 0);
1766
+ const globToRegex = (glob) => {
1767
+ const escaped = glob.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".");
1768
+ return new RegExp(escaped);
1769
+ };
1770
+ const regex = globToRegex(params.pattern);
1771
+ const matched = allPaths.filter((p) => {
1772
+ const name = p.split("/").pop() || "";
1773
+ return regex.test(name) && !shouldSkip(name);
1774
+ });
1775
+ const totalFound = matched.length;
1776
+ return { files: matched.slice(0, 100), searchDir: searchPath, totalFound };
1777
+ } catch (error) {
1778
+ return {
1779
+ files: [],
1780
+ searchDir: searchPath,
1781
+ totalFound: 0,
1782
+ error: `[GLOB ERROR] ${error instanceof Error ? error.message : String(error)}`
1783
+ };
1784
+ }
1785
+ }
1793
1786
  };
1794
1787
 
1795
1788
  // tools/warp_grep/providers/code_storage_http.ts
@@ -1816,9 +1809,9 @@ function createCodeStorageHttpCommands(config) {
1816
1809
  const { baseUrl, repoId, branch } = config;
1817
1810
  const encodedRepoId = encodeURIComponent(repoId);
1818
1811
  return {
1819
- grep: (pattern, path5, glob) => post(`${baseUrl}/api/code-search/${encodedRepoId}/grep`, { pattern, path: path5, glob, branch }, "grep"),
1820
- read: (path5, start, end) => post(`${baseUrl}/api/code-search/${encodedRepoId}/read`, { path: path5, start, end, branch }, "read"),
1821
- listDir: (path5, maxDepth) => post(`${baseUrl}/api/code-search/${encodedRepoId}/list`, { path: path5, maxDepth, branch }, "list")
1812
+ grep: (pattern, path6, glob) => post(`${baseUrl}/api/code-search/${encodedRepoId}/grep`, { pattern, path: path6, glob, branch }, "grep"),
1813
+ read: (path6, start, end) => post(`${baseUrl}/api/code-search/${encodedRepoId}/read`, { path: path6, start, end, branch }, "read"),
1814
+ listDir: (path6, maxDepth) => post(`${baseUrl}/api/code-search/${encodedRepoId}/list`, { path: path6, maxDepth, branch }, "list")
1822
1815
  };
1823
1816
  }
1824
1817
 
@@ -2150,6 +2143,25 @@ init_paths();
2150
2143
  // tools/warp_grep/agent/index.ts
2151
2144
  init_config();
2152
2145
 
2146
+ // tools/warp_grep/agent/formatter.ts
2147
+ var ToolOutputFormatter = class {
2148
+ format(toolName, _args, output, options = {}) {
2149
+ const name = (toolName ?? "").trim();
2150
+ if (!name) {
2151
+ return "";
2152
+ }
2153
+ const payload = output?.toString?.()?.trim?.() ?? "";
2154
+ const isError = Boolean(options.isError);
2155
+ if (!payload && !isError) {
2156
+ return "";
2157
+ }
2158
+ return `<tool_response>
2159
+ ${payload}
2160
+ </tool_response>`;
2161
+ }
2162
+ };
2163
+ var sharedFormatter = new ToolOutputFormatter();
2164
+
2153
2165
  // tools/warp_grep/index.ts
2154
2166
  var warpGrepInputSchema = import_zod.z.object({
2155
2167
  search_term: import_zod.z.string().describe("Search problem statement that this subagent is supposed to research for")