@morphllm/morphsdk 0.2.145 → 0.2.146

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-HBIW2XV2.js → chunk-4PBUB77N.js} +2 -2
  2. package/dist/{chunk-SUE4GYA2.js → chunk-BDHKL3MT.js} +2 -2
  3. package/dist/{chunk-S54SPKX3.js → chunk-BIQ7234U.js} +2 -2
  4. package/dist/{chunk-I3J46TSB.js → chunk-DKODF3YG.js} +4 -5
  5. package/dist/chunk-DKODF3YG.js.map +1 -0
  6. package/dist/{chunk-MRPASJBX.js → chunk-E45FW5EK.js} +2 -2
  7. package/dist/{chunk-BXRJYLRS.js → chunk-E4YKEKGW.js} +2 -2
  8. package/dist/{chunk-G23BI5CQ.js → chunk-EU7OLX4Z.js} +2 -2
  9. package/dist/chunk-EUHNJMWL.js +409 -0
  10. package/dist/chunk-EUHNJMWL.js.map +1 -0
  11. package/dist/{chunk-HE7K2QNQ.js → chunk-FBOJJ3UY.js} +17 -17
  12. package/dist/{chunk-HYRHI2UL.js → chunk-FIVYDIHX.js} +1 -1
  13. package/dist/{chunk-GXM3G7Z4.js → chunk-FYO46OT6.js} +2 -2
  14. package/dist/{chunk-GHPQYSSF.js → chunk-GJUB3ECP.js} +2 -2
  15. package/dist/{chunk-4Y2NM6JD.js → chunk-HZOTLGJH.js} +2 -42
  16. package/dist/chunk-HZOTLGJH.js.map +1 -0
  17. package/dist/{chunk-MTJ3PR4M.js → chunk-I7SFRYTX.js} +2 -2
  18. package/dist/{chunk-PX7ODEML.js → chunk-J2HIK4GB.js} +2 -2
  19. package/dist/{chunk-RZXS4ADX.js → chunk-JSWNBCGS.js} +2 -2
  20. package/dist/{chunk-GXCWKYGU.js → chunk-KYKRRF7E.js} +2 -2
  21. package/dist/{chunk-N7TTZIBK.js → chunk-MMBQKN4G.js} +2 -2
  22. package/dist/{chunk-B3AKP3RA.js → chunk-NF2QWJDY.js} +2 -31
  23. package/dist/chunk-NF2QWJDY.js.map +1 -0
  24. package/dist/{chunk-JMUAQQJU.js → chunk-NKUSUSVI.js} +3 -3
  25. package/dist/{chunk-VRV5UYTN.js → chunk-OV57JBMB.js} +2 -2
  26. package/dist/{chunk-EPIOAODF.js → chunk-Q36MNOFA.js} +2 -2
  27. package/dist/{chunk-JRBU4UNP.js → chunk-QRSWXP4K.js} +2 -2
  28. package/dist/{chunk-KELRCMA6.js → chunk-SJYAKVSS.js} +2 -2
  29. package/dist/{chunk-KELRCMA6.js.map → chunk-SJYAKVSS.js.map} +1 -1
  30. package/dist/{chunk-IRWHN55G.js → chunk-T564HFSH.js} +1 -1
  31. package/dist/{chunk-6CFKWZK3.js → chunk-UVNENJ6H.js} +3 -3
  32. package/dist/{chunk-5FCXLQJU.js → chunk-UYPWKQKV.js} +2 -2
  33. package/dist/{chunk-BAF33L6C.js → chunk-V73GO5AJ.js} +2 -2
  34. package/dist/chunk-VCKJ22DX.js +131 -0
  35. package/dist/chunk-VCKJ22DX.js.map +1 -0
  36. package/dist/{chunk-XL7R3XN5.js → chunk-VZ6VYRQB.js} +2 -2
  37. package/dist/{chunk-4LWMPKSB.js → chunk-YIETFYCL.js} +44 -71
  38. package/dist/chunk-YIETFYCL.js.map +1 -0
  39. package/dist/client.cjs +438 -426
  40. package/dist/client.cjs.map +1 -1
  41. package/dist/client.js +27 -26
  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-Ddj1MPGt.d.ts → finish-DBKuo8yj.d.ts} +1 -1
  46. package/dist/index.cjs +438 -445
  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 +434 -422
  56. package/dist/subagents/anthropic.cjs.map +1 -1
  57. package/dist/subagents/anthropic.js +9 -8
  58. package/dist/subagents/vercel.cjs +434 -422
  59. package/dist/subagents/vercel.cjs.map +1 -1
  60. package/dist/subagents/vercel.js +9 -8
  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 +3 -4
  122. package/dist/tools/warp_grep/agent/config.cjs.map +1 -1
  123. package/dist/tools/warp_grep/agent/config.d.ts +1 -2
  124. package/dist/tools/warp_grep/agent/config.js +1 -1
  125. package/dist/tools/warp_grep/agent/parser.cjs +121 -52
  126. package/dist/tools/warp_grep/agent/parser.cjs.map +1 -1
  127. package/dist/tools/warp_grep/agent/parser.d.ts +5 -12
  128. package/dist/tools/warp_grep/agent/parser.js +3 -7
  129. package/dist/tools/warp_grep/agent/runner.cjs +416 -335
  130. package/dist/tools/warp_grep/agent/runner.cjs.map +1 -1
  131. package/dist/tools/warp_grep/agent/runner.d.ts +3 -6
  132. package/dist/tools/warp_grep/agent/runner.js +6 -5
  133. package/dist/tools/warp_grep/agent/types.cjs.map +1 -1
  134. package/dist/tools/warp_grep/agent/types.d.ts +3 -22
  135. package/dist/tools/warp_grep/anthropic.cjs +434 -422
  136. package/dist/tools/warp_grep/anthropic.cjs.map +1 -1
  137. package/dist/tools/warp_grep/anthropic.js +9 -8
  138. package/dist/tools/warp_grep/client.cjs +434 -422
  139. package/dist/tools/warp_grep/client.cjs.map +1 -1
  140. package/dist/tools/warp_grep/client.js +8 -7
  141. package/dist/tools/warp_grep/gemini.cjs +434 -422
  142. package/dist/tools/warp_grep/gemini.cjs.map +1 -1
  143. package/dist/tools/warp_grep/gemini.js +8 -7
  144. package/dist/tools/warp_grep/gemini.js.map +1 -1
  145. package/dist/tools/warp_grep/harness.cjs +176 -164
  146. package/dist/tools/warp_grep/harness.cjs.map +1 -1
  147. package/dist/tools/warp_grep/harness.d.ts +38 -17
  148. package/dist/tools/warp_grep/harness.js +14 -15
  149. package/dist/tools/warp_grep/harness.js.map +1 -1
  150. package/dist/tools/warp_grep/index.cjs +434 -441
  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 +434 -422
  155. package/dist/tools/warp_grep/openai.cjs.map +1 -1
  156. package/dist/tools/warp_grep/openai.js +9 -8
  157. package/dist/tools/warp_grep/providers/local.cjs +2 -43
  158. package/dist/tools/warp_grep/providers/local.cjs.map +1 -1
  159. package/dist/tools/warp_grep/providers/local.d.ts +1 -5
  160. package/dist/tools/warp_grep/providers/local.js +2 -2
  161. package/dist/tools/warp_grep/providers/remote.cjs +2 -32
  162. package/dist/tools/warp_grep/providers/remote.cjs.map +1 -1
  163. package/dist/tools/warp_grep/providers/remote.d.ts +1 -9
  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 +1 -14
  167. package/dist/tools/warp_grep/vercel.cjs +434 -422
  168. package/dist/tools/warp_grep/vercel.cjs.map +1 -1
  169. package/dist/tools/warp_grep/vercel.js +9 -8
  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-4LWMPKSB.js.map +0 -1
  175. package/dist/chunk-4Y2NM6JD.js.map +0 -1
  176. package/dist/chunk-B3AKP3RA.js.map +0 -1
  177. package/dist/chunk-CMSHXALI.js +0 -60
  178. package/dist/chunk-CMSHXALI.js.map +0 -1
  179. package/dist/chunk-I3J46TSB.js.map +0 -1
  180. package/dist/chunk-OPEQQGST.js +0 -396
  181. package/dist/chunk-OPEQQGST.js.map +0 -1
  182. /package/dist/{chunk-HBIW2XV2.js.map → chunk-4PBUB77N.js.map} +0 -0
  183. /package/dist/{chunk-SUE4GYA2.js.map → chunk-BDHKL3MT.js.map} +0 -0
  184. /package/dist/{chunk-S54SPKX3.js.map → chunk-BIQ7234U.js.map} +0 -0
  185. /package/dist/{chunk-MRPASJBX.js.map → chunk-E45FW5EK.js.map} +0 -0
  186. /package/dist/{chunk-BXRJYLRS.js.map → chunk-E4YKEKGW.js.map} +0 -0
  187. /package/dist/{chunk-G23BI5CQ.js.map → chunk-EU7OLX4Z.js.map} +0 -0
  188. /package/dist/{chunk-HE7K2QNQ.js.map → chunk-FBOJJ3UY.js.map} +0 -0
  189. /package/dist/{chunk-HYRHI2UL.js.map → chunk-FIVYDIHX.js.map} +0 -0
  190. /package/dist/{chunk-GXM3G7Z4.js.map → chunk-FYO46OT6.js.map} +0 -0
  191. /package/dist/{chunk-GHPQYSSF.js.map → chunk-GJUB3ECP.js.map} +0 -0
  192. /package/dist/{chunk-MTJ3PR4M.js.map → chunk-I7SFRYTX.js.map} +0 -0
  193. /package/dist/{chunk-PX7ODEML.js.map → chunk-J2HIK4GB.js.map} +0 -0
  194. /package/dist/{chunk-RZXS4ADX.js.map → chunk-JSWNBCGS.js.map} +0 -0
  195. /package/dist/{chunk-GXCWKYGU.js.map → chunk-KYKRRF7E.js.map} +0 -0
  196. /package/dist/{chunk-N7TTZIBK.js.map → chunk-MMBQKN4G.js.map} +0 -0
  197. /package/dist/{chunk-JMUAQQJU.js.map → chunk-NKUSUSVI.js.map} +0 -0
  198. /package/dist/{chunk-VRV5UYTN.js.map → chunk-OV57JBMB.js.map} +0 -0
  199. /package/dist/{chunk-EPIOAODF.js.map → chunk-Q36MNOFA.js.map} +0 -0
  200. /package/dist/{chunk-JRBU4UNP.js.map → chunk-QRSWXP4K.js.map} +0 -0
  201. /package/dist/{chunk-IRWHN55G.js.map → chunk-T564HFSH.js.map} +0 -0
  202. /package/dist/{chunk-6CFKWZK3.js.map → chunk-UVNENJ6H.js.map} +0 -0
  203. /package/dist/{chunk-5FCXLQJU.js.map → chunk-UYPWKQKV.js.map} +0 -0
  204. /package/dist/{chunk-BAF33L6C.js.map → chunk-V73GO5AJ.js.map} +0 -0
  205. /package/dist/{chunk-XL7R3XN5.js.map → chunk-VZ6VYRQB.js.map} +0 -0
