wave-agent-sdk 0.18.3 → 0.18.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.
Files changed (53) hide show
  1. package/dist/agent.d.ts.map +1 -1
  2. package/dist/agent.js +0 -2
  3. package/dist/managers/MemoryRuleManager.d.ts +9 -0
  4. package/dist/managers/MemoryRuleManager.d.ts.map +1 -1
  5. package/dist/managers/MemoryRuleManager.js +18 -0
  6. package/dist/managers/aiManager.d.ts +1 -1
  7. package/dist/managers/aiManager.d.ts.map +1 -1
  8. package/dist/managers/aiManager.js +370 -345
  9. package/dist/managers/messageManager.d.ts +32 -13
  10. package/dist/managers/messageManager.d.ts.map +1 -1
  11. package/dist/managers/messageManager.js +82 -76
  12. package/dist/prompts/index.d.ts +10 -4
  13. package/dist/prompts/index.d.ts.map +1 -1
  14. package/dist/prompts/index.js +21 -20
  15. package/dist/services/aiService.d.ts +2 -1
  16. package/dist/services/aiService.d.ts.map +1 -1
  17. package/dist/services/aiService.js +37 -10
  18. package/dist/services/memory.d.ts +1 -0
  19. package/dist/services/memory.d.ts.map +1 -1
  20. package/dist/services/memory.js +35 -17
  21. package/dist/tools/agentTool.d.ts.map +1 -1
  22. package/dist/tools/agentTool.js +24 -2
  23. package/dist/tools/editTool.js +3 -3
  24. package/dist/tools/readTool.js +2 -2
  25. package/dist/tools/writeTool.js +2 -2
  26. package/dist/utils/containerSetup.d.ts.map +1 -1
  27. package/dist/utils/containerSetup.js +1 -0
  28. package/dist/utils/openaiClient.d.ts.map +1 -1
  29. package/dist/utils/openaiClient.js +28 -7
  30. package/dist/utils/taskReminder.d.ts +2 -3
  31. package/dist/utils/taskReminder.d.ts.map +1 -1
  32. package/dist/utils/taskReminder.js +7 -18
  33. package/package.json +1 -1
  34. package/scripts/install_ripgrep.js +21 -1
  35. package/src/agent.ts +0 -5
  36. package/src/managers/MemoryRuleManager.ts +23 -0
  37. package/src/managers/aiManager.ts +473 -434
  38. package/src/managers/messageManager.ts +99 -82
  39. package/src/prompts/index.ts +33 -24
  40. package/src/services/aiService.ts +39 -12
  41. package/src/services/memory.ts +34 -16
  42. package/src/tools/agentTool.ts +24 -2
  43. package/src/tools/editTool.ts +3 -3
  44. package/src/tools/readTool.ts +2 -2
  45. package/src/tools/writeTool.ts +2 -2
  46. package/src/utils/containerSetup.ts +1 -0
  47. package/src/utils/openaiClient.ts +35 -7
  48. package/src/utils/taskReminder.ts +7 -19
  49. package/vendor/ripgrep/linux-aarch64/rg +0 -0
  50. package/vendor/ripgrep/macos-aarch64/rg +0 -0
  51. package/vendor/ripgrep/macos-x86_64/rg +0 -0
  52. package/vendor/ripgrep/windows-aarch64/rg.exe +0 -0
  53. package/vendor/ripgrep/windows-x86_64/rg.exe +0 -0
@@ -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
  }
@@ -93,11 +93,11 @@ 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);
96
+ // Trigger conditional rule loading for this file
97
+ context.messageManager?.triggerFileRead(filePath);
98
98
  // Enforce read-before-edit: the file must have been read first
