wave-agent-sdk 0.10.3 → 0.10.4

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.
@@ -1 +1 @@
1
- {"version":3,"file":"bashTool.d.ts","sourceRoot":"","sources":["../../src/tools/bashTool.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAA2B,MAAM,YAAY,CAAC;AActE;;GAEG;AACH,eAAO,MAAM,QAAQ,EAAE,UA4XtB,CAAC"}
1
+ {"version":3,"file":"bashTool.d.ts","sourceRoot":"","sources":["../../src/tools/bashTool.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,UAAU,EAA2B,MAAM,YAAY,CAAC;AAyCtE;;GAEG;AACH,eAAO,MAAM,QAAQ,EAAE,UA0XtB,CAAC"}
@@ -1,9 +1,33 @@
1
1
  import { spawn } from "child_process";
2
+ import * as fs from "fs";
3
+ import * as path from "path";
4
+ import * as os from "os";
2
5
  import { logger } from "../utils/globalLogger.js";
3
6
  import { stripAnsiColors } from "../utils/stringUtils.js";
4
7
  import { BASH_TOOL_NAME, TASK_OUTPUT_TOOL_NAME, GLOB_TOOL_NAME, GREP_TOOL_NAME, READ_TOOL_NAME, EDIT_TOOL_NAME, WRITE_TOOL_NAME, } from "../constants/tools.js";
5
8
  const MAX_OUTPUT_LENGTH = 30000;
6
9
  const BASH_DEFAULT_TIMEOUT_MS = 120000;
10
+ /**
11
+ * Helper function to handle large output by truncation and persistence to a temporary file.
12
+ */
13
+ function processOutput(output) {
14
+ if (output.length <= MAX_OUTPUT_LENGTH) {
15
+ return output;
16
+ }
17
+ try {
18
+ const tempDir = os.tmpdir();
19
+ const fileName = `bash_output_${Date.now()}_${Math.random().toString(36).substring(2, 11)}.txt`;
20
+ const filePath = path.join(tempDir, fileName);
21
+ fs.writeFileSync(filePath, output, "utf8");
22
+ return (output.substring(0, MAX_OUTPUT_LENGTH) +
23
+ `\n\n... (output truncated)\nFull output persisted to: ${filePath}`);
24
+ }
25
+ catch (error) {
26
+ logger.error("Failed to persist large bash output:", error);
27
+ return (output.substring(0, MAX_OUTPUT_LENGTH) +
28
+ "\n\n... (output truncated, failed to persist full output)");
29
+ }
30
+ }
7
31
  /**
8
32
  * Bash command execution tool - supports both foreground and background execution
9
33
  */
@@ -63,7 +87,7 @@ Usage notes:
63
87
  - The command argument is required.
64
88
  - You can specify an optional timeout in milliseconds (up to ${BASH_DEFAULT_TIMEOUT_MS}ms / ${BASH_DEFAULT_TIMEOUT_MS / 60000} minutes). If not specified, commands will timeout after ${BASH_DEFAULT_TIMEOUT_MS}ms (${BASH_DEFAULT_TIMEOUT_MS / 60000} minutes).
65
89
  - It is very helpful if you write a clear, concise description of what this command does in 5-10 words.
66
- - If the output exceeds ${MAX_OUTPUT_LENGTH} characters, output will be truncated before being returned to you.
90
+ - If the output exceeds ${MAX_OUTPUT_LENGTH} characters, output will be truncated and the full output will be persisted to a temporary file.
67
91
  - You can use the \`run_in_background\` parameter to run the command in the background, which allows you to continue working while the command runs. You can monitor the output using the ${BASH_TOOL_NAME} tool as it becomes available. You do not need to use '&' at the end of the command when using this parameter.
68
92
  - Avoid using ${BASH_TOOL_NAME} with the \`find\`, \`sed\`, \`awk\`, or \`echo\` commands, unless explicitly instructed or when these commands are truly necessary for the task. Instead, always prefer using the dedicated tools for these commands:
69
93
  - File search: Use ${GLOB_TOOL_NAME} (NOT find or ls)
@@ -248,7 +272,7 @@ Usage notes:
248
272
  }
249
273
  resolve({
250
274
  success: false,
251
- content: outputBuffer + (errorBuffer ? "\n" + errorBuffer : ""),
275
+ content: processOutput(outputBuffer + (errorBuffer ? "\n" + errorBuffer : "")),
252
276
  error: reason,
253
277
  });
254
278
  }
@@ -286,12 +310,9 @@ Usage notes:
286
310
  }
287
311
  const exitCode = code ?? 0;
288
312
  const combinedOutput = outputBuffer + (errorBuffer ? "\n" + errorBuffer : "");
289
- // Handle large output by truncation if needed
313
+ // Handle large output by truncation and persistence if needed
290
314
  const finalOutput = combinedOutput || `Command executed with exit code: ${exitCode}`;
291
- const content = finalOutput.length > MAX_OUTPUT_LENGTH
292
- ? finalOutput.substring(0, MAX_OUTPUT_LENGTH) +
293
- "\n\n... (output truncated)"
294
- : finalOutput;
315
+ const content = processOutput(finalOutput);
295
316
  const shortResult = combinedOutput
296
317
  .trim()
297
318
  .split("\n")
@@ -1 +1 @@
1
- {"version":3,"file":"readTool.d.ts","sourceRoot":"","sources":["../../src/tools/readTool.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAA2B,MAAM,YAAY,CAAC;AA8HtE;;GAEG;AACH,eAAO,MAAM,QAAQ,EAAE,UAiOtB,CAAC"}
1
+ {"version":3,"file":"readTool.d.ts","sourceRoot":"","sources":["../../src/tools/readTool.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAA2B,MAAM,YAAY,CAAC;AA8HtE;;GAEG;AACH,eAAO,MAAM,QAAQ,EAAE,UA2MtB,CAAC"}
@@ -204,26 +204,14 @@ Usage:
204
204
  shortResult: "Empty file",
205
205
  };
206
206
  }
207
- // Check content size limit (100KB)
208
- const MAX_CONTENT_SIZE = 100 * 1024; // 100KB
209
- let contentToProcess = fileContent;
210
- let contentTruncated = false;
211
- if (fileContent.length > MAX_CONTENT_SIZE) {
212
- contentToProcess = fileContent.substring(0, MAX_CONTENT_SIZE);
213
- contentTruncated = true;
214
- }
215
- const lines = contentToProcess.split("\n");
216
- const totalLines = lines.length;
217
- const originalTotalLines = fileContent.split("\n").length;
207
+ const allLines = fileContent.split("\n");
208
+ const totalLines = allLines.length;
218
209
  // Handle offset and limit
219
210
  let startLine = 1;
220
- let endLine = Math.min(totalLines, 2000); // Default maximum read 2000 lines
221
211
  if (typeof offset === "number") {
222
212
  startLine = Math.max(1, offset);
223
213
  }
224
- if (typeof limit === "number") {
225
- endLine = Math.min(totalLines, startLine + limit - 1);
226
- }
214
+ let endLine = Math.min(totalLines, startLine + (typeof limit === "number" ? limit : 2000) - 1);
227
215
  // If no offset and limit specified, read entire file (maximum 2000 lines)
228
216
  if (typeof offset !== "number" && typeof limit !== "number") {
229
217
  startLine = 1;
@@ -238,7 +226,7 @@ Usage:
238
226
  };
239
227
  }
240
228
  // Extract specified line range
241
- const selectedLines = lines.slice(startLine - 1, endLine);
229
+ const selectedLines = allLines.slice(startLine - 1, endLine);
242
230
  // Format output (cat -n format, with line numbers)
243
231
  const formattedContent = selectedLines
244
232
  .map((line, index) => {
@@ -250,11 +238,7 @@ Usage:
250
238
  .join("\n");
251
239
  // Add file information header
252
240
  let content = `File: ${filePath}\n`;
253
- if (contentTruncated) {
254
- content += `Content truncated at ${MAX_CONTENT_SIZE} bytes\n`;
255
- content += `Lines ${startLine}-${endLine} of ${totalLines} (original file: ${originalTotalLines} lines)\n`;
256
- }
257
- else if (startLine > 1 || endLine < totalLines) {
241
+ if (startLine > 1 || endLine < totalLines) {
258
242
  content += `Lines ${startLine}-${endLine} of ${totalLines}\n`;
259
243
  }
260
244
  else {
@@ -263,22 +247,14 @@ Usage:
263
247
  content += "─".repeat(50) + "\n";
264
248
  content += formattedContent;
265
249
  // If only showing partial content, add prompt
266
- if (endLine < totalLines || contentTruncated) {
250
+ if (endLine < totalLines) {
267
251
  content += `\n${"─".repeat(50)}\n`;
268
- if (contentTruncated) {
269
- content += `... content truncated due to size limit (${MAX_CONTENT_SIZE} bytes)`;
270
- if (endLine < totalLines) {
271
- content += ` and ${totalLines - endLine} more lines not shown`;
272
- }
273
- }
274
- else {
275
- content += `... ${totalLines - endLine} more lines not shown`;
276
- }
252
+ content += `... ${totalLines - endLine} more lines not shown`;
277
253
  }
278
254
  return {
279
255
  success: true,
280
256
  content,
281
- shortResult: `Read ${selectedLines.length} lines${totalLines > 2000 || contentTruncated ? " (truncated)" : ""}`,
257
+ shortResult: `Read ${selectedLines.length} lines${totalLines > 2000 ? " (truncated)" : ""}`,
282
258
  };
283
259
  }
284
260
  catch (error) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wave-agent-sdk",
3
- "version": "0.10.3",
3
+ "version": "0.10.4",
4
4
  "description": "SDK for building AI-powered development tools and agents",
5
5
  "keywords": [
6
6
  "ai",
@@ -1,4 +1,7 @@
1
1
  import { spawn, ChildProcess } from "child_process";
2
+ import * as fs from "fs";
3
+ import * as path from "path";
4
+ import * as os from "os";
2
5
  import { logger } from "../utils/globalLogger.js";
3
6
  import { stripAnsiColors } from "../utils/stringUtils.js";
4
7
  import type { ToolPlugin, ToolResult, ToolContext } from "./types.js";
@@ -15,6 +18,33 @@ import {
15
18
  const MAX_OUTPUT_LENGTH = 30000;
16
19
  const BASH_DEFAULT_TIMEOUT_MS = 120000;
17
20
 
21
+ /**
22
+ * Helper function to handle large output by truncation and persistence to a temporary file.
23
+ */
24
+ function processOutput(output: string): string {
25
+ if (output.length <= MAX_OUTPUT_LENGTH) {
26
+ return output;
27
+ }
28
+
29
+ try {
30
+ const tempDir = os.tmpdir();
31
+ const fileName = `bash_output_${Date.now()}_${Math.random().toString(36).substring(2, 11)}.txt`;
32
+ const filePath = path.join(tempDir, fileName);
33
+ fs.writeFileSync(filePath, output, "utf8");
34
+
35
+ return (
36
+ output.substring(0, MAX_OUTPUT_LENGTH) +
37
+ `\n\n... (output truncated)\nFull output persisted to: ${filePath}`
38
+ );
39
+ } catch (error) {
40
+ logger.error("Failed to persist large bash output:", error);
41
+ return (
42
+ output.substring(0, MAX_OUTPUT_LENGTH) +
43
+ "\n\n... (output truncated, failed to persist full output)"
44
+ );
45
+ }
46
+ }
47
+
18
48
  /**
19
49
  * Bash command execution tool - supports both foreground and background execution
20
50
  */
@@ -75,7 +105,7 @@ Usage notes:
75
105
  - The command argument is required.
76
106
  - You can specify an optional timeout in milliseconds (up to ${BASH_DEFAULT_TIMEOUT_MS}ms / ${BASH_DEFAULT_TIMEOUT_MS / 60000} minutes). If not specified, commands will timeout after ${BASH_DEFAULT_TIMEOUT_MS}ms (${BASH_DEFAULT_TIMEOUT_MS / 60000} minutes).
77
107
  - It is very helpful if you write a clear, concise description of what this command does in 5-10 words.
78
- - If the output exceeds ${MAX_OUTPUT_LENGTH} characters, output will be truncated before being returned to you.
108
+ - If the output exceeds ${MAX_OUTPUT_LENGTH} characters, output will be truncated and the full output will be persisted to a temporary file.
79
109
  - You can use the \`run_in_background\` parameter to run the command in the background, which allows you to continue working while the command runs. You can monitor the output using the ${BASH_TOOL_NAME} tool as it becomes available. You do not need to use '&' at the end of the command when using this parameter.
80
110
  - Avoid using ${BASH_TOOL_NAME} with the \`find\`, \`sed\`, \`awk\`, or \`echo\` commands, unless explicitly instructed or when these commands are truly necessary for the task. Instead, always prefer using the dedicated tools for these commands:
81
111
  - File search: Use ${GLOB_TOOL_NAME} (NOT find or ls)
@@ -292,7 +322,9 @@ Usage notes:
292
322
 
293
323
  resolve({
294
324
  success: false,
295
- content: outputBuffer + (errorBuffer ? "\n" + errorBuffer : ""),
325
+ content: processOutput(
326
+ outputBuffer + (errorBuffer ? "\n" + errorBuffer : ""),
327
+ ),
296
328
  error: reason,
297
329
  });
298
330
  }
@@ -340,14 +372,10 @@ Usage notes:
340
372
  const combinedOutput =
341
373
  outputBuffer + (errorBuffer ? "\n" + errorBuffer : "");
342
374
 
343
- // Handle large output by truncation if needed
375
+ // Handle large output by truncation and persistence if needed
344
376
  const finalOutput =
345
377
  combinedOutput || `Command executed with exit code: ${exitCode}`;
346
- const content =
347
- finalOutput.length > MAX_OUTPUT_LENGTH
348
- ? finalOutput.substring(0, MAX_OUTPUT_LENGTH) +
349
- "\n\n... (output truncated)"
350
- : finalOutput;
378
+ const content = processOutput(finalOutput);
351
379
 
352
380
  const shortResult = combinedOutput
353
381
  .trim()
@@ -243,31 +243,19 @@ Usage:
243
243
  };
244
244
  }
245
245
 
246
- // Check content size limit (100KB)
247
- const MAX_CONTENT_SIZE = 100 * 1024; // 100KB
248
- let contentToProcess = fileContent;
249
- let contentTruncated = false;
250
-
251
- if (fileContent.length > MAX_CONTENT_SIZE) {
252
- contentToProcess = fileContent.substring(0, MAX_CONTENT_SIZE);
253
- contentTruncated = true;
254
- }
255
-
256
- const lines = contentToProcess.split("\n");
257
- const totalLines = lines.length;
258
- const originalTotalLines = fileContent.split("\n").length;
246
+ const allLines = fileContent.split("\n");
247
+ const totalLines = allLines.length;
259
248
 
260
249
  // Handle offset and limit
261
250
  let startLine = 1;
262
- let endLine = Math.min(totalLines, 2000); // Default maximum read 2000 lines
263
-
264
251
  if (typeof offset === "number") {
265
252
  startLine = Math.max(1, offset);
266
253
  }
267
254
 
268
- if (typeof limit === "number") {
269
- endLine = Math.min(totalLines, startLine + limit - 1);
270
- }
255
+ let endLine = Math.min(
256
+ totalLines,
257
+ startLine + (typeof limit === "number" ? limit : 2000) - 1,
258
+ );
271
259
 
272
260
  // If no offset and limit specified, read entire file (maximum 2000 lines)
273
261
  if (typeof offset !== "number" && typeof limit !== "number") {
@@ -285,7 +273,7 @@ Usage:
285
273
  }
286
274
 
287
275
  // Extract specified line range
288
- const selectedLines = lines.slice(startLine - 1, endLine);
276
+ const selectedLines = allLines.slice(startLine - 1, endLine);
289
277
 
290
278
  // Format output (cat -n format, with line numbers)
291
279
  const formattedContent = selectedLines
@@ -300,10 +288,7 @@ Usage:
300
288
 
301
289
  // Add file information header
302
290
  let content = `File: ${filePath}\n`;
303
- if (contentTruncated) {
304
- content += `Content truncated at ${MAX_CONTENT_SIZE} bytes\n`;
305
- content += `Lines ${startLine}-${endLine} of ${totalLines} (original file: ${originalTotalLines} lines)\n`;
306
- } else if (startLine > 1 || endLine < totalLines) {
291
+ if (startLine > 1 || endLine < totalLines) {
307
292
  content += `Lines ${startLine}-${endLine} of ${totalLines}\n`;
308
293
  } else {
309
294
  content += `Total lines: ${totalLines}\n`;
@@ -312,22 +297,15 @@ Usage:
312
297
  content += formattedContent;
313
298
 
314
299
  // If only showing partial content, add prompt
315
- if (endLine < totalLines || contentTruncated) {
300
+ if (endLine < totalLines) {
316
301
  content += `\n${"─".repeat(50)}\n`;
317
- if (contentTruncated) {
318
- content += `... content truncated due to size limit (${MAX_CONTENT_SIZE} bytes)`;
319
- if (endLine < totalLines) {
320
- content += ` and ${totalLines - endLine} more lines not shown`;
321
- }
322
- } else {
323
- content += `... ${totalLines - endLine} more lines not shown`;
324
- }
302
+ content += `... ${totalLines - endLine} more lines not shown`;
325
303
  }
326
304
 
327
305
  return {
328
306
  success: true,
329
307
  content,
330
- shortResult: `Read ${selectedLines.length} lines${totalLines > 2000 || contentTruncated ? " (truncated)" : ""}`,
308
+ shortResult: `Read ${selectedLines.length} lines${totalLines > 2000 ? " (truncated)" : ""}`,
331
309
  };
332
310
  } catch (error) {
333
311
  return {