@morphllm/morphsdk 0.2.143 → 0.2.145

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 (208) hide show
  1. package/dist/{chunk-YIETFYCL.js → chunk-4LWMPKSB.js} +71 -44
  2. package/dist/chunk-4LWMPKSB.js.map +1 -0
  3. package/dist/{chunk-HZOTLGJH.js → chunk-4Y2NM6JD.js} +42 -2
  4. package/dist/chunk-4Y2NM6JD.js.map +1 -0
  5. package/dist/{chunk-YTPYQAIO.js → chunk-5FCXLQJU.js} +3 -3
  6. package/dist/{chunk-KQ6QSAE7.js → chunk-6CFKWZK3.js} +3 -3
  7. package/dist/{chunk-NF2QWJDY.js → chunk-B3AKP3RA.js} +31 -2
  8. package/dist/chunk-B3AKP3RA.js.map +1 -0
  9. package/dist/{chunk-ALY5S4XC.js → chunk-BAF33L6C.js} +2 -2
  10. package/dist/{chunk-TRPJAKAS.js → chunk-BXRJYLRS.js} +2 -2
  11. package/dist/chunk-CMSHXALI.js +60 -0
  12. package/dist/chunk-CMSHXALI.js.map +1 -0
  13. package/dist/{chunk-X5IVORU2.js → chunk-EPIOAODF.js} +2 -2
  14. package/dist/{chunk-T53IMLYK.js → chunk-G23BI5CQ.js} +2 -2
  15. package/dist/{chunk-FZQZZP3R.js → chunk-GHPQYSSF.js} +2 -2
  16. package/dist/{chunk-7POOJWBR.js → chunk-GXCWKYGU.js} +2 -2
  17. package/dist/{chunk-PBOKDQR3.js → chunk-GXM3G7Z4.js} +3 -3
  18. package/dist/{chunk-OSQ2EMUP.js → chunk-HBIW2XV2.js} +2 -2
  19. package/dist/{chunk-FJVYE6ZX.js → chunk-HE7K2QNQ.js} +17 -17
  20. package/dist/{chunk-FIVYDIHX.js → chunk-HYRHI2UL.js} +1 -1
  21. package/dist/{chunk-DKODF3YG.js → chunk-I3J46TSB.js} +5 -4
  22. package/dist/chunk-I3J46TSB.js.map +1 -0
  23. package/dist/{chunk-F6JGAEK5.js → chunk-IRWHN55G.js} +1 -1
  24. package/dist/{chunk-QFCED636.js → chunk-JHYH3NEP.js} +2 -2
  25. package/dist/chunk-JHYH3NEP.js.map +1 -0
  26. package/dist/{chunk-YJG2KRXY.js → chunk-JMUAQQJU.js} +3 -3
  27. package/dist/{chunk-W27OOF55.js → chunk-JRBU4UNP.js} +2 -2
  28. package/dist/{chunk-DN3WS37U.js → chunk-KELRCMA6.js} +2 -2
  29. package/dist/{chunk-DN3WS37U.js.map → chunk-KELRCMA6.js.map} +1 -1
  30. package/dist/{chunk-TLPFEK5S.js → chunk-MRPASJBX.js} +2 -2
  31. package/dist/{chunk-RMBIT3I3.js → chunk-MTJ3PR4M.js} +2 -2
  32. package/dist/{chunk-VFGM343D.js → chunk-N7TTZIBK.js} +2 -2
  33. package/dist/chunk-OPEQQGST.js +396 -0
  34. package/dist/chunk-OPEQQGST.js.map +1 -0
  35. package/dist/{chunk-Z5APBTQP.js → chunk-PX7ODEML.js} +2 -2
  36. package/dist/{chunk-OOZZE5BQ.js → chunk-RZXS4ADX.js} +2 -2
  37. package/dist/{chunk-VBSO32I2.js → chunk-S54SPKX3.js} +3 -3
  38. package/dist/{chunk-ENLZ45ZZ.js → chunk-SUE4GYA2.js} +2 -2
  39. package/dist/{chunk-TXOUDREW.js → chunk-VRV5UYTN.js} +2 -2
  40. package/dist/{chunk-ZGJLA2O6.js → chunk-XL7R3XN5.js} +2 -2
  41. package/dist/client.cjs +427 -439
  42. package/dist/client.cjs.map +1 -1
  43. package/dist/client.js +27 -28
  44. package/dist/edge.cjs +1 -1
  45. package/dist/edge.cjs.map +1 -1
  46. package/dist/edge.js +4 -4
  47. package/dist/{finish-DBKuo8yj.d.ts → finish-Ddj1MPGt.d.ts} +1 -1
  48. package/dist/index.cjs +446 -439
  49. package/dist/index.cjs.map +1 -1
  50. package/dist/index.js +30 -30
  51. package/dist/modelrouter/core.cjs +1 -1
  52. package/dist/modelrouter/core.cjs.map +1 -1
  53. package/dist/modelrouter/core.js +3 -3
  54. package/dist/modelrouter/index.cjs +1 -1
  55. package/dist/modelrouter/index.cjs.map +1 -1
  56. package/dist/modelrouter/index.js +3 -3
  57. package/dist/subagents/anthropic.cjs +422 -434
  58. package/dist/subagents/anthropic.cjs.map +1 -1
  59. package/dist/subagents/anthropic.js +8 -9
  60. package/dist/subagents/vercel.cjs +422 -434
  61. package/dist/subagents/vercel.cjs.map +1 -1
  62. package/dist/subagents/vercel.js +8 -9
  63. package/dist/tools/browser/anthropic.cjs +1 -1
  64. package/dist/tools/browser/anthropic.cjs.map +1 -1
  65. package/dist/tools/browser/anthropic.js +5 -5
  66. package/dist/tools/browser/core.cjs +1 -1
  67. package/dist/tools/browser/core.cjs.map +1 -1
  68. package/dist/tools/browser/core.js +4 -4
  69. package/dist/tools/browser/index.cjs +1 -1
  70. package/dist/tools/browser/index.cjs.map +1 -1
  71. package/dist/tools/browser/index.js +7 -7
  72. package/dist/tools/browser/openai.cjs +1 -1
  73. package/dist/tools/browser/openai.cjs.map +1 -1
  74. package/dist/tools/browser/openai.js +5 -5
  75. package/dist/tools/browser/profiles/core.cjs +1 -1
  76. package/dist/tools/browser/profiles/core.cjs.map +1 -1
  77. package/dist/tools/browser/profiles/core.js +3 -3
  78. package/dist/tools/browser/profiles/index.cjs +1 -1
  79. package/dist/tools/browser/profiles/index.cjs.map +1 -1
  80. package/dist/tools/browser/profiles/index.js +3 -3
  81. package/dist/tools/browser/vercel.cjs +1 -1
  82. package/dist/tools/browser/vercel.cjs.map +1 -1
  83. package/dist/tools/browser/vercel.js +5 -5
  84. package/dist/tools/codebase_search/anthropic.cjs +1 -1
  85. package/dist/tools/codebase_search/anthropic.cjs.map +1 -1
  86. package/dist/tools/codebase_search/anthropic.js +4 -4
  87. package/dist/tools/codebase_search/core.cjs +1 -1
  88. package/dist/tools/codebase_search/core.cjs.map +1 -1
  89. package/dist/tools/codebase_search/core.js +3 -3
  90. package/dist/tools/codebase_search/index.cjs +1 -1
  91. package/dist/tools/codebase_search/index.cjs.map +1 -1
  92. package/dist/tools/codebase_search/index.js +6 -6
  93. package/dist/tools/codebase_search/openai.cjs +1 -1
  94. package/dist/tools/codebase_search/openai.cjs.map +1 -1
  95. package/dist/tools/codebase_search/openai.js +4 -4
  96. package/dist/tools/codebase_search/vercel.cjs +1 -1
  97. package/dist/tools/codebase_search/vercel.cjs.map +1 -1
  98. package/dist/tools/codebase_search/vercel.js +4 -4
  99. package/dist/tools/fastapply/anthropic.cjs +1 -1
  100. package/dist/tools/fastapply/anthropic.cjs.map +1 -1
  101. package/dist/tools/fastapply/anthropic.js +4 -4
  102. package/dist/tools/fastapply/apply.cjs +1 -1
  103. package/dist/tools/fastapply/apply.cjs.map +1 -1
  104. package/dist/tools/fastapply/apply.js +2 -2
  105. package/dist/tools/fastapply/core.cjs +1 -1
  106. package/dist/tools/fastapply/core.cjs.map +1 -1
  107. package/dist/tools/fastapply/core.js +3 -3
  108. package/dist/tools/fastapply/index.cjs +1 -1
  109. package/dist/tools/fastapply/index.cjs.map +1 -1
  110. package/dist/tools/fastapply/index.js +6 -6
  111. package/dist/tools/fastapply/openai.cjs +1 -1
  112. package/dist/tools/fastapply/openai.cjs.map +1 -1
  113. package/dist/tools/fastapply/openai.js +4 -4
  114. package/dist/tools/fastapply/vercel.cjs +1 -1
  115. package/dist/tools/fastapply/vercel.cjs.map +1 -1
  116. package/dist/tools/fastapply/vercel.js +4 -4
  117. package/dist/tools/index.cjs +1 -1
  118. package/dist/tools/index.cjs.map +1 -1
  119. package/dist/tools/index.js +6 -6
  120. package/dist/tools/utils/resilience.cjs +1 -1
  121. package/dist/tools/utils/resilience.cjs.map +1 -1
  122. package/dist/tools/utils/resilience.js +2 -2
  123. package/dist/tools/warp_grep/agent/config.cjs +4 -3
  124. package/dist/tools/warp_grep/agent/config.cjs.map +1 -1
  125. package/dist/tools/warp_grep/agent/config.d.ts +2 -1
  126. package/dist/tools/warp_grep/agent/config.js +1 -1
  127. package/dist/tools/warp_grep/agent/parser.cjs +52 -121
  128. package/dist/tools/warp_grep/agent/parser.cjs.map +1 -1
  129. package/dist/tools/warp_grep/agent/parser.d.ts +12 -5
  130. package/dist/tools/warp_grep/agent/parser.js +7 -3
  131. package/dist/tools/warp_grep/agent/runner.cjs +335 -416
  132. package/dist/tools/warp_grep/agent/runner.cjs.map +1 -1
  133. package/dist/tools/warp_grep/agent/runner.d.ts +6 -3
  134. package/dist/tools/warp_grep/agent/runner.js +5 -6
  135. package/dist/tools/warp_grep/agent/types.cjs.map +1 -1
  136. package/dist/tools/warp_grep/agent/types.d.ts +22 -3
  137. package/dist/tools/warp_grep/anthropic.cjs +423 -435
  138. package/dist/tools/warp_grep/anthropic.cjs.map +1 -1
  139. package/dist/tools/warp_grep/anthropic.js +9 -10
  140. package/dist/tools/warp_grep/client.cjs +422 -434
  141. package/dist/tools/warp_grep/client.cjs.map +1 -1
  142. package/dist/tools/warp_grep/client.js +7 -8
  143. package/dist/tools/warp_grep/gemini.cjs +423 -435
  144. package/dist/tools/warp_grep/gemini.cjs.map +1 -1
  145. package/dist/tools/warp_grep/gemini.js +8 -9
  146. package/dist/tools/warp_grep/gemini.js.map +1 -1
  147. package/dist/tools/warp_grep/harness.cjs +164 -176
  148. package/dist/tools/warp_grep/harness.cjs.map +1 -1
  149. package/dist/tools/warp_grep/harness.d.ts +17 -38
  150. package/dist/tools/warp_grep/harness.js +15 -14
  151. package/dist/tools/warp_grep/harness.js.map +1 -1
  152. package/dist/tools/warp_grep/index.cjs +442 -435
  153. package/dist/tools/warp_grep/index.cjs.map +1 -1
  154. package/dist/tools/warp_grep/index.d.ts +1 -1
  155. package/dist/tools/warp_grep/index.js +11 -11
  156. package/dist/tools/warp_grep/openai.cjs +423 -435
  157. package/dist/tools/warp_grep/openai.cjs.map +1 -1
  158. package/dist/tools/warp_grep/openai.js +9 -10
  159. package/dist/tools/warp_grep/providers/local.cjs +43 -2
  160. package/dist/tools/warp_grep/providers/local.cjs.map +1 -1
  161. package/dist/tools/warp_grep/providers/local.d.ts +5 -1
  162. package/dist/tools/warp_grep/providers/local.js +2 -2
  163. package/dist/tools/warp_grep/providers/remote.cjs +32 -2
  164. package/dist/tools/warp_grep/providers/remote.cjs.map +1 -1
  165. package/dist/tools/warp_grep/providers/remote.d.ts +9 -1
  166. package/dist/tools/warp_grep/providers/remote.js +2 -2
  167. package/dist/tools/warp_grep/providers/types.cjs.map +1 -1
  168. package/dist/tools/warp_grep/providers/types.d.ts +14 -1
  169. package/dist/tools/warp_grep/vercel.cjs +423 -435
  170. package/dist/tools/warp_grep/vercel.cjs.map +1 -1
  171. package/dist/tools/warp_grep/vercel.js +9 -10
  172. package/dist/version.cjs +1 -1
  173. package/dist/version.cjs.map +1 -1
  174. package/dist/version.js +1 -1
  175. package/package.json +1 -1
  176. package/dist/chunk-DKODF3YG.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-QFCED636.js.map +0 -1
  180. package/dist/chunk-SSQYLUKI.js +0 -409
  181. package/dist/chunk-SSQYLUKI.js.map +0 -1
  182. package/dist/chunk-VCKJ22DX.js +0 -131
  183. package/dist/chunk-VCKJ22DX.js.map +0 -1
  184. package/dist/chunk-YIETFYCL.js.map +0 -1
  185. /package/dist/{chunk-YTPYQAIO.js.map → chunk-5FCXLQJU.js.map} +0 -0
  186. /package/dist/{chunk-KQ6QSAE7.js.map → chunk-6CFKWZK3.js.map} +0 -0
  187. /package/dist/{chunk-ALY5S4XC.js.map → chunk-BAF33L6C.js.map} +0 -0
  188. /package/dist/{chunk-TRPJAKAS.js.map → chunk-BXRJYLRS.js.map} +0 -0
  189. /package/dist/{chunk-X5IVORU2.js.map → chunk-EPIOAODF.js.map} +0 -0
  190. /package/dist/{chunk-T53IMLYK.js.map → chunk-G23BI5CQ.js.map} +0 -0
  191. /package/dist/{chunk-FZQZZP3R.js.map → chunk-GHPQYSSF.js.map} +0 -0
  192. /package/dist/{chunk-7POOJWBR.js.map → chunk-GXCWKYGU.js.map} +0 -0
  193. /package/dist/{chunk-PBOKDQR3.js.map → chunk-GXM3G7Z4.js.map} +0 -0
  194. /package/dist/{chunk-OSQ2EMUP.js.map → chunk-HBIW2XV2.js.map} +0 -0
  195. /package/dist/{chunk-FJVYE6ZX.js.map → chunk-HE7K2QNQ.js.map} +0 -0
  196. /package/dist/{chunk-FIVYDIHX.js.map → chunk-HYRHI2UL.js.map} +0 -0
  197. /package/dist/{chunk-F6JGAEK5.js.map → chunk-IRWHN55G.js.map} +0 -0
  198. /package/dist/{chunk-YJG2KRXY.js.map → chunk-JMUAQQJU.js.map} +0 -0
  199. /package/dist/{chunk-W27OOF55.js.map → chunk-JRBU4UNP.js.map} +0 -0
  200. /package/dist/{chunk-TLPFEK5S.js.map → chunk-MRPASJBX.js.map} +0 -0
  201. /package/dist/{chunk-RMBIT3I3.js.map → chunk-MTJ3PR4M.js.map} +0 -0
  202. /package/dist/{chunk-VFGM343D.js.map → chunk-N7TTZIBK.js.map} +0 -0
  203. /package/dist/{chunk-Z5APBTQP.js.map → chunk-PX7ODEML.js.map} +0 -0
  204. /package/dist/{chunk-OOZZE5BQ.js.map → chunk-RZXS4ADX.js.map} +0 -0
  205. /package/dist/{chunk-VBSO32I2.js.map → chunk-S54SPKX3.js.map} +0 -0
  206. /package/dist/{chunk-ENLZ45ZZ.js.map → chunk-SUE4GYA2.js.map} +0 -0
  207. /package/dist/{chunk-TXOUDREW.js.map → chunk-VRV5UYTN.js.map} +0 -0
  208. /package/dist/{chunk-ZGJLA2O6.js.map → chunk-XL7R3XN5.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
  });