@@ -41,12 +41,11 @@ var init_config = __esm({
41
41
  return isNaN(parsed) || parsed <= 0 ? defaultMs : parsed;
42
42
  };
43
43
  AGENT_CONFIG = {
44
- MAX_TURNS: 6,
44
+ MAX_TURNS: 4,
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: 321600,
47
+ MAX_CONTEXT_CHARS: 54e4,
48
48
  MAX_OUTPUT_LINES: 200,
49
- MAX_LIST_RESULTS: 500,
50
49
  MAX_READ_LINES: 800,
51
50
  MAX_LIST_DEPTH: 3,
52
51
  LIST_TIMEOUT_MS: 2e3
@@ -131,7 +130,7 @@ var init_config = __esm({
131
130
  ".*"
132
131
  ];
133
132
  DEFAULT_EXCLUDES = (process.env.MORPH_WARP_GREP_EXCLUDE || "").split(",").map((s) => s.trim()).filter(Boolean).concat(BUILTIN_EXCLUDES);
134
- DEFAULT_MODEL = "morph-warp-grep-v2.1";
133
+ DEFAULT_MODEL = "morph-warp-grep-v2";
135
134
  }
136
135
  });
137
136
 
@@ -204,19 +203,19 @@ var init_ripgrep = __esm({
204
203
 
205
204
  // tools/warp_grep/utils/paths.ts
206
205
  function resolveUnderRepo(repoRoot, targetPath) {
207
- const absRoot = import_path4.default.resolve(repoRoot);
208
- const resolved = import_path4.default.resolve(absRoot, targetPath);
206
+ const absRoot = import_path3.default.resolve(repoRoot);
207
+ const resolved = import_path3.default.resolve(absRoot, targetPath);
209
208
  ensureWithinRepo(absRoot, resolved);
210
209
  return resolved;
211
210
  }
212
211
  function ensureWithinRepo(repoRoot, absTarget) {
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)) {
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)) {
215
214
  throw new Error(`Path outside repository root: ${absTarget}`);
216
215
  }
217
216
  }
218
217
  function toRepoRelative(repoRoot, absPath) {
219
- return import_path4.default.relative(import_path4.default.resolve(repoRoot), import_path4.default.resolve(absPath));
218
+ return import_path3.default.relative(import_path3.default.resolve(repoRoot), import_path3.default.resolve(absPath));
220
219
  }
