wave-agent-sdk 0.0.4 → 0.0.5
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 +61 -4
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +82 -6
- package/dist/hooks/manager.d.ts.map +1 -1
- package/dist/hooks/manager.js +1 -1
- package/dist/managers/aiManager.d.ts +2 -1
- package/dist/managers/aiManager.d.ts.map +1 -1
- package/dist/managers/aiManager.js +37 -6
- package/dist/managers/mcpManager.js +5 -5
- package/dist/managers/messageManager.d.ts +13 -2
- package/dist/managers/messageManager.d.ts.map +1 -1
- package/dist/managers/messageManager.js +20 -7
- package/dist/managers/skillManager.js +3 -3
- package/dist/managers/slashCommandManager.js +1 -1
- package/dist/managers/subagentManager.d.ts +3 -1
- package/dist/managers/subagentManager.d.ts.map +1 -1
- package/dist/managers/subagentManager.js +5 -1
- package/dist/services/aiService.d.ts +9 -1
- package/dist/services/aiService.d.ts.map +1 -1
- package/dist/services/aiService.js +17 -3
- package/dist/services/memory.js +3 -3
- package/dist/services/session.d.ts +64 -15
- package/dist/services/session.d.ts.map +1 -1
- package/dist/services/session.js +80 -30
- package/dist/tools/bashTool.js +2 -2
- package/dist/tools/deleteFileTool.js +1 -1
- package/dist/tools/editTool.js +1 -1
- package/dist/tools/multiEditTool.js +2 -2
- package/dist/tools/writeTool.js +1 -1
- package/dist/types.d.ts +12 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/messageOperations.d.ts +2 -2
- package/dist/utils/messageOperations.d.ts.map +1 -1
- package/dist/utils/messageOperations.js +2 -1
- package/package.json +1 -1
- package/src/agent.ts +101 -7
- package/src/hooks/manager.ts +4 -2
- package/src/managers/aiManager.ts +43 -7
- package/src/managers/mcpManager.ts +5 -5
- package/src/managers/messageManager.ts +34 -5
- package/src/managers/skillManager.ts +3 -3
- package/src/managers/slashCommandManager.ts +1 -1
- package/src/managers/subagentManager.ts +14 -2
- package/src/services/aiService.ts +28 -5
- package/src/services/memory.ts +3 -3
- package/src/services/session.ts +93 -26
- package/src/tools/bashTool.ts +2 -2
- package/src/tools/deleteFileTool.ts +1 -1
- package/src/tools/editTool.ts +1 -1
- package/src/tools/multiEditTool.ts +2 -2
- package/src/tools/writeTool.ts +1 -1
- package/src/types.ts +13 -0
- package/src/utils/messageOperations.ts +3 -1
package/src/agent.ts
CHANGED
|
@@ -20,6 +20,7 @@ import type {
|
|
|
20
20
|
McpServerStatus,
|
|
21
21
|
GatewayConfig,
|
|
22
22
|
ModelConfig,
|
|
23
|
+
Usage,
|
|
23
24
|
} from "./types.js";
|
|
24
25
|
import { HookManager } from "./hooks/index.js";
|
|
25
26
|
import { configResolver } from "./utils/configResolver.js";
|
|
@@ -51,6 +52,14 @@ export interface AgentOptions {
|
|
|
51
52
|
workdir?: string;
|
|
52
53
|
/**Optional custom system prompt - if provided, replaces default system prompt */
|
|
53
54
|
systemPrompt?: string;
|
|
55
|
+
|
|
56
|
+
// New: Session directory configuration
|
|
57
|
+
/**
|
|
58
|
+
* Optional custom directory for session file storage
|
|
59
|
+
* @default join(homedir(), ".wave", "sessions")
|
|
60
|
+
* @example "/path/to/custom/sessions"
|
|
61
|
+
*/
|
|
62
|
+
sessionDir?: string;
|
|
54
63
|
}
|
|
55
64
|
|
|
56
65
|
export interface AgentCallbacks
|
|
@@ -72,6 +81,7 @@ export class Agent {
|
|
|
72
81
|
private hookManager: HookManager; // Add hooks manager instance
|
|
73
82
|
private workdir: string; // Working directory
|
|
74
83
|
private systemPrompt?: string; // Custom system prompt
|
|
84
|
+
private _usages: Usage[] = []; // Usage tracking array
|
|
75
85
|
|
|
76
86
|
// Configuration properties
|
|
77
87
|
private gatewayConfig: GatewayConfig;
|
|
@@ -81,13 +91,21 @@ export class Agent {
|
|
|
81
91
|
/**
|
|
82
92
|
* Agent constructor - handles configuration resolution and validation
|
|
83
93
|
*
|
|
84
|
-
* IMPORTANT:
|
|
85
|
-
*
|
|
94
|
+
* IMPORTANT: This constructor is private. Use Agent.create() instead for proper
|
|
95
|
+
* async initialization. Keep this constructor's signature exactly the same as
|
|
96
|
+
* Agent.create() to maintain API consistency.
|
|
86
97
|
*
|
|
87
98
|
* @param options - Configuration options for the Agent instance
|
|
99
|
+
* @param options.sessionDir - Optional custom directory for session storage
|
|
88
100
|
*/
|
|
89
|
-
constructor(options: AgentOptions) {
|
|
90
|
-
const {
|
|
101
|
+
private constructor(options: AgentOptions) {
|
|
102
|
+
const {
|
|
103
|
+
callbacks = {},
|
|
104
|
+
logger,
|
|
105
|
+
workdir,
|
|
106
|
+
systemPrompt,
|
|
107
|
+
sessionDir,
|
|
108
|
+
} = options;
|
|
91
109
|
|
|
92
110
|
// Resolve configuration from constructor args and environment variables
|
|
93
111
|
const gatewayConfig = configResolver.resolveGatewayConfig(
|
|
@@ -139,6 +157,7 @@ export class Agent {
|
|
|
139
157
|
callbacks,
|
|
140
158
|
workdir: this.workdir,
|
|
141
159
|
logger: this.logger,
|
|
160
|
+
sessionDir,
|
|
142
161
|
});
|
|
143
162
|
|
|
144
163
|
// Initialize subagent manager with all dependencies in constructor
|
|
@@ -151,6 +170,7 @@ export class Agent {
|
|
|
151
170
|
gatewayConfig,
|
|
152
171
|
modelConfig,
|
|
153
172
|
tokenLimit,
|
|
173
|
+
onUsageAdded: (usage) => this.addUsage(usage),
|
|
154
174
|
});
|
|
155
175
|
|
|
156
176
|
// Initialize AI manager with resolved configuration
|
|
@@ -160,7 +180,12 @@ export class Agent {
|
|
|
160
180
|
logger: this.logger,
|
|
161
181
|
backgroundBashManager: this.backgroundBashManager,
|
|
162
182
|
hookManager: this.hookManager,
|
|
163
|
-
callbacks
|
|
183
|
+
callbacks: {
|
|
184
|
+
...callbacks,
|
|
185
|
+
onUsageAdded: (usage: Usage) => {
|
|
186
|
+
this.addUsage(usage);
|
|
187
|
+
},
|
|
188
|
+
},
|
|
164
189
|
workdir: this.workdir,
|
|
165
190
|
systemPrompt: this.systemPrompt,
|
|
166
191
|
gatewayConfig: this.gatewayConfig,
|
|
@@ -192,6 +217,34 @@ export class Agent {
|
|
|
192
217
|
return this.messageManager.getMessages();
|
|
193
218
|
}
|
|
194
219
|
|
|
220
|
+
public get usages(): Usage[] {
|
|
221
|
+
return [...this._usages]; // Return copy to prevent external modification
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Rebuild usage array from messages containing usage metadata
|
|
226
|
+
* Called during session restoration to reconstruct usage tracking
|
|
227
|
+
*/
|
|
228
|
+
private rebuildUsageFromMessages(): void {
|
|
229
|
+
this._usages = [];
|
|
230
|
+
this.messages.forEach((message) => {
|
|
231
|
+
if (message.role === "assistant" && message.usage) {
|
|
232
|
+
this._usages.push(message.usage);
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
// Trigger callback after rebuilding usage array
|
|
236
|
+
this.messageManager.triggerUsageChange();
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Add usage data to the tracking array and trigger callbacks
|
|
241
|
+
* @param usage Usage data from AI operations
|
|
242
|
+
*/
|
|
243
|
+
private addUsage(usage: Usage): void {
|
|
244
|
+
this._usages.push(usage);
|
|
245
|
+
this.messageManager.triggerUsageChange();
|
|
246
|
+
}
|
|
247
|
+
|
|
195
248
|
public get latestTotalTokens(): number {
|
|
196
249
|
return this.messageManager.getlatestTotalTokens();
|
|
197
250
|
}
|
|
@@ -242,6 +295,43 @@ export class Agent {
|
|
|
242
295
|
* @param options - Same AgentOptions interface used by constructor
|
|
243
296
|
* @returns Promise<Agent> - Fully initialized Agent instance
|
|
244
297
|
*/
|
|
298
|
+
/**
|
|
299
|
+
* Create a new Agent instance with async initialization
|
|
300
|
+
*
|
|
301
|
+
* This is the recommended way to create Agent instances. The constructor is private
|
|
302
|
+
* to ensure proper async initialization of all components.
|
|
303
|
+
*
|
|
304
|
+
* @param options - Configuration options for the Agent instance
|
|
305
|
+
* @param options.apiKey - API key for the AI service (or set WAVE_API_KEY env var)
|
|
306
|
+
* @param options.baseURL - Base URL for the AI service (or set WAVE_BASE_URL env var)
|
|
307
|
+
* @param options.sessionDir - Optional custom directory for session file storage.
|
|
308
|
+
* If not provided, defaults to ~/.wave/sessions/. Can be relative or absolute path.
|
|
309
|
+
* Examples: "./app-sessions", "/var/myapp/sessions", "~/Documents/sessions"
|
|
310
|
+
* @param options.callbacks - Optional callbacks for various Agent events
|
|
311
|
+
* @param options.restoreSessionId - Optional session ID to restore from
|
|
312
|
+
* @param options.continueLastSession - Whether to continue the last session automatically
|
|
313
|
+
* @param options.logger - Optional custom logger implementation
|
|
314
|
+
* @param options.messages - Optional initial messages for testing convenience
|
|
315
|
+
* @param options.workdir - Working directory (defaults to process.cwd())
|
|
316
|
+
* @param options.systemPrompt - Optional custom system prompt
|
|
317
|
+
* @returns Promise that resolves to initialized Agent instance
|
|
318
|
+
*
|
|
319
|
+
* @example
|
|
320
|
+
* ```typescript
|
|
321
|
+
* // Basic usage with default session directory
|
|
322
|
+
* const agent = await Agent.create({
|
|
323
|
+
* apiKey: 'your-api-key',
|
|
324
|
+
* baseURL: 'https://api.example.com'
|
|
325
|
+
* });
|
|
326
|
+
*
|
|
327
|
+
* // Custom session directory
|
|
328
|
+
* const agent = await Agent.create({
|
|
329
|
+
* apiKey: 'your-api-key',
|
|
330
|
+
* baseURL: 'https://api.example.com',
|
|
331
|
+
* sessionDir: './app-sessions'
|
|
332
|
+
* });
|
|
333
|
+
* ```
|
|
334
|
+
*/
|
|
245
335
|
static async create(options: AgentOptions): Promise<Agent> {
|
|
246
336
|
// Create Agent instance - configuration resolution and validation now happens in constructor
|
|
247
337
|
const instance = new Agent(options);
|
|
@@ -289,7 +379,7 @@ export class Agent {
|
|
|
289
379
|
// Initialize hooks configuration
|
|
290
380
|
try {
|
|
291
381
|
// Load hooks configuration from user and project settings
|
|
292
|
-
this.logger?.
|
|
382
|
+
this.logger?.debug("Loading hooks configuration...");
|
|
293
383
|
this.hookManager.loadConfigurationFromSettings();
|
|
294
384
|
this.logger?.debug("Hooks system initialized successfully");
|
|
295
385
|
} catch (error) {
|
|
@@ -301,12 +391,16 @@ export class Agent {
|
|
|
301
391
|
if (options?.messages) {
|
|
302
392
|
// If messages are provided, use them directly (useful for testing)
|
|
303
393
|
this.messageManager.setMessages(options.messages);
|
|
394
|
+
// Rebuild usage array from restored messages
|
|
395
|
+
this.rebuildUsageFromMessages();
|
|
304
396
|
} else {
|
|
305
397
|
// Otherwise, handle session restoration
|
|
306
398
|
await this.messageManager.handleSessionRestoration(
|
|
307
399
|
options?.restoreSessionId,
|
|
308
400
|
options?.continueLastSession,
|
|
309
401
|
);
|
|
402
|
+
// Rebuild usage array from restored messages
|
|
403
|
+
this.rebuildUsageFromMessages();
|
|
310
404
|
}
|
|
311
405
|
}
|
|
312
406
|
|
|
@@ -361,7 +455,7 @@ export class Agent {
|
|
|
361
455
|
|
|
362
456
|
/** Destroy managers, clean up resources */
|
|
363
457
|
public async destroy(): Promise<void> {
|
|
364
|
-
this.messageManager.saveSession();
|
|
458
|
+
await this.messageManager.saveSession();
|
|
365
459
|
this.abortAIMessage();
|
|
366
460
|
this.abortBashCommand();
|
|
367
461
|
this.abortSlashCommand();
|
package/src/hooks/manager.ts
CHANGED
|
@@ -174,7 +174,9 @@ export class HookManager implements IHookManager {
|
|
|
174
174
|
|
|
175
175
|
const eventConfigs = this.configuration[event];
|
|
176
176
|
if (!eventConfigs || eventConfigs.length === 0) {
|
|
177
|
-
this.logger?.debug(
|
|
177
|
+
this.logger?.debug(
|
|
178
|
+
`[HookManager] No hooks configured for ${event} event`,
|
|
179
|
+
);
|
|
178
180
|
return [];
|
|
179
181
|
}
|
|
180
182
|
|
|
@@ -261,7 +263,7 @@ export class HookManager implements IHookManager {
|
|
|
261
263
|
results,
|
|
262
264
|
totalDuration,
|
|
263
265
|
);
|
|
264
|
-
this.logger?.
|
|
266
|
+
this.logger?.debug(`[HookManager] ${event} execution summary: ${summary}`);
|
|
265
267
|
|
|
266
268
|
return results;
|
|
267
269
|
}
|
|
@@ -2,7 +2,7 @@ import { callAgent, compressMessages } from "../services/aiService.js";
|
|
|
2
2
|
import { getMessagesToCompress } from "../utils/messageOperations.js";
|
|
3
3
|
import { convertMessagesForAPI } from "../utils/convertMessagesForAPI.js";
|
|
4
4
|
import * as memory from "../services/memory.js";
|
|
5
|
-
import type { Logger, GatewayConfig, ModelConfig } from "../types.js";
|
|
5
|
+
import type { Logger, GatewayConfig, ModelConfig, Usage } from "../types.js";
|
|
6
6
|
import type { ToolManager } from "./toolManager.js";
|
|
7
7
|
import type { ToolContext, ToolResult } from "../tools/types.js";
|
|
8
8
|
import type { MessageManager } from "./messageManager.js";
|
|
@@ -13,6 +13,7 @@ import type { ExtendedHookExecutionContext } from "../hooks/types.js";
|
|
|
13
13
|
|
|
14
14
|
export interface AIManagerCallbacks {
|
|
15
15
|
onCompressionStateChange?: (isCompressing: boolean) => void;
|
|
16
|
+
onUsageAdded?: (usage: Usage) => void;
|
|
16
17
|
}
|
|
17
18
|
|
|
18
19
|
export interface AIManagerOptions {
|
|
@@ -140,7 +141,7 @@ export class AIManager {
|
|
|
140
141
|
|
|
141
142
|
// Check if token limit exceeded - use injected configuration
|
|
142
143
|
if (usage.total_tokens > this.tokenLimit) {
|
|
143
|
-
this.logger?.
|
|
144
|
+
this.logger?.debug(
|
|
144
145
|
`Token usage exceeded ${this.tokenLimit}, compressing messages...`,
|
|
145
146
|
);
|
|
146
147
|
|
|
@@ -156,7 +157,7 @@ export class AIManager {
|
|
|
156
157
|
|
|
157
158
|
this.setIsCompressing(true);
|
|
158
159
|
try {
|
|
159
|
-
const
|
|
160
|
+
const compressionResult = await compressMessages({
|
|
160
161
|
gatewayConfig: this.gatewayConfig,
|
|
161
162
|
modelConfig: this.modelConfig,
|
|
162
163
|
messages: recentChatMessages,
|
|
@@ -166,10 +167,26 @@ export class AIManager {
|
|
|
166
167
|
// Execute message reconstruction and sessionId update after compression
|
|
167
168
|
this.messageManager.compressMessagesAndUpdateSession(
|
|
168
169
|
insertIndex,
|
|
169
|
-
|
|
170
|
+
compressionResult.content,
|
|
170
171
|
);
|
|
171
172
|
|
|
172
|
-
|
|
173
|
+
// Handle usage tracking for compression operations
|
|
174
|
+
if (compressionResult.usage) {
|
|
175
|
+
const usage: Usage = {
|
|
176
|
+
prompt_tokens: compressionResult.usage.prompt_tokens,
|
|
177
|
+
completion_tokens: compressionResult.usage.completion_tokens,
|
|
178
|
+
total_tokens: compressionResult.usage.total_tokens,
|
|
179
|
+
model: this.modelConfig.fastModel,
|
|
180
|
+
operation_type: "compress",
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
// Notify Agent to add to usage tracking
|
|
184
|
+
if (this.callbacks?.onUsageAdded) {
|
|
185
|
+
this.callbacks.onUsageAdded(usage);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
this.logger?.debug(
|
|
173
190
|
`Successfully compressed ${messagesToCompress.length} messages and updated session`,
|
|
174
191
|
);
|
|
175
192
|
} catch (compressError) {
|
|
@@ -255,8 +272,27 @@ export class AIManager {
|
|
|
255
272
|
}
|
|
256
273
|
}
|
|
257
274
|
|
|
258
|
-
//
|
|
259
|
-
|
|
275
|
+
// Handle usage tracking for agent operations
|
|
276
|
+
let usage: Usage | undefined;
|
|
277
|
+
if (result.usage) {
|
|
278
|
+
usage = {
|
|
279
|
+
prompt_tokens: result.usage.prompt_tokens,
|
|
280
|
+
completion_tokens: result.usage.completion_tokens,
|
|
281
|
+
total_tokens: result.usage.total_tokens,
|
|
282
|
+
model: model || this.modelConfig.agentModel,
|
|
283
|
+
operation_type: "agent",
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// Add assistant message at once (including content, tool calls, and usage)
|
|
288
|
+
this.messageManager.addAssistantMessage(content, toolCalls, usage);
|
|
289
|
+
|
|
290
|
+
// Notify Agent to add to usage tracking
|
|
291
|
+
if (usage) {
|
|
292
|
+
if (this.callbacks?.onUsageAdded) {
|
|
293
|
+
this.callbacks.onUsageAdded(usage);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
260
296
|
|
|
261
297
|
if (toolCalls.length > 0) {
|
|
262
298
|
for (const functionToolCall of toolCalls) {
|
|
@@ -53,7 +53,7 @@ export class McpManager {
|
|
|
53
53
|
this.workdir = workdir;
|
|
54
54
|
|
|
55
55
|
if (autoConnect) {
|
|
56
|
-
this.logger?.
|
|
56
|
+
this.logger?.debug("Initializing MCP servers...");
|
|
57
57
|
|
|
58
58
|
// Ensure MCP configuration is loaded
|
|
59
59
|
const config = await this.ensureConfigLoaded();
|
|
@@ -63,10 +63,10 @@ export class McpManager {
|
|
|
63
63
|
const connectionPromises = Object.keys(config.mcpServers).map(
|
|
64
64
|
async (serverName) => {
|
|
65
65
|
try {
|
|
66
|
-
this.logger?.
|
|
66
|
+
this.logger?.debug(`Connecting to MCP server: ${serverName}`);
|
|
67
67
|
const success = await this.connectServer(serverName);
|
|
68
68
|
if (success) {
|
|
69
|
-
this.logger?.
|
|
69
|
+
this.logger?.debug(
|
|
70
70
|
`Successfully connected to MCP server: ${serverName}`,
|
|
71
71
|
);
|
|
72
72
|
} else {
|
|
@@ -86,7 +86,7 @@ export class McpManager {
|
|
|
86
86
|
await Promise.all(connectionPromises);
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
-
this.logger?.
|
|
89
|
+
this.logger?.debug("MCP servers initialization completed");
|
|
90
90
|
// Trigger state change callback after initialization
|
|
91
91
|
this.callbacks.onServersChange?.(this.getAllServers());
|
|
92
92
|
}
|
|
@@ -255,7 +255,7 @@ export class McpManager {
|
|
|
255
255
|
};
|
|
256
256
|
|
|
257
257
|
transport.onclose = () => {
|
|
258
|
-
this.logger?.
|
|
258
|
+
this.logger?.debug(`MCP Server ${name} transport closed`);
|
|
259
259
|
this.connections.delete(name);
|
|
260
260
|
this.updateServerStatus(name, {
|
|
261
261
|
status: "disconnected",
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
type UpdateSubagentBlockParams,
|
|
17
17
|
type AgentToolBlockUpdateParams,
|
|
18
18
|
} from "../utils/messageOperations.js";
|
|
19
|
-
import type { Logger, Message } from "../types.js";
|
|
19
|
+
import type { Logger, Message, Usage } from "../types.js";
|
|
20
20
|
import {
|
|
21
21
|
cleanupExpiredSessions,
|
|
22
22
|
getLatestSession,
|
|
@@ -32,6 +32,7 @@ export interface MessageManagerCallbacks {
|
|
|
32
32
|
onSessionIdChange?: (sessionId: string) => void;
|
|
33
33
|
onLatestTotalTokensChange?: (latestTotalTokens: number) => void;
|
|
34
34
|
onUserInputHistoryChange?: (history: string[]) => void;
|
|
35
|
+
onUsagesChange?: (usages: Usage[]) => void;
|
|
35
36
|
// Incremental callback
|
|
36
37
|
onUserMessageAdded?: (
|
|
37
38
|
content: string,
|
|
@@ -71,6 +72,13 @@ export interface MessageManagerOptions {
|
|
|
71
72
|
callbacks: MessageManagerCallbacks;
|
|
72
73
|
workdir: string;
|
|
73
74
|
logger?: Logger;
|
|
75
|
+
|
|
76
|
+
// New: Optional session directory override
|
|
77
|
+
/**
|
|
78
|
+
* Custom session directory path
|
|
79
|
+
* @default join(homedir(), ".wave", "sessions")
|
|
80
|
+
*/
|
|
81
|
+
sessionDir?: string;
|
|
74
82
|
}
|
|
75
83
|
|
|
76
84
|
export class MessageManager {
|
|
@@ -83,6 +91,7 @@ export class MessageManager {
|
|
|
83
91
|
private workdir: string;
|
|
84
92
|
private logger?: Logger; // Add optional logger property
|
|
85
93
|
private callbacks: MessageManagerCallbacks;
|
|
94
|
+
private sessionDir?: string; // Add session directory property
|
|
86
95
|
|
|
87
96
|
constructor(options: MessageManagerOptions) {
|
|
88
97
|
this.sessionId = randomUUID();
|
|
@@ -93,6 +102,7 @@ export class MessageManager {
|
|
|
93
102
|
this.workdir = options.workdir;
|
|
94
103
|
this.callbacks = options.callbacks;
|
|
95
104
|
this.logger = options.logger;
|
|
105
|
+
this.sessionDir = options.sessionDir;
|
|
96
106
|
}
|
|
97
107
|
|
|
98
108
|
// Getter methods
|
|
@@ -113,7 +123,7 @@ export class MessageManager {
|
|
|
113
123
|
}
|
|
114
124
|
|
|
115
125
|
public getTranscriptPath(): string {
|
|
116
|
-
return getSessionFilePath(this.sessionId);
|
|
126
|
+
return getSessionFilePath(this.sessionId, this.sessionDir);
|
|
117
127
|
}
|
|
118
128
|
|
|
119
129
|
// Setter methods, will trigger callbacks
|
|
@@ -140,6 +150,7 @@ export class MessageManager {
|
|
|
140
150
|
this.workdir,
|
|
141
151
|
this.latestTotalTokens,
|
|
142
152
|
this.sessionStartTime,
|
|
153
|
+
this.sessionDir,
|
|
143
154
|
);
|
|
144
155
|
} catch (error) {
|
|
145
156
|
this.logger?.error("Failed to save session:", error);
|
|
@@ -155,7 +166,7 @@ export class MessageManager {
|
|
|
155
166
|
): Promise<void> {
|
|
156
167
|
// Clean up expired sessions first
|
|
157
168
|
try {
|
|
158
|
-
await cleanupExpiredSessions(this.workdir);
|
|
169
|
+
await cleanupExpiredSessions(this.workdir, this.sessionDir);
|
|
159
170
|
} catch (error) {
|
|
160
171
|
console.warn("Failed to cleanup expired sessions:", error);
|
|
161
172
|
}
|
|
@@ -168,13 +179,16 @@ export class MessageManager {
|
|
|
168
179
|
let sessionToRestore: SessionData | null = null;
|
|
169
180
|
|
|
170
181
|
if (restoreSessionId) {
|
|
171
|
-
sessionToRestore = await loadSession(restoreSessionId);
|
|
182
|
+
sessionToRestore = await loadSession(restoreSessionId, this.sessionDir);
|
|
172
183
|
if (!sessionToRestore) {
|
|
173
184
|
console.error(`Session not found: ${restoreSessionId}`);
|
|
174
185
|
process.exit(1);
|
|
175
186
|
}
|
|
176
187
|
} else if (continueLastSession) {
|
|
177
|
-
sessionToRestore = await getLatestSession(
|
|
188
|
+
sessionToRestore = await getLatestSession(
|
|
189
|
+
this.workdir,
|
|
190
|
+
this.sessionDir,
|
|
191
|
+
);
|
|
178
192
|
if (!sessionToRestore) {
|
|
179
193
|
console.error(
|
|
180
194
|
`No previous session found for workdir: ${this.workdir}`,
|
|
@@ -290,11 +304,13 @@ export class MessageManager {
|
|
|
290
304
|
public addAssistantMessage(
|
|
291
305
|
content?: string,
|
|
292
306
|
toolCalls?: ChatCompletionMessageFunctionToolCall[],
|
|
307
|
+
usage?: Usage,
|
|
293
308
|
): void {
|
|
294
309
|
const newMessages = addAssistantMessageToMessages(
|
|
295
310
|
this.messages,
|
|
296
311
|
content,
|
|
297
312
|
toolCalls,
|
|
313
|
+
usage,
|
|
298
314
|
);
|
|
299
315
|
this.setMessages(newMessages);
|
|
300
316
|
this.callbacks.onAssistantMessageAdded?.(content, toolCalls);
|
|
@@ -466,4 +482,17 @@ export class MessageManager {
|
|
|
466
482
|
};
|
|
467
483
|
this.callbacks.onSubAgentBlockUpdated?.(params.subagentId, params.messages);
|
|
468
484
|
}
|
|
485
|
+
|
|
486
|
+
/**
|
|
487
|
+
* Trigger usage change callback with all usage data from assistant messages
|
|
488
|
+
*/
|
|
489
|
+
public triggerUsageChange(): void {
|
|
490
|
+
const usages: Usage[] = [];
|
|
491
|
+
for (const message of this.messages) {
|
|
492
|
+
if (message.role === "assistant" && message.usage) {
|
|
493
|
+
usages.push(message.usage);
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
this.callbacks.onUsagesChange?.(usages);
|
|
497
|
+
}
|
|
469
498
|
}
|
|
@@ -36,7 +36,7 @@ export class SkillManager {
|
|
|
36
36
|
* Initialize the skill manager by discovering available skills
|
|
37
37
|
*/
|
|
38
38
|
async initialize(): Promise<void> {
|
|
39
|
-
this.logger?.
|
|
39
|
+
this.logger?.debug("Initializing SkillManager...");
|
|
40
40
|
|
|
41
41
|
try {
|
|
42
42
|
// Clear existing data before discovery
|
|
@@ -66,7 +66,7 @@ export class SkillManager {
|
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
this.initialized = true;
|
|
69
|
-
this.logger?.
|
|
69
|
+
this.logger?.debug(
|
|
70
70
|
`SkillManager initialized with ${this.skillMetadata.size} skills`,
|
|
71
71
|
);
|
|
72
72
|
} catch (error) {
|
|
@@ -247,7 +247,7 @@ export class SkillManager {
|
|
|
247
247
|
): Promise<{ content: string; context?: SkillInvocationContext }> {
|
|
248
248
|
const { skill_name } = args;
|
|
249
249
|
|
|
250
|
-
this.logger?.
|
|
250
|
+
this.logger?.debug(`Invoking skill: ${skill_name}`);
|
|
251
251
|
|
|
252
252
|
try {
|
|
253
253
|
// Load the skill
|
|
@@ -92,7 +92,7 @@ export class SlashCommandManager {
|
|
|
92
92
|
});
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
-
this.logger?.
|
|
95
|
+
this.logger?.debug(`Loaded ${customCommands.length} custom commands`);
|
|
96
96
|
} catch (error) {
|
|
97
97
|
this.logger?.warn("Failed to load custom commands:", error);
|
|
98
98
|
}
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import { randomUUID } from "crypto";
|
|
2
2
|
import type { SubagentConfiguration } from "../utils/subagentParser.js";
|
|
3
|
-
import type {
|
|
3
|
+
import type {
|
|
4
|
+
Message,
|
|
5
|
+
Logger,
|
|
6
|
+
GatewayConfig,
|
|
7
|
+
ModelConfig,
|
|
8
|
+
Usage,
|
|
9
|
+
} from "../types.js";
|
|
4
10
|
import { AIManager } from "./aiManager.js";
|
|
5
11
|
import {
|
|
6
12
|
MessageManager,
|
|
@@ -27,6 +33,7 @@ export interface SubagentManagerOptions {
|
|
|
27
33
|
gatewayConfig: GatewayConfig;
|
|
28
34
|
modelConfig: ModelConfig;
|
|
29
35
|
tokenLimit: number;
|
|
36
|
+
onUsageAdded?: (usage: Usage) => void;
|
|
30
37
|
}
|
|
31
38
|
|
|
32
39
|
export class SubagentManager {
|
|
@@ -40,6 +47,7 @@ export class SubagentManager {
|
|
|
40
47
|
private gatewayConfig: GatewayConfig;
|
|
41
48
|
private modelConfig: ModelConfig;
|
|
42
49
|
private tokenLimit: number;
|
|
50
|
+
private onUsageAdded?: (usage: Usage) => void;
|
|
43
51
|
|
|
44
52
|
constructor(options: SubagentManagerOptions) {
|
|
45
53
|
this.workdir = options.workdir;
|
|
@@ -49,6 +57,7 @@ export class SubagentManager {
|
|
|
49
57
|
this.gatewayConfig = options.gatewayConfig;
|
|
50
58
|
this.modelConfig = options.modelConfig;
|
|
51
59
|
this.tokenLimit = options.tokenLimit;
|
|
60
|
+
this.onUsageAdded = options.onUsageAdded;
|
|
52
61
|
}
|
|
53
62
|
|
|
54
63
|
/**
|
|
@@ -156,6 +165,9 @@ export class SubagentManager {
|
|
|
156
165
|
agentModel: modelToUse,
|
|
157
166
|
},
|
|
158
167
|
tokenLimit: this.tokenLimit,
|
|
168
|
+
callbacks: {
|
|
169
|
+
onUsageAdded: this.onUsageAdded,
|
|
170
|
+
},
|
|
159
171
|
});
|
|
160
172
|
|
|
161
173
|
const instance: SubagentInstance = {
|
|
@@ -311,7 +323,7 @@ export class SubagentManager {
|
|
|
311
323
|
messages: instance.messages,
|
|
312
324
|
});
|
|
313
325
|
|
|
314
|
-
this.logger?.
|
|
326
|
+
this.logger?.debug(`Aborted subagent instance: ${subagentId}`);
|
|
315
327
|
return true;
|
|
316
328
|
} catch (error) {
|
|
317
329
|
this.logger?.error(
|
|
@@ -214,9 +214,18 @@ export interface CompressMessagesOptions {
|
|
|
214
214
|
abortSignal?: AbortSignal;
|
|
215
215
|
}
|
|
216
216
|
|
|
217
|
+
export interface CompressMessagesResult {
|
|
218
|
+
content: string;
|
|
219
|
+
usage?: {
|
|
220
|
+
prompt_tokens: number;
|
|
221
|
+
completion_tokens: number;
|
|
222
|
+
total_tokens: number;
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
|
|
217
226
|
export async function compressMessages(
|
|
218
227
|
options: CompressMessagesOptions,
|
|
219
|
-
): Promise<
|
|
228
|
+
): Promise<CompressMessagesResult> {
|
|
220
229
|
const { gatewayConfig, modelConfig, messages, abortSignal } = options;
|
|
221
230
|
|
|
222
231
|
// Create OpenAI client with injected configuration
|
|
@@ -294,15 +303,29 @@ For technical conversations, structure as:
|
|
|
294
303
|
},
|
|
295
304
|
);
|
|
296
305
|
|
|
297
|
-
|
|
306
|
+
const content =
|
|
298
307
|
response.choices[0]?.message?.content?.trim() ||
|
|
299
|
-
"Failed to compress conversation history"
|
|
300
|
-
|
|
308
|
+
"Failed to compress conversation history";
|
|
309
|
+
const usage = response.usage
|
|
310
|
+
? {
|
|
311
|
+
prompt_tokens: response.usage.prompt_tokens,
|
|
312
|
+
completion_tokens: response.usage.completion_tokens,
|
|
313
|
+
total_tokens: response.usage.total_tokens,
|
|
314
|
+
}
|
|
315
|
+
: undefined;
|
|
316
|
+
|
|
317
|
+
return {
|
|
318
|
+
content,
|
|
319
|
+
usage,
|
|
320
|
+
};
|
|
301
321
|
} catch (error) {
|
|
302
322
|
if ((error as Error).name === "AbortError") {
|
|
303
323
|
throw new Error("Compression request was aborted");
|
|
304
324
|
}
|
|
305
325
|
// // logger.error("Failed to compress messages:", error);
|
|
306
|
-
return
|
|
326
|
+
return {
|
|
327
|
+
content: "Failed to compress conversation history",
|
|
328
|
+
usage: undefined,
|
|
329
|
+
};
|
|
307
330
|
}
|
|
308
331
|
}
|
package/src/services/memory.ts
CHANGED
|
@@ -41,7 +41,7 @@ export const addMemory = async (
|
|
|
41
41
|
// Write file
|
|
42
42
|
await fs.writeFile(memoryFilePath, updatedContent, "utf-8");
|
|
43
43
|
|
|
44
|
-
// logger.
|
|
44
|
+
// logger.debug(`Memory added to ${memoryFilePath}:`, message);
|
|
45
45
|
} catch (error) {
|
|
46
46
|
// logger.error("Failed to add memory:", error);
|
|
47
47
|
throw new Error(`Failed to add memory: ${(error as Error).message}`);
|
|
@@ -63,7 +63,7 @@ export const ensureUserMemoryFile = async (): Promise<void> => {
|
|
|
63
63
|
const initialContent =
|
|
64
64
|
"# User Memory\n\nThis is the user-level memory file, recording important information and context across projects.\n\n";
|
|
65
65
|
await fs.writeFile(USER_MEMORY_FILE, initialContent, "utf-8");
|
|
66
|
-
// logger.
|
|
66
|
+
// logger.debug(`Created user memory file: ${USER_MEMORY_FILE}`);
|
|
67
67
|
} else {
|
|
68
68
|
throw error;
|
|
69
69
|
}
|
|
@@ -93,7 +93,7 @@ export const addUserMemory = async (message: string): Promise<void> => {
|
|
|
93
93
|
// Write file
|
|
94
94
|
await fs.writeFile(USER_MEMORY_FILE, updatedContent, "utf-8");
|
|
95
95
|
|
|
96
|
-
// logger.
|
|
96
|
+
// logger.debug(`User memory added to ${USER_MEMORY_FILE}:`, message);
|
|
97
97
|
} catch (error) {
|
|
98
98
|
// logger.error("Failed to add user memory:", error);
|
|
99
99
|
throw new Error(`Failed to add user memory: ${(error as Error).message}`);
|