wave-agent-sdk 0.0.5 → 0.0.7

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 (145) hide show
  1. package/dist/agent.d.ts +3 -6
  2. package/dist/agent.d.ts.map +1 -1
  3. package/dist/agent.js +24 -21
  4. package/dist/index.d.ts +3 -2
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/index.js +3 -3
  7. package/dist/managers/aiManager.d.ts +4 -2
  8. package/dist/managers/aiManager.d.ts.map +1 -1
  9. package/dist/managers/aiManager.js +91 -53
  10. package/dist/managers/backgroundBashManager.d.ts +1 -1
  11. package/dist/managers/backgroundBashManager.d.ts.map +1 -1
  12. package/dist/{hooks/manager.d.ts → managers/hookManager.d.ts} +27 -16
  13. package/dist/managers/hookManager.d.ts.map +1 -0
  14. package/dist/{hooks/manager.js → managers/hookManager.js} +112 -17
  15. package/dist/managers/mcpManager.d.ts +1 -1
  16. package/dist/managers/mcpManager.d.ts.map +1 -1
  17. package/dist/managers/messageManager.d.ts +20 -15
  18. package/dist/managers/messageManager.d.ts.map +1 -1
  19. package/dist/managers/messageManager.js +19 -25
  20. package/dist/managers/skillManager.d.ts +1 -1
  21. package/dist/managers/skillManager.d.ts.map +1 -1
  22. package/dist/managers/slashCommandManager.d.ts +1 -1
  23. package/dist/managers/slashCommandManager.d.ts.map +1 -1
  24. package/dist/managers/slashCommandManager.js +5 -2
  25. package/dist/managers/subagentManager.d.ts +7 -12
  26. package/dist/managers/subagentManager.d.ts.map +1 -1
  27. package/dist/managers/subagentManager.js +40 -46
  28. package/dist/managers/toolManager.d.ts +1 -1
  29. package/dist/managers/toolManager.d.ts.map +1 -1
  30. package/dist/services/aiService.d.ts +1 -1
  31. package/dist/services/aiService.d.ts.map +1 -1
  32. package/dist/services/aiService.js +8 -1
  33. package/dist/services/hook.d.ts +56 -0
  34. package/dist/services/hook.d.ts.map +1 -0
  35. package/dist/services/hook.js +276 -0
  36. package/dist/services/session.d.ts +1 -1
  37. package/dist/services/session.d.ts.map +1 -1
  38. package/dist/services/session.js +5 -4
  39. package/dist/tools/taskTool.d.ts.map +1 -1
  40. package/dist/tools/taskTool.js +7 -3
  41. package/dist/tools/todoWriteTool.d.ts.map +1 -1
  42. package/dist/tools/todoWriteTool.js +3 -10
  43. package/dist/types/commands.d.ts +24 -0
  44. package/dist/types/commands.d.ts.map +1 -0
  45. package/dist/types/commands.js +5 -0
  46. package/dist/types/config.d.ts +13 -0
  47. package/dist/types/config.d.ts.map +1 -0
  48. package/dist/types/config.js +5 -0
  49. package/dist/types/core.d.ts +38 -0
  50. package/dist/types/core.d.ts.map +1 -0
  51. package/dist/{types.js → types/core.js} +4 -13
  52. package/dist/{hooks/types.d.ts → types/hooks.d.ts} +2 -1
  53. package/dist/types/hooks.d.ts.map +1 -0
  54. package/dist/types/index.d.ts +20 -0
  55. package/dist/types/index.d.ts.map +1 -0
  56. package/dist/types/index.js +21 -0
  57. package/dist/types/mcp.d.ts +28 -0
  58. package/dist/types/mcp.d.ts.map +1 -0
  59. package/dist/types/mcp.js +5 -0
  60. package/dist/types/messaging.d.ts +80 -0
  61. package/dist/types/messaging.d.ts.map +1 -0
  62. package/dist/types/messaging.js +9 -0
  63. package/dist/types/processes.d.ts +17 -0
  64. package/dist/types/processes.d.ts.map +1 -0
  65. package/dist/types/processes.js +5 -0
  66. package/dist/types/skills.d.ts +78 -0
  67. package/dist/types/skills.d.ts.map +1 -0
  68. package/dist/types/skills.js +17 -0
  69. package/dist/utils/configResolver.d.ts +1 -1
  70. package/dist/utils/configResolver.d.ts.map +1 -1
  71. package/dist/utils/configResolver.js +1 -1
  72. package/dist/utils/configValidator.d.ts +1 -1
  73. package/dist/utils/configValidator.d.ts.map +1 -1
  74. package/dist/utils/configValidator.js +1 -1
  75. package/dist/utils/convertMessagesForAPI.d.ts +1 -1
  76. package/dist/utils/convertMessagesForAPI.d.ts.map +1 -1
  77. package/dist/utils/convertMessagesForAPI.js +1 -8
  78. package/dist/utils/customCommands.d.ts +1 -1
  79. package/dist/utils/customCommands.d.ts.map +1 -1
  80. package/dist/{hooks/matcher.d.ts → utils/hookMatcher.d.ts} +2 -7
  81. package/dist/utils/hookMatcher.d.ts.map +1 -0
  82. package/dist/utils/markdownParser.d.ts +1 -1
  83. package/dist/utils/markdownParser.d.ts.map +1 -1
  84. package/dist/utils/mcpUtils.d.ts +1 -1
  85. package/dist/utils/mcpUtils.d.ts.map +1 -1
  86. package/dist/utils/messageOperations.d.ts +14 -21
  87. package/dist/utils/messageOperations.d.ts.map +1 -1
  88. package/dist/utils/messageOperations.js +37 -20
  89. package/dist/utils/skillParser.d.ts +1 -1
  90. package/dist/utils/skillParser.d.ts.map +1 -1
  91. package/package.json +1 -1
  92. package/src/agent.ts +49 -43
  93. package/src/index.ts +3 -4
  94. package/src/managers/aiManager.ts +241 -160
  95. package/src/managers/backgroundBashManager.ts +1 -1
  96. package/src/{hooks/manager.ts → managers/hookManager.ts} +168 -56
  97. package/src/managers/mcpManager.ts +1 -1
  98. package/src/managers/messageManager.ts +44 -44
  99. package/src/managers/skillManager.ts +1 -1
  100. package/src/managers/slashCommandManager.ts +10 -7
  101. package/src/managers/subagentManager.ts +47 -54
  102. package/src/managers/toolManager.ts +1 -1
  103. package/src/services/aiService.ts +9 -2
  104. package/src/services/hook.ts +360 -0
  105. package/src/services/session.ts +6 -7
  106. package/src/tools/taskTool.ts +13 -5
  107. package/src/tools/todoWriteTool.ts +3 -11
  108. package/src/types/commands.ts +26 -0
  109. package/src/types/config.ts +14 -0
  110. package/src/types/core.ts +49 -0
  111. package/src/{hooks/types.ts → types/hooks.ts} +1 -0
  112. package/src/types/index.ts +23 -0
  113. package/src/types/mcp.ts +31 -0
  114. package/src/types/messaging.ts +102 -0
  115. package/src/types/processes.ts +18 -0
  116. package/src/types/skills.ts +91 -0
  117. package/src/utils/configResolver.ts +1 -1
  118. package/src/utils/configValidator.ts +5 -1
  119. package/src/utils/convertMessagesForAPI.ts +2 -10
  120. package/src/utils/customCommands.ts +1 -1
  121. package/src/{hooks/matcher.ts → utils/hookMatcher.ts} +1 -12
  122. package/src/utils/markdownParser.ts +1 -1
  123. package/src/utils/mcpUtils.ts +1 -1
  124. package/src/utils/messageOperations.ts +56 -42
  125. package/src/utils/skillParser.ts +1 -1
  126. package/dist/hooks/executor.d.ts +0 -56
  127. package/dist/hooks/executor.d.ts.map +0 -1
  128. package/dist/hooks/executor.js +0 -312
  129. package/dist/hooks/index.d.ts +0 -17
  130. package/dist/hooks/index.d.ts.map +0 -1
  131. package/dist/hooks/index.js +0 -14
  132. package/dist/hooks/manager.d.ts.map +0 -1
  133. package/dist/hooks/matcher.d.ts.map +0 -1
  134. package/dist/hooks/settings.d.ts +0 -46
  135. package/dist/hooks/settings.d.ts.map +0 -1
  136. package/dist/hooks/settings.js +0 -100
  137. package/dist/hooks/types.d.ts.map +0 -1
  138. package/dist/types.d.ts +0 -288
  139. package/dist/types.d.ts.map +0 -1
  140. package/src/hooks/executor.ts +0 -440
  141. package/src/hooks/index.ts +0 -52
  142. package/src/hooks/settings.ts +0 -129
  143. package/src/types.ts +0 -357
  144. /package/dist/{hooks/types.js → types/hooks.js} +0 -0
  145. /package/dist/{hooks/matcher.js → utils/hookMatcher.js} +0 -0