221
220
  function isSymlink(p) {
222
221
  try {
@@ -227,7 +226,7 @@ function isSymlink(p) {
227
226
  }
228
227
  }
229
228
  function fixPathRepetition(fullPath) {
230
- const segments = fullPath.split(import_path4.default.sep).filter(Boolean);
229
+ const segments = fullPath.split(import_path3.default.sep).filter(Boolean);
231
230
  if (segments.length < 2) return null;
232
231
  for (let len = Math.floor(segments.length / 2); len >= 1; len--) {
233
232
  for (let i = 0; i <= segments.length - 2 * len; i++) {
@@ -235,7 +234,7 @@ function fixPathRepetition(fullPath) {
235
234
  const second = segments.slice(i + len, i + 2 * len);
236
235
  if (first.every((seg, idx) => seg === second[idx])) {
237
236
  const fixed = [...segments.slice(0, i), ...segments.slice(i + len)];
238
- return import_path4.default.sep + fixed.join(import_path4.default.sep);
237
+ return import_path3.default.sep + fixed.join(import_path3.default.sep);
239
238
  }
240
239
  }
241
240
  }
@@ -259,12 +258,12 @@ function isTextualFile(filePath, maxBytes = 2e6) {
259
258
  return false;
260
259
  }
261
260
  }
262
- var import_fs, import_path4;
261
+ var import_fs, import_path3;
263
262
  var init_paths = __esm({
264
263
  "tools/warp_grep/utils/paths.ts"() {
265
264
  "use strict";
266
265
  import_fs = __toESM(require("fs"), 1);
267
- import_path4 = __toESM(require("path"), 1);
266
+ import_path3 = __toESM(require("path"), 1);
268
267
  }
269
268
  });
270
269
 
@@ -295,12 +294,12 @@ function shouldSkip2(name, allowNames) {
295
294
  }
296
295
  return false;
297
296
  }
298
- var import_promises2, import_path5, SKIP_NAMES2, SKIP_EXTENSIONS2, LocalRipgrepProvider;
297
+ var import_promises2, import_path4, SKIP_NAMES2, SKIP_EXTENSIONS2, LocalRipgrepProvider;
299
298
  var init_local = __esm({
300
299
  "tools/warp_grep/providers/local.ts"() {
301
300
  "use strict";
302
301
  import_promises2 = __toESM(require("fs/promises"), 1);
303
- import_path5 = __toESM(require("path"), 1);
302
+ import_path4 = __toESM(require("path"), 1);
304
303
  init_ripgrep();
305
304
  init_paths();
306
305
  init_files();
@@ -402,7 +401,7 @@ var init_local = __esm({
402
401
  }
403
402
  const stat = await import_promises2.default.stat(abs).catch(() => null);
404
403
  if (!stat) return { lines: [] };
405
- const targetArg = abs === import_path5.default.resolve(this.repoRoot) ? "." : toRepoRelative(this.repoRoot, abs);
404
+ const targetArg = abs === import_path4.default.resolve(this.repoRoot) ? "." : toRepoRelative(this.repoRoot, abs);
406
405
  const contextLines = params.context_lines !== void 0 ? String(params.context_lines) : "1";
407
406
  const args = [
408
407
  "--no-config",
@@ -557,7 +556,7 @@ Details: ${res.stderr}` : ""}`
557
556
  if (timedOut || results.length >= maxResults) break;
558
557
  if (shouldSkip2(entry.name, allowNames)) continue;
559
558
  if (regex && !regex.test(entry.name)) continue;
560
- const full = import_path5.default.join(dir, entry.name);
559
+ const full = import_path4.default.join(dir, entry.name);
561
560
  const isDir = entry.isDirectory();
562
561
  results.push({
563
562
  name: entry.name,
@@ -573,46 +572,6 @@ Details: ${res.stderr}` : ""}`
573
572
  await walk(abs, 0);
574
573
  return results;
575
574
  }
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
- }
616
575
  };
617
576
  }
618
577
  });
@@ -635,58 +594,131 @@ module.exports = __toCommonJS(gemini_exports);
635
594
  init_config();
636
595
 
637
596
  // tools/warp_grep/agent/parser.ts
638
- function parseReadLines(linesStr) {
639
- const ranges = [];
640
- for (const rangeStr of linesStr.split(",")) {
641
- const trimmed = rangeStr.trim();
642
- if (!trimmed) continue;
643
- const parts = trimmed.split("-").map((v) => parseInt(v.trim(), 10));
644
- if (parts.length >= 2 && Number.isFinite(parts[0]) && Number.isFinite(parts[1])) {
645
- ranges.push([parts[0], parts[1]]);
646
- } else if (Number.isFinite(parts[0])) {
647
- ranges.push([parts[0], parts[0]]);
648
- }
649
- }
650
- if (ranges.length === 1) return { start: ranges[0][0], end: ranges[0][1] };
651
- if (ranges.length > 1) return { lines: ranges };
652
- return {};
597
+ var VALID_COMMANDS = ["list_directory", "ripgrep", "read", "finish"];
598
+ function isValidCommand(name) {
599
+ return VALID_COMMANDS.includes(name);
653
600
  }
654
- function parseFinishFiles(filesStr) {
655
- const files = [];
656
- for (const line of filesStr.trim().split(/\s+/)) {
657
- const trimmed = line.trim();
658
- if (!trimmed) continue;
659
- const colonIdx = trimmed.indexOf(":");
660
- if (colonIdx === -1) {
661
- files.push({ path: trimmed, lines: "*" });
662
- continue;
601
+ function parseQwen3ToolCalls(text) {
602
+ const tools = [];
603
+ const toolCallRegex = /<tool_call>\s*<function=([a-z_][a-z0-9_]*)>([\s\S]*?)<\/function>\s*<\/tool_call>/gi;
604
+ let match;
605
+ while ((match = toolCallRegex.exec(text)) !== null) {
606
+ const funcName = match[1].toLowerCase();
607
+ const body = match[2];
608
+ if (!isValidCommand(funcName)) continue;
609
+ const params = {};
610
+ const paramRegex = /<parameter=([a-z_][a-z0-9_]*)>([\s\S]*?)<\/parameter>/gi;
611
+ let paramMatch;
612
+ while ((paramMatch = paramRegex.exec(body)) !== null) {
613
+ params[paramMatch[1].toLowerCase()] = paramMatch[2].trim();
663
614
  }
664
- const filePath = trimmed.slice(0, colonIdx);
665
- const rangesPart = trimmed.slice(colonIdx + 1);
666
- if (!rangesPart.trim() || rangesPart.trim() === "*") {
667
- files.push({ path: filePath, lines: "*" });
668
- continue;
669
- }
670
- const ranges = [];
671
- for (const rangeStr of rangesPart.split(",")) {
672
- const rt = rangeStr.trim();
673
- if (!rt) continue;
674
- const parts = rt.split("-").map((v) => parseInt(v.trim(), 10));
675
- if (parts.length >= 2 && Number.isFinite(parts[0]) && Number.isFinite(parts[1])) {
676
- ranges.push([parts[0], parts[1]]);
677
- } else if (Number.isFinite(parts[0])) {
678
- ranges.push([parts[0], parts[0]]);
615
+ if (funcName === "ripgrep") {
616
+ const pattern = params.pattern;
617
+ if (!pattern) continue;
618
+ const args = {
619
+ pattern,
620
+ path: params.path || ".",
621
+ ...params.glob && { glob: params.glob },
622
+ ...params.context_lines && { context_lines: parseInt(params.context_lines, 10) },
623
+ ...params.case_sensitive && { case_sensitive: params.case_sensitive === "true" }
624
+ };
625
+ tools.push({ name: "grep", arguments: args });
626
+ } else if (funcName === "list_directory") {
627
+ const command = params.command;
628
+ const directPath = params.path;
629
+ let dirPath = directPath || ".";
630
+ if (!directPath && command) {
631
+ const tokens = command.trim().split(/\s+/);
632
+ const pathTokens = tokens.slice(1).filter((t) => !t.startsWith("-") && !t.startsWith("|") && !t.startsWith("\\("));
633
+ if (pathTokens.length > 0) {
634
+ dirPath = pathTokens[0];
635
+ }
636
+ }
637
+ tools.push({ name: "list_directory", arguments: { path: dirPath, pattern: params.pattern || null } });
638
+ } else if (funcName === "read") {
639
+ const filePath = params.path;
640
+ if (!filePath) continue;
641
+ const args = { path: filePath };
642
+ const linesStr = params.lines;
643
+ if (linesStr) {
644
+ const ranges = [];
645
+ for (const rangeStr of linesStr.split(",")) {
646
+ const trimmed = rangeStr.trim();
647
+ if (!trimmed) continue;
648
+ const [s, e] = trimmed.split("-").map((v) => parseInt(v.trim(), 10));
649
+ if (Number.isFinite(s) && Number.isFinite(e)) {
650
+ ranges.push([s, e]);
651
+ } else if (Number.isFinite(s)) {
652
+ ranges.push([s, s]);
653
+ }
654
+ }
655
+ if (ranges.length === 1) {
656
+ args.start = ranges[0][0];
657
+ args.end = ranges[0][1];
658
+ } else if (ranges.length > 1) {
659
+ args.lines = ranges;
660
+ }
661
+ }
662
+ tools.push({ name: "read", arguments: args });
663
+ } else if (funcName === "finish") {
664
+ if (params.result && !params.files) {
665
+ tools.push({ name: "finish", arguments: { files: [], textResult: params.result } });
666
+ continue;
667
+ }
668
+ const filesStr = params.files;
669
+ if (!filesStr) {
670
+ tools.push({ name: "finish", arguments: { files: [], textResult: "No relevant code found." } });
671
+ continue;
672
+ }
673
+ const files = [];
674
+ for (const line of filesStr.split("\n")) {
675
+ const trimmed = line.trim();
676
+ if (!trimmed) continue;
677
+ const colonIdx = trimmed.indexOf(":");
678
+ if (colonIdx === -1) {
679
+ files.push({ path: trimmed, lines: "*" });
680
+ } else {
681
+ const filePath = trimmed.slice(0, colonIdx);
682
+ const rangesPart = trimmed.slice(colonIdx + 1);
683
+ const ranges = [];
684
+ for (const rangeStr of rangesPart.split(",")) {
685
+ const rt = rangeStr.trim();
686
+ if (!rt || rt === "*") {
687
+ files.push({ path: filePath, lines: "*" });
688
+ break;
689
+ }
690
+ const [s, e] = rt.split("-").map((v) => parseInt(v.trim(), 10));
691
+ if (Number.isFinite(s) && Number.isFinite(e)) {
692
+ ranges.push([s, e]);
693
+ } else if (Number.isFinite(s)) {
694
+ ranges.push([s, s]);
695
+ }
696
+ }
697
+ if (ranges.length > 0) {
698
+ files.push({ path: filePath, lines: ranges });
699
+ } else if (!files.some((f) => f.path === filePath)) {
700
+ files.push({ path: filePath, lines: "*" });
701
+ }
702
+ }
703
+ }
704
+ if (files.length > 0) {
705
+ tools.push({ name: "finish", arguments: { files } });
706
+ } else {
707
+ tools.push({ name: "finish", arguments: { files: [], textResult: filesStr } });
679
708
  }
680
709
  }
681
- files.push({ path: filePath, lines: ranges.length > 0 ? ranges : "*" });
682
710
  }
683
- return files;
684
- }
685
- function extractPathFromCommand(command) {
686
- const tokens = command.trim().split(/\s+/);
687
- const pathTokens = tokens.slice(1).filter((t) => !t.startsWith("-") && !t.startsWith("|") && !t.startsWith("\\("));
688
- return pathTokens[0] || ".";
711
+ return tools;
689
712
  }
713
+ var LLMResponseParser = class {
714
+ parse(text) {
715
+ if (typeof text !== "string") {
716
+ throw new TypeError("Command text must be a string.");
717
+ }
718
+ const withoutThink = text.replace(/<think>[\s\S]*?<\/think>/gi, "");
719
+ return parseQwen3ToolCalls(withoutThink);
720
+ }
721
+ };
690
722
 
691
723
  // tools/warp_grep/agent/tools/grep.ts
692
724
  async function toolGrep(provider, args) {
@@ -737,42 +769,29 @@ async function toolRead(provider, args) {
737
769
 
738
770
  // tools/warp_grep/agent/tools/list_directory.ts
739
771
  init_config();
740
- var import_path = __toESM(require("path"), 1);
741
- async function toolListDirectory(provider, args, repoRoot) {
742
- const maxResults = args.maxResults ?? AGENT_CONFIG.MAX_LIST_RESULTS;
743
- const maxDepth = args.maxDepth ?? AGENT_CONFIG.MAX_LIST_DEPTH;
744
- const entries = await provider.listDirectory({
745
- path: args.path,
746
- pattern: args.pattern ?? null,
747
- maxResults,
748
- maxDepth
749
- });
750
- if (!entries.length) return "empty";
751
- if (repoRoot) {
752
- const absRoot = import_path.default.resolve(repoRoot);
753
- const lines = entries.map((e) => import_path.default.join(absRoot, e.path));
754
- return lines.join("\n");
755
- }
756
- return entries.map((e) => e.path).join("\n");
757
- }
758
-
759
- // tools/warp_grep/agent/tools/glob.ts
760
- async function toolGlob(provider, args) {
761
- const res = await provider.glob(args);
762
- if (res.error) {
763
- return res.error;
764
- }
765
- if (!res.files.length) {
766
- return "no matches";
767
- }
768
- const header = `Found ${res.totalFound} file(s) matching "${args.pattern}" within ${res.searchDir}, sorted by modification time (newest first):`;
769
- const body = res.files.join("\n");
770
- const truncated = res.totalFound > res.files.length ? `
771
- [${res.totalFound - res.files.length} files truncated]` : "";
772
- return `${header}
773
- ---
774
- ${body}
775
- ---${truncated}`;
772
+ async function toolListDirectory(provider, args) {
773
+ const maxResults = args.maxResults ?? AGENT_CONFIG.MAX_OUTPUT_LINES;
774
+ const initialDepth = args.maxDepth ?? AGENT_CONFIG.MAX_LIST_DEPTH;
775
+ async function getListRecursive(currentDepth) {
776
+ const entries = await provider.listDirectory({
777
+ path: args.path,
778
+ pattern: args.pattern ?? null,
779
+ maxResults,
780
+ maxDepth: currentDepth
781
+ });
782
+ if (entries.length >= maxResults && currentDepth > 0) {
783
+ return getListRecursive(currentDepth - 1);
784
+ }
785
+ return { entries };
786
+ }
787
+ const { entries: list } = await getListRecursive(initialDepth);
788
+ if (!list.length) return "empty";
789
+ const tree = list.map((e) => {
790
+ const indent = " ".repeat(e.depth);
791
+ const name = e.type === "dir" ? `${e.name}/` : e.name;
792
+ return `${indent}${name}`;
793
+ }).join("\n");
794
+ return tree;
776
795
  }
777
796
 
778
797
  // tools/warp_grep/agent/tools/finish.ts
@@ -833,21 +852,32 @@ function mergeRanges(ranges) {
833
852
  return merged;
834
853
  }
835
854
 
836
- // tools/warp_grep/agent/helpers.ts
837
- var import_path2 = __toESM(require("path"), 1);
838
- init_config();
839
- var TRUNCATED_MARKER = "[truncated for context limit]";
840
- function getMessageSize(m) {
841
- if (m.role === "tool") return m.content.length;
842
- if (m.role === "assistant") {
843
- let size = typeof m.content === "string" ? m.content.length : 0;
844
- if (m.tool_calls) {
845
- size += m.tool_calls.reduce((s, tc) => s + tc.function.name.length + tc.function.arguments.length, 0);
855
+ // tools/warp_grep/agent/formatter.ts
856
+ var ToolOutputFormatter = class {
857
+ format(toolName, _args, output, options = {}) {
858
+ const name = (toolName ?? "").trim();
859
+ if (!name) {
860
+ return "";
846
861
  }
847
- return size;
862
+ const payload = output?.toString?.()?.trim?.() ?? "";
863
+ const isError = Boolean(options.isError);
864
+ if (!payload && !isError) {
865
+ return "";
866
+ }
867
+ return `<tool_response>
868
+ ${payload}
869
+ </tool_response>`;
848
870
  }
849
- return m.content.length;
871
+ };
872
+ var sharedFormatter = new ToolOutputFormatter();
873
+ function formatAgentToolOutput(toolName, args, output, options = {}) {
874
+ return sharedFormatter.format(toolName, args, output, options);
850
875
  }
876
+
877
+ // tools/warp_grep/agent/helpers.ts
878
+ var import_path = __toESM(require("path"), 1);
879
+ init_config();
880
+ var TRUNCATED_MARKER = "[truncated for context limit]";
851
881
  function formatTurnMessage(turnsUsed, maxTurns) {
852
882
  const turnsRemaining = maxTurns - turnsUsed;
853
883
  if (turnsRemaining === 1) {
@@ -858,7 +888,7 @@ You have used ${turnsUsed} turns, you only have 1 turn remaining. You have run o
858
888
  You have used ${turnsUsed} turn${turnsUsed === 1 ? "" : "s"} and have ${turnsRemaining} remaining`;
859
889
  }
860
890
  function calculateContextBudget(messages) {
861
- const totalChars = messages.reduce((sum, m) => sum + getMessageSize(m), 0);
891
+ const totalChars = messages.reduce((sum, m) => sum + m.content.length, 0);
862
892
  const maxChars = AGENT_CONFIG.MAX_CONTEXT_CHARS;
863
893
  const percent = Math.round(totalChars / maxChars * 100);
864
894
  const usedK = Math.round(totalChars / 1e3);
@@ -867,21 +897,24 @@ function calculateContextBudget(messages) {
867
897
  }
868
898
  async function buildInitialState(repoRoot, searchTerm, provider, options) {
869
899
  const budget = calculateContextBudget([]);
870
- const turnTag = `You have used 0 turns and have ${AGENT_CONFIG.MAX_TURNS} remaining`;
900
+ const turnTag = `Turn 0/${AGENT_CONFIG.MAX_TURNS}`;
871
901
  const treeDepth = options?.search_type === "node_modules" ? 1 : 2;
872
- const absRoot = import_path2.default.resolve(repoRoot);
873
902
  try {
874
903
  const entries = await provider.listDirectory({
875
904
  path: ".",
876
905
  maxResults: AGENT_CONFIG.MAX_OUTPUT_LINES,
877
906
  maxDepth: treeDepth
878
907
  });
879
- const lines = [absRoot];
880
- for (const e of entries) {
881
- lines.push(import_path2.default.join(absRoot, e.path));
882
- }
908
+ const treeLines = entries.map((e) => {
909
+ const indent = " ".repeat(e.depth);
910
+ const name = e.type === "dir" ? `${e.name}/` : e.name;
911
+ return `${indent}${name}`;
912
+ });
913
+ const repoName = import_path.default.basename(repoRoot);
914
+ const treeOutput = treeLines.length > 0 ? `${repoName}/
915
+ ${treeLines.join("\n")}` : `${repoName}/`;
883
916
  return `<repo_structure>
884
- ${lines.join("\n")}
917
+ ${treeOutput}
885
918
  </repo_structure>
886
919
 
887
920
  <search_string>
@@ -890,8 +923,9 @@ ${searchTerm}
890
923
  ${budget}
891
924
  ${turnTag}`;
892
925
  } catch {
926
+ const repoName = import_path.default.basename(repoRoot);
893
927
  return `<repo_structure>
894
- ${absRoot}
928
+ ${repoName}/
895
929
  </repo_structure>
896
930
 
897
931
  <search_string>
@@ -902,32 +936,26 @@ ${turnTag}`;
902
936
  }
903
937
  }
904
938
  function enforceContextLimit(messages, maxChars = AGENT_CONFIG.MAX_CONTEXT_CHARS) {
905
- const getTotalChars = () => messages.reduce((sum, m) => sum + getMessageSize(m), 0);
939
+ const getTotalChars = () => messages.reduce((sum, m) => sum + m.content.length, 0);
906
940
  if (getTotalChars() <= maxChars) {
907
941
  return messages;
908
942
  }
909
- const truncatableIndices = [];
943
+ const userIndices = [];
910
944
  let firstUserSkipped = false;
911
945
  for (let i = 0; i < messages.length; i++) {
912
- const m = messages[i];
913
- if (m.role === "tool") {
914
- truncatableIndices.push(i);
915
- } else if (m.role === "user") {
946
+ if (messages[i].role === "user") {
916
947
  if (!firstUserSkipped) {
917
948
  firstUserSkipped = true;
918
949
  continue;
919
950
  }
920
- truncatableIndices.push(i);
951
+ userIndices.push(i);
921
952
  }
922
953
  }
923
- for (const idx of truncatableIndices) {
954
+ for (const idx of userIndices) {
924
955
  if (getTotalChars() <= maxChars) {
925
956
  break;
926
957
  }
927
- const m = messages[idx];
928
- if (m.role === "tool" && m.content !== TRUNCATED_MARKER) {
929
- messages[idx] = { role: "tool", tool_call_id: m.tool_call_id, content: TRUNCATED_MARKER };
930
- } else if (m.role === "user" && m.content !== TRUNCATED_MARKER) {
958
+ if (messages[idx].content !== TRUNCATED_MARKER) {
931
959
  messages[idx] = { role: "user", content: TRUNCATED_MARKER };
932
960
  }
933
961
  }
@@ -940,7 +968,7 @@ var import_openai = __toESM(require("openai"), 1);
940
968
  // package.json
941
969
  var package_default = {
942
970
  name: "@morphllm/morphsdk",
943
- version: "0.2.145",
971
+ version: "0.2.146",
944
972
  description: "TypeScript SDK and CLI for Morph Fast Apply integration",
945
973
  type: "module",
946
974
  main: "./dist/index.cjs",
@@ -1179,115 +1207,9 @@ var package_default = {
1179
1207
  var SDK_VERSION = package_default.version;
1180
1208
 
1181
1209
  // tools/warp_grep/agent/runner.ts
1182
- var import_path3 = __toESM(require("path"), 1);
1210
+ var import_path2 = __toESM(require("path"), 1);
1211
+ var parser = new LLMResponseParser();
1183
1212
  var DEFAULT_API_URL = "https://api.morphllm.com";
1184
- var TOOL_SPECS = [
1185
- {
1186
- type: "function",
1187
- function: {
1188
- name: "list_directory",
1189
- description: "Execute ls or find commands to explore directory structure. Max 500 results. Common junk directories are excluded automatically.",
1190
- parameters: {
1191
- type: "object",
1192
- properties: {
1193
- command: {
1194
- type: "string",
1195
- description: "Full ls or find command (e.g. ls -la src/, find . -maxdepth 2 -type f -name '*.py', find . -type d, ls -d */)."
1196
- }
1197
- },
1198
- required: ["command"]
1199
- }
1200
- }
1201
- },
1202
- {
1203
- type: "function",
1204
- function: {
1205
- name: "grep_search",
1206
- description: "Search for a regex pattern in file contents. Returns matching lines with file paths and line numbers. Case-insensitive. Respects .gitignore.",
1207
- parameters: {
1208
- type: "object",
1209
- properties: {
1210
- pattern: {
1211
- type: "string",
1212
- description: "Regex pattern to search for in file contents (e.g. 'class\\s+\\w+Error', 'import|require|from', 'def (get|set|update)_user')."
1213
- },
1214
- path: {
1215
- type: "string",
1216
- description: "File or directory to search in. Defaults to current working directory."
1217
- },
1218
- glob: {
1219
- type: "string",
1220
- description: "Glob pattern to filter files (e.g. '*.py', '*.{ts,tsx,js,jsx,py,go}', 'src/**/*.go', '!*.test.*')."
1221
- },
1222
- limit: {
1223
- type: "integer",
1224
- description: "Limit output to first N matching lines. Shows all matches if not specified."
1225
- }
1226
- },
1227
- required: ["pattern"]
1228
- }
1229
- }
1230
- },
1231
- {
1232
- type: "function",
1233
- function: {
1234
- name: "glob",
1235
- description: "Find files by name/extension using glob patterns. Returns absolute paths sorted by modification time (newest first). Respects .gitignore. Max 100 results.",
1236
- parameters: {
1237
- type: "object",
1238
- properties: {
1239
- pattern: {
1240
- type: "string",
1241
- description: "Glob pattern to match files (e.g. '*.py', 'src/**/*.js', '*.{ts,tsx}', 'test_*.py')."
1242
- },
1243
- path: {
1244
- type: "string",
1245
- description: "Directory to search in. Defaults to repository root."
1246
- }
1247
- },
1248
- required: ["pattern"]
1249
- }
1250
- }
1251
- },
1252
- {
1253
- type: "function",
1254
- function: {
1255
- name: "read",
1256
- description: "Read entire files or specific line ranges using absolute paths.",
1257
- parameters: {
1258
- type: "object",
1259
- properties: {
1260
- path: {
1261
- type: "string",
1262
- description: "File path to read, using absolute path (e.g. '/home/ubuntu/repo/src/main.py' or windows path)."
1263
- },
1264
- lines: {
1265
- type: "string",
1266
- description: "Optional line range (e.g. '1-50' or '1-20,45-80'). Omit to read entire file."
1267
- }
1268
- },
1269
- required: ["path"]
1270
- }
1271
- }
1272
- },
1273
- {
1274
- type: "function",
1275
- function: {
1276
- name: "finish",
1277
- description: "Submit final answer with all relevant code locations. Include imports and over-include rather than miss context.",
1278
- parameters: {
1279
- type: "object",
1280
- properties: {
1281
- files: {
1282
- type: "string",
1283
- 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."
1284
- }
1285
- },
1286
- required: ["files"]
1287
- }
1288
- }
1289
- }
1290
- ];
1291
1213
  async function callModel(messages, model, options = {}) {
1292
1214
  const baseUrl = options.morphApiUrl || DEFAULT_API_URL;
1293
1215
  const apiKey = options.morphApiKey || process.env.MORPH_API_KEY || "";
@@ -1308,9 +1230,8 @@ async function callModel(messages, model, options = {}) {
1308
1230
  data = await client.chat.completions.create({
1309
1231
  model,
1310
1232
  temperature: 0,
1311
- max_tokens: 2048,
1233
+ max_tokens: 1024,
1312
1234
  messages,
1313
- tools: TOOL_SPECS,
1314
1235
  ...options.search_type ? { search_type: options.search_type } : {}
1315
1236
  });
1316
1237
  } catch (error) {
@@ -1322,87 +1243,187 @@ async function callModel(messages, model, options = {}) {
1322
1243
  throw error;
1323
1244
  }
1324
1245
  const choice = data?.choices?.[0];
1325
- const message = choice?.message;
1326
- if (!message) {
1327
- if (attempt === MAX_EMPTY_RETRIES) {
1328
- throw new Error("Invalid response from model: no message in response");
1329
- }
1330
- await new Promise((resolve) => setTimeout(resolve, 200));
1331
- continue;
1332
- }
1333
- const toolCalls = (message.tool_calls || []).map((tc) => ({
1334
- id: tc.id,
1335
- type: "function",
1336
- function: { name: tc.function.name, arguments: tc.function.arguments }
1337
- }));
1338
- if (message.content || toolCalls.length > 0) {
1339
- return { content: message.content ?? null, tool_calls: toolCalls };
1246
+ const content = choice?.message?.content;
1247
+ if (content && typeof content === "string") {
1248
+ return content;
1340
1249
  }
1341
1250
  if (attempt === MAX_EMPTY_RETRIES) {
1342
1251
  const finishReason = choice?.finish_reason ?? "unknown";
1252
+ const hasToolCalls = Array.isArray(choice?.message?.tool_calls) && choice.message.tool_calls.length > 0;
1253
+ const choicesLen = data?.choices?.length ?? 0;
1254
+ const contentType = content === null ? "null" : content === void 0 ? "undefined" : typeof content;
1343
1255
  throw new Error(
1344
- `Invalid response from model: no content and no tool_calls, finish_reason=${finishReason}`
1256
+ `Invalid response from model: content=${contentType}, finish_reason=${finishReason}, has_tool_calls=${hasToolCalls}, choices_length=${choicesLen}`
1345
1257
  );
1346
1258
  }
1347
1259
  await new Promise((resolve) => setTimeout(resolve, 200));
1348
1260
  }
1349
1261
  throw new Error("Invalid response from model");
1350
1262
  }
1351
- function safeParseJSON(s) {
1352
- try {
1353
- return JSON.parse(s);
1354
- } catch {
1355
- return {};
1356
- }
1357
- }
1358
- async function executeTool(provider, name, args, repoRoot) {
1359
- switch (name) {
1360
- case "grep_search": {
1361
- const grepArgs = {
1362
- pattern: args.pattern,
1363
- path: args.path || "."
1364
- };
1365
- if (args.glob) grepArgs.glob = args.glob;
1366
- if (args.case_sensitive !== void 0) grepArgs.case_sensitive = args.case_sensitive;
1367
- const result = await toolGrep(provider, grepArgs);
1368
- let output = result.output;
1369
- if (args.limit && typeof args.limit === "number") {
1370
- const lines = output.split("\n");
1371
- if (lines.length > args.limit) {
1372
- output = lines.slice(0, args.limit).join("\n") + `
1373
- ... (truncated at ${args.limit} lines)`;
1374
- }
1375
- }
1376
- return output;
1263
+ async function runWarpGrep(config) {
1264
+ const totalStart = Date.now();
1265
+ const timeoutMs = config.timeout ?? AGENT_CONFIG.TIMEOUT_MS;
1266
+ const timings = { turns: [], timeout_ms: timeoutMs };
1267
+ const repoRoot = import_path2.default.resolve(config.repoRoot || process.cwd());
1268
+ const model = config.model || DEFAULT_MODEL;
1269
+ const messages = [];
1270
+ const maxTurns = AGENT_CONFIG.MAX_TURNS;
1271
+ const initialStateStart = Date.now();
1272
+ const initialState = await buildInitialState(repoRoot, config.searchTerm, config.provider, { search_type: config.search_type });
1273
+ timings.initial_state_ms = Date.now() - initialStateStart;
1274
+ messages.push({ role: "user", content: initialState });
1275
+ const provider = config.provider;
1276
+ const errors = [];
1277
+ let finishMeta;
1278
+ let terminationReason = "terminated";
1279
+ for (let turn = 1; turn <= maxTurns; turn += 1) {
1280
+ const turnMetrics = { turn, morph_api_ms: 0, local_tools_ms: 0 };
1281
+ enforceContextLimit(messages);
1282
+ const modelCallStart = Date.now();
1283
+ const assistantContent = await callModel(messages, model, {
1284
+ morphApiKey: config.morphApiKey,
1285
+ morphApiUrl: config.morphApiUrl,
1286
+ retryConfig: config.retryConfig,
1287
+ timeout: timeoutMs,
1288
+ search_type: config.search_type
1289
+ }).catch((e) => {
1290
+ const errMsg = e instanceof Error ? e.message : String(e);
1291
+ console.error(`[warp_grep] Morph API call failed on turn ${turn}:`, errMsg);
1292
+ errors.push({ message: errMsg });
1293
+ return "";
1294
+ });
1295
+ turnMetrics.morph_api_ms = Date.now() - modelCallStart;
1296
+ if (!assistantContent) {
1297
+ console.error(`[warp_grep] Empty response from Morph API on turn ${turn}. Errors so far:`, errors);
1298
+ timings.turns.push(turnMetrics);
1299
+ break;
1377
1300
  }
1378
- case "glob": {
1379
- return toolGlob(provider, {
1380
- pattern: args.pattern,
1381
- path: args.path
1382
- });
1301
+ messages.push({ role: "assistant", content: assistantContent });
1302
+ const toolCalls = parser.parse(assistantContent);
1303
+ if (toolCalls.length === 0) {
1304
+ console.error(`[warp_grep] No tool calls parsed on turn ${turn}. Assistant content (first 500 chars):`, assistantContent.slice(0, 500));
1305
+ 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" });
1306
+ terminationReason = "terminated";
1307
+ timings.turns.push(turnMetrics);
1308
+ break;
1383
1309
  }
1384
- case "list_directory": {
1385
- const dirPath = extractPathFromCommand(args.command || ".");
1386
- return toolListDirectory(provider, { path: dirPath }, repoRoot);
1310
+ const finishCalls = toolCalls.filter((c) => c.name === "finish");
1311
+ const grepCalls = toolCalls.filter((c) => c.name === "grep");
1312
+ const listDirCalls = toolCalls.filter((c) => c.name === "list_directory");
1313
+ const readCalls = toolCalls.filter((c) => c.name === "read");
1314
+ const skipCalls = toolCalls.filter((c) => c.name === "_skip");
1315
+ const formatted = [];
1316
+ for (const c of skipCalls) {
1317
+ const msg = c.arguments?.message || "Command skipped due to parsing error";
1318
+ formatted.push(msg);
1387
1319
  }
1388
- case "read": {
1389
- const readArgs = {
1390
- path: args.path
1391
- };
1392
- if (args.lines && typeof args.lines === "string") {
1393
- Object.assign(readArgs, parseReadLines(args.lines));
1320
+ const allPromises = [];
1321
+ for (const c of grepCalls) {
1322
+ const args = c.arguments ?? {};
1323
+ allPromises.push(
1324
+ toolGrep(provider, args).then(
1325
+ ({ output }) => formatAgentToolOutput("grep", args, output),
1326
+ (err) => formatAgentToolOutput("grep", args, String(err), { isError: true })
1327
+ )
1328
+ );
1329
+ }
1330
+ for (const c of listDirCalls) {
1331
+ const args = c.arguments ?? {};
1332
+ allPromises.push(
1333
+ toolListDirectory(provider, args).then(
1334
+ (p) => formatAgentToolOutput("list_directory", args, p),
1335
+ (err) => formatAgentToolOutput("list_directory", args, String(err), { isError: true })
1336
+ )
1337
+ );
1338
+ }
1339
+ for (const c of readCalls) {
1340
+ const args = c.arguments ?? {};
1341
+ allPromises.push(
1342
+ toolRead(provider, args).then(
1343
+ (p) => formatAgentToolOutput("read", args, p),
1344
+ (err) => formatAgentToolOutput("read", args, String(err), { isError: true })
1345
+ )
1346
+ );
1347
+ }
1348
+ const toolExecStart = Date.now();
1349
+ const allResults = await Promise.all(allPromises);
1350
+ turnMetrics.local_tools_ms = Date.now() - toolExecStart;
1351
+ for (const result of allResults) {
1352
+ formatted.push(result);
1353
+ }
1354
+ if (formatted.length > 0) {
1355
+ const turnMessage = formatTurnMessage(turn, maxTurns);
1356
+ const contextBudget = calculateContextBudget(messages);
1357
+ messages.push({ role: "user", content: formatted.join("\n") + turnMessage + "\n" + contextBudget });
1358
+ }
1359
+ timings.turns.push(turnMetrics);
1360
+ if (finishCalls.length) {
1361
+ const fc = finishCalls[0];
1362
+ const files = fc.arguments?.files ?? [];
1363
+ const textResult = fc.arguments?.textResult;
1364
+ finishMeta = { files };
1365
+ terminationReason = "completed";
1366
+ if (files.length === 0) {
1367
+ const payload2 = textResult || "No relevant code found.";
1368
+ timings.turns.push(turnMetrics);
1369
+ timings.total_ms = Date.now() - totalStart;
1370
+ return {
1371
+ terminationReason: "completed",
1372
+ messages,
1373
+ finish: { payload: payload2, metadata: finishMeta },
1374
+ timings
1375
+ };
1376
+ }
1377
+ break;
1378
+ }
1379
+ }
1380
+ if (terminationReason !== "completed" || !finishMeta) {
1381
+ timings.total_ms = Date.now() - totalStart;
1382
+ return { terminationReason, messages, errors, timings };
1383
+ }
1384
+ const parts = ["Relevant context found:"];
1385
+ for (const f of finishMeta.files) {
1386
+ const ranges = f.lines === "*" ? "*" : Array.isArray(f.lines) ? f.lines.map(([s, e]) => `${s}-${e}`).join(", ") : "*";
1387
+ parts.push(`- ${f.path}: ${ranges}`);
1388
+ }
1389
+ const payload = parts.join("\n");
1390
+ const finishResolutionStart = Date.now();
1391
+ const fileReadErrors = [];
1392
+ const resolved = await readFinishFiles(
1393
+ repoRoot,
1394
+ finishMeta.files,
1395
+ async (p, s, e) => {
1396
+ try {
1397
+ const rr = await provider.read({ path: p, start: s, end: e });
1398
+ return rr.lines.map((l) => {
1399
+ const idx = l.indexOf("|");
1400
+ return idx >= 0 ? l.slice(idx + 1) : l;
1401
+ });
1402
+ } catch (err) {
1403
+ const errorMsg = err instanceof Error ? err.message : String(err);
1404
+ fileReadErrors.push({ path: p, error: errorMsg });
1405
+ console.error(`[warp_grep] Failed to read file: ${p} - ${errorMsg}`);
1406
+ return [`[couldn't find: ${p}]`];
1394
1407
  }
1395
- return toolRead(provider, readArgs);
1396
1408
  }
1397
- default:
1398
- return `Unknown tool: ${name}`;
1409
+ );
1410
+ timings.finish_resolution_ms = Date.now() - finishResolutionStart;
1411
+ if (fileReadErrors.length > 0) {
1412
+ errors.push(...fileReadErrors.map((e) => ({ message: `File read error: ${e.path} - ${e.error}` })));
1399
1413
  }
1414
+ timings.total_ms = Date.now() - totalStart;
1415
+ return {
1416
+ terminationReason: "completed",
1417
+ messages,
1418
+ finish: { payload, metadata: finishMeta, resolved },
1419
+ timings
1420
+ };
1400
1421
  }
1401
1422
  async function* runWarpGrepStreaming(config) {
1402
1423
  const totalStart = Date.now();
1403
1424
  const timeoutMs = config.timeout ?? AGENT_CONFIG.TIMEOUT_MS;
1404
1425
  const timings = { turns: [], timeout_ms: timeoutMs };
1405
- const repoRoot = import_path3.default.resolve(config.repoRoot || process.cwd());
1426
+ const repoRoot = import_path2.default.resolve(config.repoRoot || process.cwd());
1406
1427
  const model = config.model || DEFAULT_MODEL;
1407
1428
  const messages = [];
1408
1429
  const maxTurns = AGENT_CONFIG.MAX_TURNS;
@@ -1418,7 +1439,7 @@ async function* runWarpGrepStreaming(config) {
1418
1439
  const turnMetrics = { turn, morph_api_ms: 0, local_tools_ms: 0 };
1419
1440
  enforceContextLimit(messages);
1420
1441
  const modelCallStart = Date.now();
1421
- const response = await callModel(messages, model, {
1442
+ const assistantContent = await callModel(messages, model, {
1422
1443
  morphApiKey: config.morphApiKey,
1423
1444
  morphApiUrl: config.morphApiUrl,
1424
1445
  retryConfig: config.retryConfig,
@@ -1426,45 +1447,90 @@ async function* runWarpGrepStreaming(config) {
1426
1447
  search_type: config.search_type
1427
1448
  }).catch((e) => {
1428
1449
  const errMsg = e instanceof Error ? e.message : String(e);
1429
- console.error(`[warp_grep] Morph API call failed on turn ${turn}:`, errMsg);
1450
+ console.error(`[warp_grep:stream] Morph API call failed on turn ${turn}:`, errMsg);
1430
1451
  errors.push({ message: errMsg });
1431
- return null;
1452
+ return "";
1432
1453
  });
1433
1454
  turnMetrics.morph_api_ms = Date.now() - modelCallStart;
1434
- if (!response) {
1455
+ if (!assistantContent) {
1456
+ console.error(`[warp_grep:stream] Empty response from Morph API on turn ${turn}. Errors so far:`, errors);
1435
1457
  timings.turns.push(turnMetrics);
1436
1458
  break;
1437
1459
  }
1438
- const toolCalls = response.tool_calls;
1439
- messages.push({
1440
- role: "assistant",
1441
- content: response.content,
1442
- ...toolCalls.length > 0 ? { tool_calls: toolCalls } : {}
1443
- });
1460
+ messages.push({ role: "assistant", content: assistantContent });
1461
+ const toolCalls = parser.parse(assistantContent);
1444
1462
  if (toolCalls.length === 0) {
1445
- console.error(`[warp_grep] No tool calls on turn ${turn}. Content: ${(response.content || "").slice(0, 500)}`);
1446
- errors.push({ message: "No tool calls produced by the model." });
1463
+ console.error(`[warp_grep:stream] No tool calls parsed on turn ${turn}. Assistant content (first 500 chars):`, assistantContent.slice(0, 500));
1464
+ 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" });
1447
1465
  terminationReason = "terminated";
1448
1466
  timings.turns.push(turnMetrics);
1449
1467
  break;
1450
1468
  }
1451
1469
  yield {
1452
1470
  turn,
1453
- toolCalls: toolCalls.map((tc) => ({
1454
- name: tc.function.name,
1455
- arguments: safeParseJSON(tc.function.arguments)
1471
+ toolCalls: toolCalls.map((c) => ({
1472
+ name: c.name,
1473
+ arguments: c.arguments ?? {}
1456
1474
  }))
1457
1475
  };
1458
- const finishCall = toolCalls.find((tc) => tc.function.name === "finish");
1459
- if (finishCall) {
1460
- const args = safeParseJSON(finishCall.function.arguments);
1461
- const filesStr = args.files || "";
1462
- const files = parseFinishFiles(filesStr);
1476
+ const finishCalls = toolCalls.filter((c) => c.name === "finish");
1477
+ const grepCalls = toolCalls.filter((c) => c.name === "grep");
1478
+ const listDirCalls = toolCalls.filter((c) => c.name === "list_directory");
1479
+ const readCalls = toolCalls.filter((c) => c.name === "read");
1480
+ const skipCalls = toolCalls.filter((c) => c.name === "_skip");
1481
+ const formatted = [];
1482
+ for (const c of skipCalls) {
1483
+ const msg = c.arguments?.message || "Command skipped due to parsing error";
1484
+ formatted.push(msg);
1485
+ }
1486
+ const allPromises = [];
1487
+ for (const c of grepCalls) {
1488
+ const args = c.arguments ?? {};
1489
+ allPromises.push(
1490
+ toolGrep(provider, args).then(
1491
+ ({ output }) => formatAgentToolOutput("grep", args, output),
1492
+ (err) => formatAgentToolOutput("grep", args, String(err), { isError: true })
1493
+ )
1494
+ );
1495
+ }
1496
+ for (const c of listDirCalls) {
1497
+ const args = c.arguments ?? {};
1498
+ allPromises.push(
1499
+ toolListDirectory(provider, args).then(
1500
+ (p) => formatAgentToolOutput("list_directory", args, p),
1501
+ (err) => formatAgentToolOutput("list_directory", args, String(err), { isError: true })
1502
+ )
1503
+ );
1504
+ }
1505
+ for (const c of readCalls) {
1506
+ const args = c.arguments ?? {};
1507
+ allPromises.push(
1508
+ toolRead(provider, args).then(
1509
+ (p) => formatAgentToolOutput("read", args, p),
1510
+ (err) => formatAgentToolOutput("read", args, String(err), { isError: true })
1511
+ )
1512
+ );
1513
+ }
1514
+ const toolExecStart = Date.now();
1515
+ const allResults = await Promise.all(allPromises);
1516
+ turnMetrics.local_tools_ms = Date.now() - toolExecStart;
1517
+ for (const result of allResults) {
1518
+ formatted.push(result);
1519
+ }
1520
+ if (formatted.length > 0) {
1521
+ const turnMessage = formatTurnMessage(turn, maxTurns);
1522
+ const contextBudget = calculateContextBudget(messages);
1523
+ messages.push({ role: "user", content: formatted.join("\n") + turnMessage + "\n" + contextBudget });
1524
+ }
1525
+ timings.turns.push(turnMetrics);
1526
+ if (finishCalls.length) {
1527
+ const fc = finishCalls[0];
1528
+ const files = fc.arguments?.files ?? [];
1529
+ const textResult = fc.arguments?.textResult;
1463
1530
  finishMeta = { files };
1464
1531
  terminationReason = "completed";
1465
1532
  if (files.length === 0) {
1466
- const payload2 = filesStr || "No relevant code found.";
1467
- timings.turns.push(turnMetrics);
1533
+ const payload2 = textResult || "No relevant code found.";
1468
1534
  timings.total_ms = Date.now() - totalStart;
1469
1535
  return {
1470
1536
  terminationReason: "completed",
@@ -1473,25 +1539,8 @@ async function* runWarpGrepStreaming(config) {
1473
1539
  timings
1474
1540
  };
1475
1541
  }
1476
- timings.turns.push(turnMetrics);
1477
1542
  break;
1478
1543
  }
1479
- const toolExecStart = Date.now();
1480
- const results = await Promise.all(
1481
- toolCalls.map(async (tc) => {
1482
- const args = safeParseJSON(tc.function.arguments);
1483
- const output = await executeTool(provider, tc.function.name, args, repoRoot).catch((err) => String(err));
1484
- return { tool_call_id: tc.id, content: output };
1485
- })
1486
- );
1487
- turnMetrics.local_tools_ms = Date.now() - toolExecStart;
1488
- for (const result of results) {
1489
- messages.push({ role: "tool", tool_call_id: result.tool_call_id, content: result.content });
1490
- }
1491
- const turnMsg = formatTurnMessage(turn, maxTurns);
1492
- const budget = calculateContextBudget(messages);
1493
- messages.push({ role: "user", content: turnMsg + "\n" + budget });
1494
- timings.turns.push(turnMetrics);
1495
1544
  }
1496
1545
  if (terminationReason !== "completed" || !finishMeta) {
1497
1546
  timings.total_ms = Date.now() - totalStart;
@@ -1535,14 +1584,6 @@ async function* runWarpGrepStreaming(config) {
1535
1584
  timings
1536
1585
  };
1537
1586
  }
1538
- async function runWarpGrep(config) {
1539
- const gen = runWarpGrepStreaming(config);
1540
- let result = await gen.next();
1541
- while (!result.done) {
1542
- result = await gen.next();
1543
- }
1544
- return result.value;
1545
- }
1546
1587
 
1547
1588
  // tools/warp_grep/providers/remote.ts
1548
1589
  init_config();
@@ -1722,35 +1763,6 @@ var RemoteCommandsProvider = class {
1722
1763
  return [];
1723
1764
  }
1724
1765
  }
1725
- /**
1726
- * Glob search - finds files matching a pattern.
1727
- * Falls back to a grep --files approach via the listDir command.
1728
- */
1729
- async glob(params) {
1730
- const searchPath = params.path || this.repoRoot;
1731
- try {
1732
- const stdout = await this.commands.listDir(searchPath, 10);
1733
- const allPaths = (stdout || "").trim().split(/\r?\n/).filter((p) => p.length > 0);
1734
- const globToRegex = (glob) => {
1735
- const escaped = glob.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".");
1736
- return new RegExp(escaped);
1737
- };
1738
- const regex = globToRegex(params.pattern);
1739
- const matched = allPaths.filter((p) => {
1740
- const name = p.split("/").pop() || "";
1741
- return regex.test(name) && !shouldSkip(name);
1742
- });
1743
- const totalFound = matched.length;
1744
- return { files: matched.slice(0, 100), searchDir: searchPath, totalFound };
1745
- } catch (error) {
1746
- return {
1747
- files: [],
1748
- searchDir: searchPath,
1749
- totalFound: 0,
1750
- error: `[GLOB ERROR] ${error instanceof Error ? error.message : String(error)}`
1751
- };
1752
- }
1753
- }
1754
1766
  };
1755
1767
 
1756
1768
  // tools/warp_grep/providers/code_storage_http.ts
@@ -1777,9 +1789,9 @@ function createCodeStorageHttpCommands(config) {
1777
1789
  const { baseUrl, repoId, branch } = config;
1778
1790
  const encodedRepoId = encodeURIComponent(repoId);
1779
1791
  return {
1780
- grep: (pattern, path6, glob) => post(`${baseUrl}/api/code-search/${encodedRepoId}/grep`, { pattern, path: path6, glob, branch }, "grep"),
1781
- read: (path6, start, end) => post(`${baseUrl}/api/code-search/${encodedRepoId}/read`, { path: path6, start, end, branch }, "read"),
1782
- listDir: (path6, maxDepth) => post(`${baseUrl}/api/code-search/${encodedRepoId}/list`, { path: path6, maxDepth, branch }, "list")
1792
+ grep: (pattern, path5, glob) => post(`${baseUrl}/api/code-search/${encodedRepoId}/grep`, { pattern, path: path5, glob, branch }, "grep"),
1793
+ read: (path5, start, end) => post(`${baseUrl}/api/code-search/${encodedRepoId}/read`, { path: path5, start, end, branch }, "read"),
1794
+ listDir: (path5, maxDepth) => post(`${baseUrl}/api/code-search/${encodedRepoId}/list`, { path: path5, maxDepth, branch }, "list")
1783
1795
  };
1784
1796
  }
1785
1797