@@ -587,131 +628,58 @@ module.exports = __toCommonJS(anthropic_exports);
587
628
  init_config();
588
629
 
589
630
  // tools/warp_grep/agent/parser.ts
590
- var VALID_COMMANDS = ["list_directory", "ripgrep", "read", "finish"];
591
- function isValidCommand(name) {
592
- return VALID_COMMANDS.includes(name);
631
+ function parseReadLines(linesStr) {
632
+ const ranges = [];
633
+ for (const rangeStr of linesStr.split(",")) {
634
+ const trimmed = rangeStr.trim();
635
+ if (!trimmed) continue;
636
+ const parts = trimmed.split("-").map((v) => parseInt(v.trim(), 10));
637
+ if (parts.length >= 2 && Number.isFinite(parts[0]) && Number.isFinite(parts[1])) {
638
+ ranges.push([parts[0], parts[1]]);
639
+ } else if (Number.isFinite(parts[0])) {
640
+ ranges.push([parts[0], parts[0]]);
641
+ }
642
+ }
643
+ if (ranges.length === 1) return { start: ranges[0][0], end: ranges[0][1] };
644
+ if (ranges.length > 1) return { lines: ranges };
645
+ return {};
593
646
  }
594
- function parseQwen3ToolCalls(text) {
595
- const tools = [];
596
- const toolCallRegex = /<tool_call>\s*<function=([a-z_][a-z0-9_]*)>([\s\S]*?)<\/function>\s*<\/tool_call>/gi;
597
- let match;
598
- while ((match = toolCallRegex.exec(text)) !== null) {
599
- const funcName = match[1].toLowerCase();
600
- const body = match[2];
601
- if (!isValidCommand(funcName)) continue;
602
- const params = {};
603
- const paramRegex = /<parameter=([a-z_][a-z0-9_]*)>([\s\S]*?)<\/parameter>/gi;
604
- let paramMatch;
605
- while ((paramMatch = paramRegex.exec(body)) !== null) {
606
- params[paramMatch[1].toLowerCase()] = paramMatch[2].trim();
647
+ function parseFinishFiles(filesStr) {
648
+ const files = [];
649
+ for (const line of filesStr.trim().split(/\s+/)) {
650
+ const trimmed = line.trim();
651
+ if (!trimmed) continue;
652
+ const colonIdx = trimmed.indexOf(":");
653
+ if (colonIdx === -1) {
654
+ files.push({ path: trimmed, lines: "*" });
655
+ continue;
607
656
  }
608
- if (funcName === "ripgrep") {
609
- const pattern = params.pattern;
610
- if (!pattern) continue;
611
- const args = {
612
- pattern,
613
- path: params.path || ".",
614
- ...params.glob && { glob: params.glob },
615
- ...params.context_lines && { context_lines: parseInt(params.context_lines, 10) },
616
- ...params.case_sensitive && { case_sensitive: params.case_sensitive === "true" }
617
- };
618
- tools.push({ name: "grep", arguments: args });
619
- } else if (funcName === "list_directory") {
620
- const command = params.command;
621
- const directPath = params.path;
622
- let dirPath = directPath || ".";
623
- if (!directPath && command) {
624
- const tokens = command.trim().split(/\s+/);
625
- const pathTokens = tokens.slice(1).filter((t) => !t.startsWith("-") && !t.startsWith("|") && !t.startsWith("\\("));
626
- if (pathTokens.length > 0) {
627
- dirPath = pathTokens[0];
628
- }
629
- }
630
- tools.push({ name: "list_directory", arguments: { path: dirPath, pattern: params.pattern || null } });
631
- } else if (funcName === "read") {
632
- const filePath = params.path;
633
- if (!filePath) continue;
634
- const args = { path: filePath };
635
- const linesStr = params.lines;
636
- if (linesStr) {
637
- const ranges = [];
638
- for (const rangeStr of linesStr.split(",")) {
639
- const trimmed = rangeStr.trim();
640
- if (!trimmed) continue;
641
- const [s, e] = trimmed.split("-").map((v) => parseInt(v.trim(), 10));
642
- if (Number.isFinite(s) && Number.isFinite(e)) {
643
- ranges.push([s, e]);
644
- } else if (Number.isFinite(s)) {
645
- ranges.push([s, s]);
646
- }
647
- }
648
- if (ranges.length === 1) {
649
- args.start = ranges[0][0];
650
- args.end = ranges[0][1];
651
- } else if (ranges.length > 1) {
652
- args.lines = ranges;
653
- }
654
- }
655
- tools.push({ name: "read", arguments: args });
656
- } else if (funcName === "finish") {
657
- if (params.result && !params.files) {
658
- tools.push({ name: "finish", arguments: { files: [], textResult: params.result } });
659
- continue;
660
- }
661
- const filesStr = params.files;
662
- if (!filesStr) {
663
- tools.push({ name: "finish", arguments: { files: [], textResult: "No relevant code found." } });
664
- continue;
665
- }
666
- const files = [];
667
- for (const line of filesStr.split("\n")) {
668
- const trimmed = line.trim();
669
- if (!trimmed) continue;
670
- const colonIdx = trimmed.indexOf(":");
671
- if (colonIdx === -1) {
672
- files.push({ path: trimmed, lines: "*" });
673
- } else {
674
- const filePath = trimmed.slice(0, colonIdx);
675
- const rangesPart = trimmed.slice(colonIdx + 1);
676
- const ranges = [];
677
- for (const rangeStr of rangesPart.split(",")) {
678
- const rt = rangeStr.trim();
679
- if (!rt || rt === "*") {
680
- files.push({ path: filePath, lines: "*" });
681
- break;
682
- }
683
- const [s, e] = rt.split("-").map((v) => parseInt(v.trim(), 10));
684
- if (Number.isFinite(s) && Number.isFinite(e)) {
685
- ranges.push([s, e]);
686
- } else if (Number.isFinite(s)) {
687
- ranges.push([s, s]);
688
- }
689
- }
690
- if (ranges.length > 0) {
691
- files.push({ path: filePath, lines: ranges });
692
- } else if (!files.some((f) => f.path === filePath)) {
693
- files.push({ path: filePath, lines: "*" });
694
- }
695
- }
696
- }
697
- if (files.length > 0) {
698
- tools.push({ name: "finish", arguments: { files } });
699
- } else {
700
- tools.push({ name: "finish", arguments: { files: [], textResult: filesStr } });
657
+ const filePath = trimmed.slice(0, colonIdx);
658
+ const rangesPart = trimmed.slice(colonIdx + 1);
659
+ if (!rangesPart.trim() || rangesPart.trim() === "*") {
660
+ files.push({ path: filePath, lines: "*" });
661
+ continue;
662
+ }
663
+ const ranges = [];
664
+ for (const rangeStr of rangesPart.split(",")) {
665
+ const rt = rangeStr.trim();
666
+ if (!rt) continue;
667
+ const parts = rt.split("-").map((v) => parseInt(v.trim(), 10));
668
+ if (parts.length >= 2 && Number.isFinite(parts[0]) && Number.isFinite(parts[1])) {
669
+ ranges.push([parts[0], parts[1]]);
670
+ } else if (Number.isFinite(parts[0])) {
671
+ ranges.push([parts[0], parts[0]]);
701
672
  }
702
673
  }
674
+ files.push({ path: filePath, lines: ranges.length > 0 ? ranges : "*" });
703
675
  }
704
- return tools;
676
+ return files;
677
+ }
678
+ function extractPathFromCommand(command) {
679
+ const tokens = command.trim().split(/\s+/);
680
+ const pathTokens = tokens.slice(1).filter((t) => !t.startsWith("-") && !t.startsWith("|") && !t.startsWith("\\("));
681
+ return pathTokens[0] || ".";
705
682
  }
706
- var LLMResponseParser = class {
707
- parse(text) {
708
- if (typeof text !== "string") {
709
- throw new TypeError("Command text must be a string.");
710
- }
711
- const withoutThink = text.replace(/<think>[\s\S]*?<\/think>/gi, "");
712
- return parseQwen3ToolCalls(withoutThink);
713
- }
714
- };
715
683
 
716
684
  // tools/warp_grep/agent/tools/grep.ts
717
685
  async function toolGrep(provider, args) {
@@ -762,29 +730,42 @@ async function toolRead(provider, args) {
762
730
 
763
731
  // tools/warp_grep/agent/tools/list_directory.ts
764
732
  init_config();
765
- async function toolListDirectory(provider, args) {
766
- const maxResults = args.maxResults ?? AGENT_CONFIG.MAX_OUTPUT_LINES;
767
- const initialDepth = args.maxDepth ?? AGENT_CONFIG.MAX_LIST_DEPTH;
768
- async function getListRecursive(currentDepth) {
769
- const entries = await provider.listDirectory({
770
- path: args.path,
771
- pattern: args.pattern ?? null,
772
- maxResults,
773
- maxDepth: currentDepth
774
- });
775
- if (entries.length >= maxResults && currentDepth > 0) {
776
- return getListRecursive(currentDepth - 1);
777
- }
778
- return { entries };
779
- }
780
- const { entries: list } = await getListRecursive(initialDepth);
781
- if (!list.length) return "empty";
782
- const tree = list.map((e) => {
783
- const indent = " ".repeat(e.depth);
784
- const name = e.type === "dir" ? `${e.name}/` : e.name;
785
- return `${indent}${name}`;
786
- }).join("\n");
787
- return tree;
733
+ var import_path = __toESM(require("path"), 1);
734
+ async function toolListDirectory(provider, args, repoRoot) {
735
+ const maxResults = args.maxResults ?? AGENT_CONFIG.MAX_LIST_RESULTS;
736
+ const maxDepth = args.maxDepth ?? AGENT_CONFIG.MAX_LIST_DEPTH;
737
+ const entries = await provider.listDirectory({
738
+ path: args.path,
739
+ pattern: args.pattern ?? null,
740
+ maxResults,
741
+ maxDepth
742
+ });
743
+ if (!entries.length) return "empty";
744
+ if (repoRoot) {
745
+ const absRoot = import_path.default.resolve(repoRoot);
746
+ const lines = entries.map((e) => import_path.default.join(absRoot, e.path));
747
+ return lines.join("\n");
748
+ }
749
+ return entries.map((e) => e.path).join("\n");
750
+ }
751
+
752
+ // tools/warp_grep/agent/tools/glob.ts
753
+ async function toolGlob(provider, args) {
754
+ const res = await provider.glob(args);
755
+ if (res.error) {
756
+ return res.error;
757
+ }
758
+ if (!res.files.length) {
759
+ return "no matches";
760
+ }
761
+ const header = `Found ${res.totalFound} file(s) matching "${args.pattern}" within ${res.searchDir}, sorted by modification time (newest first):`;
762
+ const body = res.files.join("\n");
763
+ const truncated = res.totalFound > res.files.length ? `
764
+ [${res.totalFound - res.files.length} files truncated]` : "";
765
+ return `${header}
766
+ ---
767
+ ${body}
768
+ ---${truncated}`;
788
769
  }
789
770
 
790
771
  // tools/warp_grep/agent/tools/finish.ts
@@ -845,32 +826,21 @@ function mergeRanges(ranges) {
845
826
  return merged;
846
827
  }
847
828
 
848
- // tools/warp_grep/agent/formatter.ts
849
- var ToolOutputFormatter = class {
850
- format(toolName, _args, output, options = {}) {
851
- const name = (toolName ?? "").trim();
852
- if (!name) {
853
- return "";
854
- }
855
- const payload = output?.toString?.()?.trim?.() ?? "";
856
- const isError = Boolean(options.isError);
857
- if (!payload && !isError) {
858
- return "";
859
- }
860
- return `<tool_response>
861
- ${payload}
862
- </tool_response>`;
863
- }
864
- };
865
- var sharedFormatter = new ToolOutputFormatter();
866
- function formatAgentToolOutput(toolName, args, output, options = {}) {
867
- return sharedFormatter.format(toolName, args, output, options);
868
- }
869
-
870
829
  // tools/warp_grep/agent/helpers.ts
871
- var import_path = __toESM(require("path"), 1);
830
+ var import_path2 = __toESM(require("path"), 1);
872
831
  init_config();
873
832
  var TRUNCATED_MARKER = "[truncated for context limit]";
833
+ function getMessageSize(m) {
834
+ if (m.role === "tool") return m.content.length;
835
+ if (m.role === "assistant") {
836
+ let size = typeof m.content === "string" ? m.content.length : 0;
837
+ if (m.tool_calls) {
838
+ size += m.tool_calls.reduce((s, tc) => s + tc.function.name.length + tc.function.arguments.length, 0);
839
+ }
840
+ return size;
841
+ }
842
+ return m.content.length;
843
+ }
874
844
  function formatTurnMessage(turnsUsed, maxTurns) {
875
845
  const turnsRemaining = maxTurns - turnsUsed;
876
846
  if (turnsRemaining === 1) {
@@ -881,7 +851,7 @@ You have used ${turnsUsed} turns, you only have 1 turn remaining. You have run o
881
851
  You have used ${turnsUsed} turn${turnsUsed === 1 ? "" : "s"} and have ${turnsRemaining} remaining`;
882
852
  }
883
853
  function calculateContextBudget(messages) {
884
- const totalChars = messages.reduce((sum, m) => sum + m.content.length, 0);
854
+ const totalChars = messages.reduce((sum, m) => sum + getMessageSize(m), 0);
885
855
  const maxChars = AGENT_CONFIG.MAX_CONTEXT_CHARS;
886
856
  const percent = Math.round(totalChars / maxChars * 100);
887
857
  const usedK = Math.round(totalChars / 1e3);
@@ -890,24 +860,21 @@ function calculateContextBudget(messages) {
890
860
  }
891
861
  async function buildInitialState(repoRoot, searchTerm, provider, options) {
892
862
  const budget = calculateContextBudget([]);
893
- const turnTag = `Turn 0/${AGENT_CONFIG.MAX_TURNS}`;
863
+ const turnTag = `You have used 0 turns and have ${AGENT_CONFIG.MAX_TURNS} remaining`;
894
864
  const treeDepth = options?.search_type === "node_modules" ? 1 : 2;
865
+ const absRoot = import_path2.default.resolve(repoRoot);
895
866
  try {
896
867
  const entries = await provider.listDirectory({
897
868
  path: ".",
898
869
  maxResults: AGENT_CONFIG.MAX_OUTPUT_LINES,
899
870
  maxDepth: treeDepth
900
871
  });
901
- const treeLines = entries.map((e) => {
902
- const indent = " ".repeat(e.depth);
903
- const name = e.type === "dir" ? `${e.name}/` : e.name;
904
- return `${indent}${name}`;
905
- });
906
- const repoName = import_path.default.basename(repoRoot);
907
- const treeOutput = treeLines.length > 0 ? `${repoName}/
908
- ${treeLines.join("\n")}` : `${repoName}/`;
872
+ const lines = [absRoot];
873
+ for (const e of entries) {
874
+ lines.push(import_path2.default.join(absRoot, e.path));
875
+ }
909
876
  return `<repo_structure>
910
- ${treeOutput}
877
+ ${lines.join("\n")}
911
878
  </repo_structure>
912
879
 
913
880
  <search_string>
@@ -916,9 +883,8 @@ ${searchTerm}
916
883
  ${budget}
917
884
  ${turnTag}`;
918
885
  } catch {
919
- const repoName = import_path.default.basename(repoRoot);
920
886
  return `<repo_structure>
921
- ${repoName}/
887
+ ${absRoot}
922
888
  </repo_structure>
923
889
 
924
890
  <search_string>
@@ -929,26 +895,32 @@ ${turnTag}`;
929
895
  }
930
896
  }
931
897
  function enforceContextLimit(messages, maxChars = AGENT_CONFIG.MAX_CONTEXT_CHARS) {
932
- const getTotalChars = () => messages.reduce((sum, m) => sum + m.content.length, 0);
898
+ const getTotalChars = () => messages.reduce((sum, m) => sum + getMessageSize(m), 0);
933
899
  if (getTotalChars() <= maxChars) {
934
900
  return messages;
935
901
  }
936
- const userIndices = [];
902
+ const truncatableIndices = [];
937
903
  let firstUserSkipped = false;
938
904
  for (let i = 0; i < messages.length; i++) {
939
- if (messages[i].role === "user") {
905
+ const m = messages[i];
906
+ if (m.role === "tool") {
907
+ truncatableIndices.push(i);
908
+ } else if (m.role === "user") {
940
909
  if (!firstUserSkipped) {
941
910
  firstUserSkipped = true;
942
911
  continue;
943
912
  }
944
- userIndices.push(i);
913
+ truncatableIndices.push(i);
945
914
  }
946
915
  }
947
- for (const idx of userIndices) {
916
+ for (const idx of truncatableIndices) {
948
917
  if (getTotalChars() <= maxChars) {
949
918
  break;
950
919
  }
951
- if (messages[idx].content !== TRUNCATED_MARKER) {
920
+ const m = messages[idx];
921
+ if (m.role === "tool" && m.content !== TRUNCATED_MARKER) {
922
+ messages[idx] = { role: "tool", tool_call_id: m.tool_call_id, content: TRUNCATED_MARKER };
923
+ } else if (m.role === "user" && m.content !== TRUNCATED_MARKER) {
952
924
  messages[idx] = { role: "user", content: TRUNCATED_MARKER };
953
925
  }
954
926
  }
@@ -961,7 +933,7 @@ var import_openai = __toESM(require("openai"), 1);
961
933
  // package.json
962
934
  var package_default = {
963
935
  name: "@morphllm/morphsdk",
964
- version: "0.2.143",
936
+ version: "0.2.145",
965
937
  description: "TypeScript SDK and CLI for Morph Fast Apply integration",
966
938
  type: "module",
967
939
  main: "./dist/index.cjs",
@@ -1200,9 +1172,115 @@ var package_default = {
1200
1172
  var SDK_VERSION = package_default.version;
1201
1173
 
1202
1174
  // tools/warp_grep/agent/runner.ts
1203
- var import_path2 = __toESM(require("path"), 1);
1204
- var parser = new LLMResponseParser();
1175
+ var import_path3 = __toESM(require("path"), 1);
1205
1176
  var DEFAULT_API_URL = "https://api.morphllm.com";
1177
+ var TOOL_SPECS = [
1178
+ {
1179
+ type: "function",
1180
+ function: {
1181
+ name: "list_directory",
1182
+ description: "Execute ls or find commands to explore directory structure. Max 500 results. Common junk directories are excluded automatically.",
1183
+ parameters: {
1184
+ type: "object",
1185
+ properties: {
1186
+ command: {
1187
+ type: "string",
1188
+ description: "Full ls or find command (e.g. ls -la src/, find . -maxdepth 2 -type f -name '*.py', find . -type d, ls -d */)."
1189
+ }
1190
+ },
1191
+ required: ["command"]
1192
+ }
1193
+ }
1194
+ },
1195
+ {
1196
+ type: "function",
1197
+ function: {
1198
+ name: "grep_search",
1199
+ description: "Search for a regex pattern in file contents. Returns matching lines with file paths and line numbers. Case-insensitive. Respects .gitignore.",
1200
+ parameters: {
1201
+ type: "object",
1202
+ properties: {
1203
+ pattern: {
1204
+ type: "string",
1205
+ description: "Regex pattern to search for in file contents (e.g. 'class\\s+\\w+Error', 'import|require|from', 'def (get|set|update)_user')."
1206
+ },
1207
+ path: {
1208
+ type: "string",
1209
+ description: "File or directory to search in. Defaults to current working directory."
1210
+ },
1211
+ glob: {
1212
+ type: "string",
1213
+ description: "Glob pattern to filter files (e.g. '*.py', '*.{ts,tsx,js,jsx,py,go}', 'src/**/*.go', '!*.test.*')."
1214
+ },
1215
+ limit: {
1216
+ type: "integer",
1217
+ description: "Limit output to first N matching lines. Shows all matches if not specified."
1218
+ }
1219
+ },
1220
+ required: ["pattern"]
1221
+ }
1222
+ }
1223
+ },
1224
+ {
1225
+ type: "function",
1226
+ function: {
1227
+ name: "glob",
1228
+ description: "Find files by name/extension using glob patterns. Returns absolute paths sorted by modification time (newest first). Respects .gitignore. Max 100 results.",
1229
+ parameters: {
1230
+ type: "object",
1231
+ properties: {
1232
+ pattern: {
1233
+ type: "string",
1234
+ description: "Glob pattern to match files (e.g. '*.py', 'src/**/*.js', '*.{ts,tsx}', 'test_*.py')."
1235
+ },
1236
+ path: {
1237
+ type: "string",
1238
+ description: "Directory to search in. Defaults to repository root."
1239
+ }
1240
+ },
1241
+ required: ["pattern"]
1242
+ }
1243
+ }
1244
+ },
1245
+ {
1246
+ type: "function",
1247
+ function: {
1248
+ name: "read",
1249
+ description: "Read entire files or specific line ranges using absolute paths.",
1250
+ parameters: {
1251
+ type: "object",
1252
+ properties: {
1253
+ path: {
1254
+ type: "string",
1255
+ description: "File path to read, using absolute path (e.g. '/home/ubuntu/repo/src/main.py' or windows path)."
1256
+ },
1257
+ lines: {
1258
+ type: "string",
1259
+ description: "Optional line range (e.g. '1-50' or '1-20,45-80'). Omit to read entire file."
1260
+ }
1261
+ },
1262
+ required: ["path"]
1263
+ }
1264
+ }
1265
+ },
1266
+ {
1267
+ type: "function",
1268
+ function: {
1269
+ name: "finish",
1270
+ description: "Submit final answer with all relevant code locations. Include imports and over-include rather than miss context.",
1271
+ parameters: {
1272
+ type: "object",
1273
+ properties: {
1274
+ files: {
1275
+ type: "string",
1276
+ 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."
1277
+ }
1278
+ },
1279
+ required: ["files"]
1280
+ }
1281
+ }
1282
+ }
1283
+ ];
1206
1284
  async function callModel(messages, model, options = {}) {
1207
1285
  const baseUrl = options.morphApiUrl || DEFAULT_API_URL;
1208
1286
  const apiKey = options.morphApiKey || process.env.MORPH_API_KEY || "";
@@ -1223,8 +1301,9 @@ async function callModel(messages, model, options = {}) {
1223
1301
  data = await client.chat.completions.create({
1224
1302
  model,
1225
1303
  temperature: 0,
1226
- max_tokens: 1024,
1304
+ max_tokens: 2048,
1227
1305
  messages,
1306
+ tools: TOOL_SPECS,
1228
1307
  ...options.search_type ? { search_type: options.search_type } : {}
1229
1308
  });
1230
1309
  } catch (error) {
@@ -1236,187 +1315,87 @@ async function callModel(messages, model, options = {}) {
1236
1315
  throw error;
1237
1316
  }
1238
1317
  const choice = data?.choices?.[0];
1239
- const content = choice?.message?.content;
1240
- if (content && typeof content === "string") {
1241
- return content;
1318
+ const message = choice?.message;
1319
+ if (!message) {
1320
+ if (attempt === MAX_EMPTY_RETRIES) {
1321
+ throw new Error("Invalid response from model: no message in response");
1322
+ }
1323
+ await new Promise((resolve) => setTimeout(resolve, 200));
1324
+ continue;
1325
+ }
1326
+ const toolCalls = (message.tool_calls || []).map((tc) => ({
1327
+ id: tc.id,
1328
+ type: "function",
1329
+ function: { name: tc.function.name, arguments: tc.function.arguments }
1330
+ }));
1331
+ if (message.content || toolCalls.length > 0) {
1332
+ return { content: message.content ?? null, tool_calls: toolCalls };
1242
1333
  }
1243
1334
  if (attempt === MAX_EMPTY_RETRIES) {
1244
1335
  const finishReason = choice?.finish_reason ?? "unknown";
1245
- const hasToolCalls = Array.isArray(choice?.message?.tool_calls) && choice.message.tool_calls.length > 0;
1246
- const choicesLen = data?.choices?.length ?? 0;
1247
- const contentType = content === null ? "null" : content === void 0 ? "undefined" : typeof content;
1248
1336
  throw new Error(
1249
- `Invalid response from model: content=${contentType}, finish_reason=${finishReason}, has_tool_calls=${hasToolCalls}, choices_length=${choicesLen}`
1337
+ `Invalid response from model: no content and no tool_calls, finish_reason=${finishReason}`
1250
1338
  );
1251
1339
  }
1252
1340
  await new Promise((resolve) => setTimeout(resolve, 200));
1253
1341
  }
1254
1342
  throw new Error("Invalid response from model");
1255
1343
  }
1256
- async function runWarpGrep(config) {
1257
- const totalStart = Date.now();
1258
- const timeoutMs = config.timeout ?? AGENT_CONFIG.TIMEOUT_MS;
1259
- const timings = { turns: [], timeout_ms: timeoutMs };
1260
- const repoRoot = import_path2.default.resolve(config.repoRoot || process.cwd());
1261
- const model = config.model || DEFAULT_MODEL;
1262
- const messages = [];
1263
- const maxTurns = AGENT_CONFIG.MAX_TURNS;
1264
- const initialStateStart = Date.now();
1265
- const initialState = await buildInitialState(repoRoot, config.searchTerm, config.provider, { search_type: config.search_type });
1266
- timings.initial_state_ms = Date.now() - initialStateStart;
1267
- messages.push({ role: "user", content: initialState });
1268
- const provider = config.provider;
1269
- const errors = [];
1270
- let finishMeta;
1271
- let terminationReason = "terminated";
1272
- for (let turn = 1; turn <= maxTurns; turn += 1) {
1273
- const turnMetrics = { turn, morph_api_ms: 0, local_tools_ms: 0 };
1274
- enforceContextLimit(messages);
1275
- const modelCallStart = Date.now();
1276
- const assistantContent = await callModel(messages, model, {
1277
- morphApiKey: config.morphApiKey,
1278
- morphApiUrl: config.morphApiUrl,
1279
- retryConfig: config.retryConfig,
1280
- timeout: timeoutMs,
1281
- search_type: config.search_type
1282
- }).catch((e) => {
1283
- const errMsg = e instanceof Error ? e.message : String(e);
1284
- console.error(`[warp_grep] Morph API call failed on turn ${turn}:`, errMsg);
1285
- errors.push({ message: errMsg });
1286
- return "";
1287
- });
1288
- turnMetrics.morph_api_ms = Date.now() - modelCallStart;
1289
- if (!assistantContent) {
1290
- console.error(`[warp_grep] Empty response from Morph API on turn ${turn}. Errors so far:`, errors);
1291
- timings.turns.push(turnMetrics);
1292
- break;
1293
- }
1294
- messages.push({ role: "assistant", content: assistantContent });
1295
- const toolCalls = parser.parse(assistantContent);
1296
- if (toolCalls.length === 0) {
1297
- console.error(`[warp_grep] No tool calls parsed on turn ${turn}. Assistant content (first 500 chars):`, assistantContent.slice(0, 500));
1298
- 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" });
1299
- terminationReason = "terminated";
1300
- timings.turns.push(turnMetrics);
1301
- break;
1302
- }
1303
- const finishCalls = toolCalls.filter((c) => c.name === "finish");
1304
- const grepCalls = toolCalls.filter((c) => c.name === "grep");
1305
- const listDirCalls = toolCalls.filter((c) => c.name === "list_directory");
1306
- const readCalls = toolCalls.filter((c) => c.name === "read");
1307
- const skipCalls = toolCalls.filter((c) => c.name === "_skip");
1308
- const formatted = [];
1309
- for (const c of skipCalls) {
1310
- const msg = c.arguments?.message || "Command skipped due to parsing error";
1311
- formatted.push(msg);
1312
- }
1313
- const allPromises = [];
1314
- for (const c of grepCalls) {
1315
- const args = c.arguments ?? {};
1316
- allPromises.push(
1317
- toolGrep(provider, args).then(
1318
- ({ output }) => formatAgentToolOutput("grep", args, output),
1319
- (err) => formatAgentToolOutput("grep", args, String(err), { isError: true })
1320
- )
1321
- );
1322
- }
1323
- for (const c of listDirCalls) {
1324
- const args = c.arguments ?? {};
1325
- allPromises.push(
1326
- toolListDirectory(provider, args).then(
1327
- (p) => formatAgentToolOutput("list_directory", args, p),
1328
- (err) => formatAgentToolOutput("list_directory", args, String(err), { isError: true })
1329
- )
1330
- );
1331
- }
1332
- for (const c of readCalls) {
1333
- const args = c.arguments ?? {};
1334
- allPromises.push(
1335
- toolRead(provider, args).then(
1336
- (p) => formatAgentToolOutput("read", args, p),
1337
- (err) => formatAgentToolOutput("read", args, String(err), { isError: true })
1338
- )
1339
- );
1344
+ function safeParseJSON(s) {
1345
+ try {
1346
+ return JSON.parse(s);
1347
+ } catch {
1348
+ return {};
1349
+ }
1350
+ }
1351
+ async function executeTool(provider, name, args, repoRoot) {
1352
+ switch (name) {
1353
+ case "grep_search": {
1354
+ const grepArgs = {
1355
+ pattern: args.pattern,
1356
+ path: args.path || "."
1357
+ };
1358
+ if (args.glob) grepArgs.glob = args.glob;
1359
+ if (args.case_sensitive !== void 0) grepArgs.case_sensitive = args.case_sensitive;
1360
+ const result = await toolGrep(provider, grepArgs);
1361
+ let output = result.output;
1362
+ if (args.limit && typeof args.limit === "number") {
1363
+ const lines = output.split("\n");
1364
+ if (lines.length > args.limit) {
1365
+ output = lines.slice(0, args.limit).join("\n") + `
1366
+ ... (truncated at ${args.limit} lines)`;
1367
+ }
1368
+ }
1369
+ return output;
1340
1370
  }
1341
- const toolExecStart = Date.now();
1342
- const allResults = await Promise.all(allPromises);
1343
- turnMetrics.local_tools_ms = Date.now() - toolExecStart;
1344
- for (const result of allResults) {
1345
- formatted.push(result);
1371
+ case "glob": {
1372
+ return toolGlob(provider, {
1373
+ pattern: args.pattern,
1374
+ path: args.path
1375
+ });
1346
1376
  }
1347
- if (formatted.length > 0) {
1348
- const turnMessage = formatTurnMessage(turn, maxTurns);
1349
- const contextBudget = calculateContextBudget(messages);
1350
- messages.push({ role: "user", content: formatted.join("\n") + turnMessage + "\n" + contextBudget });
1377
+ case "list_directory": {
1378
+ const dirPath = extractPathFromCommand(args.command || ".");
1379
+ return toolListDirectory(provider, { path: dirPath }, repoRoot);
1351
1380
  }
1352
- timings.turns.push(turnMetrics);
1353
- if (finishCalls.length) {
1354
- const fc = finishCalls[0];
1355
- const files = fc.arguments?.files ?? [];
1356
- const textResult = fc.arguments?.textResult;
1357
- finishMeta = { files };
1358
- terminationReason = "completed";
1359
- if (files.length === 0) {
1360
- const payload2 = textResult || "No relevant code found.";
1361
- timings.turns.push(turnMetrics);
1362
- timings.total_ms = Date.now() - totalStart;
1363
- return {
1364
- terminationReason: "completed",
1365
- messages,
1366
- finish: { payload: payload2, metadata: finishMeta },
1367
- timings
1368
- };
1369
- }
1370
- break;
1371
- }
1372
- }
1373
- if (terminationReason !== "completed" || !finishMeta) {
1374
- timings.total_ms = Date.now() - totalStart;
1375
- return { terminationReason, messages, errors, timings };
1376
- }
1377
- const parts = ["Relevant context found:"];
1378
- for (const f of finishMeta.files) {
1379
- const ranges = f.lines === "*" ? "*" : Array.isArray(f.lines) ? f.lines.map(([s, e]) => `${s}-${e}`).join(", ") : "*";
1380
- parts.push(`- ${f.path}: ${ranges}`);
1381
- }
1382
- const payload = parts.join("\n");
1383
- const finishResolutionStart = Date.now();
1384
- const fileReadErrors = [];
1385
- const resolved = await readFinishFiles(
1386
- repoRoot,
1387
- finishMeta.files,
1388
- async (p, s, e) => {
1389
- try {
1390
- const rr = await provider.read({ path: p, start: s, end: e });
1391
- return rr.lines.map((l) => {
1392
- const idx = l.indexOf("|");
1393
- return idx >= 0 ? l.slice(idx + 1) : l;
1394
- });
1395
- } catch (err) {
1396
- const errorMsg = err instanceof Error ? err.message : String(err);
1397
- fileReadErrors.push({ path: p, error: errorMsg });
1398
- console.error(`[warp_grep] Failed to read file: ${p} - ${errorMsg}`);
1399
- return [`[couldn't find: ${p}]`];
1381
+ case "read": {
1382
+ const readArgs = {
1383
+ path: args.path
1384
+ };
1385
+ if (args.lines && typeof args.lines === "string") {
1386
+ Object.assign(readArgs, parseReadLines(args.lines));
1400
1387
  }
1388
+ return toolRead(provider, readArgs);
1401
1389
  }
1402
- );
1403
- timings.finish_resolution_ms = Date.now() - finishResolutionStart;
1404
- if (fileReadErrors.length > 0) {
1405
- errors.push(...fileReadErrors.map((e) => ({ message: `File read error: ${e.path} - ${e.error}` })));
1390
+ default:
1391
+ return `Unknown tool: ${name}`;
1406
1392
  }
1407
- timings.total_ms = Date.now() - totalStart;
1408
- return {
1409
- terminationReason: "completed",
1410
- messages,
1411
- finish: { payload, metadata: finishMeta, resolved },
1412
- timings
1413
- };
1414
1393
  }
1415
1394
  async function* runWarpGrepStreaming(config) {
1416
1395
  const totalStart = Date.now();
1417
1396
  const timeoutMs = config.timeout ?? AGENT_CONFIG.TIMEOUT_MS;
1418
1397
  const timings = { turns: [], timeout_ms: timeoutMs };
1419
- const repoRoot = import_path2.default.resolve(config.repoRoot || process.cwd());
1398
+ const repoRoot = import_path3.default.resolve(config.repoRoot || process.cwd());
1420
1399
  const model = config.model || DEFAULT_MODEL;
1421
1400
  const messages = [];
1422
1401
  const maxTurns = AGENT_CONFIG.MAX_TURNS;
@@ -1432,7 +1411,7 @@ async function* runWarpGrepStreaming(config) {
1432
1411
  const turnMetrics = { turn, morph_api_ms: 0, local_tools_ms: 0 };
1433
1412
  enforceContextLimit(messages);
1434
1413
  const modelCallStart = Date.now();
1435
- const assistantContent = await callModel(messages, model, {
1414
+ const response = await callModel(messages, model, {
1436
1415
  morphApiKey: config.morphApiKey,
1437
1416
  morphApiUrl: config.morphApiUrl,
1438
1417
  retryConfig: config.retryConfig,
@@ -1440,90 +1419,45 @@ async function* runWarpGrepStreaming(config) {
1440
1419
  search_type: config.search_type
1441
1420
  }).catch((e) => {
1442
1421
  const errMsg = e instanceof Error ? e.message : String(e);
1443
- console.error(`[warp_grep:stream] Morph API call failed on turn ${turn}:`, errMsg);
1422
+ console.error(`[warp_grep] Morph API call failed on turn ${turn}:`, errMsg);
1444
1423
  errors.push({ message: errMsg });
1445
- return "";
1424
+ return null;
1446
1425
  });
1447
1426
  turnMetrics.morph_api_ms = Date.now() - modelCallStart;
1448
- if (!assistantContent) {
1449
- console.error(`[warp_grep:stream] Empty response from Morph API on turn ${turn}. Errors so far:`, errors);
1427
+ if (!response) {
1450
1428
  timings.turns.push(turnMetrics);
1451
1429
  break;
1452
1430
  }
1453
- messages.push({ role: "assistant", content: assistantContent });
1454
- const toolCalls = parser.parse(assistantContent);
1431
+ const toolCalls = response.tool_calls;
1432
+ messages.push({
1433
+ role: "assistant",
1434
+ content: response.content,
1435
+ ...toolCalls.length > 0 ? { tool_calls: toolCalls } : {}
1436
+ });
1455
1437
  if (toolCalls.length === 0) {
1456
- console.error(`[warp_grep:stream] No tool calls parsed on turn ${turn}. Assistant content (first 500 chars):`, assistantContent.slice(0, 500));
1457
- 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" });
1438
+ console.error(`[warp_grep] No tool calls on turn ${turn}. Content: ${(response.content || "").slice(0, 500)}`);
1439
+ errors.push({ message: "No tool calls produced by the model." });
1458
1440
  terminationReason = "terminated";
1459
1441
  timings.turns.push(turnMetrics);
1460
1442
  break;
1461
1443
  }
1462
1444
  yield {
1463
1445
  turn,
1464
- toolCalls: toolCalls.map((c) => ({
1465
- name: c.name,
1466
- arguments: c.arguments ?? {}
1446
+ toolCalls: toolCalls.map((tc) => ({
1447
+ name: tc.function.name,
1448
+ arguments: safeParseJSON(tc.function.arguments)
1467
1449
  }))
1468
1450
  };
1469
- const finishCalls = toolCalls.filter((c) => c.name === "finish");
1470
- const grepCalls = toolCalls.filter((c) => c.name === "grep");
1471
- const listDirCalls = toolCalls.filter((c) => c.name === "list_directory");
1472
- const readCalls = toolCalls.filter((c) => c.name === "read");
1473
- const skipCalls = toolCalls.filter((c) => c.name === "_skip");
1474
- const formatted = [];
1475
- for (const c of skipCalls) {
1476
- const msg = c.arguments?.message || "Command skipped due to parsing error";
1477
- formatted.push(msg);
1478
- }
1479
- const allPromises = [];
1480
- for (const c of grepCalls) {
1481
- const args = c.arguments ?? {};
1482
- allPromises.push(
1483
- toolGrep(provider, args).then(
1484
- ({ output }) => formatAgentToolOutput("grep", args, output),
1485
- (err) => formatAgentToolOutput("grep", args, String(err), { isError: true })
1486
- )
1487
- );
1488
- }
1489
- for (const c of listDirCalls) {
1490
- const args = c.arguments ?? {};
1491
- allPromises.push(
1492
- toolListDirectory(provider, args).then(
1493
- (p) => formatAgentToolOutput("list_directory", args, p),
1494
- (err) => formatAgentToolOutput("list_directory", args, String(err), { isError: true })
1495
- )
1496
- );
1497
- }
1498
- for (const c of readCalls) {
1499
- const args = c.arguments ?? {};
1500
- allPromises.push(
1501
- toolRead(provider, args).then(
1502
- (p) => formatAgentToolOutput("read", args, p),
1503
- (err) => formatAgentToolOutput("read", args, String(err), { isError: true })
1504
- )
1505
- );
1506
- }
1507
- const toolExecStart = Date.now();
1508
- const allResults = await Promise.all(allPromises);
1509
- turnMetrics.local_tools_ms = Date.now() - toolExecStart;
1510
- for (const result of allResults) {
1511
- formatted.push(result);
1512
- }
1513
- if (formatted.length > 0) {
1514
- const turnMessage = formatTurnMessage(turn, maxTurns);
1515
- const contextBudget = calculateContextBudget(messages);
1516
- messages.push({ role: "user", content: formatted.join("\n") + turnMessage + "\n" + contextBudget });
1517
- }
1518
- timings.turns.push(turnMetrics);
1519
- if (finishCalls.length) {
1520
- const fc = finishCalls[0];
1521
- const files = fc.arguments?.files ?? [];
1522
- const textResult = fc.arguments?.textResult;
1451
+ const finishCall = toolCalls.find((tc) => tc.function.name === "finish");
1452
+ if (finishCall) {
1453
+ const args = safeParseJSON(finishCall.function.arguments);
1454
+ const filesStr = args.files || "";
1455
+ const files = parseFinishFiles(filesStr);
1523
1456
  finishMeta = { files };
1524
1457
  terminationReason = "completed";
1525
1458
  if (files.length === 0) {
1526
- const payload2 = textResult || "No relevant code found.";
1459
+ const payload2 = filesStr || "No relevant code found.";
1460
+ timings.turns.push(turnMetrics);
1527
1461
  timings.total_ms = Date.now() - totalStart;
1528
1462
  return {
1529
1463
  terminationReason: "completed",
@@ -1532,8 +1466,25 @@ async function* runWarpGrepStreaming(config) {
1532
1466
  timings
1533
1467
  };
1534
1468
  }
1469
+ timings.turns.push(turnMetrics);
1535
1470
  break;
1536
1471
  }
1472
+ const toolExecStart = Date.now();
1473
+ const results = await Promise.all(
1474
+ toolCalls.map(async (tc) => {
1475
+ const args = safeParseJSON(tc.function.arguments);
1476
+ const output = await executeTool(provider, tc.function.name, args, repoRoot).catch((err) => String(err));
1477
+ return { tool_call_id: tc.id, content: output };
1478
+ })
1479
+ );
1480
+ turnMetrics.local_tools_ms = Date.now() - toolExecStart;
1481
+ for (const result of results) {
1482
+ messages.push({ role: "tool", tool_call_id: result.tool_call_id, content: result.content });
1483
+ }
1484
+ const turnMsg = formatTurnMessage(turn, maxTurns);
1485
+ const budget = calculateContextBudget(messages);
1486
+ messages.push({ role: "user", content: turnMsg + "\n" + budget });
1487
+ timings.turns.push(turnMetrics);
1537
1488
  }
1538
1489
  if (terminationReason !== "completed" || !finishMeta) {
1539
1490
  timings.total_ms = Date.now() - totalStart;
@@ -1577,6 +1528,14 @@ async function* runWarpGrepStreaming(config) {
1577
1528
  timings
1578
1529
  };
1579
1530
  }
1531
+ async function runWarpGrep(config) {
1532
+ const gen = runWarpGrepStreaming(config);
1533
+ let result = await gen.next();
1534
+ while (!result.done) {
1535
+ result = await gen.next();
1536
+ }
1537
+ return result.value;
1538
+ }
1580
1539
 
1581
1540
  // tools/warp_grep/providers/remote.ts
1582
1541
  init_config();
@@ -1756,6 +1715,35 @@ var RemoteCommandsProvider = class {
1756
1715
  return [];
1757
1716
  }
1758
1717
  }
1718
+ /**
1719
+ * Glob search - finds files matching a pattern.
1720
+ * Falls back to a grep --files approach via the listDir command.
1721
+ */
1722
+ async glob(params) {
1723
+ const searchPath = params.path || this.repoRoot;
1724
+ try {
1725
+ const stdout = await this.commands.listDir(searchPath, 10);
1726
+ const allPaths = (stdout || "").trim().split(/\r?\n/).filter((p) => p.length > 0);
1727
+ const globToRegex = (glob) => {
1728
+ const escaped = glob.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".");
1729
+ return new RegExp(escaped);
1730
+ };
1731
+ const regex = globToRegex(params.pattern);
1732
+ const matched = allPaths.filter((p) => {
1733
+ const name = p.split("/").pop() || "";
1734
+ return regex.test(name) && !shouldSkip(name);
1735
+ });
1736
+ const totalFound = matched.length;
1737
+ return { files: matched.slice(0, 100), searchDir: searchPath, totalFound };
1738
+ } catch (error) {
1739
+ return {
1740
+ files: [],
1741
+ searchDir: searchPath,
1742
+ totalFound: 0,
1743
+ error: `[GLOB ERROR] ${error instanceof Error ? error.message : String(error)}`
1744
+ };
1745
+ }
1746
+ }
1759
1747
  };
1760
1748
 
1761
1749
  // tools/warp_grep/providers/code_storage_http.ts
@@ -1782,9 +1770,9 @@ function createCodeStorageHttpCommands(config) {
1782
1770
  const { baseUrl, repoId, branch } = config;
1783
1771
  const encodedRepoId = encodeURIComponent(repoId);
1784
1772
  return {
1785
- grep: (pattern, path5, glob) => post(`${baseUrl}/api/code-search/${encodedRepoId}/grep`, { pattern, path: path5, glob, branch }, "grep"),
1786
- read: (path5, start, end) => post(`${baseUrl}/api/code-search/${encodedRepoId}/read`, { path: path5, start, end, branch }, "read"),
1787
- listDir: (path5, maxDepth) => post(`${baseUrl}/api/code-search/${encodedRepoId}/list`, { path: path5, maxDepth, branch }, "list")
1773
+ grep: (pattern, path6, glob) => post(`${baseUrl}/api/code-search/${encodedRepoId}/grep`, { pattern, path: path6, glob, branch }, "grep"),
1774
+ read: (path6, start, end) => post(`${baseUrl}/api/code-search/${encodedRepoId}/read`, { path: path6, start, end, branch }, "read"),
1775
+ listDir: (path6, maxDepth) => post(`${baseUrl}/api/code-search/${encodedRepoId}/list`, { path: path6, maxDepth, branch }, "list")
1788
1776
  };
1789
1777
  }
1790
1778