wave-agent-sdk 0.18.3 → 0.18.5

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 (76) hide show
  1. package/builtin/skills/settings/ENV.md +1 -1
  2. package/builtin/skills/settings/MEMORY.md +76 -0
  3. package/builtin/skills/settings/SKILL.md +4 -4
  4. package/dist/agent.d.ts.map +1 -1
  5. package/dist/agent.js +0 -2
  6. package/dist/managers/MemoryRuleManager.d.ts +9 -0
  7. package/dist/managers/MemoryRuleManager.d.ts.map +1 -1
  8. package/dist/managers/MemoryRuleManager.js +18 -0
  9. package/dist/managers/aiManager.d.ts +1 -1
  10. package/dist/managers/aiManager.d.ts.map +1 -1
  11. package/dist/managers/aiManager.js +370 -345
  12. package/dist/managers/mcpManager.d.ts.map +1 -1
  13. package/dist/managers/mcpManager.js +22 -15
  14. package/dist/managers/messageManager.d.ts +32 -13
  15. package/dist/managers/messageManager.d.ts.map +1 -1
  16. package/dist/managers/messageManager.js +82 -76
  17. package/dist/prompts/index.d.ts +10 -4
  18. package/dist/prompts/index.d.ts.map +1 -1
  19. package/dist/prompts/index.js +21 -20
  20. package/dist/services/aiService.d.ts +2 -1
  21. package/dist/services/aiService.d.ts.map +1 -1
  22. package/dist/services/aiService.js +37 -10
  23. package/dist/services/memory.d.ts +1 -0
  24. package/dist/services/memory.d.ts.map +1 -1
  25. package/dist/services/memory.js +35 -17
  26. package/dist/tools/agentTool.d.ts.map +1 -1
  27. package/dist/tools/agentTool.js +24 -2
  28. package/dist/tools/bashTool.d.ts.map +1 -1
  29. package/dist/tools/bashTool.js +17 -10
  30. package/dist/tools/editTool.d.ts.map +1 -1
  31. package/dist/tools/editTool.js +7 -6
  32. package/dist/tools/readTool.js +2 -2
  33. package/dist/tools/writeTool.js +2 -2
  34. package/dist/utils/constants.d.ts +1 -1
  35. package/dist/utils/constants.js +1 -1
  36. package/dist/utils/containerSetup.d.ts.map +1 -1
  37. package/dist/utils/containerSetup.js +1 -0
  38. package/dist/utils/gitUtils.js +6 -6
  39. package/dist/utils/openaiClient.d.ts.map +1 -1
  40. package/dist/utils/openaiClient.js +33 -32
  41. package/dist/utils/path.d.ts +7 -0
  42. package/dist/utils/path.d.ts.map +1 -1
  43. package/dist/utils/path.js +9 -0
  44. package/dist/utils/taskReminder.d.ts +2 -3
  45. package/dist/utils/taskReminder.d.ts.map +1 -1
  46. package/dist/utils/taskReminder.js +7 -18
  47. package/dist/utils/worktreeUtils.d.ts.map +1 -1
  48. package/dist/utils/worktreeUtils.js +19 -13
  49. package/package.json +1 -1
  50. package/scripts/install_ripgrep.js +21 -1
  51. package/src/agent.ts +0 -5
  52. package/src/managers/MemoryRuleManager.ts +23 -0
  53. package/src/managers/aiManager.ts +473 -434
  54. package/src/managers/mcpManager.ts +26 -15
  55. package/src/managers/messageManager.ts +99 -82
  56. package/src/prompts/index.ts +33 -24
  57. package/src/services/aiService.ts +39 -12
  58. package/src/services/memory.ts +34 -16
  59. package/src/tools/agentTool.ts +24 -2
  60. package/src/tools/bashTool.ts +18 -9
  61. package/src/tools/editTool.ts +7 -9
  62. package/src/tools/readTool.ts +2 -2
  63. package/src/tools/writeTool.ts +2 -2
  64. package/src/utils/constants.ts +1 -1
  65. package/src/utils/containerSetup.ts +1 -0
  66. package/src/utils/gitUtils.ts +6 -6
  67. package/src/utils/openaiClient.ts +40 -31
  68. package/src/utils/path.ts +10 -0
  69. package/src/utils/taskReminder.ts +7 -19
  70. package/src/utils/worktreeUtils.ts +46 -28
  71. package/builtin/skills/settings/MEMORY_RULES.md +0 -60
  72. package/vendor/ripgrep/linux-aarch64/rg +0 -0
  73. package/vendor/ripgrep/macos-aarch64/rg +0 -0
  74. package/vendor/ripgrep/macos-x86_64/rg +0 -0
  75. package/vendor/ripgrep/windows-aarch64/rg.exe +0 -0
  76. package/vendor/ripgrep/windows-x86_64/rg.exe +0 -0
@@ -101,18 +101,45 @@ export async function callAgent(options) {
101
101
  fetchOptions: gatewayConfig.fetchOptions,
102
102
  fetch: gatewayConfig.fetch,
103
103
  });
