wave-agent-sdk 0.13.4 → 0.13.6

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.
Files changed (91) hide show
  1. package/builtin/skills/settings/HOOKS.md +25 -0
  2. package/builtin/skills/settings/MCP.md +22 -0
  3. package/builtin/skills/settings/SKILL.md +4 -1
  4. package/dist/agent.d.ts +21 -0
  5. package/dist/agent.d.ts.map +1 -1
  6. package/dist/agent.js +102 -1
  7. package/dist/index.d.ts +1 -0
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js +1 -0
  10. package/dist/managers/aiManager.d.ts +5 -0
  11. package/dist/managers/aiManager.d.ts.map +1 -1
  12. package/dist/managers/aiManager.js +19 -4
  13. package/dist/managers/bangManager.d.ts +1 -0
  14. package/dist/managers/bangManager.d.ts.map +1 -1
  15. package/dist/managers/bangManager.js +1 -0
  16. package/dist/managers/hookManager.d.ts +5 -1
  17. package/dist/managers/hookManager.d.ts.map +1 -1
  18. package/dist/managers/hookManager.js +55 -5
  19. package/dist/managers/lspManager.d.ts.map +1 -1
  20. package/dist/managers/lspManager.js +17 -2
  21. package/dist/managers/mcpManager.d.ts.map +1 -1
  22. package/dist/managers/mcpManager.js +20 -6
  23. package/dist/managers/messageManager.d.ts.map +1 -1
  24. package/dist/managers/messageManager.js +22 -0
  25. package/dist/managers/messageQueue.d.ts +20 -0
  26. package/dist/managers/messageQueue.d.ts.map +1 -0
  27. package/dist/managers/messageQueue.js +29 -0
  28. package/dist/managers/permissionManager.d.ts +5 -7
  29. package/dist/managers/permissionManager.d.ts.map +1 -1
  30. package/dist/managers/permissionManager.js +27 -22
  31. package/dist/managers/pluginManager.d.ts.map +1 -1
  32. package/dist/managers/pluginManager.js +5 -3
  33. package/dist/managers/skillManager.d.ts.map +1 -1
  34. package/dist/managers/skillManager.js +5 -0
  35. package/dist/managers/slashCommandManager.d.ts.map +1 -1
  36. package/dist/managers/slashCommandManager.js +12 -1
  37. package/dist/managers/subagentManager.d.ts.map +1 -1
  38. package/dist/managers/subagentManager.js +6 -18
  39. package/dist/services/autoMemoryService.d.ts.map +1 -1
  40. package/dist/services/autoMemoryService.js +1 -0
  41. package/dist/services/hook.d.ts +4 -0
  42. package/dist/services/hook.d.ts.map +1 -1
  43. package/dist/services/hook.js +10 -0
  44. package/dist/services/interactionService.d.ts.map +1 -1
  45. package/dist/services/interactionService.js +3 -0
  46. package/dist/services/pluginLoader.d.ts.map +1 -1
  47. package/dist/services/pluginLoader.js +3 -1
  48. package/dist/tools/bashTool.d.ts.map +1 -1
  49. package/dist/tools/bashTool.js +33 -2
  50. package/dist/tools/types.d.ts +2 -0
  51. package/dist/tools/types.d.ts.map +1 -1
  52. package/dist/types/agent.d.ts +4 -0
  53. package/dist/types/agent.d.ts.map +1 -1
  54. package/dist/types/hooks.d.ts +6 -1
  55. package/dist/types/hooks.d.ts.map +1 -1
  56. package/dist/types/hooks.js +4 -0
  57. package/dist/types/lsp.d.ts +2 -0
  58. package/dist/types/lsp.d.ts.map +1 -1
  59. package/dist/types/mcp.d.ts +2 -0
  60. package/dist/types/mcp.d.ts.map +1 -1
  61. package/dist/types/skills.d.ts +1 -0
  62. package/dist/types/skills.d.ts.map +1 -1
  63. package/dist/utils/containerSetup.d.ts.map +1 -1
  64. package/dist/utils/containerSetup.js +4 -1
  65. package/package.json +1 -1
  66. package/src/agent.ts +122 -2
  67. package/src/index.ts +1 -0
  68. package/src/managers/aiManager.ts +35 -5
  69. package/src/managers/bangManager.ts +2 -0
  70. package/src/managers/hookManager.ts +78 -19
  71. package/src/managers/lspManager.ts +23 -2
  72. package/src/managers/mcpManager.ts +29 -6
  73. package/src/managers/messageManager.ts +38 -0
  74. package/src/managers/messageQueue.ts +41 -0
  75. package/src/managers/permissionManager.ts +32 -26
  76. package/src/managers/pluginManager.ts +5 -3
  77. package/src/managers/skillManager.ts +9 -0
  78. package/src/managers/slashCommandManager.ts +16 -4
  79. package/src/managers/subagentManager.ts +10 -25
  80. package/src/services/autoMemoryService.ts +1 -0
  81. package/src/services/hook.ts +15 -0
  82. package/src/services/interactionService.ts +3 -0
  83. package/src/services/pluginLoader.ts +3 -1
  84. package/src/tools/bashTool.ts +39 -2
  85. package/src/tools/types.ts +2 -0
  86. package/src/types/agent.ts +4 -0
  87. package/src/types/hooks.ts +13 -2
  88. package/src/types/lsp.ts +2 -0
  89. package/src/types/mcp.ts +2 -0
  90. package/src/types/skills.ts +1 -0
  91. package/src/utils/containerSetup.ts +5 -1
