wave-agent-sdk 0.14.3 → 0.15.0

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 (84) hide show
  1. package/builtin/skills/settings/SKILLS.md +34 -6
  2. package/dist/agent.d.ts +0 -5
  3. package/dist/agent.d.ts.map +1 -1
  4. package/dist/agent.js +0 -15
  5. package/dist/constants/toolLimits.d.ts +10 -0
  6. package/dist/constants/toolLimits.d.ts.map +1 -0
  7. package/dist/constants/toolLimits.js +9 -0
  8. package/dist/managers/aiManager.d.ts +3 -5
  9. package/dist/managers/aiManager.d.ts.map +1 -1
  10. package/dist/managers/aiManager.js +107 -104
  11. package/dist/managers/forkedAgentManager.d.ts +1 -0
  12. package/dist/managers/forkedAgentManager.d.ts.map +1 -1
  13. package/dist/managers/forkedAgentManager.js +1 -0
  14. package/dist/managers/hookManager.d.ts +0 -4
  15. package/dist/managers/hookManager.d.ts.map +1 -1
  16. package/dist/managers/hookManager.js +0 -25
  17. package/dist/managers/permissionManager.d.ts +1 -1
  18. package/dist/managers/permissionManager.d.ts.map +1 -1
  19. package/dist/managers/permissionManager.js +5 -5
  20. package/dist/managers/subagentManager.d.ts +1 -0
  21. package/dist/managers/subagentManager.d.ts.map +1 -1
  22. package/dist/managers/subagentManager.js +1 -0
  23. package/dist/prompts/index.d.ts +0 -1
  24. package/dist/prompts/index.d.ts.map +1 -1
  25. package/dist/prompts/index.js +3 -4
  26. package/dist/services/aiService.d.ts.map +1 -1
  27. package/dist/services/aiService.js +10 -8
  28. package/dist/services/autoMemoryService.d.ts.map +1 -1
  29. package/dist/services/autoMemoryService.js +1 -0
  30. package/dist/services/hook.d.ts +0 -4
  31. package/dist/services/hook.d.ts.map +1 -1
  32. package/dist/services/hook.js +0 -10
  33. package/dist/services/session.d.ts.map +1 -1
  34. package/dist/services/session.js +4 -1
  35. package/dist/tools/bashTool.d.ts.map +1 -1
  36. package/dist/tools/bashTool.js +2 -45
  37. package/dist/tools/editTool.js +1 -1
  38. package/dist/tools/types.d.ts +0 -3
  39. package/dist/tools/types.d.ts.map +1 -1
  40. package/dist/types/agent.d.ts +0 -1
  41. package/dist/types/agent.d.ts.map +1 -1
  42. package/dist/types/hooks.d.ts +1 -5
  43. package/dist/types/hooks.d.ts.map +1 -1
  44. package/dist/types/hooks.js +0 -1
  45. package/dist/utils/constants.d.ts +2 -2
  46. package/dist/utils/constants.d.ts.map +1 -1
  47. package/dist/utils/constants.js +2 -2
  48. package/dist/utils/convertMessagesForAPI.d.ts.map +1 -1
  49. package/dist/utils/convertMessagesForAPI.js +16 -8
  50. package/dist/utils/editUtils.d.ts +5 -2
  51. package/dist/utils/editUtils.d.ts.map +1 -1
  52. package/dist/utils/editUtils.js +3 -57
  53. package/dist/utils/markdownParser.d.ts +8 -1
  54. package/dist/utils/markdownParser.d.ts.map +1 -1
  55. package/dist/utils/markdownParser.js +64 -11
  56. package/dist/utils/openaiClient.d.ts.map +1 -1
  57. package/dist/utils/openaiClient.js +0 -11
  58. package/dist/utils/stringUtils.d.ts +8 -0
  59. package/dist/utils/stringUtils.d.ts.map +1 -1
  60. package/dist/utils/stringUtils.js +45 -0
  61. package/package.json +1 -1
  62. package/src/agent.ts +0 -17
  63. package/src/constants/toolLimits.ts +12 -0
  64. package/src/managers/aiManager.ts +141 -148
  65. package/src/managers/forkedAgentManager.ts +3 -0
  66. package/src/managers/hookManager.ts +0 -32
  67. package/src/managers/permissionManager.ts +6 -6
  68. package/src/managers/subagentManager.ts +2 -0
  69. package/src/prompts/index.ts +3 -5
  70. package/src/services/aiService.ts +10 -12
  71. package/src/services/autoMemoryService.ts +1 -0
  72. package/src/services/hook.ts +0 -15
  73. package/src/services/session.ts +6 -1
  74. package/src/tools/bashTool.ts +2 -51
  75. package/src/tools/editTool.ts +1 -1
  76. package/src/tools/types.ts +0 -3
  77. package/src/types/agent.ts +0 -1
  78. package/src/types/hooks.ts +1 -7
  79. package/src/utils/constants.ts +2 -2
  80. package/src/utils/convertMessagesForAPI.ts +15 -8
  81. package/src/utils/editUtils.ts +3 -73
  82. package/src/utils/markdownParser.ts +85 -11
  83. package/src/utils/openaiClient.ts +0 -11
  84. package/src/utils/stringUtils.ts +43 -0