@@ -17,57 +17,30 @@ import {
17
17
  HookConfigurationError,
18
18
  isValidHookEvent,
19
19
  isValidHookEventConfig,
20
- } from "./types.js";
21
- import { type IHookMatcher, HookMatcher } from "./matcher.js";
22
- import { type IHookExecutor, HookExecutor } from "./executor.js";
23
- import { loadMergedHooksConfig } from "./settings.js";
24
- import type { Logger } from "../types.js";
25
-
26
- export interface IHookManager {
27
- // Load configuration from settings
28
- loadConfiguration(
29
- userHooks?: PartialHookConfiguration,
30
- projectHooks?: PartialHookConfiguration,
31
- ): void;
32
-
33
- // Load configuration from filesystem settings
34
- loadConfigurationFromSettings(): void;
35
-
36
- // Execute hooks for specific event
37
- executeHooks(
38
- event: HookEvent,
39
- context: HookExecutionContext | ExtendedHookExecutionContext,
40
- ): Promise<HookExecutionResult[]>;
41
-
42
- // Check if hooks are configured for event
43
- hasHooks(event: HookEvent, toolName?: string): boolean;
44
-
45
- // Validate hook configuration
46
- validateConfiguration(config: HookConfiguration): ValidationResult;
47
-
48
- // Get current configuration
49
- getConfiguration(): PartialHookConfiguration | undefined;
50
- }
51
-
52
- export class HookManager implements IHookManager {
20
+ } from "../types/hooks.js";
21
+ import { HookMatcher } from "../utils/hookMatcher.js";
22
+ import {
23
+ executeCommand,
24
+ isCommandSafe,
25
+ loadMergedHooksConfig,
26
+ } from "../services/hook.js";
27
+ import type { Logger } from "../types/index.js";
28
+ import { MessageSource } from "../types/index.js";
29
+ import type { MessageManager } from "./messageManager.js";
30
+
31
+ export class HookManager {
53
32
  private configuration: PartialHookConfiguration | undefined;
54
- private readonly matcher: IHookMatcher;
55
- private readonly executor: IHookExecutor;
33
+ private readonly matcher: HookMatcher;
56
34
  private readonly logger?: Logger;
57
35
  private readonly workdir: string;
58
36
 
59
37
  constructor(
60
38
  workdir: string,
61
- matcher: IHookMatcher = new HookMatcher(),
62
- executor?: IHookExecutor,
39
+ matcher: HookMatcher = new HookMatcher(),
63
40
  logger?: Logger,
64
41
  ) {
65
42
  this.workdir = workdir;
66
43
  this.matcher = matcher;
67
- // Create executor with logger if provided, or use passed executor, or create default
68
- this.executor = logger
69
- ? new HookExecutor(logger)
70
- : executor || new HookExecutor();
71
44
  this.logger = logger;
72
45
  }
73
46
 
@@ -112,19 +85,21 @@ export class HookManager implements IHookManager {
112
85
  this.logger?.debug(`[HookManager] Loading configuration...`);
113
86
  const mergedConfig = loadMergedHooksConfig(this.workdir);
114
87
  this.logger?.debug(`[HookManager] Merged config result:`, mergedConfig);
115
- this.configuration = mergedConfig;
116
-
117
- // Validate the loaded configuration
118
- const validation = this.validatePartialConfiguration(mergedConfig);
119
- if (!validation.valid) {
120
- throw new HookConfigurationError(
121
- "filesystem settings",
122
- validation.errors,
123
- );
88
+ this.configuration = mergedConfig || undefined;
89
+
90
+ // Validate the loaded configuration if it exists
91
+ if (mergedConfig) {
92
+ const validation = this.validatePartialConfiguration(mergedConfig);
93
+ if (!validation.valid) {
94
+ throw new HookConfigurationError(
95
+ "filesystem settings",
96
+ validation.errors,
97
+ );
98
+ }
124
99
  }
125
100
 
126
101
  this.logger?.debug(
127
- `[HookManager] Configuration loaded successfully with ${Object.keys(mergedConfig).length} event types`,
102
+ `[HookManager] Configuration loaded successfully with ${Object.keys(mergedConfig || {}).length} event types`,
128
103
  );
129
104
  } catch (error) {
130
105
  // If loading fails, start with undefined configuration (no hooks)
@@ -219,10 +194,7 @@ export class HookManager implements IHookManager {
219
194
  `[HookManager] Executing command ${commandIndex + 1}/${config.hooks.length} in configuration ${configIndex + 1}`,
220
195
  );
221
196
 
222
- const result = await this.executor.executeCommand(
223
- hookCommand.command,
224
- context,
225
- );
197
+ const result = await executeCommand(hookCommand.command, context);
226
198
  results.push(result);
227
199
 
228
200
  // Report individual command result
@@ -268,6 +240,146 @@ export class HookManager implements IHookManager {
268
240
  return results;
269
241
  }
270
242
 
243
+ /**
244
+ * Process hook execution results and determine appropriate actions
245
+ * based on exit codes and hook event type
246
+ */
247
+ processHookResults(
248
+ event: HookEvent,
249
+ results: HookExecutionResult[],
250
+ messageManager?: MessageManager,
251
+ toolId?: string,
252
+ toolParameters?: string,
253
+ ): {
254
+ shouldBlock: boolean;
255
+ errorMessage?: string;
256
+ } {
257
+ if (!messageManager || results.length === 0) {
258
+ return { shouldBlock: false };
259
+ }
260
+
261
+ // First pass: Check for any blocking errors (exit code 2)
262
+ // Blocking errors take precedence and stop all processing
263
+ for (const result of results) {
264
+ if (result.exitCode === 2) {
265
+ // Handle blocking error immediately and return
266
+ return this.handleBlockingError(
267
+ event,
268
+ result,
269
+ messageManager,
270
+ toolId,
271
+ toolParameters,
272
+ );
273
+ }
274
+ }
275
+
276
+ // Second pass: Process all non-blocking results
277
+ for (const result of results) {
278
+ if (result.exitCode === undefined) {
279
+ continue; // Skip results without exit codes
280
+ }
281
+
282
+ // Handle exit code interpretation
283
+ if (result.exitCode === 0) {
284
+ // Success case - handle stdout based on hook type
285
+ this.handleHookSuccess(event, result, messageManager);
286
+ } else {
287
+ // Non-blocking error case (any exit code except 0 and 2)
288
+ this.handleNonBlockingError(result, messageManager);
289
+ }
290
+ }
291
+
292
+ return { shouldBlock: false };
293
+ }
294
+
295
+ /**
296
+ * Handle successful hook execution (exit code 0)
297
+ */
298
+ private handleHookSuccess(
299
+ event: HookEvent,
300
+ result: HookExecutionResult,
301
+ messageManager: MessageManager,
302
+ ): void {
303
+ if (event === "UserPromptSubmit" && result.stdout?.trim()) {
304
+ // Inject stdout as user message context for UserPromptSubmit
305
+ messageManager.addUserMessage({
306
+ content: result.stdout.trim(),
307
+ source: MessageSource.HOOK,
308
+ });
309
+ }
310
+ // For other hook types (PreToolUse, PostToolUse, Stop), ignore stdout
311
+ }
312
+
313
+ /**
314
+ * Handle blocking error (exit code 2) - behavior varies by hook type
315
+ */
316
+ private handleBlockingError(
317
+ event: HookEvent,
318
+ result: HookExecutionResult,
319
+ messageManager: MessageManager,
320
+ toolId?: string,
321
+ toolParameters?: string,
322
+ ): {
323
+ shouldBlock: boolean;
324
+ errorMessage?: string;
325
+ } {
326
+ const errorMessage = result.stderr?.trim() || "Hook execution failed";
327
+
328
+ switch (event) {
329
+ case "UserPromptSubmit":
330
+ // Block prompt processing, show error to user, erase prompt
331
+ messageManager.addErrorBlock(errorMessage);
332
+ messageManager.removeLastUserMessage();
333
+ return {
334
+ shouldBlock: true,
335
+ errorMessage,
336
+ };
337
+
338
+ case "PreToolUse":
339
+ // Block tool execution and show error to Wave Agent via tool block
340
+ if (toolId) {
341
+ messageManager.updateToolBlock({
342
+ id: toolId,
343
+ parameters: toolParameters || "",
344
+ result: errorMessage,
345
+ success: false,
346
+ error: "Hook blocked tool execution",
347
+ });
348
+ }
349
+ return { shouldBlock: true };
350
+
351
+ case "PostToolUse":
352
+ // Show error to Wave Agent via user message and allow AI to continue
353
+ messageManager.addUserMessage({
354
+ content: errorMessage,
355
+ source: MessageSource.HOOK,
356
+ });
357
+ return { shouldBlock: false };
358
+
359
+ case "Stop":
360
+ // Show error to Wave Agent via user message and block stopping to continue conversation
361
+ messageManager.addUserMessage({
362
+ content: errorMessage,
363
+ source: MessageSource.HOOK,
364
+ });
365
+ return { shouldBlock: true, errorMessage };
366
+
367
+ default:
368
+ return { shouldBlock: false };
369
+ }
370
+ }
371
+
372
+ /**
373
+ * Handle non-blocking error (other exit codes)
374
+ */
375
+ private handleNonBlockingError(
376
+ result: HookExecutionResult,
377
+ messageManager: MessageManager,
378
+ ): void {
379
+ const errorMessage = result.stderr?.trim() || "Hook execution failed";
380
+ messageManager.addErrorBlock(errorMessage);
381
+ }
382
+
271
383
  /**
272
384
  * Check if hooks are configured for an event/tool combination
273
385
  */
@@ -556,7 +668,7 @@ export class HookManager implements IHookManager {
556
668
 
557
669
  // Validate commands
558
670
  config.hooks.forEach((hookCommand, cmdIndex) => {
559
- if (!this.executor.isCommandSafe(hookCommand.command)) {
671
+ if (!isCommandSafe(hookCommand.command)) {
560
672
  errors.push(
561
673
  `${prefix}.hooks[${cmdIndex}]: Command may be unsafe: ${hookCommand.command}`,
562
674
  );
@@ -11,7 +11,7 @@ import type {
11
11
  McpConfig,
12
12
  McpTool,
13
13
  McpServerStatus,
14
- } from "../types.js";
14
+ } from "../types/index.js";
15
15
 
16
16
  interface McpConnection {
17
17
  client: Client;
@@ -12,11 +12,13 @@ import {
12
12
  completeCommandInMessage,
13
13
  addSubagentBlockToMessage,
14
14
  updateSubagentBlockInMessage,
15
+ removeLastUserMessage,
16
+ UserMessageParams,
15
17
  type AddSubagentBlockParams,
16
18
  type UpdateSubagentBlockParams,
17
19
  type AgentToolBlockUpdateParams,
18
20
  } from "../utils/messageOperations.js";
19
- import type { Logger, Message, Usage } from "../types.js";
21
+ import type { Logger, Message, Usage } from "../types/index.js";
20
22
  import {
21
23
  cleanupExpiredSessions,
22
24
  getLatestSession,
@@ -34,10 +36,7 @@ export interface MessageManagerCallbacks {
34
36
  onUserInputHistoryChange?: (history: string[]) => void;
35
37
  onUsagesChange?: (usages: Usage[]) => void;
36
38
  // Incremental callback
37
- onUserMessageAdded?: (
38
- content: string,
39
- images?: Array<{ path: string; mimeType: string }>,
40
- ) => void;
39
+ onUserMessageAdded?: (params: UserMessageParams) => void;
41
40
  onAssistantMessageAdded?: (
42
41
  content?: string,
43
42
  toolCalls?: ChatCompletionMessageFunctionToolCall[],
@@ -53,19 +52,24 @@ export interface MessageManagerCallbacks {
53
52
  type: "project" | "user",
54
53
  storagePath: string,
55
54
  ) => void;
56
- // Custom command callback
57
- onCustomCommandAdded?: (
58
- commandName: string,
59
- content: string,
60
- originalInput?: string,
61
- ) => void;
62
55
  // Bash command callback
63
56
  onAddCommandOutputMessage?: (command: string) => void;
64
57
  onUpdateCommandOutputMessage?: (command: string, output: string) => void;
65
58
  onCompleteCommandMessage?: (command: string, exitCode: number) => void;
66
59
  // Subagent callbacks
67
- onSubAgentBlockAdded?: (subagentId: string) => void;
68
- onSubAgentBlockUpdated?: (subagentId: string, messages: Message[]) => void;
60
+ onSubAgentBlockAdded?: (
61
+ subagentId: string,
62
+ parameters: {
63
+ description: string;
64
+ prompt: string;
65
+ subagent_type: string;
66
+ },
67
+ ) => void;
68
+ onSubAgentBlockUpdated?: (
69
+ subagentId: string,
70
+ messages: Message[],
71
+ status: "active" | "completed" | "error" | "aborted",
72
+ ) => void;
69
73
  }
70
74
 
71
75
  export interface MessageManagerOptions {
@@ -168,7 +172,7 @@ export class MessageManager {
168
172
  try {
169
173
  await cleanupExpiredSessions(this.workdir, this.sessionDir);
170
174
  } catch (error) {
171
- console.warn("Failed to cleanup expired sessions:", error);
175
+ this.logger?.warn("Failed to cleanup expired sessions:", error);
172
176
  }
173
177
 
174
178
  if (!restoreSessionId && !continueLastSession) {
@@ -269,36 +273,13 @@ export class MessageManager {
269
273
  }
270
274
 
271
275
  // Encapsulated message operation functions
272
- public addUserMessage(
273
- content: string,
274
- images?: Array<{ path: string; mimeType: string }>,
275
- ): void {
276
- const newMessages = addUserMessageToMessages({
277
- messages: this.messages,
278
- content,
279
- images,
280
- });
281
- this.setMessages(newMessages);
282
- this.callbacks.onUserMessageAdded?.(content, images);
283
- }
284
-
285
- public addCustomCommandMessage(
286
- commandName: string,
287
- content: string,
288
- originalInput?: string,
289
- ): void {
276
+ public addUserMessage(params: UserMessageParams): void {
290
277
  const newMessages = addUserMessageToMessages({
291
278
  messages: this.messages,
292
- content: "", // Empty content, as we will use CustomCommandBlock
293
- customCommandBlock: {
294
- type: "custom_command",
295
- commandName,
296
- content,
297
- originalInput,
298
- },
279
+ ...params,
299
280
  });
300
281
  this.setMessages(newMessages);
301
- this.callbacks.onCustomCommandAdded?.(commandName, content, originalInput);
282
+ this.callbacks.onUserMessageAdded?.(params);
302
283
  }
303
284
 
304
285
  public addAssistantMessage(
@@ -319,14 +300,15 @@ export class MessageManager {
319
300
  public updateToolBlock(params: AgentToolBlockUpdateParams): void {
320
301
  const newMessages = updateToolBlockInMessage({
321
302
  messages: this.messages,
322
- id: params.toolId,
323
- parameters: params.args || "",
303
+ id: params.id,
304
+ parameters: params.parameters,
324
305
  result: params.result,
325
306
  success: params.success,
326
307
  error: params.error,
327
308
  isRunning: params.isRunning,
328
309
  name: params.name,
329
310
  shortResult: params.shortResult,
311
+ images: params.images,
330
312
  compactParams: params.compactParams,
331
313
  });
332
314
  this.setMessages(newMessages);
@@ -448,6 +430,11 @@ export class MessageManager {
448
430
  subagentName: string,
449
431
  status: "active" | "completed" | "error" = "active",
450
432
  subagentMessages: Message[] = [],
433
+ parameters: {
434
+ description: string;
435
+ prompt: string;
436
+ subagent_type: string;
437
+ },
451
438
  ): void {
452
439
  const params: AddSubagentBlockParams = {
453
440
  messages: this.messages,
@@ -458,7 +445,7 @@ export class MessageManager {
458
445
  };
459
446
  const updatedMessages = addSubagentBlockToMessage(params);
460
447
  this.setMessages(updatedMessages);
461
- this.callbacks.onSubAgentBlockAdded?.(params.subagentId);
448
+ this.callbacks.onSubAgentBlockAdded?.(params.subagentId, parameters);
462
449
  }
463
450
 
464
451
  public updateSubagentBlock(
@@ -480,7 +467,11 @@ export class MessageManager {
480
467
  status: updates.status || "active",
481
468
  subagentMessages: updates.messages || [],
482
469
  };
483
- this.callbacks.onSubAgentBlockUpdated?.(params.subagentId, params.messages);
470
+ this.callbacks.onSubAgentBlockUpdated?.(
471
+ params.subagentId,
472
+ params.messages,
473
+ params.status,
474
+ );
484
475
  }
485
476
 
486
477
  /**
@@ -495,4 +486,13 @@ export class MessageManager {
495
486
  }
496
487
  this.callbacks.onUsagesChange?.(usages);
497
488
  }
489
+
490
+ /**
491
+ * Remove the last user message from the conversation
492
+ * Used for hook error handling when the user prompt needs to be erased
493
+ */
494
+ public removeLastUserMessage(): void {
495
+ const newMessages = removeLastUserMessage(this.messages);
496
+ this.setMessages(newMessages);
497
+ }
498
498
  }
@@ -10,7 +10,7 @@ import type {
10
10
  SkillToolArgs,
11
11
  SkillInvocationContext,
12
12
  Logger,
13
- } from "../types.js";
13
+ } from "../types/index.js";
14
14
  import { parseSkillFile, formatSkillError } from "../utils/skillParser.js";
15
15
 
16
16
  /**
@@ -1,6 +1,10 @@
1
1
  import type { MessageManager } from "./messageManager.js";
2
2
  import type { AIManager } from "./aiManager.js";
3
- import type { SlashCommand, CustomSlashCommand, Logger } from "../types.js";
3
+ import type {
4
+ SlashCommand,
5
+ CustomSlashCommand,
6
+ Logger,
7
+ } from "../types/index.js";
4
8
  import { loadCustomSlashCommands } from "../utils/customCommands.js";
5
9
 
6
10
  import {
@@ -253,15 +257,14 @@ export class SlashCommandManager {
253
257
  ? replaceBashCommandsWithOutput(processedContent, bashResults)
254
258
  : processedContent;
255
259
 
256
- // Add custom command block to show the command being executed
260
+ // Add custom command message to show the command being executed
257
261
  const originalInput = args
258
262
  ? `/${commandName} ${args}`
259
263
  : `/${commandName}`;
260
- this.messageManager.addCustomCommandMessage(
261
- commandName,
262
- finalContent,
263
- originalInput,
264
- );
264
+ this.messageManager.addUserMessage({
265
+ content: originalInput,
266
+ customCommandContent: finalContent,
267
+ });
265
268
 
266
269
  // Execute the AI conversation with custom configuration
267
270
  await this.aiManager.sendAIMessage({
@@ -6,7 +6,7 @@ import type {
6
6
  GatewayConfig,
7
7
  ModelConfig,
8
8
  Usage,
9
- } from "../types.js";
9
+ } from "../types/index.js";
10
10
  import { AIManager } from "./aiManager.js";
11
11
  import {
12
12
  MessageManager,
@@ -21,7 +21,6 @@ export interface SubagentInstance {
21
21
  messageManager: MessageManager;
22
22
  toolManager: ToolManager;
23
23
  status: "initializing" | "active" | "completed" | "error" | "aborted";
24
- taskDescription: string;
25
24
  messages: Message[];
26
25
  }
27
26
 
@@ -107,7 +106,11 @@ export class SubagentManager {
107
106
  */
108
107
  async createInstance(
109
108
  configuration: SubagentConfiguration,
110
- taskDescription: string,
109
+ parameters: {
110
+ description: string;
111
+ prompt: string;
112
+ subagent_type: string;
113
+ },
111
114
  ): Promise<SubagentInstance> {
112
115
  if (
113
116
  !this.parentToolManager ||
@@ -177,7 +180,6 @@ export class SubagentManager {
177
180
  messageManager,
178
181
  toolManager,
179
182
  status: "initializing",
180
- taskDescription,
181
183
  messages: [],
182
184
  };
183
185
 
@@ -189,6 +191,7 @@ export class SubagentManager {
189
191
  configuration.name,
190
192
  "active",
191
193
  [],
194
+ parameters,
192
195
  );
193
196
 
194
197
  return instance;
@@ -203,16 +206,33 @@ export class SubagentManager {
203
206
  async executeTask(
204
207
  instance: SubagentInstance,
205
208
  prompt: string,
209
+ abortSignal?: AbortSignal,
206
210
  ): Promise<string> {
207
211
  try {
212
+ // Check if already aborted before starting
213
+ if (abortSignal?.aborted) {
214
+ throw new Error("Task was aborted before execution started");
215
+ }
216
+
208
217
  // Set status to active and update parent
209
218
  this.updateInstanceStatus(instance.subagentId, "active");
210
219
  this.parentMessageManager.updateSubagentBlock(instance.subagentId, {
211
220
  status: "active",
212
221
  });
213
222
 
223
+ // Set up abort handler
224
+ if (abortSignal) {
225
+ abortSignal.addEventListener("abort", () => {
226
+ this.updateInstanceStatus(instance.subagentId, "aborted");
227
+ this.parentMessageManager.updateSubagentBlock(instance.subagentId, {
228
+ status: "aborted",
229
+ messages: instance.messages,
230
+ });
231
+ });
232
+ }
233
+
214
234
  // Add the user's prompt as a message
215
- instance.messageManager.addUserMessage(prompt);
235
+ instance.messageManager.addUserMessage({ content: prompt });
216
236
 
217
237
  // Create allowed tools list - always exclude Task tool to prevent subagent recursion
218
238
  let allowedTools = instance.configuration.tools;
@@ -227,7 +247,9 @@ export class SubagentManager {
227
247
  }
228
248
 
229
249
  // Execute the AI request with tool restrictions
230
- await instance.aiManager.sendAIMessage({
250
+ // The AIManager will handle abort signals through its own abort controllers
251
+ // We need to abort the AI execution if the external abort signal is triggered
252
+ const executeAI = instance.aiManager.sendAIMessage({
231
253
  allowedTools,
232
254
  model:
233
255
  instance.configuration.model !== "inherit"
@@ -235,6 +257,25 @@ export class SubagentManager {
235
257
  : undefined,
236
258
  });
237
259
 
260
+ // If we have an abort signal, race against it
261
+ if (abortSignal) {
262
+ await Promise.race([
263
+ executeAI,
264
+ new Promise<never>((_, reject) => {
265
+ if (abortSignal.aborted) {
266
+ reject(new Error("Task was aborted"));
267
+ }
268
+ abortSignal.addEventListener("abort", () => {
269
+ // Abort the AI execution
270
+ instance.aiManager.abortAIMessage();
271
+ reject(new Error("Task was aborted"));
272
+ });
273
+ }),
274
+ ]);
275
+ } else {
276
+ await executeAI;
277
+ }
278
+
238
279
  // Get the latest messages to extract the response
239
280
  const messages = instance.messageManager.getMessages();
240
281
  const lastAssistantMessage = messages
@@ -298,52 +339,6 @@ export class SubagentManager {
298
339
  }
299
340
  }
300
341
 
301
- /**
302
- * Abort a running subagent instance
303
- */
304
- abortInstance(subagentId: string): boolean {
305
- const instance = this.instances.get(subagentId);
306
- if (!instance) {
307
- return false;
308
- }
309
-
310
- // Only abort active or initializing instances
311
- if (instance.status !== "active" && instance.status !== "initializing") {
312
- return false;
313
- }
314
-
315
- try {
316
- // Abort the AI manager operations
317
- instance.aiManager.abortAIMessage();
318
-
319
- // Update status
320
- this.updateInstanceStatus(subagentId, "aborted");
321
- this.parentMessageManager.updateSubagentBlock(subagentId, {
322
- status: "aborted",
323
- messages: instance.messages,
324
- });
325
-
326
- this.logger?.debug(`Aborted subagent instance: ${subagentId}`);
327
- return true;
328
- } catch (error) {
329
- this.logger?.error(
330
- `Failed to abort subagent instance ${subagentId}:`,
331
- error,
332
- );
333
- return false;
334
- }
335
- }
336
-
337
- /**
338
- * Abort all active subagent instances
339
- */
340
- abortAllInstances(): void {
341
- const activeInstances = this.getActiveInstances();
342
- for (const instance of activeInstances) {
343
- this.abortInstance(instance.subagentId);
344
- }
345
- }
346
-
347
342
  /**
348
343
  * Clean up completed, errored, or aborted instances
349
344
  */
@@ -373,8 +368,6 @@ export class SubagentManager {
373
368
  * Clean up all instances (for session end)
374
369
  */
375
370
  cleanup(): void {
376
- // Abort all active instances before cleanup
377
- this.abortAllInstances();
378
371
  this.instances.clear();
379
372
  }
380
373
  }
@@ -14,7 +14,7 @@ import { createTaskTool } from "../tools/taskTool.js";
14
14
  import { createSkillTool } from "../tools/skillTool.js";
15
15
  import { McpManager } from "./mcpManager.js";
16
16
  import { ChatCompletionFunctionTool } from "openai/resources.js";
17
- import type { Logger } from "../types.js";
17
+ import type { Logger } from "../types/index.js";
18
18
  import type { SubagentManager } from "./subagentManager.js";
19
19
  import type { SkillManager } from "./skillManager.js";
20
20