@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
package/dist/client.cjs CHANGED
@@ -36,7 +36,7 @@ var init_package = __esm({
36
36
  "package.json"() {
37
37
  package_default = {
38
38
  name: "@morphllm/morphsdk",
39
- version: "0.2.145",
39
+ version: "0.2.146",
40
40
  description: "TypeScript SDK and CLI for Morph Fast Apply integration",
41
41
  type: "module",
42
42
  main: "./dist/index.cjs",
@@ -416,12 +416,11 @@ var init_config = __esm({
416
416
  return isNaN(parsed) || parsed <= 0 ? defaultMs : parsed;
417
417
  };
418
418
  AGENT_CONFIG = {
419
- MAX_TURNS: 6,
419
+ MAX_TURNS: 4,
420
420
  /** Default timeout for model calls. Can be overridden via MORPH_WARP_GREP_TIMEOUT env var (in ms) */
421
421
  TIMEOUT_MS: parseEnvTimeout(process.env.MORPH_WARP_GREP_TIMEOUT, 3e4),
422
- MAX_CONTEXT_CHARS: 321600,
422
+ MAX_CONTEXT_CHARS: 54e4,
423
423
  MAX_OUTPUT_LINES: 200,
424
- MAX_LIST_RESULTS: 500,
425
424
  MAX_READ_LINES: 800,
426
425
  MAX_LIST_DEPTH: 3,
427
426
  LIST_TIMEOUT_MS: 2e3
@@ -506,7 +505,7 @@ var init_config = __esm({
506
505
  ".*"
507
506
  ];
508
507
  DEFAULT_EXCLUDES = (process.env.MORPH_WARP_GREP_EXCLUDE || "").split(",").map((s) => s.trim()).filter(Boolean).concat(BUILTIN_EXCLUDES);
509
- DEFAULT_MODEL = "morph-warp-grep-v2.1";
508
+ DEFAULT_MODEL = "morph-warp-grep-v2";
510
509
  }
511
510
  });
512
511
 
@@ -579,19 +578,19 @@ var init_ripgrep = __esm({
579
578
 
580
579
  // tools/warp_grep/utils/paths.ts
581
580
  function resolveUnderRepo(repoRoot, targetPath) {
582
- const absRoot = import_path5.default.resolve(repoRoot);
583
- const resolved = import_path5.default.resolve(absRoot, targetPath);
581
+ const absRoot = import_path4.default.resolve(repoRoot);
582
+ const resolved = import_path4.default.resolve(absRoot, targetPath);
584
583
  ensureWithinRepo(absRoot, resolved);
585
584
  return resolved;
586
585
  }
587
586
  function ensureWithinRepo(repoRoot, absTarget) {
588
- const rel = import_path5.default.relative(import_path5.default.resolve(repoRoot), import_path5.default.resolve(absTarget));
589
- if (rel.startsWith("..") || import_path5.default.isAbsolute(rel)) {
587
+ const rel = import_path4.default.relative(import_path4.default.resolve(repoRoot), import_path4.default.resolve(absTarget));
588
+ if (rel.startsWith("..") || import_path4.default.isAbsolute(rel)) {
590
589
  throw new Error(`Path outside repository root: ${absTarget}`);
591
590
  }
592
591
  }
593
592
  function toRepoRelative(repoRoot, absPath) {
594
- return import_path5.default.relative(import_path5.default.resolve(repoRoot), import_path5.default.resolve(absPath));
593
+ return import_path4.default.relative(import_path4.default.resolve(repoRoot), import_path4.default.resolve(absPath));
595
594
  }
596
595
  function isSymlink(p) {
597
596
  try {
@@ -602,7 +601,7 @@ function isSymlink(p) {
602
601
  }
603
602
  }
604
603
  function fixPathRepetition(fullPath) {
605
- const segments = fullPath.split(import_path5.default.sep).filter(Boolean);
604
+ const segments = fullPath.split(import_path4.default.sep).filter(Boolean);
606
605
  if (segments.length < 2) return null;
607
606
  for (let len = Math.floor(segments.length / 2); len >= 1; len--) {
608
607
  for (let i = 0; i <= segments.length - 2 * len; i++) {
@@ -610,7 +609,7 @@ function fixPathRepetition(fullPath) {
610
609
  const second = segments.slice(i + len, i + 2 * len);
611
610
  if (first.every((seg, idx) => seg === second[idx])) {
612
611
  const fixed = [...segments.slice(0, i), ...segments.slice(i + len)];
613
- return import_path5.default.sep + fixed.join(import_path5.default.sep);
612
+ return import_path4.default.sep + fixed.join(import_path4.default.sep);
614
613
  }
615
614
  }
616
615
  }
@@ -634,12 +633,12 @@ function isTextualFile(filePath, maxBytes = 2e6) {
634
633
  return false;
635
634
  }
636
635
  }
637
- var import_fs, import_path5;
636
+ var import_fs, import_path4;
638
637
  var init_paths = __esm({
639
638
  "tools/warp_grep/utils/paths.ts"() {
640
639
  "use strict";
641
640
  import_fs = __toESM(require("fs"), 1);
642
- import_path5 = __toESM(require("path"), 1);
641
+ import_path4 = __toESM(require("path"), 1);
643
642
  }
644
643
  });
645
644
 
@@ -670,12 +669,12 @@ function shouldSkip2(name, allowNames) {
670
669
  }
671
670
  return false;
672
671
  }
673
- var import_promises2, import_path6, SKIP_NAMES2, SKIP_EXTENSIONS2, LocalRipgrepProvider;
672
+ var import_promises2, import_path5, SKIP_NAMES2, SKIP_EXTENSIONS2, LocalRipgrepProvider;
674
673
  var init_local = __esm({
675
674
  "tools/warp_grep/providers/local.ts"() {
676
675
  "use strict";
677
676
  import_promises2 = __toESM(require("fs/promises"), 1);
678
- import_path6 = __toESM(require("path"), 1);
677
+ import_path5 = __toESM(require("path"), 1);
679
678
  init_ripgrep();
680
679
  init_paths();
681
680
  init_files();
@@ -777,7 +776,7 @@ var init_local = __esm({
777
776
  }
778
777
  const stat = await import_promises2.default.stat(abs).catch(() => null);
779
778
  if (!stat) return { lines: [] };
780
- const targetArg = abs === import_path6.default.resolve(this.repoRoot) ? "." : toRepoRelative(this.repoRoot, abs);
779
+ const targetArg = abs === import_path5.default.resolve(this.repoRoot) ? "." : toRepoRelative(this.repoRoot, abs);
781
780
  const contextLines = params.context_lines !== void 0 ? String(params.context_lines) : "1";
782
781
  const args = [
783
782
  "--no-config",
@@ -932,7 +931,7 @@ Details: ${res.stderr}` : ""}`
932
931
  if (timedOut || results.length >= maxResults) break;
933
932
  if (shouldSkip2(entry.name, allowNames)) continue;
934
933
  if (regex && !regex.test(entry.name)) continue;
935
- const full = import_path6.default.join(dir, entry.name);
934
+ const full = import_path5.default.join(dir, entry.name);
936
935
  const isDir = entry.isDirectory();
937
936
  results.push({
938
937
  name: entry.name,
@@ -948,46 +947,6 @@ Details: ${res.stderr}` : ""}`
948
947
  await walk(abs, 0);
949
948
  return results;
950
949
  }
951
- async glob(params) {
952
- let abs;
953
- try {
954
- abs = params.path ? resolveUnderRepo(this.repoRoot, params.path) : this.repoRoot;
955
- } catch (err) {
956
- return { files: [], searchDir: this.repoRoot, totalFound: 0, error: `[PATH ERROR] ${err instanceof Error ? err.message : String(err)}` };
957
- }
958
- const stat = await import_promises2.default.stat(abs).catch(() => null);
959
- if (!stat || !stat.isDirectory()) {
960
- return { files: [], searchDir: abs, totalFound: 0, error: `[PATH ERROR] Directory not found: ${params.path || "."}` };
961
- }
962
- const targetArg = abs === import_path6.default.resolve(this.repoRoot) ? "." : toRepoRelative(this.repoRoot, abs);
963
- const args = [
964
- "--no-config",
965
- "--files",
966
- "--color=never",
967
- "-g",
968
- params.pattern,
969
- ...this.excludes.filter((e) => !this.allowNames?.has(e)).flatMap((e) => ["-g", `!${e}`]),
970
- targetArg || "."
971
- ];
972
- const res = await runRipgrep(args, { cwd: this.repoRoot });
973
- if (res.exitCode === -1) {
974
- return { files: [], searchDir: abs, totalFound: 0, error: `[RIPGREP NOT AVAILABLE] ripgrep (rg) is required for glob search.` };
975
- }
976
- if (res.exitCode !== 0 && res.exitCode !== 1) {
977
- return { files: [], searchDir: abs, totalFound: 0, error: `[GLOB ERROR] glob failed with exit code ${res.exitCode}${res.stderr ? `: ${res.stderr}` : ""}` };
978
- }
979
- const absRoot = import_path6.default.resolve(this.repoRoot);
980
- const relFiles = (res.stdout || "").trim().split(/\r?\n/).filter((l) => l.length > 0);
981
- const absFiles = relFiles.map((f) => import_path6.default.resolve(absRoot, f));
982
- const withMtime = [];
983
- for (const f of absFiles) {
984
- const s = await import_promises2.default.stat(f).catch(() => null);
985
- withMtime.push({ file: f, mtime: s?.mtimeMs ?? 0 });
986
- }
987
- withMtime.sort((a, b) => b.mtime - a.mtime);
988
- const totalFound = withMtime.length;
989
- return { files: withMtime.slice(0, 100).map((f) => f.file), searchDir: abs, totalFound };
990
- }
991
950
  };
992
951
  }
993
952
  });
@@ -2534,58 +2493,131 @@ async function checkHealth(config = {}) {
2534
2493
  init_config();
2535
2494
 
2536
2495
  // tools/warp_grep/agent/parser.ts
2537
- function parseReadLines(linesStr) {
2538
- const ranges = [];
2539
- for (const rangeStr of linesStr.split(",")) {
2540
- const trimmed = rangeStr.trim();
2541
- if (!trimmed) continue;
2542
- const parts = trimmed.split("-").map((v) => parseInt(v.trim(), 10));
2543
- if (parts.length >= 2 && Number.isFinite(parts[0]) && Number.isFinite(parts[1])) {
2544
- ranges.push([parts[0], parts[1]]);
2545
- } else if (Number.isFinite(parts[0])) {
2546
- ranges.push([parts[0], parts[0]]);
2547
- }
2548
- }
2549
- if (ranges.length === 1) return { start: ranges[0][0], end: ranges[0][1] };
2550
- if (ranges.length > 1) return { lines: ranges };
2551
- return {};
2496
+ var VALID_COMMANDS = ["list_directory", "ripgrep", "read", "finish"];
2497
+ function isValidCommand(name) {
2498
+ return VALID_COMMANDS.includes(name);
2552
2499
  }
2553
- function parseFinishFiles(filesStr) {
2554
- const files = [];
2555
- for (const line of filesStr.trim().split(/\s+/)) {
2556
- const trimmed = line.trim();
2557
- if (!trimmed) continue;
2558
- const colonIdx = trimmed.indexOf(":");
2559
- if (colonIdx === -1) {
2560
- files.push({ path: trimmed, lines: "*" });
2561
- continue;
2562
- }
2563
- const filePath = trimmed.slice(0, colonIdx);
2564
- const rangesPart = trimmed.slice(colonIdx + 1);
2565
- if (!rangesPart.trim() || rangesPart.trim() === "*") {
2566
- files.push({ path: filePath, lines: "*" });
2567
- continue;
2568
- }
2569
- const ranges = [];
2570
- for (const rangeStr of rangesPart.split(",")) {
2571
- const rt = rangeStr.trim();
2572
- if (!rt) continue;
2573
- const parts = rt.split("-").map((v) => parseInt(v.trim(), 10));
2574
- if (parts.length >= 2 && Number.isFinite(parts[0]) && Number.isFinite(parts[1])) {
2575
- ranges.push([parts[0], parts[1]]);
2576
- } else if (Number.isFinite(parts[0])) {
2577
- ranges.push([parts[0], parts[0]]);
2500
+ function parseQwen3ToolCalls(text) {
2501
+ const tools = [];
2502
+ const toolCallRegex = /<tool_call>\s*<function=([a-z_][a-z0-9_]*)>([\s\S]*?)<\/function>\s*<\/tool_call>/gi;
2503
+ let match;
2504
+ while ((match = toolCallRegex.exec(text)) !== null) {
2505
+ const funcName = match[1].toLowerCase();
2506
+ const body = match[2];
2507
+ if (!isValidCommand(funcName)) continue;
2508
+ const params = {};
2509
+ const paramRegex = /<parameter=([a-z_][a-z0-9_]*)>([\s\S]*?)<\/parameter>/gi;
2510
+ let paramMatch;
2511
+ while ((paramMatch = paramRegex.exec(body)) !== null) {
2512
+ params[paramMatch[1].toLowerCase()] = paramMatch[2].trim();
2513
+ }
2514
+ if (funcName === "ripgrep") {
2515
+ const pattern = params.pattern;
2516
+ if (!pattern) continue;
2517
+ const args = {
2518
+ pattern,
2519
+ path: params.path || ".",
2520
+ ...params.glob && { glob: params.glob },
2521
+ ...params.context_lines && { context_lines: parseInt(params.context_lines, 10) },
2522
+ ...params.case_sensitive && { case_sensitive: params.case_sensitive === "true" }
2523
+ };
2524
+ tools.push({ name: "grep", arguments: args });
2525
+ } else if (funcName === "list_directory") {
2526
+ const command = params.command;
2527
+ const directPath = params.path;
2528
+ let dirPath = directPath || ".";
2529
+ if (!directPath && command) {
2530
+ const tokens = command.trim().split(/\s+/);
2531
+ const pathTokens = tokens.slice(1).filter((t) => !t.startsWith("-") && !t.startsWith("|") && !t.startsWith("\\("));
2532
+ if (pathTokens.length > 0) {
2533
+ dirPath = pathTokens[0];
2534
+ }
2535
+ }
2536
+ tools.push({ name: "list_directory", arguments: { path: dirPath, pattern: params.pattern || null } });
2537
+ } else if (funcName === "read") {
2538
+ const filePath = params.path;
2539
+ if (!filePath) continue;
2540
+ const args = { path: filePath };
2541
+ const linesStr = params.lines;
2542
+ if (linesStr) {
2543
+ const ranges = [];
2544
+ for (const rangeStr of linesStr.split(",")) {
2545
+ const trimmed = rangeStr.trim();
2546
+ if (!trimmed) continue;
2547
+ const [s, e] = trimmed.split("-").map((v) => parseInt(v.trim(), 10));
2548
+ if (Number.isFinite(s) && Number.isFinite(e)) {
2549
+ ranges.push([s, e]);
2550
+ } else if (Number.isFinite(s)) {
2551
+ ranges.push([s, s]);
2552
+ }
2553
+ }
2554
+ if (ranges.length === 1) {
2555
+ args.start = ranges[0][0];
2556
+ args.end = ranges[0][1];
2557
+ } else if (ranges.length > 1) {
2558
+ args.lines = ranges;
2559
+ }
2560
+ }
2561
+ tools.push({ name: "read", arguments: args });
2562
+ } else if (funcName === "finish") {
2563
+ if (params.result && !params.files) {
2564
+ tools.push({ name: "finish", arguments: { files: [], textResult: params.result } });
2565
+ continue;
2566
+ }
2567
+ const filesStr = params.files;
2568
+ if (!filesStr) {
2569
+ tools.push({ name: "finish", arguments: { files: [], textResult: "No relevant code found." } });
2570
+ continue;
2571
+ }
2572
+ const files = [];
2573
+ for (const line of filesStr.split("\n")) {
2574
+ const trimmed = line.trim();
2575
+ if (!trimmed) continue;
2576
+ const colonIdx = trimmed.indexOf(":");
2577
+ if (colonIdx === -1) {
2578
+ files.push({ path: trimmed, lines: "*" });
2579
+ } else {
2580
+ const filePath = trimmed.slice(0, colonIdx);
2581
+ const rangesPart = trimmed.slice(colonIdx + 1);
2582
+ const ranges = [];
2583
+ for (const rangeStr of rangesPart.split(",")) {
2584
+ const rt = rangeStr.trim();
2585
+ if (!rt || rt === "*") {
2586
+ files.push({ path: filePath, lines: "*" });
2587
+ break;
2588
+ }
2589
+ const [s, e] = rt.split("-").map((v) => parseInt(v.trim(), 10));
2590
+ if (Number.isFinite(s) && Number.isFinite(e)) {
2591
+ ranges.push([s, e]);
2592
+ } else if (Number.isFinite(s)) {
2593
+ ranges.push([s, s]);
2594
+ }
2595
+ }
2596
+ if (ranges.length > 0) {
2597
+ files.push({ path: filePath, lines: ranges });
2598
+ } else if (!files.some((f) => f.path === filePath)) {
2599
+ files.push({ path: filePath, lines: "*" });
2600
+ }
2601
+ }
2602
+ }
2603
+ if (files.length > 0) {
2604
+ tools.push({ name: "finish", arguments: { files } });
2605
+ } else {
2606
+ tools.push({ name: "finish", arguments: { files: [], textResult: filesStr } });
2578
2607
  }
2579
2608
  }
2580
- files.push({ path: filePath, lines: ranges.length > 0 ? ranges : "*" });
2581
2609
  }
2582
- return files;
2583
- }
2584
- function extractPathFromCommand(command) {
2585
- const tokens = command.trim().split(/\s+/);
2586
- const pathTokens = tokens.slice(1).filter((t) => !t.startsWith("-") && !t.startsWith("|") && !t.startsWith("\\("));
2587
- return pathTokens[0] || ".";
2610
+ return tools;
2588
2611
  }
2612
+ var LLMResponseParser = class {
2613
+ parse(text) {
2614
+ if (typeof text !== "string") {
2615
+ throw new TypeError("Command text must be a string.");
2616
+ }
2617
+ const withoutThink = text.replace(/<think>[\s\S]*?<\/think>/gi, "");
2618
+ return parseQwen3ToolCalls(withoutThink);
2619
+ }
2620
+ };
2589
2621
 
2590
2622
  // tools/warp_grep/agent/tools/grep.ts
2591
2623
  async function toolGrep(provider, args) {
@@ -2636,42 +2668,29 @@ async function toolRead(provider, args) {
2636
2668
 
2637
2669
  // tools/warp_grep/agent/tools/list_directory.ts
2638
2670
  init_config();
2639
- var import_path2 = __toESM(require("path"), 1);
2640
- async function toolListDirectory(provider, args, repoRoot) {
2641
- const maxResults = args.maxResults ?? AGENT_CONFIG.MAX_LIST_RESULTS;
2642
- const maxDepth = args.maxDepth ?? AGENT_CONFIG.MAX_LIST_DEPTH;
2643
- const entries = await provider.listDirectory({
2644
- path: args.path,
2645
- pattern: args.pattern ?? null,
2646
- maxResults,
2647
- maxDepth
2648
- });
2649
- if (!entries.length) return "empty";
2650
- if (repoRoot) {
2651
- const absRoot = import_path2.default.resolve(repoRoot);
2652
- const lines = entries.map((e) => import_path2.default.join(absRoot, e.path));
2653
- return lines.join("\n");
2654
- }
2655
- return entries.map((e) => e.path).join("\n");
2656
- }
2657
-
2658
- // tools/warp_grep/agent/tools/glob.ts
2659
- async function toolGlob(provider, args) {
2660
- const res = await provider.glob(args);
2661
- if (res.error) {
2662
- return res.error;
2663
- }
2664
- if (!res.files.length) {
2665
- return "no matches";
2666
- }
2667
- const header = `Found ${res.totalFound} file(s) matching "${args.pattern}" within ${res.searchDir}, sorted by modification time (newest first):`;
2668
- const body = res.files.join("\n");
2669
- const truncated = res.totalFound > res.files.length ? `
2670
- [${res.totalFound - res.files.length} files truncated]` : "";
2671
- return `${header}
2672
- ---
2673
- ${body}
2674
- ---${truncated}`;
2671
+ async function toolListDirectory(provider, args) {
2672
+ const maxResults = args.maxResults ?? AGENT_CONFIG.MAX_OUTPUT_LINES;
2673
+ const initialDepth = args.maxDepth ?? AGENT_CONFIG.MAX_LIST_DEPTH;
2674
+ async function getListRecursive(currentDepth) {
2675
+ const entries = await provider.listDirectory({
2676
+ path: args.path,
2677
+ pattern: args.pattern ?? null,
2678
+ maxResults,
2679
+ maxDepth: currentDepth
2680
+ });
2681
+ if (entries.length >= maxResults && currentDepth > 0) {
2682
+ return getListRecursive(currentDepth - 1);
2683
+ }
2684
+ return { entries };
2685
+ }
2686
+ const { entries: list } = await getListRecursive(initialDepth);
2687
+ if (!list.length) return "empty";
2688
+ const tree = list.map((e) => {
2689
+ const indent = " ".repeat(e.depth);
2690
+ const name = e.type === "dir" ? `${e.name}/` : e.name;
2691
+ return `${indent}${name}`;
2692
+ }).join("\n");
2693
+ return tree;
2675
2694
  }
2676
2695
 
2677
2696
  // tools/warp_grep/agent/tools/finish.ts
@@ -2732,21 +2751,32 @@ function mergeRanges(ranges) {
2732
2751
  return merged;
2733
2752
  }
2734
2753
 
2754
+ // tools/warp_grep/agent/formatter.ts
2755
+ var ToolOutputFormatter = class {
2756
+ format(toolName, _args, output, options = {}) {
2757
+ const name = (toolName ?? "").trim();
2758
+ if (!name) {
2759
+ return "";
2760
+ }
2761
+ const payload = output?.toString?.()?.trim?.() ?? "";
2762
+ const isError = Boolean(options.isError);
2763
+ if (!payload && !isError) {
2764
+ return "";
2765
+ }
2766
+ return `<tool_response>
2767
+ ${payload}
2768
+ </tool_response>`;
2769
+ }
2770
+ };
2771
+ var sharedFormatter = new ToolOutputFormatter();
2772
+ function formatAgentToolOutput(toolName, args, output, options = {}) {
2773
+ return sharedFormatter.format(toolName, args, output, options);
2774
+ }
2775
+
2735
2776
  // tools/warp_grep/agent/helpers.ts
2736
- var import_path3 = __toESM(require("path"), 1);
2777
+ var import_path2 = __toESM(require("path"), 1);
2737
2778
  init_config();
2738
2779
  var TRUNCATED_MARKER = "[truncated for context limit]";
2739
- function getMessageSize(m) {
2740
- if (m.role === "tool") return m.content.length;
2741
- if (m.role === "assistant") {
2742
- let size = typeof m.content === "string" ? m.content.length : 0;
2743
- if (m.tool_calls) {
2744
- size += m.tool_calls.reduce((s, tc) => s + tc.function.name.length + tc.function.arguments.length, 0);
2745
- }
2746
- return size;
2747
- }
2748
- return m.content.length;
2749
- }
2750
2780
  function formatTurnMessage(turnsUsed, maxTurns) {
2751
2781
  const turnsRemaining = maxTurns - turnsUsed;
2752
2782
  if (turnsRemaining === 1) {
@@ -2757,7 +2787,7 @@ You have used ${turnsUsed} turns, you only have 1 turn remaining. You have run o
2757
2787
  You have used ${turnsUsed} turn${turnsUsed === 1 ? "" : "s"} and have ${turnsRemaining} remaining`;
2758
2788
  }
2759
2789
  function calculateContextBudget(messages) {
2760
- const totalChars = messages.reduce((sum, m) => sum + getMessageSize(m), 0);
2790
+ const totalChars = messages.reduce((sum, m) => sum + m.content.length, 0);
2761
2791
  const maxChars = AGENT_CONFIG.MAX_CONTEXT_CHARS;
2762
2792
  const percent = Math.round(totalChars / maxChars * 100);
2763
2793
  const usedK = Math.round(totalChars / 1e3);
@@ -2766,21 +2796,24 @@ function calculateContextBudget(messages) {
2766
2796
  }
2767
2797
  async function buildInitialState(repoRoot, searchTerm, provider, options) {
2768
2798
  const budget = calculateContextBudget([]);
2769
- const turnTag = `You have used 0 turns and have ${AGENT_CONFIG.MAX_TURNS} remaining`;
2799
+ const turnTag = `Turn 0/${AGENT_CONFIG.MAX_TURNS}`;
2770
2800
  const treeDepth = options?.search_type === "node_modules" ? 1 : 2;
2771
- const absRoot = import_path3.default.resolve(repoRoot);
2772
2801
  try {
2773
2802
  const entries = await provider.listDirectory({
2774
2803
  path: ".",
2775
2804
  maxResults: AGENT_CONFIG.MAX_OUTPUT_LINES,
2776
2805
  maxDepth: treeDepth
2777
2806
  });
2778
- const lines = [absRoot];
2779
- for (const e of entries) {
2780
- lines.push(import_path3.default.join(absRoot, e.path));
2781
- }
2807
+ const treeLines = entries.map((e) => {
2808
+ const indent = " ".repeat(e.depth);
2809
+ const name = e.type === "dir" ? `${e.name}/` : e.name;
2810
+ return `${indent}${name}`;
2811
+ });
2812
+ const repoName = import_path2.default.basename(repoRoot);
2813
+ const treeOutput = treeLines.length > 0 ? `${repoName}/
2814
+ ${treeLines.join("\n")}` : `${repoName}/`;
2782
2815
  return `<repo_structure>
2783
- ${lines.join("\n")}
2816
+ ${treeOutput}
2784
2817
  </repo_structure>
2785
2818
 
2786
2819
  <search_string>
@@ -2789,8 +2822,9 @@ ${searchTerm}
2789
2822
  ${budget}
2790
2823
  ${turnTag}`;
2791
2824
  } catch {
2825
+ const repoName = import_path2.default.basename(repoRoot);
2792
2826
  return `<repo_structure>
2793
- ${absRoot}
2827
+ ${repoName}/
2794
2828
  </repo_structure>
2795
2829
 
2796
2830
  <search_string>
@@ -2801,32 +2835,26 @@ ${turnTag}`;
2801
2835
  }
2802
2836
  }
2803
2837
  function enforceContextLimit(messages, maxChars = AGENT_CONFIG.MAX_CONTEXT_CHARS) {
2804
- const getTotalChars = () => messages.reduce((sum, m) => sum + getMessageSize(m), 0);
2838
+ const getTotalChars = () => messages.reduce((sum, m) => sum + m.content.length, 0);
2805
2839
  if (getTotalChars() <= maxChars) {
2806
2840
  return messages;
2807
2841
  }
2808
- const truncatableIndices = [];
2842
+ const userIndices = [];
2809
2843
  let firstUserSkipped = false;
2810
2844
  for (let i = 0; i < messages.length; i++) {
2811
- const m = messages[i];
2812
- if (m.role === "tool") {
2813
- truncatableIndices.push(i);
2814
- } else if (m.role === "user") {
2845
+ if (messages[i].role === "user") {
2815
2846
  if (!firstUserSkipped) {
2816
2847
  firstUserSkipped = true;
2817
2848
  continue;
2818
2849
  }
2819
- truncatableIndices.push(i);
2850
+ userIndices.push(i);
2820
2851
  }
2821
2852
  }
2822
- for (const idx of truncatableIndices) {
2853
+ for (const idx of userIndices) {
2823
2854
  if (getTotalChars() <= maxChars) {
2824
2855
  break;
2825
2856
  }
2826
- const m = messages[idx];
2827
- if (m.role === "tool" && m.content !== TRUNCATED_MARKER) {
2828
- messages[idx] = { role: "tool", tool_call_id: m.tool_call_id, content: TRUNCATED_MARKER };
2829
- } else if (m.role === "user" && m.content !== TRUNCATED_MARKER) {
2857
+ if (messages[idx].content !== TRUNCATED_MARKER) {
2830
2858
  messages[idx] = { role: "user", content: TRUNCATED_MARKER };
2831
2859
  }
2832
2860
  }
@@ -2836,115 +2864,9 @@ function enforceContextLimit(messages, maxChars = AGENT_CONFIG.MAX_CONTEXT_CHARS
2836
2864
  // tools/warp_grep/agent/runner.ts
2837
2865
  var import_openai2 = __toESM(require("openai"), 1);
2838
2866
  init_version();
2839
- var import_path4 = __toESM(require("path"), 1);
2867
+ var import_path3 = __toESM(require("path"), 1);
2868
+ var parser = new LLMResponseParser();
2840
2869
  var DEFAULT_API_URL3 = "https://api.morphllm.com";
2841
- var TOOL_SPECS = [
2842
- {
2843
- type: "function",
2844
- function: {
2845
- name: "list_directory",
2846
- description: "Execute ls or find commands to explore directory structure. Max 500 results. Common junk directories are excluded automatically.",
2847
- parameters: {
2848
- type: "object",
2849
- properties: {
2850
- command: {
2851
- type: "string",
2852
- description: "Full ls or find command (e.g. ls -la src/, find . -maxdepth 2 -type f -name '*.py', find . -type d, ls -d */)."
2853
- }
2854
- },
2855
- required: ["command"]
2856
- }
2857
- }
2858
- },
2859
- {
2860
- type: "function",
2861
- function: {
2862
- name: "grep_search",
2863
- description: "Search for a regex pattern in file contents. Returns matching lines with file paths and line numbers. Case-insensitive. Respects .gitignore.",
2864
- parameters: {
2865
- type: "object",
2866
- properties: {
2867
- pattern: {
2868
- type: "string",
2869
- description: "Regex pattern to search for in file contents (e.g. 'class\\s+\\w+Error', 'import|require|from', 'def (get|set|update)_user')."
2870
- },
2871
- path: {
2872
- type: "string",
2873
- description: "File or directory to search in. Defaults to current working directory."
2874
- },
2875
- glob: {
2876
- type: "string",
2877
- description: "Glob pattern to filter files (e.g. '*.py', '*.{ts,tsx,js,jsx,py,go}', 'src/**/*.go', '!*.test.*')."
2878
- },
2879
- limit: {
2880
- type: "integer",
2881
- description: "Limit output to first N matching lines. Shows all matches if not specified."
2882
- }
2883
- },
2884
- required: ["pattern"]
2885
- }
2886
- }
2887
- },
2888
- {
2889
- type: "function",
2890
- function: {
2891
- name: "glob",
2892
- description: "Find files by name/extension using glob patterns. Returns absolute paths sorted by modification time (newest first). Respects .gitignore. Max 100 results.",
2893
- parameters: {
2894
- type: "object",
2895
- properties: {
2896
- pattern: {
2897
- type: "string",
2898
- description: "Glob pattern to match files (e.g. '*.py', 'src/**/*.js', '*.{ts,tsx}', 'test_*.py')."
2899
- },
2900
- path: {
2901
- type: "string",
2902
- description: "Directory to search in. Defaults to repository root."
2903
- }
2904
- },
2905
- required: ["pattern"]
2906
- }
2907
- }
2908
- },
2909
- {
2910
- type: "function",
2911
- function: {
2912
- name: "read",
2913
- description: "Read entire files or specific line ranges using absolute paths.",
2914
- parameters: {
2915
- type: "object",
2916
- properties: {
2917
- path: {
2918
- type: "string",
2919
- description: "File path to read, using absolute path (e.g. '/home/ubuntu/repo/src/main.py' or windows path)."
2920
- },
2921
- lines: {
2922
- type: "string",
2923
- description: "Optional line range (e.g. '1-50' or '1-20,45-80'). Omit to read entire file."
2924
- }
2925
- },
2926
- required: ["path"]
2927
- }
2928
- }
2929
- },
2930
- {
2931
- type: "function",
2932
- function: {
2933
- name: "finish",
2934
- description: "Submit final answer with all relevant code locations. Include imports and over-include rather than miss context.",
2935
- parameters: {
2936
- type: "object",
2937
- properties: {
2938
- files: {
2939
- type: "string",
2940
- 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."
2941
- }
2942
- },
2943
- required: ["files"]
2944
- }
2945
- }
2946
- }
2947
- ];
2948
2870
  async function callModel(messages, model, options = {}) {
2949
2871
  const baseUrl = options.morphApiUrl || DEFAULT_API_URL3;
2950
2872
  const apiKey = options.morphApiKey || process.env.MORPH_API_KEY || "";
@@ -2965,9 +2887,8 @@ async function callModel(messages, model, options = {}) {
2965
2887
  data = await client.chat.completions.create({
2966
2888
  model,
2967
2889
  temperature: 0,
2968
- max_tokens: 2048,
2890
+ max_tokens: 1024,
2969
2891
  messages,
2970
- tools: TOOL_SPECS,
2971
2892
  ...options.search_type ? { search_type: options.search_type } : {}
2972
2893
  });
2973
2894
  } catch (error) {
@@ -2979,87 +2900,187 @@ async function callModel(messages, model, options = {}) {
2979
2900
  throw error;
2980
2901
  }
2981
2902
  const choice = data?.choices?.[0];
2982
- const message = choice?.message;
2983
- if (!message) {
2984
- if (attempt === MAX_EMPTY_RETRIES) {
2985
- throw new Error("Invalid response from model: no message in response");
2986
- }
2987
- await new Promise((resolve2) => setTimeout(resolve2, 200));
2988
- continue;
2989
- }
2990
- const toolCalls = (message.tool_calls || []).map((tc) => ({
2991
- id: tc.id,
2992
- type: "function",
2993
- function: { name: tc.function.name, arguments: tc.function.arguments }
2994
- }));
2995
- if (message.content || toolCalls.length > 0) {
2996
- return { content: message.content ?? null, tool_calls: toolCalls };
2903
+ const content = choice?.message?.content;
2904
+ if (content && typeof content === "string") {
2905
+ return content;
2997
2906
  }
2998
2907
  if (attempt === MAX_EMPTY_RETRIES) {
2999
2908
  const finishReason = choice?.finish_reason ?? "unknown";
2909
+ const hasToolCalls = Array.isArray(choice?.message?.tool_calls) && choice.message.tool_calls.length > 0;
2910
+ const choicesLen = data?.choices?.length ?? 0;
2911
+ const contentType = content === null ? "null" : content === void 0 ? "undefined" : typeof content;
3000
2912
  throw new Error(
3001
- `Invalid response from model: no content and no tool_calls, finish_reason=${finishReason}`
2913
+ `Invalid response from model: content=${contentType}, finish_reason=${finishReason}, has_tool_calls=${hasToolCalls}, choices_length=${choicesLen}`
3002
2914
  );
3003
2915
  }
3004
2916
  await new Promise((resolve2) => setTimeout(resolve2, 200));
3005
2917
  }
3006
2918
  throw new Error("Invalid response from model");
3007
2919
  }
3008
- function safeParseJSON(s) {
3009
- try {
3010
- return JSON.parse(s);
3011
- } catch {
3012
- return {};
3013
- }
3014
- }
3015
- async function executeTool(provider, name, args, repoRoot) {
3016
- switch (name) {
3017
- case "grep_search": {
3018
- const grepArgs = {
3019
- pattern: args.pattern,
3020
- path: args.path || "."
3021
- };
3022
- if (args.glob) grepArgs.glob = args.glob;
3023
- if (args.case_sensitive !== void 0) grepArgs.case_sensitive = args.case_sensitive;
3024
- const result = await toolGrep(provider, grepArgs);
3025
- let output = result.output;
3026
- if (args.limit && typeof args.limit === "number") {
3027
- const lines = output.split("\n");
3028
- if (lines.length > args.limit) {
3029
- output = lines.slice(0, args.limit).join("\n") + `
3030
- ... (truncated at ${args.limit} lines)`;
3031
- }
3032
- }
3033
- return output;
2920
+ async function runWarpGrep(config) {
2921
+ const totalStart = Date.now();
2922
+ const timeoutMs = config.timeout ?? AGENT_CONFIG.TIMEOUT_MS;
2923
+ const timings = { turns: [], timeout_ms: timeoutMs };
2924
+ const repoRoot = import_path3.default.resolve(config.repoRoot || process.cwd());
2925
+ const model = config.model || DEFAULT_MODEL;
2926
+ const messages = [];
2927
+ const maxTurns = AGENT_CONFIG.MAX_TURNS;
2928
+ const initialStateStart = Date.now();
2929
+ const initialState = await buildInitialState(repoRoot, config.searchTerm, config.provider, { search_type: config.search_type });
2930
+ timings.initial_state_ms = Date.now() - initialStateStart;
2931
+ messages.push({ role: "user", content: initialState });
2932
+ const provider = config.provider;
2933
+ const errors = [];
2934
+ let finishMeta;
2935
+ let terminationReason = "terminated";
2936
+ for (let turn = 1; turn <= maxTurns; turn += 1) {
2937
+ const turnMetrics = { turn, morph_api_ms: 0, local_tools_ms: 0 };
2938
+ enforceContextLimit(messages);
2939
+ const modelCallStart = Date.now();
2940
+ const assistantContent = await callModel(messages, model, {
2941
+ morphApiKey: config.morphApiKey,
2942
+ morphApiUrl: config.morphApiUrl,
2943
+ retryConfig: config.retryConfig,
2944
+ timeout: timeoutMs,
2945
+ search_type: config.search_type
2946
+ }).catch((e) => {
2947
+ const errMsg = e instanceof Error ? e.message : String(e);
2948
+ console.error(`[warp_grep] Morph API call failed on turn ${turn}:`, errMsg);
2949
+ errors.push({ message: errMsg });
2950
+ return "";
2951
+ });
2952
+ turnMetrics.morph_api_ms = Date.now() - modelCallStart;
2953
+ if (!assistantContent) {
2954
+ console.error(`[warp_grep] Empty response from Morph API on turn ${turn}. Errors so far:`, errors);
2955
+ timings.turns.push(turnMetrics);
2956
+ break;
3034
2957
  }
3035
- case "glob": {
3036
- return toolGlob(provider, {
3037
- pattern: args.pattern,
3038
- path: args.path
3039
- });
2958
+ messages.push({ role: "assistant", content: assistantContent });
2959
+ const toolCalls = parser.parse(assistantContent);
2960
+ if (toolCalls.length === 0) {
2961
+ console.error(`[warp_grep] No tool calls parsed on turn ${turn}. Assistant content (first 500 chars):`, assistantContent.slice(0, 500));
2962
+ 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" });
2963
+ terminationReason = "terminated";
2964
+ timings.turns.push(turnMetrics);
2965
+ break;
3040
2966
  }
3041
- case "list_directory": {
3042
- const dirPath = extractPathFromCommand(args.command || ".");
3043
- return toolListDirectory(provider, { path: dirPath }, repoRoot);
2967
+ const finishCalls = toolCalls.filter((c) => c.name === "finish");
2968
+ const grepCalls = toolCalls.filter((c) => c.name === "grep");
2969
+ const listDirCalls = toolCalls.filter((c) => c.name === "list_directory");
2970
+ const readCalls = toolCalls.filter((c) => c.name === "read");
2971
+ const skipCalls = toolCalls.filter((c) => c.name === "_skip");
2972
+ const formatted = [];
2973
+ for (const c of skipCalls) {
2974
+ const msg = c.arguments?.message || "Command skipped due to parsing error";
2975
+ formatted.push(msg);
2976
+ }
2977
+ const allPromises = [];
2978
+ for (const c of grepCalls) {
2979
+ const args = c.arguments ?? {};
2980
+ allPromises.push(
2981
+ toolGrep(provider, args).then(
2982
+ ({ output }) => formatAgentToolOutput("grep", args, output),
2983
+ (err) => formatAgentToolOutput("grep", args, String(err), { isError: true })
2984
+ )
2985
+ );
3044
2986
  }
3045
- case "read": {
3046
- const readArgs = {
3047
- path: args.path
3048
- };
3049
- if (args.lines && typeof args.lines === "string") {
3050
- Object.assign(readArgs, parseReadLines(args.lines));
2987
+ for (const c of listDirCalls) {
2988
+ const args = c.arguments ?? {};
2989
+ allPromises.push(
2990
+ toolListDirectory(provider, args).then(
2991
+ (p) => formatAgentToolOutput("list_directory", args, p),
2992
+ (err) => formatAgentToolOutput("list_directory", args, String(err), { isError: true })
2993
+ )
2994
+ );
2995
+ }
2996
+ for (const c of readCalls) {
2997
+ const args = c.arguments ?? {};
2998
+ allPromises.push(
2999
+ toolRead(provider, args).then(
3000
+ (p) => formatAgentToolOutput("read", args, p),
3001
+ (err) => formatAgentToolOutput("read", args, String(err), { isError: true })
3002
+ )
3003
+ );
3004
+ }
3005
+ const toolExecStart = Date.now();
3006
+ const allResults = await Promise.all(allPromises);
3007
+ turnMetrics.local_tools_ms = Date.now() - toolExecStart;
3008
+ for (const result of allResults) {
3009
+ formatted.push(result);
3010
+ }
3011
+ if (formatted.length > 0) {
3012
+ const turnMessage = formatTurnMessage(turn, maxTurns);
3013
+ const contextBudget = calculateContextBudget(messages);
3014
+ messages.push({ role: "user", content: formatted.join("\n") + turnMessage + "\n" + contextBudget });
3015
+ }
3016
+ timings.turns.push(turnMetrics);
3017
+ if (finishCalls.length) {
3018
+ const fc = finishCalls[0];
3019
+ const files = fc.arguments?.files ?? [];
3020
+ const textResult = fc.arguments?.textResult;
3021
+ finishMeta = { files };
3022
+ terminationReason = "completed";
3023
+ if (files.length === 0) {
3024
+ const payload2 = textResult || "No relevant code found.";
3025
+ timings.turns.push(turnMetrics);
3026
+ timings.total_ms = Date.now() - totalStart;
3027
+ return {
3028
+ terminationReason: "completed",
3029
+ messages,
3030
+ finish: { payload: payload2, metadata: finishMeta },
3031
+ timings
3032
+ };
3051
3033
  }
3052
- return toolRead(provider, readArgs);
3034
+ break;
3053
3035
  }
3054
- default:
3055
- return `Unknown tool: ${name}`;
3056
3036
  }
3037
+ if (terminationReason !== "completed" || !finishMeta) {
3038
+ timings.total_ms = Date.now() - totalStart;
3039
+ return { terminationReason, messages, errors, timings };
3040
+ }
3041
+ const parts = ["Relevant context found:"];
3042
+ for (const f of finishMeta.files) {
3043
+ const ranges = f.lines === "*" ? "*" : Array.isArray(f.lines) ? f.lines.map(([s, e]) => `${s}-${e}`).join(", ") : "*";
3044
+ parts.push(`- ${f.path}: ${ranges}`);
3045
+ }
3046
+ const payload = parts.join("\n");
3047
+ const finishResolutionStart = Date.now();
3048
+ const fileReadErrors = [];
3049
+ const resolved = await readFinishFiles(
3050
+ repoRoot,
3051
+ finishMeta.files,
3052
+ async (p, s, e) => {
3053
+ try {
3054
+ const rr = await provider.read({ path: p, start: s, end: e });
3055
+ return rr.lines.map((l) => {
3056
+ const idx = l.indexOf("|");
3057
+ return idx >= 0 ? l.slice(idx + 1) : l;
3058
+ });
3059
+ } catch (err) {
3060
+ const errorMsg = err instanceof Error ? err.message : String(err);
3061
+ fileReadErrors.push({ path: p, error: errorMsg });
3062
+ console.error(`[warp_grep] Failed to read file: ${p} - ${errorMsg}`);
3063
+ return [`[couldn't find: ${p}]`];
3064
+ }
3065
+ }
3066
+ );
3067
+ timings.finish_resolution_ms = Date.now() - finishResolutionStart;
3068
+ if (fileReadErrors.length > 0) {
3069
+ errors.push(...fileReadErrors.map((e) => ({ message: `File read error: ${e.path} - ${e.error}` })));
3070
+ }
3071
+ timings.total_ms = Date.now() - totalStart;
3072
+ return {
3073
+ terminationReason: "completed",
3074
+ messages,
3075
+ finish: { payload, metadata: finishMeta, resolved },
3076
+ timings
3077
+ };
3057
3078
  }
3058
3079
  async function* runWarpGrepStreaming(config) {
3059
3080
  const totalStart = Date.now();
3060
3081
  const timeoutMs = config.timeout ?? AGENT_CONFIG.TIMEOUT_MS;
3061
3082
  const timings = { turns: [], timeout_ms: timeoutMs };
3062
- const repoRoot = import_path4.default.resolve(config.repoRoot || process.cwd());
3083
+ const repoRoot = import_path3.default.resolve(config.repoRoot || process.cwd());
3063
3084
  const model = config.model || DEFAULT_MODEL;
3064
3085
  const messages = [];
3065
3086
  const maxTurns = AGENT_CONFIG.MAX_TURNS;
@@ -3075,7 +3096,7 @@ async function* runWarpGrepStreaming(config) {
3075
3096
  const turnMetrics = { turn, morph_api_ms: 0, local_tools_ms: 0 };
3076
3097
  enforceContextLimit(messages);
3077
3098
  const modelCallStart = Date.now();
3078
- const response = await callModel(messages, model, {
3099
+ const assistantContent = await callModel(messages, model, {
3079
3100
  morphApiKey: config.morphApiKey,
3080
3101
  morphApiUrl: config.morphApiUrl,
3081
3102
  retryConfig: config.retryConfig,
@@ -3083,45 +3104,90 @@ async function* runWarpGrepStreaming(config) {
3083
3104
  search_type: config.search_type
3084
3105
  }).catch((e) => {
3085
3106
  const errMsg = e instanceof Error ? e.message : String(e);
3086
- console.error(`[warp_grep] Morph API call failed on turn ${turn}:`, errMsg);
3107
+ console.error(`[warp_grep:stream] Morph API call failed on turn ${turn}:`, errMsg);
3087
3108
  errors.push({ message: errMsg });
3088
- return null;
3109
+ return "";
3089
3110
  });
3090
3111
  turnMetrics.morph_api_ms = Date.now() - modelCallStart;
3091
- if (!response) {
3112
+ if (!assistantContent) {
3113
+ console.error(`[warp_grep:stream] Empty response from Morph API on turn ${turn}. Errors so far:`, errors);
3092
3114
  timings.turns.push(turnMetrics);
3093
3115
  break;
3094
3116
  }
3095
- const toolCalls = response.tool_calls;
3096
- messages.push({
3097
- role: "assistant",
3098
- content: response.content,
3099
- ...toolCalls.length > 0 ? { tool_calls: toolCalls } : {}
3100
- });
3117
+ messages.push({ role: "assistant", content: assistantContent });
3118
+ const toolCalls = parser.parse(assistantContent);
3101
3119
  if (toolCalls.length === 0) {
3102
- console.error(`[warp_grep] No tool calls on turn ${turn}. Content: ${(response.content || "").slice(0, 500)}`);
3103
- errors.push({ message: "No tool calls produced by the model." });
3120
+ console.error(`[warp_grep:stream] No tool calls parsed on turn ${turn}. Assistant content (first 500 chars):`, assistantContent.slice(0, 500));
3121
+ 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" });
3104
3122
  terminationReason = "terminated";
3105
3123
  timings.turns.push(turnMetrics);
3106
3124
  break;
3107
3125
  }
3108
3126
  yield {
3109
3127
  turn,
3110
- toolCalls: toolCalls.map((tc) => ({
3111
- name: tc.function.name,
3112
- arguments: safeParseJSON(tc.function.arguments)
3128
+ toolCalls: toolCalls.map((c) => ({
3129
+ name: c.name,
3130
+ arguments: c.arguments ?? {}
3113
3131
  }))
3114
3132
  };
3115
- const finishCall = toolCalls.find((tc) => tc.function.name === "finish");
3116
- if (finishCall) {
3117
- const args = safeParseJSON(finishCall.function.arguments);
3118
- const filesStr = args.files || "";
3119
- const files = parseFinishFiles(filesStr);
3133
+ const finishCalls = toolCalls.filter((c) => c.name === "finish");
3134
+ const grepCalls = toolCalls.filter((c) => c.name === "grep");
3135
+ const listDirCalls = toolCalls.filter((c) => c.name === "list_directory");
3136
+ const readCalls = toolCalls.filter((c) => c.name === "read");
3137
+ const skipCalls = toolCalls.filter((c) => c.name === "_skip");
3138
+ const formatted = [];
3139
+ for (const c of skipCalls) {
3140
+ const msg = c.arguments?.message || "Command skipped due to parsing error";
3141
+ formatted.push(msg);
3142
+ }
3143
+ const allPromises = [];
3144
+ for (const c of grepCalls) {
3145
+ const args = c.arguments ?? {};
3146
+ allPromises.push(
3147
+ toolGrep(provider, args).then(
3148
+ ({ output }) => formatAgentToolOutput("grep", args, output),
3149
+ (err) => formatAgentToolOutput("grep", args, String(err), { isError: true })
3150
+ )
3151
+ );
3152
+ }
3153
+ for (const c of listDirCalls) {
3154
+ const args = c.arguments ?? {};
3155
+ allPromises.push(
3156
+ toolListDirectory(provider, args).then(
3157
+ (p) => formatAgentToolOutput("list_directory", args, p),
3158
+ (err) => formatAgentToolOutput("list_directory", args, String(err), { isError: true })
3159
+ )
3160
+ );
3161
+ }
3162
+ for (const c of readCalls) {
3163
+ const args = c.arguments ?? {};
3164
+ allPromises.push(
3165
+ toolRead(provider, args).then(
3166
+ (p) => formatAgentToolOutput("read", args, p),
3167
+ (err) => formatAgentToolOutput("read", args, String(err), { isError: true })
3168
+ )
3169
+ );
3170
+ }
3171
+ const toolExecStart = Date.now();
3172
+ const allResults = await Promise.all(allPromises);
3173
+ turnMetrics.local_tools_ms = Date.now() - toolExecStart;
3174
+ for (const result of allResults) {
3175
+ formatted.push(result);
3176
+ }
3177
+ if (formatted.length > 0) {
3178
+ const turnMessage = formatTurnMessage(turn, maxTurns);
3179
+ const contextBudget = calculateContextBudget(messages);
3180
+ messages.push({ role: "user", content: formatted.join("\n") + turnMessage + "\n" + contextBudget });
3181
+ }
3182
+ timings.turns.push(turnMetrics);
3183
+ if (finishCalls.length) {
3184
+ const fc = finishCalls[0];
3185
+ const files = fc.arguments?.files ?? [];
3186
+ const textResult = fc.arguments?.textResult;
3120
3187
  finishMeta = { files };
3121
3188
  terminationReason = "completed";
3122
3189
  if (files.length === 0) {
3123
- const payload2 = filesStr || "No relevant code found.";
3124
- timings.turns.push(turnMetrics);
3190
+ const payload2 = textResult || "No relevant code found.";
3125
3191
  timings.total_ms = Date.now() - totalStart;
3126
3192
  return {
3127
3193
  terminationReason: "completed",
@@ -3130,25 +3196,8 @@ async function* runWarpGrepStreaming(config) {
3130
3196
  timings
3131
3197
  };
3132
3198
  }
3133
- timings.turns.push(turnMetrics);
3134
3199
  break;
3135
3200
  }
3136
- const toolExecStart = Date.now();
3137
- const results = await Promise.all(
3138
- toolCalls.map(async (tc) => {
3139
- const args = safeParseJSON(tc.function.arguments);
3140
- const output = await executeTool(provider, tc.function.name, args, repoRoot).catch((err) => String(err));
3141
- return { tool_call_id: tc.id, content: output };
3142
- })
3143
- );
3144
- turnMetrics.local_tools_ms = Date.now() - toolExecStart;
3145
- for (const result of results) {
3146
- messages.push({ role: "tool", tool_call_id: result.tool_call_id, content: result.content });
3147
- }
3148
- const turnMsg = formatTurnMessage(turn, maxTurns);
3149
- const budget = calculateContextBudget(messages);
3150
- messages.push({ role: "user", content: turnMsg + "\n" + budget });
3151
- timings.turns.push(turnMetrics);
3152
3201
  }
3153
3202
  if (terminationReason !== "completed" || !finishMeta) {
3154
3203
  timings.total_ms = Date.now() - totalStart;
@@ -3192,14 +3241,6 @@ async function* runWarpGrepStreaming(config) {
3192
3241
  timings
3193
3242
  };
3194
3243
  }
3195
- async function runWarpGrep(config) {
3196
- const gen = runWarpGrepStreaming(config);
3197
- let result = await gen.next();
3198
- while (!result.done) {
3199
- result = await gen.next();
3200
- }
3201
- return result.value;
3202
- }
3203
3244
 
3204
3245
  // tools/warp_grep/providers/remote.ts
3205
3246
  init_config();
@@ -3379,35 +3420,6 @@ var RemoteCommandsProvider = class {
3379
3420
  return [];
3380
3421
  }
3381
3422
  }
3382
- /**
3383
- * Glob search - finds files matching a pattern.
3384
- * Falls back to a grep --files approach via the listDir command.
3385
- */
3386
- async glob(params) {
3387
- const searchPath = params.path || this.repoRoot;
3388
- try {
3389
- const stdout = await this.commands.listDir(searchPath, 10);
3390
- const allPaths = (stdout || "").trim().split(/\r?\n/).filter((p) => p.length > 0);
3391
- const globToRegex = (glob) => {
3392
- const escaped = glob.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".");
3393
- return new RegExp(escaped);
3394
- };
3395
- const regex = globToRegex(params.pattern);
3396
- const matched = allPaths.filter((p) => {
3397
- const name = p.split("/").pop() || "";
3398
- return regex.test(name) && !shouldSkip(name);
3399
- });
3400
- const totalFound = matched.length;
3401
- return { files: matched.slice(0, 100), searchDir: searchPath, totalFound };
3402
- } catch (error) {
3403
- return {
3404
- files: [],
3405
- searchDir: searchPath,
3406
- totalFound: 0,
3407
- error: `[GLOB ERROR] ${error instanceof Error ? error.message : String(error)}`
3408
- };
3409
- }
3410
- }
3411
3423
  };
3412
3424
 
3413
3425
  // tools/warp_grep/providers/code_storage_http.ts
@@ -3434,9 +3446,9 @@ function createCodeStorageHttpCommands(config) {
3434
3446
  const { baseUrl, repoId, branch } = config;
3435
3447
  const encodedRepoId = encodeURIComponent(repoId);
3436
3448
  return {
3437
- grep: (pattern, path6, glob) => post(`${baseUrl}/api/code-search/${encodedRepoId}/grep`, { pattern, path: path6, glob, branch }, "grep"),
3438
- read: (path6, start, end) => post(`${baseUrl}/api/code-search/${encodedRepoId}/read`, { path: path6, start, end, branch }, "read"),
3439
- listDir: (path6, maxDepth) => post(`${baseUrl}/api/code-search/${encodedRepoId}/list`, { path: path6, maxDepth, branch }, "list")
3449
+ grep: (pattern, path5, glob) => post(`${baseUrl}/api/code-search/${encodedRepoId}/grep`, { pattern, path: path5, glob, branch }, "grep"),
3450
+ read: (path5, start, end) => post(`${baseUrl}/api/code-search/${encodedRepoId}/read`, { path: path5, start, end, branch }, "read"),
3451
+ listDir: (path5, maxDepth) => post(`${baseUrl}/api/code-search/${encodedRepoId}/list`, { path: path5, maxDepth, branch }, "list")
3440
3452
  };
3441
3453
  }
3442
3454
 
@@ -3819,10 +3831,10 @@ var GitHubClient = class {
3819
3831
  /**
3820
3832
  * Make an authenticated API request
3821
3833
  */
3822
- async request(method, path6, body) {
3823
- const url = `${this.baseUrl}${path6}`;
3834
+ async request(method, path5, body) {
3835
+ const url = `${this.baseUrl}${path5}`;
3824
3836
  if (this.debug) {
3825
- console.log(`[GitHub SDK] ${method} ${path6}`, body || "");
3837
+ console.log(`[GitHub SDK] ${method} ${path5}`, body || "");
3826
3838
  }
3827
3839
  const controller = new AbortController();
3828
3840
  const timeoutId = setTimeout(() => controller.abort(), this.timeout);