@@ -1,7 +1,6 @@
1
1
  /**
2
2
  * Utility functions for file editing tools
3
3
  */
4
- import { formatLineNumberPrefix } from "./stringUtils.js";
5
4
  /**
6
5
  * Escape regular expression special characters
7
6
  */
@@ -9,61 +8,8 @@ export function escapeRegExp(string) {
9
8
  return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
10
9
  }
11
10
  /**
12
- * Analyze why an edit failed by finding the best partial match and highlighting mismatches.
11
+ * Returns a generic error message when old_string is not found.
13
12
  */
14
- export function analyzeEditMismatch(content, searchString) {
15
- const contentLines = content.split("\n");
16
- const searchLines = searchString.split("\n");
17
- if (searchLines.length === 0 || contentLines.length === 0) {
18
- return "old_string not found in file (empty search or content)";
19
- }
20
- let bestMatchIndex = -1;
21
- let bestMatchScore = -1;
22
- // Sliding window to find the best partial match
23
- for (let i = 0; i <= contentLines.length - searchLines.length; i++) {
24
- let currentScore = 0;
25
- for (let j = 0; j < searchLines.length; j++) {
26
- if (contentLines[i + j] === searchLines[j]) {
27
- currentScore++;
28
- }
29
- }
30
- // Heuristic: prioritize matches where first or last lines match
31
- if (contentLines[i] === searchLines[0])
32
- currentScore += 0.5;
33
- if (contentLines[i + searchLines.length - 1] ===
34
- searchLines[searchLines.length - 1])
35
- currentScore += 0.5;
36
- // Also consider trimmed matches to catch indentation issues
37
- for (let j = 0; j < searchLines.length; j++) {
38
- if (contentLines[i + j].trim() === searchLines[j].trim() &&
39
- contentLines[i + j] !== searchLines[j]) {
40
- currentScore += 0.1;
41
- }
42
- }
43
- if (currentScore > bestMatchScore) {
44
- bestMatchScore = currentScore;
45
- bestMatchIndex = i;
46
- }
47
- }
48
- // If no decent match found (score <= 0), return generic message
49
- if (bestMatchScore <= 0) {
50
- return "old_string not found in file (no similar block found)";
51
- }
52
- // Generate detailed report
53
- const reportLines = [
54
- `old_string not found in file. Best partial match found at line ${bestMatchIndex + 1}:`,
55
- ];
56
- for (let j = 0; j < searchLines.length; j++) {
57
- const lineNum = bestMatchIndex + j + 1;
58
- const actualLine = contentLines[bestMatchIndex + j];
59
- const expectedLine = searchLines[j];
60
- if (actualLine === expectedLine) {
61
- reportLines.push(`${formatLineNumberPrefix(lineNum)}${actualLine}`);
62
- }
63
- else {
64
- reportLines.push(`${formatLineNumberPrefix(lineNum)}- ${expectedLine}`);
65
- reportLines.push(`${formatLineNumberPrefix(lineNum)}+ ${actualLine}`);
66
- }
67
- }
68
- return reportLines.join("\n");
13
+ export function analyzeEditMismatch() {
14
+ return "old_string not found in file";
69
15
  }
@@ -27,7 +27,14 @@ export declare function parseBashCommands(content: string): {
27
27
  processedContent: string;
28
28
  };
29
29
  /**
30
- * Replace bash command placeholders with their outputs
30
+ * Truncate output if it exceeds the size limit.
31
+ * Writes to a temp file and returns a preview + file path if truncated.
32
+ */
33
+ export declare function truncateOutput(output: string): string;
34
+ /**
35
+ * Replace bash command placeholders with their outputs.
36
+ * Uses function replacer to avoid $$, $&, $' corruption in shell output.
37
+ * Handles both inline (!`cmd`) and block (```! cmd ```) syntax.
31
38
  */
32
39
  export declare function replaceBashCommandsWithOutput(content: string, results: BashCommandResult[]): string;
33
40
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"markdownParser.d.ts","sourceRoot":"","sources":["../../src/utils/markdownParser.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAC;AAIlE,UAAU,kBAAkB;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,wBAAwB,CAAC;CACnC;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG;IACjD,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtC,OAAO,EAAE,MAAM,CAAC;CACjB,CA8DA;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,kBAAkB,CA4CtE;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG;IAClD,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,gBAAgB,EAAE,MAAM,CAAC;CAC1B,CAgBA;AAED;;GAEG;AACH,wBAAgB,6BAA6B,CAC3C,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,iBAAiB,EAAE,GAC3B,MAAM,CAcR;AAED;;GAEG;AACH,wBAAsB,mBAAmB,CACvC,QAAQ,EAAE,MAAM,EAAE,EAClB,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,MAAc,GACtB,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAkC9B"}
1
+ {"version":3,"file":"markdownParser.d.ts","sourceRoot":"","sources":["../../src/utils/markdownParser.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAC;AAQlE,UAAU,kBAAkB;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,wBAAwB,CAAC;CACnC;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG;IACjD,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtC,OAAO,EAAE,MAAM,CAAC;CACjB,CA8DA;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,kBAAkB,CA4CtE;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB;AAYD,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG;IAClD,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,gBAAgB,EAAE,MAAM,CAAC;CAC1B,CAyCA;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAgBrD;AAED;;;;GAIG;AACH,wBAAgB,6BAA6B,CAC3C,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,iBAAiB,EAAE,GAC3B,MAAM,CAuBR;AAED;;GAEG;AACH,wBAAsB,mBAAmB,CACvC,QAAQ,EAAE,MAAM,EAAE,EAClB,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,MAAc,GACtB,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAkC9B"}
@@ -1,6 +1,9 @@
1
- import { readFileSync } from "fs";
1
+ import { readFileSync, writeFileSync, mkdirSync } from "fs";
2
2
  import { exec } from "child_process";
3
3
  import { promisify } from "util";
4
+ import { join } from "path";
5
+ import { tmpdir } from "os";
6
+ import { SKILL_BASH_MAX_OUTPUT_CHARS, PREVIEW_SIZE_BYTES, } from "../constants/toolLimits.js";
4
7
  const execAsync = promisify(exec);
5
8
  /**
6
9
  * Parse YAML frontmatter from markdown content
@@ -97,13 +100,38 @@ export function parseMarkdownFile(filePath) {
97
100
  throw new Error(`Failed to parse markdown file ${filePath}: ${error instanceof Error ? error.message : String(error)}`);
98
101
  }
99
102
  }
103
+ /**
104
+ * Block syntax pattern: ```! command ```
105
+ */
106
+ const BLOCK_BASH_REGEX = /```!\s*\n?([\s\S]*?)\n?```/g;
107
+ /**
108
+ * Inline syntax pattern: !`command`
109
+ */
110
+ const INLINE_BASH_REGEX = /!`([^`]+)`/g;
100
111
  export function parseBashCommands(content) {
101
- const bashCommandRegex = /!`([^`]+)`/g;
112
+ // Performance gate: skip expensive regex if no bash pattern exists
113
+ // Covers the common case where 93% of skills have no bash substitution
114
+ if (!content.includes("!`") && !content.includes("```!")) {
115
+ return { commands: [], processedContent: content };
116
+ }
102
117
  const commands = [];
103
- let match;
104
- // Extract all bash commands
105
- while ((match = bashCommandRegex.exec(content)) !== null) {
106
- commands.push(match[1]);
118
+ // Extract block commands
119
+ let blockMatch;
120
+ const blockRegex = new RegExp(BLOCK_BASH_REGEX.source, BLOCK_BASH_REGEX.flags);
121
+ while ((blockMatch = blockRegex.exec(content)) !== null) {
122
+ const cmd = blockMatch[1].trim();
123
+ if (cmd) {
124
+ commands.push(cmd);
125
+ }
126
+ }
127
+ // Extract inline commands
128
+ let inlineMatch;
129
+ const inlineRegex = new RegExp(INLINE_BASH_REGEX.source, INLINE_BASH_REGEX.flags);
130
+ while ((inlineMatch = inlineRegex.exec(content)) !== null) {
131
+ const cmd = inlineMatch[1].trim();
132
+ if (cmd) {
133
+ commands.push(cmd);
134
+ }
107
135
  }
108
136
  // For now, return the content as-is. The actual command execution
109
137
  // will be handled by the slash command manager
@@ -113,18 +141,43 @@ export function parseBashCommands(content) {
113
141
  };
114
142
  }
115
143
  /**
116
- * Replace bash command placeholders with their outputs
144
+ * Truncate output if it exceeds the size limit.
145
+ * Writes to a temp file and returns a preview + file path if truncated.
146
+ */
147
+ export function truncateOutput(output) {
148
+ if (output.length <= SKILL_BASH_MAX_OUTPUT_CHARS) {
149
+ return output;
150
+ }
151
+ const preview = output.slice(0, PREVIEW_SIZE_BYTES);
152
+ const tempDir = join(tmpdir(), "wave-skill-bash");
153
+ mkdirSync(tempDir, { recursive: true });
154
+ const tempFile = join(tempDir, `output-${Date.now()}-${Math.random().toString(36).slice(2, 8)}.txt`);
155
+ writeFileSync(tempFile, output, "utf-8");
156
+ return `${preview}\n\n[Output truncated (${output.length} chars). Full output saved to: ${tempFile}]`;
157
+ }
158
+ /**
159
+ * Replace bash command placeholders with their outputs.
160
+ * Uses function replacer to avoid $$, $&, $' corruption in shell output.
161
+ * Handles both inline (!`cmd`) and block (```! cmd ```) syntax.
117
162
  */
118
163
  export function replaceBashCommandsWithOutput(content, results) {
119
- const bashCommandRegex = /!`([^`]+)`/g;
120
164
  let processedContent = content;
121
165
  let commandIndex = 0;
122
- processedContent = processedContent.replace(bashCommandRegex, (match) => {
166
+ // Replace block syntax first: ```! command ```
167
+ processedContent = processedContent.replace(BLOCK_BASH_REGEX, () => {
168
+ if (commandIndex < results.length) {
169
+ const result = results[commandIndex++];
170
+ return truncateOutput(result.output);
171
+ }
172
+ return "";
173
+ });
174
+ // Replace inline syntax: !`command`
175
+ processedContent = processedContent.replace(INLINE_BASH_REGEX, () => {
123
176
  if (commandIndex < results.length) {
124
177
  const result = results[commandIndex++];
125
- return result.output;
178
+ return truncateOutput(result.output);
126
179
  }
127
- return match;
180
+ return "";
128
181
  });
129
182
  return processedContent;
130
183
  }
@@ -1 +1 @@
1
- {"version":3,"file":"openaiClient.d.ts","sourceRoot":"","sources":["../../src/utils/openaiClient.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,sCAAsC,EACtC,mCAAmC,EACnC,mBAAmB,EACnB,cAAc,EACf,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAGnD,KAAK,YAAY,GACb,sCAAsC,GACtC,mCAAmC,CAAC;AAExC,UAAU,WAAW,CAAC,CAAC;IACrB,IAAI,EAAE,CAAC,CAAC;IACR,QAAQ,EAAE,QAAQ,CAAC;CACpB;AAED,UAAU,UAAU,CAAC,CAAC,CAAE,SAAQ,OAAO,CAAC,CAAC,CAAC;IACxC,YAAY,IAAI,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;CACzC;AAED,qBAAa,YAAY;IACX,OAAO,CAAC,MAAM;gBAAN,MAAM,EAAE,aAAa;IAEzC,IAAI,IAAI;;qBAGO,CAAC,SAAS,YAAY,UACrB,CAAC,YACC;gBAAE,MAAM,CAAC,EAAE,WAAW,CAAA;aAAE,KACjC,UAAU,CACX,CAAC,SAAS,mCAAmC,GACzC,aAAa,CAAC,mBAAmB,CAAC,GAClC,cAAc,CACnB;;MA2BN;YAEa,OAAO;YAgJN,oBAAoB;CAqCpC"}
1
+ {"version":3,"file":"openaiClient.d.ts","sourceRoot":"","sources":["../../src/utils/openaiClient.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,sCAAsC,EACtC,mCAAmC,EACnC,mBAAmB,EACnB,cAAc,EACf,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAGnD,KAAK,YAAY,GACb,sCAAsC,GACtC,mCAAmC,CAAC;AAExC,UAAU,WAAW,CAAC,CAAC;IACrB,IAAI,EAAE,CAAC,CAAC;IACR,QAAQ,EAAE,QAAQ,CAAC;CACpB;AAED,UAAU,UAAU,CAAC,CAAC,CAAE,SAAQ,OAAO,CAAC,CAAC,CAAC;IACxC,YAAY,IAAI,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;CACzC;AAED,qBAAa,YAAY;IACX,OAAO,CAAC,MAAM;gBAAN,MAAM,EAAE,aAAa;IAEzC,IAAI,IAAI;;qBAGO,CAAC,SAAS,YAAY,UACrB,CAAC,YACC;gBAAE,MAAM,CAAC,EAAE,WAAW,CAAA;aAAE,KACjC,UAAU,CACX,CAAC,SAAS,mCAAmC,GACzC,aAAa,CAAC,mBAAmB,CAAC,GAClC,cAAc,CACnB;;MA2BN;YAEa,OAAO;YAqIN,oBAAoB;CAqCpC"}
@@ -108,27 +108,16 @@ export class OpenAIClient {
108
108
  error.status = response.status;
109
109
  error.body = errorBody;
110
110
  if (response.status === 429 && attempt < maxRetries) {
111
- const responseHeaders = {};
112
- response.headers.forEach((value, key) => {
113
- responseHeaders[key] = value;
114
- });
115
111
  logger.warn("OpenAI API 429 Too Many Requests, retrying...", {
116
112
  attempt: attempt + 1,
117
113
  status: response.status,
118
- responseHeaders,
119
114
  });
120
115
  lastError = error;
121
116
  continue;
122
117
  }
123
- const responseHeaders = {};
124
- response.headers.forEach((value, key) => {
125
- responseHeaders[key] = value;
126
- });
127
118
  logger.error("OpenAI API Error:", {
128
119
  status: response.status,
129
120
  statusText: response.statusText,
130
- requestHeaders: headers,
131
- responseHeaders,
132
121
  errorBody,
133
122
  });
134
123
  throw error;
@@ -23,6 +23,14 @@ export declare const stripAnsiColors: (text: string) => string;
23
23
  * @returns Formatted line number prefix
24
24
  */
25
25
  export declare function formatLineNumberPrefix(lineNumber: number): string;
26
+ /**
27
+ * Attempt to recover truncated JSON (e.g., missing closing braces due to max tokens).
28
+ * Tracks brace depth and only recovers if there are unclosed `{` braces.
29
+ * Will NOT recover if there are unclosed `[` brackets (can't guess the content).
30
+ * @param jsonStr Potentially truncated JSON string
31
+ * @returns Recovered JSON string, or the original if unrecoverable
32
+ */
33
+ export declare function recoverTruncatedJson(jsonStr: string): string;
26
34
  /**
27
35
  * Efficiently get the last N lines of a string without splitting the whole string.
28
36
  */
@@ -1 +1 @@
1
- {"version":3,"file":"stringUtils.d.ts","sourceRoot":"","sources":["../../src/utils/stringUtils.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAgC/D;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,aAAa,EAAE,MAAM,GACpB,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAwBxB;AAED;;;;GAIG;AACH,eAAO,MAAM,eAAe,GAAI,MAAM,MAAM,KAAG,MAK9C,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAEjE;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAehE"}
1
+ {"version":3,"file":"stringUtils.d.ts","sourceRoot":"","sources":["../../src/utils/stringUtils.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAgC/D;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,aAAa,EAAE,MAAM,GACpB,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAwBxB;AAED;;;;GAIG;AACH,eAAO,MAAM,eAAe,GAAI,MAAM,MAAM,KAAG,MAK9C,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAEjE;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAkC5D;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAehE"}
@@ -77,6 +77,51 @@ export const stripAnsiColors = (text) => {
77
77
  export function formatLineNumberPrefix(lineNumber) {
78
78
  return `${lineNumber.toString().padStart(6)}\t`;
79
79
  }
80
+ /**
81
+ * Attempt to recover truncated JSON (e.g., missing closing braces due to max tokens).
82
+ * Tracks brace depth and only recovers if there are unclosed `{` braces.
83
+ * Will NOT recover if there are unclosed `[` brackets (can't guess the content).
84
+ * @param jsonStr Potentially truncated JSON string
85
+ * @returns Recovered JSON string, or the original if unrecoverable
86
+ */
87
+ export function recoverTruncatedJson(jsonStr) {
88
+ let braceDepth = 0;
89
+ let bracketDepth = 0;
90
+ let inString = false;
91
+ let escaped = false;
92
+ for (const ch of jsonStr) {
93
+ if (escaped) {
94
+ escaped = false;
95
+ continue;
96
+ }
97
+ if (ch === "\\" && inString) {
98
+ escaped = true;
99
+ continue;
100
+ }
101
+ if (ch === '"') {
102
+ inString = !inString;
103
+ continue;
104
+ }
105
+ if (!inString) {
106
+ if (ch === "{")
107
+ braceDepth++;
108
+ if (ch === "}")
109
+ braceDepth--;
110
+ if (ch === "[")
111
+ bracketDepth++;
112
+ if (ch === "]")
113
+ bracketDepth--;
114
+ }
115
+ }
116
+ // Build recovery suffix
117
+ let suffix = "";
118
+ if (inString)
119
+ suffix += '"'; // Close unclosed string
120
+ if (braceDepth > 0 && bracketDepth === 0) {
121
+ suffix += "}".repeat(braceDepth);
122
+ }
123
+ return suffix ? jsonStr + suffix : jsonStr;
124
+ }
80
125
  /**
81
126
  * Efficiently get the last N lines of a string without splitting the whole string.
82
127
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wave-agent-sdk",
3
- "version": "0.14.3",
3
+ "version": "0.15.0",
4
4
  "description": "SDK for building AI-powered development tools and agents",
5
5
  "keywords": [
6
6
  "ai",
package/src/agent.ts CHANGED
@@ -218,13 +218,6 @@ export class Agent {
218
218
  }
219
219
  };
220
220
 
221
- // Wire up CWD change callback from AIManager to sync Agent's workdir
222
- this.aiManager.setOnCwdChange((newCwd) => {
223
- this.workdir = newCwd;
224
- this.container.register("Workdir", newCwd);
225
- this.options.callbacks?.onWorkdirChange?.(newCwd);
226
- });
227
-
228
221
  // Wire up message queue to process when agent becomes idle
229
222
  this.messageQueue.onMessageEnqueued = () => {
230
223
  // If the AI is NOT loading and command is not running, trigger dequeue
@@ -287,16 +280,6 @@ export class Agent {
287
280
  return this.workdir;
288
281
  }
289
282
 
290
- /**
291
- * Set the working directory
292
- * @param newCwd - The new working directory
293
- */
294
- public setWorkdir(newCwd: string): void {
295
- this.workdir = newCwd;
296
- this.container.register("Workdir", newCwd);
297
- this.options.callbacks?.onWorkdirChange?.(newCwd);
298
- }
299
-
300
283
  /** Get project memory content */
301
284
  public get projectMemory(): string {
302
285
  return this._projectMemoryContent;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Tool output size limits matching Claude Code patterns.
3
+ */
4
+
5
+ /** System-wide default max result size in characters. */
6
+ export const DEFAULT_MAX_RESULT_SIZE_CHARS = 50_000;
7
+
8
+ /** Per-command cap for skill bash substitution (inline/block). */
9
+ export const SKILL_BASH_MAX_OUTPUT_CHARS = 30_000;
10
+
11
+ /** Preview size in characters when output is persisted to disk. */
12
+ export const PREVIEW_SIZE_BYTES = 2_048;