wave-agent-sdk 0.10.4 → 0.11.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.
Files changed (133) hide show
  1. package/dist/agent.d.ts +8 -6
  2. package/dist/agent.d.ts.map +1 -1
  3. package/dist/agent.js +12 -9
  4. package/dist/builtin-skills/builtin-skills/loop/SKILL.md +53 -0
  5. package/dist/builtin-skills/builtin-skills/loop/parsing.ts +159 -0
  6. package/dist/builtin-skills/builtin-skills/settings/HOOKS.md +82 -0
  7. package/dist/builtin-skills/{settings → builtin-skills/settings}/SKILL.md +1 -1
  8. package/dist/builtin-skills/loop/parsing.d.ts +13 -0
  9. package/dist/builtin-skills/loop/parsing.d.ts.map +1 -0
  10. package/dist/builtin-skills/loop/parsing.js +125 -0
  11. package/dist/constants/tools.d.ts +3 -0
  12. package/dist/constants/tools.d.ts.map +1 -1
  13. package/dist/constants/tools.js +3 -0
  14. package/dist/index.d.ts +1 -0
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +1 -0
  17. package/dist/managers/aiManager.d.ts +0 -2
  18. package/dist/managers/aiManager.d.ts.map +1 -1
  19. package/dist/managers/aiManager.js +53 -14
  20. package/dist/managers/cronManager.d.ts +19 -0
  21. package/dist/managers/cronManager.d.ts.map +1 -0
  22. package/dist/managers/cronManager.js +124 -0
  23. package/dist/managers/hookManager.d.ts.map +1 -1
  24. package/dist/managers/hookManager.js +21 -13
  25. package/dist/managers/liveConfigManager.js +1 -1
  26. package/dist/managers/mcpManager.d.ts +1 -1
  27. package/dist/managers/mcpManager.d.ts.map +1 -1
  28. package/dist/managers/mcpManager.js +10 -2
  29. package/dist/managers/messageManager.d.ts +0 -1
  30. package/dist/managers/messageManager.d.ts.map +1 -1
  31. package/dist/managers/permissionManager.d.ts +27 -7
  32. package/dist/managers/permissionManager.d.ts.map +1 -1
  33. package/dist/managers/permissionManager.js +119 -14
  34. package/dist/managers/slashCommandManager.d.ts.map +1 -1
  35. package/dist/managers/slashCommandManager.js +11 -0
  36. package/dist/managers/subagentManager.d.ts +3 -0
  37. package/dist/managers/subagentManager.d.ts.map +1 -1
  38. package/dist/managers/subagentManager.js +10 -17
  39. package/dist/managers/toolManager.d.ts +1 -1
  40. package/dist/managers/toolManager.d.ts.map +1 -1
  41. package/dist/managers/toolManager.js +28 -4
  42. package/dist/services/configurationService.d.ts.map +1 -1
  43. package/dist/services/configurationService.js +8 -7
  44. package/dist/services/hook.d.ts.map +1 -1
  45. package/dist/services/hook.js +3 -10
  46. package/dist/services/initializationService.js +2 -2
  47. package/dist/services/jsonlHandler.d.ts.map +1 -1
  48. package/dist/services/jsonlHandler.js +3 -0
  49. package/dist/services/reversionService.d.ts +2 -2
  50. package/dist/services/reversionService.d.ts.map +1 -1
  51. package/dist/services/reversionService.js +3 -3
  52. package/dist/services/session.d.ts.map +1 -1
  53. package/dist/services/session.js +18 -11
  54. package/dist/tools/agentTool.js +1 -1
  55. package/dist/tools/bashTool.d.ts.map +1 -1
  56. package/dist/tools/bashTool.js +5 -5
  57. package/dist/tools/cronCreateTool.d.ts +3 -0
  58. package/dist/tools/cronCreateTool.d.ts.map +1 -0
  59. package/dist/tools/cronCreateTool.js +59 -0
  60. package/dist/tools/cronDeleteTool.d.ts +3 -0
  61. package/dist/tools/cronDeleteTool.d.ts.map +1 -0
  62. package/dist/tools/cronDeleteTool.js +38 -0
  63. package/dist/tools/cronListTool.d.ts +3 -0
  64. package/dist/tools/cronListTool.d.ts.map +1 -0
  65. package/dist/tools/cronListTool.js +30 -0
  66. package/dist/tools/skillTool.d.ts +0 -3
  67. package/dist/tools/skillTool.d.ts.map +1 -1
  68. package/dist/tools/skillTool.js +4 -3
  69. package/dist/tools/taskOutputTool.d.ts.map +1 -1
  70. package/dist/tools/taskOutputTool.js +15 -8
  71. package/dist/tools/types.d.ts +2 -0
  72. package/dist/tools/types.d.ts.map +1 -1
  73. package/dist/types/agent.d.ts +10 -0
  74. package/dist/types/agent.d.ts.map +1 -1
  75. package/dist/types/configuration.d.ts +1 -1
  76. package/dist/types/configuration.d.ts.map +1 -1
  77. package/dist/types/cron.d.ts +10 -0
  78. package/dist/types/cron.d.ts.map +1 -0
  79. package/dist/types/cron.js +1 -0
  80. package/dist/types/hooks.d.ts +1 -5
  81. package/dist/types/hooks.d.ts.map +1 -1
  82. package/dist/types/hooks.js +1 -1
  83. package/dist/types/index.d.ts +1 -0
  84. package/dist/types/index.d.ts.map +1 -1
  85. package/dist/types/index.js +1 -0
  86. package/dist/types/messaging.d.ts +1 -1
  87. package/dist/types/messaging.d.ts.map +1 -1
  88. package/dist/utils/containerSetup.d.ts.map +1 -1
  89. package/dist/utils/containerSetup.js +40 -13
  90. package/dist/utils/mcpUtils.d.ts +2 -2
  91. package/dist/utils/mcpUtils.d.ts.map +1 -1
  92. package/dist/utils/mcpUtils.js +1 -5
  93. package/package.json +2 -1
  94. package/src/agent.ts +17 -12
  95. package/src/builtin-skills/loop/SKILL.md +53 -0
  96. package/src/builtin-skills/loop/parsing.ts +159 -0
  97. package/src/builtin-skills/settings/HOOKS.md +44 -57
  98. package/src/builtin-skills/settings/SKILL.md +1 -1
  99. package/src/constants/tools.ts +3 -0
  100. package/src/index.ts +1 -0
  101. package/src/managers/aiManager.ts +72 -24
  102. package/src/managers/cronManager.ts +167 -0
  103. package/src/managers/hookManager.ts +27 -13
  104. package/src/managers/liveConfigManager.ts +2 -2
  105. package/src/managers/mcpManager.ts +23 -2
  106. package/src/managers/messageManager.ts +0 -6
  107. package/src/managers/permissionManager.ts +154 -18
  108. package/src/managers/slashCommandManager.ts +12 -0
  109. package/src/managers/subagentManager.ts +15 -19
  110. package/src/managers/toolManager.ts +37 -4
  111. package/src/services/configurationService.ts +8 -7
  112. package/src/services/hook.ts +5 -11
  113. package/src/services/initializationService.ts +3 -3
  114. package/src/services/jsonlHandler.ts +4 -0
  115. package/src/services/reversionService.ts +9 -4
  116. package/src/services/session.ts +19 -12
  117. package/src/tools/agentTool.ts +1 -1
  118. package/src/tools/bashTool.ts +6 -5
  119. package/src/tools/cronCreateTool.ts +73 -0
  120. package/src/tools/cronDeleteTool.ts +47 -0
  121. package/src/tools/cronListTool.ts +38 -0
  122. package/src/tools/skillTool.ts +6 -4
  123. package/src/tools/taskOutputTool.ts +14 -8
  124. package/src/tools/types.ts +2 -0
  125. package/src/types/agent.ts +10 -0
  126. package/src/types/configuration.ts +1 -1
  127. package/src/types/cron.ts +9 -0
  128. package/src/types/hooks.ts +5 -9
  129. package/src/types/index.ts +1 -0
  130. package/src/types/messaging.ts +1 -1
  131. package/src/utils/containerSetup.ts +50 -16
  132. package/src/utils/mcpUtils.ts +2 -5
  133. package/dist/builtin-skills/settings/HOOKS.md +0 -95
