wave-agent-sdk 1.1.4 → 1.2.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 +72 -24
- package/dist/agent.js +123 -31
- package/dist/builtin/plugins.js +17 -2
- package/dist/builtin/skills/settings.js +6 -8
- package/dist/core/plugin.d.ts +4 -0
- package/dist/core/plugin.js +7 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/managers/aiManager.d.ts +0 -24
- package/dist/managers/aiManager.js +23 -181
- package/dist/managers/bashModeManager.d.ts +33 -0
- package/dist/managers/bashModeManager.js +110 -0
- package/dist/managers/hookManager.d.ts +5 -0
- package/dist/managers/hookManager.js +7 -0
- package/dist/managers/liveConfigManager.js +3 -3
- package/dist/managers/mcpManager.d.ts +23 -0
- package/dist/managers/mcpManager.js +162 -14
- package/dist/managers/messageManager.d.ts +12 -13
- package/dist/managers/messageManager.js +63 -60
- package/dist/managers/permissionManager.d.ts +29 -0
- package/dist/managers/permissionManager.js +158 -70
- package/dist/managers/planManager.d.ts +9 -0
- package/dist/managers/planManager.js +19 -1
- package/dist/managers/skillManager.d.ts +31 -0
- package/dist/managers/skillManager.js +122 -12
- package/dist/managers/slashCommandManager.js +9 -28
- package/dist/managers/subagentManager.d.ts +7 -0
- package/dist/managers/subagentManager.js +61 -9
- package/dist/managers/workflowManager.js +6 -0
- package/dist/prompts/index.d.ts +0 -1
- package/dist/prompts/index.js +0 -4
- package/dist/services/MarketplaceService.js +36 -14
- package/dist/services/configurationService.d.ts +34 -2
- package/dist/services/configurationService.js +123 -16
- package/dist/services/initializationService.js +2 -2
- package/dist/services/jsonlHandler.d.ts +14 -0
- package/dist/services/jsonlHandler.js +44 -1
- package/dist/services/memory.d.ts +14 -0
- package/dist/services/memory.js +33 -0
- package/dist/services/officialMarketplaceMirror.d.ts +85 -0
- package/dist/services/officialMarketplaceMirror.js +289 -0
- package/dist/services/remoteSettingsService.js +4 -4
- package/dist/services/session.js +30 -13
- package/dist/services/worktreeHooks.js +6 -1
- package/dist/stdio/index.d.ts +10 -0
- package/dist/stdio/index.js +10 -0
- package/dist/stdio/notificationRouter.d.ts +38 -0
- package/dist/stdio/notificationRouter.js +96 -0
- package/dist/stdio/rpcClient.d.ts +18 -0
- package/dist/stdio/rpcClient.js +10 -0
- package/dist/stdio/stdioAgent.d.ts +222 -0
- package/dist/stdio/stdioAgent.js +341 -0
- package/dist/tools/bashTool.js +2 -0
- package/dist/tools/exitPlanMode.js +10 -2
- package/dist/types/agent.d.ts +8 -0
- package/dist/types/commands.d.ts +7 -0
- package/dist/types/configuration.d.ts +6 -1
- package/dist/types/hooks.d.ts +1 -0
- package/dist/types/hooks.js +19 -0
- package/dist/types/mcp.d.ts +3 -0
- package/dist/types/messaging.d.ts +1 -8
- package/dist/types/skills.d.ts +11 -0
- package/dist/utils/bashParser.d.ts +17 -0
- package/dist/utils/bashParser.js +72 -0
- package/dist/utils/bashStructure/bashLexer.d.ts +96 -0
- package/dist/utils/bashStructure/bashLexer.js +676 -0
- package/dist/utils/bashStructure/bashParser.d.ts +144 -0
- package/dist/utils/bashStructure/bashParser.js +606 -0
- package/dist/utils/bashStructure/bashSemantics.d.ts +70 -0
- package/dist/utils/bashStructure/bashSemantics.js +477 -0
- package/dist/utils/bashStructure/index.d.ts +26 -0
- package/dist/utils/bashStructure/index.js +27 -0
- package/dist/utils/bashStructure/types.d.ts +62 -0
- package/dist/utils/bashStructure/types.js +47 -0
- package/dist/utils/container.d.ts +6 -0
- package/dist/utils/container.js +9 -0
- package/dist/utils/containerSetup.d.ts +11 -1
- package/dist/utils/containerSetup.js +26 -11
- package/dist/utils/fileUtils.d.ts +11 -0
- package/dist/utils/fileUtils.js +37 -0
- package/dist/utils/messageOperations.d.ts +0 -18
- package/dist/utils/messageOperations.js +0 -62
- package/dist/utils/subagentParser.js +9 -2
- package/dist/utils/tokenCalculation.js +0 -8
- package/dist/utils/worktreeUtils.d.ts +2 -1
- package/dist/utils/worktreeUtils.js +64 -34
- package/package.json +6 -1
- package/dist/managers/bangManager.d.ts +0 -26
- package/dist/managers/bangManager.js +0 -78
package/dist/agent.d.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { type QueuedMessage } from "./managers/messageQueue.js";
|
|
2
2
|
import { SlashCommand, CustomSlashCommand, AgentOptions } from "./types/index.js";
|
|
3
3
|
import type { Message, McpServerStatus, GatewayConfig, ModelConfig, Usage, PermissionMode, ForegroundTask, SkillMetadata } from "./types/index.js";
|
|
4
|
+
import type { HookEvent, HookEventConfig } from "./types/hooks.js";
|
|
4
5
|
import type { WorktreeSession } from "./utils/worktreeSession.js";
|
|
5
6
|
export declare class Agent {
|
|
6
7
|
private messageManager;
|
|
7
8
|
private aiManager;
|
|
8
|
-
private
|
|
9
|
+
private bashModeManager;
|
|
9
10
|
private backgroundTaskManager;
|
|
10
11
|
private logger?;
|
|
11
12
|
private toolManager;
|
|
@@ -31,6 +32,8 @@ export declare class Agent {
|
|
|
31
32
|
private taskManager;
|
|
32
33
|
private foregroundTaskManager;
|
|
33
34
|
private container;
|
|
35
|
+
/** Unregister module-level listeners registered during container setup; invoked at the end of destroy(). */
|
|
36
|
+
private teardown;
|
|
34
37
|
private configurationService;
|
|
35
38
|
private workdir;
|
|
36
39
|
private systemPrompt?;
|
|
@@ -63,6 +66,10 @@ export declare class Agent {
|
|
|
63
66
|
private constructor();
|
|
64
67
|
get sessionId(): string;
|
|
65
68
|
get messages(): Message[];
|
|
69
|
+
/** Full UI display message stream (includes pre-compaction history).
|
|
70
|
+
* Consumed by hosts for the full-list message view (getMessages RPC,
|
|
71
|
+
* CLI structural refresh); the API context stays on `messages`. */
|
|
72
|
+
get displayMessages(): Message[];
|
|
66
73
|
get usages(): Usage[];
|
|
67
74
|
get sessionFilePath(): string;
|
|
68
75
|
get latestTotalTokens(): number;
|
|
@@ -85,6 +92,21 @@ export declare class Agent {
|
|
|
85
92
|
get userMemory(): string;
|
|
86
93
|
/** Get combined memory content (project + user + modular rules) */
|
|
87
94
|
getCombinedMemory(): Promise<string>;
|
|
95
|
+
/**
|
|
96
|
+
* Read the user-level memory file (~/.wave/AGENTS.md). Unlike the cached
|
|
97
|
+
* `userMemory` getter this always reads fresh content (the settings UI edits
|
|
98
|
+
* the file while the agent may already hold a cache).
|
|
99
|
+
*/
|
|
100
|
+
readUserMemoryContent(): Promise<string>;
|
|
101
|
+
/**
|
|
102
|
+
* Read the project-level memory file (<workdir>/AGENTS.md, CLAUDE.md
|
|
103
|
+
* fallback). Always reads fresh content for the settings UI.
|
|
104
|
+
*/
|
|
105
|
+
readProjectMemoryContent(workdir: string): Promise<string>;
|
|
106
|
+
/** Persist the user-level memory file (~/.wave/AGENTS.md). */
|
|
107
|
+
writeUserMemoryContent(content: string): Promise<void>;
|
|
108
|
+
/** Persist the project-level memory file (<workdir>/AGENTS.md). */
|
|
109
|
+
writeProjectMemoryContent(workdir: string, content: string): Promise<void>;
|
|
88
110
|
/** Get AI loading status */
|
|
89
111
|
get isLoading(): boolean;
|
|
90
112
|
/** Get message compaction status */
|
|
@@ -227,7 +249,7 @@ export declare class Agent {
|
|
|
227
249
|
*/
|
|
228
250
|
restoreSession(sessionId: string): Promise<void>;
|
|
229
251
|
abortAIMessage(): void;
|
|
230
|
-
/** Execute bash command (
|
|
252
|
+
/** Execute a bash-mode command (`!command`) */
|
|
231
253
|
bang(command: string): Promise<void>;
|
|
232
254
|
clearMessages(): Promise<void>;
|
|
233
255
|
/**
|
|
@@ -272,28 +294,6 @@ export declare class Agent {
|
|
|
272
294
|
* @returns Promise that resolves to the AI's answer
|
|
273
295
|
*/
|
|
274
296
|
askBtw(question: string, abortSignal?: AbortSignal, onContent?: (content: string) => void, onReasoning?: (content: string) => void): Promise<string>;
|
|
275
|
-
/**
|
|
276
|
-
* Start a fork subagent in the background (the "/subtask" command path).
|
|
277
|
-
*
|
|
278
|
-
* The fork inherits the parent's full conversation context — same system
|
|
279
|
-
* prompt, tools, model, and message prefix — so the prompt cache is reused,
|
|
280
|
-
* then works independently in the background. On completion its final
|
|
281
|
-
* response is delivered back to the main conversation as a task notification
|
|
282
|
-
* carrying the `<result>` tag. Recursion is prevented inside the fork (the
|
|
283
|
-
* Agent tool and Task tools are denied).
|
|
284
|
-
*
|
|
285
|
-
* @param prompt - The task description for the fork subagent
|
|
286
|
-
* @param options - Background task label and optional loose turn bound
|
|
287
|
-
* @param options.description - Display label for the background task
|
|
288
|
-
* @param options.maxTurns - Loose turn bound (defaults to 200, aligned with
|
|
289
|
-
* Claude Code's fork subagent)
|
|
290
|
-
* @param abortSignal - Optional signal to abort the fork
|
|
291
|
-
* @returns Promise that resolves to the background task ID
|
|
292
|
-
*/
|
|
293
|
-
forkSubagent(prompt: string, options: {
|
|
294
|
-
description: string;
|
|
295
|
-
maxTurns?: number;
|
|
296
|
-
}, abortSignal?: AbortSignal): Promise<string>;
|
|
297
297
|
/**
|
|
298
298
|
* Send a message to the AI agent with optional images
|
|
299
299
|
*
|
|
@@ -349,6 +349,15 @@ export declare class Agent {
|
|
|
349
349
|
connectMcpServer(serverName: string): Promise<boolean>;
|
|
350
350
|
/** Disconnect MCP server */
|
|
351
351
|
disconnectMcpServer(serverName: string): Promise<boolean>;
|
|
352
|
+
/**
|
|
353
|
+
* Remove an MCP server from a persisted config file (user ~/.wave/mcp.json
|
|
354
|
+
* or project <workdir>/.mcp.json), then disconnect and drop it from memory.
|
|
355
|
+
*/
|
|
356
|
+
removeMcpServer(scope: "user" | "project", serverName: string): Promise<boolean>;
|
|
357
|
+
/** The user-level MCP config path (~/.wave/mcp.json) */
|
|
358
|
+
getUserMcpConfigPath(): string;
|
|
359
|
+
/** The project-level MCP config path (<workdir>/.mcp.json) */
|
|
360
|
+
getProjectMcpConfigPath(): string;
|
|
352
361
|
/** Get all available slash commands */
|
|
353
362
|
getSlashCommands(): SlashCommand[];
|
|
354
363
|
/** Check if slash command exists */
|
|
@@ -393,6 +402,14 @@ export declare class Agent {
|
|
|
393
402
|
* Get the current plan file path (for testing and UI)
|
|
394
403
|
*/
|
|
395
404
|
getPlanFilePath(): string | undefined;
|
|
405
|
+
/**
|
|
406
|
+
* Resolve once the plan file path has been generated and set after entering
|
|
407
|
+
* plan mode. Resolves `undefined` when no generation is in flight.
|
|
408
|
+
* Used by the CLI /plan command and the stdio getPlanFile RPC to avoid
|
|
409
|
+
* querying the model before the path is known (the plan mode reminder needs
|
|
410
|
+
* the path to be set).
|
|
411
|
+
*/
|
|
412
|
+
awaitPlanFilePath(): Promise<string | undefined>;
|
|
396
413
|
/**
|
|
397
414
|
* Get all currently allowed rules (user-defined and default)
|
|
398
415
|
*/
|
|
@@ -428,6 +445,37 @@ export declare class Agent {
|
|
|
428
445
|
* @returns The list of skill metadata
|
|
429
446
|
*/
|
|
430
447
|
getSkillMetadata(): SkillMetadata[];
|
|
448
|
+
/**
|
|
449
|
+
* Delete a user/personal or project skill by removing its directory.
|
|
450
|
+
* Builtin and plugin skills are read-only and cannot be deleted.
|
|
451
|
+
* @param name - The skill name as shown in skill metadata
|
|
452
|
+
* @returns true if the skill was deleted
|
|
453
|
+
*/
|
|
454
|
+
deleteSkill(name: string): Promise<boolean>;
|
|
455
|
+
/**
|
|
456
|
+
* Delete a user-level or project-level subagent by removing its markdown
|
|
457
|
+
* file. Builtin and plugin agents are read-only and cannot be deleted.
|
|
458
|
+
* @param name - The subagent name as shown in configurations
|
|
459
|
+
* @returns true if the subagent was deleted
|
|
460
|
+
*/
|
|
461
|
+
deleteSubagent(name: string): Promise<boolean>;
|
|
462
|
+
/**
|
|
463
|
+
* Get hook configurations at a specific scope (user or project settings.json;
|
|
464
|
+
* plugin hooks come from the hook manager's plugin-registered set).
|
|
465
|
+
*/
|
|
466
|
+
getHooksByScope(scope: "user" | "project" | "plugin"): Promise<Partial<Record<HookEvent, HookEventConfig[]>>>;
|
|
467
|
+
/**
|
|
468
|
+
* Absolute path of the settings.json holding hooks at `scope` — user
|
|
469
|
+
* `~/.wave/settings.json` or project `<workdir>/.wave/settings.json`. Hosts
|
|
470
|
+
* pass it to the GUI so editors / file panels can open the file (they cannot
|
|
471
|
+
* expand `~`).
|
|
472
|
+
*/
|
|
473
|
+
getHookConfigPath(scope: "user" | "project"): string;
|
|
474
|
+
/**
|
|
475
|
+
* Delete a hook at a specific scope, identified by hookName of the form
|
|
476
|
+
* `Event:Matcher`.
|
|
477
|
+
*/
|
|
478
|
+
deleteHook(scope: "user" | "project", hookName: string): Promise<void>;
|
|
431
479
|
/**
|
|
432
480
|
* Get currently active subagent instances (status active/initializing).
|
|
433
481
|
* @returns The list of active subagent instances
|
package/dist/agent.js
CHANGED
|
@@ -51,11 +51,13 @@ export class Agent {
|
|
|
51
51
|
* @param options - Configuration options for the Agent instance
|
|
52
52
|
*/
|
|
53
53
|
constructor(options) {
|
|
54
|
-
this.
|
|
54
|
+
this.bashModeManager = null;
|
|
55
55
|
this.dispatchPromise = null; // Track current dispatch for teardown
|
|
56
56
|
this.isAborting = false; // Transient guard: prevents tryDispatch from firing during abortMessage (reset when the abort completes)
|
|
57
57
|
this.isDestroyed = false; // Terminal guard: set in destroy(), never reset — no dispatch may ever start after destroy
|
|
58
58
|
this.dispatchAborted = false; // Set on abort while a dispatch is running: suppress the .finally re-check so preserved notifications don't get dispatched after abort
|
|
59
|
+
/** Unregister module-level listeners registered during container setup; invoked at the end of destroy(). */
|
|
60
|
+
this.teardown = () => { };
|
|
59
61
|
this.sessionStartTime = Date.now();
|
|
60
62
|
const { logger, workdir, systemPrompt, stream = true } = options;
|
|
61
63
|
// Set working directory early as we need it for loading configuration
|
|
@@ -68,7 +70,7 @@ export class Agent {
|
|
|
68
70
|
this.stream = stream; // Save streaming mode flag
|
|
69
71
|
// Store options for dynamic configuration resolution
|
|
70
72
|
this.options = options;
|
|
71
|
-
|
|
73
|
+
const { container, teardown } = setupAgentContainer({
|
|
72
74
|
options,
|
|
73
75
|
workdir: this.workdir,
|
|
74
76
|
configurationService: this.configurationService,
|
|
@@ -92,6 +94,8 @@ export class Agent {
|
|
|
92
94
|
addPermissionRule: (rule) => this.addPermissionRule(rule),
|
|
93
95
|
addUsage: (usage) => this.messageManager.addUsage(usage),
|
|
94
96
|
});
|
|
97
|
+
this.container = container;
|
|
98
|
+
this.teardown = teardown;
|
|
95
99
|
// Retrieve managers from container
|
|
96
100
|
this.foregroundTaskManager = this.container.get("ForegroundTaskManager");
|
|
97
101
|
this.memoryRuleManager = this.container.get("MemoryRuleManager");
|
|
@@ -111,7 +115,7 @@ export class Agent {
|
|
|
111
115
|
this.aiManager = this.container.get("AIManager");
|
|
112
116
|
this.slashCommandManager = this.container.get("SlashCommandManager");
|
|
113
117
|
this.pluginManager = this.container.get("PluginManager");
|
|
114
|
-
this.
|
|
118
|
+
this.bashModeManager = this.container.get("BashModeManager");
|
|
115
119
|
this.cronManager = this.container.get("CronManager");
|
|
116
120
|
this.messageQueue = this.container.get("MessageQueue");
|
|
117
121
|
this.asyncWorkRegistry = this.container.get("AsyncWorkRegistry");
|
|
@@ -131,7 +135,7 @@ export class Agent {
|
|
|
131
135
|
this.tryDispatch();
|
|
132
136
|
};
|
|
133
137
|
// Wire up bang manager callback for command running changes
|
|
134
|
-
this.
|
|
138
|
+
this.bashModeManager.onCommandRunningChange = (running) => {
|
|
135
139
|
this.options.callbacks?.onCommandRunningChange?.(running);
|
|
136
140
|
if (!running)
|
|
137
141
|
this.tryDispatch();
|
|
@@ -148,6 +152,12 @@ export class Agent {
|
|
|
148
152
|
get messages() {
|
|
149
153
|
return this.messageManager.getMessages();
|
|
150
154
|
}
|
|
155
|
+
/** Full UI display message stream (includes pre-compaction history).
|
|
156
|
+
* Consumed by hosts for the full-list message view (getMessages RPC,
|
|
157
|
+
* CLI structural refresh); the API context stays on `messages`. */
|
|
158
|
+
get displayMessages() {
|
|
159
|
+
return this.messageManager.getDisplayMessages();
|
|
160
|
+
}
|
|
151
161
|
get usages() {
|
|
152
162
|
return this.messageManager.getUsages();
|
|
153
163
|
}
|
|
@@ -192,6 +202,33 @@ export class Agent {
|
|
|
192
202
|
async getCombinedMemory() {
|
|
193
203
|
return this.messageManager.getCombinedMemory();
|
|
194
204
|
}
|
|
205
|
+
/**
|
|
206
|
+
* Read the user-level memory file (~/.wave/AGENTS.md). Unlike the cached
|
|
207
|
+
* `userMemory` getter this always reads fresh content (the settings UI edits
|
|
208
|
+
* the file while the agent may already hold a cache).
|
|
209
|
+
*/
|
|
210
|
+
async readUserMemoryContent() {
|
|
211
|
+
const memoryService = this.container.get("MemoryService");
|
|
212
|
+
return (await memoryService?.getUserMemoryContent()) ?? "";
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Read the project-level memory file (<workdir>/AGENTS.md, CLAUDE.md
|
|
216
|
+
* fallback). Always reads fresh content for the settings UI.
|
|
217
|
+
*/
|
|
218
|
+
async readProjectMemoryContent(workdir) {
|
|
219
|
+
const memoryService = this.container.get("MemoryService");
|
|
220
|
+
return (await memoryService?.readMemoryFile(workdir)) ?? "";
|
|
221
|
+
}
|
|
222
|
+
/** Persist the user-level memory file (~/.wave/AGENTS.md). */
|
|
223
|
+
async writeUserMemoryContent(content) {
|
|
224
|
+
const memoryService = this.container.get("MemoryService");
|
|
225
|
+
await memoryService?.writeUserMemoryContent(content);
|
|
226
|
+
}
|
|
227
|
+
/** Persist the project-level memory file (<workdir>/AGENTS.md). */
|
|
228
|
+
async writeProjectMemoryContent(workdir, content) {
|
|
229
|
+
const memoryService = this.container.get("MemoryService");
|
|
230
|
+
await memoryService?.writeProjectMemoryContent(workdir, content);
|
|
231
|
+
}
|
|
195
232
|
/** Get AI loading status */
|
|
196
233
|
get isLoading() {
|
|
197
234
|
return this.aiManager.isLoading;
|
|
@@ -202,7 +239,7 @@ export class Agent {
|
|
|
202
239
|
}
|
|
203
240
|
/** Get bash command execution status */
|
|
204
241
|
get isCommandRunning() {
|
|
205
|
-
return this.
|
|
242
|
+
return this.bashModeManager?.isCommandRunning ?? false;
|
|
206
243
|
}
|
|
207
244
|
/** Get queued user-facing messages (excludes background notifications) */
|
|
208
245
|
get queuedMessages() {
|
|
@@ -313,7 +350,7 @@ export class Agent {
|
|
|
313
350
|
this.messageQueue.transitionTo("running");
|
|
314
351
|
this.options.callbacks?.onQueuedMessagesChange?.(this.queuedMessages);
|
|
315
352
|
if (next.type === "bang") {
|
|
316
|
-
await this.
|
|
353
|
+
await this.bashModeManager?.executeCommand(next.content);
|
|
317
354
|
await this.messageManager.saveSession();
|
|
318
355
|
}
|
|
319
356
|
else if (next.type === "notification") {
|
|
@@ -512,7 +549,7 @@ export class Agent {
|
|
|
512
549
|
abortAIMessage() {
|
|
513
550
|
this.aiManager.abortAIMessage();
|
|
514
551
|
}
|
|
515
|
-
/** Execute bash command (
|
|
552
|
+
/** Execute a bash-mode command (`!command`) */
|
|
516
553
|
async bang(command) {
|
|
517
554
|
this.assertNotDestroyed();
|
|
518
555
|
// If the agent is busy, enqueue the bang command
|
|
@@ -521,7 +558,7 @@ export class Agent {
|
|
|
521
558
|
this.options.callbacks?.onQueuedMessagesChange?.(this.queuedMessages);
|
|
522
559
|
return;
|
|
523
560
|
}
|
|
524
|
-
await this.
|
|
561
|
+
await this.bashModeManager?.executeCommand(command);
|
|
525
562
|
await this.messageManager.saveSession();
|
|
526
563
|
}
|
|
527
564
|
async clearMessages() {
|
|
@@ -616,7 +653,7 @@ export class Agent {
|
|
|
616
653
|
}
|
|
617
654
|
/** Interrupt bash command execution */
|
|
618
655
|
abortBashCommand() {
|
|
619
|
-
this.
|
|
656
|
+
this.bashModeManager?.abortCommand();
|
|
620
657
|
}
|
|
621
658
|
/** Interrupt slash command execution */
|
|
622
659
|
abortSlashCommand() {
|
|
@@ -717,6 +754,14 @@ export class Agent {
|
|
|
717
754
|
if (!drained) {
|
|
718
755
|
this.logger?.error(`Async work did not drain: ${this.asyncWorkRegistry.size} live work item(s) remain after destroy`);
|
|
719
756
|
}
|
|
757
|
+
// Unregister module-level listeners (remote settings hot-update, auth
|
|
758
|
+
// change) so the agent's object graph becomes collectable. Without this,
|
|
759
|
+
// the module-level callback arrays pin every created agent via the
|
|
760
|
+
// per-agent closure contexts, even after the host drops its references.
|
|
761
|
+
this.teardown();
|
|
762
|
+
// Break the DI container's internal references (services/factories) so no
|
|
763
|
+
// per-agent manager is retained through it after destroy.
|
|
764
|
+
this.container.clear();
|
|
720
765
|
}
|
|
721
766
|
/**
|
|
722
767
|
* Trigger the rewind UI callback
|
|
@@ -741,28 +786,6 @@ export class Agent {
|
|
|
741
786
|
const result = await this.aiManager.runBtwFork(question, abortSignal, onContent, onReasoning);
|
|
742
787
|
return result.content ?? result.error ?? "No response received";
|
|
743
788
|
}
|
|
744
|
-
/**
|
|
745
|
-
* Start a fork subagent in the background (the "/subtask" command path).
|
|
746
|
-
*
|
|
747
|
-
* The fork inherits the parent's full conversation context — same system
|
|
748
|
-
* prompt, tools, model, and message prefix — so the prompt cache is reused,
|
|
749
|
-
* then works independently in the background. On completion its final
|
|
750
|
-
* response is delivered back to the main conversation as a task notification
|
|
751
|
-
* carrying the `<result>` tag. Recursion is prevented inside the fork (the
|
|
752
|
-
* Agent tool and Task tools are denied).
|
|
753
|
-
*
|
|
754
|
-
* @param prompt - The task description for the fork subagent
|
|
755
|
-
* @param options - Background task label and optional loose turn bound
|
|
756
|
-
* @param options.description - Display label for the background task
|
|
757
|
-
* @param options.maxTurns - Loose turn bound (defaults to 200, aligned with
|
|
758
|
-
* Claude Code's fork subagent)
|
|
759
|
-
* @param abortSignal - Optional signal to abort the fork
|
|
760
|
-
* @returns Promise that resolves to the background task ID
|
|
761
|
-
*/
|
|
762
|
-
async forkSubagent(prompt, options, abortSignal) {
|
|
763
|
-
this.assertNotDestroyed();
|
|
764
|
-
return this.aiManager.runForkSubagent(prompt, options, abortSignal);
|
|
765
|
-
}
|
|
766
789
|
/**
|
|
767
790
|
* Send a message to the AI agent with optional images
|
|
768
791
|
*
|
|
@@ -864,6 +887,21 @@ export class Agent {
|
|
|
864
887
|
async disconnectMcpServer(serverName) {
|
|
865
888
|
return await this.mcpManager.disconnectServer(serverName);
|
|
866
889
|
}
|
|
890
|
+
/**
|
|
891
|
+
* Remove an MCP server from a persisted config file (user ~/.wave/mcp.json
|
|
892
|
+
* or project <workdir>/.mcp.json), then disconnect and drop it from memory.
|
|
893
|
+
*/
|
|
894
|
+
async removeMcpServer(scope, serverName) {
|
|
895
|
+
return await this.mcpManager.removeServerFromConfig(scope, serverName);
|
|
896
|
+
}
|
|
897
|
+
/** The user-level MCP config path (~/.wave/mcp.json) */
|
|
898
|
+
getUserMcpConfigPath() {
|
|
899
|
+
return this.mcpManager.getUserConfigPath();
|
|
900
|
+
}
|
|
901
|
+
/** The project-level MCP config path (<workdir>/.mcp.json) */
|
|
902
|
+
getProjectMcpConfigPath() {
|
|
903
|
+
return this.mcpManager.getProjectConfigPath();
|
|
904
|
+
}
|
|
867
905
|
// ========== Slash Command Management Methods ==========
|
|
868
906
|
/** Get all available slash commands */
|
|
869
907
|
getSlashCommands() {
|
|
@@ -939,6 +977,16 @@ export class Agent {
|
|
|
939
977
|
getPlanFilePath() {
|
|
940
978
|
return this.permissionManager.getPlanFilePath();
|
|
941
979
|
}
|
|
980
|
+
/**
|
|
981
|
+
* Resolve once the plan file path has been generated and set after entering
|
|
982
|
+
* plan mode. Resolves `undefined` when no generation is in flight.
|
|
983
|
+
* Used by the CLI /plan command and the stdio getPlanFile RPC to avoid
|
|
984
|
+
* querying the model before the path is known (the plan mode reminder needs
|
|
985
|
+
* the path to be set).
|
|
986
|
+
*/
|
|
987
|
+
awaitPlanFilePath() {
|
|
988
|
+
return this.planManager.awaitPlanFilePath();
|
|
989
|
+
}
|
|
942
990
|
/**
|
|
943
991
|
* Get all currently allowed rules (user-defined and default)
|
|
944
992
|
*/
|
|
@@ -991,6 +1039,50 @@ export class Agent {
|
|
|
991
1039
|
getSkillMetadata() {
|
|
992
1040
|
return this.skillManager.getAvailableSkills();
|
|
993
1041
|
}
|
|
1042
|
+
/**
|
|
1043
|
+
* Delete a user/personal or project skill by removing its directory.
|
|
1044
|
+
* Builtin and plugin skills are read-only and cannot be deleted.
|
|
1045
|
+
* @param name - The skill name as shown in skill metadata
|
|
1046
|
+
* @returns true if the skill was deleted
|
|
1047
|
+
*/
|
|
1048
|
+
async deleteSkill(name) {
|
|
1049
|
+
return await this.skillManager.deleteSkill(name);
|
|
1050
|
+
}
|
|
1051
|
+
/**
|
|
1052
|
+
* Delete a user-level or project-level subagent by removing its markdown
|
|
1053
|
+
* file. Builtin and plugin agents are read-only and cannot be deleted.
|
|
1054
|
+
* @param name - The subagent name as shown in configurations
|
|
1055
|
+
* @returns true if the subagent was deleted
|
|
1056
|
+
*/
|
|
1057
|
+
async deleteSubagent(name) {
|
|
1058
|
+
return await this.subagentManager.deleteSubagent(name);
|
|
1059
|
+
}
|
|
1060
|
+
/**
|
|
1061
|
+
* Get hook configurations at a specific scope (user or project settings.json;
|
|
1062
|
+
* plugin hooks come from the hook manager's plugin-registered set).
|
|
1063
|
+
*/
|
|
1064
|
+
async getHooksByScope(scope) {
|
|
1065
|
+
if (scope === "plugin") {
|
|
1066
|
+
return this.hookManager.getPluginHooks();
|
|
1067
|
+
}
|
|
1068
|
+
return await this.configurationService.getHooksByScope(this.workdir, scope);
|
|
1069
|
+
}
|
|
1070
|
+
/**
|
|
1071
|
+
* Absolute path of the settings.json holding hooks at `scope` — user
|
|
1072
|
+
* `~/.wave/settings.json` or project `<workdir>/.wave/settings.json`. Hosts
|
|
1073
|
+
* pass it to the GUI so editors / file panels can open the file (they cannot
|
|
1074
|
+
* expand `~`).
|
|
1075
|
+
*/
|
|
1076
|
+
getHookConfigPath(scope) {
|
|
1077
|
+
return this.configurationService.getHookConfigPath(this.workdir, scope);
|
|
1078
|
+
}
|
|
1079
|
+
/**
|
|
1080
|
+
* Delete a hook at a specific scope, identified by hookName of the form
|
|
1081
|
+
* `Event:Matcher`.
|
|
1082
|
+
*/
|
|
1083
|
+
async deleteHook(scope, hookName) {
|
|
1084
|
+
await this.configurationService.deleteHook(this.workdir, scope, hookName);
|
|
1085
|
+
}
|
|
994
1086
|
/**
|
|
995
1087
|
* Get currently active subagent instances (status active/initializing).
|
|
996
1088
|
* @returns The list of active subagent instances
|
package/dist/builtin/plugins.js
CHANGED
|
@@ -41,6 +41,9 @@ const guidance = [
|
|
|
41
41
|
"- 边界模糊时也先写 spec 草稿请用户确认,不要直接改代码。",
|
|
42
42
|
"- 规格编写技能(specify)由 AI 自动触发:对话中涉及新需求或需求变更时主动创建或更新规格文件,不需要用户手动调用(不出现在斜杠命令列表中)。",
|
|
43
43
|
\`- 新增或修改 spec 后运行校验:\${specCount}(自动检测 docs/specs/,否则 specs/,否则退出)。\`,
|
|
44
|
+
"- 规格确认与阶段衔接一次完成:通过 AskUserQuestion 单选让用户点击决策(选项只传「直接实现 / 制定技术方案」,并在问题文案中说明如需调整规格可选「其他」输入修改意见;「其他」由 AskUserQuestion 自动附加,勿手动添加;选「其他」=规格需调整,按反馈修改后再次询问)。",
|
|
45
|
+
"- 可选技术方案阶段:制定技术方案并批准后再编码;也可跳过——最短流程=规格+编码,最长=规格+技术方案+编码。",
|
|
46
|
+
"- 用 task 工具追踪进度:规格、技术方案、编码各阶段开始前用 TaskCreate 创建任务并标记进行中(TaskUpdate),完成/批准/确认后标记完成,让用户在任务列表中看到当前所处阶段。",
|
|
44
47
|
].join("\\n");
|
|
45
48
|
|
|
46
49
|
// JSON form → parsed as hookSpecificOutput.additionalContext by the hook manager.
|
|
@@ -128,7 +131,7 @@ if (warnings.length) {
|
|
|
128
131
|
`,
|
|
129
132
|
"plugins/sdd/skills/specify/SKILL.md": `---
|
|
130
133
|
name: specify
|
|
131
|
-
description:
|
|
134
|
+
description: 根据自然语言描述创建或更新功能规格说明,并通过单选衔接可选技术方案与编码阶段。
|
|
132
135
|
user-invocable: false
|
|
133
136
|
---
|
|
134
137
|
|
|
@@ -144,6 +147,8 @@ $ARGUMENTS
|
|
|
144
147
|
|
|
145
148
|
根据对话中的功能描述,执行以下步骤:
|
|
146
149
|
|
|
150
|
+
0. **创建进度任务**:用 TaskCreate 创建「编写功能规格」任务,并用 TaskUpdate 标记进行中。后续每个阶段(技术方案、编码)同样在开始前创建任务、结束后更新状态,让用户在任务列表中看到当前进度。
|
|
151
|
+
|
|
147
152
|
1. **确定规格文件路径**:
|
|
148
153
|
- **确定规格根目录**:优先复用项目中已有的规格目录——若 \`docs/specs/\` 存在则用之,否则若 \`specs/\` 存在则用之,否则默认 \`specs/\`(并在完成报告中说明所选目录,便于用户纠正)。
|
|
149
154
|
- **选择分组**:若规格目录下已有分组子目录,沿用其既有分组约定;否则默认扁平结构(直接放在规格根目录下)。
|
|
@@ -165,7 +170,17 @@ $ARGUMENTS
|
|
|
165
170
|
- 将每个标记作为问题展示,附带建议答案
|
|
166
171
|
- 等待用户回复后更新规格文件
|
|
167
172
|
|
|
168
|
-
5.
|
|
173
|
+
5. **校验并确认规格**:
|
|
174
|
+
- 运行会话引导中给出的 spec-count 校验命令(自动检测 docs/specs/,否则 specs/,否则跳过)
|
|
175
|
+
- 输出规格文件路径,并通过 AskUserQuestion 单选请求决策(选项只传:直接实现 / 制定技术方案;AskUserQuestion 会自动附加「其他」选项,勿手动添加「其他」;在问题文案中说明如需调整规格可选「其他」并输入修改意见)
|
|
176
|
+
- 用户选「其他」并输入意见 → 视为规格需要调整:按用户反馈更新规格后重新校验,并再次单选
|
|
177
|
+
- 选「直接实现」→ 将「编写功能规格」任务标记完成,进入编码阶段
|
|
178
|
+
- 选「制定技术方案」→ 将「编写功能规格」任务标记完成;用 TaskCreate 创建「制定技术方案」任务并标记进行中;调用 EnterPlanMode 进入技术方案模式,制定技术方案(技术选型、架构设计、实现步骤)并写入计划文件;用 ExitPlanMode 请求批准——被拒绝则按反馈更新方案后重新请求,批准后标记任务完成
|
|
179
|
+
|
|
180
|
+
6. **编码阶段**:
|
|
181
|
+
- 用 TaskCreate 创建「实现功能」任务并标记进行中
|
|
182
|
+
- 按已确认的规格实现;若批准了技术方案则遵循其架构
|
|
183
|
+
- 实现完成后将任务标记完成
|
|
169
184
|
|
|
170
185
|
## 指南
|
|
171
186
|
|
|
@@ -702,7 +702,7 @@ You can extend the Safe Zone by adding \`additionalDirectories\` to your \`permi
|
|
|
702
702
|
|
|
703
703
|
## Permission Modes
|
|
704
704
|
|
|
705
|
-
The \`
|
|
705
|
+
The \`defaultMode\` setting (under \`permissions\`, aligning with Claude Code's settings key) determines how Wave handles requests to use restricted tools (e.g., \`Bash\`, \`Edit\`, \`Write\`, \`AskUserQuestion\`).
|
|
706
706
|
|
|
707
707
|
| Mode | Description |
|
|
708
708
|
| :--- | :--- |
|
|
@@ -717,7 +717,7 @@ The \`permissionMode\` setting determines how Wave handles requests to use restr
|
|
|
717
717
|
\`\`\`json
|
|
718
718
|
{
|
|
719
719
|
"permissions": {
|
|
720
|
-
"
|
|
720
|
+
"defaultMode": "default",
|
|
721
721
|
"additionalDirectories": ["/home/user/my-exports"],
|
|
722
722
|
"allow": ["ls -R", "git status"],
|
|
723
723
|
"deny": ["rm -rf"]
|
|
@@ -735,7 +735,7 @@ You can pre-approve or explicitly forbid specific operations using \`allow\` and
|
|
|
735
735
|
When a tool is called, Wave checks:
|
|
736
736
|
1. If the operation matches a \`deny\` rule, it is rejected.
|
|
737
737
|
2. If the operation matches an \`allow\` rule, it is permitted.
|
|
738
|
-
3. If no rules match, the behavior depends on the \`
|
|
738
|
+
3. If no rules match, the behavior depends on the \`defaultMode\`.
|
|
739
739
|
|
|
740
740
|
### Rule Syntax
|
|
741
741
|
|
|
@@ -998,7 +998,7 @@ For detailed permission configuration and available permission modes, see [PERMI
|
|
|
998
998
|
"permissions": {
|
|
999
999
|
"allow": ["Bash", "Read"],
|
|
1000
1000
|
"deny": ["Write"],
|
|
1001
|
-
"
|
|
1001
|
+
"defaultMode": "default",
|
|
1002
1002
|
"additionalDirectories": ["/tmp/wave-exports"]
|
|
1003
1003
|
}
|
|
1004
1004
|
}
|
|
@@ -1104,10 +1104,6 @@ The \`SKILL.md\` file uses YAML frontmatter for configuration and Markdown for i
|
|
|
1104
1104
|
---
|
|
1105
1105
|
name: my-skill
|
|
1106
1106
|
description: A brief description of what the skill does.
|
|
1107
|
-
context: fork
|
|
1108
|
-
allowed-tools:
|
|
1109
|
-
- Bash
|
|
1110
|
-
- Read
|
|
1111
1107
|
---
|
|
1112
1108
|
|
|
1113
1109
|
# My Skill Instructions
|
|
@@ -1117,6 +1113,8 @@ When this skill is invoked, follow these steps:
|
|
|
1117
1113
|
2. Use the \`Bash\` tool to run \`npm test\`.
|
|
1118
1114
|
\`\`\`
|
|
1119
1115
|
|
|
1116
|
+
By default, new skills only include \`name\` and \`description\`. Only add \`context: fork\` or \`allowed-tools\` when the user explicitly asks for them.
|
|
1117
|
+
|
|
1120
1118
|
### YAML Frontmatter Fields
|
|
1121
1119
|
|
|
1122
1120
|
- \`name\`: (Required) Unique identifier (lowercase, numbers, hyphens).
|
package/dist/core/plugin.d.ts
CHANGED
|
@@ -56,6 +56,10 @@ export declare class PluginCore {
|
|
|
56
56
|
removeMarketplace(name: string, scope?: Scope): Promise<void>;
|
|
57
57
|
/**
|
|
58
58
|
* Updates a specific marketplace or all marketplaces
|
|
59
|
+
*
|
|
60
|
+
* Pulls the latest marketplace source and reinstalls any plugins that are
|
|
61
|
+
* already installed from it, so a manual "update marketplace" also brings
|
|
62
|
+
* installed plugins up to date (mirrors Claude Code's refresh-and-bump).
|
|
59
63
|
*/
|
|
60
64
|
updateMarketplace(name?: string): Promise<void>;
|
|
61
65
|
/**
|
package/dist/core/plugin.js
CHANGED
|
@@ -121,9 +121,15 @@ export class PluginCore {
|
|
|
121
121
|
}
|
|
122
122
|
/**
|
|
123
123
|
* Updates a specific marketplace or all marketplaces
|
|
124
|
+
*
|
|
125
|
+
* Pulls the latest marketplace source and reinstalls any plugins that are
|
|
126
|
+
* already installed from it, so a manual "update marketplace" also brings
|
|
127
|
+
* installed plugins up to date (mirrors Claude Code's refresh-and-bump).
|
|
124
128
|
*/
|
|
125
129
|
async updateMarketplace(name) {
|
|
126
|
-
await this.marketplaceService.updateMarketplace(name
|
|
130
|
+
await this.marketplaceService.updateMarketplace(name, {
|
|
131
|
+
updatePlugins: true,
|
|
132
|
+
});
|
|
127
133
|
}
|
|
128
134
|
/**
|
|
129
135
|
* Lists all registered marketplaces
|
package/dist/index.d.ts
CHANGED
|
@@ -25,7 +25,8 @@ export * from "./utils/nameGenerator.js";
|
|
|
25
25
|
export * from "./utils/pathEncoder.js";
|
|
26
26
|
export * from "./utils/worktreeSession.js";
|
|
27
27
|
export * from "./utils/worktreeUtils.js";
|
|
28
|
-
export { loadMergedWaveConfig, loadUserConfigEnv, } from "./services/configurationService.js";
|
|
28
|
+
export { loadMergedWaveConfig, loadUserConfigEnv, loadWaveConfigFromFile, } from "./services/configurationService.js";
|
|
29
|
+
export { getUserConfigPaths, getProjectConfigPaths, } from "./utils/configPaths.js";
|
|
29
30
|
export * from "./types/index.js";
|
|
30
31
|
export type { SubagentConfiguration } from "./utils/subagentParser.js";
|
|
31
32
|
export type { SubagentInstance } from "./managers/subagentManager.js";
|
package/dist/index.js
CHANGED
|
@@ -29,7 +29,8 @@ export * from "./utils/nameGenerator.js";
|
|
|
29
29
|
export * from "./utils/pathEncoder.js";
|
|
30
30
|
export * from "./utils/worktreeSession.js";
|
|
31
31
|
export * from "./utils/worktreeUtils.js";
|
|
32
|
-
export { loadMergedWaveConfig, loadUserConfigEnv, } from "./services/configurationService.js";
|
|
32
|
+
export { loadMergedWaveConfig, loadUserConfigEnv, loadWaveConfigFromFile, } from "./services/configurationService.js";
|
|
33
|
+
export { getUserConfigPaths, getProjectConfigPaths, } from "./utils/configPaths.js";
|
|
33
34
|
export * from "./types/index.js";
|
|
34
35
|
// Export tool building utilities
|
|
35
36
|
export * from "./tools/buildTool.js";
|
|
@@ -189,30 +189,6 @@ export declare class AIManager {
|
|
|
189
189
|
deniedToolMessage?: string;
|
|
190
190
|
maxTurns?: number;
|
|
191
191
|
}, abortSignal?: AbortSignal): Promise<ForkLoopResult>;
|
|
192
|
-
/**
|
|
193
|
-
* Fork subagent ("/subtask"): spawn a background agent that inherits the
|
|
194
|
-
* parent's full conversation context — the same system prompt, tools, model,
|
|
195
|
-
* and message prefix as the main loop — so the forked request prefix matches
|
|
196
|
-
* exactly and the prompt cache is reused. The fork runs to completion in the
|
|
197
|
-
* background via the shared BackgroundTaskManager (visible in /tasks, log at
|
|
198
|
-
* `os.tmpdir()/wave-subagent-<taskId>.log`); its final response is enqueued
|
|
199
|
-
* as a task notification carrying the `<result>` tag, so the main agent
|
|
200
|
-
* picks it up on the next turn and can act on it.
|
|
201
|
-
*
|
|
202
|
-
* Recursion is prevented by denying the Agent tool and the Task management
|
|
203
|
-
* tools (the fork cannot spawn further subagents or mutate the shared task
|
|
204
|
-
* list), and by honoring the parent's denied rules for everything else.
|
|
205
|
-
* Gate-approved tools execute locally in the stripped fork context (no
|
|
206
|
-
* permission prompts), mirroring the other fork paths.
|
|
207
|
-
*
|
|
208
|
-
* Returns the background task ID (empty string when no BackgroundTaskManager
|
|
209
|
-
* is registered, in which case the fork still runs but without task
|
|
210
|
-
* bookkeeping or notification).
|
|
211
|
-
*/
|
|
212
|
-
runForkSubagent(prompt: string, options: {
|
|
213
|
-
description: string;
|
|
214
|
-
maxTurns?: number;
|
|
215
|
-
}, abortSignal?: AbortSignal): Promise<string>;
|
|
216
192
|
/**
|
|
217
193
|
* Parse a fork tool call's JSON arguments, recovering truncated JSON the
|
|
218
194
|
* same way the main loop does. Unparseable arguments fall back to `{}` and
|