@@ -260,6 +260,21 @@ export async function executeCommands(
260
260
  return results;
261
261
  }
262
262
 
263
+ /**
264
+ * Execute a CwdChanged hook
265
+ */
266
+ export async function executeCwdChangedHooks(
267
+ oldCwd: string,
268
+ newCwd: string,
269
+ context: ExtendedHookExecutionContext,
270
+ ): Promise<HookExecutionResult[]> {
271
+ // CwdChanged hooks are executed through HookManager.executeCwdChangedHooks()
272
+ void context;
273
+ void oldCwd;
274
+ void newCwd;
275
+ return [];
276
+ }
277
+
263
278
  /**
264
279
  * Validate command safety (basic checks)
265
280
  */
@@ -50,6 +50,9 @@ export class InteractionService {
50
50
 
51
51
  if (isValid && commandId !== undefined) {
52
52
  // Execute valid slash command
53
+ // Note: executeCommand sets isLoading early (e.g., custom commands set it
54
+ // before bash execution). sendAIMessage() no longer guards against isLoading,
55
+ // so callers can safely set it early without blocking subsequent AI calls.
53
56
  await slashCommandManager.executeCommand(commandId, args);
54
57
 
55
58
  return;
@@ -10,6 +10,7 @@ import {
10
10
  } from "../types/index.js";
11
11
  import { scanCommandsDirectory } from "../utils/customCommands.js";
12
12
  import { parseSkillFile } from "../utils/skillParser.js";
13
+ import { resolveMcpConfig } from "../managers/mcpManager.js";
13
14
 
14
15
  export class PluginLoader {
15
16
  /**
@@ -94,6 +95,7 @@ export class PluginLoader {
94
95
  skills.push({
95
96
  ...parsed.skillMetadata,
96
97
  type: "project", // Plugin skills are treated as project skills
98
+ pluginRoot: pluginPath,
97
99
  content: parsed.content,
98
100
  frontmatter: parsed.frontmatter,
99
101
  isValid: parsed.isValid,
@@ -136,7 +138,7 @@ export class PluginLoader {
136
138
  const mcpPath = path.join(pluginPath, ".mcp.json");
137
139
  try {
138
140
  const content = await fs.readFile(mcpPath, "utf-8");
139
- return JSON.parse(content) as McpConfig;
141
+ return resolveMcpConfig(JSON.parse(content)) as McpConfig;
140
142
  } catch {
141
143
  return undefined;
142
144
  }
@@ -230,7 +230,14 @@ Use the gh command via the Bash tool for GitHub-related tasks including working
230
230
 
231
231
  // Foreground execution (original behavior)
232
232
  return new Promise((resolve) => {
233
- const child: ChildProcess = spawn(command, {
233
+ // Create a temporary file to store the CWD
234
+ const tempCwdFile = path.join(
235
+ os.tmpdir(),
236
+ `wave_cwd_${Date.now()}_${Math.random().toString(36).substring(2, 11)}.tmp`,
237
+ );
238
+ const wrappedCommand = `${command} && pwd -P >| ${tempCwdFile}`;
239
+
240
+ const child: ChildProcess = spawn(wrappedCommand, {
234
241
  shell: true,
235
242
  stdio: "pipe",
236
243
  cwd: context.workdir,
@@ -401,7 +408,7 @@ Use the gh command via the Bash tool for GitHub-related tasks including working
401
408
  }
402
409
  });
403
410
 
404
- child.on("exit", (code) => {
411
+ child.on("exit", async (code) => {
405
412
  isFinished = true;
406
413
  if (context.foregroundTaskManager) {
407
414
  context.foregroundTaskManager.unregisterForegroundTask(
@@ -414,6 +421,36 @@ Use the gh command via the Bash tool for GitHub-related tasks including working
414
421
  clearTimeout(timeoutHandle);
415
422
  }
416
423
 
424
+ // Read the new CWD from the temporary file
425
+ let newCwd: string | undefined;
426
+ try {
427
+ if (fs.existsSync(tempCwdFile)) {
428
+ newCwd = fs.readFileSync(tempCwdFile, "utf8").trim();
429
+ // Validate the path exists before calling the callback
430
+ fs.accessSync(newCwd, fs.constants.F_OK);
431
+ }
432
+ } catch (fileError) {
433
+ logger.warn(
434
+ `Could not read or validate new CWD from temp file ${tempCwdFile}:`,
435
+ fileError,
436
+ );
437
+ newCwd = undefined;
438
+ } finally {
439
+ // Ensure temp file is cleaned up even if reading fails
440
+ try {
441
+ if (fs.existsSync(tempCwdFile)) {
442
+ fs.unlinkSync(tempCwdFile);
443
+ }
444
+ } catch (fileError) {
445
+ logger.error("Failed to clean up temp CWD file:", fileError);
446
+ }
447
+ }
448
+
449
+ // If CWD changed, call the onCwdChange callback
450
+ if (newCwd && newCwd !== context.workdir && context.onCwdChange) {
451
+ context.onCwdChange(newCwd);
452
+ }
453
+
417
454
  const exitCode = code ?? 0;
418
455
  const combinedOutput =
419
456
  outputBuffer + (errorBuffer ? "\n" + errorBuffer : "");
@@ -103,4 +103,6 @@ export interface ToolContext {
103
103
  };
104
104
  /** State of files read in the current session for deduplication */
105
105
  readFileState?: Map<string, { mtime: number; hash: string }>;
106
+ /** Callback to notify when the current working directory changes */
107
+ onCwdChange?: (newCwd: string) => void;
106
108
  }
@@ -1,4 +1,5 @@
1
1
  import type { ClientOptions } from "openai";
2
+ import type { QueuedMessage } from "../managers/messageQueue.js";
2
3
  import type {
3
4
  Message,
4
5
  Logger,
@@ -96,4 +97,7 @@ export interface AgentCallbacks
96
97
  onModelChange?: (model: string) => void;
97
98
  onConfiguredModelsChange?: (models: string[]) => void;
98
99
  onLoadingChange?: (loading: boolean) => void;
100
+ onCommandRunningChange?: (running: boolean) => void;
101
+ onWorkdirChange?: (newCwd: string) => void;
102
+ onQueuedMessagesChange?: (messages: QueuedMessage[]) => void;
99
103
  }
@@ -20,7 +20,8 @@ export type HookEvent =
20
20
  | "Stop"
21
21
  | "SubagentStop"
22
22
  | "PermissionRequest"
23
- | "WorktreeCreate";
23
+ | "WorktreeCreate"
24
+ | "CwdChanged";
24
25
 
25
26
  // Individual hook command configuration
26
27
  export interface HookCommand {
@@ -28,6 +29,7 @@ export interface HookCommand {
28
29
  command: string;
29
30
  async?: boolean;
30
31
  timeout?: number; // seconds
32
+ pluginRoot?: string; // Plugin directory path for plugin-originated hooks
31
33
  }
32
34
 
33
35
  // Hook event configuration with optional pattern matching
@@ -102,6 +104,7 @@ export function isValidHookEvent(event: string): event is HookEvent {
102
104
  "SubagentStop",
103
105
  "PermissionRequest",
104
106
  "WorktreeCreate",
107
+ "CwdChanged",
105
108
  ].includes(event);
106
109
  }
107
110
 
@@ -128,6 +131,10 @@ export function isValidHookCommand(cmd: unknown): cmd is HookCommand {
128
131
  return false;
129
132
  }
130
133
 
134
+ if ("pluginRoot" in hookCmd && typeof hookCmd.pluginRoot !== "string") {
135
+ return false;
136
+ }
137
+
131
138
  return true;
132
139
  }
133
140
 
@@ -154,7 +161,7 @@ export interface HookJsonInput {
154
161
  session_id: string; // Format: "wave_session_{uuid}_{shortId}"
155
162
  transcript_path: string; // Format: "~/.wave/sessions/session_{shortId}.json"
156
163
  cwd: string; // Absolute path to current working directory
157
- hook_event_name: HookEvent; // "PreToolUse" | "PostToolUse" | "UserPromptSubmit" | "Stop" | "SubagentStop" | "PermissionRequest" | "WorktreeCreate"
164
+ hook_event_name: HookEvent; // "PreToolUse" | "PostToolUse" | "UserPromptSubmit" | "Stop" | "SubagentStop" | "PermissionRequest" | "WorktreeCreate" | "CwdChanged"
158
165
 
159
166
  // Optional fields based on event type
160
167
  tool_name?: string; // Present for PreToolUse, PostToolUse, PermissionRequest
@@ -163,6 +170,8 @@ export interface HookJsonInput {
163
170
  user_prompt?: string; // Present for UserPromptSubmit only
164
171
  subagent_type?: string; // Present when hook is executed by a subagent
165
172
  name?: string; // Present for WorktreeCreate events
173
+ old_cwd?: string; // Present for CwdChanged events
174
+ new_cwd?: string; // Present for CwdChanged events
166
175
  }
167
176
 
168
177
  // Extended context interface for passing additional data to hook executor
@@ -176,6 +185,8 @@ export interface ExtendedHookExecutionContext extends HookExecutionContext {
176
185
  userPrompt?: string; // User prompt text (UserPromptSubmit only)
177
186
  subagentType?: string; // Subagent type when hook is executed by a subagent
178
187
  worktreeName?: string; // Worktree name (WorktreeCreate only)
188
+ oldCwd?: string; // Previous working directory (CwdChanged only)
189
+ newCwd?: string; // New working directory (CwdChanged only)
179
190
  }
180
191
 
181
192
  // Environment variables injected into hook processes
package/src/types/lsp.ts CHANGED
@@ -15,6 +15,8 @@ export interface LspServerConfig {
15
15
  shutdownTimeout?: number;
16
16
  restartOnCrash?: boolean;
17
17
  maxRestarts?: number;
18
+ /** Internal: plugin directory path when the server is registered by a plugin */
19
+ pluginRoot?: string;
18
20
  }
19
21
 
20
22
  export interface LspConfig {
package/src/types/mcp.ts CHANGED
@@ -9,6 +9,8 @@ export interface McpServerConfig {
9
9
  env?: Record<string, string>;
10
10
  url?: string;
11
11
  headers?: Record<string, string>;
12
+ /** Internal: plugin directory path when the server is registered by a plugin */
13
+ pluginRoot?: string;
12
14
  }
13
15
 
14
16
  export interface McpConfig {
@@ -15,6 +15,7 @@ export interface SkillMetadata {
15
15
  disableModelInvocation?: boolean;
16
16
  userInvocable?: boolean;
17
17
  pluginName?: string;
18
+ pluginRoot?: string;
18
19
  }
19
20
 
20
21
  export interface Skill extends SkillMetadata {
@@ -2,6 +2,7 @@ import { Container } from "./container.js";
2
2
  import { ForegroundTaskManager } from "../managers/foregroundTaskManager.js";
3
3
  import { BackgroundTaskManager } from "../managers/backgroundTaskManager.js";
4
4
  import { NotificationQueue } from "../managers/notificationQueue.js";
5
+ import { MessageQueue } from "../managers/messageQueue.js";
5
6
  import { TaskManager } from "../services/taskManager.js";
6
7
  import { MessageManager } from "../managers/messageManager.js";
7
8
  import { AIManager } from "../managers/aiManager.js";
@@ -76,10 +77,14 @@ export function setupAgentContainer(
76
77
  const callbacks = options.callbacks || {};
77
78
  const container = new Container();
78
79
  container.register("AgentOptions", options);
80
+ container.register("Workdir", workdir);
79
81
 
80
82
  const notificationQueue = new NotificationQueue();
81
83
  container.register("NotificationQueue", notificationQueue);
82
84
 
85
+ const messageQueue = new MessageQueue();
86
+ container.register("MessageQueue", messageQueue);
87
+
83
88
  const foregroundTaskManager = new ForegroundTaskManager(container);
84
89
  container.register("ForegroundTaskManager", foregroundTaskManager);
85
90
  container.register("ConfigurationService", configurationService);
@@ -147,7 +152,6 @@ export function setupAgentContainer(
147
152
  container.register("LspManager", lspManager);
148
153
 
149
154
  const permissionManager = new PermissionManager(container, {
150
- workdir,
151
155
  instanceAllowedRules: options.allowedTools,
152
156
  instanceDeniedRules: options.disallowedTools,
153
157
  });