104
- // Build system prompt content
105
- const systemContent = systemPrompt || "";
106
- // Add system prompt
107
- const systemMessage = {
108
- role: "system",
109
- content: systemContent,
110
- };
111
- // ChatCompletionMessageParam[] is already in OpenAI format, add system prompt to the beginning
112
- openaiMessages = [systemMessage, ...messages];
113
- // Apply cache control for Claude models
104
+ // Determine model early (needed for system prompt construction)
114
105
  const currentModel = model || modelConfig.model;
115
106
  const resolvedMaxTokens = options.maxTokens ?? modelConfig.maxTokens;
107
+ // Build system message content
108
+ let systemMessage;
109
+ if (Array.isArray(systemPrompt)) {
110
+ if (supportsPromptCaching(currentModel)) {
111
+ // For Claude models, map blocks to content parts with cache_control on cacheable blocks
112
+ const contentParts = systemPrompt.map((block) => {
113
+ const part = {
114
+ type: "text",
115
+ text: block.text,
116
+ };
117
+ if (block.cacheable) {
118
+ part.cache_control = { type: "ephemeral" };
119
+ }
120
+ return part;
121
+ });
122
+ systemMessage = {
123
+ role: "system",
124
+ content: contentParts,
125
+ };
126
+ }
127
+ else {
128
+ // For non-Claude models, join blocks into a single string
129
+ systemMessage = {
130
+ role: "system",
131
+ content: systemPrompt.map((b) => b.text).join("\n\n"),
132
+ };
133
+ }
134
+ }
135
+ else {
136
+ systemMessage = {
137
+ role: "system",
138
+ content: systemPrompt || "",
139
+ };
140
+ }
141
+ // ChatCompletionMessageParam[] is already in OpenAI format, add system prompt to the beginning
142
+ openaiMessages = [systemMessage, ...messages];
116
143
  processedTools = tools;
