wave-agent-sdk 0.2.1 → 0.5.0
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 +66 -20
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +156 -83
- package/dist/constants/prompts.d.ts +7 -2
- package/dist/constants/prompts.d.ts.map +1 -1
- package/dist/constants/prompts.js +41 -5
- package/dist/constants/tools.d.ts +2 -2
- package/dist/constants/tools.js +2 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/managers/MemoryRuleManager.d.ts.map +1 -1
- package/dist/managers/MemoryRuleManager.js +16 -2
- package/dist/managers/aiManager.d.ts +14 -4
- package/dist/managers/aiManager.d.ts.map +1 -1
- package/dist/managers/aiManager.js +61 -9
- package/dist/managers/backgroundBashManager.d.ts.map +1 -1
- package/dist/managers/backgroundBashManager.js +1 -0
- package/dist/managers/backgroundTaskManager.d.ts +35 -0
- package/dist/managers/backgroundTaskManager.d.ts.map +1 -0
- package/dist/managers/backgroundTaskManager.js +249 -0
- package/dist/managers/bashManager.d.ts.map +1 -1
- package/dist/managers/bashManager.js +0 -3
- package/dist/managers/foregroundTaskManager.d.ts +9 -0
- package/dist/managers/foregroundTaskManager.d.ts.map +1 -0
- package/dist/managers/foregroundTaskManager.js +20 -0
- package/dist/managers/liveConfigManager.d.ts +1 -1
- package/dist/managers/liveConfigManager.d.ts.map +1 -1
- package/dist/managers/lspManager.d.ts.map +1 -1
- package/dist/managers/lspManager.js +3 -1
- package/dist/managers/messageManager.d.ts +34 -4
- package/dist/managers/messageManager.d.ts.map +1 -1
- package/dist/managers/messageManager.js +104 -13
- package/dist/managers/permissionManager.d.ts.map +1 -1
- package/dist/managers/permissionManager.js +11 -13
- package/dist/managers/pluginManager.d.ts.map +1 -1
- package/dist/managers/pluginManager.js +3 -2
- package/dist/managers/pluginScopeManager.d.ts +13 -2
- package/dist/managers/pluginScopeManager.d.ts.map +1 -1
- package/dist/managers/pluginScopeManager.js +38 -0
- package/dist/managers/reversionManager.d.ts +39 -0
- package/dist/managers/reversionManager.d.ts.map +1 -0
- package/dist/managers/reversionManager.js +118 -0
- package/dist/managers/slashCommandManager.d.ts +4 -1
- package/dist/managers/slashCommandManager.d.ts.map +1 -1
- package/dist/managers/slashCommandManager.js +16 -6
- package/dist/managers/subagentManager.d.ts +13 -2
- package/dist/managers/subagentManager.d.ts.map +1 -1
- package/dist/managers/subagentManager.js +144 -35
- package/dist/managers/toolManager.d.ts +11 -1
- package/dist/managers/toolManager.d.ts.map +1 -1
- package/dist/managers/toolManager.js +11 -3
- package/dist/services/GitService.d.ts.map +1 -1
- package/dist/services/GitService.js +6 -2
- package/dist/services/MarketplaceService.d.ts +14 -1
- package/dist/services/MarketplaceService.d.ts.map +1 -1
- package/dist/services/MarketplaceService.js +72 -4
- package/dist/services/MemoryRuleService.d.ts +1 -1
- package/dist/services/MemoryRuleService.d.ts.map +1 -1
- package/dist/services/MemoryRuleService.js +13 -2
- package/dist/services/aiService.js +1 -1
- package/dist/services/configurationService.d.ts +18 -2
- package/dist/services/configurationService.d.ts.map +1 -1
- package/dist/services/configurationService.js +62 -0
- package/dist/services/fileWatcher.d.ts +0 -5
- package/dist/services/fileWatcher.d.ts.map +1 -1
- package/dist/services/fileWatcher.js +0 -11
- package/dist/services/memory.js +1 -1
- package/dist/services/pluginLoader.d.ts.map +1 -1
- package/dist/services/pluginLoader.js +6 -1
- package/dist/services/reversionService.d.ts +24 -0
- package/dist/services/reversionService.d.ts.map +1 -0
- package/dist/services/reversionService.js +76 -0
- package/dist/services/session.d.ts +7 -0
- package/dist/services/session.d.ts.map +1 -1
- package/dist/services/session.js +126 -3
- package/dist/tools/bashTool.d.ts +0 -8
- package/dist/tools/bashTool.d.ts.map +1 -1
- package/dist/tools/bashTool.js +52 -174
- package/dist/tools/deleteFileTool.d.ts.map +1 -1
- package/dist/tools/deleteFileTool.js +9 -0
- package/dist/tools/editTool.d.ts.map +1 -1
- package/dist/tools/editTool.js +15 -4
- package/dist/tools/multiEditTool.d.ts.map +1 -1
- package/dist/tools/multiEditTool.js +16 -5
- package/dist/tools/taskOutputTool.d.ts +3 -0
- package/dist/tools/taskOutputTool.d.ts.map +1 -0
- package/dist/tools/taskOutputTool.js +149 -0
- package/dist/tools/taskStopTool.d.ts +3 -0
- package/dist/tools/taskStopTool.d.ts.map +1 -0
- package/dist/tools/taskStopTool.js +65 -0
- package/dist/tools/taskTool.d.ts.map +1 -1
- package/dist/tools/taskTool.js +105 -63
- package/dist/tools/types.d.ts +7 -0
- package/dist/tools/types.d.ts.map +1 -1
- package/dist/tools/writeTool.d.ts.map +1 -1
- package/dist/tools/writeTool.js +9 -0
- package/dist/types/commands.d.ts +1 -0
- package/dist/types/commands.d.ts.map +1 -1
- package/dist/types/configuration.d.ts +3 -0
- package/dist/types/configuration.d.ts.map +1 -1
- package/dist/types/environment.d.ts +2 -1
- package/dist/types/environment.d.ts.map +1 -1
- package/dist/types/environment.js +0 -6
- package/dist/types/history.d.ts +5 -0
- package/dist/types/history.d.ts.map +1 -0
- package/dist/types/history.js +1 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +1 -0
- package/dist/types/marketplace.d.ts +4 -0
- package/dist/types/marketplace.d.ts.map +1 -1
- package/dist/types/messaging.d.ts +7 -1
- package/dist/types/messaging.d.ts.map +1 -1
- package/dist/types/processes.d.ts +24 -4
- package/dist/types/processes.d.ts.map +1 -1
- package/dist/types/reversion.d.ts +29 -0
- package/dist/types/reversion.d.ts.map +1 -0
- package/dist/types/reversion.js +1 -0
- package/dist/utils/builtinSubagents.d.ts.map +1 -1
- package/dist/utils/builtinSubagents.js +16 -0
- package/dist/utils/constants.d.ts +2 -2
- package/dist/utils/constants.d.ts.map +1 -1
- package/dist/utils/constants.js +2 -2
- package/dist/utils/editUtils.d.ts +4 -9
- package/dist/utils/editUtils.d.ts.map +1 -1
- package/dist/utils/editUtils.js +54 -55
- package/dist/utils/messageOperations.d.ts +3 -1
- package/dist/utils/messageOperations.d.ts.map +1 -1
- package/dist/utils/messageOperations.js +8 -1
- package/dist/utils/openaiClient.d.ts.map +1 -1
- package/dist/utils/openaiClient.js +56 -26
- package/dist/utils/promptHistory.d.ts +20 -0
- package/dist/utils/promptHistory.d.ts.map +1 -0
- package/dist/utils/promptHistory.js +117 -0
- package/package.json +5 -3
- package/src/agent.ts +193 -109
- package/src/constants/prompts.ts +45 -5
- package/src/constants/tools.ts +2 -2
- package/src/index.ts +1 -1
- package/src/managers/MemoryRuleManager.ts +18 -2
- package/src/managers/aiManager.ts +87 -18
- package/src/managers/backgroundBashManager.ts +1 -0
- package/src/managers/backgroundTaskManager.ts +306 -0
- package/src/managers/bashManager.ts +0 -4
- package/src/managers/foregroundTaskManager.ts +26 -0
- package/src/managers/liveConfigManager.ts +2 -1
- package/src/managers/lspManager.ts +3 -1
- package/src/managers/messageManager.ts +136 -18
- package/src/managers/permissionManager.ts +11 -13
- package/src/managers/pluginManager.ts +4 -3
- package/src/managers/pluginScopeManager.ts +57 -8
- package/src/managers/reversionManager.ts +152 -0
- package/src/managers/slashCommandManager.ts +30 -7
- package/src/managers/subagentManager.ts +176 -31
- package/src/managers/toolManager.ts +23 -4
- package/src/services/GitService.ts +6 -2
- package/src/services/MarketplaceService.ts +100 -4
- package/src/services/MemoryRuleService.ts +18 -6
- package/src/services/aiService.ts +1 -1
- package/src/services/configurationService.ts +79 -1
- package/src/services/fileWatcher.ts +0 -13
- package/src/services/memory.ts +1 -1
- package/src/services/pluginLoader.ts +7 -1
- package/src/services/reversionService.ts +94 -0
- package/src/services/session.ts +161 -3
- package/src/tools/bashTool.ts +73 -200
- package/src/tools/deleteFileTool.ts +15 -0
- package/src/tools/editTool.ts +20 -10
- package/src/tools/multiEditTool.ts +21 -11
- package/src/tools/taskOutputTool.ts +174 -0
- package/src/tools/taskStopTool.ts +72 -0
- package/src/tools/taskTool.ts +130 -74
- package/src/tools/types.ts +7 -0
- package/src/tools/writeTool.ts +14 -0
- package/src/types/commands.ts +3 -0
- package/src/types/configuration.ts +4 -0
- package/src/types/environment.ts +3 -1
- package/src/types/history.ts +4 -0
- package/src/types/index.ts +1 -0
- package/src/types/marketplace.ts +5 -0
- package/src/types/messaging.ts +9 -1
- package/src/types/processes.ts +33 -4
- package/src/types/reversion.ts +29 -0
- package/src/utils/builtinSubagents.ts +18 -0
- package/src/utils/constants.ts +2 -2
- package/src/utils/editUtils.ts +66 -58
- package/src/utils/messageOperations.ts +10 -0
- package/src/utils/openaiClient.ts +69 -35
- package/src/utils/promptHistory.ts +133 -0
- package/dist/utils/bashHistory.d.ts +0 -50
- package/dist/utils/bashHistory.d.ts.map +0 -1
- package/dist/utils/bashHistory.js +0 -256
- package/src/utils/bashHistory.ts +0 -320
|
@@ -4,7 +4,6 @@ import {
|
|
|
4
4
|
addErrorBlockToMessage,
|
|
5
5
|
addUserMessageToMessages,
|
|
6
6
|
extractUserInputHistory,
|
|
7
|
-
addMemoryBlockToMessage,
|
|
8
7
|
addCommandOutputMessage,
|
|
9
8
|
updateCommandOutputInMessage,
|
|
10
9
|
completeCommandInMessage,
|
|
@@ -17,7 +16,7 @@ import {
|
|
|
17
16
|
type AgentToolBlockUpdateParams,
|
|
18
17
|
} from "../utils/messageOperations.js";
|
|
19
18
|
import type { SubagentConfiguration } from "../utils/subagentParser.js";
|
|
20
|
-
import type { Logger, Message, Usage } from "../types/index.js";
|
|
19
|
+
import type { Logger, Message, Usage, SlashCommand } from "../types/index.js";
|
|
21
20
|
import { join } from "path";
|
|
22
21
|
import {
|
|
23
22
|
appendMessages,
|
|
@@ -27,6 +26,7 @@ import {
|
|
|
27
26
|
SESSION_DIR,
|
|
28
27
|
} from "../services/session.js";
|
|
29
28
|
import { ChatCompletionMessageFunctionToolCall } from "openai/resources.js";
|
|
29
|
+
import type { MemoryRuleManager } from "./MemoryRuleManager.js";
|
|
30
30
|
import { pathEncoder } from "../utils/pathEncoder.js";
|
|
31
31
|
|
|
32
32
|
export interface MessageManagerCallbacks {
|
|
@@ -57,6 +57,10 @@ export interface MessageManagerCallbacks {
|
|
|
57
57
|
onAddCommandOutputMessage?: (command: string) => void;
|
|
58
58
|
onUpdateCommandOutputMessage?: (command: string, output: string) => void;
|
|
59
59
|
onCompleteCommandMessage?: (command: string, exitCode: number) => void;
|
|
60
|
+
onSlashCommandsChange?: (commands: SlashCommand[]) => void;
|
|
61
|
+
onInfoBlockAdded?: (content: string) => void;
|
|
62
|
+
// Rewind callbacks
|
|
63
|
+
onShowRewind?: () => void;
|
|
60
64
|
// Subagent callbacks
|
|
61
65
|
onSubAgentBlockAdded?: (
|
|
62
66
|
subagentId: string,
|
|
@@ -78,6 +82,7 @@ export interface MessageManagerOptions {
|
|
|
78
82
|
logger?: Logger;
|
|
79
83
|
sessionType?: "main" | "subagent";
|
|
80
84
|
subagentType?: string;
|
|
85
|
+
memoryRuleManager?: MemoryRuleManager;
|
|
81
86
|
}
|
|
82
87
|
|
|
83
88
|
export class MessageManager {
|
|
@@ -93,6 +98,7 @@ export class MessageManager {
|
|
|
93
98
|
private transcriptPath: string; // Cached transcript path
|
|
94
99
|
private savedMessageCount: number; // Track how many messages have been saved to prevent duplication
|
|
95
100
|
private filesInContext: Set<string> = new Set(); // Track files mentioned in the conversation
|
|
101
|
+
private memoryRuleManager?: MemoryRuleManager;
|
|
96
102
|
private sessionType: "main" | "subagent";
|
|
97
103
|
private subagentType?: string;
|
|
98
104
|
|
|
@@ -108,6 +114,7 @@ export class MessageManager {
|
|
|
108
114
|
this.savedMessageCount = 0; // Initialize saved message count tracker
|
|
109
115
|
this.sessionType = options.sessionType || "main";
|
|
110
116
|
this.subagentType = options.subagentType;
|
|
117
|
+
this.memoryRuleManager = options.memoryRuleManager;
|
|
111
118
|
|
|
112
119
|
// Compute and cache the transcript path
|
|
113
120
|
this.transcriptPath = this.computeTranscriptPath();
|
|
@@ -149,6 +156,30 @@ export class MessageManager {
|
|
|
149
156
|
return this.transcriptPath;
|
|
150
157
|
}
|
|
151
158
|
|
|
159
|
+
/**
|
|
160
|
+
* Get combined memory content (project memory + user memory + modular rules)
|
|
161
|
+
*/
|
|
162
|
+
public async getCombinedMemory(): Promise<string> {
|
|
163
|
+
const memory = await import("../services/memory.js");
|
|
164
|
+
let combined = await memory.getCombinedMemoryContent(this.workdir);
|
|
165
|
+
|
|
166
|
+
if (this.memoryRuleManager) {
|
|
167
|
+
const filesInContext = this.getFilesInContext();
|
|
168
|
+
const activeRules = this.memoryRuleManager.getActiveRules(filesInContext);
|
|
169
|
+
if (activeRules.length > 0) {
|
|
170
|
+
this.logger?.debug(
|
|
171
|
+
`Active modular rules (${activeRules.length}): ${activeRules.map((r) => r.id).join(", ")}`,
|
|
172
|
+
);
|
|
173
|
+
if (combined) {
|
|
174
|
+
combined += "\n\n";
|
|
175
|
+
}
|
|
176
|
+
combined += activeRules.map((r) => r.content).join("\n\n");
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return combined;
|
|
181
|
+
}
|
|
182
|
+
|
|
152
183
|
/**
|
|
153
184
|
* Compute the transcript path using cached encoded workdir
|
|
154
185
|
* Called during construction and when sessionId changes
|
|
@@ -249,6 +280,20 @@ export class MessageManager {
|
|
|
249
280
|
this.savedMessageCount = 0; // Reset saved message count
|
|
250
281
|
}
|
|
251
282
|
|
|
283
|
+
/**
|
|
284
|
+
* Trigger the rewind UI callback
|
|
285
|
+
*/
|
|
286
|
+
public triggerShowRewind(): void {
|
|
287
|
+
this.callbacks.onShowRewind?.();
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Trigger slash commands change callback
|
|
292
|
+
*/
|
|
293
|
+
public triggerSlashCommandsChange(commands: SlashCommand[]): void {
|
|
294
|
+
this.callbacks.onSlashCommandsChange?.(commands);
|
|
295
|
+
}
|
|
296
|
+
|
|
252
297
|
// Initialize state from session data
|
|
253
298
|
public initializeFromSession(sessionData: SessionData): void {
|
|
254
299
|
this.setSessionId(sessionData.id);
|
|
@@ -384,6 +429,18 @@ export class MessageManager {
|
|
|
384
429
|
this.callbacks.onErrorBlockAdded?.(error);
|
|
385
430
|
}
|
|
386
431
|
|
|
432
|
+
public addInfoBlock(content: string): void {
|
|
433
|
+
const lastMessage = this.messages[this.messages.length - 1];
|
|
434
|
+
if (lastMessage && lastMessage.role === "assistant") {
|
|
435
|
+
lastMessage.blocks.push({
|
|
436
|
+
type: "info",
|
|
437
|
+
content,
|
|
438
|
+
} as unknown as import("../types/index.js").MessageBlock);
|
|
439
|
+
this.setMessages([...this.messages]);
|
|
440
|
+
this.callbacks.onInfoBlockAdded?.(content);
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
|
|
387
444
|
/**
|
|
388
445
|
* Compress messages and update session, delete compressed messages, only keep compressed messages and subsequent messages
|
|
389
446
|
*/
|
|
@@ -427,21 +484,19 @@ export class MessageManager {
|
|
|
427
484
|
this.callbacks.onCompressBlockAdded?.(insertIndex, compressedContent);
|
|
428
485
|
}
|
|
429
486
|
|
|
430
|
-
public
|
|
431
|
-
|
|
432
|
-
success: boolean,
|
|
433
|
-
type: "project" | "user",
|
|
434
|
-
storagePath: string,
|
|
487
|
+
public addFileHistoryBlock(
|
|
488
|
+
snapshots: import("../types/reversion.js").FileSnapshot[],
|
|
435
489
|
): void {
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
490
|
+
if (snapshots.length === 0) return;
|
|
491
|
+
|
|
492
|
+
const lastMessage = this.messages[this.messages.length - 1];
|
|
493
|
+
if (lastMessage && lastMessage.role === "assistant") {
|
|
494
|
+
lastMessage.blocks.push({
|
|
495
|
+
type: "file_history",
|
|
496
|
+
snapshots,
|
|
497
|
+
} as unknown as import("../types/index.js").MessageBlock);
|
|
498
|
+
this.setMessages([...this.messages]);
|
|
499
|
+
}
|
|
445
500
|
}
|
|
446
501
|
|
|
447
502
|
// Bash command related message operations
|
|
@@ -480,12 +535,13 @@ export class MessageManager {
|
|
|
480
535
|
subagentName: string,
|
|
481
536
|
sessionId: string,
|
|
482
537
|
configuration: SubagentConfiguration,
|
|
483
|
-
status: "active" | "completed" | "error" = "active",
|
|
538
|
+
status: "active" | "completed" | "error" | "aborted" = "active",
|
|
484
539
|
parameters: {
|
|
485
540
|
description: string;
|
|
486
541
|
prompt: string;
|
|
487
542
|
subagent_type: string;
|
|
488
543
|
},
|
|
544
|
+
runInBackground?: boolean,
|
|
489
545
|
): void {
|
|
490
546
|
const params: AddSubagentBlockParams = {
|
|
491
547
|
messages: this.messages,
|
|
@@ -494,6 +550,7 @@ export class MessageManager {
|
|
|
494
550
|
sessionId,
|
|
495
551
|
status,
|
|
496
552
|
configuration,
|
|
553
|
+
runInBackground,
|
|
497
554
|
};
|
|
498
555
|
const updatedMessages = addSubagentBlockToMessage(params);
|
|
499
556
|
this.setMessages(updatedMessages);
|
|
@@ -505,6 +562,7 @@ export class MessageManager {
|
|
|
505
562
|
updates: Partial<{
|
|
506
563
|
status: "active" | "completed" | "error" | "aborted";
|
|
507
564
|
sessionId: string;
|
|
565
|
+
runInBackground: boolean;
|
|
508
566
|
}>,
|
|
509
567
|
): void {
|
|
510
568
|
const updatedMessages = updateSubagentBlockInMessage(
|
|
@@ -518,7 +576,9 @@ export class MessageManager {
|
|
|
518
576
|
subagentId,
|
|
519
577
|
status: updates.status || "active",
|
|
520
578
|
};
|
|
521
|
-
|
|
579
|
+
if (updates.status) {
|
|
580
|
+
this.callbacks.onSubAgentBlockUpdated?.(params.subagentId, params.status);
|
|
581
|
+
}
|
|
522
582
|
}
|
|
523
583
|
|
|
524
584
|
/**
|
|
@@ -643,6 +703,64 @@ export class MessageManager {
|
|
|
643
703
|
this.setMessages(newMessages);
|
|
644
704
|
}
|
|
645
705
|
|
|
706
|
+
/**
|
|
707
|
+
* Truncate history to a specific index and revert file changes.
|
|
708
|
+
* @param index - The index of the user message to truncate to.
|
|
709
|
+
* @param reversionManager - Optional ReversionManager to handle file rollbacks.
|
|
710
|
+
*/
|
|
711
|
+
public async truncateHistory(
|
|
712
|
+
index: number,
|
|
713
|
+
reversionManager?: import("./reversionManager.js").ReversionManager,
|
|
714
|
+
): Promise<void> {
|
|
715
|
+
if (index < 0 || index >= this.messages.length) {
|
|
716
|
+
throw new Error(`Invalid message index: ${index}`);
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
// Identify messages to be removed
|
|
720
|
+
const messagesToRemove = this.messages.slice(index);
|
|
721
|
+
const messageIdsToRemove = messagesToRemove
|
|
722
|
+
.map((m) => m.id as string)
|
|
723
|
+
.filter((id) => !!id);
|
|
724
|
+
|
|
725
|
+
// Revert file changes if manager is provided
|
|
726
|
+
if (reversionManager && messageIdsToRemove.length > 0) {
|
|
727
|
+
await reversionManager.revertTo(messageIdsToRemove, this.messages);
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
// Truncate messages in memory
|
|
731
|
+
const newMessages = this.messages.slice(0, index);
|
|
732
|
+
this.setMessages(newMessages);
|
|
733
|
+
|
|
734
|
+
// Update persistence: rewrite the session file
|
|
735
|
+
await this.rewriteSessionFile(newMessages);
|
|
736
|
+
|
|
737
|
+
// Update saved message count
|
|
738
|
+
this.savedMessageCount = newMessages.length;
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
/**
|
|
742
|
+
* Rewrite the session file with the current messages.
|
|
743
|
+
*/
|
|
744
|
+
private async rewriteSessionFile(messages: Message[]): Promise<void> {
|
|
745
|
+
try {
|
|
746
|
+
const { writeFile } = await import("fs/promises");
|
|
747
|
+
|
|
748
|
+
const sessionMessages: import("../types/session.js").SessionMessage[] =
|
|
749
|
+
messages.map((message) => ({
|
|
750
|
+
...message,
|
|
751
|
+
timestamp: new Date().toISOString(),
|
|
752
|
+
}));
|
|
753
|
+
|
|
754
|
+
const content =
|
|
755
|
+
sessionMessages.map((m) => JSON.stringify(m)).join("\n") +
|
|
756
|
+
(sessionMessages.length > 0 ? "\n" : "");
|
|
757
|
+
|
|
758
|
+
await writeFile(this.transcriptPath, content, "utf8");
|
|
759
|
+
} catch (error) {
|
|
760
|
+
this.logger?.error("Failed to rewrite session file:", error);
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
|
|
646
764
|
/**
|
|
647
765
|
* Updates the set of files mentioned in the conversation.
|
|
648
766
|
*/
|
|
@@ -237,13 +237,7 @@ export class PermissionManager {
|
|
|
237
237
|
* Get the current effective permission mode for tool execution context
|
|
238
238
|
*/
|
|
239
239
|
getCurrentEffectiveMode(cliPermissionMode?: PermissionMode): PermissionMode {
|
|
240
|
-
|
|
241
|
-
this.logger?.debug("getCurrentEffectiveMode", {
|
|
242
|
-
cliPermissionMode,
|
|
243
|
-
configuredDefaultMode: this.configuredDefaultMode,
|
|
244
|
-
resolvedMode: mode,
|
|
245
|
-
});
|
|
246
|
-
return mode;
|
|
240
|
+
return this.resolveEffectivePermissionMode(cliPermissionMode);
|
|
247
241
|
}
|
|
248
242
|
|
|
249
243
|
/**
|
|
@@ -558,7 +552,7 @@ export class PermissionManager {
|
|
|
558
552
|
return true;
|
|
559
553
|
}
|
|
560
554
|
|
|
561
|
-
// 2. Tool with pattern match (e.g., "Bash(rm
|
|
555
|
+
// 2. Tool with pattern match (e.g., "Bash(rm *)", "Read(**/*.env)")
|
|
562
556
|
const match = rule.match(/^(\w+)\((.*)\)$/);
|
|
563
557
|
if (!match) {
|
|
564
558
|
return false;
|
|
@@ -573,10 +567,14 @@ export class PermissionManager {
|
|
|
573
567
|
if (toolName === BASH_TOOL_NAME) {
|
|
574
568
|
const command = String(context.toolInput?.command || "");
|
|
575
569
|
const processedPart = stripRedirections(stripEnvVars(command));
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
570
|
+
// For Bash commands, we want '*' to match everything including slashes and spaces
|
|
571
|
+
// minimatch's default behavior for '*' is to not match across directory separators
|
|
572
|
+
// We use a regex to replace '*' with '.*' (match anything)
|
|
573
|
+
const regexPattern = pattern
|
|
574
|
+
.replace(/[.+^${}()|[\]\\?]/g, "\\$&") // Escape regex special chars including ?
|
|
575
|
+
.replace(/\*/g, ".*"); // Replace * with .*
|
|
576
|
+
const regex = new RegExp(`^${regexPattern}$`);
|
|
577
|
+
return regex.test(processedPart);
|
|
580
578
|
}
|
|
581
579
|
|
|
582
580
|
// Handle path-based rules (e.g., "Read(**/*.env)")
|
|
@@ -768,7 +766,7 @@ export class PermissionManager {
|
|
|
768
766
|
|
|
769
767
|
const smartPrefix = getSmartPrefix(processedPart);
|
|
770
768
|
if (smartPrefix) {
|
|
771
|
-
rules.push(`Bash(${smartPrefix}
|
|
769
|
+
rules.push(`Bash(${smartPrefix}*)`);
|
|
772
770
|
} else {
|
|
773
771
|
rules.push(`Bash(${processedPart})`);
|
|
774
772
|
}
|
|
@@ -149,9 +149,7 @@ export class PluginManager {
|
|
|
149
149
|
* @param configs Array of plugin configurations
|
|
150
150
|
*/
|
|
151
151
|
async loadPlugins(configs: PluginConfig[]): Promise<void> {
|
|
152
|
-
// Load
|
|
153
|
-
await this.loadInstalledPlugins();
|
|
154
|
-
|
|
152
|
+
// Load plugins from configuration (e.g. --plugin-dir) first to give them higher priority
|
|
155
153
|
for (const config of configs) {
|
|
156
154
|
if (config.type !== "local") {
|
|
157
155
|
this.logger?.warn(`Unsupported plugin type: ${config.type}`);
|
|
@@ -164,6 +162,9 @@ export class PluginManager {
|
|
|
164
162
|
|
|
165
163
|
await this.loadSinglePlugin(absolutePath);
|
|
166
164
|
}
|
|
165
|
+
|
|
166
|
+
// Load installed plugins from marketplace
|
|
167
|
+
await this.loadInstalledPlugins();
|
|
167
168
|
}
|
|
168
169
|
|
|
169
170
|
/**
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ConfigurationService } from "../services/configurationService.js";
|
|
2
2
|
import { PluginManager } from "./pluginManager.js";
|
|
3
3
|
import { Logger } from "../types/index.js";
|
|
4
|
+
import { Scope } from "../types/configuration.js";
|
|
4
5
|
|
|
5
6
|
export interface PluginScopeManagerOptions {
|
|
6
7
|
workdir: string;
|
|
@@ -25,10 +26,7 @@ export class PluginScopeManager {
|
|
|
25
26
|
/**
|
|
26
27
|
* Enable a plugin in the specified scope
|
|
27
28
|
*/
|
|
28
|
-
async enablePlugin(
|
|
29
|
-
scope: "user" | "project" | "local",
|
|
30
|
-
pluginId: string,
|
|
31
|
-
): Promise<void> {
|
|
29
|
+
async enablePlugin(scope: Scope, pluginId: string): Promise<void> {
|
|
32
30
|
await this.configurationService.updateEnabledPlugin(
|
|
33
31
|
this.workdir,
|
|
34
32
|
scope,
|
|
@@ -42,10 +40,7 @@ export class PluginScopeManager {
|
|
|
42
40
|
/**
|
|
43
41
|
* Disable a plugin in the specified scope
|
|
44
42
|
*/
|
|
45
|
-
async disablePlugin(
|
|
46
|
-
scope: "user" | "project" | "local",
|
|
47
|
-
pluginId: string,
|
|
48
|
-
): Promise<void> {
|
|
43
|
+
async disablePlugin(scope: Scope, pluginId: string): Promise<void> {
|
|
49
44
|
await this.configurationService.updateEnabledPlugin(
|
|
50
45
|
this.workdir,
|
|
51
46
|
scope,
|
|
@@ -63,6 +58,60 @@ export class PluginScopeManager {
|
|
|
63
58
|
return this.configurationService.getMergedEnabledPlugins(this.workdir);
|
|
64
59
|
}
|
|
65
60
|
|
|
61
|
+
/**
|
|
62
|
+
* Find the scope where a plugin is currently enabled/disabled.
|
|
63
|
+
* Priority: local > project > user
|
|
64
|
+
*/
|
|
65
|
+
findPluginScope(pluginId: string): Scope | null {
|
|
66
|
+
const projectPaths = this.configurationService.getConfigurationPaths(
|
|
67
|
+
this.workdir,
|
|
68
|
+
).projectPaths; // [local, json]
|
|
69
|
+
const userPaths = this.configurationService.getConfigurationPaths(
|
|
70
|
+
this.workdir,
|
|
71
|
+
).userPaths; // [local, json]
|
|
72
|
+
|
|
73
|
+
const checkPaths: { path: string; scope: Scope }[] = [
|
|
74
|
+
{ path: projectPaths[0], scope: "local" },
|
|
75
|
+
{ path: projectPaths[1], scope: "project" },
|
|
76
|
+
{ path: userPaths[0], scope: "user" }, // user local is still user scope
|
|
77
|
+
{ path: userPaths[1], scope: "user" },
|
|
78
|
+
];
|
|
79
|
+
|
|
80
|
+
for (const { path, scope } of checkPaths) {
|
|
81
|
+
const config = this.configurationService.loadWaveConfigFromFile(path);
|
|
82
|
+
if (config?.enabledPlugins && pluginId in config.enabledPlugins) {
|
|
83
|
+
return scope;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Remove a plugin from all scopes (user, project, local)
|
|
92
|
+
* This is useful when uninstalling a plugin to clean up all configuration
|
|
93
|
+
*/
|
|
94
|
+
async removePluginFromAllScopes(pluginId: string): Promise<void> {
|
|
95
|
+
const scopes: Scope[] = ["user", "project", "local"];
|
|
96
|
+
|
|
97
|
+
for (const scope of scopes) {
|
|
98
|
+
try {
|
|
99
|
+
await this.configurationService.removeEnabledPlugin(
|
|
100
|
+
this.workdir,
|
|
101
|
+
scope,
|
|
102
|
+
pluginId,
|
|
103
|
+
);
|
|
104
|
+
} catch (error) {
|
|
105
|
+
// Continue removing from other scopes even if one fails
|
|
106
|
+
this.logger?.warn(
|
|
107
|
+
`Failed to remove plugin ${pluginId} from ${scope} scope: ${error instanceof Error ? error.message : String(error)}`,
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
this.refreshPluginManager();
|
|
113
|
+
}
|
|
114
|
+
|
|
66
115
|
/**
|
|
67
116
|
* Refresh the plugin manager with the latest configuration
|
|
68
117
|
* Note: This only updates the configuration, it doesn't reload plugins.
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import fs from "fs/promises";
|
|
2
|
+
import { FileSnapshot } from "../types/reversion.js";
|
|
3
|
+
import { ReversionService } from "../services/reversionService.js";
|
|
4
|
+
|
|
5
|
+
export class ReversionManager {
|
|
6
|
+
private buffer: Map<string, FileSnapshot> = new Map();
|
|
7
|
+
private reversionService: ReversionService;
|
|
8
|
+
|
|
9
|
+
constructor(reversionService: ReversionService) {
|
|
10
|
+
this.reversionService = reversionService;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Records the current state of a file into a temporary buffer.
|
|
15
|
+
* Returns a snapshotId.
|
|
16
|
+
*/
|
|
17
|
+
async recordSnapshot(
|
|
18
|
+
messageId: string,
|
|
19
|
+
filePath: string,
|
|
20
|
+
operation: "create" | "modify" | "delete",
|
|
21
|
+
): Promise<string> {
|
|
22
|
+
let content: string | null = null;
|
|
23
|
+
try {
|
|
24
|
+
content = await fs.readFile(filePath, "utf-8");
|
|
25
|
+
} catch {
|
|
26
|
+
// File doesn't exist, which is expected for 'create' operation
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const snapshot: FileSnapshot = {
|
|
30
|
+
messageId,
|
|
31
|
+
filePath,
|
|
32
|
+
timestamp: Date.now(),
|
|
33
|
+
operation,
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
// We temporarily store the content in the buffer, it will be saved to disk on commit
|
|
37
|
+
const snapshotId = `${messageId}-${filePath}-${snapshot.timestamp}`;
|
|
38
|
+
this.buffer.set(snapshotId, { ...snapshot, content } as FileSnapshot & {
|
|
39
|
+
content: string | null;
|
|
40
|
+
});
|
|
41
|
+
return snapshotId;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Records the current state of a file into a temporary buffer.
|
|
46
|
+
* Returns a snapshotId.
|
|
47
|
+
*/
|
|
48
|
+
async recordSnapshotWithId(
|
|
49
|
+
messageId: string,
|
|
50
|
+
filePath: string,
|
|
51
|
+
operation: "create" | "modify" | "delete",
|
|
52
|
+
): Promise<string> {
|
|
53
|
+
return this.recordSnapshot(messageId, filePath, operation);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Moves the buffered snapshot to the permanent session log.
|
|
58
|
+
* Called only if the tool succeeds.
|
|
59
|
+
*/
|
|
60
|
+
async commitSnapshot(snapshotId: string): Promise<void> {
|
|
61
|
+
const snapshotWithContent = this.buffer.get(snapshotId) as FileSnapshot & {
|
|
62
|
+
content: string | null;
|
|
63
|
+
};
|
|
64
|
+
if (snapshotWithContent) {
|
|
65
|
+
const { content, ...snapshot } = snapshotWithContent;
|
|
66
|
+
const snapshotPath = await this.reversionService.saveSnapshot({
|
|
67
|
+
...snapshot,
|
|
68
|
+
content,
|
|
69
|
+
} as FileSnapshot);
|
|
70
|
+
snapshot.snapshotPath = snapshotPath;
|
|
71
|
+
this.buffer.delete(snapshotId);
|
|
72
|
+
|
|
73
|
+
// We need to return the snapshot so it can be added to the message block
|
|
74
|
+
// But the current API doesn't support it.
|
|
75
|
+
// Let's store committed snapshots in another buffer for the current turn.
|
|
76
|
+
this.committedSnapshots.push(snapshot);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
private committedSnapshots: FileSnapshot[] = [];
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Gets and clears committed snapshots for the current turn.
|
|
84
|
+
*/
|
|
85
|
+
getAndClearCommittedSnapshots(): FileSnapshot[] {
|
|
86
|
+
const snapshots = [...this.committedSnapshots];
|
|
87
|
+
this.committedSnapshots = [];
|
|
88
|
+
return snapshots;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Discards the buffered snapshot.
|
|
93
|
+
* Called if the tool fails.
|
|
94
|
+
*/
|
|
95
|
+
discardSnapshot(snapshotId: string): void {
|
|
96
|
+
this.buffer.delete(snapshotId);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Reverts all file changes associated with messages from the end of history
|
|
101
|
+
* down to (and including) the specified message index.
|
|
102
|
+
* This should be called by MessageManager.
|
|
103
|
+
*/
|
|
104
|
+
async revertTo(
|
|
105
|
+
messageIds: string[],
|
|
106
|
+
allMessages: import("../types/index.js").Message[],
|
|
107
|
+
): Promise<number> {
|
|
108
|
+
const messageIdSet = new Set(messageIds);
|
|
109
|
+
const snapshots: FileSnapshot[] = [];
|
|
110
|
+
|
|
111
|
+
for (const message of allMessages) {
|
|
112
|
+
if (message.id && messageIdSet.has(message.id)) {
|
|
113
|
+
const historyBlock = message.blocks.find(
|
|
114
|
+
(b) => b.type === "file_history",
|
|
115
|
+
) as { type: "file_history"; snapshots: FileSnapshot[] } | undefined;
|
|
116
|
+
if (historyBlock && historyBlock.snapshots) {
|
|
117
|
+
snapshots.push(...historyBlock.snapshots);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Revert in reverse chronological order (LIFO)
|
|
123
|
+
const sortedSnapshots = snapshots.sort((a, b) => b.timestamp - a.timestamp);
|
|
124
|
+
|
|
125
|
+
let revertedCount = 0;
|
|
126
|
+
for (const snapshot of sortedSnapshots) {
|
|
127
|
+
try {
|
|
128
|
+
if (!snapshot.snapshotPath) {
|
|
129
|
+
// File didn't exist before, so delete it
|
|
130
|
+
await fs.rm(snapshot.filePath, { force: true });
|
|
131
|
+
} else {
|
|
132
|
+
// Restore previous content
|
|
133
|
+
const content = await this.reversionService.readSnapshotContent(
|
|
134
|
+
snapshot.snapshotPath,
|
|
135
|
+
);
|
|
136
|
+
if (content !== null) {
|
|
137
|
+
await fs.writeFile(snapshot.filePath, content, "utf-8");
|
|
138
|
+
} else {
|
|
139
|
+
// If snapshotPath exists but content is null, it means the file should be deleted
|
|
140
|
+
// (This handles the case where snapshotPath was set but content was null in saveSnapshot)
|
|
141
|
+
await fs.rm(snapshot.filePath, { force: true });
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
revertedCount++;
|
|
145
|
+
} catch (error) {
|
|
146
|
+
console.error(`Failed to revert file ${snapshot.filePath}:`, error);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return revertedCount;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { MessageManager } from "./messageManager.js";
|
|
2
2
|
import type { AIManager } from "./aiManager.js";
|
|
3
|
+
import type { BackgroundTaskManager } from "./backgroundTaskManager.js";
|
|
3
4
|
import type {
|
|
4
5
|
SlashCommand,
|
|
5
6
|
CustomSlashCommand,
|
|
@@ -26,6 +27,7 @@ const execAsync = promisify(exec);
|
|
|
26
27
|
export interface SlashCommandManagerOptions {
|
|
27
28
|
messageManager: MessageManager;
|
|
28
29
|
aiManager: AIManager;
|
|
30
|
+
backgroundTaskManager: BackgroundTaskManager;
|
|
29
31
|
workdir: string;
|
|
30
32
|
logger?: Logger;
|
|
31
33
|
}
|
|
@@ -35,12 +37,14 @@ export class SlashCommandManager {
|
|
|
35
37
|
private customCommands = new Map<string, CustomSlashCommand>();
|
|
36
38
|
private messageManager: MessageManager;
|
|
37
39
|
private aiManager: AIManager;
|
|
40
|
+
private backgroundTaskManager: BackgroundTaskManager;
|
|
38
41
|
private workdir: string;
|
|
39
42
|
private logger?: Logger;
|
|
40
43
|
|
|
41
44
|
constructor(options: SlashCommandManagerOptions) {
|
|
42
45
|
this.messageManager = options.messageManager;
|
|
43
46
|
this.aiManager = options.aiManager;
|
|
47
|
+
this.backgroundTaskManager = options.backgroundTaskManager;
|
|
44
48
|
this.workdir = options.workdir;
|
|
45
49
|
this.logger = options.logger;
|
|
46
50
|
|
|
@@ -104,15 +108,24 @@ export class SlashCommandManager {
|
|
|
104
108
|
handler: async (args?: string) => {
|
|
105
109
|
// Substitute parameters in the command content
|
|
106
110
|
let processedContent = command.content;
|
|
111
|
+
|
|
112
|
+
// Substitute $WAVE_PLUGIN_ROOT placeholder for plugin commands
|
|
113
|
+
if (command.pluginPath) {
|
|
114
|
+
processedContent = processedContent.replace(
|
|
115
|
+
/\$WAVE_PLUGIN_ROOT/g,
|
|
116
|
+
command.pluginPath,
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
|
|
107
120
|
if (args) {
|
|
108
|
-
if (hasParameterPlaceholders(
|
|
121
|
+
if (hasParameterPlaceholders(processedContent)) {
|
|
109
122
|
processedContent = substituteCommandParameters(
|
|
110
|
-
|
|
123
|
+
processedContent,
|
|
111
124
|
args,
|
|
112
125
|
);
|
|
113
126
|
} else {
|
|
114
127
|
// If no placeholders, append arguments to the content
|
|
115
|
-
processedContent = `${
|
|
128
|
+
processedContent = `${processedContent.trim()} ${args}`;
|
|
116
129
|
}
|
|
117
130
|
}
|
|
118
131
|
|
|
@@ -158,15 +171,24 @@ export class SlashCommandManager {
|
|
|
158
171
|
handler: async (args?: string) => {
|
|
159
172
|
// Substitute parameters in the command content
|
|
160
173
|
let processedContent = command.content;
|
|
174
|
+
|
|
175
|
+
// Substitute $WAVE_PLUGIN_ROOT placeholder for plugin commands
|
|
176
|
+
if (command.pluginPath) {
|
|
177
|
+
processedContent = processedContent.replace(
|
|
178
|
+
/\$WAVE_PLUGIN_ROOT/g,
|
|
179
|
+
command.pluginPath,
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
|
|
161
183
|
if (args) {
|
|
162
|
-
if (hasParameterPlaceholders(
|
|
184
|
+
if (hasParameterPlaceholders(processedContent)) {
|
|
163
185
|
processedContent = substituteCommandParameters(
|
|
164
|
-
|
|
186
|
+
processedContent,
|
|
165
187
|
args,
|
|
166
188
|
);
|
|
167
189
|
} else {
|
|
168
190
|
// If no placeholders, append arguments to the content
|
|
169
|
-
processedContent = `${
|
|
191
|
+
processedContent = `${processedContent.trim()} ${args}`;
|
|
170
192
|
}
|
|
171
193
|
}
|
|
172
194
|
|
|
@@ -202,8 +224,9 @@ export class SlashCommandManager {
|
|
|
202
224
|
/**
|
|
203
225
|
* Register new command
|
|
204
226
|
*/
|
|
205
|
-
|
|
227
|
+
public registerCommand(command: SlashCommand): void {
|
|
206
228
|
this.commands.set(command.id, command);
|
|
229
|
+
this.messageManager.triggerSlashCommandsChange(this.getCommands());
|
|
207
230
|
}
|
|
208
231
|
|
|
209
232
|
/**
|