wave-agent-sdk 0.0.8 → 0.0.10
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 +92 -23
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +340 -137
- 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 +14 -36
- package/dist/managers/aiManager.d.ts.map +1 -1
- package/dist/managers/aiManager.js +74 -77
- package/dist/managers/backgroundBashManager.d.ts.map +1 -1
- package/dist/managers/backgroundBashManager.js +4 -3
- package/dist/managers/hookManager.d.ts +3 -8
- package/dist/managers/hookManager.d.ts.map +1 -1
- package/dist/managers/hookManager.js +39 -29
- package/dist/managers/liveConfigManager.d.ts +55 -18
- package/dist/managers/liveConfigManager.d.ts.map +1 -1
- package/dist/managers/liveConfigManager.js +372 -90
- package/dist/managers/lspManager.d.ts +43 -0
- package/dist/managers/lspManager.d.ts.map +1 -0
- package/dist/managers/lspManager.js +326 -0
- package/dist/managers/messageManager.d.ts +8 -16
- package/dist/managers/messageManager.d.ts.map +1 -1
- package/dist/managers/messageManager.js +52 -74
- package/dist/managers/permissionManager.d.ts +66 -0
- package/dist/managers/permissionManager.d.ts.map +1 -0
- package/dist/managers/permissionManager.js +208 -0
- package/dist/managers/skillManager.d.ts +1 -0
- package/dist/managers/skillManager.d.ts.map +1 -1
- package/dist/managers/skillManager.js +2 -1
- package/dist/managers/slashCommandManager.d.ts.map +1 -1
- package/dist/managers/slashCommandManager.js +0 -1
- package/dist/managers/subagentManager.d.ts +8 -23
- package/dist/managers/subagentManager.d.ts.map +1 -1
- package/dist/managers/subagentManager.js +97 -117
- package/dist/managers/toolManager.d.ts +38 -1
- package/dist/managers/toolManager.d.ts.map +1 -1
- package/dist/managers/toolManager.js +66 -2
- package/dist/services/aiService.d.ts +3 -1
- package/dist/services/aiService.d.ts.map +1 -1
- package/dist/services/aiService.js +123 -30
- package/dist/services/configurationService.d.ts +116 -0
- package/dist/services/configurationService.d.ts.map +1 -0
- package/dist/services/configurationService.js +585 -0
- package/dist/services/fileWatcher.d.ts.map +1 -1
- package/dist/services/fileWatcher.js +5 -6
- package/dist/services/hook.d.ts +7 -124
- package/dist/services/hook.d.ts.map +1 -1
- package/dist/services/hook.js +46 -458
- package/dist/services/jsonlHandler.d.ts +24 -15
- package/dist/services/jsonlHandler.d.ts.map +1 -1
- package/dist/services/jsonlHandler.js +67 -88
- package/dist/services/memory.d.ts +0 -9
- package/dist/services/memory.d.ts.map +1 -1
- package/dist/services/memory.js +2 -49
- package/dist/services/session.d.ts +82 -33
- package/dist/services/session.d.ts.map +1 -1
- package/dist/services/session.js +275 -181
- package/dist/tools/bashTool.d.ts.map +1 -1
- package/dist/tools/bashTool.js +72 -13
- package/dist/tools/deleteFileTool.d.ts.map +1 -1
- package/dist/tools/deleteFileTool.js +25 -0
- package/dist/tools/editTool.d.ts.map +1 -1
- package/dist/tools/editTool.js +30 -6
- package/dist/tools/lspTool.d.ts +6 -0
- package/dist/tools/lspTool.d.ts.map +1 -0
- package/dist/tools/lspTool.js +589 -0
- package/dist/tools/multiEditTool.d.ts.map +1 -1
- package/dist/tools/multiEditTool.js +26 -7
- package/dist/tools/readTool.d.ts.map +1 -1
- package/dist/tools/readTool.js +111 -2
- package/dist/tools/skillTool.js +2 -2
- package/dist/tools/todoWriteTool.d.ts.map +1 -1
- package/dist/tools/todoWriteTool.js +23 -0
- package/dist/tools/types.d.ts +11 -8
- package/dist/tools/types.d.ts.map +1 -1
- package/dist/tools/writeTool.d.ts.map +1 -1
- package/dist/tools/writeTool.js +25 -9
- package/dist/types/commands.d.ts +0 -1
- package/dist/types/commands.d.ts.map +1 -1
- package/dist/types/config.d.ts +4 -0
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/configuration.d.ts +69 -0
- package/dist/types/configuration.d.ts.map +1 -0
- package/dist/types/configuration.js +8 -0
- package/dist/types/core.d.ts +10 -0
- package/dist/types/core.d.ts.map +1 -1
- package/dist/types/environment.d.ts +41 -0
- package/dist/types/environment.d.ts.map +1 -1
- package/dist/types/fileSearch.d.ts +5 -0
- package/dist/types/fileSearch.d.ts.map +1 -0
- package/dist/types/fileSearch.js +1 -0
- package/dist/types/hooks.d.ts +11 -2
- package/dist/types/hooks.d.ts.map +1 -1
- package/dist/types/hooks.js +1 -7
- package/dist/types/index.d.ts +5 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +5 -0
- package/dist/types/lsp.d.ts +90 -0
- package/dist/types/lsp.d.ts.map +1 -0
- package/dist/types/lsp.js +4 -0
- package/dist/types/messaging.d.ts +6 -11
- package/dist/types/messaging.d.ts.map +1 -1
- package/dist/types/permissions.d.ts +35 -0
- package/dist/types/permissions.d.ts.map +1 -0
- package/dist/types/permissions.js +12 -0
- package/dist/types/session.d.ts +1 -6
- package/dist/types/session.d.ts.map +1 -1
- package/dist/types/skills.d.ts +1 -0
- package/dist/types/skills.d.ts.map +1 -1
- package/dist/types/tools.d.ts +35 -0
- package/dist/types/tools.d.ts.map +1 -0
- package/dist/types/tools.js +4 -0
- package/dist/utils/abortUtils.d.ts +34 -0
- package/dist/utils/abortUtils.d.ts.map +1 -0
- package/dist/utils/abortUtils.js +92 -0
- package/dist/utils/bashHistory.d.ts +4 -0
- package/dist/utils/bashHistory.d.ts.map +1 -1
- package/dist/utils/bashHistory.js +21 -4
- package/dist/utils/builtinSubagents.d.ts +7 -0
- package/dist/utils/builtinSubagents.d.ts.map +1 -0
- package/dist/utils/builtinSubagents.js +65 -0
- package/dist/utils/cacheControlUtils.d.ts +8 -33
- package/dist/utils/cacheControlUtils.d.ts.map +1 -1
- package/dist/utils/cacheControlUtils.js +83 -126
- package/dist/utils/constants.d.ts +0 -12
- package/dist/utils/constants.d.ts.map +1 -1
- package/dist/utils/constants.js +1 -13
- package/dist/utils/convertMessagesForAPI.d.ts +2 -1
- package/dist/utils/convertMessagesForAPI.d.ts.map +1 -1
- package/dist/utils/convertMessagesForAPI.js +33 -14
- package/dist/utils/fileSearch.d.ts +14 -0
- package/dist/utils/fileSearch.d.ts.map +1 -0
- package/dist/utils/fileSearch.js +88 -0
- package/dist/utils/fileUtils.d.ts +14 -2
- package/dist/utils/fileUtils.d.ts.map +1 -1
- package/dist/utils/fileUtils.js +101 -17
- package/dist/utils/globalLogger.d.ts +0 -14
- package/dist/utils/globalLogger.d.ts.map +1 -1
- package/dist/utils/globalLogger.js +0 -16
- package/dist/utils/largeOutputHandler.d.ts +15 -0
- package/dist/utils/largeOutputHandler.d.ts.map +1 -0
- package/dist/utils/largeOutputHandler.js +40 -0
- package/dist/utils/markdownParser.d.ts.map +1 -1
- package/dist/utils/markdownParser.js +1 -17
- package/dist/utils/messageOperations.d.ts +1 -11
- package/dist/utils/messageOperations.d.ts.map +1 -1
- package/dist/utils/messageOperations.js +7 -24
- package/dist/utils/pathEncoder.d.ts +4 -0
- package/dist/utils/pathEncoder.d.ts.map +1 -1
- package/dist/utils/pathEncoder.js +16 -9
- package/dist/utils/subagentParser.d.ts +2 -2
- package/dist/utils/subagentParser.d.ts.map +1 -1
- package/dist/utils/subagentParser.js +10 -7
- package/dist/utils/tokenEstimator.d.ts +39 -0
- package/dist/utils/tokenEstimator.d.ts.map +1 -0
- package/dist/utils/tokenEstimator.js +55 -0
- package/package.json +5 -8
- package/src/agent.ts +460 -216
- package/src/index.ts +2 -0
- package/src/managers/aiManager.ts +107 -111
- package/src/managers/backgroundBashManager.ts +4 -3
- package/src/managers/hookManager.ts +44 -39
- package/src/managers/liveConfigManager.ts +524 -138
- package/src/managers/lspManager.ts +434 -0
- package/src/managers/messageManager.ts +73 -103
- package/src/managers/permissionManager.ts +276 -0
- package/src/managers/skillManager.ts +3 -1
- package/src/managers/slashCommandManager.ts +1 -2
- package/src/managers/subagentManager.ts +116 -159
- package/src/managers/toolManager.ts +95 -3
- package/src/services/aiService.ts +207 -26
- package/src/services/configurationService.ts +762 -0
- package/src/services/fileWatcher.ts +5 -6
- package/src/services/hook.ts +50 -631
- package/src/services/jsonlHandler.ts +84 -100
- package/src/services/memory.ts +2 -59
- package/src/services/session.ts +338 -213
- package/src/tools/bashTool.ts +89 -16
- package/src/tools/deleteFileTool.ts +36 -0
- package/src/tools/editTool.ts +41 -7
- package/src/tools/lspTool.ts +760 -0
- package/src/tools/multiEditTool.ts +37 -8
- package/src/tools/readTool.ts +125 -2
- package/src/tools/skillTool.ts +2 -2
- package/src/tools/todoWriteTool.ts +33 -1
- package/src/tools/types.ts +15 -9
- package/src/tools/writeTool.ts +36 -10
- package/src/types/commands.ts +0 -1
- package/src/types/config.ts +5 -0
- package/src/types/configuration.ts +73 -0
- package/src/types/core.ts +11 -0
- package/src/types/environment.ts +44 -0
- package/src/types/fileSearch.ts +4 -0
- package/src/types/hooks.ts +14 -11
- package/src/types/index.ts +5 -0
- package/src/types/lsp.ts +96 -0
- package/src/types/messaging.ts +8 -13
- package/src/types/permissions.ts +48 -0
- package/src/types/session.ts +3 -8
- package/src/types/skills.ts +1 -0
- package/src/types/tools.ts +38 -0
- package/src/utils/abortUtils.ts +118 -0
- package/src/utils/bashHistory.ts +28 -4
- package/src/utils/builtinSubagents.ts +71 -0
- package/src/utils/cacheControlUtils.ts +106 -171
- package/src/utils/constants.ts +1 -16
- package/src/utils/convertMessagesForAPI.ts +38 -14
- package/src/utils/fileSearch.ts +107 -0
- package/src/utils/fileUtils.ts +114 -19
- package/src/utils/globalLogger.ts +0 -17
- package/src/utils/largeOutputHandler.ts +55 -0
- package/src/utils/markdownParser.ts +1 -19
- package/src/utils/messageOperations.ts +7 -35
- package/src/utils/pathEncoder.ts +24 -9
- package/src/utils/subagentParser.ts +11 -8
- package/src/utils/tokenEstimator.ts +68 -0
- package/dist/constants/events.d.ts +0 -28
- package/dist/constants/events.d.ts.map +0 -1
- package/dist/constants/events.js +0 -27
- package/dist/services/configurationWatcher.d.ts +0 -120
- package/dist/services/configurationWatcher.d.ts.map +0 -1
- package/dist/services/configurationWatcher.js +0 -439
- package/dist/services/memoryStore.d.ts +0 -81
- package/dist/services/memoryStore.d.ts.map +0 -1
- package/dist/services/memoryStore.js +0 -200
- package/dist/types/memoryStore.d.ts +0 -82
- package/dist/types/memoryStore.d.ts.map +0 -1
- package/dist/types/memoryStore.js +0 -7
- package/dist/utils/configResolver.d.ts +0 -65
- package/dist/utils/configResolver.d.ts.map +0 -1
- package/dist/utils/configResolver.js +0 -210
- package/src/constants/events.ts +0 -38
- package/src/services/configurationWatcher.ts +0 -622
- package/src/services/memoryStore.ts +0 -279
- package/src/types/memoryStore.ts +0 -94
- package/src/utils/configResolver.ts +0 -302
|
@@ -2,7 +2,6 @@ import {
|
|
|
2
2
|
addAssistantMessageToMessages,
|
|
3
3
|
updateToolBlockInMessage,
|
|
4
4
|
addErrorBlockToMessage,
|
|
5
|
-
addDiffBlockToMessage,
|
|
6
5
|
addUserMessageToMessages,
|
|
7
6
|
extractUserInputHistory,
|
|
8
7
|
addMemoryBlockToMessage,
|
|
@@ -21,9 +20,6 @@ import type { SubagentConfiguration } from "../utils/subagentParser.js";
|
|
|
21
20
|
import type { Logger, Message, Usage } from "../types/index.js";
|
|
22
21
|
import { join } from "path";
|
|
23
22
|
import {
|
|
24
|
-
cleanupExpiredSessionsFromJsonl,
|
|
25
|
-
getLatestSessionFromJsonl,
|
|
26
|
-
loadSessionFromJsonl,
|
|
27
23
|
appendMessages,
|
|
28
24
|
createSession,
|
|
29
25
|
generateSessionId,
|
|
@@ -45,8 +41,9 @@ export interface MessageManagerCallbacks {
|
|
|
45
41
|
onAssistantMessageAdded?: () => void;
|
|
46
42
|
// NEW: Streaming content callback - FR-001: receives chunk and accumulated content
|
|
47
43
|
onAssistantContentUpdated?: (chunk: string, accumulated: string) => void;
|
|
44
|
+
// NEW: Streaming reasoning callback
|
|
45
|
+
onAssistantReasoningUpdated?: (chunk: string, accumulated: string) => void;
|
|
48
46
|
onToolBlockUpdated?: (params: AgentToolBlockUpdateParams) => void;
|
|
49
|
-
onDiffBlockAdded?: (filePath: string, diffResult: string) => void;
|
|
50
47
|
onErrorBlockAdded?: (error: string) => void;
|
|
51
48
|
onCompressBlockAdded?: (insertIndex: number, content: string) => void;
|
|
52
49
|
onCompressionStateChange?: (isCompressing: boolean) => void;
|
|
@@ -80,7 +77,6 @@ export interface MessageManagerOptions {
|
|
|
80
77
|
workdir: string;
|
|
81
78
|
logger?: Logger;
|
|
82
79
|
sessionType?: "main" | "subagent";
|
|
83
|
-
parentSessionId?: string;
|
|
84
80
|
subagentType?: string;
|
|
85
81
|
}
|
|
86
82
|
|
|
@@ -90,7 +86,6 @@ export class MessageManager {
|
|
|
90
86
|
private messages: Message[];
|
|
91
87
|
private latestTotalTokens: number;
|
|
92
88
|
private userInputHistory: string[];
|
|
93
|
-
private sessionStartTime: string;
|
|
94
89
|
private workdir: string;
|
|
95
90
|
private encodedWorkdir: string; // Cached encoded workdir
|
|
96
91
|
private logger?: Logger; // Add optional logger property
|
|
@@ -98,7 +93,6 @@ export class MessageManager {
|
|
|
98
93
|
private transcriptPath: string; // Cached transcript path
|
|
99
94
|
private savedMessageCount: number; // Track how many messages have been saved to prevent duplication
|
|
100
95
|
private sessionType: "main" | "subagent";
|
|
101
|
-
private parentSessionId?: string;
|
|
102
96
|
private subagentType?: string;
|
|
103
97
|
|
|
104
98
|
constructor(options: MessageManagerOptions) {
|
|
@@ -106,14 +100,12 @@ export class MessageManager {
|
|
|
106
100
|
this.messages = [];
|
|
107
101
|
this.latestTotalTokens = 0;
|
|
108
102
|
this.userInputHistory = [];
|
|
109
|
-
this.sessionStartTime = new Date().toISOString();
|
|
110
103
|
this.workdir = options.workdir;
|
|
111
104
|
this.encodedWorkdir = pathEncoder.encodeSync(this.workdir); // Cache encoded workdir
|
|
112
105
|
this.callbacks = options.callbacks;
|
|
113
106
|
this.logger = options.logger;
|
|
114
107
|
this.savedMessageCount = 0; // Initialize saved message count tracker
|
|
115
108
|
this.sessionType = options.sessionType || "main";
|
|
116
|
-
this.parentSessionId = options.parentSessionId;
|
|
117
109
|
this.subagentType = options.subagentType;
|
|
118
110
|
|
|
119
111
|
// Compute and cache the transcript path
|
|
@@ -137,10 +129,6 @@ export class MessageManager {
|
|
|
137
129
|
return [...this.userInputHistory];
|
|
138
130
|
}
|
|
139
131
|
|
|
140
|
-
public getSessionStartTime(): string {
|
|
141
|
-
return this.sessionStartTime;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
132
|
public getWorkdir(): string {
|
|
145
133
|
return this.workdir;
|
|
146
134
|
}
|
|
@@ -173,6 +161,7 @@ export class MessageManager {
|
|
|
173
161
|
this.savedMessageCount = 0;
|
|
174
162
|
// Recompute transcript path since session ID changed
|
|
175
163
|
this.transcriptPath = this.computeTranscriptPath();
|
|
164
|
+
|
|
176
165
|
this.callbacks.onSessionIdChange?.(sessionId);
|
|
177
166
|
}
|
|
178
167
|
}
|
|
@@ -182,13 +171,7 @@ export class MessageManager {
|
|
|
182
171
|
*/
|
|
183
172
|
private async createSessionIfNeeded(): Promise<void> {
|
|
184
173
|
try {
|
|
185
|
-
await createSession(
|
|
186
|
-
this.sessionId,
|
|
187
|
-
this.workdir,
|
|
188
|
-
this.sessionType,
|
|
189
|
-
this.parentSessionId,
|
|
190
|
-
this.subagentType,
|
|
191
|
-
);
|
|
174
|
+
await createSession(this.sessionId, this.workdir, this.sessionType);
|
|
192
175
|
} catch (error) {
|
|
193
176
|
this.logger?.error("Failed to create session:", error);
|
|
194
177
|
}
|
|
@@ -223,6 +206,7 @@ export class MessageManager {
|
|
|
223
206
|
this.sessionId,
|
|
224
207
|
unsavedMessages, // Only append new messages
|
|
225
208
|
this.workdir,
|
|
209
|
+
this.sessionType,
|
|
226
210
|
);
|
|
227
211
|
|
|
228
212
|
// Update the saved message count
|
|
@@ -232,61 +216,10 @@ export class MessageManager {
|
|
|
232
216
|
}
|
|
233
217
|
}
|
|
234
218
|
|
|
235
|
-
/**
|
|
236
|
-
* Handle session restoration logic
|
|
237
|
-
*/
|
|
238
|
-
public async handleSessionRestoration(
|
|
239
|
-
restoreSessionId?: string,
|
|
240
|
-
continueLastSession?: boolean,
|
|
241
|
-
): Promise<void> {
|
|
242
|
-
// Clean up expired sessions first
|
|
243
|
-
cleanupExpiredSessionsFromJsonl(this.workdir).catch((error) => {
|
|
244
|
-
this.logger?.warn("Failed to cleanup expired sessions:", error);
|
|
245
|
-
});
|
|
246
|
-
|
|
247
|
-
if (!restoreSessionId && !continueLastSession) {
|
|
248
|
-
return;
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
try {
|
|
252
|
-
let sessionToRestore: SessionData | null = null;
|
|
253
|
-
|
|
254
|
-
if (restoreSessionId) {
|
|
255
|
-
// Use only JSONL format - no legacy support
|
|
256
|
-
sessionToRestore = await loadSessionFromJsonl(
|
|
257
|
-
restoreSessionId,
|
|
258
|
-
this.workdir,
|
|
259
|
-
);
|
|
260
|
-
if (!sessionToRestore) {
|
|
261
|
-
console.error(`Session not found: ${restoreSessionId}`);
|
|
262
|
-
process.exit(1);
|
|
263
|
-
}
|
|
264
|
-
} else if (continueLastSession) {
|
|
265
|
-
// Use only JSONL format - no legacy support
|
|
266
|
-
sessionToRestore = await getLatestSessionFromJsonl(this.workdir);
|
|
267
|
-
if (!sessionToRestore) {
|
|
268
|
-
console.error(
|
|
269
|
-
`No previous session found for workdir: ${this.workdir}`,
|
|
270
|
-
);
|
|
271
|
-
process.exit(1);
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
if (sessionToRestore) {
|
|
276
|
-
console.log(`Restoring session: ${sessionToRestore.id}`);
|
|
277
|
-
|
|
278
|
-
// Initialize from session data
|
|
279
|
-
this.initializeFromSession(sessionToRestore);
|
|
280
|
-
}
|
|
281
|
-
} catch (error) {
|
|
282
|
-
console.error("Failed to restore session:", error);
|
|
283
|
-
process.exit(1);
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
|
|
287
219
|
public setlatestTotalTokens(latestTotalTokens: number): void {
|
|
288
220
|
if (this.latestTotalTokens !== latestTotalTokens) {
|
|
289
221
|
this.latestTotalTokens = latestTotalTokens;
|
|
222
|
+
|
|
290
223
|
this.callbacks.onLatestTotalTokensChange?.(latestTotalTokens);
|
|
291
224
|
}
|
|
292
225
|
}
|
|
@@ -304,7 +237,6 @@ export class MessageManager {
|
|
|
304
237
|
this.setUserInputHistory([]);
|
|
305
238
|
this.setSessionId(generateSessionId());
|
|
306
239
|
this.setlatestTotalTokens(0);
|
|
307
|
-
this.sessionStartTime = new Date().toISOString();
|
|
308
240
|
this.savedMessageCount = 0; // Reset saved message count
|
|
309
241
|
}
|
|
310
242
|
|
|
@@ -314,9 +246,6 @@ export class MessageManager {
|
|
|
314
246
|
this.setMessages([...sessionData.messages]);
|
|
315
247
|
this.setlatestTotalTokens(sessionData.metadata.latestTotalTokens);
|
|
316
248
|
|
|
317
|
-
// Restore the original session start time
|
|
318
|
-
this.sessionStartTime = sessionData.metadata.startedAt;
|
|
319
|
-
|
|
320
249
|
// Extract user input history from session messages
|
|
321
250
|
this.setUserInputHistory(extractUserInputHistory(sessionData.messages));
|
|
322
251
|
|
|
@@ -359,11 +288,13 @@ export class MessageManager {
|
|
|
359
288
|
content?: string,
|
|
360
289
|
toolCalls?: ChatCompletionMessageFunctionToolCall[],
|
|
361
290
|
usage?: Usage,
|
|
362
|
-
|
|
291
|
+
additionalFields?: Record<string, unknown>,
|
|
363
292
|
): void {
|
|
364
|
-
const
|
|
293
|
+
const additionalFieldsRecord = additionalFields
|
|
365
294
|
? Object.fromEntries(
|
|
366
|
-
Object.entries(
|
|
295
|
+
Object.entries(additionalFields).filter(
|
|
296
|
+
([, value]) => value !== undefined,
|
|
297
|
+
),
|
|
367
298
|
)
|
|
368
299
|
: undefined;
|
|
369
300
|
|
|
@@ -372,7 +303,7 @@ export class MessageManager {
|
|
|
372
303
|
content,
|
|
373
304
|
toolCalls,
|
|
374
305
|
usage,
|
|
375
|
-
|
|
306
|
+
additionalFieldsRecord,
|
|
376
307
|
);
|
|
377
308
|
this.setMessages(newMessages);
|
|
378
309
|
this.callbacks.onAssistantMessageAdded?.();
|
|
@@ -380,8 +311,10 @@ export class MessageManager {
|
|
|
380
311
|
// Note: Subagent-specific callbacks are now handled by SubagentManager
|
|
381
312
|
}
|
|
382
313
|
|
|
383
|
-
public
|
|
384
|
-
|
|
314
|
+
public mergeAssistantAdditionalFields(
|
|
315
|
+
additionalFields: Record<string, unknown>,
|
|
316
|
+
): void {
|
|
317
|
+
if (!additionalFields || Object.keys(additionalFields).length === 0) {
|
|
385
318
|
return;
|
|
386
319
|
}
|
|
387
320
|
|
|
@@ -389,22 +322,22 @@ export class MessageManager {
|
|
|
389
322
|
for (let i = newMessages.length - 1; i >= 0; i--) {
|
|
390
323
|
const message = newMessages[i];
|
|
391
324
|
if (message.role === "assistant") {
|
|
392
|
-
const
|
|
393
|
-
...(message.
|
|
325
|
+
const mergedAdditionalFields = {
|
|
326
|
+
...(message.additionalFields || {}),
|
|
394
327
|
} as Record<string, unknown>;
|
|
395
328
|
|
|
396
|
-
for (const [key, value] of Object.entries(
|
|
329
|
+
for (const [key, value] of Object.entries(additionalFields)) {
|
|
397
330
|
if (value === undefined) {
|
|
398
331
|
continue;
|
|
399
332
|
}
|
|
400
|
-
|
|
333
|
+
mergedAdditionalFields[key] = value;
|
|
401
334
|
}
|
|
402
335
|
|
|
403
|
-
if (Object.keys(
|
|
336
|
+
if (Object.keys(mergedAdditionalFields).length === 0) {
|
|
404
337
|
return;
|
|
405
338
|
}
|
|
406
339
|
|
|
407
|
-
message.
|
|
340
|
+
message.additionalFields = mergedAdditionalFields;
|
|
408
341
|
this.setMessages(newMessages);
|
|
409
342
|
return;
|
|
410
343
|
}
|
|
@@ -432,19 +365,6 @@ export class MessageManager {
|
|
|
432
365
|
// Note: Subagent-specific callbacks are now handled by SubagentManager
|
|
433
366
|
}
|
|
434
367
|
|
|
435
|
-
public addDiffBlock(
|
|
436
|
-
filePath: string,
|
|
437
|
-
diffResult: Array<{ value: string; added?: boolean; removed?: boolean }>,
|
|
438
|
-
): void {
|
|
439
|
-
const newMessages = addDiffBlockToMessage({
|
|
440
|
-
messages: this.messages,
|
|
441
|
-
path: filePath,
|
|
442
|
-
diffResult,
|
|
443
|
-
});
|
|
444
|
-
this.setMessages(newMessages);
|
|
445
|
-
this.callbacks.onDiffBlockAdded?.(filePath, JSON.stringify(diffResult));
|
|
446
|
-
}
|
|
447
|
-
|
|
448
368
|
public addErrorBlock(error: string): void {
|
|
449
369
|
const newMessages = addErrorBlockToMessage({
|
|
450
370
|
messages: this.messages,
|
|
@@ -640,7 +560,7 @@ export class MessageManager {
|
|
|
640
560
|
};
|
|
641
561
|
} else {
|
|
642
562
|
// Add new text block if none exists
|
|
643
|
-
lastMessage.blocks.
|
|
563
|
+
lastMessage.blocks.push({
|
|
644
564
|
type: "text",
|
|
645
565
|
content: newAccumulatedContent,
|
|
646
566
|
});
|
|
@@ -654,6 +574,56 @@ export class MessageManager {
|
|
|
654
574
|
this.callbacks.onMessagesChange?.([...this.messages]); // Still need to notify of changes
|
|
655
575
|
}
|
|
656
576
|
|
|
577
|
+
/**
|
|
578
|
+
* Update the current assistant message reasoning during streaming
|
|
579
|
+
* This method updates the last assistant message's reasoning content without creating a new message
|
|
580
|
+
*/
|
|
581
|
+
public updateCurrentMessageReasoning(newAccumulatedReasoning: string): void {
|
|
582
|
+
if (this.messages.length === 0) return;
|
|
583
|
+
|
|
584
|
+
const lastMessage = this.messages[this.messages.length - 1];
|
|
585
|
+
if (lastMessage.role !== "assistant") return;
|
|
586
|
+
|
|
587
|
+
// Get the current reasoning content to calculate the chunk
|
|
588
|
+
const reasoningBlockIndex = lastMessage.blocks.findIndex(
|
|
589
|
+
(block) => block.type === "reasoning",
|
|
590
|
+
);
|
|
591
|
+
const currentReasoning =
|
|
592
|
+
reasoningBlockIndex >= 0
|
|
593
|
+
? (
|
|
594
|
+
lastMessage.blocks[reasoningBlockIndex] as {
|
|
595
|
+
type: "reasoning";
|
|
596
|
+
content: string;
|
|
597
|
+
}
|
|
598
|
+
).content || ""
|
|
599
|
+
: "";
|
|
600
|
+
|
|
601
|
+
// Calculate the chunk (new content since last update)
|
|
602
|
+
const chunk = newAccumulatedReasoning.slice(currentReasoning.length);
|
|
603
|
+
|
|
604
|
+
if (reasoningBlockIndex >= 0) {
|
|
605
|
+
// Update existing reasoning block
|
|
606
|
+
lastMessage.blocks[reasoningBlockIndex] = {
|
|
607
|
+
type: "reasoning",
|
|
608
|
+
content: newAccumulatedReasoning,
|
|
609
|
+
};
|
|
610
|
+
} else {
|
|
611
|
+
// Add new reasoning block if none exists
|
|
612
|
+
lastMessage.blocks.push({
|
|
613
|
+
type: "reasoning",
|
|
614
|
+
content: newAccumulatedReasoning,
|
|
615
|
+
});
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
// Trigger callbacks with chunk and accumulated reasoning content
|
|
619
|
+
this.callbacks.onAssistantReasoningUpdated?.(
|
|
620
|
+
chunk,
|
|
621
|
+
newAccumulatedReasoning,
|
|
622
|
+
);
|
|
623
|
+
|
|
624
|
+
this.callbacks.onMessagesChange?.([...this.messages]); // Still need to notify of changes
|
|
625
|
+
}
|
|
626
|
+
|
|
657
627
|
/**
|
|
658
628
|
* Remove the last user message from the conversation
|
|
659
629
|
* Used for hook error handling when the user prompt needs to be erased
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Permission Manager for handling tool permission checks
|
|
3
|
+
*
|
|
4
|
+
* This manager provides utilities for checking permissions before tool execution.
|
|
5
|
+
* It implements the permission logic for different modes (default vs bypass) and
|
|
6
|
+
* handles custom callback integration.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type {
|
|
10
|
+
PermissionDecision,
|
|
11
|
+
ToolPermissionContext,
|
|
12
|
+
PermissionCallback,
|
|
13
|
+
PermissionMode,
|
|
14
|
+
} from "../types/permissions.js";
|
|
15
|
+
import { RESTRICTED_TOOLS } from "../types/permissions.js";
|
|
16
|
+
import type { Logger } from "../types/index.js";
|
|
17
|
+
|
|
18
|
+
export interface PermissionManagerOptions {
|
|
19
|
+
/** Logger for debugging permission decisions */
|
|
20
|
+
logger?: Logger;
|
|
21
|
+
/** Configured default permission mode from settings */
|
|
22
|
+
configuredDefaultMode?: PermissionMode;
|
|
23
|
+
/** Allowed rules from settings */
|
|
24
|
+
allowedRules?: string[];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export class PermissionManager {
|
|
28
|
+
private logger?: Logger;
|
|
29
|
+
private configuredDefaultMode?: PermissionMode;
|
|
30
|
+
private allowedRules: string[] = [];
|
|
31
|
+
private onConfiguredDefaultModeChange?: (mode: PermissionMode) => void;
|
|
32
|
+
|
|
33
|
+
constructor(options: PermissionManagerOptions = {}) {
|
|
34
|
+
this.logger = options.logger;
|
|
35
|
+
this.configuredDefaultMode = options.configuredDefaultMode;
|
|
36
|
+
this.allowedRules = options.allowedRules || [];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Set a callback to be notified when the effective permission mode changes due to configuration updates
|
|
41
|
+
*/
|
|
42
|
+
public setOnConfiguredDefaultModeChange(
|
|
43
|
+
callback: (mode: PermissionMode) => void,
|
|
44
|
+
): void {
|
|
45
|
+
this.onConfiguredDefaultModeChange = callback;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Update the configured default mode (e.g., when configuration reloads)
|
|
50
|
+
*/
|
|
51
|
+
updateConfiguredDefaultMode(defaultMode?: PermissionMode): void {
|
|
52
|
+
const oldEffectiveMode = this.getCurrentEffectiveMode();
|
|
53
|
+
|
|
54
|
+
this.logger?.debug("Updating configured default permission mode", {
|
|
55
|
+
previous: this.configuredDefaultMode,
|
|
56
|
+
new: defaultMode,
|
|
57
|
+
});
|
|
58
|
+
this.configuredDefaultMode = defaultMode;
|
|
59
|
+
|
|
60
|
+
const newEffectiveMode = this.getCurrentEffectiveMode();
|
|
61
|
+
if (
|
|
62
|
+
oldEffectiveMode !== newEffectiveMode &&
|
|
63
|
+
this.onConfiguredDefaultModeChange
|
|
64
|
+
) {
|
|
65
|
+
this.logger?.debug(
|
|
66
|
+
"Effective permission mode changed due to configuration update",
|
|
67
|
+
{
|
|
68
|
+
oldMode: oldEffectiveMode,
|
|
69
|
+
newMode: newEffectiveMode,
|
|
70
|
+
},
|
|
71
|
+
);
|
|
72
|
+
this.onConfiguredDefaultModeChange(newEffectiveMode);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Get all currently allowed rules
|
|
78
|
+
*/
|
|
79
|
+
public getAllowedRules(): string[] {
|
|
80
|
+
return [...this.allowedRules];
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Update the allowed rules (e.g., when configuration reloads)
|
|
85
|
+
*/
|
|
86
|
+
updateAllowedRules(rules: string[]): void {
|
|
87
|
+
this.logger?.debug("Updating allowed permission rules", {
|
|
88
|
+
count: rules.length,
|
|
89
|
+
});
|
|
90
|
+
this.allowedRules = rules;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Get the current effective permission mode for tool execution context
|
|
95
|
+
*/
|
|
96
|
+
getCurrentEffectiveMode(cliPermissionMode?: PermissionMode): PermissionMode {
|
|
97
|
+
return this.resolveEffectivePermissionMode(cliPermissionMode);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Resolve the effective permission mode based on CLI override and configured default
|
|
102
|
+
*/
|
|
103
|
+
resolveEffectivePermissionMode(
|
|
104
|
+
cliPermissionMode?: PermissionMode,
|
|
105
|
+
): PermissionMode {
|
|
106
|
+
// CLI override takes highest precedence
|
|
107
|
+
if (cliPermissionMode !== undefined) {
|
|
108
|
+
this.logger?.debug("Using CLI permission mode override", {
|
|
109
|
+
cliMode: cliPermissionMode,
|
|
110
|
+
configuredDefault: this.configuredDefaultMode,
|
|
111
|
+
});
|
|
112
|
+
return cliPermissionMode;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Use configured default mode if available
|
|
116
|
+
if (this.configuredDefaultMode !== undefined) {
|
|
117
|
+
this.logger?.debug("Using configured default permission mode", {
|
|
118
|
+
configuredDefault: this.configuredDefaultMode,
|
|
119
|
+
});
|
|
120
|
+
return this.configuredDefaultMode;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Fall back to system default
|
|
124
|
+
this.logger?.debug("Using system default permission mode");
|
|
125
|
+
return "default";
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Check if a tool execution requires permission and handle the authorization flow
|
|
130
|
+
* Called by individual tools after validation/diff, before real operation
|
|
131
|
+
*/
|
|
132
|
+
async checkPermission(
|
|
133
|
+
context: ToolPermissionContext,
|
|
134
|
+
): Promise<PermissionDecision> {
|
|
135
|
+
this.logger?.debug("Checking permission for tool", {
|
|
136
|
+
toolName: context.toolName,
|
|
137
|
+
permissionMode: context.permissionMode,
|
|
138
|
+
hasCallback: !!context.canUseToolCallback,
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
// 1. If bypassPermissions mode, always allow
|
|
142
|
+
if (context.permissionMode === "bypassPermissions") {
|
|
143
|
+
this.logger?.debug("Permission bypassed for tool", {
|
|
144
|
+
toolName: context.toolName,
|
|
145
|
+
});
|
|
146
|
+
return { behavior: "allow" };
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// 1.1 If acceptEdits mode, allow Edit, MultiEdit, Delete, Write
|
|
150
|
+
if (context.permissionMode === "acceptEdits") {
|
|
151
|
+
const autoAcceptedTools = ["Edit", "MultiEdit", "Delete", "Write"];
|
|
152
|
+
if (autoAcceptedTools.includes(context.toolName)) {
|
|
153
|
+
this.logger?.debug(
|
|
154
|
+
"Permission automatically accepted for tool in acceptEdits mode",
|
|
155
|
+
{
|
|
156
|
+
toolName: context.toolName,
|
|
157
|
+
},
|
|
158
|
+
);
|
|
159
|
+
return { behavior: "allow" };
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// 1.2 Check if tool call matches any allowed rule
|
|
164
|
+
if (this.isAllowedByRule(context)) {
|
|
165
|
+
this.logger?.debug("Permission allowed by persistent rule", {
|
|
166
|
+
toolName: context.toolName,
|
|
167
|
+
});
|
|
168
|
+
return { behavior: "allow" };
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// 2. If not a restricted tool, always allow
|
|
172
|
+
if (!this.isRestrictedTool(context.toolName)) {
|
|
173
|
+
this.logger?.debug("Tool is not restricted, allowing", {
|
|
174
|
+
toolName: context.toolName,
|
|
175
|
+
});
|
|
176
|
+
return { behavior: "allow" };
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// 3. If custom callback provided, call it and return result
|
|
180
|
+
if (context.canUseToolCallback) {
|
|
181
|
+
try {
|
|
182
|
+
this.logger?.debug("Calling custom permission callback for tool", {
|
|
183
|
+
toolName: context.toolName,
|
|
184
|
+
});
|
|
185
|
+
const decision = await context.canUseToolCallback(context);
|
|
186
|
+
this.logger?.debug("Custom callback returned decision", {
|
|
187
|
+
toolName: context.toolName,
|
|
188
|
+
decision,
|
|
189
|
+
});
|
|
190
|
+
return decision;
|
|
191
|
+
} catch (error) {
|
|
192
|
+
const errorMessage =
|
|
193
|
+
error instanceof Error ? error.message : String(error);
|
|
194
|
+
this.logger?.error("Error in permission callback", {
|
|
195
|
+
toolName: context.toolName,
|
|
196
|
+
error: errorMessage,
|
|
197
|
+
});
|
|
198
|
+
return {
|
|
199
|
+
behavior: "deny",
|
|
200
|
+
message: "Error in permission callback",
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// 4. For default mode on restricted tools without callback, integrate with CLI confirmation
|
|
206
|
+
// Note: CLI confirmation integration will be implemented in Phase 2
|
|
207
|
+
this.logger?.warn(
|
|
208
|
+
"No permission callback provided for restricted tool in default mode",
|
|
209
|
+
{
|
|
210
|
+
toolName: context.toolName,
|
|
211
|
+
},
|
|
212
|
+
);
|
|
213
|
+
return {
|
|
214
|
+
behavior: "deny",
|
|
215
|
+
message: `Tool '${context.toolName}' requires permission approval. No permission callback configured.`,
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Determine if a tool requires permission checks based on its name
|
|
221
|
+
*/
|
|
222
|
+
isRestrictedTool(toolName: string): boolean {
|
|
223
|
+
const isRestricted = (RESTRICTED_TOOLS as readonly string[]).includes(
|
|
224
|
+
toolName,
|
|
225
|
+
);
|
|
226
|
+
this.logger?.debug("Checking if tool is restricted", {
|
|
227
|
+
toolName,
|
|
228
|
+
isRestricted,
|
|
229
|
+
});
|
|
230
|
+
return isRestricted;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Helper method to create a permission context for CLI integration
|
|
235
|
+
*/
|
|
236
|
+
createContext(
|
|
237
|
+
toolName: string,
|
|
238
|
+
permissionMode: PermissionMode,
|
|
239
|
+
callback?: PermissionCallback,
|
|
240
|
+
toolInput?: Record<string, unknown>,
|
|
241
|
+
): ToolPermissionContext {
|
|
242
|
+
const context: ToolPermissionContext = {
|
|
243
|
+
toolName,
|
|
244
|
+
permissionMode,
|
|
245
|
+
canUseToolCallback: callback,
|
|
246
|
+
toolInput,
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
this.logger?.debug("Created permission context", {
|
|
250
|
+
toolName,
|
|
251
|
+
permissionMode,
|
|
252
|
+
hasCallback: !!callback,
|
|
253
|
+
hasToolInput: !!toolInput,
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
return context;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Check if a tool call is allowed by persistent rules
|
|
261
|
+
*/
|
|
262
|
+
private isAllowedByRule(context: ToolPermissionContext): boolean {
|
|
263
|
+
if (context.toolName === "Bash" && context.toolInput?.command) {
|
|
264
|
+
const action = `Bash(${context.toolInput.command})`;
|
|
265
|
+
return this.allowedRules.some((rule) => {
|
|
266
|
+
if (rule.endsWith(":*)")) {
|
|
267
|
+
const prefix = rule.slice(0, -3);
|
|
268
|
+
return action.startsWith(prefix);
|
|
269
|
+
}
|
|
270
|
+
return action === rule;
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
// Add other tools if needed in the future
|
|
274
|
+
return false;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
@@ -20,6 +20,7 @@ export class SkillManager {
|
|
|
20
20
|
private personalSkillsPath: string;
|
|
21
21
|
private scanTimeout: number;
|
|
22
22
|
private logger?: Logger;
|
|
23
|
+
private workdir: string;
|
|
23
24
|
|
|
24
25
|
private skillMetadata = new Map<string, SkillMetadata>();
|
|
25
26
|
private skillContent = new Map<string, Skill>();
|
|
@@ -30,6 +31,7 @@ export class SkillManager {
|
|
|
30
31
|
options.personalSkillsPath || join(homedir(), ".wave", "skills");
|
|
31
32
|
this.scanTimeout = options.scanTimeout || 5000;
|
|
32
33
|
this.logger = options.logger;
|
|
34
|
+
this.workdir = options.workdir || process.cwd();
|
|
33
35
|
}
|
|
34
36
|
|
|
35
37
|
/**
|
|
@@ -123,7 +125,7 @@ export class SkillManager {
|
|
|
123
125
|
);
|
|
124
126
|
|
|
125
127
|
const projectCollection = await this.discoverSkillCollection(
|
|
126
|
-
|
|
128
|
+
this.workdir,
|
|
127
129
|
"project",
|
|
128
130
|
);
|
|
129
131
|
|
|
@@ -218,7 +218,7 @@ export class SlashCommandManager {
|
|
|
218
218
|
private async executeCustomCommandInMainAgent(
|
|
219
219
|
commandName: string,
|
|
220
220
|
content: string,
|
|
221
|
-
config?: { model?: string
|
|
221
|
+
config?: { model?: string },
|
|
222
222
|
args?: string,
|
|
223
223
|
): Promise<void> {
|
|
224
224
|
try {
|
|
@@ -272,7 +272,6 @@ export class SlashCommandManager {
|
|
|
272
272
|
// Execute the AI conversation with custom configuration
|
|
273
273
|
await this.aiManager.sendAIMessage({
|
|
274
274
|
model: config?.model,
|
|
275
|
-
allowedTools: config?.allowedTools,
|
|
276
275
|
});
|
|
277
276
|
} catch (error) {
|
|
278
277
|
this.logger?.error(
|