117
144
  if (supportsPromptCaching(currentModel)) {
118
145
  openaiMessages = transformMessagesForExplicitCache(openaiMessages, currentModel);
@@ -4,6 +4,7 @@ export declare class MemoryService {
4
4
  private _cachedProjectMemory;
5
5
  private _cachedUserMemory;
6
6
  private _cachedCombinedMemory;
7
+ private _cachedAutoMemoryContent;
7
8
  constructor(container: Container);
8
9
  get cachedProjectMemory(): string;
9
10
  get cachedUserMemory(): string;
@@ -1 +1 @@
1
- {"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../../src/services/memory.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAIlD,qBAAa,aAAa;IAKZ,OAAO,CAAC,SAAS;IAJ7B,OAAO,CAAC,oBAAoB,CAAc;IAC1C,OAAO,CAAC,iBAAiB,CAAc;IACvC,OAAO,CAAC,qBAAqB,CAAuB;gBAEhC,SAAS,EAAE,SAAS;IAExC,IAAW,mBAAmB,IAAI,MAAM,CAEvC;IAED,IAAW,gBAAgB,IAAI,MAAM,CAEpC;IAEM,UAAU,IAAI,IAAI;IAMzB;;;OAGG;IACH,sBAAsB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;IAU/C;;OAEG;IACG,yBAAyB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA2B/D;;OAEG;IACG,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAiBtD,oBAAoB,IAAI,OAAO,CAAC,IAAI,CAAC;IA8BrC,oBAAoB,IAAI,OAAO,CAAC,MAAM,CAAC;IAoCvC,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAuChD,wBAAwB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CAgBjE"}
1
+ {"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../../src/services/memory.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAIlD,qBAAa,aAAa;IAMZ,OAAO,CAAC,SAAS;IAL7B,OAAO,CAAC,oBAAoB,CAAuB;IACnD,OAAO,CAAC,iBAAiB,CAAuB;IAChD,OAAO,CAAC,qBAAqB,CAAuB;IACpD,OAAO,CAAC,wBAAwB,CAAuB;gBAEnC,SAAS,EAAE,SAAS;IAExC,IAAW,mBAAmB,IAAI,MAAM,CAEvC;IAED,IAAW,gBAAgB,IAAI,MAAM,CAEpC;IAEM,UAAU,IAAI,IAAI;IAOzB;;;OAGG;IACH,sBAAsB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;IAU/C;;OAEG;IACG,yBAAyB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA2B/D;;OAEG;IACG,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAsBtD,oBAAoB,IAAI,OAAO,CAAC,IAAI,CAAC;IA8BrC,oBAAoB,IAAI,OAAO,CAAC,MAAM,CAAC;IAyCvC,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IA6ChD,wBAAwB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CAgBjE"}
@@ -8,20 +8,22 @@ import { pathEncoder } from "../utils/pathEncoder.js";
8
8
  export class MemoryService {
9
9
  constructor(container) {
10
10
  this.container = container;
11
- this._cachedProjectMemory = "";
12
- this._cachedUserMemory = "";
11
+ this._cachedProjectMemory = null;
12
+ this._cachedUserMemory = null;
13
13
  this._cachedCombinedMemory = null;
14
+ this._cachedAutoMemoryContent = null;
14
15
  }
15
16
  get cachedProjectMemory() {
16
- return this._cachedProjectMemory;
17
+ return this._cachedProjectMemory ?? "";
17
18
  }
18
19
  get cachedUserMemory() {
19
- return this._cachedUserMemory;
20
+ return this._cachedUserMemory ?? "";
20
21
  }
21
22
  clearCache() {
22
- this._cachedProjectMemory = "";
23
- this._cachedUserMemory = "";
23
+ this._cachedProjectMemory = null;
24
+ this._cachedUserMemory = null;
24
25
  this._cachedCombinedMemory = null;
26
+ this._cachedAutoMemoryContent = null;
25
27
  }
26
28
  /**
27
29
  * Get the project-specific auto-memory directory.
@@ -66,15 +68,20 @@ export class MemoryService {
66
68
  * Get the first 200 lines of MEMORY.md.
67
69
  */
68
70
  async getAutoMemoryContent(workdir) {
71
+ if (this._cachedAutoMemoryContent !== null) {
72
+ return this._cachedAutoMemoryContent;
73
+ }
69
74
  const memoryDir = this.getAutoMemoryDirectory(workdir);
70
75
  const memoryFile = path.join(memoryDir, "MEMORY.md");
71
76
  try {
72
77
  const content = await fs.readFile(memoryFile, "utf-8");
73
78
  const lines = content.split("\n").slice(0, 200);
74
- return lines.join("\n");
79
+ this._cachedAutoMemoryContent = lines.join("\n");
80
+ return this._cachedAutoMemoryContent;
75
81
  }
76
82
  catch (error) {
77
83
  if (error.code === "ENOENT") {
84
+ this._cachedAutoMemoryContent = "";
78
85
  return "";
79
86
  }
80
87
  logger.error("Failed to read auto-memory content:", error);
@@ -110,6 +117,9 @@ export class MemoryService {
110
117
  }
111
118
  }
112
119
  async getUserMemoryContent() {
120
+ if (this._cachedUserMemory !== null) {
121
+ return this._cachedUserMemory;
122
+ }
113
123
  try {
114
124
  await this.ensureUserMemoryFile();
115
125
  const content = await fs.readFile(USER_MEMORY_FILE, "utf-8");
@@ -127,13 +137,15 @@ export class MemoryService {
127
137
  claudeMemoryPath,
128
138
  contentLength: claudeContent.length,
129
139
  });
130
- return claudeContent;
140
+ this._cachedUserMemory = claudeContent;
141
+ return this._cachedUserMemory;
131
142
  }
132
143
  catch {
133
144
  // CLAUDE.md doesn't exist or can't be read, return the default template
134
145
  }
135
146
  }
136
- return content;
147
+ this._cachedUserMemory = content;
148
+ return this._cachedUserMemory;
137
149
  }
138
150
  catch (error) {
139
151
  logger.error("Failed to read user memory:", error);
@@ -141,6 +153,9 @@ export class MemoryService {
141
153
  }
142
154
  }
143
155
  async readMemoryFile(workdir) {
156
+ if (this._cachedProjectMemory !== null) {
157
+ return this._cachedProjectMemory;
158
+ }
144
159
  const memoryFilePath = path.join(workdir, "AGENTS.md");
145
160
  try {
146
161
  const content = await fs.readFile(memoryFilePath, "utf-8");
@@ -148,7 +163,8 @@ export class MemoryService {
148
163
  memoryFilePath,
149
164
  contentLength: content.length,
150
165
  });
151
- return content;
166
+ this._cachedProjectMemory = content;
167
+ return this._cachedProjectMemory;
152
168
  }
153
169
  catch (error) {
154
170
  if (error.code === "ENOENT") {
@@ -160,13 +176,15 @@ export class MemoryService {
160
176
  memoryFilePath: claudeMemoryPath,
161
177
  contentLength: content.length,
162
178
  });
163
- return content;
179
+ this._cachedProjectMemory = content;
180
+ return this._cachedProjectMemory;
164
181
  }
165
182
  catch (claudeError) {
166
183
  if (claudeError.code === "ENOENT") {
167
184
  logger.debug("Neither AGENTS.md nor CLAUDE.md found", {
168
185
  memoryFilePath,
169
186
  });
187
+ this._cachedProjectMemory = "";
170
188
  return "";
171
189
  }
172
190
  logger.error("Failed to read CLAUDE.md fallback", {
@@ -184,15 +202,15 @@ export class MemoryService {
184
202
  if (this._cachedCombinedMemory !== null) {
185
203
  return this._cachedCombinedMemory;
186
204
  }
187
- this._cachedProjectMemory = await this.readMemoryFile(workdir);
188
- this._cachedUserMemory = await this.getUserMemoryContent();
205
+ const projectMemory = await this.readMemoryFile(workdir);
206
+ const userMemory = await this.getUserMemoryContent();
189
207
  let combined = "";
190
- if (this._cachedProjectMemory.trim())
191
- combined += this._cachedProjectMemory;
192
- if (this._cachedUserMemory.trim()) {
208
+ if (projectMemory.trim())
209
+ combined += projectMemory;
210
+ if (userMemory.trim()) {
193
211
  if (combined)
194
212
  combined += "\n\n";
195
- combined += this._cachedUserMemory;
213
+ combined += userMemory;
196
214
  }
197
215
  this._cachedCombinedMemory = combined;
198
216
  return combined;
@@ -1 +1 @@
1
- {"version":3,"file":"agentTool.d.ts","sourceRoot":"","sources":["../../src/tools/agentTool.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAA2B,MAAM,YAAY,CAAC;AAStE;;GAEG;AACH,eAAO,MAAM,SAAS,EAAE,UA+RvB,CAAC"}
1
+ {"version":3,"file":"agentTool.d.ts","sourceRoot":"","sources":["../../src/tools/agentTool.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAA2B,MAAM,YAAY,CAAC;AAStE;;GAEG;AACH,eAAO,MAAM,SAAS,EAAE,UAqTvB,CAAC"}
@@ -214,10 +214,32 @@ When using the Agent tool, you must specify a subagent_type parameter to select
214
214
  }
215
215
  catch (error) {
216
216
  if (!isBackgrounded) {
217
+ // Extract content from the subagent's last assistant message.
218
+ // aiManager.sendAIMessage() catch block already added an error
219
+ // block via addErrorBlock(), so the error info is in the message
220
+ // history — just return it directly.
221
+ let content = "";
222
+ try {
223
+ const messages = instance.messageManager.getMessages();
224
+ const lastAssistant = messages
225
+ .filter((m) => m.role === "assistant")
226
+ .pop();
227
+ if (lastAssistant) {
228
+ content = lastAssistant.blocks
229
+ .filter((b) => b.type === "text" || b.type === "error")
230
+ .map((b) => b.content)
231
+ .join("\n")
232
+ .trim();
233
+ }
234
+ }
235
+ catch {
236
+ // Ignore errors when extracting messages
237
+ }
238
+ const errorMsg = `Agent delegation failed: ${error instanceof Error ? error.message : String(error)}`;
217
239
  resolve({
218
240
  success: false,
219
- content: "",
220
- error: `Agent delegation failed: ${error instanceof Error ? error.message : String(error)}`,
241
+ content: content || errorMsg,
242
+ error: errorMsg,
221
243
  shortResult: "Delegation error",
222
244
  });
223
245
  }
@@ -1 +1 @@
1
- {"version":3,"file":"bashTool.d.ts","sourceRoot":"","sources":["../../src/tools/bashTool.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,UAAU,EAA2B,MAAM,YAAY,CAAC;AAuBtE;;GAEG;AACH,eAAO,MAAM,QAAQ,EAAE,UAmiBtB,CAAC"}
1
+ {"version":3,"file":"bashTool.d.ts","sourceRoot":"","sources":["../../src/tools/bashTool.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,UAAU,EAA2B,MAAM,YAAY,CAAC;AAuBtE;;GAEG;AACH,eAAO,MAAM,QAAQ,EAAE,UA2iBtB,CAAC"}
@@ -4,6 +4,7 @@ import * as path from "path";
4
4
  import * as os from "os";
5
5
  import { logger } from "../utils/globalLogger.js";
6
6
  import { resolveShellPath } from "../utils/shellResolver.js";
7
+ import { toPosixPath } from "../utils/path.js";
7
8
  import { stripAnsiColors } from "../utils/stringUtils.js";
8
9
  import { processToolResult } from "../utils/toolResultStorage.js";
9
10
  import { BASH_MAX_OUTPUT_CHARS } from "../constants/toolLimits.js";
@@ -211,7 +212,8 @@ The working directory persists between commands. Try to maintain your current wo
211
212
  return new Promise((resolve) => {
212
213
  // Create a temporary file to store the CWD
213
214
  const tempCwdFile = path.join(os.tmpdir(), `wave_cwd_${Date.now()}_${Math.random().toString(36).substring(2, 11)}.tmp`);
214
- const wrappedCommand = `${command} && pwd -P >| ${tempCwdFile}`;
215
+ const tempCwdFileForBash = toPosixPath(tempCwdFile);
216
+ const wrappedCommand = `${command} && pwd -P >| ${tempCwdFileForBash}`;
215
217
  const child = spawn(wrappedCommand, {
216
218
  shell: shellPath || true,
217
219
  stdio: "pipe",
@@ -226,6 +228,17 @@ The working directory persists between commands. Try to maintain your current wo
226
228
  let isAborted = false;
227
229
  let isBackgrounded = false;
228
230
  let isFinished = false;
231
+ // Best-effort cleanup of the temp CWD file — used by abort/error/exit paths
232
+ const cleanupTempFile = () => {
233
+ try {
234
+ if (fs.existsSync(tempCwdFile)) {
235
+ fs.unlinkSync(tempCwdFile);
236
+ }
237
+ }
238
+ catch {
239
+ // ignore — best-effort cleanup
240
+ }
241
+ };
229
242
  const updateRealtimeResults = () => {
230
243
  if (isAborted || isBackgrounded || isFinished)
231
244
  return;
@@ -352,6 +365,7 @@ The working directory persists between commands. Try to maintain your current wo
352
365
  }
353
366
  }
354
367
  }
368
+ cleanupTempFile();
355
369
  const processedOutput = processToolResult(outputBuffer + (errorBuffer ? "\n" + errorBuffer : ""), BASH_MAX_OUTPUT_CHARS, "bash");
356
370
  resolve({
357
371
  success: false,
@@ -410,15 +424,7 @@ The working directory persists between commands. Try to maintain your current wo
410
424
  newCwd = undefined;
411
425
  }
412
426
  finally {
413
- // Ensure temp file is cleaned up even if reading fails
414
- try {
415
- if (fs.existsSync(tempCwdFile)) {
416
- fs.unlinkSync(tempCwdFile);
417
- }
418
- }
419
- catch (fileError) {
420
- logger.error("Failed to clean up temp CWD file:", fileError);
421
- }
427
+ cleanupTempFile();
422
428
  }
423
429
  // If CWD changed, call the onCwdChange callback and add notification
424
430
  let cwdMessage;
@@ -462,6 +468,7 @@ The working directory persists between commands. Try to maintain your current wo
462
468
  if (timeoutHandle) {
463
469
  clearTimeout(timeoutHandle);
464
470
  }
471
+ cleanupTempFile();
465
472
  resolve({
466
473
  success: false,
467
474
  content: "",
@@ -1 +1 @@
1
- {"version":3,"file":"editTool.d.ts","sourceRoot":"","sources":["../../src/tools/editTool.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAA2B,MAAM,YAAY,CAAC;AAgBtE;;GAEG;AACH,eAAO,MAAM,QAAQ,EAAE,UAkRtB,CAAC"}
1
+ {"version":3,"file":"editTool.d.ts","sourceRoot":"","sources":["../../src/tools/editTool.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAA2B,MAAM,YAAY,CAAC;AAgBtE;;GAEG;AACH,eAAO,MAAM,QAAQ,EAAE,UAgRtB,CAAC"}
@@ -93,11 +93,13 @@ Usage:
93
93
  error: "old_string and new_string must be different",
94
94
  };
95
95
  }
96
- // Touch file to track it in context
97
- context.messageManager?.touchFile(filePath);
98
- // Enforce read-before-edit: the file must have been read first
99
- if (context.messageManager &&
100
- !context.messageManager.hasFileInContext(filePath)) {
96
+ // Trigger conditional rule loading for this file
97
+ context.messageManager?.triggerFileRead(filePath);
98
+ // Enforce read-before-edit: the file must have been read or written first.
99
+ // readFileState is populated by Read, Write, and Edit tools — single source
100
+ // of truth, aligned with Claude Code's readFileState approach.
101
+ const resolvedPath = resolvePath(filePath, context.workdir);
102
+ if (!context.readFileState?.has(resolvedPath)) {
101
103
  return {
102
104
  success: false,
103
105
  content: "",
@@ -105,7 +107,6 @@ Usage:
105
107
  };
106
108
  }
107
109
  try {
108
- const resolvedPath = resolvePath(filePath, context.workdir);
109
110
  // Read file content
110
111
  let originalContent;
111
112
  try {
@@ -169,8 +169,8 @@ Usage:
169
169
  error: "file_path parameter is required and must be a string",
170
170
  };
171
171
  }
172
- // Touch file to track it in context
173
- context.messageManager?.touchFile(filePath);
172
+ // Trigger conditional rule loading for this file
173
+ context.messageManager?.triggerFileRead(filePath);
174
174
  // Check for binary document formats
175
175
  if (isBinaryDocument(filePath)) {
176
176
  const isPdf = filePath.toLowerCase().endsWith(".pdf");
@@ -59,8 +59,8 @@ Usage:
59
59
  error: "content parameter is required and must be a string",
60
60
  };
61
61
  }
62
- // Touch file to track it in context
63
- context.messageManager?.touchFile(filePath);
62
+ // Trigger conditional rule loading for this file
63
+ context.messageManager?.triggerFileRead(filePath);
64
64
  try {
65
65
  const resolvedPath = resolvePath(filePath, context.workdir);
66
66
  // Check if file already exists
@@ -22,5 +22,5 @@ export declare const USER_MEMORY_FILE: string;
22
22
  * AI related constants
23
23
  */
24
24
  export declare const DEFAULT_WAVE_MAX_INPUT_TOKENS = 200000;
25
- export declare const DEFAULT_WAVE_MAX_OUTPUT_TOKENS = 16384;
25
+ export declare const DEFAULT_WAVE_MAX_OUTPUT_TOKENS = 32000;
26
26
  //# sourceMappingURL=constants.d.ts.map
@@ -24,4 +24,4 @@ export const USER_MEMORY_FILE = path.join(DATA_DIRECTORY, "AGENTS.md");
24
24
  * AI related constants
25
25
  */
26
26
  export const DEFAULT_WAVE_MAX_INPUT_TOKENS = 200000; // Default token limit
27
- export const DEFAULT_WAVE_MAX_OUTPUT_TOKENS = 16384; // Default output token limit
27
+ export const DEFAULT_WAVE_MAX_OUTPUT_TOKENS = 32000; // Default output token limit (aligned with Claude Code)
@@ -1 +1 @@
1
- {"version":3,"file":"containerSetup.d.ts","sourceRoot":"","sources":["../../src/utils/containerSetup.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AA0B3C,OAAO,EAAE,oBAAoB,EAAE,MAAM,qCAAqC,CAAC;AAM3E,OAAO,KAAK,EAAE,YAAY,EAAmB,MAAM,mBAAmB,CAAC;AACvE,OAAO,KAAK,EACV,cAAc,EACd,KAAK,EACL,IAAI,EACJ,cAAc,EAEf,MAAM,mBAAmB,CAAC;AAM3B,MAAM,WAAW,0BAA0B;IACzC,OAAO,EAAE,YAAY,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,oBAAoB,EAAE,oBAAoB,CAAC;IAC3C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,OAAO,CAAC;IAGhB,uBAAuB,EAAE,CAAC,KAAK,EAAE,cAAc,EAAE,KAAK,IAAI,CAAC;IAC3D,aAAa,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,IAAI,CAAC;IACvC,sBAAsB,EAAE,CAAC,IAAI,EAAE,cAAc,KAAK,IAAI,CAAC;IACvD,wBAAwB,EAAE,CAAC,IAAI,EAAE,cAAc,KAAK,IAAI,CAAC;IACzD,iBAAiB,EAAE,CAAC,IAAI,EAAE,cAAc,KAAK,IAAI,CAAC;IAClD,iBAAiB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACnD,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CAClC;AAED,wBAAgB,mBAAmB,CACjC,YAAY,EAAE,0BAA0B,GACvC,SAAS,CA6SX"}
1
+ {"version":3,"file":"containerSetup.d.ts","sourceRoot":"","sources":["../../src/utils/containerSetup.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AA0B3C,OAAO,EAAE,oBAAoB,EAAE,MAAM,qCAAqC,CAAC;AAM3E,OAAO,KAAK,EAAE,YAAY,EAAmB,MAAM,mBAAmB,CAAC;AACvE,OAAO,KAAK,EACV,cAAc,EACd,KAAK,EACL,IAAI,EACJ,cAAc,EAEf,MAAM,mBAAmB,CAAC;AAM3B,MAAM,WAAW,0BAA0B;IACzC,OAAO,EAAE,YAAY,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,oBAAoB,EAAE,oBAAoB,CAAC;IAC3C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,OAAO,CAAC;IAGhB,uBAAuB,EAAE,CAAC,KAAK,EAAE,cAAc,EAAE,KAAK,IAAI,CAAC;IAC3D,aAAa,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,IAAI,CAAC;IACvC,sBAAsB,EAAE,CAAC,IAAI,EAAE,cAAc,KAAK,IAAI,CAAC;IACvD,wBAAwB,EAAE,CAAC,IAAI,EAAE,cAAc,KAAK,IAAI,CAAC;IACzD,iBAAiB,EAAE,CAAC,IAAI,EAAE,cAAc,KAAK,IAAI,CAAC;IAClD,iBAAiB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACnD,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CAClC;AAED,wBAAgB,mBAAmB,CACjC,YAAY,EAAE,0BAA0B,GACvC,SAAS,CA8SX"}
@@ -65,6 +65,7 @@ export function setupAgentContainer(setupOptions) {
65
65
  logger.error("Failed to sync task list with session:", error);
66
66
  });
67
67
  }
68
+ memoryService.clearCache();
68
69
  callbacks.onSessionIdChange?.(sessionId);
69
70
  },
70
71
  },
@@ -1,6 +1,6 @@
1
1
  import * as path from "node:path";
2
2
  import * as fsSync from "node:fs";
3
- import { execSync } from "node:child_process";
3
+ import { execFileSync } from "node:child_process";
4
4
  /**
5
5
  * Check if a directory is a git repository
6
6
  * @param dirPath Directory path
@@ -30,7 +30,7 @@ export function isGitRepository(dirPath) {
30
30
  */
31
31
  export function getGitRepoRoot(cwd) {
32
32
  try {
33
- return execSync("git rev-parse --show-toplevel", {
33
+ return execFileSync("git", ["rev-parse", "--show-toplevel"], {
34
34
  cwd,
35
35
  encoding: "utf8",
36
36
  stdio: ["ignore", "pipe", "ignore"],
@@ -47,7 +47,7 @@ export function getGitRepoRoot(cwd) {
47
47
  */
48
48
  export function getGitCommonDir(cwd) {
49
49
  try {
50
- const commonDir = execSync("git rev-parse --git-common-dir", {
50
+ const commonDir = execFileSync("git", ["rev-parse", "--git-common-dir"], {
51
51
  cwd,
52
52
  encoding: "utf8",
53
53
  stdio: ["ignore", "pipe", "ignore"],
@@ -65,7 +65,7 @@ export function getGitCommonDir(cwd) {
65
65
  */
66
66
  export function getGitMainRepoRoot(cwd) {
67
67
  try {
68
- const output = execSync("git worktree list --porcelain", {
68
+ const output = execFileSync("git", ["worktree", "list", "--porcelain"], {
69
69
  cwd,
70
70
  encoding: "utf8",
71
71
  stdio: ["ignore", "pipe", "ignore"],
@@ -223,7 +223,7 @@ export function getDefaultRemoteBranch(cwd) {
223
223
  */
224
224
  export function hasUncommittedChanges(cwd) {
225
225
  try {
226
- const status = execSync("git status --porcelain", {
226
+ const status = execFileSync("git", ["status", "--porcelain"], {
227
227
  cwd,
228
228
  encoding: "utf8",
229
229
  stdio: ["ignore", "pipe", "ignore"],
@@ -243,7 +243,7 @@ export function hasUncommittedChanges(cwd) {
243
243
  export function hasNewCommits(cwd, baseBranch) {
244
244
  try {
245
245
  const range = baseBranch ? `${baseBranch}..HEAD` : "@{u}..HEAD";
246
- const log = execSync(`git log ${range} --oneline`, {
246
+ const log = execFileSync("git", ["log", range, "--oneline"], {
247
247
  cwd,
248
248
  encoding: "utf8",
249
249
  stdio: ["ignore", "pipe", "ignore"],
@@ -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;YAwIN,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;AAyBnD,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;YAuHN,oBAAoB;CAyCpC"}
@@ -1,4 +1,18 @@
1
1
  import { logger } from "./globalLogger.js";
2
+ const MAX_RETRIES = 10;
3
+ const BASE_DELAY_MS = 500;
4
+ const MAX_DELAY_MS = 32000;
5
+ function getRetryDelay(attempt, retryAfterHeader) {
6
+ if (retryAfterHeader) {
7
+ const seconds = parseInt(retryAfterHeader, 10);
8
+ if (!isNaN(seconds)) {
9
+ return seconds * 1000;
10
+ }
11
+ }
12
+ const baseDelay = Math.min(BASE_DELAY_MS * Math.pow(2, attempt - 1), MAX_DELAY_MS);
13
+ const jitter = Math.random() * 0.25 * baseDelay;
14
+ return baseDelay + jitter;
15
+ }
2
16
  export class OpenAIClient {
3
17
  constructor(config) {
4
18
  this.config = config;
@@ -31,11 +45,10 @@ export class OpenAIClient {
31
45
  };
32
46
  const fetchFn = customFetch || fetch;
33
47
  let lastError;
34
- const maxRetries = 5;
35
- const initialDelay = 2000;
36
- for (let attempt = 0; attempt <= maxRetries; attempt++) {
48
+ let lastRetryAfter = null;
49
+ for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
37
50
  if (attempt > 0) {
38
- const delay = initialDelay * Math.pow(2, attempt - 1);
51
+ const delay = getRetryDelay(attempt, lastRetryAfter);
39
52
  await new Promise((resolve) => setTimeout(resolve, delay));
40
53
  }
41
54
  let response;
@@ -52,12 +65,13 @@ export class OpenAIClient {
52
65
  if (e instanceof Error && e.name === "AbortError") {
53
66
  throw e;
54
67
  }
55
- if (attempt < maxRetries) {
68
+ if (attempt < MAX_RETRIES) {
56
69
  logger.warn("OpenAI API network error, retrying...", {
57
70
  attempt: attempt + 1,
58
71
  error: e,
59
72
  });
60
73
  lastError = e;
74
+ lastRetryAfter = null;
61
75
  continue;
62
76
  }
63
77
  throw e;
@@ -77,42 +91,24 @@ export class OpenAIClient {
77
91
  };
78
92
  }
79
93
  }
80
- let errorBody;
94
+ let responseText = "";
81
95
  try {
82
- const text = await response.text();
83
- try {
84
- errorBody = JSON.parse(text);
85
- }
86
- catch (e) {
87
- logger.error("Failed to parse error response body as JSON", {
88
- error: e,
89
- text,
90
- });
91
- errorBody = text;
92
- }
96
+ responseText = await response.text();
93
97
  }
94
98
  catch (e) {
95
99
  logger.error("Failed to read error response text", { error: e });
96
- errorBody = {};
97
100
  }
98
- const error = new Error(typeof errorBody === "object" &&
99
- errorBody !== null &&
100
- "error" in errorBody &&
101
- typeof errorBody.error === "object" &&
102
- errorBody.error !== null &&
103
- "message" in errorBody.error
104
- ? String(errorBody.error.message)
105
- : typeof errorBody === "string"
106
- ? errorBody
107
- : response.statusText);
101
+ const error = new Error(`HTTP ${response.status}${response.statusText ? ` ${response.statusText}` : ""}: ${responseText}`);
108
102
  error.status = response.status;
109
- error.body = errorBody;
103
+ error.body = responseText;
110
104
  const retryableStatus = response.status === 429 ||
111
105
  (response.status >= 500 && response.status !== 501);
112
- if (retryableStatus && attempt < maxRetries) {
106
+ if (retryableStatus && attempt < MAX_RETRIES) {
107
+ lastRetryAfter = response.headers.get("retry-after");
113
108
  logger.warn("OpenAI API error, retrying...", {
114
109
  attempt: attempt + 1,
115
110
  status: response.status,
111
+ retryAfter: lastRetryAfter,
116
112
  });
117
113
  lastError = error;
118
114
  continue;
@@ -120,7 +116,7 @@ export class OpenAIClient {
120
116
  logger.error("OpenAI API Error:", {
121
117
  status: response.status,
122
118
  statusText: response.statusText,
123
- errorBody,
119
+ body: responseText,
124
120
  });
125
121
  throw error;
126
122
  }
@@ -147,12 +143,17 @@ export class OpenAIClient {
147
143
  const data = trimmedLine.slice(5).trim();
148
144
  if (data === "[DONE]")
149
145
  return;
146
+ if (!data)
147
+ continue; // Skip empty data lines (keepalive heartbeats)
150
148
  try {
151
149
  const json = JSON.parse(data);
152
150
  yield json;
153
151
  }
154
152
  catch (e) {
155
- logger.error("Failed to parse stream chunk", { error: e, data });
153
+ logger.error("Failed to parse stream chunk", {
154
+ error: e instanceof Error ? e.message : String(e),
155
+ data,
156
+ });
156
157
  }
157
158
  }
158
159
  }
@@ -12,4 +12,11 @@ export declare function resolvePath(filePath: string, workdir: string): string;
12
12
  * @returns Path for display (relative or absolute)
13
13
  */
14
14
  export declare function getDisplayPath(filePath: string, workdir: string): string;
15
+ /**
16
+ * Convert backslashes to forward slashes for shell compatibility on Windows.
17
+ * On Windows, os.tmpdir() returns paths with backslashes (e.g., C:\Users\foo\Temp),
18
+ * which are treated as escape characters when interpolated into shell command strings.
19
+ * Returns the path as-is on non-Windows platforms.
20
+ */
21
+ export declare function toPosixPath(p: string): string;
15
22
  //# sourceMappingURL=path.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"path.d.ts","sourceRoot":"","sources":["../../src/utils/path.ts"],"names":[],"mappings":"AAIA;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAarE;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAwBxE"}
1
+ {"version":3,"file":"path.d.ts","sourceRoot":"","sources":["../../src/utils/path.ts"],"names":[],"mappings":"AAIA;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAarE;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAwBxE;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAE7C"}
@@ -46,3 +46,12 @@ export function getDisplayPath(filePath, workdir) {
46
46
  }
47
47
  return filePath;
48
48
  }
49
+ /**
50
+ * Convert backslashes to forward slashes for shell compatibility on Windows.
51
+ * On Windows, os.tmpdir() returns paths with backslashes (e.g., C:\Users\foo\Temp),
52
+ * which are treated as escape characters when interpolated into shell command strings.
53
+ * Returns the path as-is on non-Windows platforms.
54
+ */
55
+ export function toPosixPath(p) {
56
+ return process.platform === "win32" ? p.replace(/\\/g, "/") : p;
57
+ }
@@ -1,6 +1,5 @@
1
1
  import type { Message } from "../types/messaging.js";
2
2
  import type { Task } from "../types/tasks.js";
3
- import type { ChatCompletionMessageParam } from "openai/resources.js";
4
3
  export declare const TASK_REMINDER_CONFIG: {
5
4
  TURNS_SINCE_WRITE: number;
6
5
  TURNS_BETWEEN_REMINDERS: number;
@@ -10,8 +9,8 @@ export declare function getTaskReminderTurnCounts(messages: Message[]): {
10
9
  turnsSinceLastReminder: number;
11
10
  };
12
11
  export declare function buildTaskReminderText(tasks: Task[]): string;
13
- export declare function maybeInjectTaskReminder(messages: ChatCompletionMessageParam[], turnCounts: {
12
+ export declare function maybeInjectTaskReminder(turnCounts: {
14
13
  turnsSinceLastTaskManagement: number;
15
14
  turnsSinceLastReminder: number;
16
- }, tasks: Task[]): void;
15
+ }, tasks: Task[]): string | null;
17
16
  //# sourceMappingURL=taskReminder.d.ts.map