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.
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +0 -2
- package/dist/managers/MemoryRuleManager.d.ts +9 -0
- package/dist/managers/MemoryRuleManager.d.ts.map +1 -1
- package/dist/managers/MemoryRuleManager.js +18 -0
- package/dist/managers/aiManager.d.ts +1 -1
- package/dist/managers/aiManager.d.ts.map +1 -1
- package/dist/managers/aiManager.js +370 -345
- package/dist/managers/messageManager.d.ts +32 -13
- package/dist/managers/messageManager.d.ts.map +1 -1
- package/dist/managers/messageManager.js +82 -76
- package/dist/prompts/index.d.ts +10 -4
- package/dist/prompts/index.d.ts.map +1 -1
- package/dist/prompts/index.js +21 -20
- package/dist/services/aiService.d.ts +2 -1
- package/dist/services/aiService.d.ts.map +1 -1
- package/dist/services/aiService.js +37 -10
- package/dist/services/memory.d.ts +1 -0
- package/dist/services/memory.d.ts.map +1 -1
- package/dist/services/memory.js +35 -17
- package/dist/tools/agentTool.d.ts.map +1 -1
- package/dist/tools/agentTool.js +24 -2
- package/dist/tools/editTool.js +3 -3
- package/dist/tools/readTool.js +2 -2
- package/dist/tools/writeTool.js +2 -2
- package/dist/utils/containerSetup.d.ts.map +1 -1
- package/dist/utils/containerSetup.js +1 -0
- package/dist/utils/openaiClient.d.ts.map +1 -1
- package/dist/utils/openaiClient.js +28 -7
- package/dist/utils/taskReminder.d.ts +2 -3
- package/dist/utils/taskReminder.d.ts.map +1 -1
- package/dist/utils/taskReminder.js +7 -18
- package/package.json +1 -1
- package/scripts/install_ripgrep.js +21 -1
- package/src/agent.ts +0 -5
- package/src/managers/MemoryRuleManager.ts +23 -0
- package/src/managers/aiManager.ts +473 -434
- package/src/managers/messageManager.ts +99 -82
- package/src/prompts/index.ts +33 -24
- package/src/services/aiService.ts +39 -12
- package/src/services/memory.ts +34 -16
- package/src/tools/agentTool.ts +24 -2
- package/src/tools/editTool.ts +3 -3
- package/src/tools/readTool.ts +2 -2
- package/src/tools/writeTool.ts +2 -2
- package/src/utils/containerSetup.ts +1 -0
- package/src/utils/openaiClient.ts +35 -7
- package/src/utils/taskReminder.ts +7 -19
- package/vendor/ripgrep/linux-aarch64/rg +0 -0
- package/vendor/ripgrep/macos-aarch64/rg +0 -0
- package/vendor/ripgrep/macos-x86_64/rg +0 -0
- package/vendor/ripgrep/windows-aarch64/rg.exe +0 -0
- package/vendor/ripgrep/windows-x86_64/rg.exe +0 -0
package/dist/services/memory.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
188
|
-
|
|
205
|
+
const projectMemory = await this.readMemoryFile(workdir);
|
|
206
|
+
const userMemory = await this.getUserMemoryContent();
|
|
189
207
|
let combined = "";
|
|
190
|
-
if (
|
|
191
|
-
combined +=
|
|
192
|
-
if (
|
|
208
|
+
if (projectMemory.trim())
|
|
209
|
+
combined += projectMemory;
|
|
210
|
+
if (userMemory.trim()) {
|
|
193
211
|
if (combined)
|
|
194
212
|
combined += "\n\n";
|
|
195
|
-
combined +=
|
|
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,
|
|
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"}
|
package/dist/tools/agentTool.js
CHANGED
|
@@ -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:
|
|
241
|
+
content: content || errorMsg,
|
|
242
|
+
error: errorMsg,
|
|
221
243
|
shortResult: "Delegation error",
|
|
222
244
|
});
|
|
223
245
|
}
|
package/dist/tools/editTool.js
CHANGED
|
@@ -93,11 +93,11 @@ Usage:
|
|
|
93
93
|
error: "old_string and new_string must be different",
|
|
94
94
|
};
|
|
95
95
|
}
|
|
96
|
-
//
|
|
97
|
-
context.messageManager?.
|
|
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.
|
|
100
|
+
!context.messageManager.hasFileBeenRead(filePath)) {
|
|
101
101
|
return {
|
|
102
102
|
success: false,
|
|
103
103
|
content: "",
|
package/dist/tools/readTool.js
CHANGED
|
@@ -169,8 +169,8 @@ Usage:
|
|
|
169
169
|
error: "file_path parameter is required and must be a string",
|
|
170
170
|
};
|
|
171
171
|
}
|
|
172
|
-
//
|
|
173
|
-
context.messageManager?.
|
|
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");
|
package/dist/tools/writeTool.js
CHANGED
|
@@ -59,8 +59,8 @@ Usage:
|
|
|
59
59
|
error: "content parameter is required and must be a string",
|
|
60
60
|
};
|
|
61
61
|
}
|
|
62
|
-
//
|
|
63
|
-
context.messageManager?.
|
|
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,
|
|
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"}
|
|
@@ -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;
|
|
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
|
-
|
|
35
|
-
|
|
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 =
|
|
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 <
|
|
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 <
|
|
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", {
|
|
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(
|
|
12
|
+
export declare function maybeInjectTaskReminder(turnCounts: {
|
|
14
13
|
turnsSinceLastTaskManagement: number;
|
|
15
14
|
turnsSinceLastReminder: number;
|
|
16
|
-
}, tasks: Task[]):
|
|
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;
|
|
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
|
-
"
|
|
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(
|
|
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
|
-
|
|
73
|
-
role: "user",
|
|
74
|
-
content: buildTaskReminderText(tasks),
|
|
75
|
-
});
|
|
64
|
+
return buildTaskReminderText(tasks);
|
|
76
65
|
}
|
package/package.json
CHANGED
|
@@ -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
|
|
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
|
*/
|