wave-agent-sdk 0.11.6 → 0.11.7
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/builtin/skills/init/SKILL.md +2 -0
- package/builtin/skills/settings/SKILLS.md +3 -2
- package/builtin/skills/settings/SUBAGENTS.md +1 -3
- package/dist/agent.d.ts +6 -0
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +18 -1
- package/dist/constants/tools.d.ts +1 -1
- package/dist/constants/tools.d.ts.map +1 -1
- package/dist/constants/tools.js +1 -1
- package/dist/managers/MemoryRuleManager.d.ts.map +1 -1
- package/dist/managers/MemoryRuleManager.js +1 -9
- package/dist/managers/aiManager.d.ts.map +1 -1
- package/dist/managers/aiManager.js +22 -3
- package/dist/managers/messageManager.d.ts +13 -5
- package/dist/managers/messageManager.d.ts.map +1 -1
- package/dist/managers/messageManager.js +62 -34
- package/dist/managers/pluginManager.d.ts.map +1 -1
- package/dist/managers/pluginManager.js +4 -2
- package/dist/managers/slashCommandManager.d.ts +2 -0
- package/dist/managers/slashCommandManager.d.ts.map +1 -1
- package/dist/managers/slashCommandManager.js +98 -4
- package/dist/managers/toolManager.d.ts.map +1 -1
- package/dist/managers/toolManager.js +8 -2
- package/dist/prompts/index.d.ts +2 -0
- package/dist/prompts/index.d.ts.map +1 -1
- package/dist/prompts/index.js +5 -0
- package/dist/services/GitService.d.ts +1 -0
- package/dist/services/GitService.d.ts.map +1 -1
- package/dist/services/GitService.js +16 -0
- package/dist/services/MarketplaceService.d.ts +7 -0
- package/dist/services/MarketplaceService.d.ts.map +1 -1
- package/dist/services/MarketplaceService.js +321 -252
- package/dist/services/aiService.d.ts +34 -0
- package/dist/services/aiService.d.ts.map +1 -1
- package/dist/services/aiService.js +124 -1
- package/dist/services/initializationService.d.ts.map +1 -1
- package/dist/services/initializationService.js +18 -0
- package/dist/tools/agentTool.js +3 -3
- package/dist/tools/bashTool.d.ts.map +1 -1
- package/dist/tools/bashTool.js +4 -4
- package/dist/tools/editTool.d.ts.map +1 -1
- package/dist/tools/editTool.js +2 -0
- package/dist/tools/globTool.d.ts.map +1 -1
- package/dist/tools/globTool.js +15 -3
- package/dist/tools/grepTool.d.ts.map +1 -1
- package/dist/tools/grepTool.js +38 -12
- package/dist/tools/readTool.d.ts.map +1 -1
- package/dist/tools/readTool.js +61 -0
- package/dist/tools/skillTool.js +2 -2
- package/dist/tools/types.d.ts +16 -0
- package/dist/tools/types.d.ts.map +1 -1
- package/dist/tools/webFetchTool.d.ts +3 -0
- package/dist/tools/webFetchTool.d.ts.map +1 -0
- package/dist/tools/webFetchTool.js +171 -0
- package/dist/tools/writeTool.d.ts.map +1 -1
- package/dist/tools/writeTool.js +2 -0
- package/dist/types/commands.d.ts +1 -1
- package/dist/types/commands.d.ts.map +1 -1
- package/dist/types/messaging.d.ts +1 -0
- package/dist/types/messaging.d.ts.map +1 -1
- package/dist/utils/bashParser.d.ts +14 -0
- package/dist/utils/bashParser.d.ts.map +1 -1
- package/dist/utils/bashParser.js +243 -142
- package/dist/utils/convertMessagesForAPI.d.ts.map +1 -1
- package/dist/utils/convertMessagesForAPI.js +7 -0
- package/dist/utils/fileUtils.d.ts +8 -0
- package/dist/utils/fileUtils.d.ts.map +1 -1
- package/dist/utils/fileUtils.js +52 -0
- package/dist/utils/messageOperations.d.ts +12 -3
- package/dist/utils/messageOperations.d.ts.map +1 -1
- package/dist/utils/messageOperations.js +77 -9
- package/package.json +4 -2
- package/src/agent.ts +19 -1
- package/src/constants/tools.ts +1 -1
- package/src/managers/MemoryRuleManager.ts +1 -10
- package/src/managers/aiManager.ts +23 -3
- package/src/managers/messageManager.ts +76 -38
- package/src/managers/pluginManager.ts +4 -2
- package/src/managers/slashCommandManager.ts +130 -4
- package/src/managers/toolManager.ts +11 -2
- package/src/prompts/index.ts +6 -0
- package/src/services/GitService.ts +20 -0
- package/src/services/MarketplaceService.ts +397 -324
- package/src/services/aiService.ts +197 -1
- package/src/services/initializationService.ts +38 -0
- package/src/tools/agentTool.ts +3 -3
- package/src/tools/bashTool.ts +3 -4
- package/src/tools/editTool.ts +3 -0
- package/src/tools/globTool.ts +16 -3
- package/src/tools/grepTool.ts +41 -13
- package/src/tools/readTool.ts +69 -0
- package/src/tools/skillTool.ts +2 -2
- package/src/tools/types.ts +13 -0
- package/src/tools/webFetchTool.ts +194 -0
- package/src/tools/writeTool.ts +3 -0
- package/src/types/commands.ts +1 -1
- package/src/types/messaging.ts +1 -0
- package/src/utils/bashParser.ts +268 -157
- package/src/utils/convertMessagesForAPI.ts +8 -0
- package/src/utils/fileUtils.ts +69 -0
- package/src/utils/messageOperations.ts +84 -9
- package/dist/tools/taskOutputTool.d.ts +0 -3
- package/dist/tools/taskOutputTool.d.ts.map +0 -1
- package/dist/tools/taskOutputTool.js +0 -198
- package/src/tools/taskOutputTool.ts +0 -222
|
@@ -44,7 +44,7 @@ export const convertImageToBase64 = (imagePath) => {
|
|
|
44
44
|
};
|
|
45
45
|
export const generateMessageId = () => `msg-${randomUUID()}`;
|
|
46
46
|
// Add user message
|
|
47
|
-
export const addUserMessageToMessages = ({ messages, content, images, customCommandContent, source, id, }) => {
|
|
47
|
+
export const addUserMessageToMessages = ({ messages, content, images, customCommandContent, source, id, isMeta, }) => {
|
|
48
48
|
const blocks = [];
|
|
49
49
|
// Create text block with optional customCommandContent and source
|
|
50
50
|
const textBlock = {
|
|
@@ -66,6 +66,7 @@ export const addUserMessageToMessages = ({ messages, content, images, customComm
|
|
|
66
66
|
id: id || generateMessageId(),
|
|
67
67
|
role: "user",
|
|
68
68
|
blocks,
|
|
69
|
+
...(isMeta !== undefined && { isMeta }),
|
|
69
70
|
};
|
|
70
71
|
return [...messages, userMessage];
|
|
71
72
|
};
|
|
@@ -88,7 +89,11 @@ export const updateUserMessageInMessages = (messages, id, params) => {
|
|
|
88
89
|
}
|
|
89
90
|
return block;
|
|
90
91
|
});
|
|
91
|
-
return {
|
|
92
|
+
return {
|
|
93
|
+
...msg,
|
|
94
|
+
blocks: newBlocks,
|
|
95
|
+
...(params.isMeta !== undefined && { isMeta: params.isMeta }),
|
|
96
|
+
};
|
|
92
97
|
}
|
|
93
98
|
return msg;
|
|
94
99
|
});
|
|
@@ -122,12 +127,74 @@ export const addAssistantMessageToMessages = (messages, content, toolCalls, usag
|
|
|
122
127
|
};
|
|
123
128
|
return [...messages, initialAssistantMessage];
|
|
124
129
|
};
|
|
125
|
-
|
|
126
|
-
|
|
130
|
+
/**
|
|
131
|
+
* Add a tool block to a specific message by ID.
|
|
132
|
+
*/
|
|
133
|
+
export const addToolBlockToMessageInMessages = (messages, messageId, params) => {
|
|
134
|
+
const toolBlockId = randomUUID();
|
|
135
|
+
const newMessages = messages.map((msg) => {
|
|
136
|
+
if (msg.id === messageId) {
|
|
137
|
+
return {
|
|
138
|
+
...msg,
|
|
139
|
+
blocks: [
|
|
140
|
+
...msg.blocks,
|
|
141
|
+
{
|
|
142
|
+
type: "tool",
|
|
143
|
+
id: toolBlockId,
|
|
144
|
+
name: params.name || "unknown",
|
|
145
|
+
parameters: params.parameters || "",
|
|
146
|
+
result: params.result || "",
|
|
147
|
+
stage: "start",
|
|
148
|
+
...params,
|
|
149
|
+
},
|
|
150
|
+
],
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
return msg;
|
|
154
|
+
});
|
|
155
|
+
return { messages: newMessages, toolBlockId };
|
|
156
|
+
};
|
|
157
|
+
// Update Tool Block of the last assistant or user message
|
|
158
|
+
export const updateToolBlockInMessage = ({ messages, id, messageId, parameters, result, success, error, stage, name, shortResult, startLineNumber, images, compactParams, parametersChunk, isManuallyBackgrounded, }) => {
|
|
127
159
|
const newMessages = [...messages];
|
|
128
|
-
//
|
|
160
|
+
// If messageId is provided, target that specific message
|
|
161
|
+
if (messageId) {
|
|
162
|
+
const messageIndex = newMessages.findIndex((msg) => msg.id === messageId);
|
|
163
|
+
if (messageIndex !== -1) {
|
|
164
|
+
const toolBlockIndex = newMessages[messageIndex].blocks.findIndex((block) => block.type === "tool" && block.id === id);
|
|
165
|
+
if (toolBlockIndex !== -1) {
|
|
166
|
+
const toolBlock = newMessages[messageIndex].blocks[toolBlockIndex];
|
|
167
|
+
if (toolBlock.type === "tool") {
|
|
168
|
+
if (parameters !== undefined)
|
|
169
|
+
toolBlock.parameters = parameters;
|
|
170
|
+
if (result !== undefined)
|
|
171
|
+
toolBlock.result = result;
|
|
172
|
+
if (shortResult !== undefined)
|
|
173
|
+
toolBlock.shortResult = shortResult;
|
|
174
|
+
if (startLineNumber !== undefined)
|
|
175
|
+
toolBlock.startLineNumber = startLineNumber;
|
|
176
|
+
if (images !== undefined)
|
|
177
|
+
toolBlock.images = images;
|
|
178
|
+
if (success !== undefined)
|
|
179
|
+
toolBlock.success = success;
|
|
180
|
+
if (error !== undefined)
|
|
181
|
+
toolBlock.error = error;
|
|
182
|
+
if (stage !== undefined)
|
|
183
|
+
toolBlock.stage = stage;
|
|
184
|
+
if (compactParams !== undefined)
|
|
185
|
+
toolBlock.compactParams = compactParams;
|
|
186
|
+
if (parametersChunk !== undefined)
|
|
187
|
+
toolBlock.parametersChunk = parametersChunk;
|
|
188
|
+
if (isManuallyBackgrounded !== undefined)
|
|
189
|
+
toolBlock.isManuallyBackgrounded = isManuallyBackgrounded;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return newMessages;
|
|
194
|
+
}
|
|
195
|
+
// Find the last assistant or user message
|
|
129
196
|
for (let i = newMessages.length - 1; i >= 0; i--) {
|
|
130
|
-
if (newMessages[i].role === "assistant") {
|
|
197
|
+
if (newMessages[i].role === "assistant" || newMessages[i].role === "user") {
|
|
131
198
|
const toolBlockIndex = newMessages[i].blocks.findIndex((block) => block.type === "tool" && block.id === id);
|
|
132
199
|
if (toolBlockIndex !== -1) {
|
|
133
200
|
const toolBlock = newMessages[i].blocks[toolBlockIndex];
|
|
@@ -155,9 +222,10 @@ export const updateToolBlockInMessage = ({ messages, id, parameters, result, suc
|
|
|
155
222
|
if (isManuallyBackgrounded !== undefined)
|
|
156
223
|
toolBlock.isManuallyBackgrounded = isManuallyBackgrounded;
|
|
157
224
|
}
|
|
225
|
+
break; // Found and updated, stop searching
|
|
158
226
|
}
|
|
159
|
-
else {
|
|
160
|
-
// If existing block not found, create new one
|
|
227
|
+
else if (newMessages[i].role === "assistant") {
|
|
228
|
+
// If existing block not found in assistant message, create new one
|
|
161
229
|
// This handles cases where we're streaming tool parameters before execution
|
|
162
230
|
newMessages[i].blocks.push({
|
|
163
231
|
type: "tool",
|
|
@@ -175,8 +243,8 @@ export const updateToolBlockInMessage = ({ messages, id, parameters, result, suc
|
|
|
175
243
|
parametersChunk: parametersChunk,
|
|
176
244
|
isManuallyBackgrounded: isManuallyBackgrounded,
|
|
177
245
|
});
|
|
246
|
+
break; // Created and added, stop searching
|
|
178
247
|
}
|
|
179
|
-
break;
|
|
180
248
|
}
|
|
181
249
|
}
|
|
182
250
|
return newMessages;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wave-agent-sdk",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.7",
|
|
4
4
|
"description": "SDK for building AI-powered development tools and agents",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -34,9 +34,11 @@
|
|
|
34
34
|
"fuzzysort": "^3.1.0",
|
|
35
35
|
"glob": "^13.0.0",
|
|
36
36
|
"minimatch": "^10.0.3",
|
|
37
|
-
"openai": "^5.12.2"
|
|
37
|
+
"openai": "^5.12.2",
|
|
38
|
+
"turndown": "^7.2.2"
|
|
38
39
|
},
|
|
39
40
|
"devDependencies": {
|
|
41
|
+
"@types/turndown": "^5.0.6",
|
|
40
42
|
"@vitest/coverage-v8": "^4.0.18",
|
|
41
43
|
"rimraf": "^6.1.2",
|
|
42
44
|
"tsc-alias": "^1.8.16",
|
package/src/agent.ts
CHANGED
|
@@ -35,6 +35,8 @@ import { LiveConfigManager } from "./managers/liveConfigManager.js";
|
|
|
35
35
|
import { configValidator } from "./utils/configValidator.js";
|
|
36
36
|
import { SkillManager } from "./managers/skillManager.js";
|
|
37
37
|
import { TaskManager } from "./services/taskManager.js";
|
|
38
|
+
import { btw } from "./services/aiService.js";
|
|
39
|
+
import { convertMessagesForAPI } from "./utils/convertMessagesForAPI.js";
|
|
38
40
|
import { InitializationService } from "./services/initializationService.js";
|
|
39
41
|
import { InteractionService } from "./services/interactionService.js";
|
|
40
42
|
import { ConfigurationService } from "./services/configurationService.js";
|
|
@@ -196,7 +198,7 @@ export class Agent {
|
|
|
196
198
|
}
|
|
197
199
|
|
|
198
200
|
public get latestTotalTokens(): number {
|
|
199
|
-
return this.messageManager.
|
|
201
|
+
return this.messageManager.getLatestTotalTokens();
|
|
200
202
|
}
|
|
201
203
|
|
|
202
204
|
/** Get working directory */
|
|
@@ -495,6 +497,22 @@ export class Agent {
|
|
|
495
497
|
this.messageManager.triggerShowRewind();
|
|
496
498
|
}
|
|
497
499
|
|
|
500
|
+
/**
|
|
501
|
+
* Ask a side question without tool use
|
|
502
|
+
* @param question - The side question to ask
|
|
503
|
+
* @returns Promise that resolves to the AI's answer
|
|
504
|
+
*/
|
|
505
|
+
public async askBtw(question: string): Promise<string> {
|
|
506
|
+
const messages = convertMessagesForAPI(this.messageManager.getMessages());
|
|
507
|
+
const result = await btw({
|
|
508
|
+
gatewayConfig: this.getGatewayConfig(),
|
|
509
|
+
modelConfig: this.getModelConfig(),
|
|
510
|
+
messages,
|
|
511
|
+
question,
|
|
512
|
+
});
|
|
513
|
+
return result.content;
|
|
514
|
+
}
|
|
515
|
+
|
|
498
516
|
/**
|
|
499
517
|
* Send a message to the AI agent with optional images
|
|
500
518
|
*
|
package/src/constants/tools.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
export const ASK_USER_QUESTION_TOOL_NAME = "AskUserQuestion";
|
|
2
2
|
export const BASH_TOOL_NAME = "Bash";
|
|
3
|
-
export const TASK_OUTPUT_TOOL_NAME = "TaskOutput";
|
|
4
3
|
export const TASK_STOP_TOOL_NAME = "TaskStop";
|
|
5
4
|
export const EDIT_TOOL_NAME = "Edit";
|
|
6
5
|
export const EXIT_PLAN_MODE_TOOL_NAME = "ExitPlanMode";
|
|
@@ -18,3 +17,4 @@ export const WRITE_TOOL_NAME = "Write";
|
|
|
18
17
|
export const CRON_CREATE_TOOL_NAME = "CronCreate";
|
|
19
18
|
export const CRON_DELETE_TOOL_NAME = "CronDelete";
|
|
20
19
|
export const CRON_LIST_TOOL_NAME = "CronList";
|
|
20
|
+
export const WEB_FETCH_TOOL_NAME = "WebFetch";
|
|
@@ -55,17 +55,8 @@ export class MemoryRuleManager {
|
|
|
55
55
|
|
|
56
56
|
this.state.rules = newRules;
|
|
57
57
|
const ruleCount = Object.keys(newRules).length;
|
|
58
|
+
// Removed verbose logging of all discovered rules
|
|
58
59
|
logger.debug(`Discovered ${ruleCount} modular memory rules`);
|
|
59
|
-
|
|
60
|
-
if (ruleCount > 0) {
|
|
61
|
-
logger.debug("Loaded memory rules:");
|
|
62
|
-
for (const [id, rule] of Object.entries(newRules)) {
|
|
63
|
-
const pathInfo = rule.metadata.paths
|
|
64
|
-
? `paths: ${JSON.stringify(rule.metadata.paths)}`
|
|
65
|
-
: "global (no path restriction)";
|
|
66
|
-
logger.debug(` - ${id} (${rule.source}): ${pathInfo}`);
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
60
|
}
|
|
70
61
|
|
|
71
62
|
private async scanDirectory(
|
|
@@ -339,6 +339,25 @@ export class AIManager {
|
|
|
339
339
|
return;
|
|
340
340
|
}
|
|
341
341
|
|
|
342
|
+
// Scan for file mentions in the last user message
|
|
343
|
+
if (recursionDepth === 0) {
|
|
344
|
+
const messages = this.messageManager.getMessages();
|
|
345
|
+
const lastMessage = messages[messages.length - 1];
|
|
346
|
+
if (lastMessage && lastMessage.role === "user") {
|
|
347
|
+
for (const block of lastMessage.blocks) {
|
|
348
|
+
if (block.type === "text") {
|
|
349
|
+
const content = block.content;
|
|
350
|
+
const fileMentionRegex = /(?:^|\s)@([\w.\-/]+)/g;
|
|
351
|
+
let match;
|
|
352
|
+
while ((match = fileMentionRegex.exec(content)) !== null) {
|
|
353
|
+
const filePath = match[1];
|
|
354
|
+
this.messageManager.touchFile(filePath);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
342
361
|
// Save session in each recursion to ensure message persistence
|
|
343
362
|
await this.messageManager.saveSession();
|
|
344
363
|
|
|
@@ -798,11 +817,12 @@ export class AIManager {
|
|
|
798
817
|
"Some tools were manually backgrounded, stopping recursion.",
|
|
799
818
|
);
|
|
800
819
|
} else if (!isCurrentlyAborted) {
|
|
801
|
-
// If response was truncated
|
|
802
|
-
if (result.finish_reason === "length"
|
|
820
|
+
// If response was truncated, add a hidden continuation message
|
|
821
|
+
if (result.finish_reason === "length") {
|
|
803
822
|
this.messageManager.addUserMessage({
|
|
804
823
|
content:
|
|
805
|
-
"
|
|
824
|
+
"Output token limit hit. Resume directly — no apology, no recap of what you were doing. Pick up mid-thought if that is where the cut happened. Break remaining work into smaller pieces.",
|
|
825
|
+
isMeta: true,
|
|
806
826
|
});
|
|
807
827
|
}
|
|
808
828
|
|
|
@@ -8,12 +8,13 @@ import {
|
|
|
8
8
|
updateBangInMessage,
|
|
9
9
|
completeBangInMessage,
|
|
10
10
|
removeLastUserMessage,
|
|
11
|
+
addToolBlockToMessageInMessages,
|
|
11
12
|
UserMessageParams,
|
|
12
13
|
type AgentToolBlockUpdateParams,
|
|
13
14
|
generateMessageId,
|
|
14
15
|
} from "../utils/messageOperations.js";
|
|
15
16
|
import type { Message, Usage } from "../types/index.js";
|
|
16
|
-
import { join } from "path";
|
|
17
|
+
import { join, isAbsolute, relative } from "path";
|
|
17
18
|
import {
|
|
18
19
|
appendMessages,
|
|
19
20
|
createSession,
|
|
@@ -137,7 +138,7 @@ export class MessageManager {
|
|
|
137
138
|
return [...this._usages];
|
|
138
139
|
}
|
|
139
140
|
|
|
140
|
-
public
|
|
141
|
+
public getLatestTotalTokens(): number {
|
|
141
142
|
return this.latestTotalTokens;
|
|
142
143
|
}
|
|
143
144
|
|
|
@@ -172,12 +173,7 @@ export class MessageManager {
|
|
|
172
173
|
const filesInContext = this.getFilesInContext();
|
|
173
174
|
const activeRules = this.memoryRuleManager.getActiveRules(filesInContext);
|
|
174
175
|
if (activeRules.length > 0) {
|
|
175
|
-
|
|
176
|
-
`Active modular rules (${activeRules.length}): ${activeRules.map((r) => r.id).join(", ")}`,
|
|
177
|
-
);
|
|
178
|
-
if (combined) {
|
|
179
|
-
combined += "\n\n";
|
|
180
|
-
}
|
|
176
|
+
combined += "\n\n";
|
|
181
177
|
combined += activeRules.map((r) => r.content).join("\n\n");
|
|
182
178
|
}
|
|
183
179
|
}
|
|
@@ -221,9 +217,53 @@ export class MessageManager {
|
|
|
221
217
|
}
|
|
222
218
|
}
|
|
223
219
|
|
|
220
|
+
/**
|
|
221
|
+
* Adds a file path to the set of files in context, normalizing it if necessary.
|
|
222
|
+
*/
|
|
223
|
+
public touchFile(filePath: string): void {
|
|
224
|
+
const normalizedPath = isAbsolute(filePath)
|
|
225
|
+
? relative(this.workdir, filePath)
|
|
226
|
+
: filePath;
|
|
227
|
+
this.filesInContext.add(normalizedPath);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Extracts and adds file paths from a message's tool blocks.
|
|
232
|
+
*/
|
|
233
|
+
private addPathsFromMessage(message: Message): void {
|
|
234
|
+
for (const block of message.blocks) {
|
|
235
|
+
if (block.type === "tool" && block.parameters) {
|
|
236
|
+
try {
|
|
237
|
+
const params = JSON.parse(block.parameters) as Record<
|
|
238
|
+
string,
|
|
239
|
+
unknown
|
|
240
|
+
>;
|
|
241
|
+
const paths = this.extractPathsFromParams(params);
|
|
242
|
+
for (const p of paths) {
|
|
243
|
+
this.touchFile(p);
|
|
244
|
+
}
|
|
245
|
+
} catch {
|
|
246
|
+
// Ignore parse errors
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
224
252
|
public setMessages(messages: Message[]): void {
|
|
253
|
+
const oldLength = this.messages.length;
|
|
225
254
|
this.messages = [...messages];
|
|
226
|
-
|
|
255
|
+
|
|
256
|
+
// Incrementally add paths from new messages
|
|
257
|
+
const newMessages = messages.slice(oldLength);
|
|
258
|
+
for (const message of newMessages) {
|
|
259
|
+
this.addPathsFromMessage(message);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Also check if the last message was updated (common for tool blocks)
|
|
263
|
+
if (messages.length > 0 && messages.length === oldLength) {
|
|
264
|
+
this.addPathsFromMessage(messages[messages.length - 1]);
|
|
265
|
+
}
|
|
266
|
+
|
|
227
267
|
this.callbacks.onMessagesChange?.([...messages]);
|
|
228
268
|
}
|
|
229
269
|
|
|
@@ -296,7 +336,6 @@ export class MessageManager {
|
|
|
296
336
|
this.rootSessionId = sessionData.rootSessionId || sessionData.id;
|
|
297
337
|
this.parentSessionId = sessionData.parentSessionId;
|
|
298
338
|
this.setMessages([...sessionData.messages]);
|
|
299
|
-
this.updateFilesInContext(sessionData.messages);
|
|
300
339
|
this.setlatestTotalTokens(sessionData.metadata.latestTotalTokens);
|
|
301
340
|
|
|
302
341
|
// Set saved message count to the number of loaded messages since they're already saved
|
|
@@ -401,6 +440,20 @@ export class MessageManager {
|
|
|
401
440
|
// Note: Subagent-specific callbacks are now handled by SubagentManager
|
|
402
441
|
}
|
|
403
442
|
|
|
443
|
+
/**
|
|
444
|
+
* Add a tool block to a specific message by ID.
|
|
445
|
+
*/
|
|
446
|
+
public addToolBlockToMessage(
|
|
447
|
+
messageId: string,
|
|
448
|
+
params: Omit<AgentToolBlockUpdateParams, "id">,
|
|
449
|
+
): string {
|
|
450
|
+
const { messages: newMessages, toolBlockId } =
|
|
451
|
+
addToolBlockToMessageInMessages(this.messages, messageId, params);
|
|
452
|
+
this.setMessages(newMessages);
|
|
453
|
+
this.callbacks.onToolBlockUpdated?.({ ...params, id: toolBlockId });
|
|
454
|
+
return toolBlockId;
|
|
455
|
+
}
|
|
456
|
+
|
|
404
457
|
public addErrorBlock(error: string): void {
|
|
405
458
|
const newMessages = addErrorBlockToMessage({
|
|
406
459
|
messages: this.messages,
|
|
@@ -462,6 +515,19 @@ export class MessageManager {
|
|
|
462
515
|
// Set new message list
|
|
463
516
|
this.setMessages(newMessages);
|
|
464
517
|
|
|
518
|
+
// Reset and re-populate filesInContext
|
|
519
|
+
this.filesInContext.clear();
|
|
520
|
+
for (const message of this.messages) {
|
|
521
|
+
this.addPathsFromMessage(message);
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
// Scan compressedContent for file mentions
|
|
525
|
+
const fileMentionRegex = /(?:^|\s)@([\w.\-/]+)/g;
|
|
526
|
+
let match;
|
|
527
|
+
while ((match = fileMentionRegex.exec(compressedContent)) !== null) {
|
|
528
|
+
this.touchFile(match[1]);
|
|
529
|
+
}
|
|
530
|
+
|
|
465
531
|
// Trigger compression callback
|
|
466
532
|
this.callbacks.onCompressBlockAdded?.(compressedContent);
|
|
467
533
|
}
|
|
@@ -794,34 +860,6 @@ export class MessageManager {
|
|
|
794
860
|
}
|
|
795
861
|
}
|
|
796
862
|
|
|
797
|
-
/**
|
|
798
|
-
* Updates the set of files mentioned in the conversation.
|
|
799
|
-
*/
|
|
800
|
-
private updateFilesInContext(messages: Message[]): void {
|
|
801
|
-
this.filesInContext.clear();
|
|
802
|
-
for (const message of messages) {
|
|
803
|
-
for (const block of message.blocks) {
|
|
804
|
-
if (block.type === "tool") {
|
|
805
|
-
// Extract file paths from common tool parameters
|
|
806
|
-
if (block.parameters) {
|
|
807
|
-
try {
|
|
808
|
-
const params = JSON.parse(block.parameters) as Record<
|
|
809
|
-
string,
|
|
810
|
-
unknown
|
|
811
|
-
>;
|
|
812
|
-
const paths = this.extractPathsFromParams(params);
|
|
813
|
-
for (const p of paths) {
|
|
814
|
-
this.filesInContext.add(p);
|
|
815
|
-
}
|
|
816
|
-
} catch {
|
|
817
|
-
// Ignore parse errors
|
|
818
|
-
}
|
|
819
|
-
}
|
|
820
|
-
}
|
|
821
|
-
}
|
|
822
|
-
}
|
|
823
|
-
}
|
|
824
|
-
|
|
825
863
|
private extractPathsFromParams(params: Record<string, unknown>): string[] {
|
|
826
864
|
const paths: string[] = [];
|
|
827
865
|
if (typeof params !== "object" || params === null) return paths;
|
|
@@ -74,9 +74,11 @@ export class PluginManager {
|
|
|
74
74
|
|
|
75
75
|
const marketplaceService = new MarketplaceService();
|
|
76
76
|
|
|
77
|
-
// Trigger auto-update for marketplaces
|
|
77
|
+
// Trigger auto-update for marketplaces in the background
|
|
78
78
|
if (!process.env.VITEST) {
|
|
79
|
-
|
|
79
|
+
marketplaceService.autoUpdateAll().catch((error) => {
|
|
80
|
+
logger?.error("Background marketplace auto-update failed:", error);
|
|
81
|
+
});
|
|
80
82
|
}
|
|
81
83
|
|
|
82
84
|
let installedRegistry = await marketplaceService.getInstalledPlugins();
|
|
@@ -16,8 +16,13 @@ import {
|
|
|
16
16
|
replaceBashCommandsWithOutput,
|
|
17
17
|
executeBashCommands,
|
|
18
18
|
} from "../utils/markdownParser.js";
|
|
19
|
+
import {
|
|
20
|
+
countToolBlocks,
|
|
21
|
+
formatToolTokenSummary,
|
|
22
|
+
} from "../utils/messageOperations.js";
|
|
19
23
|
import type { SkillManager } from "./skillManager.js";
|
|
20
24
|
import type { SkillMetadata } from "../types/skills.js";
|
|
25
|
+
import type { SubagentManager } from "./subagentManager.js";
|
|
21
26
|
|
|
22
27
|
import { logger } from "../utils/globalLogger.js";
|
|
23
28
|
|
|
@@ -30,6 +35,7 @@ export class SlashCommandManager {
|
|
|
30
35
|
private customCommands = new Map<string, CustomSlashCommand>();
|
|
31
36
|
private skillCommandIds = new Set<string>();
|
|
32
37
|
private workdir: string;
|
|
38
|
+
private currentCommandAbortController: AbortController | null = null;
|
|
33
39
|
|
|
34
40
|
constructor(
|
|
35
41
|
private container: Container,
|
|
@@ -71,6 +77,10 @@ export class SlashCommandManager {
|
|
|
71
77
|
return this.container.get<SkillManager>("SkillManager")!;
|
|
72
78
|
}
|
|
73
79
|
|
|
80
|
+
private get subagentManager(): SubagentManager {
|
|
81
|
+
return this.container.get<SubagentManager>("SubagentManager")!;
|
|
82
|
+
}
|
|
83
|
+
|
|
74
84
|
private initializeBuiltinCommands(): void {
|
|
75
85
|
// Register built-in clear command
|
|
76
86
|
this.registerCommand({
|
|
@@ -153,7 +163,7 @@ export class SlashCommandManager {
|
|
|
153
163
|
id: commandId,
|
|
154
164
|
name: skill.name,
|
|
155
165
|
description: `Skill: ${skill.description}`,
|
|
156
|
-
handler: async (args?: string) => {
|
|
166
|
+
handler: async (args?: string, signal?: AbortSignal) => {
|
|
157
167
|
try {
|
|
158
168
|
// 1. Prepare skill content immediately
|
|
159
169
|
const prepared = await this.skillManager.prepareSkill({
|
|
@@ -176,6 +186,109 @@ export class SlashCommandManager {
|
|
|
176
186
|
return;
|
|
177
187
|
}
|
|
178
188
|
|
|
189
|
+
if (skill.context === "fork") {
|
|
190
|
+
// Forked skill execution
|
|
191
|
+
const subagentConfigs =
|
|
192
|
+
await this.subagentManager.loadConfigurations();
|
|
193
|
+
const subagentType = skill.agent || "general-purpose";
|
|
194
|
+
const config = subagentConfigs.find(
|
|
195
|
+
(c) => c.name === subagentType,
|
|
196
|
+
);
|
|
197
|
+
if (!config) {
|
|
198
|
+
throw new Error(
|
|
199
|
+
`Subagent configuration for ${subagentType} not found`,
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Add a ToolBlock to the initial command message for progress tracking
|
|
204
|
+
const toolBlockId = this.messageManager.addToolBlockToMessage(
|
|
205
|
+
messageId,
|
|
206
|
+
{
|
|
207
|
+
name: subagentType,
|
|
208
|
+
parameters: JSON.stringify({
|
|
209
|
+
description: skill.description,
|
|
210
|
+
prompt: prepared.content,
|
|
211
|
+
subagent_type: subagentType,
|
|
212
|
+
}),
|
|
213
|
+
stage: "running",
|
|
214
|
+
},
|
|
215
|
+
);
|
|
216
|
+
|
|
217
|
+
try {
|
|
218
|
+
const instance = await this.subagentManager.createInstance(
|
|
219
|
+
config,
|
|
220
|
+
{
|
|
221
|
+
description: skill.description,
|
|
222
|
+
prompt: prepared.content,
|
|
223
|
+
subagent_type: subagentType,
|
|
224
|
+
model: skill.model,
|
|
225
|
+
},
|
|
226
|
+
false,
|
|
227
|
+
() => {
|
|
228
|
+
// Update the tool block with progress
|
|
229
|
+
const subagent = this.subagentManager.getInstance(
|
|
230
|
+
instance.subagentId,
|
|
231
|
+
);
|
|
232
|
+
if (subagent) {
|
|
233
|
+
const messages = subagent.messages;
|
|
234
|
+
const tokens =
|
|
235
|
+
subagent.messageManager.getLatestTotalTokens();
|
|
236
|
+
const lastTools = subagent.lastTools;
|
|
237
|
+
|
|
238
|
+
const toolCount = countToolBlocks(messages);
|
|
239
|
+
const summary = formatToolTokenSummary(toolCount, tokens);
|
|
240
|
+
|
|
241
|
+
let shortResult = "";
|
|
242
|
+
if (toolCount > 2) {
|
|
243
|
+
shortResult += "... ";
|
|
244
|
+
}
|
|
245
|
+
if (lastTools.length > 0) {
|
|
246
|
+
shortResult += `${lastTools.join(", ")} `;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
shortResult += summary;
|
|
250
|
+
|
|
251
|
+
this.messageManager.updateToolBlock({
|
|
252
|
+
id: toolBlockId,
|
|
253
|
+
messageId,
|
|
254
|
+
shortResult,
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
},
|
|
258
|
+
);
|
|
259
|
+
|
|
260
|
+
try {
|
|
261
|
+
const result = await this.subagentManager.executeAgent(
|
|
262
|
+
instance,
|
|
263
|
+
prepared.content,
|
|
264
|
+
signal,
|
|
265
|
+
);
|
|
266
|
+
|
|
267
|
+
// Update the ToolBlock with final result
|
|
268
|
+
this.messageManager.updateToolBlock({
|
|
269
|
+
id: toolBlockId,
|
|
270
|
+
messageId,
|
|
271
|
+
result,
|
|
272
|
+
success: true,
|
|
273
|
+
stage: "end",
|
|
274
|
+
});
|
|
275
|
+
} finally {
|
|
276
|
+
this.subagentManager.cleanupInstance(instance.subagentId);
|
|
277
|
+
}
|
|
278
|
+
} catch (error) {
|
|
279
|
+
// Update the ToolBlock with error
|
|
280
|
+
this.messageManager.updateToolBlock({
|
|
281
|
+
id: toolBlockId,
|
|
282
|
+
messageId,
|
|
283
|
+
success: false,
|
|
284
|
+
error: error instanceof Error ? error.message : String(error),
|
|
285
|
+
stage: "end",
|
|
286
|
+
});
|
|
287
|
+
throw error; // Re-throw to be caught by outer catch for logging/error block
|
|
288
|
+
}
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
|
|
179
292
|
// 3. Execute bash commands asynchronously
|
|
180
293
|
const result = await this.skillManager.executeSkill({
|
|
181
294
|
skill_name: skill.name,
|
|
@@ -228,7 +341,6 @@ export class SlashCommandManager {
|
|
|
228
341
|
command.description ||
|
|
229
342
|
`Plugin command: ${namespacedName}${hasParameterPlaceholders(command.content) ? " (supports parameters)" : ""}`;
|
|
230
343
|
|
|
231
|
-
// Register as a regular command with a handler that executes the custom command
|
|
232
344
|
this.registerCommand({
|
|
233
345
|
id: namespacedId,
|
|
234
346
|
name: namespacedName,
|
|
@@ -313,12 +425,22 @@ export class SlashCommandManager {
|
|
|
313
425
|
return false;
|
|
314
426
|
}
|
|
315
427
|
|
|
428
|
+
// Abort any previous command if it's still running
|
|
429
|
+
this.currentCommandAbortController?.abort();
|
|
430
|
+
this.currentCommandAbortController = new AbortController();
|
|
431
|
+
|
|
316
432
|
try {
|
|
317
|
-
await command.handler(args);
|
|
433
|
+
await command.handler(args, this.currentCommandAbortController.signal);
|
|
318
434
|
return true;
|
|
319
435
|
} catch (error) {
|
|
320
|
-
|
|
436
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
437
|
+
logger?.debug(`Slash command ${commandId} was aborted`);
|
|
438
|
+
} else {
|
|
439
|
+
console.error(`Failed to execute slash command ${commandId}:`, error);
|
|
440
|
+
}
|
|
321
441
|
return false;
|
|
442
|
+
} finally {
|
|
443
|
+
this.currentCommandAbortController = null;
|
|
322
444
|
}
|
|
323
445
|
}
|
|
324
446
|
|
|
@@ -429,5 +551,9 @@ export class SlashCommandManager {
|
|
|
429
551
|
public abortCurrentCommand(): void {
|
|
430
552
|
// Abort the AI manager if it's running
|
|
431
553
|
this.aiManager.abortAIMessage();
|
|
554
|
+
|
|
555
|
+
// Abort the current slash command handler
|
|
556
|
+
this.currentCommandAbortController?.abort();
|
|
557
|
+
this.currentCommandAbortController = null;
|
|
432
558
|
}
|
|
433
559
|
}
|