wave-agent-sdk 0.0.7 → 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 +202 -20
- 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 +243 -128
- package/dist/managers/backgroundBashManager.d.ts.map +1 -1
- package/dist/managers/backgroundBashManager.js +7 -6
- package/dist/managers/hookManager.d.ts +9 -4
- package/dist/managers/hookManager.d.ts.map +1 -1
- package/dist/managers/hookManager.js +62 -30
- 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 +38 -13
- package/dist/managers/messageManager.d.ts.map +1 -1
- package/dist/managers/messageManager.js +163 -30
- package/dist/managers/slashCommandManager.d.ts.map +1 -1
- package/dist/managers/slashCommandManager.js +4 -1
- package/dist/managers/subagentManager.d.ts +51 -0
- package/dist/managers/subagentManager.d.ts.map +1 -1
- package/dist/managers/subagentManager.js +189 -18
- 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/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 +14 -2
- package/dist/types/messaging.d.ts.map +1 -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 +7 -5
- 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/mcpUtils.d.ts.map +1 -1
- package/dist/utils/mcpUtils.js +25 -3
- package/dist/utils/messageOperations.d.ts +20 -8
- package/dist/utils/messageOperations.d.ts.map +1 -1
- package/dist/utils/messageOperations.js +25 -16
- 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 +298 -34
- package/src/constants/events.ts +38 -0
- package/src/index.ts +2 -0
- package/src/managers/aiManager.ts +323 -170
- package/src/managers/backgroundBashManager.ts +7 -6
- package/src/managers/hookManager.ts +83 -40
- package/src/managers/liveConfigManager.ts +248 -0
- package/src/managers/messageManager.ts +230 -63
- package/src/managers/slashCommandManager.ts +4 -1
- package/src/managers/subagentManager.ts +283 -21
- 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/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 +14 -2
- 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 +7 -5
- package/src/utils/customCommands.ts +90 -22
- package/src/utils/fileUtils.ts +65 -0
- package/src/utils/globalLogger.ts +145 -0
- package/src/utils/mcpUtils.ts +34 -3
- package/src/utils/messageOperations.ts +42 -20
- package/src/utils/pathEncoder.ts +379 -0
- package/src/utils/subagentParser.ts +2 -1
- package/src/utils/tokenCalculation.ts +43 -0
|
@@ -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,19 +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
|
-
|
|
289
|
-
|
|
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);
|
|
290
407
|
}
|
|
291
408
|
|
|
292
409
|
// Handle usage tracking for agent operations
|
|
@@ -298,156 +415,183 @@ export class AIManager {
|
|
|
298
415
|
total_tokens: result.usage.total_tokens,
|
|
299
416
|
model: model || this.modelConfig.agentModel,
|
|
300
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
|
+
}),
|
|
301
429
|
};
|
|
302
430
|
}
|
|
303
431
|
|
|
304
|
-
//
|
|
305
|
-
this.messageManager.addAssistantMessage(content, toolCalls, usage);
|
|
306
|
-
|
|
307
|
-
// Notify Agent to add to usage tracking
|
|
432
|
+
// Set usage on the assistant message if available
|
|
308
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
|
|
309
442
|
if (this.callbacks?.onUsageAdded) {
|
|
310
443
|
this.callbacks.onUsageAdded(usage);
|
|
311
444
|
}
|
|
312
445
|
}
|
|
313
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
|
+
|
|
314
457
|
if (toolCalls.length > 0) {
|
|
315
458
|
// Execute all tools in parallel using Promise.all
|
|
316
459
|
const toolExecutionPromises = toolCalls.map(
|
|
317
460
|
async (functionToolCall) => {
|
|
318
461
|
const toolId = functionToolCall.id || "";
|
|
319
462
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
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
|
+
});
|
|
326
497
|
return;
|
|
327
498
|
}
|
|
499
|
+
}
|
|
328
500
|
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
if (!argsString || argsString === "") {
|
|
334
|
-
// Tool without parameters, use empty object
|
|
335
|
-
toolArgs = {};
|
|
336
|
-
} else {
|
|
337
|
-
try {
|
|
338
|
-
toolArgs = JSON.parse(argsString);
|
|
339
|
-
} catch (parseError) {
|
|
340
|
-
// For non-empty but malformed JSON, still throw exception
|
|
341
|
-
const errorMessage = `Failed to parse tool arguments: ${argsString}`;
|
|
342
|
-
this.logger?.error(errorMessage, parseError);
|
|
343
|
-
throw new Error(errorMessage);
|
|
344
|
-
}
|
|
345
|
-
}
|
|
501
|
+
const compactParams = this.generateCompactParams(
|
|
502
|
+
toolName,
|
|
503
|
+
toolArgs,
|
|
504
|
+
);
|
|
346
505
|
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
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(
|
|
350
519
|
toolName,
|
|
351
520
|
toolArgs,
|
|
521
|
+
toolId,
|
|
352
522
|
);
|
|
353
523
|
|
|
524
|
+
// If PreToolUse hooks blocked execution, skip tool execution
|
|
525
|
+
if (!shouldExecuteTool) {
|
|
526
|
+
this.logger?.info(
|
|
527
|
+
`Tool ${toolName} execution blocked by PreToolUse hooks`,
|
|
528
|
+
);
|
|
529
|
+
return; // Skip this tool and return from this map function
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
// Create tool execution context
|
|
533
|
+
const context: ToolContext = {
|
|
534
|
+
abortSignal: toolAbortController.signal,
|
|
535
|
+
backgroundBashManager: this.backgroundBashManager,
|
|
536
|
+
workdir: this.workdir,
|
|
537
|
+
};
|
|
538
|
+
|
|
539
|
+
// Execute tool
|
|
540
|
+
const toolResult = await this.toolManager.execute(
|
|
541
|
+
functionToolCall.function?.name || "",
|
|
542
|
+
toolArgs,
|
|
543
|
+
context,
|
|
544
|
+
);
|
|
545
|
+
|
|
546
|
+
// Update message state - tool execution completed
|
|
354
547
|
this.messageManager.updateToolBlock({
|
|
355
548
|
id: toolId,
|
|
356
|
-
parameters:
|
|
357
|
-
|
|
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",
|
|
358
556
|
name: toolName,
|
|
359
|
-
|
|
557
|
+
shortResult: toolResult.shortResult,
|
|
360
558
|
});
|
|
361
559
|
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
if (!shouldExecuteTool) {
|
|
372
|
-
this.logger?.info(
|
|
373
|
-
`Tool ${toolName} execution blocked by PreToolUse hooks`,
|
|
374
|
-
);
|
|
375
|
-
return; // Skip this tool and return from this map function
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
// Create tool execution context
|
|
379
|
-
const context: ToolContext = {
|
|
380
|
-
abortSignal: toolAbortController.signal,
|
|
381
|
-
backgroundBashManager: this.backgroundBashManager,
|
|
382
|
-
workdir: this.workdir,
|
|
383
|
-
};
|
|
384
|
-
|
|
385
|
-
// Execute tool
|
|
386
|
-
const toolResult = await this.toolManager.execute(
|
|
387
|
-
functionToolCall.function?.name || "",
|
|
388
|
-
toolArgs,
|
|
389
|
-
context,
|
|
390
|
-
);
|
|
391
|
-
|
|
392
|
-
// Update message state - tool execution completed
|
|
393
|
-
this.messageManager.updateToolBlock({
|
|
394
|
-
id: toolId,
|
|
395
|
-
parameters: JSON.stringify(toolArgs, null, 2),
|
|
396
|
-
result:
|
|
397
|
-
toolResult.content ||
|
|
398
|
-
(toolResult.error ? `Error: ${toolResult.error}` : ""),
|
|
399
|
-
success: toolResult.success,
|
|
400
|
-
error: toolResult.error,
|
|
401
|
-
isRunning: false, // isRunning: false
|
|
402
|
-
name: toolName,
|
|
403
|
-
shortResult: toolResult.shortResult,
|
|
404
|
-
compactParams,
|
|
405
|
-
});
|
|
406
|
-
|
|
407
|
-
// If tool returns diff information, add diff block
|
|
408
|
-
if (
|
|
409
|
-
toolResult.success &&
|
|
410
|
-
toolResult.diffResult &&
|
|
411
|
-
toolResult.filePath
|
|
412
|
-
) {
|
|
413
|
-
this.messageManager.addDiffBlock(
|
|
414
|
-
toolResult.filePath,
|
|
415
|
-
toolResult.diffResult,
|
|
416
|
-
);
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
// Execute PostToolUse hooks after successful tool completion
|
|
420
|
-
await this.executePostToolUseHooks(
|
|
421
|
-
toolId,
|
|
422
|
-
toolName,
|
|
423
|
-
toolArgs,
|
|
424
|
-
toolResult,
|
|
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,
|
|
425
569
|
);
|
|
426
|
-
} catch (toolError) {
|
|
427
|
-
const errorMessage =
|
|
428
|
-
toolError instanceof Error
|
|
429
|
-
? toolError.message
|
|
430
|
-
: String(toolError);
|
|
431
|
-
|
|
432
|
-
this.messageManager.updateToolBlock({
|
|
433
|
-
id: toolId,
|
|
434
|
-
parameters: JSON.stringify(toolArgs, null, 2),
|
|
435
|
-
result: `Tool execution failed: ${errorMessage}`,
|
|
436
|
-
success: false,
|
|
437
|
-
error: errorMessage,
|
|
438
|
-
isRunning: false,
|
|
439
|
-
name: toolName,
|
|
440
|
-
compactParams,
|
|
441
|
-
});
|
|
442
570
|
}
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
571
|
+
|
|
572
|
+
// Execute PostToolUse hooks after successful tool completion
|
|
573
|
+
await this.executePostToolUseHooks(
|
|
574
|
+
toolId,
|
|
575
|
+
toolName,
|
|
576
|
+
toolArgs,
|
|
577
|
+
toolResult,
|
|
450
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
|
+
});
|
|
451
595
|
}
|
|
452
596
|
},
|
|
453
597
|
);
|
|
@@ -479,70 +623,74 @@ export class AIManager {
|
|
|
479
623
|
error instanceof Error ? error.message : "Unknown error occurred",
|
|
480
624
|
);
|
|
481
625
|
} finally {
|
|
482
|
-
// Only execute
|
|
626
|
+
// Only execute cleanup and hooks for the initial call
|
|
483
627
|
if (recursionDepth === 0) {
|
|
484
|
-
//
|
|
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
|
|
485
638
|
const isCurrentlyAborted =
|
|
486
639
|
abortController.signal.aborted || toolAbortController.signal.aborted;
|
|
487
640
|
|
|
488
641
|
if (!isCurrentlyAborted) {
|
|
489
642
|
const shouldContinue = await this.executeStopHooks();
|
|
490
643
|
|
|
491
|
-
// If Stop hooks indicate we should continue (due to blocking errors),
|
|
644
|
+
// If Stop/SubagentStop hooks indicate we should continue (due to blocking errors),
|
|
492
645
|
// restart the AI conversation cycle
|
|
493
646
|
if (shouldContinue) {
|
|
494
647
|
this.logger?.info(
|
|
495
|
-
"Stop hooks indicate issues need fixing, continuing conversation
|
|
648
|
+
`${this.subagentType ? "SubagentStop" : "Stop"} hooks indicate issues need fixing, continuing conversation...`,
|
|
496
649
|
);
|
|
497
650
|
|
|
498
651
|
// Restart the conversation to let AI fix the issues
|
|
499
|
-
// Use recursionDepth =
|
|
652
|
+
// Use recursionDepth = 0 to set loading false again for continuation
|
|
500
653
|
await this.sendAIMessage({
|
|
501
|
-
recursionDepth:
|
|
654
|
+
recursionDepth: 0,
|
|
502
655
|
model,
|
|
503
656
|
allowedTools,
|
|
504
657
|
});
|
|
505
658
|
}
|
|
506
659
|
}
|
|
507
|
-
|
|
508
|
-
// Save session after all operations (including continuation) are complete
|
|
509
|
-
await this.messageManager.saveSession();
|
|
510
|
-
|
|
511
|
-
// Clear abort controllers and loading state after all operations are complete
|
|
512
|
-
this.abortController = null;
|
|
513
|
-
this.toolAbortController = null;
|
|
514
|
-
|
|
515
|
-
// Set loading to false at the very end, after all operations including continuation
|
|
516
|
-
this.setIsLoading(false);
|
|
517
660
|
}
|
|
518
661
|
}
|
|
519
662
|
}
|
|
520
663
|
|
|
521
664
|
/**
|
|
522
|
-
* 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"
|
|
523
667
|
* @returns Promise<boolean> - true if should continue conversation, false if should stop
|
|
524
668
|
*/
|
|
525
669
|
private async executeStopHooks(): Promise<boolean> {
|
|
526
670
|
if (!this.hookManager) return false;
|
|
527
671
|
|
|
528
672
|
try {
|
|
673
|
+
// Use "SubagentStop" hook name when triggered by a subagent, otherwise use "Stop"
|
|
674
|
+
const hookName = this.subagentType ? "SubagentStop" : "Stop";
|
|
675
|
+
|
|
529
676
|
const context: ExtendedHookExecutionContext = {
|
|
530
|
-
event:
|
|
677
|
+
event: hookName,
|
|
531
678
|
projectDir: this.workdir,
|
|
532
679
|
timestamp: new Date(),
|
|
533
680
|
sessionId: this.messageManager.getSessionId(),
|
|
534
681
|
transcriptPath: this.messageManager.getTranscriptPath(),
|
|
535
682
|
cwd: this.workdir,
|
|
683
|
+
subagentType: this.subagentType, // Include subagent type in hook context
|
|
536
684
|
// Stop hooks don't need toolName, toolInput, toolResponse, or userPrompt
|
|
537
685
|
};
|
|
538
686
|
|
|
539
|
-
const results = await this.hookManager.executeHooks(
|
|
687
|
+
const results = await this.hookManager.executeHooks(hookName, context);
|
|
540
688
|
|
|
541
689
|
// Process hook results to handle exit codes and appropriate responses
|
|
542
690
|
let shouldContinue = false;
|
|
543
691
|
if (results.length > 0) {
|
|
544
692
|
const processResult = this.hookManager.processHookResults(
|
|
545
|
-
|
|
693
|
+
hookName,
|
|
546
694
|
results,
|
|
547
695
|
this.messageManager,
|
|
548
696
|
);
|
|
@@ -550,7 +698,7 @@ export class AIManager {
|
|
|
550
698
|
// If hook processing indicates we should block (exit code 2), continue conversation
|
|
551
699
|
if (processResult.shouldBlock) {
|
|
552
700
|
this.logger?.info(
|
|
553
|
-
|
|
701
|
+
`${hookName} hook blocked stopping with error:`,
|
|
554
702
|
processResult.errorMessage,
|
|
555
703
|
);
|
|
556
704
|
shouldContinue = true;
|
|
@@ -560,7 +708,7 @@ export class AIManager {
|
|
|
560
708
|
// Log hook execution results for debugging
|
|
561
709
|
if (results.length > 0) {
|
|
562
710
|
this.logger?.debug(
|
|
563
|
-
`Executed ${results.length}
|
|
711
|
+
`Executed ${results.length} ${hookName} hook(s):`,
|
|
564
712
|
results.map((r) => ({
|
|
565
713
|
success: r.success,
|
|
566
714
|
duration: r.duration,
|
|
@@ -574,7 +722,10 @@ export class AIManager {
|
|
|
574
722
|
return shouldContinue;
|
|
575
723
|
} catch (error) {
|
|
576
724
|
// Hook execution errors should not interrupt the main workflow
|
|
577
|
-
this.logger?.error(
|
|
725
|
+
this.logger?.error(
|
|
726
|
+
`${this.subagentType ? "SubagentStop" : "Stop"} hook execution failed:`,
|
|
727
|
+
error,
|
|
728
|
+
);
|
|
578
729
|
return false;
|
|
579
730
|
}
|
|
580
731
|
}
|
|
@@ -600,6 +751,7 @@ export class AIManager {
|
|
|
600
751
|
transcriptPath: this.messageManager.getTranscriptPath(),
|
|
601
752
|
cwd: this.workdir,
|
|
602
753
|
toolInput,
|
|
754
|
+
subagentType: this.subagentType, // Include subagent type in hook context
|
|
603
755
|
};
|
|
604
756
|
|
|
605
757
|
const results = await this.hookManager.executeHooks(
|
|
@@ -664,6 +816,7 @@ export class AIManager {
|
|
|
664
816
|
cwd: this.workdir,
|
|
665
817
|
toolInput,
|
|
666
818
|
toolResponse,
|
|
819
|
+
subagentType: this.subagentType, // Include subagent type in hook context
|
|
667
820
|
};
|
|
668
821
|
|
|
669
822
|
const results = await this.hookManager.executeHooks(
|