99
99
  if (context.messageManager &&
100
- !context.messageManager.hasFileInContext(filePath)) {
100
+ !context.messageManager.hasFileBeenRead(filePath)) {
101
101
  return {
102
102
  success: false,
103
103
  content: "",
@@ -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
@@ -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 +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;YA0IN,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;
@@ -109,10 +123,12 @@ export class OpenAIClient {
109
123
  error.body = errorBody;
110
124
  const retryableStatus = response.status === 429 ||
111
125
  (response.status >= 500 && response.status !== 501);
112
- if (retryableStatus && attempt < maxRetries) {
126
+ if (retryableStatus && attempt < MAX_RETRIES) {
127
+ lastRetryAfter = response.headers.get("retry-after");
113
128
  logger.warn("OpenAI API error, retrying...", {
114
129
  attempt: attempt + 1,
115
130
  status: response.status,
131
+ retryAfter: lastRetryAfter,
116
132
  });
117
133
  lastError = error;
118
134
  continue;
@@ -147,12 +163,17 @@ export class OpenAIClient {
147
163
  const data = trimmedLine.slice(5).trim();
148
164
  if (data === "[DONE]")
149
165
  return;
166
+ if (!data)
167
+ continue; // Skip empty data lines (keepalive heartbeats)
150
168
  try {
151
169
  const json = JSON.parse(data);
152
170
  yield json;
153
171
  }
154
172
  catch (e) {
155
- logger.error("Failed to parse stream chunk", { error: e, data });
173
+ logger.error("Failed to parse stream chunk", {
174
+ error: e instanceof Error ? e.message : String(e),
175
+ data,
176
+ });
156
177
  }
157
178
  }
158
179
  }
@@ -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
@@ -1 +1 @@
1
- {"version":3,"file":"taskReminder.d.ts","sourceRoot":"","sources":["../../src/utils/taskReminder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAC9C,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,qBAAqB,CAAC;AAEtE,eAAO,MAAM,oBAAoB;;;CAGhC,CAAC;AAgBF,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG;IAC9D,4BAA4B,EAAE,MAAM,CAAC;IACrC,sBAAsB,EAAE,MAAM,CAAC;CAChC,CAqCA;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,MAAM,CAe3D;AAED,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,0BAA0B,EAAE,EACtC,UAAU,EAAE;IACV,4BAA4B,EAAE,MAAM,CAAC;IACrC,sBAAsB,EAAE,MAAM,CAAC;CAChC,EACD,KAAK,EAAE,IAAI,EAAE,GACZ,IAAI,CAkBN"}
1
+ {"version":3,"file":"taskReminder.d.ts","sourceRoot":"","sources":["../../src/utils/taskReminder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAE9C,eAAO,MAAM,oBAAoB;;;CAGhC,CAAC;AAWF,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG;IAC9D,4BAA4B,EAAE,MAAM,CAAC;IACrC,sBAAsB,EAAE,MAAM,CAAC;CAChC,CAqCA;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,MAAM,CAa3D;AAED,wBAAgB,uBAAuB,CACrC,UAAU,EAAE;IACV,4BAA4B,EAAE,MAAM,CAAC;IACrC,sBAAsB,EAAE,MAAM,CAAC;CAChC,EACD,KAAK,EAAE,IAAI,EAAE,GACZ,MAAM,GAAG,IAAI,CAef"}
@@ -2,12 +2,7 @@ export const TASK_REMINDER_CONFIG = {
2
2
  TURNS_SINCE_WRITE: 10,
3
3
  TURNS_BETWEEN_REMINDERS: 10,
4
4
  };
5
- const TASK_MANAGEMENT_TOOLS = new Set([
6
- "TaskCreate",
7
- "TaskUpdate",
8
- "TaskList",
9
- "TaskGet",
10
- ]);
5
+ const TASK_MANAGEMENT_TOOLS = new Set(["TaskCreate", "TaskUpdate"]);
11
6
  const TASK_REMINDER_MARKER = "<!-- task-reminder -->";
12
7
  function isQualifyingAssistantMessage(message) {
13
8
  if (message.role !== "assistant")
@@ -48,29 +43,23 @@ export function getTaskReminderTurnCounts(messages) {
48
43
  export function buildTaskReminderText(tasks) {
49
44
  let text = `${TASK_REMINDER_MARKER}\n`;
50
45
  text +=
51
- "Here is a gentle reminder about the task list. The user may not have explicitly asked for the following to be done, but you should check the task list to see if any tasks need attention.\n";
46
+ "The task tools haven't been used recently. If you're working on tasks that would benefit from tracking progress, consider using TaskCreate to add new tasks and TaskUpdate to update task status (set to in_progress when starting, completed when done). Also consider cleaning up the task list if it has become stale. Only use these if relevant to the current work. This is just a gentle reminder - ignore if not applicable. Make sure that you never mention this reminder to the user";
52
47
  if (tasks.length > 0) {
53
- text += "\n";
48
+ text += "\n\nHere are the existing tasks:\n";
54
49
  for (const task of tasks) {
55
50
  text += `#${task.id} [${task.status}] ${task.subject}\n`;
56
51
  }
57
52
  }
58
- else {
59
- text += "The task list is currently empty.\n";
60
- }
61
53
  return text;
62
54
  }
63
- export function maybeInjectTaskReminder(messages, turnCounts, tasks) {
55
+ export function maybeInjectTaskReminder(turnCounts, tasks) {
64
56
  if (turnCounts.turnsSinceLastTaskManagement <
65
57
  TASK_REMINDER_CONFIG.TURNS_SINCE_WRITE) {
66
- return;
58
+ return null;
67
59
  }
68
60
  if (turnCounts.turnsSinceLastReminder <
69
61
  TASK_REMINDER_CONFIG.TURNS_BETWEEN_REMINDERS) {
70
- return;
62
+ return null;
71
63
  }
72
- messages.push({
73
- role: "user",
74
- content: buildTaskReminderText(tasks),
75
- });
64
+ return buildTaskReminderText(tasks);
76
65
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wave-agent-sdk",
3
- "version": "0.18.3",
3
+ "version": "0.18.4",
4
4
  "description": "SDK for building AI-powered development tools and agents",
5
5
  "keywords": [
6
6
  "ai",
@@ -9,6 +9,18 @@ const __dirname = path.dirname(__filename);
9
9
  const MANIFEST_PATH = path.resolve(__dirname, "../bin/rg");
10
10
  const VENDOR_DIR = path.resolve(__dirname, "../vendor/ripgrep");
11
11
 
12
+ function getCurrentPlatform() {
13
+ const platform = process.platform;
14
+ const arch = process.arch;
15
+ if (platform === "darwin")
16
+ return `macos-${arch === "arm64" ? "aarch64" : "x86_64"}`;
17
+ if (platform === "linux")
18
+ return `linux-${arch === "arm64" ? "aarch64" : "x86_64"}`;
19
+ if (platform === "win32")
20
+ return `windows-${arch === "arm64" ? "aarch64" : "x86_64"}`;
21
+ return null;
22
+ }
23
+
12
24
  async function main() {
13
25
  if (!fs.existsSync(MANIFEST_PATH)) {
14
26
  console.error(`Manifest not found: ${MANIFEST_PATH}`);
@@ -18,7 +30,15 @@ async function main() {
18
30
  const manifestContent = fs.readFileSync(MANIFEST_PATH, "utf-8");
19
31
  const jsonContent = manifestContent.replace(/^#!.*\n/, "");
20
32
  const manifest = JSON.parse(jsonContent);
21
- const platforms = manifest.platforms;
33
+ const allPlatforms = manifest.platforms;
34
+
35
+ // In CI, only download for the current platform
36
+ const isCI = process.env.CI === "true";
37
+ const currentPlatform = isCI ? getCurrentPlatform() : null;
38
+ const platforms =
39
+ currentPlatform && currentPlatform in allPlatforms
40
+ ? { [currentPlatform]: allPlatforms[currentPlatform] }
41
+ : allPlatforms;
22
42
 
23
43
  if (!fs.existsSync(VENDOR_DIR)) {
24
44
  fs.mkdirSync(VENDOR_DIR, { recursive: true });
package/src/agent.ts CHANGED
@@ -709,11 +709,6 @@ export class Agent {
709
709
 
710
710
  // Clear messages and generate new session
711
711
  this.messageManager.clearMessages();
712
- const memoryService =
713
- this.container.get<import("./services/memory.js").MemoryService>(
714
- "MemoryService",
715
- );
716
- memoryService?.clearCache();
717
712
  await this.taskManager.syncWithSession();
718
713
 
719
714
  // Run SessionStart hooks (restore context for new session)
@@ -166,6 +166,29 @@ export class MemoryRuleManager {
166
166
  return activeRules;
167
167
  }
168
168
 
169
+ /**
170
+ * Returns rules split by type:
171
+ * - unconditional: rules with no `paths` metadata (always active)
172
+ * - conditional: rules with `paths` metadata that match filesInContext
173
+ */
174
+ getActiveRulesSplit(filesInContext: string[]): {
175
+ unconditional: MemoryRule[];
176
+ conditional: MemoryRule[];
177
+ } {
178
+ const unconditional: MemoryRule[] = [];
179
+ const conditional: MemoryRule[] = [];
180
+ for (const rule of Object.values(this.state.rules)) {
181
+ if (!rule.metadata.paths || rule.metadata.paths.length === 0) {
182
+ unconditional.push(rule);
183
+ } else if (
184
+ this.service.isRuleActive(rule, filesInContext, this.workdir)
185
+ ) {
186
+ conditional.push(rule);
187
+ }
188
+ }
189
+ return { unconditional, conditional };
190
+ }
191
+
169
192
  /**
170
193
  * Reloads rules from disk.
171
194
  */