@x-code-cli/core 0.2.1 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (129) hide show
  1. package/dist/agent/api-errors.js +1 -1
  2. package/dist/agent/api-errors.js.map +1 -1
  3. package/dist/agent/compression.d.ts +25 -0
  4. package/dist/agent/compression.d.ts.map +1 -0
  5. package/dist/agent/compression.js +105 -0
  6. package/dist/agent/compression.js.map +1 -0
  7. package/dist/agent/diff.js.map +1 -1
  8. package/dist/agent/file-ingest.d.ts +14 -0
  9. package/dist/agent/file-ingest.d.ts.map +1 -1
  10. package/dist/agent/file-ingest.js +125 -34
  11. package/dist/agent/file-ingest.js.map +1 -1
  12. package/dist/agent/light-compact.d.ts.map +1 -1
  13. package/dist/agent/light-compact.js +0 -19
  14. package/dist/agent/light-compact.js.map +1 -1
  15. package/dist/agent/loop-guard.d.ts.map +1 -1
  16. package/dist/agent/loop-guard.js.map +1 -1
  17. package/dist/agent/loop-state.d.ts.map +1 -1
  18. package/dist/agent/loop-state.js +8 -1
  19. package/dist/agent/loop-state.js.map +1 -1
  20. package/dist/agent/loop.d.ts +2 -3
  21. package/dist/agent/loop.d.ts.map +1 -1
  22. package/dist/agent/loop.js +16 -92
  23. package/dist/agent/loop.js.map +1 -1
  24. package/dist/agent/messages.d.ts +9 -0
  25. package/dist/agent/messages.d.ts.map +1 -1
  26. package/dist/agent/messages.js +15 -0
  27. package/dist/agent/messages.js.map +1 -1
  28. package/dist/agent/plan-storage.d.ts.map +1 -1
  29. package/dist/agent/plan-storage.js +1 -1
  30. package/dist/agent/plan-storage.js.map +1 -1
  31. package/dist/agent/plan-tools.d.ts +8 -0
  32. package/dist/agent/plan-tools.d.ts.map +1 -0
  33. package/dist/agent/plan-tools.js +150 -0
  34. package/dist/agent/plan-tools.js.map +1 -0
  35. package/dist/agent/provider-compat.d.ts.map +1 -1
  36. package/dist/agent/provider-compat.js.map +1 -1
  37. package/dist/agent/sub-agents/built-in.d.ts.map +1 -1
  38. package/dist/agent/sub-agents/built-in.js +41 -15
  39. package/dist/agent/sub-agents/built-in.js.map +1 -1
  40. package/dist/agent/sub-agents/loader.d.ts.map +1 -1
  41. package/dist/agent/sub-agents/loader.js.map +1 -1
  42. package/dist/agent/sub-agents/runner.d.ts.map +1 -1
  43. package/dist/agent/sub-agents/runner.js +12 -8
  44. package/dist/agent/sub-agents/runner.js.map +1 -1
  45. package/dist/agent/tool-execution.d.ts +34 -2
  46. package/dist/agent/tool-execution.d.ts.map +1 -1
  47. package/dist/agent/tool-execution.js +363 -360
  48. package/dist/agent/tool-execution.js.map +1 -1
  49. package/dist/agent/tool-result-sanitize.d.ts +21 -7
  50. package/dist/agent/tool-result-sanitize.d.ts.map +1 -1
  51. package/dist/agent/tool-result-sanitize.js +56 -30
  52. package/dist/agent/tool-result-sanitize.js.map +1 -1
  53. package/dist/agent/vision-fallback.d.ts.map +1 -1
  54. package/dist/agent/vision-fallback.js +3 -14
  55. package/dist/agent/vision-fallback.js.map +1 -1
  56. package/dist/index.d.ts +3 -0
  57. package/dist/index.d.ts.map +1 -1
  58. package/dist/index.js +3 -0
  59. package/dist/index.js.map +1 -1
  60. package/dist/permissions/index.d.ts +9 -4
  61. package/dist/permissions/index.d.ts.map +1 -1
  62. package/dist/permissions/index.js +41 -6
  63. package/dist/permissions/index.js.map +1 -1
  64. package/dist/permissions/session-store.d.ts +34 -20
  65. package/dist/permissions/session-store.d.ts.map +1 -1
  66. package/dist/permissions/session-store.js +94 -34
  67. package/dist/permissions/session-store.js.map +1 -1
  68. package/dist/providers/cache-control.d.ts.map +1 -1
  69. package/dist/providers/cache-control.js +0 -29
  70. package/dist/providers/cache-control.js.map +1 -1
  71. package/dist/providers/thinking.d.ts.map +1 -1
  72. package/dist/providers/thinking.js +2 -6
  73. package/dist/providers/thinking.js.map +1 -1
  74. package/dist/tools/ask-user.d.ts.map +1 -1
  75. package/dist/tools/ask-user.js +17 -7
  76. package/dist/tools/ask-user.js.map +1 -1
  77. package/dist/tools/edit.d.ts.map +1 -1
  78. package/dist/tools/edit.js +8 -1
  79. package/dist/tools/edit.js.map +1 -1
  80. package/dist/tools/glob.d.ts.map +1 -1
  81. package/dist/tools/glob.js +9 -2
  82. package/dist/tools/glob.js.map +1 -1
  83. package/dist/tools/grep.d.ts +1 -1
  84. package/dist/tools/grep.d.ts.map +1 -1
  85. package/dist/tools/grep.js +29 -6
  86. package/dist/tools/grep.js.map +1 -1
  87. package/dist/tools/index.d.ts +1 -1
  88. package/dist/tools/list-dir.d.ts.map +1 -1
  89. package/dist/tools/list-dir.js.map +1 -1
  90. package/dist/tools/read-file.d.ts.map +1 -1
  91. package/dist/tools/read-file.js +78 -36
  92. package/dist/tools/read-file.js.map +1 -1
  93. package/dist/tools/shell-provider.d.ts +1 -0
  94. package/dist/tools/shell-provider.d.ts.map +1 -1
  95. package/dist/tools/shell-provider.js +7 -0
  96. package/dist/tools/shell-provider.js.map +1 -1
  97. package/dist/tools/shell-utils.d.ts.map +1 -1
  98. package/dist/tools/shell-utils.js +45 -2
  99. package/dist/tools/shell-utils.js.map +1 -1
  100. package/dist/tools/shell.d.ts.map +1 -1
  101. package/dist/tools/shell.js +15 -1
  102. package/dist/tools/shell.js.map +1 -1
  103. package/dist/tools/task.d.ts.map +1 -1
  104. package/dist/tools/task.js +4 -4
  105. package/dist/tools/task.js.map +1 -1
  106. package/dist/tools/todo-write.d.ts.map +1 -1
  107. package/dist/tools/todo-write.js.map +1 -1
  108. package/dist/tools/web-fetch.d.ts +2 -0
  109. package/dist/tools/web-fetch.d.ts.map +1 -1
  110. package/dist/tools/web-fetch.js +92 -27
  111. package/dist/tools/web-fetch.js.map +1 -1
  112. package/dist/tools/write-file.d.ts.map +1 -1
  113. package/dist/tools/write-file.js +7 -1
  114. package/dist/tools/write-file.js.map +1 -1
  115. package/dist/types/index.d.ts +1 -1
  116. package/dist/types/index.d.ts.map +1 -1
  117. package/dist/utils/lru-cache.d.ts +17 -0
  118. package/dist/utils/lru-cache.d.ts.map +1 -0
  119. package/dist/utils/lru-cache.js +40 -0
  120. package/dist/utils/lru-cache.js.map +1 -0
  121. package/dist/utils/media-type.d.ts +5 -0
  122. package/dist/utils/media-type.d.ts.map +1 -0
  123. package/dist/utils/media-type.js +19 -0
  124. package/dist/utils/media-type.js.map +1 -0
  125. package/dist/utils/message-helpers.d.ts +6 -0
  126. package/dist/utils/message-helpers.d.ts.map +1 -0
  127. package/dist/utils/message-helpers.js +14 -0
  128. package/dist/utils/message-helpers.js.map +1 -0
  129. package/package.json +1 -1
