wave-agent-sdk 0.0.6 → 0.0.8
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 +32 -20
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +209 -24
- package/dist/constants/events.d.ts +28 -0
- package/dist/constants/events.d.ts.map +1 -0
- package/dist/constants/events.js +27 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/managers/aiManager.d.ts +34 -1
- package/dist/managers/aiManager.d.ts.map +1 -1
- package/dist/managers/aiManager.js +248 -132
- package/dist/managers/backgroundBashManager.d.ts.map +1 -1
- package/dist/managers/backgroundBashManager.js +7 -6
- package/dist/managers/hookManager.d.ts +13 -16
- package/dist/managers/hookManager.d.ts.map +1 -1
- package/dist/managers/hookManager.js +81 -44
- package/dist/managers/liveConfigManager.d.ts +58 -0
- package/dist/managers/liveConfigManager.d.ts.map +1 -0
- package/dist/managers/liveConfigManager.js +160 -0
- package/dist/managers/messageManager.d.ts +41 -24
- package/dist/managers/messageManager.d.ts.map +1 -1
- package/dist/managers/messageManager.js +168 -49
- package/dist/managers/slashCommandManager.d.ts.map +1 -1
- package/dist/managers/slashCommandManager.js +9 -3
- package/dist/managers/subagentManager.d.ts +51 -0
- package/dist/managers/subagentManager.d.ts.map +1 -1
- package/dist/managers/subagentManager.js +190 -19
- package/dist/services/aiService.d.ts +13 -5
- package/dist/services/aiService.d.ts.map +1 -1
- package/dist/services/aiService.js +350 -74
- package/dist/services/configurationWatcher.d.ts +120 -0
- package/dist/services/configurationWatcher.d.ts.map +1 -0
- package/dist/services/configurationWatcher.js +439 -0
- package/dist/services/fileWatcher.d.ts +69 -0
- package/dist/services/fileWatcher.d.ts.map +1 -0
- package/dist/services/fileWatcher.js +213 -0
- package/dist/services/hook.d.ts +91 -9
- package/dist/services/hook.d.ts.map +1 -1
- package/dist/services/hook.js +393 -43
- package/dist/services/jsonlHandler.d.ts +62 -0
- package/dist/services/jsonlHandler.d.ts.map +1 -0
- package/dist/services/jsonlHandler.js +257 -0
- package/dist/services/memory.d.ts +9 -0
- package/dist/services/memory.d.ts.map +1 -1
- package/dist/services/memory.js +81 -12
- package/dist/services/memoryStore.d.ts +81 -0
- package/dist/services/memoryStore.d.ts.map +1 -0
- package/dist/services/memoryStore.js +200 -0
- package/dist/services/session.d.ts +64 -49
- package/dist/services/session.d.ts.map +1 -1
- package/dist/services/session.js +310 -132
- package/dist/tools/bashTool.d.ts.map +1 -1
- package/dist/tools/bashTool.js +5 -4
- package/dist/tools/deleteFileTool.d.ts.map +1 -1
- package/dist/tools/deleteFileTool.js +2 -1
- package/dist/tools/editTool.d.ts.map +1 -1
- package/dist/tools/editTool.js +3 -2
- package/dist/tools/multiEditTool.d.ts.map +1 -1
- package/dist/tools/multiEditTool.js +4 -3
- package/dist/tools/readTool.d.ts.map +1 -1
- package/dist/tools/readTool.js +2 -1
- package/dist/tools/todoWriteTool.d.ts.map +1 -1
- package/dist/tools/todoWriteTool.js +3 -10
- package/dist/tools/writeTool.d.ts.map +1 -1
- package/dist/tools/writeTool.js +5 -6
- package/dist/types/commands.d.ts +4 -0
- package/dist/types/commands.d.ts.map +1 -1
- package/dist/types/core.d.ts +35 -0
- package/dist/types/core.d.ts.map +1 -1
- package/dist/types/environment.d.ts +42 -0
- package/dist/types/environment.d.ts.map +1 -0
- package/dist/types/environment.js +21 -0
- package/dist/types/hooks.d.ts +8 -2
- package/dist/types/hooks.d.ts.map +1 -1
- package/dist/types/hooks.js +8 -2
- package/dist/types/index.d.ts +2 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +2 -0
- package/dist/types/memoryStore.d.ts +82 -0
- package/dist/types/memoryStore.d.ts.map +1 -0
- package/dist/types/memoryStore.js +7 -0
- package/dist/types/messaging.d.ts +21 -9
- package/dist/types/messaging.d.ts.map +1 -1
- package/dist/types/messaging.js +5 -1
- package/dist/types/session.d.ts +20 -0
- package/dist/types/session.d.ts.map +1 -0
- package/dist/types/session.js +7 -0
- package/dist/utils/bashHistory.d.ts.map +1 -1
- package/dist/utils/bashHistory.js +27 -26
- package/dist/utils/cacheControlUtils.d.ts +121 -0
- package/dist/utils/cacheControlUtils.d.ts.map +1 -0
- package/dist/utils/cacheControlUtils.js +367 -0
- package/dist/utils/commandPathResolver.d.ts +52 -0
- package/dist/utils/commandPathResolver.d.ts.map +1 -0
- package/dist/utils/commandPathResolver.js +145 -0
- package/dist/utils/configPaths.d.ts +85 -0
- package/dist/utils/configPaths.d.ts.map +1 -0
- package/dist/utils/configPaths.js +121 -0
- package/dist/utils/configResolver.d.ts +37 -10
- package/dist/utils/configResolver.d.ts.map +1 -1
- package/dist/utils/configResolver.js +127 -23
- package/dist/utils/constants.d.ts +1 -1
- package/dist/utils/constants.js +1 -1
- package/dist/utils/convertMessagesForAPI.d.ts.map +1 -1
- package/dist/utils/convertMessagesForAPI.js +8 -13
- package/dist/utils/customCommands.d.ts.map +1 -1
- package/dist/utils/customCommands.js +66 -21
- package/dist/utils/fileUtils.d.ts +15 -0
- package/dist/utils/fileUtils.d.ts.map +1 -0
- package/dist/utils/fileUtils.js +61 -0
- package/dist/utils/globalLogger.d.ts +102 -0
- package/dist/utils/globalLogger.d.ts.map +1 -0
- package/dist/utils/globalLogger.js +136 -0
- package/dist/utils/hookMatcher.d.ts +1 -6
- package/dist/utils/hookMatcher.d.ts.map +1 -1
- package/dist/utils/mcpUtils.d.ts.map +1 -1
- package/dist/utils/mcpUtils.js +25 -3
- package/dist/utils/messageOperations.d.ts +27 -27
- package/dist/utils/messageOperations.d.ts.map +1 -1
- package/dist/utils/messageOperations.js +46 -36
- package/dist/utils/pathEncoder.d.ts +104 -0
- package/dist/utils/pathEncoder.d.ts.map +1 -0
- package/dist/utils/pathEncoder.js +272 -0
- package/dist/utils/subagentParser.d.ts.map +1 -1
- package/dist/utils/subagentParser.js +2 -1
- package/dist/utils/tokenCalculation.d.ts +26 -0
- package/dist/utils/tokenCalculation.d.ts.map +1 -0
- package/dist/utils/tokenCalculation.js +36 -0
- package/package.json +6 -3
- package/src/agent.ts +301 -37
- package/src/constants/events.ts +38 -0
- package/src/index.ts +2 -0
- package/src/managers/aiManager.ts +325 -173
- package/src/managers/backgroundBashManager.ts +7 -6
- package/src/managers/hookManager.ts +106 -84
- package/src/managers/liveConfigManager.ts +248 -0
- package/src/managers/messageManager.ts +237 -100
- package/src/managers/slashCommandManager.ts +9 -7
- package/src/managers/subagentManager.ts +284 -22
- package/src/services/aiService.ts +474 -83
- package/src/services/configurationWatcher.ts +622 -0
- package/src/services/fileWatcher.ts +301 -0
- package/src/services/hook.ts +538 -47
- package/src/services/jsonlHandler.ts +319 -0
- package/src/services/memory.ts +92 -12
- package/src/services/memoryStore.ts +279 -0
- package/src/services/session.ts +381 -157
- package/src/tools/bashTool.ts +5 -4
- package/src/tools/deleteFileTool.ts +2 -1
- package/src/tools/editTool.ts +3 -2
- package/src/tools/multiEditTool.ts +4 -3
- package/src/tools/readTool.ts +2 -1
- package/src/tools/todoWriteTool.ts +3 -11
- package/src/tools/writeTool.ts +7 -6
- package/src/types/commands.ts +6 -0
- package/src/types/core.ts +44 -0
- package/src/types/environment.ts +60 -0
- package/src/types/hooks.ts +21 -8
- package/src/types/index.ts +2 -0
- package/src/types/memoryStore.ts +94 -0
- package/src/types/messaging.ts +21 -10
- package/src/types/session.ts +25 -0
- package/src/utils/bashHistory.ts +27 -27
- package/src/utils/cacheControlUtils.ts +540 -0
- package/src/utils/commandPathResolver.ts +189 -0
- package/src/utils/configPaths.ts +163 -0
- package/src/utils/configResolver.ts +182 -22
- package/src/utils/constants.ts +1 -1
- package/src/utils/convertMessagesForAPI.ts +8 -14
- package/src/utils/customCommands.ts +90 -22
- package/src/utils/fileUtils.ts +65 -0
- package/src/utils/globalLogger.ts +145 -0
- package/src/utils/hookMatcher.ts +1 -12
- package/src/utils/mcpUtils.ts +34 -3
- package/src/utils/messageOperations.ts +77 -60
- package/src/utils/pathEncoder.ts +379 -0
- package/src/utils/subagentParser.ts +2 -1
- package/src/utils/tokenCalculation.ts +43 -0
- package/src/types/index.ts.backup +0 -357
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { callAgent, compressMessages } from "../services/aiService.js";
|
|
2
2
|
import { getMessagesToCompress } from "../utils/messageOperations.js";
|
|
3
3
|
import { convertMessagesForAPI } from "../utils/convertMessagesForAPI.js";
|
|
4
|
+
import { calculateComprehensiveTotalTokens } from "../utils/tokenCalculation.js";
|
|
4
5
|
import * as memory from "../services/memory.js";
|
|
5
6
|
import type {
|
|
6
7
|
Logger,
|
|
@@ -30,6 +31,7 @@ export interface AIManagerOptions {
|
|
|
30
31
|
callbacks?: AIManagerCallbacks;
|
|
31
32
|
workdir: string;
|
|
32
33
|
systemPrompt?: string;
|
|
34
|
+
subagentType?: string; // Optional subagent type for hook context
|
|
33
35
|
// Resolved configuration
|
|
34
36
|
gatewayConfig: GatewayConfig;
|
|
35
37
|
modelConfig: ModelConfig;
|
|
@@ -47,6 +49,7 @@ export class AIManager {
|
|
|
47
49
|
private hookManager?: HookManager;
|
|
48
50
|
private workdir: string;
|
|
49
51
|
private systemPrompt?: string;
|
|
52
|
+
private subagentType?: string; // Store subagent type for hook context
|
|
50
53
|
|
|
51
54
|
// Configuration properties
|
|
52
55
|
private gatewayConfig: GatewayConfig;
|
|
@@ -61,6 +64,7 @@ export class AIManager {
|
|
|
61
64
|
this.logger = options.logger;
|
|
62
65
|
this.workdir = options.workdir;
|
|
63
66
|
this.systemPrompt = options.systemPrompt;
|
|
67
|
+
this.subagentType = options.subagentType; // Store subagent type
|
|
64
68
|
this.callbacks = options.callbacks ?? {};
|
|
65
69
|
|
|
66
70
|
// Store resolved configuration
|
|
@@ -72,6 +76,74 @@ export class AIManager {
|
|
|
72
76
|
private isCompressing: boolean = false;
|
|
73
77
|
private callbacks: AIManagerCallbacks;
|
|
74
78
|
|
|
79
|
+
/**
|
|
80
|
+
* Update gateway configuration at runtime for live config reload
|
|
81
|
+
* @param newConfig - New gateway configuration
|
|
82
|
+
*/
|
|
83
|
+
updateGatewayConfig(newConfig: GatewayConfig): void {
|
|
84
|
+
this.logger?.info(
|
|
85
|
+
`Live Config: Updating AIManager gateway config - baseURL: ${newConfig.baseURL}`,
|
|
86
|
+
);
|
|
87
|
+
this.gatewayConfig = newConfig;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Update model configuration at runtime for live config reload
|
|
92
|
+
* @param newConfig - New model configuration
|
|
93
|
+
*/
|
|
94
|
+
updateModelConfig(newConfig: ModelConfig): void {
|
|
95
|
+
this.logger?.info(
|
|
96
|
+
`Live Config: Updating AIManager model config - agent: ${newConfig.agentModel}, fast: ${newConfig.fastModel}`,
|
|
97
|
+
);
|
|
98
|
+
this.modelConfig = newConfig;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Update token limit at runtime for live config reload
|
|
103
|
+
* @param newLimit - New token limit
|
|
104
|
+
*/
|
|
105
|
+
updateTokenLimit(newLimit: number): void {
|
|
106
|
+
this.logger?.info(
|
|
107
|
+
`Live Config: Updating AIManager token limit: ${newLimit}`,
|
|
108
|
+
);
|
|
109
|
+
this.tokenLimit = newLimit;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Update all configurations at once for live config reload
|
|
114
|
+
* @param newGatewayConfig - New gateway configuration
|
|
115
|
+
* @param newModelConfig - New model configuration
|
|
116
|
+
* @param newTokenLimit - New token limit
|
|
117
|
+
*/
|
|
118
|
+
updateConfiguration(
|
|
119
|
+
newGatewayConfig: GatewayConfig,
|
|
120
|
+
newModelConfig: ModelConfig,
|
|
121
|
+
newTokenLimit: number,
|
|
122
|
+
): void {
|
|
123
|
+
this.logger?.info("Live Config: Updating all AIManager configuration");
|
|
124
|
+
this.gatewayConfig = newGatewayConfig;
|
|
125
|
+
this.modelConfig = newModelConfig;
|
|
126
|
+
this.tokenLimit = newTokenLimit;
|
|
127
|
+
this.logger?.info(
|
|
128
|
+
`Live Config: Configuration updated - model: ${newModelConfig.agentModel}, tokenLimit: ${newTokenLimit}`,
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Get current configuration for debugging
|
|
134
|
+
*/
|
|
135
|
+
getCurrentConfiguration(): {
|
|
136
|
+
gatewayConfig: GatewayConfig;
|
|
137
|
+
modelConfig: ModelConfig;
|
|
138
|
+
tokenLimit: number;
|
|
139
|
+
} {
|
|
140
|
+
return {
|
|
141
|
+
gatewayConfig: { ...this.gatewayConfig },
|
|
142
|
+
modelConfig: { ...this.modelConfig },
|
|
143
|
+
tokenLimit: this.tokenLimit,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
75
147
|
/**
|
|
76
148
|
* Get filtered tool configuration
|
|
77
149
|
*/
|
|
@@ -117,7 +189,7 @@ export class AIManager {
|
|
|
117
189
|
private generateCompactParams(
|
|
118
190
|
toolName: string,
|
|
119
191
|
toolArgs: Record<string, unknown>,
|
|
120
|
-
): string
|
|
192
|
+
): string {
|
|
121
193
|
try {
|
|
122
194
|
const toolPlugin = this.toolManager
|
|
123
195
|
.list()
|
|
@@ -131,21 +203,27 @@ export class AIManager {
|
|
|
131
203
|
} catch (error) {
|
|
132
204
|
this.logger?.warn("Failed to generate compactParams", error);
|
|
133
205
|
}
|
|
134
|
-
return
|
|
206
|
+
return "";
|
|
135
207
|
}
|
|
136
208
|
|
|
137
209
|
// Private method to handle token statistics and message compression
|
|
138
210
|
private async handleTokenUsageAndCompression(
|
|
139
|
-
usage:
|
|
211
|
+
usage: Usage | undefined,
|
|
140
212
|
abortController: AbortController,
|
|
141
213
|
): Promise<void> {
|
|
142
214
|
if (!usage) return;
|
|
143
215
|
|
|
144
|
-
// Update token statistics - display
|
|
145
|
-
|
|
216
|
+
// Update token statistics - display comprehensive token usage including cache tokens
|
|
217
|
+
const comprehensiveTotalTokens = calculateComprehensiveTotalTokens(usage);
|
|
218
|
+
this.messageManager.setlatestTotalTokens(comprehensiveTotalTokens);
|
|
146
219
|
|
|
147
220
|
// Check if token limit exceeded - use injected configuration
|
|
148
|
-
if (
|
|
221
|
+
if (
|
|
222
|
+
usage.total_tokens +
|
|
223
|
+
(usage.cache_read_input_tokens || 0) +
|
|
224
|
+
(usage.cache_creation_input_tokens || 0) >
|
|
225
|
+
this.tokenLimit
|
|
226
|
+
) {
|
|
149
227
|
this.logger?.debug(
|
|
150
228
|
`Token usage exceeded ${this.tokenLimit}, compressing messages...`,
|
|
151
229
|
);
|
|
@@ -160,6 +238,9 @@ export class AIManager {
|
|
|
160
238
|
if (messagesToCompress.length > 0) {
|
|
161
239
|
const recentChatMessages = convertMessagesForAPI(messagesToCompress);
|
|
162
240
|
|
|
241
|
+
// Save session before compression to preserve original messages
|
|
242
|
+
await this.messageManager.saveSession();
|
|
243
|
+
|
|
163
244
|
this.setIsCompressing(true);
|
|
164
245
|
try {
|
|
165
246
|
const compressionResult = await compressMessages({
|
|
@@ -169,26 +250,28 @@ export class AIManager {
|
|
|
169
250
|
abortSignal: abortController.signal,
|
|
170
251
|
});
|
|
171
252
|
|
|
172
|
-
// Execute message reconstruction and sessionId update after compression
|
|
173
|
-
this.messageManager.compressMessagesAndUpdateSession(
|
|
174
|
-
insertIndex,
|
|
175
|
-
compressionResult.content,
|
|
176
|
-
);
|
|
177
|
-
|
|
178
253
|
// Handle usage tracking for compression operations
|
|
254
|
+
let compressionUsage: Usage | undefined;
|
|
179
255
|
if (compressionResult.usage) {
|
|
180
|
-
|
|
256
|
+
compressionUsage = {
|
|
181
257
|
prompt_tokens: compressionResult.usage.prompt_tokens,
|
|
182
258
|
completion_tokens: compressionResult.usage.completion_tokens,
|
|
183
259
|
total_tokens: compressionResult.usage.total_tokens,
|
|
184
260
|
model: this.modelConfig.fastModel,
|
|
185
261
|
operation_type: "compress",
|
|
186
262
|
};
|
|
263
|
+
}
|
|
187
264
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
265
|
+
// Execute message reconstruction and sessionId update after compression
|
|
266
|
+
this.messageManager.compressMessagesAndUpdateSession(
|
|
267
|
+
insertIndex,
|
|
268
|
+
compressionResult.content,
|
|
269
|
+
compressionUsage,
|
|
270
|
+
);
|
|
271
|
+
|
|
272
|
+
// Notify Agent to add to usage tracking
|
|
273
|
+
if (compressionUsage && this.callbacks?.onUsageAdded) {
|
|
274
|
+
this.callbacks.onUsageAdded(compressionUsage);
|
|
192
275
|
}
|
|
193
276
|
|
|
194
277
|
this.logger?.debug(
|
|
@@ -228,6 +311,9 @@ export class AIManager {
|
|
|
228
311
|
return;
|
|
229
312
|
}
|
|
230
313
|
|
|
314
|
+
// Save session in each recursion to ensure message persistence
|
|
315
|
+
await this.messageManager.saveSession();
|
|
316
|
+
|
|
231
317
|
// Only create new AbortControllers for the initial call (recursionDepth === 0)
|
|
232
318
|
// For recursive calls, reuse existing controllers to maintain abort signal
|
|
233
319
|
let abortController: AbortController;
|
|
@@ -262,7 +348,10 @@ export class AIManager {
|
|
|
262
348
|
this.workdir,
|
|
263
349
|
);
|
|
264
350
|
|
|
265
|
-
//
|
|
351
|
+
// Add assistant message first (for streaming updates)
|
|
352
|
+
this.messageManager.addAssistantMessage();
|
|
353
|
+
|
|
354
|
+
// Call AI service with streaming callbacks
|
|
266
355
|
const result = await callAgent({
|
|
267
356
|
gatewayConfig: this.gatewayConfig,
|
|
268
357
|
modelConfig: this.modelConfig,
|
|
@@ -274,18 +363,47 @@ export class AIManager {
|
|
|
274
363
|
tools: this.getFilteredToolsConfig(allowedTools), // Pass filtered tool configuration
|
|
275
364
|
model: model, // Use passed model
|
|
276
365
|
systemPrompt: this.systemPrompt, // Pass custom system prompt
|
|
366
|
+
// Streaming callbacks
|
|
367
|
+
onContentUpdate: (content: string) => {
|
|
368
|
+
this.messageManager.updateCurrentMessageContent(content);
|
|
369
|
+
},
|
|
370
|
+
onToolUpdate: (toolCall) => {
|
|
371
|
+
// Use parametersChunk as compact param for better performance
|
|
372
|
+
// No need to extract params or generate compact params during streaming
|
|
373
|
+
this.logger?.debug("Tool streaming update:", toolCall);
|
|
374
|
+
|
|
375
|
+
// Update tool block with streaming parameters using parametersChunk as compact param
|
|
376
|
+
this.messageManager.updateToolBlock({
|
|
377
|
+
id: toolCall.id,
|
|
378
|
+
name: toolCall.name,
|
|
379
|
+
parameters: toolCall.parameters,
|
|
380
|
+
parametersChunk: toolCall.parametersChunk,
|
|
381
|
+
compactParams: toolCall.parameters?.split("\n").pop()?.slice(-30),
|
|
382
|
+
stage: toolCall.stage || "streaming", // Default to streaming if stage not provided
|
|
383
|
+
});
|
|
384
|
+
},
|
|
277
385
|
});
|
|
278
386
|
|
|
279
|
-
//
|
|
280
|
-
|
|
281
|
-
|
|
387
|
+
// Log finish reason and response headers if available
|
|
388
|
+
if (result.finish_reason) {
|
|
389
|
+
this.logger?.debug(
|
|
390
|
+
`AI response finished with reason: ${result.finish_reason}`,
|
|
391
|
+
);
|
|
392
|
+
}
|
|
393
|
+
if (
|
|
394
|
+
result.response_headers &&
|
|
395
|
+
Object.keys(result.response_headers).length > 0
|
|
396
|
+
) {
|
|
397
|
+
this.logger?.debug("AI response headers:", result.response_headers);
|
|
398
|
+
}
|
|
282
399
|
|
|
283
|
-
if (result.
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
400
|
+
if (result.metadata && Object.keys(result.metadata).length > 0) {
|
|
401
|
+
this.messageManager.mergeAssistantMetadata(result.metadata);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// Handle result content from non-streaming mode
|
|
405
|
+
if (result.content) {
|
|
406
|
+
this.messageManager.updateCurrentMessageContent(result.content);
|
|
289
407
|
}
|
|
290
408
|
|
|
291
409
|
// Handle usage tracking for agent operations
|
|
@@ -297,156 +415,183 @@ export class AIManager {
|
|
|
297
415
|
total_tokens: result.usage.total_tokens,
|
|
298
416
|
model: model || this.modelConfig.agentModel,
|
|
299
417
|
operation_type: "agent",
|
|
418
|
+
// Preserve cache fields if present
|
|
419
|
+
...(result.usage.cache_read_input_tokens !== undefined && {
|
|
420
|
+
cache_read_input_tokens: result.usage.cache_read_input_tokens,
|
|
421
|
+
}),
|
|
422
|
+
...(result.usage.cache_creation_input_tokens !== undefined && {
|
|
423
|
+
cache_creation_input_tokens:
|
|
424
|
+
result.usage.cache_creation_input_tokens,
|
|
425
|
+
}),
|
|
426
|
+
...(result.usage.cache_creation && {
|
|
427
|
+
cache_creation: result.usage.cache_creation,
|
|
428
|
+
}),
|
|
300
429
|
};
|
|
301
430
|
}
|
|
302
431
|
|
|
303
|
-
//
|
|
304
|
-
this.messageManager.addAssistantMessage(content, toolCalls, usage);
|
|
305
|
-
|
|
306
|
-
// Notify Agent to add to usage tracking
|
|
432
|
+
// Set usage on the assistant message if available
|
|
307
433
|
if (usage) {
|
|
434
|
+
const messages = this.messageManager.getMessages();
|
|
435
|
+
const lastMessage = messages[messages.length - 1];
|
|
436
|
+
if (lastMessage && lastMessage.role === "assistant") {
|
|
437
|
+
lastMessage.usage = usage;
|
|
438
|
+
this.messageManager.setMessages(messages);
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
// Notify Agent to add to usage tracking
|
|
308
442
|
if (this.callbacks?.onUsageAdded) {
|
|
309
443
|
this.callbacks.onUsageAdded(usage);
|
|
310
444
|
}
|
|
311
445
|
}
|
|
312
446
|
|
|
447
|
+
// Collect tool calls for processing
|
|
448
|
+
const toolCalls: ChatCompletionMessageFunctionToolCall[] = [];
|
|
449
|
+
if (result.tool_calls) {
|
|
450
|
+
for (const toolCall of result.tool_calls) {
|
|
451
|
+
if (toolCall.type === "function") {
|
|
452
|
+
toolCalls.push(toolCall);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
|
|
313
457
|
if (toolCalls.length > 0) {
|
|
314
458
|
// Execute all tools in parallel using Promise.all
|
|
315
459
|
const toolExecutionPromises = toolCalls.map(
|
|
316
460
|
async (functionToolCall) => {
|
|
317
461
|
const toolId = functionToolCall.id || "";
|
|
318
462
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
463
|
+
// Check if already interrupted, skip tool execution if so
|
|
464
|
+
if (
|
|
465
|
+
abortController.signal.aborted ||
|
|
466
|
+
toolAbortController.signal.aborted
|
|
467
|
+
) {
|
|
468
|
+
return;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
const toolName = functionToolCall.function?.name || "";
|
|
472
|
+
// Safely parse tool parameters, handle tools without parameters
|
|
473
|
+
let toolArgs: Record<string, unknown> = {};
|
|
474
|
+
const argsString = functionToolCall.function?.arguments?.trim();
|
|
475
|
+
|
|
476
|
+
if (!argsString || argsString === "") {
|
|
477
|
+
// Tool without parameters, use empty object
|
|
478
|
+
toolArgs = {};
|
|
479
|
+
} else {
|
|
480
|
+
try {
|
|
481
|
+
toolArgs = JSON.parse(argsString);
|
|
482
|
+
} catch (parseError) {
|
|
483
|
+
// For non-empty but malformed JSON, still throw exception
|
|
484
|
+
const errorMessage = `Failed to parse tool arguments, finish_reason: ${result.finish_reason}`;
|
|
485
|
+
const fullErrorMessage = `${errorMessage}\nAI response headers:, ${JSON.stringify(result.response_headers)}`;
|
|
486
|
+
this.logger?.error(fullErrorMessage, parseError);
|
|
487
|
+
this.messageManager.updateToolBlock({
|
|
488
|
+
id: toolId,
|
|
489
|
+
parameters: argsString,
|
|
490
|
+
result: errorMessage,
|
|
491
|
+
success: false,
|
|
492
|
+
error: fullErrorMessage,
|
|
493
|
+
stage: "end",
|
|
494
|
+
name: toolName,
|
|
495
|
+
compactParams: "",
|
|
496
|
+
});
|
|
325
497
|
return;
|
|
326
498
|
}
|
|
499
|
+
}
|
|
327
500
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
if (!argsString || argsString === "") {
|
|
333
|
-
// Tool without parameters, use empty object
|
|
334
|
-
toolArgs = {};
|
|
335
|
-
} else {
|
|
336
|
-
try {
|
|
337
|
-
toolArgs = JSON.parse(argsString);
|
|
338
|
-
} catch (parseError) {
|
|
339
|
-
// For non-empty but malformed JSON, still throw exception
|
|
340
|
-
const errorMessage = `Failed to parse tool arguments: ${argsString}`;
|
|
341
|
-
this.logger?.error(errorMessage, parseError);
|
|
342
|
-
throw new Error(errorMessage);
|
|
343
|
-
}
|
|
344
|
-
}
|
|
501
|
+
const compactParams = this.generateCompactParams(
|
|
502
|
+
toolName,
|
|
503
|
+
toolArgs,
|
|
504
|
+
);
|
|
345
505
|
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
506
|
+
// Emit running stage for non-streaming tool calls (tool execution about to start)
|
|
507
|
+
this.messageManager.updateToolBlock({
|
|
508
|
+
id: toolId,
|
|
509
|
+
stage: "running",
|
|
510
|
+
name: toolName,
|
|
511
|
+
compactParams,
|
|
512
|
+
parameters: argsString,
|
|
513
|
+
parametersChunk: "",
|
|
514
|
+
});
|
|
515
|
+
|
|
516
|
+
try {
|
|
517
|
+
// Execute PreToolUse hooks before tool execution
|
|
518
|
+
const shouldExecuteTool = await this.executePreToolUseHooks(
|
|
349
519
|
toolName,
|
|
350
520
|
toolArgs,
|
|
351
|
-
);
|
|
352
|
-
|
|
353
|
-
this.messageManager.updateToolBlock({
|
|
354
521
|
toolId,
|
|
355
|
-
|
|
356
|
-
isRunning: true, // isRunning: true
|
|
357
|
-
name: toolName,
|
|
358
|
-
compactParams,
|
|
359
|
-
});
|
|
522
|
+
);
|
|
360
523
|
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
toolName
|
|
365
|
-
toolArgs,
|
|
366
|
-
toolId,
|
|
524
|
+
// If PreToolUse hooks blocked execution, skip tool execution
|
|
525
|
+
if (!shouldExecuteTool) {
|
|
526
|
+
this.logger?.info(
|
|
527
|
+
`Tool ${toolName} execution blocked by PreToolUse hooks`,
|
|
367
528
|
);
|
|
529
|
+
return; // Skip this tool and return from this map function
|
|
530
|
+
}
|
|
368
531
|
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
// Create tool execution context
|
|
378
|
-
const context: ToolContext = {
|
|
379
|
-
abortSignal: toolAbortController.signal,
|
|
380
|
-
backgroundBashManager: this.backgroundBashManager,
|
|
381
|
-
workdir: this.workdir,
|
|
382
|
-
};
|
|
383
|
-
|
|
384
|
-
// Execute tool
|
|
385
|
-
const toolResult = await this.toolManager.execute(
|
|
386
|
-
functionToolCall.function?.name || "",
|
|
387
|
-
toolArgs,
|
|
388
|
-
context,
|
|
389
|
-
);
|
|
532
|
+
// Create tool execution context
|
|
533
|
+
const context: ToolContext = {
|
|
534
|
+
abortSignal: toolAbortController.signal,
|
|
535
|
+
backgroundBashManager: this.backgroundBashManager,
|
|
536
|
+
workdir: this.workdir,
|
|
537
|
+
};
|
|
390
538
|
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
(toolResult.error ? `Error: ${toolResult.error}` : ""),
|
|
398
|
-
success: toolResult.success,
|
|
399
|
-
error: toolResult.error,
|
|
400
|
-
isRunning: false, // isRunning: false
|
|
401
|
-
name: toolName,
|
|
402
|
-
shortResult: toolResult.shortResult,
|
|
403
|
-
compactParams,
|
|
404
|
-
});
|
|
539
|
+
// Execute tool
|
|
540
|
+
const toolResult = await this.toolManager.execute(
|
|
541
|
+
functionToolCall.function?.name || "",
|
|
542
|
+
toolArgs,
|
|
543
|
+
context,
|
|
544
|
+
);
|
|
405
545
|
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
await this.executePostToolUseHooks(
|
|
420
|
-
toolId,
|
|
421
|
-
toolName,
|
|
422
|
-
toolArgs,
|
|
423
|
-
toolResult,
|
|
424
|
-
);
|
|
425
|
-
} catch (toolError) {
|
|
426
|
-
const errorMessage =
|
|
427
|
-
toolError instanceof Error
|
|
428
|
-
? toolError.message
|
|
429
|
-
: String(toolError);
|
|
546
|
+
// Update message state - tool execution completed
|
|
547
|
+
this.messageManager.updateToolBlock({
|
|
548
|
+
id: toolId,
|
|
549
|
+
parameters: argsString,
|
|
550
|
+
result:
|
|
551
|
+
toolResult.content ||
|
|
552
|
+
(toolResult.error ? `Error: ${toolResult.error}` : ""),
|
|
553
|
+
success: toolResult.success,
|
|
554
|
+
error: toolResult.error,
|
|
555
|
+
stage: "end",
|
|
556
|
+
name: toolName,
|
|
557
|
+
shortResult: toolResult.shortResult,
|
|
558
|
+
});
|
|
430
559
|
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
560
|
+
// If tool returns diff information, add diff block
|
|
561
|
+
if (
|
|
562
|
+
toolResult.success &&
|
|
563
|
+
toolResult.diffResult &&
|
|
564
|
+
toolResult.filePath
|
|
565
|
+
) {
|
|
566
|
+
this.messageManager.addDiffBlock(
|
|
567
|
+
toolResult.filePath,
|
|
568
|
+
toolResult.diffResult,
|
|
569
|
+
);
|
|
441
570
|
}
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
571
|
+
|
|
572
|
+
// Execute PostToolUse hooks after successful tool completion
|
|
573
|
+
await this.executePostToolUseHooks(
|
|
574
|
+
toolId,
|
|
575
|
+
toolName,
|
|
576
|
+
toolArgs,
|
|
577
|
+
toolResult,
|
|
449
578
|
);
|
|
579
|
+
} catch (toolError) {
|
|
580
|
+
const errorMessage =
|
|
581
|
+
toolError instanceof Error
|
|
582
|
+
? toolError.message
|
|
583
|
+
: String(toolError);
|
|
584
|
+
|
|
585
|
+
this.messageManager.updateToolBlock({
|
|
586
|
+
id: toolId,
|
|
587
|
+
parameters: JSON.stringify(toolArgs, null, 2),
|
|
588
|
+
result: `Tool execution failed: ${errorMessage}`,
|
|
589
|
+
success: false,
|
|
590
|
+
error: errorMessage,
|
|
591
|
+
stage: "end",
|
|
592
|
+
name: toolName,
|
|
593
|
+
compactParams,
|
|
594
|
+
});
|
|
450
595
|
}
|
|
451
596
|
},
|
|
452
597
|
);
|
|
@@ -478,70 +623,74 @@ export class AIManager {
|
|
|
478
623
|
error instanceof Error ? error.message : "Unknown error occurred",
|
|
479
624
|
);
|
|
480
625
|
} finally {
|
|
481
|
-
// Only execute
|
|
626
|
+
// Only execute cleanup and hooks for the initial call
|
|
482
627
|
if (recursionDepth === 0) {
|
|
483
|
-
//
|
|
628
|
+
// Save session in each recursion to ensure message persistence
|
|
629
|
+
await this.messageManager.saveSession();
|
|
630
|
+
// Set loading to false first
|
|
631
|
+
this.setIsLoading(false);
|
|
632
|
+
|
|
633
|
+
// Clear abort controllers
|
|
634
|
+
this.abortController = null;
|
|
635
|
+
this.toolAbortController = null;
|
|
636
|
+
|
|
637
|
+
// Execute Stop/SubagentStop hooks only if the operation was not aborted
|
|
484
638
|
const isCurrentlyAborted =
|
|
485
639
|
abortController.signal.aborted || toolAbortController.signal.aborted;
|
|
486
640
|
|
|
487
641
|
if (!isCurrentlyAborted) {
|
|
488
642
|
const shouldContinue = await this.executeStopHooks();
|
|
489
643
|
|
|
490
|
-
// If Stop hooks indicate we should continue (due to blocking errors),
|
|
644
|
+
// If Stop/SubagentStop hooks indicate we should continue (due to blocking errors),
|
|
491
645
|
// restart the AI conversation cycle
|
|
492
646
|
if (shouldContinue) {
|
|
493
647
|
this.logger?.info(
|
|
494
|
-
"Stop hooks indicate issues need fixing, continuing conversation
|
|
648
|
+
`${this.subagentType ? "SubagentStop" : "Stop"} hooks indicate issues need fixing, continuing conversation...`,
|
|
495
649
|
);
|
|
496
650
|
|
|
497
651
|
// Restart the conversation to let AI fix the issues
|
|
498
|
-
// Use recursionDepth =
|
|
652
|
+
// Use recursionDepth = 0 to set loading false again for continuation
|
|
499
653
|
await this.sendAIMessage({
|
|
500
|
-
recursionDepth:
|
|
654
|
+
recursionDepth: 0,
|
|
501
655
|
model,
|
|
502
656
|
allowedTools,
|
|
503
657
|
});
|
|
504
658
|
}
|
|
505
659
|
}
|
|
506
|
-
|
|
507
|
-
// Save session after all operations (including continuation) are complete
|
|
508
|
-
await this.messageManager.saveSession();
|
|
509
|
-
|
|
510
|
-
// Clear abort controllers and loading state after all operations are complete
|
|
511
|
-
this.abortController = null;
|
|
512
|
-
this.toolAbortController = null;
|
|
513
|
-
|
|
514
|
-
// Set loading to false at the very end, after all operations including continuation
|
|
515
|
-
this.setIsLoading(false);
|
|
516
660
|
}
|
|
517
661
|
}
|
|
518
662
|
}
|
|
519
663
|
|
|
520
664
|
/**
|
|
521
|
-
* Execute Stop hooks when AI response cycle completes
|
|
665
|
+
* Execute Stop or SubagentStop hooks when AI response cycle completes
|
|
666
|
+
* Uses "SubagentStop" hook name when triggered by a subagent, otherwise uses "Stop"
|
|
522
667
|
* @returns Promise<boolean> - true if should continue conversation, false if should stop
|
|
523
668
|
*/
|
|
524
669
|
private async executeStopHooks(): Promise<boolean> {
|
|
525
670
|
if (!this.hookManager) return false;
|
|
526
671
|
|
|
527
672
|
try {
|
|
673
|
+
// Use "SubagentStop" hook name when triggered by a subagent, otherwise use "Stop"
|
|
674
|
+
const hookName = this.subagentType ? "SubagentStop" : "Stop";
|
|
675
|
+
|
|
528
676
|
const context: ExtendedHookExecutionContext = {
|
|
529
|
-
event:
|
|
677
|
+
event: hookName,
|
|
530
678
|
projectDir: this.workdir,
|
|
531
679
|
timestamp: new Date(),
|
|
532
680
|
sessionId: this.messageManager.getSessionId(),
|
|
533
681
|
transcriptPath: this.messageManager.getTranscriptPath(),
|
|
534
682
|
cwd: this.workdir,
|
|
683
|
+
subagentType: this.subagentType, // Include subagent type in hook context
|
|
535
684
|
// Stop hooks don't need toolName, toolInput, toolResponse, or userPrompt
|
|
536
685
|
};
|
|
537
686
|
|
|
538
|
-
const results = await this.hookManager.executeHooks(
|
|
687
|
+
const results = await this.hookManager.executeHooks(hookName, context);
|
|
539
688
|
|
|
540
689
|
// Process hook results to handle exit codes and appropriate responses
|
|
541
690
|
let shouldContinue = false;
|
|
542
691
|
if (results.length > 0) {
|
|
543
692
|
const processResult = this.hookManager.processHookResults(
|
|
544
|
-
|
|
693
|
+
hookName,
|
|
545
694
|
results,
|
|
546
695
|
this.messageManager,
|
|
547
696
|
);
|
|
@@ -549,7 +698,7 @@ export class AIManager {
|
|
|
549
698
|
// If hook processing indicates we should block (exit code 2), continue conversation
|
|
550
699
|
if (processResult.shouldBlock) {
|
|
551
700
|
this.logger?.info(
|
|
552
|
-
|
|
701
|
+
`${hookName} hook blocked stopping with error:`,
|
|
553
702
|
processResult.errorMessage,
|
|
554
703
|
);
|
|
555
704
|
shouldContinue = true;
|
|
@@ -559,7 +708,7 @@ export class AIManager {
|
|
|
559
708
|
// Log hook execution results for debugging
|
|
560
709
|
if (results.length > 0) {
|
|
561
710
|
this.logger?.debug(
|
|
562
|
-
`Executed ${results.length}
|
|
711
|
+
`Executed ${results.length} ${hookName} hook(s):`,
|
|
563
712
|
results.map((r) => ({
|
|
564
713
|
success: r.success,
|
|
565
714
|
duration: r.duration,
|
|
@@ -573,7 +722,10 @@ export class AIManager {
|
|
|
573
722
|
return shouldContinue;
|
|
574
723
|
} catch (error) {
|
|
575
724
|
// Hook execution errors should not interrupt the main workflow
|
|
576
|
-
this.logger?.error(
|
|
725
|
+
this.logger?.error(
|
|
726
|
+
`${this.subagentType ? "SubagentStop" : "Stop"} hook execution failed:`,
|
|
727
|
+
error,
|
|
728
|
+
);
|
|
577
729
|
return false;
|
|
578
730
|
}
|
|
579
731
|
}
|
|
@@ -599,6 +751,7 @@ export class AIManager {
|
|
|
599
751
|
transcriptPath: this.messageManager.getTranscriptPath(),
|
|
600
752
|
cwd: this.workdir,
|
|
601
753
|
toolInput,
|
|
754
|
+
subagentType: this.subagentType, // Include subagent type in hook context
|
|
602
755
|
};
|
|
603
756
|
|
|
604
757
|
const results = await this.hookManager.executeHooks(
|
|
@@ -614,6 +767,7 @@ export class AIManager {
|
|
|
614
767
|
results,
|
|
615
768
|
this.messageManager,
|
|
616
769
|
toolId, // Pass toolId for proper PreToolUse blocking error handling
|
|
770
|
+
JSON.stringify(toolInput || {}, null, 2), // Pass serialized tool parameters
|
|
617
771
|
);
|
|
618
772
|
shouldContinue = !processResult.shouldBlock;
|
|
619
773
|
}
|
|
@@ -662,6 +816,7 @@ export class AIManager {
|
|
|
662
816
|
cwd: this.workdir,
|
|
663
817
|
toolInput,
|
|
664
818
|
toolResponse,
|
|
819
|
+
subagentType: this.subagentType, // Include subagent type in hook context
|
|
665
820
|
};
|
|
666
821
|
|
|
667
822
|
const results = await this.hookManager.executeHooks(
|
|
@@ -671,14 +826,11 @@ export class AIManager {
|
|
|
671
826
|
|
|
672
827
|
// Process hook results to handle exit codes and update tool results
|
|
673
828
|
if (results.length > 0) {
|
|
674
|
-
const originalToolResult = toolResponse?.content || "";
|
|
675
|
-
|
|
676
829
|
this.hookManager.processHookResults(
|
|
677
830
|
"PostToolUse",
|
|
678
831
|
results,
|
|
679
832
|
this.messageManager,
|
|
680
833
|
toolId,
|
|
681
|
-
originalToolResult,
|
|
682
834
|
);
|
|
683
835
|
}
|
|
684
836
|
|