@@ -377,11 +377,12 @@ Usage notes:
377
377
  combinedOutput || `Command executed with exit code: ${exitCode}`;
378
378
  const content = processOutput(finalOutput);
379
379
 
380
- const shortResult = combinedOutput
381
- .trim()
382
- .split("\n")
383
- .slice(-3)
384
- .join("\n");
380
+ const lines = combinedOutput.trim().split("\n");
381
+ const shortResult =
382
+ lines.length <= 3
383
+ ? lines.join("\n")
384
+ : lines.slice(0, 3).join("\n") +
385
+ `\n... +${lines.length - 3} lines`;
385
386
 
386
387
  resolve({
387
388
  success: exitCode === 0,
@@ -0,0 +1,73 @@
1
+ import { ToolPlugin, ToolResult, ToolContext } from "./types.js";
2
+ import { CRON_CREATE_TOOL_NAME } from "../constants/tools.js";
3
+
4
+ export const cronCreateTool: ToolPlugin = {
5
+ name: CRON_CREATE_TOOL_NAME,
6
+ config: {
7
+ type: "function",
8
+ function: {
9
+ name: CRON_CREATE_TOOL_NAME,
10
+ description:
11
+ "Schedule a prompt to be enqueued at a future time. Use for both recurring schedules and one-shot reminders.",
12
+ parameters: {
13
+ type: "object",
14
+ properties: {
15
+ cron: {
16
+ type: "string",
17
+ description:
18
+ 'Standard 5-field cron expression in local time: "M H DoM Mon DoW"',
19
+ },
20
+ prompt: {
21
+ type: "string",
22
+ description: "The prompt to enqueue at each fire time",
23
+ },
24
+ recurring: {
25
+ type: "boolean",
26
+ description:
27
+ "Default: true. true = fire on every cron match until deleted or auto-expired after 7 days. false = fire once at the next match, then auto-delete",
28
+ default: true,
29
+ },
30
+ },
31
+ required: ["cron", "prompt"],
32
+ },
33
+ },
34
+ },
35
+ execute: async (
36
+ args: Record<string, unknown>,
37
+ context: ToolContext,
38
+ ): Promise<ToolResult> => {
39
+ const {
40
+ cron,
41
+ prompt,
42
+ recurring = true,
43
+ } = args as { cron: string; prompt: string; recurring?: boolean };
44
+
45
+ if (!context.cronManager) {
46
+ return {
47
+ success: false,
48
+ content: "",
49
+ error: "CronManager not available",
50
+ };
51
+ }
52
+
53
+ try {
54
+ const job = context.cronManager.createJob({
55
+ cron,
56
+ prompt,
57
+ recurring,
58
+ });
59
+
60
+ return {
61
+ success: true,
62
+ content: JSON.stringify({ id: job.id }, null, 2),
63
+ shortResult: `Scheduled job ${job.id}`,
64
+ };
65
+ } catch (error) {
66
+ return {
67
+ success: false,
68
+ content: "",
69
+ error: error instanceof Error ? error.message : String(error),
70
+ };
71
+ }
72
+ },
73
+ };
@@ -0,0 +1,47 @@
1
+ import { ToolPlugin, ToolResult, ToolContext } from "./types.js";
2
+ import { CRON_DELETE_TOOL_NAME } from "../constants/tools.js";
3
+
4
+ export const cronDeleteTool: ToolPlugin = {
5
+ name: CRON_DELETE_TOOL_NAME,
6
+ config: {
7
+ type: "function",
8
+ function: {
9
+ name: CRON_DELETE_TOOL_NAME,
10
+ description:
11
+ "Cancel a cron job previously scheduled with CronCreate. Removes it from the in-memory session store.",
12
+ parameters: {
13
+ type: "object",
14
+ properties: {
15
+ id: {
16
+ type: "string",
17
+ description: "Job ID returned by CronCreate",
18
+ },
19
+ },
20
+ required: ["id"],
21
+ },
22
+ },
23
+ },
24
+ execute: async (
25
+ args: Record<string, unknown>,
26
+ context: ToolContext,
27
+ ): Promise<ToolResult> => {
28
+ const { id } = args as { id: string };
29
+
30
+ if (!context.cronManager) {
31
+ return {
32
+ success: false,
33
+ content: "",
34
+ error: "CronManager not available",
35
+ };
36
+ }
37
+
38
+ const success = context.cronManager.deleteJob(id);
39
+
40
+ return {
41
+ success,
42
+ content: JSON.stringify({ success }, null, 2),
43
+ shortResult: success ? `Deleted job ${id}` : `Job ${id} not found`,
44
+ error: success ? undefined : `Job ${id} not found`,
45
+ };
46
+ },
47
+ };
@@ -0,0 +1,38 @@
1
+ import { ToolPlugin, ToolResult, ToolContext } from "./types.js";
2
+ import { CRON_LIST_TOOL_NAME } from "../constants/tools.js";
3
+
4
+ export const cronListTool: ToolPlugin = {
5
+ name: CRON_LIST_TOOL_NAME,
6
+ config: {
7
+ type: "function",
8
+ function: {
9
+ name: CRON_LIST_TOOL_NAME,
10
+ description:
11
+ "List all cron jobs scheduled via CronCreate in this session.",
12
+ parameters: {
13
+ type: "object",
14
+ properties: {},
15
+ },
16
+ },
17
+ },
18
+ execute: async (
19
+ _args: Record<string, unknown>,
20
+ context: ToolContext,
21
+ ): Promise<ToolResult> => {
22
+ if (!context.cronManager) {
23
+ return {
24
+ success: false,
25
+ content: "",
26
+ error: "CronManager not available",
27
+ };
28
+ }
29
+
30
+ const jobs = context.cronManager.listJobs();
31
+
32
+ return {
33
+ success: true,
34
+ content: JSON.stringify({ jobs }, null, 2),
35
+ shortResult: `Found ${jobs.length} jobs`,
36
+ };
37
+ },
38
+ };
@@ -11,14 +11,16 @@ import {
11
11
  /**
12
12
  * Skill tool plugin for invoking Wave skills
13
13
  */
14
+ const SKILL_TOOL_DESCRIPTION =
15
+ "Execute a skill within the main conversation. When users ask you to perform tasks, check if any of the available skills match. Skills provide specialized capabilities and domain knowledge.";
16
+
14
17
  export const skillTool: ToolPlugin = {
15
18
  name: SKILL_TOOL_NAME,
16
19
  config: {
17
20
  type: "function" as const,
18
21
  function: {
19
22
  name: SKILL_TOOL_NAME,
20
- description:
21
- "Invoke a Wave skill by name. Skills are user-defined automation templates that can be personal or project-specific.",
23
+ description: SKILL_TOOL_DESCRIPTION,
22
24
  parameters: {
23
25
  type: "object",
24
26
  properties: {
@@ -41,7 +43,7 @@ export const skillTool: ToolPlugin = {
41
43
  (skill) => !skill.disableModelInvocation,
42
44
  );
43
45
  if (!availableSkills || availableSkills.length === 0) {
44
- return "Invoke a Wave skill by name. Skills are user-defined automation templates that can be personal or project-specific. No skills are currently available.";
46
+ return `${SKILL_TOOL_DESCRIPTION} No skills are currently available.`;
45
47
  }
46
48
 
47
49
  const skillList = availableSkills
@@ -50,7 +52,7 @@ export const skillTool: ToolPlugin = {
50
52
  )
51
53
  .join("\n");
52
54
 
53
- return `Invoke a Wave skill by name. Skills are user-defined automation templates that can be personal or project-specific.\n\nAvailable skills:\n${skillList}`;
55
+ return `${SKILL_TOOL_DESCRIPTION} Do not invoke the same skill repeatedly if it has already been called with the same arguments.\n\nAvailable skills:\n${skillList}`;
54
56
  },
55
57
 
56
58
  execute: async (
@@ -31,7 +31,7 @@ export const taskOutputTool: ToolPlugin = {
31
31
  description: "Max wait time in ms",
32
32
  },
33
33
  },
34
- required: ["task_id"],
34
+ required: ["task_id", "block", "timeout"],
35
35
  },
36
36
  },
37
37
  },
@@ -87,8 +87,8 @@ export const taskOutputTool: ToolPlugin = {
87
87
 
88
88
  if (finalContent.length > MAX_OUTPUT_LENGTH) {
89
89
  processedContent =
90
- finalContent.substring(0, MAX_OUTPUT_LENGTH) +
91
- "\n\n... (output truncated)";
90
+ "\n\n... (output truncated)\n" +
91
+ finalContent.substring(finalContent.length - MAX_OUTPUT_LENGTH);
92
92
  isTruncated = true;
93
93
  }
94
94
 
@@ -155,11 +155,17 @@ export const taskOutputTool: ToolPlugin = {
155
155
  if (context.abortSignal) {
156
156
  context.abortSignal.removeEventListener("abort", onAbort);
157
157
  }
158
- resolve({
159
- success: true,
160
- content: "Retrieval timed out",
161
- shortResult: `${taskId}: timeout`,
162
- });
158
+ const result = getResult();
159
+ if (result) {
160
+ result.shortResult = `${taskId}: timeout`;
161
+ resolve(result);
162
+ } else {
163
+ resolve({
164
+ success: true,
165
+ content: "Retrieval timed out",
166
+ shortResult: `${taskId}: timeout`,
167
+ });
168
+ }
163
169
  return;
164
170
  }
165
171
 
@@ -79,6 +79,8 @@ export interface ToolContext {
79
79
  subagentManager?: import("../managers/subagentManager.js").SubagentManager;
80
80
  /** Skill manager instance for skill invocation */
81
81
  skillManager?: import("../managers/skillManager.js").SkillManager;
82
+ /** Cron manager instance for scheduling tasks */
83
+ cronManager?: import("../managers/cronManager.js").CronManager;
82
84
  /** Current session ID */
83
85
  sessionId?: string;
84
86
  /** The ID of the current tool call */
@@ -67,6 +67,16 @@ export interface AgentOptions {
67
67
  isNewWorktree?: boolean;
68
68
  /**Whether to watch for skill changes - defaults to true */
69
69
  watchSkills?: boolean;
70
+ /**
71
+ * Optional list of tool names to always allow.
72
+ * These rules follow the standard permission rule syntax: `ToolName` or `ToolName(pattern)`.
73
+ */
74
+ allowedTools?: string[];
75
+ /**
76
+ * Optional list of tool names to always disallow.
77
+ * These rules follow the standard permission rule syntax: `ToolName` or `ToolName(pattern)`.
78
+ */
79
+ disallowedTools?: string[];
70
80
  }
71
81
 
72
82
  export interface AgentCallbacks
@@ -21,7 +21,7 @@ export interface WaveConfiguration {
21
21
  permissions?: {
22
22
  allow?: string[];
23
23
  deny?: string[];
24
- defaultMode?: PermissionMode; // Default permission mode for restricted tools
24
+ permissionMode?: PermissionMode; // Default permission mode for restricted tools
25
25
  /**
26
26
  * List of directories that are considered part of the Safe Zone.
27
27
  * File operations within these directories can be auto-accepted.
@@ -0,0 +1,9 @@
1
+ export interface CronJob {
2
+ id: string;
3
+ cron: string;
4
+ prompt: string;
5
+ recurring: boolean;
6
+ createdAt: number;
7
+ nextRun: number;
8
+ periodMs: number;
9
+ }
@@ -19,7 +19,7 @@ export type HookEvent =
19
19
  | "UserPromptSubmit"
20
20
  | "Stop"
21
21
  | "SubagentStop"
22
- | "Notification"
22
+ | "PermissionRequest"
23
23
  | "WorktreeCreate";
24
24
 
25
25
  // Individual hook command configuration
@@ -100,7 +100,7 @@ export function isValidHookEvent(event: string): event is HookEvent {
100
100
  "UserPromptSubmit",
101
101
  "Stop",
102
102
  "SubagentStop",
103
- "Notification",
103
+ "PermissionRequest",
104
104
  "WorktreeCreate",
105
105
  ].includes(event);
106
106
  }
@@ -154,16 +154,14 @@ export interface HookJsonInput {
154
154
  session_id: string; // Format: "wave_session_{uuid}_{shortId}"
155
155
  transcript_path: string; // Format: "~/.wave/sessions/session_{shortId}.json"
156
156
  cwd: string; // Absolute path to current working directory
157
- hook_event_name: HookEvent; // "PreToolUse" | "PostToolUse" | "UserPromptSubmit" | "Stop" | "SubagentStop" | "Notification"
157
+ hook_event_name: HookEvent; // "PreToolUse" | "PostToolUse" | "UserPromptSubmit" | "Stop" | "SubagentStop" | "PermissionRequest" | "WorktreeCreate"
158
158
 
159
159
  // Optional fields based on event type
160
- tool_name?: string; // Present for PreToolUse, PostToolUse
161
- tool_input?: unknown; // Present for PreToolUse, PostToolUse
160
+ tool_name?: string; // Present for PreToolUse, PostToolUse, PermissionRequest
161
+ tool_input?: unknown; // Present for PreToolUse, PostToolUse, PermissionRequest
162
162
  tool_response?: unknown; // Present for PostToolUse only
163
163
  user_prompt?: string; // Present for UserPromptSubmit only
164
164
  subagent_type?: string; // Present when hook is executed by a subagent
165
- message?: string; // Present for Notification events
166
- notification_type?: string; // Present for Notification events
167
165
  name?: string; // Present for WorktreeCreate events
168
166
  }
169
167
 
@@ -177,8 +175,6 @@ export interface ExtendedHookExecutionContext extends HookExecutionContext {
177
175
  env?: Record<string, string>; // Additional environment variables (from configuration)
178
176
  userPrompt?: string; // User prompt text (UserPromptSubmit only)
179
177
  subagentType?: string; // Subagent type when hook is executed by a subagent
180
- message?: string; // Notification message (Notification only)
181
- notificationType?: string; // Notification type (Notification only)
182
178
  worktreeName?: string; // Worktree name (WorktreeCreate only)
183
179
  }
184
180
 
@@ -35,3 +35,4 @@ export * from "./memoryRule.js";
35
35
  export * from "./history.js";
36
36
  export * from "./tasks.js";
37
37
  export * from "./agent.js";
38
+ export * from "./cron.js";
@@ -11,7 +11,7 @@ export enum MessageSource {
11
11
  }
12
12
 
13
13
  export interface Message {
14
- id?: string; // Unique identifier for the message
14
+ id: string; // Unique identifier for the message
15
15
  role: "user" | "assistant";
16
16
  blocks: MessageBlock[];
17
17
  usage?: Usage; // Usage data for this message's AI operation (assistant messages only)
@@ -14,6 +14,7 @@ import { SkillManager } from "../managers/skillManager.js";
14
14
  import { SlashCommandManager } from "../managers/slashCommandManager.js";
15
15
  import { PluginManager } from "../managers/pluginManager.js";
16
16
  import { BangManager } from "../managers/bangManager.js";
17
+ import { CronManager } from "../managers/cronManager.js";
17
18
  import { MemoryRuleManager } from "../managers/MemoryRuleManager.js";
18
19
  import { ReversionManager } from "../managers/reversionManager.js";
19
20
  import { SubagentManager } from "../managers/subagentManager.js";
@@ -21,6 +22,7 @@ import { LiveConfigManager } from "../managers/liveConfigManager.js";
21
22
  import { ConfigurationService } from "../services/configurationService.js";
22
23
  import { ReversionService } from "../services/reversionService.js";
23
24
  import { MemoryService } from "../services/memory.js";
25
+ import { getGitMainRepoRoot } from "./gitUtils.js";
24
26
  import type { AgentOptions } from "../types/index.js";
25
27
  import type {
26
28
  PermissionMode,
@@ -69,11 +71,17 @@ export function setupAgentContainer(
69
71
 
70
72
  const callbacks = options.callbacks || {};
71
73
  const container = new Container();
74
+ container.register("AgentOptions", options);
72
75
 
73
76
  const foregroundTaskManager = new ForegroundTaskManager(container);
74
77
  container.register("ForegroundTaskManager", foregroundTaskManager);
75
78
  container.register("ConfigurationService", configurationService);
76
79
 
80
+ if (options.worktreeName) {
81
+ container.register("WorktreeName", options.worktreeName);
82
+ container.register("MainRepoRoot", getGitMainRepoRoot(workdir));
83
+ }
84
+
77
85
  const memoryService = new MemoryService(container);
78
86
  container.register("MemoryService", memoryService);
79
87
 
@@ -130,13 +138,17 @@ export function setupAgentContainer(
130
138
  const lspManager = options.lspManager || new LspManager(container);
131
139
  container.register("LspManager", lspManager);
132
140
 
133
- const permissionManager = new PermissionManager(container, { workdir });
141
+ const permissionManager = new PermissionManager(container, {
142
+ workdir,
143
+ instanceAllowedRules: options.allowedTools,
144
+ instanceDeniedRules: options.disallowedTools,
145
+ });
134
146
  if (configurationService.resolveAutoMemoryEnabled()) {
135
147
  const autoMemoryDir = memoryService.getAutoMemoryDirectory(workdir);
136
148
  permissionManager.addSystemAdditionalDirectory(autoMemoryDir);
137
149
  }
138
150
  container.register("PermissionManager", permissionManager);
139
- permissionManager.setOnConfiguredDefaultModeChange((mode) => {
151
+ permissionManager.setOnConfiguredPermissionModeChange((mode) => {
140
152
  handlePlanModeTransition(mode);
141
153
  onPermissionModeChange(mode);
142
154
  });
@@ -153,30 +165,45 @@ export function setupAgentContainer(
153
165
  });
154
166
  container.register("SkillManager", skillManager);
155
167
 
156
- container.register(
157
- "ReversionService",
158
- new ReversionService(messageManager.getTranscriptPath()),
159
- );
168
+ const rootSessionId = messageManager.getRootSessionId();
169
+
170
+ container.register("ReversionService", new ReversionService(rootSessionId));
160
171
  const reversionManager = new ReversionManager(container);
161
172
  container.register("ReversionManager", reversionManager);
162
173
 
163
- const canUseToolWithNotification = options.canUseTool
174
+ const canUseToolWithPermissionRequest = options.canUseTool
164
175
  ? async (context: ToolPermissionContext) => {
165
176
  try {
166
- const notificationMessage = `Claude needs your permission to use ${context.toolName}`;
167
- await hookManager.executeHooks("Notification", {
168
- event: "Notification",
177
+ const results = await hookManager.executeHooks("PermissionRequest", {
178
+ event: "PermissionRequest",
169
179
  projectDir: workdir,
170
180
  timestamp: new Date(),
171
181
  sessionId: messageManager.getSessionId(),
172
182
  transcriptPath: messageManager.getTranscriptPath(),
173
183
  cwd: workdir,
174
- message: notificationMessage,
175
- notificationType: "permission_prompt",
184
+ toolName: context.toolName,
185
+ toolInput: context.toolInput,
176
186
  env: configurationService.getEnvironmentVars(),
177
187
  });
188
+
189
+ if (results.length > 0) {
190
+ const processResult = hookManager.processHookResults(
191
+ "PermissionRequest",
192
+ results,
193
+ messageManager,
194
+ );
195
+
196
+ if (processResult.shouldBlock) {
197
+ return {
198
+ behavior: "deny",
199
+ message:
200
+ processResult.errorMessage ||
201
+ "Permission denied by hook execution",
202
+ };
203
+ }
204
+ }
178
205
  } catch (error) {
179
- logger.warn("Failed to execute notification hooks", {
206
+ logger.warn("Failed to execute permission request hooks", {
180
207
  toolName: context.toolName,
181
208
  error: error instanceof Error ? error.message : String(error),
182
209
  });
@@ -215,9 +242,9 @@ export function setupAgentContainer(
215
242
 
216
243
  container.register("PermissionMode", options.permissionMode);
217
244
  logger.info("Registering CanUseToolCallback", {
218
- hasCallback: !!canUseToolWithNotification,
245
+ hasCallback: !!canUseToolWithPermissionRequest,
219
246
  });
220
- container.register("CanUseToolCallback", canUseToolWithNotification);
247
+ container.register("CanUseToolCallback", canUseToolWithPermissionRequest);
221
248
 
222
249
  const liveConfigManager = new LiveConfigManager(container, { workdir });
223
250
  container.register("LiveConfigManager", liveConfigManager);
@@ -233,11 +260,14 @@ export function setupAgentContainer(
233
260
  onSubagentAssistantReasoningUpdated:
234
261
  callbacks.onSubagentAssistantReasoningUpdated,
235
262
  onSubagentToolBlockUpdated: callbacks.onSubagentToolBlockUpdated,
236
- onSubagentMessagesChange: callbacks.onSubagentMessagesChange,
263
+ onSubagentMessagesChange: (subagentId, messages) => {
264
+ callbacks.onSubagentMessagesChange?.(subagentId, messages);
265
+ },
237
266
  onSubagentLatestTotalTokensChange:
238
267
  callbacks.onSubagentLatestTotalTokensChange,
239
268
  },
240
269
  onUsageAdded: (usage: Usage) => addUsage(usage),
270
+ stream,
241
271
  });
242
272
  container.register("SubagentManager", subagentManager);
243
273
 
@@ -262,5 +292,9 @@ export function setupAgentContainer(
262
292
  const bangManager = new BangManager(container, { workdir });
263
293
  container.register("BangManager", bangManager);
264
294
 
295
+ const cronManager = new CronManager(container);
296
+ container.register("CronManager", cronManager);
297
+ cronManager.start();
298
+
265
299
  return container;
266
300
  }
@@ -79,6 +79,7 @@ export function createMcpToolPlugin(
79
79
  executeTool: (
80
80
  name: string,
81
81
  args: Record<string, unknown>,
82
+ context?: ToolContext,
82
83
  ) => Promise<{
83
84
  success: boolean;
84
85
  content: string;
@@ -95,11 +96,7 @@ export function createMcpToolPlugin(
95
96
  context?: ToolContext,
96
97
  ): Promise<ToolResult> {
97
98
  try {
98
- // Context is available for future use when MCP tools need execution context
99
- if (context) {
100
- // Future: Could pass working directory or other context to MCP tools
101
- }
102
- const result = await executeTool(prefixedName, args);
99
+ const result = await executeTool(prefixedName, args, context);
103
100
  return {
104
101
  success: true,
105
102
  content: result.content || `Executed ${mcpTool.name}`,
@@ -1,95 +0,0 @@
1
- # Wave Hooks Configuration
2
-
3
- Hooks allow you to automate tasks when certain events occur in Wave. This document provides detailed guidance on how to configure complex hooks in `settings.json`.
4
-
5
- ## Hook Events
6
-
7
- Wave supports the following hook events:
8
-
9
- - `WorktreeCreate`: Triggered when a new worktree is created.
10
- - `TaskStart`: Triggered when a task starts.
11
- - `TaskComplete`: Triggered when a task is completed.
12
- - `TaskError`: Triggered when a task fails.
13
- - `SessionStart`: Triggered when a new session starts.
14
- - `SessionEnd`: Triggered when a session ends.
15
-
16
- ## Hook Configuration Structure
17
-
18
- Hooks are configured in the `hooks` field of `settings.json`. Each event can have multiple hook configurations.
19
-
20
- ```json
21
- {
22
- "hooks": {
23
- "WorktreeCreate": [
24
- {
25
- "command": "pnpm install",
26
- "description": "Install dependencies in new worktree",
27
- "blocking": true,
28
- "timeout": 300000
29
- }
30
- ],
31
- "TaskComplete": [
32
- {
33
- "command": "pnpm test",
34
- "description": "Run tests after task completion",
35
- "blocking": false
36
- }
37
- ]
38
- }
39
- }
40
- ```
41
-
42
- ## Hook Configuration Fields
43
-
44
- - `command`: The shell command to execute.
45
- - `description`: A brief description of the hook's purpose.
46
- - `blocking`: (Optional) Whether the hook should block the main agent's execution (default: `false`).
47
- - `timeout`: (Optional) Maximum execution time in milliseconds (default: `60000`).
48
- - `env`: (Optional) Environment variables specific to this hook.
49
- - `cwd`: (Optional) Working directory for the hook command.
50
-
51
- ## Advanced Hook Examples
52
-
53
- ### 1. Conditional Hooks
54
- You can use shell logic within the `command` field to create conditional hooks.
55
- ```json
56
- {
57
- "hooks": {
58
- "TaskComplete": [
59
- {
60
- "command": "if [ \"$WAVE_TASK_STATUS\" = \"completed\" ]; then pnpm lint; fi",
61
- "description": "Run linting only on successful task completion"
62
- }
63
- ]
64
- }
65
- }
66
- ```
67
-
68
- ### 2. Hook Chaining
69
- You can chain multiple commands in a single hook or define multiple hooks for the same event.
70
- ```json
71
- {
72
- "hooks": {
73
- "WorktreeCreate": [
74
- {
75
- "command": "pnpm install && pnpm build",
76
- "description": "Install and build in new worktree"
77
- }
78
- ]
79
- }
80
- }
81
- ```
82
-
83
- ### 3. Using Environment Variables
84
- Wave provides several environment variables to hooks:
85
- - `WAVE_PROJECT_DIR`: The root directory of the project.
86
- - `WAVE_SESSION_ID`: The current session ID.
87
- - `WAVE_TASK_ID`: The current task ID (if applicable).
88
- - `WAVE_TASK_STATUS`: The status of the task (for `TaskComplete` and `TaskError`).
89
-
90
- ## Best Practices
91
-
92
- - **Keep hooks fast**: Long-running hooks can slow down your workflow, especially if they are `blocking`.
93
- - **Use descriptive names**: Help yourself and others understand what each hook does.
94
- - **Test your hooks**: Run the commands manually first to ensure they work as expected.
95
- - **Use local overrides**: For machine-specific hooks, use `.wave/settings.local.json`.