@@ -16,53 +16,95 @@ import path from 'node:path';
16
16
  import { tool } from 'ai';
17
17
  import { z } from 'zod';
18
18
  import { classifyFile } from '../agent/file-ingest.js';
19
+ import { mediaTypeFor } from '../utils/media-type.js';
19
20
  import { formatToolError } from '../utils/tool-errors.js';
20
21
  import { reportProgress } from './progress.js';
21
- function mediaTypeFor(filePath) {
22
- const ext = path.extname(filePath).toLowerCase();
23
- if (ext === '.jpg' || ext === '.jpeg')
24
- return 'image/jpeg';
25
- if (ext === '.png')
26
- return 'image/png';
27
- if (ext === '.webp')
28
- return 'image/webp';
29
- if (ext === '.gif')
30
- return 'image/gif';
31
- if (ext === '.bmp')
32
- return 'image/bmp';
33
- return 'image/png';
34
- }
35
- /** Threshold above which a no-args readFile call returns a partial head plus a
36
- * hint to re-read specific ranges. Picked empirically: 500 lines of code is
37
- * a realistic ceiling for "skim the whole thing", and anything bigger is
38
- * almost always used with grep first. */
39
- const LARGE_FILE_LINE_THRESHOLD = 500;
22
+ /** Default cap on lines returned by a no-args readFile call. Aligned with
23
+ * Claude Code's MAX_LINES_TO_READ — picked empirically: 2000 lines is a
24
+ * realistic ceiling for "skim the whole thing", anything bigger is almost
25
+ * always used with grep first. Was 500 originally, bumped after observing
26
+ * that 500 forced too many round-trips for legitimate "read this whole
27
+ * module" cases (4× more calls than CC for the same coverage). */
28
+ const LARGE_FILE_LINE_THRESHOLD = 2000;
29
+ /** Byte cap on a single tool-result payload. Mirrors the @-attach ingest cap
30
+ * in file-ingest.ts and Claude Code's Read-tool 25K-token default (~100 KB
31
+ * English / ~75 KB CJK; 256 KB gives headroom). Applies to BOTH the
32
+ * default head case AND the explicit offset/limit case — without this,
33
+ * a model that asks for `limit: 90000` on a multi-MB file gets the entire
34
+ * thing dumped into context and the next turn fails with
35
+ * context_length_exceeded. CC enforces the same invariant via
36
+ * `validateContentTokens`. */
37
+ const MAX_READ_BYTES = 256 * 1024;
40
38
  async function readTextResult(filePath, offset, limit) {
41
39
  const content = await fs.readFile(filePath, 'utf-8');
42
40
  const lines = content.split('\n');
43
41
  const totalLines = lines.length;
44
- // When the caller passes neither offset nor limit and the file is large,
45
- // return only the head and tell the model how to request the rest. Without
46
- // this guard, models happily read 2000-line files "just to see what's in
47
- // there" and the full content rides along on every subsequent turn. The
48
- // downstream truncator (tool-result-sanitize) would eventually clip this,
49
- // but doing it at the tool level preserves intent — the model sees
50
- // explicitly that the file was large and that it should narrow the range.
51
42
  const userSpecifiedRange = offset != null || limit != null;
52
- if (!userSpecifiedRange && totalLines > LARGE_FILE_LINE_THRESHOLD) {
53
- const head = lines.slice(0, LARGE_FILE_LINE_THRESHOLD);
54
- const body = head.map((line, i) => `${i + 1}\t${line}`).join('\n');
55
- return (body +
56
- `\n\n[readFile: showing first ${LARGE_FILE_LINE_THRESHOLD}/${totalLines} lines. ` +
57
- `Call readFile again with offset/limit to view other ranges, or use grep to find specific symbols.]`);
43
+ // Decide which slice the caller asked for; head-truncation is its own
44
+ // mode so the trailing hint can say "showing first N" vs "byte cap hit".
45
+ let start;
46
+ let end;
47
+ let isHeadTruncation = false;
48
+ if (userSpecifiedRange) {
49
+ start = (offset ?? 1) - 1;
50
+ end = limit ? start + limit : lines.length;
51
+ }
52
+ else if (totalLines > LARGE_FILE_LINE_THRESHOLD) {
53
+ start = 0;
54
+ end = LARGE_FILE_LINE_THRESHOLD;
55
+ isHeadTruncation = true;
56
+ }
57
+ else {
58
+ start = 0;
59
+ end = lines.length;
58
60
  }
59
- const start = (offset ?? 1) - 1;
60
- const end = limit ? start + limit : lines.length;
61
61
  const sliced = lines.slice(start, end);
62
- return sliced.map((line, i) => `${start + i + 1}\t${line}`).join('\n');
62
+ // Build the numbered-line output line-by-line, stopping as soon as adding
63
+ // the next line would push past MAX_READ_BYTES. Per-line byte counting
64
+ // is necessary for CJK / wide-char content where line.length lies about
65
+ // the on-the-wire size.
66
+ const formatted = [];
67
+ let bytes = 0;
68
+ for (let i = 0; i < sliced.length; i++) {
69
+ const numbered = `${start + i + 1}\t${sliced[i]}`;
70
+ const addedBytes = Buffer.byteLength(numbered, 'utf-8') + (formatted.length > 0 ? 1 : 0);
71
+ if (bytes + addedBytes > MAX_READ_BYTES && formatted.length > 0)
72
+ break;
73
+ formatted.push(numbered);
74
+ bytes += addedBytes;
75
+ }
76
+ const includedLines = formatted.length;
77
+ const body = formatted.join('\n');
78
+ // Trailing hint — same shape as Claude Code's MaxFileReadTokenExceededError
79
+ // message: tells the model exactly which next call will work, so it can
80
+ // self-recover instead of giving up or repeating the same call.
81
+ if (isHeadTruncation) {
82
+ const note = includedLines < sliced.length ? ` (further capped at ${MAX_READ_BYTES / 1024} KB)` : '';
83
+ return (body +
84
+ `\n\n[readFile: showing first ${includedLines}/${totalLines} lines${note}. ` +
85
+ `Call readFile again with offset/limit to view other ranges, or use grep to find specific symbols. ` +
86
+ `For whole-file analysis of very large files, consider delegating to a sub-agent via the task tool — ` +
87
+ `each sub-agent reads in isolated context and returns only a summary.]`);
88
+ }
89
+ if (includedLines < sliced.length) {
90
+ const nextOffset = start + includedLines + 1;
91
+ return (body +
92
+ `\n\n[readFile: output capped at ${MAX_READ_BYTES / 1024} KB; ` +
93
+ `returned ${includedLines}/${sliced.length} requested lines (lines ${start + 1}-${start + includedLines}). ` +
94
+ `Call readFile again with offset=${nextOffset} for the next chunk, or narrow the range.]`);
95
+ }
96
+ return body;
63
97
  }
64
98
  export const readFile = tool({
65
- description: 'Read the contents of a file at the given path. Returns line-numbered text for code/docs, and inline media for images/PDFs so the model can inspect them directly.',
99
+ description: `Read a file from the local filesystem. Assume this tool can read all files on the machine.
100
+
101
+ Usage:
102
+ - The filePath parameter must be an absolute path, not a relative path.
103
+ - You can optionally specify offset and limit (especially handy for long files), but it's recommended to read the whole file first.
104
+ - Results are returned with line numbers starting at 1.
105
+ - This tool can read images (PNG, JPG, etc.) and PDFs — their content is presented inline.
106
+ - This tool can only read files, not directories. To list a directory, use listDir or shell with ls.
107
+ - If a file path is provided by the user, assume it is valid.`,
66
108
  inputSchema: z.object({
67
109
  filePath: z.string().describe('Absolute path to the file'),
68
110
  offset: z.number().optional().describe('Start line (1-based, text files only)'),
@@ -1 +1 @@
1
- {"version":3,"file":"read-file.js","sourceRoot":"","sources":["../../src/tools/read-file.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"read-file.js","sourceRoot":"","sources":["../../src/tools/read-file.ts"],"names":[],"mappings":"AAAA,mCAAmC;AACnC,EAAE;AACF,2EAA2E;AAC3E,wEAAwE;AACxE,qEAAqE;AACrE,2EAA2E;AAC3E,gCAAgC;AAChC,EAAE;AACF,sEAAsE;AACtE,sEAAsE;AACtE,wEAAwE;AACxE,wEAAwE;AACxE,0BAA0B;AAC1B,OAAO,EAAE,MAAM,kBAAkB,CAAA;AACjC,OAAO,IAAI,MAAM,WAAW,CAAA;AAE5B,OAAO,EAAE,IAAI,EAAE,MAAM,IAAI,CAAA;AAEzB,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAA;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAA;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAA;AACzD,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAE9C;;;;;mEAKmE;AACnE,MAAM,yBAAyB,GAAG,IAAI,CAAA;AAEtC;;;;;;;+BAO+B;AAC/B,MAAM,cAAc,GAAG,GAAG,GAAG,IAAI,CAAA;AAEjC,KAAK,UAAU,cAAc,CAAC,QAAgB,EAAE,MAAe,EAAE,KAAc;IAC7E,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAA;IACpD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;IACjC,MAAM,UAAU,GAAG,KAAK,CAAC,MAAM,CAAA;IAE/B,MAAM,kBAAkB,GAAG,MAAM,IAAI,IAAI,IAAI,KAAK,IAAI,IAAI,CAAA;IAE1D,sEAAsE;IACtE,yEAAyE;IACzE,IAAI,KAAa,CAAA;IACjB,IAAI,GAAW,CAAA;IACf,IAAI,gBAAgB,GAAG,KAAK,CAAA;IAC5B,IAAI,kBAAkB,EAAE,CAAC;QACvB,KAAK,GAAG,CAAC,MAAM,IAAI,CAAC,CAAC,GAAG,CAAC,CAAA;QACzB,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAA;IAC5C,CAAC;SAAM,IAAI,UAAU,GAAG,yBAAyB,EAAE,CAAC;QAClD,KAAK,GAAG,CAAC,CAAA;QACT,GAAG,GAAG,yBAAyB,CAAA;QAC/B,gBAAgB,GAAG,IAAI,CAAA;IACzB,CAAC;SAAM,CAAC;QACN,KAAK,GAAG,CAAC,CAAA;QACT,GAAG,GAAG,KAAK,CAAC,MAAM,CAAA;IACpB,CAAC;IACD,MAAM,MAAM,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,CAAC,CAAA;IAEtC,0EAA0E;IAC1E,uEAAuE;IACvE,wEAAwE;IACxE,wBAAwB;IACxB,MAAM,SAAS,GAAa,EAAE,CAAA;IAC9B,IAAI,KAAK,GAAG,CAAC,CAAA;IACb,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACvC,MAAM,QAAQ,GAAG,GAAG,KAAK,GAAG,CAAC,GAAG,CAAC,KAAK,MAAM,CAAC,CAAC,CAAC,EAAE,CAAA;QACjD,MAAM,UAAU,GAAG,MAAM,CAAC,UAAU,CAAC,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;QACxF,IAAI,KAAK,GAAG,UAAU,GAAG,cAAc,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC;YAAE,MAAK;QACtE,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;QACxB,KAAK,IAAI,UAAU,CAAA;IACrB,CAAC;IACD,MAAM,aAAa,GAAG,SAAS,CAAC,MAAM,CAAA;IACtC,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAEjC,4EAA4E;IAC5E,wEAAwE;IACxE,gEAAgE;IAChE,IAAI,gBAAgB,EAAE,CAAC;QACrB,MAAM,IAAI,GAAG,aAAa,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,uBAAuB,cAAc,GAAG,IAAI,MAAM,CAAC,CAAC,CAAC,EAAE,CAAA;QACpG,OAAO,CACL,IAAI;YACJ,gCAAgC,aAAa,IAAI,UAAU,SAAS,IAAI,IAAI;YAC5E,oGAAoG;YACpG,sGAAsG;YACtG,uEAAuE,CACxE,CAAA;IACH,CAAC;IACD,IAAI,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC;QAClC,MAAM,UAAU,GAAG,KAAK,GAAG,aAAa,GAAG,CAAC,CAAA;QAC5C,OAAO,CACL,IAAI;YACJ,mCAAmC,cAAc,GAAG,IAAI,OAAO;YAC/D,YAAY,aAAa,IAAI,MAAM,CAAC,MAAM,2BAA2B,KAAK,GAAG,CAAC,IAAI,KAAK,GAAG,aAAa,KAAK;YAC5G,mCAAmC,UAAU,4CAA4C,CAC1F,CAAA;IACH,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC;AAED,MAAM,CAAC,MAAM,QAAQ,GAAG,IAAI,CAAC;IAC3B,WAAW,EAAE;;;;;;;;8DAQ+C;IAC5D,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC;QACpB,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,2BAA2B,CAAC;QAC1D,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,uCAAuC,CAAC;QAC/E,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,qCAAqC,CAAC;KAC7E,CAAC;IACF,OAAO,EAAE,KAAK,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE;QAC7D,IAAI,CAAC;YACH,cAAc,CAAC,UAAU,EAAE,WAAW,QAAQ,EAAE,CAAC,CAAA;YACjD,MAAM,IAAI,GAAG,MAAM,YAAY,CAAC,QAAQ,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,MAAe,CAAC,CAAA;YAEtE,IAAI,IAAI,KAAK,OAAO,EAAE,CAAC;gBACrB,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAA;gBAC1C,qEAAqE;gBACrE,iEAAiE;gBACjE,4DAA4D;gBAC5D,mEAAmE;gBACnE,kEAAkE;gBAClE,wBAAwB;gBACxB,OAAO;oBACL,IAAI,EAAE,SAAS;oBACf,KAAK,EAAE;wBACL,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,iBAAiB,QAAQ,EAAE,EAAE;wBACnD;4BACE,IAAI,EAAE,YAAY;4BAClB,IAAI,EAAE,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC;4BAC/B,SAAS,EAAE,YAAY,CAAC,QAAQ,CAAC;yBAClC;qBACF;iBACF,CAAA;YACH,CAAC;YAED,IAAI,IAAI,KAAK,KAAK,EAAE,CAAC;gBACnB,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAA;gBAC1C,OAAO;oBACL,IAAI,EAAE,SAAS;oBACf,KAAK,EAAE;wBACL,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,eAAe,QAAQ,EAAE,EAAE;wBACjD;4BACE,IAAI,EAAE,WAAW;4BACjB,IAAI,EAAE,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC;4BAC/B,SAAS,EAAE,iBAAiB;4BAC5B,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC;yBAClC;qBACF;iBACF,CAAA;YACH,CAAC;YAED,0CAA0C;YAC1C,uEAAuE;YACvE,wEAAwE;YACxE,wEAAwE;YACxE,kEAAkE;YAClE,mBAAmB;YACnB,OAAO,MAAM,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,CAAC,CAAA;QACtD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,eAAe,CAAC,cAAc,EAAE,GAAG,CAAC,CAAA;QAC7C,CAAC;IACH,CAAC;CACF,CAAC,CAAA"}
@@ -1,5 +1,6 @@
1
1
  import { type ResultPromise } from 'execa';
2
2
  export type ShellType = 'bash' | 'zsh' | 'powershell';
3
+ export declare const MAX_SHELL_BUFFER: number;
3
4
  export interface ShellSpawnOptions {
4
5
  timeout: number;
5
6
  env?: NodeJS.ProcessEnv;
@@ -1 +1 @@
1
- {"version":3,"file":"shell-provider.d.ts","sourceRoot":"","sources":["../../src/tools/shell-provider.ts"],"names":[],"mappings":"AAMA,OAAO,EAAS,KAAK,aAAa,EAAE,MAAM,OAAO,CAAA;AAGjD,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,KAAK,GAAG,YAAY,CAAA;AAErD,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAA;IACf,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAA;IACvB,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ;;uBAEmB;IACnB,MAAM,CAAC,EAAE,WAAW,CAAA;CACrB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,SAAS,CAAA;IACf,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,iBAAiB,GAAG,aAAa,CAAA;CAC/D;AA+DD,wBAAgB,gBAAgB,IAAI,aAAa,CAYhD"}
1
+ {"version":3,"file":"shell-provider.d.ts","sourceRoot":"","sources":["../../src/tools/shell-provider.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,KAAK,aAAa,EAAS,MAAM,OAAO,CAAA;AAIjD,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,KAAK,GAAG,YAAY,CAAA;AAMrD,eAAO,MAAM,gBAAgB,QAAmB,CAAA;AAEhD,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAA;IACf,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAA;IACvB,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ;;uBAEmB;IACnB,MAAM,CAAC,EAAE,WAAW,CAAA;CACrB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,SAAS,CAAA;IACf,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,iBAAiB,GAAG,aAAa,CAAA;CAC/D;AA6DD,wBAAgB,gBAAgB,IAAI,aAAa,CAYhD"}
@@ -6,12 +6,18 @@
6
6
  // hand-roll quote escapes for PowerShell.
7
7
  import { execa } from 'execa';
8
8
  import os from 'node:os';
9
+ // 20 MB — matches Claude Code's ripgrep buffer; generous enough for real
10
+ // workloads, small enough to prevent an accidental `yes` or `find /` from
11
+ // eating all memory. When exceeded, execa terminates the child with SIGTERM
12
+ // and surfaces a "maxBuffer exceeded" error.
13
+ export const MAX_SHELL_BUFFER = 20 * 1024 * 1024;
9
14
  function createPosixProvider(executable, type) {
10
15
  return {
11
16
  type,
12
17
  spawn(command, opts) {
13
18
  return execa(executable, ['-c', command], {
14
19
  timeout: opts.timeout,
20
+ maxBuffer: MAX_SHELL_BUFFER,
15
21
  cwd: opts.cwd,
16
22
  reject: false,
17
23
  cancelSignal: opts.signal,
@@ -52,6 +58,7 @@ function createPowerShellProvider(executable) {
52
58
  ].join('\n');
53
59
  return execa(executable, ['-NoProfile', '-NonInteractive', '-EncodedCommand', encodePowerShellCommand(wrapped)], {
54
60
  timeout: opts.timeout,
61
+ maxBuffer: MAX_SHELL_BUFFER,
55
62
  cwd: opts.cwd,
56
63
  reject: false,
57
64
  cancelSignal: opts.signal,
@@ -1 +1 @@
1
- {"version":3,"file":"shell-provider.js","sourceRoot":"","sources":["../../src/tools/shell-provider.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"shell-provider.js","sourceRoot":"","sources":["../../src/tools/shell-provider.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"shell-utils.d.ts","sourceRoot":"","sources":["../../src/tools/shell-utils.ts"],"names":[],"mappings":"AAKA,YAAY,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAA;AAEpD,oFAAoF;AACpF,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CA0CxD;AAkDD,+DAA+D;AAC/D,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAE/C;AAED,+DAA+D;AAC/D,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAGlD"}
1
+ {"version":3,"file":"shell-utils.d.ts","sourceRoot":"","sources":["../../src/tools/shell-utils.ts"],"names":[],"mappings":"AAKA,YAAY,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAA;AAEpD,oFAAoF;AACpF,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CA0CxD;AAoGD,+DAA+D;AAC/D,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAE/C;AAED,+DAA+D;AAC/D,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAGlD"}
@@ -76,18 +76,61 @@ const READ_ONLY_COMMANDS = [
76
76
  'Test-Path',
77
77
  ];
78
78
  /** Git sub-commands that are read-only */
79
- const READ_ONLY_GIT_SUBCOMMANDS = ['status', 'log', 'diff', 'branch', 'show', 'remote', 'tag'];
79
+ const READ_ONLY_GIT_SUBCOMMANDS = ['status', 'log', 'diff', 'branch', 'show', 'remote', 'tag', 'stash list', 'reflog'];
80
80
  // Pre-compiled regexes for performance
81
81
  const READ_ONLY_REGEX = new RegExp(`^\\s*(${READ_ONLY_COMMANDS.join('|')}|git\\s+(${READ_ONLY_GIT_SUBCOMMANDS.join('|')}))\\b`);
82
82
  const DESTRUCTIVE_PATTERNS = [
83
+ // ── Filesystem destruction ──
83
84
  /\brm\s+(-[a-z]*f|-[a-z]*r|--force|--recursive)/,
85
+ /\brm\s+-rf\b/,
84
86
  /\bsudo\b/,
85
87
  /\bmkfs\b/,
86
88
  /\bdd\s+if=/,
87
89
  /\b(chmod|chown)\s+.*\//,
88
90
  />\s*\/dev\/sd/,
89
91
  /\bformat\b/,
90
- /\bRemove-Item\s+.*-Recurse/,
92
+ /\bRemove-Item\s+.*-Recurse/i,
93
+ /\bRemove-Item\s+.*-Force/i,
94
+ /\bdel\s+\/[sS]/,
95
+ /\brmdir\s+\/[sS]/,
96
+ // ── Git destructive operations ──
97
+ /\bgit\s+push\s+.*--force\b/,
98
+ /\bgit\s+push\s+-f\b/,
99
+ /\bgit\s+reset\s+--hard\b/,
100
+ /\bgit\s+clean\s+-[a-z]*f/,
101
+ /\bgit\s+checkout\s+--\s*\./,
102
+ /\bgit\s+rebase\b/,
103
+ /\bgit\s+filter-branch\b/,
104
+ /\bgit\s+reflog\s+expire\b/,
105
+ /\bgit\s+gc\s+--prune\b/,
106
+ // ── Remote code execution / download-and-exec ──
107
+ /\bcurl\s.*\|\s*(ba)?sh\b/,
108
+ /\bwget\s.*\|\s*(ba)?sh\b/,
109
+ /\bcurl\s.*\|\s*python/,
110
+ /\bwget\s.*\|\s*python/,
111
+ // ── System control ──
112
+ /\bshutdown\b/,
113
+ /\breboot\b/,
114
+ /\binit\s+[06]\b/,
115
+ /\bsystemctl\s+(stop|disable|mask|halt|poweroff)\b/,
116
+ /\bkillall\b/,
117
+ /\bpkill\s+-9\b/,
118
+ /\bStop-Computer\b/i,
119
+ /\bRestart-Computer\b/i,
120
+ // ── Database destruction ──
121
+ /\bDROP\s+(DATABASE|TABLE|SCHEMA)\b/i,
122
+ /\bTRUNCATE\s+TABLE\b/i,
123
+ /\bDELETE\s+FROM\s+\S+\s*;?\s*$/im,
124
+ // ── Container / infra destruction ──
125
+ /\bdocker\s+(rm|rmi|system\s+prune|volume\s+rm)\b/,
126
+ /\bkubectl\s+delete\b/,
127
+ // ── Environment pollution ──
128
+ /\bnpm\s+publish\b/,
129
+ /\bpnpm\s+publish\b/,
130
+ /\byarn\s+publish\b/,
131
+ // ── Disk / partition ──
132
+ /\bfdisk\b/,
133
+ /\bparted\b/,
91
134
  ];
92
135
  /** Check if a sub-command is read-only (safe to auto-allow) */
93
136
  export function isReadOnly(cmd) {
@@ -1 +1 @@
1
- {"version":3,"file":"shell-utils.js","sourceRoot":"","sources":["../../src/tools/shell-utils.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"shell-utils.js","sourceRoot":"","sources":["../../src/tools/shell-utils.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"shell.d.ts","sourceRoot":"","sources":["../../src/tools/shell.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,KAAK;;;SAQhB,CAAA"}
1
+ {"version":3,"file":"shell.d.ts","sourceRoot":"","sources":["../../src/tools/shell.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,KAAK;;;SAqBhB,CAAA"}
@@ -2,7 +2,21 @@
2
2
  import { tool } from 'ai';
3
3
  import { z } from 'zod';
4
4
  export const shell = tool({
5
- description: 'Execute a shell command and return stdout/stderr. Commands should be compatible with the current platform shell.',
5
+ description: `Execute a shell command and return stdout/stderr. The working directory persists between commands.
6
+
7
+ IMPORTANT: Avoid using this tool to run grep, rg, cat, head, tail, sed, or awk commands. Instead, use the appropriate dedicated tool — they provide a better user experience:
8
+ - File search: Use glob (NOT find or ls)
9
+ - Content search: Use grep tool (NOT grep/rg command)
10
+ - Read files: Use readFile (NOT cat/head/tail)
11
+ - Edit files: Use edit (NOT sed/awk)
12
+ - Write files: Use writeFile (NOT echo >/cat <<EOF)
13
+
14
+ Instructions:
15
+ - If your command will create new directories or files, first run ls to verify the parent directory exists and is the correct location.
16
+ - Always quote file paths that contain spaces with double quotes.
17
+ - When issuing multiple commands: if they are independent, make multiple shell tool calls in a single message for parallelism. If they depend on each other, use '&&' to chain them. Use ';' only when you need sequential execution but don't care if earlier commands fail. Do NOT use newlines to separate commands.
18
+ - For git commands: prefer creating a new commit rather than amending. Never skip hooks (--no-verify) unless the user explicitly asks. Before running destructive operations (git reset --hard, git push --force), consider safer alternatives.
19
+ - Do not sleep between commands that can run immediately.`,
6
20
  inputSchema: z.object({
7
21
  command: z.string().describe('The command to execute'),
8
22
  timeout: z.number().optional().describe('Timeout in milliseconds (default: 30000)'),
@@ -1 +1 @@
1
- {"version":3,"file":"shell.js","sourceRoot":"","sources":["../../src/tools/shell.ts"],"names":[],"mappings":"AAAA,wGAAwG;AACxG,OAAO,EAAE,IAAI,EAAE,MAAM,IAAI,CAAA;AAEzB,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,MAAM,CAAC,MAAM,KAAK,GAAG,IAAI,CAAC;IACxB,WAAW,EACT,kHAAkH;IACpH,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC;QACpB,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,wBAAwB,CAAC;QACtD,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,0CAA0C,CAAC;KACpF,CAAC;IACF,sGAAsG;CACvG,CAAC,CAAA"}
1
+ {"version":3,"file":"shell.js","sourceRoot":"","sources":["../../src/tools/shell.ts"],"names":[],"mappings":"AAAA,wGAAwG;AACxG,OAAO,EAAE,IAAI,EAAE,MAAM,IAAI,CAAA;AAEzB,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,MAAM,CAAC,MAAM,KAAK,GAAG,IAAI,CAAC;IACxB,WAAW,EAAE;;;;;;;;;;;;;;0DAc2C;IACxD,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC;QACpB,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,wBAAwB,CAAC;QACtD,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,0CAA0C,CAAC;KACpF,CAAC;IACF,sGAAsG;CACvG,CAAC,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"task.d.ts","sourceRoot":"","sources":["../../src/tools/task.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAA;AAEvE;;;2DAG2D;AAC3D,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,gBAAgB,GAAG,MAAM,CAoE3E;AAED;4DAC4D;AAC5D,wBAAgB,cAAc,CAAC,QAAQ,EAAE,gBAAgB;;;;UAcxD"}
1
+ {"version":3,"file":"task.d.ts","sourceRoot":"","sources":["../../src/tools/task.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAA;AAEvE;;;2DAG2D;AAC3D,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,gBAAgB,GAAG,MAAM,CAkE3E;AAED;4DAC4D;AAC5D,wBAAgB,cAAc,CAAC,QAAQ,EAAE,gBAAgB;;;;UAcxD"}
@@ -13,9 +13,7 @@ import { z } from 'zod';
13
13
  * the model knows what subagent_type values are valid. */
14
14
  export function buildTaskToolDescription(registry) {
15
15
  const agents = registry.list();
16
- const agentList = agents
17
- .map((a) => ` - ${a.name}: ${a.description}`)
18
- .join('\n');
16
+ const agentList = agents.map((a) => ` - ${a.name}: ${a.description}`).join('\n');
19
17
  return `Launch a sub-agent to handle a task in an isolated context. The sub-agent runs with its own message history and returns only its final conclusion — its intermediate tool calls never enter your context window, keeping the main conversation lean.
20
18
 
21
19
  Available sub-agents:
@@ -87,7 +85,9 @@ export function createTaskTool(registry) {
87
85
  inputSchema: z.object({
88
86
  description: z.string().describe('A short (3-5 words) description of the task'),
89
87
  subagent_type: z.string().describe(`Which sub-agent to use. Available: ${registry.names().join(', ')}`),
90
- prompt: z.string().describe('The complete task instruction sent to the sub-agent. Be specific — the sub-agent has no prior context.'),
88
+ prompt: z
89
+ .string()
90
+ .describe('The complete task instruction sent to the sub-agent. Be specific — the sub-agent has no prior context.'),
91
91
  }),
92
92
  // No execute — handled manually in tool-execution.ts
93
93
  });
@@ -1 +1 @@
1
- {"version":3,"file":"task.js","sourceRoot":"","sources":["../../src/tools/task.ts"],"names":[],"mappings":"AAAA,oDAAoD;AACpD,EAAE;AACF,sEAAsE;AACtE,kEAAkE;AAClE,gEAAgE;AAChE,kEAAkE;AAClE,mBAAmB;AACnB,OAAO,EAAE,IAAI,EAAE,MAAM,IAAI,CAAA;AACzB,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAIvB;;;2DAG2D;AAC3D,MAAM,UAAU,wBAAwB,CAAC,QAA0B;IACjE,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAA;IAC9B,MAAM,SAAS,GAAG,MAAM;SACrB,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,WAAW,EAAE,CAAC;SAC7C,IAAI,CAAC,IAAI,CAAC,CAAA;IAEb,OAAO;;;EAGP,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;WA0DA,CAAA;AACX,CAAC;AAED;4DAC4D;AAC5D,MAAM,UAAU,cAAc,CAAC,QAA0B;IACvD,OAAO,IAAI,CAAC;QACV,WAAW,EAAE,wBAAwB,CAAC,QAAQ,CAAC;QAC/C,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC;YACpB,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,6CAA6C,CAAC;YAC/E,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAChC,sCAAsC,QAAQ,CAAC,KAAK,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CACpE;YACD,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CACzB,wGAAwG,CACzG;SACF,CAAC;QACF,qDAAqD;KACtD,CAAC,CAAA;AACJ,CAAC"}
1
+ {"version":3,"file":"task.js","sourceRoot":"","sources":["../../src/tools/task.ts"],"names":[],"mappings":"AAAA,oDAAoD;AACpD,EAAE;AACF,sEAAsE;AACtE,kEAAkE;AAClE,gEAAgE;AAChE,kEAAkE;AAClE,mBAAmB;AACnB,OAAO,EAAE,IAAI,EAAE,MAAM,IAAI,CAAA;AAEzB,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAIvB;;;2DAG2D;AAC3D,MAAM,UAAU,wBAAwB,CAAC,QAA0B;IACjE,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAA;IAC9B,MAAM,SAAS,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAEjF,OAAO;;;EAGP,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;WA0DA,CAAA;AACX,CAAC;AAED;4DAC4D;AAC5D,MAAM,UAAU,cAAc,CAAC,QAA0B;IACvD,OAAO,IAAI,CAAC;QACV,WAAW,EAAE,wBAAwB,CAAC,QAAQ,CAAC;QAC/C,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC;YACpB,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,6CAA6C,CAAC;YAC/E,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,sCAAsC,QAAQ,CAAC,KAAK,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YACvG,MAAM,EAAE,CAAC;iBACN,MAAM,EAAE;iBACR,QAAQ,CACP,wGAAwG,CACzG;SACJ,CAAC;QACF,qDAAqD;KACtD,CAAC,CAAA;AACJ,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"todo-write.d.ts","sourceRoot":"","sources":["../../src/tools/todo-write.ts"],"names":[],"mappings":"AAKA;;;;;;;;;;;;0CAY0C;AAC1C,eAAO,MAAM,SAAS;;;;;;SAqGpB,CAAA"}
1
+ {"version":3,"file":"todo-write.d.ts","sourceRoot":"","sources":["../../src/tools/todo-write.ts"],"names":[],"mappings":"AAKA;;;;;;;;;;;;0CAY0C;AAC1C,eAAO,MAAM,SAAS;;;;;;SA2GpB,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"todo-write.js","sourceRoot":"","sources":["../../src/tools/todo-write.ts"],"names":[],"mappings":"AAAA,kGAAkG;AAClG,OAAO,EAAE,IAAI,EAAE,MAAM,IAAI,CAAA;AAEzB,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB;;;;;;;;;;;;0CAY0C;AAC1C,MAAM,CAAC,MAAM,SAAS,GAAG,IAAI,CAAC;IAC5B,WAAW,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0DAqE2C;IACxD,8DAA8D;IAC9D,mEAAmE;IACnE,qEAAqE;IACrE,kEAAkE;IAClE,iEAAiE;IACjE,gEAAgE;IAChE,4DAA4D;IAC5D,iEAAiE;IACjE,oEAAoE;IACpE,oEAAoE;IACpE,6DAA6D;IAC7D,kEAAkE;IAClE,+BAA+B;IAC/B,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC;QACpB,KAAK,EAAE,CAAC;aACL,KAAK,CACJ,CAAC,CAAC,MAAM,CAAC;YACP,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,gDAAgD,CAAC;YACzF,UAAU,EAAE,CAAC;iBACV,MAAM,EAAE;iBACR,QAAQ,EAAE;iBACV,QAAQ,CAAC,oGAAoG,CAAC;YACjH,MAAM,EAAE,CAAC;iBACN,IAAI,CAAC,CAAC,SAAS,EAAE,aAAa,EAAE,WAAW,CAAC,CAAC;iBAC7C,QAAQ,EAAE;iBACV,QAAQ,CAAC,wGAAwG,CAAC;SACtH,CAAC,CACH;aACA,QAAQ,CAAC,8GAA8G,CAAC;KAC5H,CAAC;CACH,CAAC,CAAA"}
1
+ {"version":3,"file":"todo-write.js","sourceRoot":"","sources":["../../src/tools/todo-write.ts"],"names":[],"mappings":"AAAA,kGAAkG;AAClG,OAAO,EAAE,IAAI,EAAE,MAAM,IAAI,CAAA;AAEzB,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB;;;;;;;;;;;;0CAY0C;AAC1C,MAAM,CAAC,MAAM,SAAS,GAAG,IAAI,CAAC;IAC5B,WAAW,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0DAqE2C;IACxD,8DAA8D;IAC9D,mEAAmE;IACnE,qEAAqE;IACrE,kEAAkE;IAClE,iEAAiE;IACjE,gEAAgE;IAChE,4DAA4D;IAC5D,iEAAiE;IACjE,oEAAoE;IACpE,oEAAoE;IACpE,6DAA6D;IAC7D,kEAAkE;IAClE,+BAA+B;IAC/B,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC;QACpB,KAAK,EAAE,CAAC;aACL,KAAK,CACJ,CAAC,CAAC,MAAM,CAAC;YACP,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,gDAAgD,CAAC;YACzF,UAAU,EAAE,CAAC;iBACV,MAAM,EAAE;iBACR,QAAQ,EAAE;iBACV,QAAQ,CACP,oGAAoG,CACrG;YACH,MAAM,EAAE,CAAC;iBACN,IAAI,CAAC,CAAC,SAAS,EAAE,aAAa,EAAE,WAAW,CAAC,CAAC;iBAC7C,QAAQ,EAAE;iBACV,QAAQ,CACP,wGAAwG,CACzG;SACJ,CAAC,CACH;aACA,QAAQ,CACP,8GAA8G,CAC/G;KACJ,CAAC;CACH,CAAC,CAAA"}
@@ -1,3 +1,5 @@
1
+ /** @internal Exported for testing only. */
2
+ export declare function validateFetchUrl(url: string): string | null;
1
3
  export declare const webFetch: import("ai").Tool<{
2
4
  url: string;
3
5
  prompt?: string | undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"web-fetch.d.ts","sourceRoot":"","sources":["../../src/tools/web-fetch.ts"],"names":[],"mappings":"AAoFA,eAAO,MAAM,QAAQ;;;UAqEnB,CAAA"}
1
+ {"version":3,"file":"web-fetch.d.ts","sourceRoot":"","sources":["../../src/tools/web-fetch.ts"],"names":[],"mappings":"AA6DA,2CAA2C;AAC3C,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAkB3D;AAyDD,eAAO,MAAM,QAAQ;;;UAyEnB,CAAA"}
@@ -4,6 +4,7 @@ import * as cheerio from 'cheerio';
4
4
  import TurndownService from 'turndown';
5
5
  import { tool } from 'ai';
6
6
  import { z } from 'zod';
7
+ import { LruCache } from '../utils/lru-cache.js';
7
8
  import { formatToolError } from '../utils/tool-errors.js';
8
9
  import { reportProgress } from './progress.js';
9
10
  const FETCH_TIMEOUT_MS = 15_000;
@@ -13,9 +14,10 @@ const FETCH_TIMEOUT_MS = 15_000;
13
14
  // This is a per-call cap; the model can always fetch again with a narrower prompt.
14
15
  const MAX_CONTENT_CHARS = 100_000;
15
16
  // Raw HTML ceiling before turndown. 10 MB is comfortable for any real doc page;
16
- // enforced via content-length header (best-effort chunked responses skip this,
17
- // in which case the 15 s fetch timeout bounds the download).
17
+ // enforced both by content-length header AND by streaming body read (see
18
+ // readResponseBody) so chunked responses are also bounded.
18
19
  const MAX_HTTP_BYTES = 10 * 1024 * 1024;
20
+ const MAX_URL_LENGTH = 2000;
19
21
  const CACHE_TTL_MS = 15 * 60 * 1000;
20
22
  const CACHE_MAX_ENTRIES = 50;
21
23
  const BROWSER_UA = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36';
@@ -23,28 +25,55 @@ const BROWSER_UA = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36
23
25
  // while blocking browser impersonators that fail TLS-fingerprint checks.
24
26
  const FALLBACK_UA = 'x-code-cli/0.1 (+https://github.com/woai3c/x-code-cli)';
25
27
  const YEAR = new Date().getFullYear();
26
- const fetchCache = new Map();
27
- function cacheGet(url) {
28
- const entry = fetchCache.get(url);
29
- if (!entry)
30
- return null;
31
- if (Date.now() - entry.at > CACHE_TTL_MS) {
32
- fetchCache.delete(url);
33
- return null;
34
- }
35
- // LRU: re-insert to move this entry to the tail (most-recently-used)
36
- fetchCache.delete(url);
37
- fetchCache.set(url, entry);
38
- return entry.markdown;
28
+ // ── SSRF protection ──
29
+ // Reject URLs targeting internal/private networks. Mirrors Claude Code's
30
+ // validateURL: hostname must have ≥2 dot-separated segments (rejects
31
+ // `localhost`, bare hostnames), no embedded credentials, no non-HTTP schemes,
32
+ // and no IPs in private/link-local/loopback ranges.
33
+ const PRIVATE_IP_PATTERNS = [
34
+ /^127\./, // loopback
35
+ /^10\./, // 10.0.0.0/8
36
+ /^172\.(1[6-9]|2\d|3[01])\./, // 172.16.0.0/12
37
+ /^192\.168\./, // 192.168.0.0/16
38
+ /^169\.254\./, // link-local (AWS/GCP metadata)
39
+ /^0\./, // 0.0.0.0/8
40
+ /^::1$/, // IPv6 loopback
41
+ /^fd[0-9a-f]{2}:/i, // IPv6 ULA
42
+ /^fe80:/i, // IPv6 link-local
43
+ ];
44
+ function isPrivateHost(hostname) {
45
+ const lower = hostname.toLowerCase();
46
+ if (lower === 'localhost' || lower.endsWith('.local') || lower.endsWith('.internal'))
47
+ return true;
48
+ // IP-literal in URL — strip surrounding brackets for IPv6
49
+ const bare = lower.startsWith('[') ? lower.slice(1, -1) : lower;
50
+ return PRIVATE_IP_PATTERNS.some((re) => re.test(bare));
39
51
  }
40
- function cacheSet(url, markdown) {
41
- if (fetchCache.size >= CACHE_MAX_ENTRIES) {
42
- const oldest = fetchCache.keys().next().value;
43
- if (oldest !== undefined)
44
- fetchCache.delete(oldest);
52
+ /** @internal Exported for testing only. */
53
+ export function validateFetchUrl(url) {
54
+ if (url.length > MAX_URL_LENGTH)
55
+ return `URL exceeds ${MAX_URL_LENGTH} character limit`;
56
+ let parsed;
57
+ try {
58
+ parsed = new URL(url);
59
+ }
60
+ catch {
61
+ return 'Invalid URL';
62
+ }
63
+ if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
64
+ return `Unsupported protocol: ${parsed.protocol} (only http/https allowed)`;
65
+ }
66
+ if (parsed.username || parsed.password)
67
+ return 'URLs with embedded credentials are not allowed';
68
+ const parts = parsed.hostname.split('.');
69
+ if (parts.length < 2)
70
+ return `Hostname "${parsed.hostname}" is not a public domain (must have at least two segments)`;
71
+ if (isPrivateHost(parsed.hostname)) {
72
+ return `Fetching private/internal address "${parsed.hostname}" is blocked for security`;
45
73
  }
46
- fetchCache.set(url, { markdown, at: Date.now() });
74
+ return null;
47
75
  }
76
+ const fetchCache = new LruCache({ maxEntries: CACHE_MAX_ENTRIES, ttlMs: CACHE_TTL_MS });
48
77
  const turndown = new TurndownService({
49
78
  headingStyle: 'atx',
50
79
  codeBlockStyle: 'fenced',
@@ -56,9 +85,41 @@ async function doFetch(url, userAgent) {
56
85
  Accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,text/plain;q=0.8,*/*;q=0.5',
57
86
  'Accept-Language': 'en-US,en;q=0.9',
58
87
  },
88
+ redirect: 'follow',
59
89
  signal: AbortSignal.timeout(FETCH_TIMEOUT_MS),
60
90
  });
61
91
  }
92
+ /** Stream-read response body with a hard byte cap. Prevents OOM on chunked
93
+ * responses where content-length is absent or lying. */
94
+ async function readResponseBody(response, maxBytes) {
95
+ const reader = response.body?.getReader();
96
+ if (!reader)
97
+ return response.text();
98
+ const chunks = [];
99
+ let totalBytes = 0;
100
+ while (true) {
101
+ const { done, value } = await reader.read();
102
+ if (done)
103
+ break;
104
+ totalBytes += value.byteLength;
105
+ if (totalBytes > maxBytes) {
106
+ await reader.cancel();
107
+ break;
108
+ }
109
+ chunks.push(value);
110
+ }
111
+ const merged = new Uint8Array(Math.min(totalBytes, maxBytes));
112
+ let offset = 0;
113
+ for (const chunk of chunks) {
114
+ const remaining = merged.byteLength - offset;
115
+ if (remaining <= 0)
116
+ break;
117
+ const slice = chunk.byteLength > remaining ? chunk.subarray(0, remaining) : chunk;
118
+ merged.set(slice, offset);
119
+ offset += slice.byteLength;
120
+ }
121
+ return new TextDecoder().decode(merged);
122
+ }
62
123
  function formatOutput(url, markdown, prompt) {
63
124
  if (prompt) {
64
125
  return `# Content from ${url}\n\n${markdown}\n\n---\nExtract instruction: ${prompt}`;
@@ -77,7 +138,10 @@ export const webFetch = tool({
77
138
  }),
78
139
  execute: async ({ url, prompt }, { toolCallId }) => {
79
140
  try {
80
- const cached = cacheGet(url);
141
+ const urlError = validateFetchUrl(url);
142
+ if (urlError)
143
+ return `Error: ${urlError}`;
144
+ const cached = fetchCache.get(url);
81
145
  if (cached) {
82
146
  reportProgress(toolCallId, 'Using cached copy');
83
147
  return formatOutput(url, cached, prompt);
@@ -93,18 +157,19 @@ export const webFetch = tool({
93
157
  if (!response.ok) {
94
158
  return `Error: HTTP ${response.status} ${response.statusText}`;
95
159
  }
96
- // Best-effort size guard: content-length is optional under chunked encoding,
97
- // so we also rely on fetch's 15s timeout to bound pathological pages.
160
+ // Reject upfront when content-length exceeds the cap.
98
161
  const contentLength = Number(response.headers.get('content-length') ?? '0');
99
162
  if (contentLength > MAX_HTTP_BYTES) {
100
163
  const mb = Math.round(contentLength / 1024 / 1024);
101
164
  return `Error: Content too large (${mb} MB, limit ${MAX_HTTP_BYTES / 1024 / 1024} MB)`;
102
165
  }
103
166
  const contentType = response.headers.get('content-type') ?? '';
104
- const body = await response.text();
167
+ // Stream-read with hard byte cap — prevents OOM on chunked responses
168
+ // where content-length is absent or lies.
169
+ const body = await readResponseBody(response, MAX_HTTP_BYTES);
105
170
  if (contentType.includes('application/json')) {
106
171
  const json = body.slice(0, MAX_CONTENT_CHARS);
107
- cacheSet(url, json);
172
+ fetchCache.set(url, json);
108
173
  return formatOutput(url, json, prompt);
109
174
  }
110
175
  const $ = cheerio.load(body);
@@ -117,7 +182,7 @@ export const webFetch = tool({
117
182
  if (markdown.length > MAX_CONTENT_CHARS) {
118
183
  markdown = markdown.slice(0, MAX_CONTENT_CHARS) + '\n\n... [content truncated]';
119
184
  }
120
- cacheSet(url, markdown);
185
+ fetchCache.set(url, markdown);
121
186
  return formatOutput(url, markdown, prompt);
122
187
  }
123
188
  catch (err) {