wave-agent-sdk 0.8.1 → 0.8.3
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/managers/hookManager.d.ts.map +1 -1
- package/dist/managers/hookManager.js +0 -21
- package/dist/managers/liveConfigManager.d.ts.map +1 -1
- package/dist/managers/liveConfigManager.js +0 -36
- package/dist/managers/messageManager.d.ts.map +1 -1
- package/dist/managers/messageManager.js +2 -1
- package/dist/managers/permissionManager.d.ts.map +1 -1
- package/dist/managers/permissionManager.js +47 -29
- package/dist/managers/pluginManager.d.ts.map +1 -1
- package/dist/managers/pluginManager.js +28 -1
- package/dist/managers/skillManager.d.ts.map +1 -1
- package/dist/managers/skillManager.js +8 -2
- package/dist/services/aiService.d.ts.map +1 -1
- package/dist/services/aiService.js +2 -0
- package/dist/services/fileWatcher.d.ts.map +1 -1
- package/dist/services/fileWatcher.js +0 -4
- package/dist/services/initializationService.d.ts.map +1 -1
- package/dist/services/initializationService.js +2 -10
- package/dist/services/pluginLoader.d.ts.map +1 -1
- package/dist/services/pluginLoader.js +1 -3
- package/dist/services/taskManager.d.ts +2 -0
- package/dist/services/taskManager.d.ts.map +1 -1
- package/dist/services/taskManager.js +48 -0
- package/dist/tools/taskManagementTools.d.ts.map +1 -1
- package/dist/tools/taskManagementTools.js +58 -0
- package/dist/tools/taskTool.d.ts.map +1 -1
- package/dist/tools/taskTool.js +60 -50
- package/dist/utils/bashParser.d.ts +4 -0
- package/dist/utils/bashParser.d.ts.map +1 -1
- package/dist/utils/bashParser.js +39 -2
- package/dist/utils/containerSetup.d.ts.map +1 -1
- package/dist/utils/containerSetup.js +3 -0
- package/dist/utils/messageOperations.d.ts +1 -0
- package/dist/utils/messageOperations.d.ts.map +1 -1
- package/dist/utils/messageOperations.js +6 -2
- package/dist/utils/openaiClient.d.ts.map +1 -1
- package/dist/utils/openaiClient.js +3 -1
- package/package.json +2 -5
- package/src/managers/hookManager.ts +0 -52
- package/src/managers/liveConfigManager.ts +0 -75
- package/src/managers/messageManager.ts +2 -0
- package/src/managers/permissionManager.ts +60 -37
- package/src/managers/pluginManager.ts +39 -1
- package/src/managers/skillManager.ts +8 -2
- package/src/services/aiService.ts +2 -0
- package/src/services/fileWatcher.ts +0 -8
- package/src/services/initializationService.ts +2 -19
- package/src/services/pluginLoader.ts +1 -3
- package/src/services/taskManager.ts +51 -0
- package/src/tools/taskManagementTools.ts +77 -0
- package/src/tools/taskTool.ts +70 -61
- package/src/utils/bashParser.ts +50 -2
- package/src/utils/containerSetup.ts +3 -0
- package/src/utils/messageOperations.ts +7 -2
- package/src/utils/openaiClient.ts +3 -1
|
@@ -121,7 +121,6 @@ export class InitializationService {
|
|
|
121
121
|
// Initialize hooks configuration
|
|
122
122
|
try {
|
|
123
123
|
// Load hooks configuration using ConfigurationService
|
|
124
|
-
logger?.debug("Loading hooks configuration...");
|
|
125
124
|
const configResult =
|
|
126
125
|
await configurationService.loadMergedConfiguration(workdir);
|
|
127
126
|
|
|
@@ -156,8 +155,6 @@ export class InitializationService {
|
|
|
156
155
|
}
|
|
157
156
|
}
|
|
158
157
|
}
|
|
159
|
-
|
|
160
|
-
logger?.debug("Hooks system initialized successfully");
|
|
161
158
|
} catch (error) {
|
|
162
159
|
logger?.error("Failed to initialize hooks system:", error);
|
|
163
160
|
// Don't throw error to prevent app startup failure
|
|
@@ -206,9 +203,7 @@ export class InitializationService {
|
|
|
206
203
|
|
|
207
204
|
// Initialize live configuration reload
|
|
208
205
|
try {
|
|
209
|
-
logger?.debug("Initializing live configuration reload...");
|
|
210
206
|
await liveConfigManager.initialize();
|
|
211
|
-
logger?.debug("Live configuration reload initialized successfully");
|
|
212
207
|
} catch (error) {
|
|
213
208
|
logger?.error("Failed to initialize live configuration reload:", error);
|
|
214
209
|
// Don't throw error to prevent app startup failure - continue without live reload
|
|
@@ -216,8 +211,6 @@ export class InitializationService {
|
|
|
216
211
|
|
|
217
212
|
// Load memory files during initialization
|
|
218
213
|
try {
|
|
219
|
-
logger?.debug("Loading memory files...");
|
|
220
|
-
|
|
221
214
|
// Load project memory from AGENTS.md (bypass memory store for direct file access)
|
|
222
215
|
try {
|
|
223
216
|
const projectMemoryPath = path.join(workdir, "AGENTS.md");
|
|
@@ -226,13 +219,9 @@ export class InitializationService {
|
|
|
226
219
|
"utf-8",
|
|
227
220
|
);
|
|
228
221
|
setProjectMemory(projectMemoryContent);
|
|
229
|
-
logger?.debug("Project memory loaded successfully");
|
|
230
222
|
} catch (error) {
|
|
223
|
+
logger?.warn("Failed to load project memory file:", error);
|
|
231
224
|
setProjectMemory("");
|
|
232
|
-
logger?.debug(
|
|
233
|
-
"Project memory file not found or unreadable, using empty content:",
|
|
234
|
-
error instanceof Error ? error.message : String(error),
|
|
235
|
-
);
|
|
236
225
|
}
|
|
237
226
|
|
|
238
227
|
// Load user memory (bypass memory store for direct file access)
|
|
@@ -240,16 +229,10 @@ export class InitializationService {
|
|
|
240
229
|
const userMemoryPath = path.join(os.homedir(), ".wave", "AGENTS.md");
|
|
241
230
|
const userMemoryContent = await fs.readFile(userMemoryPath, "utf-8");
|
|
242
231
|
setUserMemory(userMemoryContent);
|
|
243
|
-
logger?.debug("User memory loaded successfully");
|
|
244
232
|
} catch (error) {
|
|
233
|
+
logger?.warn("Failed to load user memory file:", error);
|
|
245
234
|
setUserMemory("");
|
|
246
|
-
logger?.debug(
|
|
247
|
-
"User memory file not found or unreadable, using empty content:",
|
|
248
|
-
error instanceof Error ? error.message : String(error),
|
|
249
|
-
);
|
|
250
235
|
}
|
|
251
|
-
|
|
252
|
-
logger?.debug("Memory initialization completed");
|
|
253
236
|
} catch (error) {
|
|
254
237
|
// Ensure memory is always initialized even if loading fails
|
|
255
238
|
setProjectMemory("");
|
|
@@ -98,10 +98,8 @@ export class PluginLoader {
|
|
|
98
98
|
});
|
|
99
99
|
if (parsed.isValid) {
|
|
100
100
|
skills.push({
|
|
101
|
-
|
|
102
|
-
description: parsed.skillMetadata.description,
|
|
101
|
+
...parsed.skillMetadata,
|
|
103
102
|
type: "project", // Plugin skills are treated as project skills
|
|
104
|
-
skillPath: parsed.skillMetadata.skillPath,
|
|
105
103
|
content: parsed.content,
|
|
106
104
|
frontmatter: parsed.frontmatter,
|
|
107
105
|
isValid: parsed.isValid,
|
|
@@ -161,6 +161,57 @@ export class TaskManager extends EventEmitter {
|
|
|
161
161
|
});
|
|
162
162
|
}
|
|
163
163
|
|
|
164
|
+
async deleteTask(taskId: string): Promise<void> {
|
|
165
|
+
await this.withLock(async () => {
|
|
166
|
+
const taskPath = this.getTaskPath(taskId);
|
|
167
|
+
try {
|
|
168
|
+
await fs.unlink(taskPath);
|
|
169
|
+
this.emit("tasksChange", this.taskListId);
|
|
170
|
+
logger.debug(
|
|
171
|
+
`Task ${taskId} deleted from task list ${this.taskListId}`,
|
|
172
|
+
);
|
|
173
|
+
} catch (error) {
|
|
174
|
+
if ((error as NodeJS.ErrnoException).code !== "ENOENT") {
|
|
175
|
+
throw error;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
async cleanupOldTaskLists(days: number = 30): Promise<void> {
|
|
182
|
+
const threshold = Date.now() - days * 24 * 60 * 60 * 1000;
|
|
183
|
+
try {
|
|
184
|
+
const dirs = await fs.readdir(this.baseDir);
|
|
185
|
+
for (const dir of dirs) {
|
|
186
|
+
if (dir === this.taskListId) continue;
|
|
187
|
+
|
|
188
|
+
const dirPath = join(this.baseDir, dir);
|
|
189
|
+
const stats = await fs.stat(dirPath);
|
|
190
|
+
if (!stats.isDirectory()) continue;
|
|
191
|
+
|
|
192
|
+
// Check mtime of the directory and its contents
|
|
193
|
+
let latestMtime = stats.mtimeMs;
|
|
194
|
+
const files = await fs.readdir(dirPath);
|
|
195
|
+
for (const file of files) {
|
|
196
|
+
const filePath = join(dirPath, file);
|
|
197
|
+
const fileStats = await fs.stat(filePath);
|
|
198
|
+
if (fileStats.mtimeMs > latestMtime) {
|
|
199
|
+
latestMtime = fileStats.mtimeMs;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (latestMtime < threshold) {
|
|
204
|
+
logger.info(`Cleaning up old task list directory: ${dirPath}`);
|
|
205
|
+
await fs.rm(dirPath, { recursive: true, force: true });
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
} catch (error) {
|
|
209
|
+
if ((error as NodeJS.ErrnoException).code !== "ENOENT") {
|
|
210
|
+
logger.error("Failed to cleanup old task lists:", error);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
164
215
|
async listTasks(): Promise<Task[]> {
|
|
165
216
|
const sessionDir = this.getSessionDir();
|
|
166
217
|
try {
|
|
@@ -101,6 +101,15 @@ NOTE that you should not use this tool if there is only one trivial task to do.
|
|
|
101
101
|
- Check TaskList first to avoid creating duplicate tasks`,
|
|
102
102
|
execute: async (args, context: ToolContext): Promise<ToolResult> => {
|
|
103
103
|
const taskManager = context.taskManager;
|
|
104
|
+
|
|
105
|
+
if (args.status === "deleted") {
|
|
106
|
+
return {
|
|
107
|
+
success: true,
|
|
108
|
+
content: `Task creation skipped because status was set to 'deleted'.`,
|
|
109
|
+
shortResult: `Skipped deleted task`,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
104
113
|
const task: Omit<Task, "id"> = {
|
|
105
114
|
subject: args.subject as string,
|
|
106
115
|
description: args.description as string,
|
|
@@ -334,6 +343,74 @@ Set up task dependencies:
|
|
|
334
343
|
};
|
|
335
344
|
}
|
|
336
345
|
|
|
346
|
+
if (args.status === "deleted") {
|
|
347
|
+
// Reciprocal Dependency Cleanup
|
|
348
|
+
// For each task in the deleted task's blocks list, remove the deleted task's ID from their blockedBy list.
|
|
349
|
+
for (const targetId of existingTask.blocks) {
|
|
350
|
+
const targetTask = await taskManager.getTask(targetId);
|
|
351
|
+
if (targetTask && targetTask.blockedBy.includes(taskId)) {
|
|
352
|
+
let targetSnapshotId: string | undefined;
|
|
353
|
+
if (context.reversionManager && context.messageId) {
|
|
354
|
+
const targetPath = taskManager.getTaskPath(targetId);
|
|
355
|
+
targetSnapshotId = await context.reversionManager.recordSnapshot(
|
|
356
|
+
context.messageId,
|
|
357
|
+
targetPath,
|
|
358
|
+
"modify",
|
|
359
|
+
);
|
|
360
|
+
}
|
|
361
|
+
await taskManager.updateTask({
|
|
362
|
+
...targetTask,
|
|
363
|
+
blockedBy: targetTask.blockedBy.filter((id) => id !== taskId),
|
|
364
|
+
});
|
|
365
|
+
if (context.reversionManager && targetSnapshotId) {
|
|
366
|
+
await context.reversionManager.commitSnapshot(targetSnapshotId);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// For each task in the deleted task's blockedBy list, remove the deleted task's ID from their blocks list.
|
|
372
|
+
for (const targetId of existingTask.blockedBy) {
|
|
373
|
+
const targetTask = await taskManager.getTask(targetId);
|
|
374
|
+
if (targetTask && targetTask.blocks.includes(taskId)) {
|
|
375
|
+
let targetSnapshotId: string | undefined;
|
|
376
|
+
if (context.reversionManager && context.messageId) {
|
|
377
|
+
const targetPath = taskManager.getTaskPath(targetId);
|
|
378
|
+
targetSnapshotId = await context.reversionManager.recordSnapshot(
|
|
379
|
+
context.messageId,
|
|
380
|
+
targetPath,
|
|
381
|
+
"modify",
|
|
382
|
+
);
|
|
383
|
+
}
|
|
384
|
+
await taskManager.updateTask({
|
|
385
|
+
...targetTask,
|
|
386
|
+
blocks: targetTask.blocks.filter((id) => id !== taskId),
|
|
387
|
+
});
|
|
388
|
+
if (context.reversionManager && targetSnapshotId) {
|
|
389
|
+
await context.reversionManager.commitSnapshot(targetSnapshotId);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// Record delete snapshot for the task itself
|
|
395
|
+
if (context.reversionManager && context.messageId) {
|
|
396
|
+
const taskPath = taskManager.getTaskPath(taskId);
|
|
397
|
+
const deleteSnapshotId = await context.reversionManager.recordSnapshot(
|
|
398
|
+
context.messageId,
|
|
399
|
+
taskPath,
|
|
400
|
+
"delete",
|
|
401
|
+
);
|
|
402
|
+
await context.reversionManager.commitSnapshot(deleteSnapshotId);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
await taskManager.deleteTask(taskId);
|
|
406
|
+
|
|
407
|
+
return {
|
|
408
|
+
success: true,
|
|
409
|
+
content: `Task #${taskId} deleted and removed from disk.`,
|
|
410
|
+
shortResult: `Deleted task ${taskId}`,
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
|
|
337
414
|
let snapshotId: string | undefined;
|
|
338
415
|
if (context.reversionManager && context.messageId) {
|
|
339
416
|
const taskPath = taskManager.getTaskPath(taskId);
|
package/src/tools/taskTool.ts
CHANGED
|
@@ -162,68 +162,77 @@ ${subagentList || "No subagents configured"}
|
|
|
162
162
|
},
|
|
163
163
|
);
|
|
164
164
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
if (isBackgrounded) {
|
|
185
|
-
// If it was backgrounded during execution, the backgroundHandler already returned/will return
|
|
186
|
-
// But wait, the backgroundHandler is async and returns a ToolResult.
|
|
187
|
-
// In the current ToolManager/AIManager implementation, the backgroundHandler's return value
|
|
188
|
-
// is what's used when backgrounding happens.
|
|
189
|
-
// However, executeTask might still be running.
|
|
190
|
-
// We should return a special value or just let it be.
|
|
191
|
-
return {
|
|
192
|
-
success: true,
|
|
193
|
-
content: "Task backgrounded",
|
|
194
|
-
shortResult: "Task backgrounded",
|
|
195
|
-
isManuallyBackgrounded: true,
|
|
196
|
-
};
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
if (run_in_background) {
|
|
200
|
-
return {
|
|
201
|
-
success: true,
|
|
202
|
-
content: `Task started in background with ID: ${result}`,
|
|
203
|
-
shortResult: `Task started in background: ${result}`,
|
|
204
|
-
};
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
// Cleanup subagent instance after task completion
|
|
208
|
-
subagentManager.cleanupInstance(instance.subagentId);
|
|
209
|
-
|
|
210
|
-
const messages = instance.messageManager.getMessages();
|
|
211
|
-
const tokens = instance.messageManager.getlatestTotalTokens();
|
|
212
|
-
const toolCount = countToolBlocks(messages);
|
|
213
|
-
const summary = formatToolTokenSummary(toolCount, tokens);
|
|
165
|
+
return new Promise<ToolResult>((resolve) => {
|
|
166
|
+
(async () => {
|
|
167
|
+
// Register for backgrounding if not already in background
|
|
168
|
+
if (!run_in_background && context.foregroundTaskManager) {
|
|
169
|
+
context.foregroundTaskManager.registerForegroundTask({
|
|
170
|
+
id: instance.subagentId,
|
|
171
|
+
backgroundHandler: async () => {
|
|
172
|
+
isBackgrounded = true;
|
|
173
|
+
await subagentManager.backgroundInstance(instance.subagentId);
|
|
174
|
+
resolve({
|
|
175
|
+
success: true,
|
|
176
|
+
content: "Task backgrounded",
|
|
177
|
+
shortResult: "Task backgrounded",
|
|
178
|
+
isManuallyBackgrounded: true,
|
|
179
|
+
});
|
|
180
|
+
},
|
|
181
|
+
});
|
|
182
|
+
}
|
|
214
183
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
184
|
+
try {
|
|
185
|
+
const result = await subagentManager.executeTask(
|
|
186
|
+
instance,
|
|
187
|
+
prompt,
|
|
188
|
+
context.abortSignal,
|
|
189
|
+
run_in_background,
|
|
190
|
+
);
|
|
191
|
+
|
|
192
|
+
if (isBackgrounded) {
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (run_in_background) {
|
|
197
|
+
resolve({
|
|
198
|
+
success: true,
|
|
199
|
+
content: `Task started in background with ID: ${result}`,
|
|
200
|
+
shortResult: `Task started in background: ${result}`,
|
|
201
|
+
});
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Cleanup subagent instance after task completion
|
|
206
|
+
subagentManager.cleanupInstance(instance.subagentId);
|
|
207
|
+
|
|
208
|
+
const messages = instance.messageManager.getMessages();
|
|
209
|
+
const tokens = instance.messageManager.getlatestTotalTokens();
|
|
210
|
+
const toolCount = countToolBlocks(messages);
|
|
211
|
+
const summary = formatToolTokenSummary(toolCount, tokens);
|
|
212
|
+
|
|
213
|
+
resolve({
|
|
214
|
+
success: true,
|
|
215
|
+
content: result,
|
|
216
|
+
shortResult: `Task completed${summary ? ` ${summary}` : ""}`,
|
|
217
|
+
});
|
|
218
|
+
} catch (error) {
|
|
219
|
+
if (!isBackgrounded) {
|
|
220
|
+
resolve({
|
|
221
|
+
success: false,
|
|
222
|
+
content: "",
|
|
223
|
+
error: `Task delegation failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
224
|
+
shortResult: "Delegation error",
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
} finally {
|
|
228
|
+
if (!run_in_background && context.foregroundTaskManager) {
|
|
229
|
+
context.foregroundTaskManager.unregisterForegroundTask(
|
|
230
|
+
instance.subagentId,
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
})();
|
|
235
|
+
});
|
|
227
236
|
} catch (error) {
|
|
228
237
|
return {
|
|
229
238
|
success: false,
|
package/src/utils/bashParser.ts
CHANGED
|
@@ -100,8 +100,13 @@ export function splitBashCommand(command: string): string[] {
|
|
|
100
100
|
|
|
101
101
|
const finalResult: string[] = [];
|
|
102
102
|
for (const part of parts) {
|
|
103
|
-
const
|
|
104
|
-
|
|
103
|
+
const envStripped = stripEnvVars(part);
|
|
104
|
+
const stripped = stripRedirections(envStripped);
|
|
105
|
+
if (
|
|
106
|
+
stripped.startsWith("(") &&
|
|
107
|
+
stripped.endsWith(")") &&
|
|
108
|
+
stripped === envStripped
|
|
109
|
+
) {
|
|
105
110
|
const inner = stripped.substring(1, stripped.length - 1).trim();
|
|
106
111
|
if (inner) {
|
|
107
112
|
finalResult.push(...splitBashCommand(inner));
|
|
@@ -286,6 +291,49 @@ export function stripRedirections(command: string): string {
|
|
|
286
291
|
return result.trim();
|
|
287
292
|
}
|
|
288
293
|
|
|
294
|
+
/**
|
|
295
|
+
* Checks if a bash command contains any write redirections (>, >>, &>, 2>, >|).
|
|
296
|
+
*/
|
|
297
|
+
export function hasWriteRedirections(command: string): boolean {
|
|
298
|
+
let inSingleQuote = false;
|
|
299
|
+
let inDoubleQuote = false;
|
|
300
|
+
let escaped = false;
|
|
301
|
+
|
|
302
|
+
for (let i = 0; i < command.length; i++) {
|
|
303
|
+
const char = command[i];
|
|
304
|
+
|
|
305
|
+
if (escaped) {
|
|
306
|
+
escaped = false;
|
|
307
|
+
continue;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
if (char === "\\") {
|
|
311
|
+
escaped = true;
|
|
312
|
+
continue;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
if (char === "'" && !inDoubleQuote) {
|
|
316
|
+
inSingleQuote = !inSingleQuote;
|
|
317
|
+
continue;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
if (char === '"' && !inSingleQuote) {
|
|
321
|
+
inDoubleQuote = !inDoubleQuote;
|
|
322
|
+
continue;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
if (inSingleQuote || inDoubleQuote) {
|
|
326
|
+
continue;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
if (char === ">") {
|
|
330
|
+
return true;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
return false;
|
|
335
|
+
}
|
|
336
|
+
|
|
289
337
|
/**
|
|
290
338
|
* Blacklist of dangerous commands that should not be safely prefix-matched
|
|
291
339
|
* and should not have persistent permissions.
|
|
@@ -116,6 +116,9 @@ export function setupAgentContainer(
|
|
|
116
116
|
const tasks = await taskManager.listTasks();
|
|
117
117
|
onTasksChange(tasks);
|
|
118
118
|
});
|
|
119
|
+
taskManager.cleanupOldTaskLists(30).catch((error) => {
|
|
120
|
+
logger.error("Failed to cleanup old task lists:", error);
|
|
121
|
+
});
|
|
119
122
|
|
|
120
123
|
const backgroundTaskManager = new BackgroundTaskManager(container, {
|
|
121
124
|
callbacks: {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { randomUUID } from "crypto";
|
|
1
2
|
import type { Message, Usage } from "../types/index.js";
|
|
2
3
|
import { MessageSource } from "../types/index.js";
|
|
3
4
|
import { readFileSync } from "fs";
|
|
@@ -113,6 +114,8 @@ export const convertImageToBase64 = (imagePath: string): string => {
|
|
|
113
114
|
}
|
|
114
115
|
};
|
|
115
116
|
|
|
117
|
+
export const generateMessageId = (): string => `msg-${randomUUID()}`;
|
|
118
|
+
|
|
116
119
|
// Add user message
|
|
117
120
|
export const addUserMessageToMessages = ({
|
|
118
121
|
messages,
|
|
@@ -142,7 +145,7 @@ export const addUserMessageToMessages = ({
|
|
|
142
145
|
}
|
|
143
146
|
|
|
144
147
|
const userMessage: Message = {
|
|
145
|
-
id:
|
|
148
|
+
id: generateMessageId(),
|
|
146
149
|
role: "user",
|
|
147
150
|
blocks,
|
|
148
151
|
};
|
|
@@ -179,7 +182,7 @@ export const addAssistantMessageToMessages = (
|
|
|
179
182
|
}
|
|
180
183
|
|
|
181
184
|
const initialAssistantMessage: Message = {
|
|
182
|
-
id:
|
|
185
|
+
id: generateMessageId(),
|
|
183
186
|
role: "assistant",
|
|
184
187
|
blocks,
|
|
185
188
|
usage, // Include usage data if provided
|
|
@@ -283,6 +286,7 @@ export const addErrorBlockToMessage = ({
|
|
|
283
286
|
} else {
|
|
284
287
|
// If the last message is not an assistant message, create a new assistant message
|
|
285
288
|
newMessages.push({
|
|
289
|
+
id: generateMessageId(),
|
|
286
290
|
role: "assistant",
|
|
287
291
|
blocks: [
|
|
288
292
|
{
|
|
@@ -302,6 +306,7 @@ export const addBangMessage = ({
|
|
|
302
306
|
command,
|
|
303
307
|
}: AddBangParams): Message[] => {
|
|
304
308
|
const outputMessage: Message = {
|
|
309
|
+
id: generateMessageId(),
|
|
305
310
|
role: "user",
|
|
306
311
|
blocks: [
|
|
307
312
|
{
|
|
@@ -52,7 +52,9 @@ export class OpenAIClient {
|
|
|
52
52
|
>;
|
|
53
53
|
// Prevent unhandled rejection if only withResponse() is used
|
|
54
54
|
promise.catch((e) => {
|
|
55
|
-
|
|
55
|
+
if (!(e instanceof Error && e.name === "AbortError")) {
|
|
56
|
+
logger.error("Unhandled OpenAI promise rejection:", e);
|
|
57
|
+
}
|
|
56
58
|
});
|
|
57
59
|
return promise;
|
|
58
60
|
},
|