wave-agent-sdk 0.6.1 → 0.6.2

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 (73) hide show
  1. package/dist/agent.d.ts +0 -5
  2. package/dist/agent.d.ts.map +1 -1
  3. package/dist/agent.js +3 -67
  4. package/dist/constants/subagents.d.ts +5 -0
  5. package/dist/constants/subagents.d.ts.map +1 -0
  6. package/dist/constants/subagents.js +4 -0
  7. package/dist/managers/aiManager.d.ts.map +1 -1
  8. package/dist/managers/aiManager.js +9 -25
  9. package/dist/managers/backgroundTaskManager.d.ts.map +1 -1
  10. package/dist/managers/backgroundTaskManager.js +4 -0
  11. package/dist/managers/messageManager.d.ts +0 -18
  12. package/dist/managers/messageManager.d.ts.map +1 -1
  13. package/dist/managers/messageManager.js +1 -36
  14. package/dist/managers/reversionManager.d.ts.map +1 -1
  15. package/dist/managers/reversionManager.js +23 -2
  16. package/dist/managers/slashCommandManager.js +1 -1
  17. package/dist/managers/subagentManager.d.ts +4 -14
  18. package/dist/managers/subagentManager.d.ts.map +1 -1
  19. package/dist/managers/subagentManager.js +35 -109
  20. package/dist/{constants/prompts.d.ts → prompts/index.d.ts} +4 -6
  21. package/dist/prompts/index.d.ts.map +1 -0
  22. package/dist/{constants/prompts.js → prompts/index.js} +124 -9
  23. package/dist/services/aiService.js +1 -1
  24. package/dist/services/taskManager.d.ts +5 -1
  25. package/dist/services/taskManager.d.ts.map +1 -1
  26. package/dist/services/taskManager.js +6 -0
  27. package/dist/tools/bashTool.d.ts.map +1 -1
  28. package/dist/tools/bashTool.js +6 -2
  29. package/dist/tools/exitPlanMode.js +1 -1
  30. package/dist/tools/taskManagementTools.d.ts.map +1 -1
  31. package/dist/tools/taskManagementTools.js +8 -0
  32. package/dist/tools/taskTool.d.ts.map +1 -1
  33. package/dist/tools/taskTool.js +32 -2
  34. package/dist/tools/types.d.ts +2 -0
  35. package/dist/tools/types.d.ts.map +1 -1
  36. package/dist/types/messaging.d.ts +1 -11
  37. package/dist/types/messaging.d.ts.map +1 -1
  38. package/dist/types/processes.d.ts +5 -0
  39. package/dist/types/processes.d.ts.map +1 -1
  40. package/dist/utils/builtinSubagents.d.ts.map +1 -1
  41. package/dist/utils/builtinSubagents.js +28 -50
  42. package/dist/utils/editUtils.d.ts.map +1 -1
  43. package/dist/utils/editUtils.js +2 -2
  44. package/dist/utils/gitUtils.d.ts +7 -0
  45. package/dist/utils/gitUtils.d.ts.map +1 -0
  46. package/dist/utils/gitUtils.js +24 -0
  47. package/dist/utils/messageOperations.d.ts +1 -23
  48. package/dist/utils/messageOperations.d.ts.map +1 -1
  49. package/dist/utils/messageOperations.js +0 -49
  50. package/package.json +1 -1
  51. package/src/agent.ts +3 -89
  52. package/src/constants/subagents.ts +4 -0
  53. package/src/managers/aiManager.ts +9 -25
  54. package/src/managers/backgroundTaskManager.ts +5 -0
  55. package/src/managers/messageManager.ts +0 -80
  56. package/src/managers/reversionManager.ts +26 -2
  57. package/src/managers/slashCommandManager.ts +1 -1
  58. package/src/managers/subagentManager.ts +42 -145
  59. package/src/{constants/prompts.ts → prompts/index.ts} +132 -12
  60. package/src/services/aiService.ts +1 -1
  61. package/src/services/taskManager.ts +8 -1
  62. package/src/tools/bashTool.ts +6 -2
  63. package/src/tools/exitPlanMode.ts +1 -1
  64. package/src/tools/taskManagementTools.ts +18 -0
  65. package/src/tools/taskTool.ts +39 -1
  66. package/src/tools/types.ts +2 -0
  67. package/src/types/messaging.ts +0 -12
  68. package/src/types/processes.ts +5 -0
  69. package/src/utils/builtinSubagents.ts +41 -51
  70. package/src/utils/editUtils.ts +2 -6
  71. package/src/utils/gitUtils.ts +24 -0
  72. package/src/utils/messageOperations.ts +1 -93
  73. package/dist/constants/prompts.d.ts.map +0 -1
@@ -1,5 +1,3 @@
1
- import * as os from "node:os";
2
- import * as path from "node:path";
3
1
  import {
4
2
  callAgent,
5
3
  compressMessages,
@@ -7,7 +5,6 @@ import {
7
5
  } from "../services/aiService.js";
8
6
  import { convertMessagesForAPI } from "../utils/convertMessagesForAPI.js";
9
7
  import { calculateComprehensiveTotalTokens } from "../utils/tokenCalculation.js";
10
- import * as fsSync from "node:fs";
11
8
  import * as fs from "node:fs/promises";
12
9
  import type {
13
10
  Logger,
@@ -23,24 +20,7 @@ import { ChatCompletionMessageFunctionToolCall } from "openai/resources.js";
23
20
  import type { HookManager } from "./hookManager.js";
24
21
  import type { ExtendedHookExecutionContext } from "../types/hooks.js";
25
22
  import type { PermissionManager } from "./permissionManager.js";
26
- import { buildSystemPrompt } from "../constants/prompts.js";
27
-
28
- function isGitRepository(dirPath: string): string {
29
- try {
30
- // Check if .git directory exists in current directory or any parent directory
31
- let currentPath = path.resolve(dirPath);
32
- while (currentPath !== path.dirname(currentPath)) {
33
- const gitPath = path.join(currentPath, ".git");
34
- if (fsSync.existsSync(gitPath)) {
35
- return "Yes";
36
- }
37
- currentPath = path.dirname(currentPath);
38
- }
39
- return "No";
40
- } catch {
41
- return "No";
42
- }
43
- }
23
+ import { buildSystemPrompt } from "../prompts/index.js";
44
24
 
45
25
  export interface AIManagerCallbacks {
46
26
  onCompressionStateChange?: (isCompressing: boolean) => void;
@@ -399,12 +379,9 @@ export class AIManager {
399
379
  filteredToolPlugins,
400
380
  {
401
381
  workdir: this.workdir,
402
- isGitRepo: isGitRepository(this.workdir),
403
- platform: os.platform(),
404
- osVersion: `${os.type()} ${os.release()}`,
405
- today: new Date().toISOString().split("T")[0],
406
382
  memory: combinedMemory,
407
383
  language: this.getLanguage(),
384
+ isSubagent: !!this.subagentType,
408
385
  planMode: planModeOptions,
409
386
  },
410
387
  ), // Pass custom system prompt
@@ -643,6 +620,13 @@ export class AIManager {
643
620
  messageId: this.messageManager.getMessages().slice(-1)[0]?.id,
644
621
  sessionId: this.messageManager.getSessionId(),
645
622
  taskManager: this.taskManager,
623
+ onShortResultUpdate: (shortResult: string) => {
624
+ this.messageManager.updateToolBlock({
625
+ id: toolId,
626
+ shortResult,
627
+ stage: "running", // Keep it in running stage while updating shortResult
628
+ });
629
+ },
646
630
  };
647
631
 
648
632
  // Execute tool
@@ -285,6 +285,11 @@ export class BackgroundTaskManager {
285
285
  }
286
286
  }
287
287
 
288
+ // If it's a subagent task, we should also notify the subagent manager to cleanup
289
+ // However, to avoid circular dependency, we rely on the onStop callback
290
+ // which is already set to instance.aiManager.abortAIMessage()
291
+ // The subagentManager.cleanupInstance will be called by the tool or by status change.
292
+
288
293
  task.status = "killed";
289
294
  task.endTime = Date.now();
290
295
  task.runtime = task.endTime - task.startTime;
@@ -6,15 +6,10 @@ import {
6
6
  addCommandOutputMessage,
7
7
  updateCommandOutputInMessage,
8
8
  completeCommandInMessage,
9
- addSubagentBlockToMessage,
10
- updateSubagentBlockInMessage,
11
9
  removeLastUserMessage,
12
10
  UserMessageParams,
13
- type AddSubagentBlockParams,
14
- type UpdateSubagentBlockParams,
15
11
  type AgentToolBlockUpdateParams,
16
12
  } from "../utils/messageOperations.js";
17
- import type { SubagentConfiguration } from "../utils/subagentParser.js";
18
13
  import type { Logger, Message, Usage, SlashCommand } from "../types/index.js";
19
14
  import { join } from "path";
20
15
  import {
@@ -59,23 +54,9 @@ export interface MessageManagerCallbacks {
59
54
  onInfoBlockAdded?: (content: string) => void;
60
55
  // Rewind callbacks
61
56
  onShowRewind?: () => void;
62
- // Subagent callbacks
63
- onSubAgentBlockAdded?: (
64
- subagentId: string,
65
- parameters: {
66
- description: string;
67
- prompt: string;
68
- subagent_type: string;
69
- },
70
- ) => void;
71
- onSubAgentBlockUpdated?: (
72
- subagentId: string,
73
- status: "active" | "completed" | "error" | "aborted",
74
- ) => void;
75
57
  onFileHistoryBlockAdded?: (
76
58
  snapshots: import("../types/reversion.js").FileSnapshot[],
77
59
  ) => void;
78
- onSubagentTaskStopRequested?: (subagentId: string) => void;
79
60
  }
80
61
 
81
62
  export interface MessageManagerOptions {
@@ -505,58 +486,6 @@ export class MessageManager {
505
486
  this.callbacks.onCompleteCommandMessage?.(command, exitCode);
506
487
  }
507
488
 
508
- // Subagent block methods
509
- public addSubagentBlock(
510
- subagentId: string,
511
- subagentName: string,
512
- sessionId: string,
513
- configuration: SubagentConfiguration,
514
- status: "active" | "completed" | "error" | "aborted" = "active",
515
- parameters: {
516
- description: string;
517
- prompt: string;
518
- subagent_type: string;
519
- },
520
- runInBackground?: boolean,
521
- ): void {
522
- const params: AddSubagentBlockParams = {
523
- messages: this.messages,
524
- subagentId,
525
- subagentName,
526
- sessionId,
527
- status,
528
- configuration,
529
- runInBackground,
530
- };
531
- const updatedMessages = addSubagentBlockToMessage(params);
532
- this.setMessages(updatedMessages);
533
- this.callbacks.onSubAgentBlockAdded?.(params.subagentId, parameters);
534
- }
535
-
536
- public updateSubagentBlock(
537
- subagentId: string,
538
- updates: Partial<{
539
- status: "active" | "completed" | "error" | "aborted";
540
- sessionId: string;
541
- runInBackground: boolean;
542
- }>,
543
- ): void {
544
- const updatedMessages = updateSubagentBlockInMessage(
545
- this.messages,
546
- subagentId,
547
- updates,
548
- );
549
- this.setMessages(updatedMessages);
550
- const params: UpdateSubagentBlockParams = {
551
- messages: this.messages,
552
- subagentId,
553
- status: updates.status || "active",
554
- };
555
- if (updates.status) {
556
- this.callbacks.onSubAgentBlockUpdated?.(params.subagentId, params.status);
557
- }
558
- }
559
-
560
489
  /**
561
490
  * Trigger usage change callback with all usage data from assistant messages
562
491
  */
@@ -773,15 +702,6 @@ export class MessageManager {
773
702
  targetIndexInSession,
774
703
  );
775
704
 
776
- // Identify subagent tasks to stop
777
- for (const message of messagesToRemove) {
778
- for (const block of message.blocks) {
779
- if (block.type === "subagent" && block.subagentId) {
780
- this.callbacks.onSubagentTaskStopRequested?.(block.subagentId);
781
- }
782
- }
783
- }
784
-
785
705
  // Update target session file
786
706
  this.sessionId = targetSessionId;
787
707
  this.rootSessionId = targetSessionData.rootSessionId || targetSessionId;
@@ -123,10 +123,15 @@ export class ReversionManager {
123
123
  const sortedSnapshots = snapshots.sort((a, b) => b.timestamp - a.timestamp);
124
124
 
125
125
  let revertedCount = 0;
126
+ const affectedTaskListIds = new Set<string>();
127
+
126
128
  for (const snapshot of sortedSnapshots) {
127
129
  try {
128
- if (!snapshot.snapshotPath) {
129
- // File didn't exist before, so delete it
130
+ if (snapshot.operation === "create") {
131
+ // For 'create' operations, we should hard delete the file upon reversion
132
+ await fs.rm(snapshot.filePath, { force: true });
133
+ } else if (!snapshot.snapshotPath) {
134
+ // Fallback for older snapshots or if snapshotPath is missing
130
135
  await fs.rm(snapshot.filePath, { force: true });
131
136
  } else {
132
137
  // Restore previous content
@@ -141,12 +146,31 @@ export class ReversionManager {
141
146
  await fs.rm(snapshot.filePath, { force: true });
142
147
  }
143
148
  }
149
+
150
+ // Check if this is a task file and track the task list ID
151
+ if (snapshot.filePath.includes("/.wave/tasks/")) {
152
+ const parts = snapshot.filePath.split("/.wave/tasks/");
153
+ if (parts.length > 1) {
154
+ const taskListId = parts[1].split("/")[0];
155
+ if (taskListId) {
156
+ affectedTaskListIds.add(taskListId);
157
+ }
158
+ }
159
+ }
160
+
144
161
  revertedCount++;
145
162
  } catch (error) {
146
163
  console.error(`Failed to revert file ${snapshot.filePath}:`, error);
147
164
  }
148
165
  }
149
166
 
167
+ // Notify TaskManager if any task lists were affected
168
+ // Note: In the current architecture, TaskManager is managed by Agent and
169
+ // listens for file changes if it's a foreground process, or we might need
170
+ // to explicitly trigger a refresh. Since ReversionManager doesn't have
171
+ // a direct reference to TaskManager, we rely on the fact that TaskManager
172
+ // will see the file system changes or be refreshed by the Agent/UI.
173
+
150
174
  return revertedCount;
151
175
  }
152
176
  }
@@ -21,7 +21,7 @@ import {
21
21
  } from "../utils/markdownParser.js";
22
22
  import { exec } from "child_process";
23
23
  import { promisify } from "util";
24
- import { INIT_PROMPT } from "../constants/prompts.js";
24
+ import { INIT_PROMPT } from "../prompts/index.js";
25
25
 
26
26
  const execAsync = promisify(exec);
27
27
 
@@ -8,20 +8,19 @@ import type {
8
8
  ModelConfig,
9
9
  Usage,
10
10
  } from "../types/index.js";
11
- import type { SessionData } from "../services/session.js";
12
11
  import { AIManager } from "./aiManager.js";
13
12
  import { MessageManager } from "./messageManager.js";
14
13
  import { ToolManager } from "./toolManager.js";
15
14
  import { HookManager } from "./hookManager.js";
16
- import {
17
- UserMessageParams,
18
- type AgentToolBlockUpdateParams,
19
- } from "../utils/messageOperations.js";
20
15
  import {
21
16
  addConsolidatedAbortListener,
22
17
  createAbortPromise,
23
18
  } from "../utils/abortUtils.js";
24
19
  import { BackgroundTaskManager } from "./backgroundTaskManager.js";
20
+ import {
21
+ UserMessageParams,
22
+ type AgentToolBlockUpdateParams,
23
+ } from "../utils/messageOperations.js";
25
24
 
26
25
  export interface SubagentManagerCallbacks {
27
26
  // Granular subagent message callbacks (015-subagent-message-callbacks)
@@ -66,14 +65,15 @@ export interface SubagentInstance {
66
65
  toolManager: ToolManager;
67
66
  status: "initializing" | "active" | "completed" | "error" | "aborted";
68
67
  messages: Message[];
68
+ lastTools: string[]; // Track last two tool names
69
69
  subagentType: string; // Store the subagent type for hook context
70
70
  backgroundTaskId?: string; // ID of the background task if transitioned
71
+ onUpdate?: () => void; // Optional callback for real-time updates
71
72
  }
72
73
 
73
74
  export interface SubagentManagerOptions {
74
75
  workdir: string;
75
76
  parentToolManager: ToolManager;
76
- parentMessageManager: MessageManager;
77
77
  taskManager: import("../services/taskManager.js").TaskManager;
78
78
  callbacks?: SubagentManagerCallbacks; // Use SubagentManagerCallbacks instead of parentCallbacks
79
79
  logger?: Logger;
@@ -93,7 +93,6 @@ export class SubagentManager {
93
93
 
94
94
  private workdir: string;
95
95
  private parentToolManager: ToolManager;
96
- private parentMessageManager: MessageManager;
97
96
  private taskManager: import("../services/taskManager.js").TaskManager;
98
97
  private callbacks?: SubagentManagerCallbacks; // Use SubagentManagerCallbacks instead of parentCallbacks
99
98
  private logger?: Logger;
@@ -109,7 +108,6 @@ export class SubagentManager {
109
108
  constructor(options: SubagentManagerOptions) {
110
109
  this.workdir = options.workdir;
111
110
  this.parentToolManager = options.parentToolManager;
112
- this.parentMessageManager = options.parentMessageManager;
113
111
  this.taskManager = options.taskManager;
114
112
  this.callbacks = options.callbacks; // Store SubagentManagerCallbacks
115
113
  this.logger = options.logger;
@@ -176,6 +174,7 @@ export class SubagentManager {
176
174
  subagent_type: string;
177
175
  },
178
176
  runInBackground?: boolean,
177
+ onUpdate?: () => void,
179
178
  ): Promise<SubagentInstance> {
180
179
  if (!this.parentToolManager) {
181
180
  throw new Error(
@@ -248,22 +247,13 @@ export class SubagentManager {
248
247
  toolManager,
249
248
  status: "initializing",
250
249
  messages: [],
250
+ lastTools: [], // Initialize lastTools
251
251
  subagentType: parameters.subagent_type, // Store the subagent type
252
+ onUpdate,
252
253
  };
253
254
 
254
255
  this.instances.set(subagentId, instance);
255
256
 
256
- // Create subagent block in parent message manager
257
- this.parentMessageManager.addSubagentBlock(
258
- subagentId,
259
- configuration.name,
260
- messageManager.getSessionId(),
261
- configuration,
262
- "active",
263
- parameters,
264
- runInBackground,
265
- );
266
-
267
257
  return instance;
268
258
  }
269
259
 
@@ -287,9 +277,6 @@ export class SubagentManager {
287
277
 
288
278
  // Set status to active and update parent
289
279
  this.updateInstanceStatus(instance.subagentId, "active");
290
- this.parentMessageManager.updateSubagentBlock(instance.subagentId, {
291
- status: "active",
292
- });
293
280
 
294
281
  if (runInBackground && this.backgroundTaskManager) {
295
282
  const taskId = this.backgroundTaskManager.generateId();
@@ -303,7 +290,11 @@ export class SubagentManager {
303
290
  description: instance.configuration.description,
304
291
  stdout: "",
305
292
  stderr: "",
306
- onStop: () => instance.aiManager.abortAIMessage(),
293
+ subagentId: instance.subagentId,
294
+ onStop: () => {
295
+ instance.aiManager.abortAIMessage();
296
+ this.cleanupInstance(instance.subagentId);
297
+ },
307
298
  });
308
299
 
309
300
  instance.backgroundTaskId = taskId;
@@ -341,9 +332,6 @@ export class SubagentManager {
341
332
  return await this.internalExecute(instance, prompt, abortSignal);
342
333
  } catch (error) {
343
334
  this.updateInstanceStatus(instance.subagentId, "error");
344
- this.parentMessageManager.updateSubagentBlock(instance.subagentId, {
345
- status: "error",
346
- });
347
335
  throw error;
348
336
  }
349
337
  }
@@ -369,16 +357,15 @@ export class SubagentManager {
369
357
  description: instance.configuration.description,
370
358
  stdout: "",
371
359
  stderr: "",
372
- onStop: () => instance.aiManager.abortAIMessage(),
360
+ subagentId: instance.subagentId,
361
+ onStop: () => {
362
+ instance.aiManager.abortAIMessage();
363
+ this.cleanupInstance(instance.subagentId);
364
+ },
373
365
  });
374
366
 
375
367
  instance.backgroundTaskId = taskId;
376
368
 
377
- // Update parent message manager to reflect background status
378
- this.parentMessageManager.updateSubagentBlock(subagentId, {
379
- runInBackground: true,
380
- });
381
-
382
369
  return taskId;
383
370
  }
384
371
 
@@ -395,9 +382,8 @@ export class SubagentManager {
395
382
  () => {
396
383
  // Update status to aborted
397
384
  this.updateInstanceStatus(instance.subagentId, "aborted");
398
- this.parentMessageManager.updateSubagentBlock(instance.subagentId, {
399
- status: "aborted",
400
- });
385
+ // Cleanup instance immediately on abort
386
+ this.cleanupInstance(instance.subagentId);
401
387
  },
402
388
  () => {
403
389
  // Abort the AI execution
@@ -474,9 +460,6 @@ export class SubagentManager {
474
460
 
475
461
  // Update status to completed and update parent
476
462
  this.updateInstanceStatus(instance.subagentId, "completed");
477
- this.parentMessageManager.updateSubagentBlock(instance.subagentId, {
478
- status: "completed",
479
- });
480
463
 
481
464
  // If this was transitioned to background, update the background task
482
465
  if (instance.backgroundTaskId && this.backgroundTaskManager) {
@@ -580,99 +563,6 @@ export class SubagentManager {
580
563
  this.instances.clear();
581
564
  }
582
565
 
583
- /**
584
- * Restore subagent instances from saved session data
585
- * This method is called during agent initialization to restore previous subagent states
586
- */
587
- async restoreSubagentSessions(
588
- subagentSessions: Array<{
589
- sessionData: SessionData;
590
- subagentId: string;
591
- configuration: SubagentConfiguration;
592
- }>,
593
- ): Promise<void> {
594
- for (const { sessionData, subagentId, configuration } of subagentSessions) {
595
- try {
596
- // Use the configuration from the SubagentBlock
597
-
598
- // Create the subagent instance without executing - just restore the state
599
-
600
- // Create MessageManager for the restored subagent
601
- const subagentCallbacks = this.createSubagentCallbacks(subagentId);
602
- const messageManager = new MessageManager({
603
- callbacks: subagentCallbacks,
604
- workdir: this.workdir,
605
- logger: this.logger,
606
- sessionType: "subagent",
607
- subagentType: configuration.name, // Use configuration name for restored sessions
608
- memoryRuleManager: this.memoryRuleManager,
609
- });
610
-
611
- // Use the parent tool manager
612
- const toolManager = this.parentToolManager;
613
-
614
- // Determine model to use
615
- let modelToUse: string;
616
- const parentModelConfig = this.getModelConfig();
617
-
618
- if (!configuration.model || configuration.model === "inherit") {
619
- modelToUse = parentModelConfig.agentModel;
620
- } else if (configuration.model === "fastModel") {
621
- modelToUse = parentModelConfig.fastModel;
622
- } else {
623
- modelToUse = configuration.model;
624
- }
625
-
626
- // Create AIManager for the restored subagent
627
- const aiManager = new AIManager({
628
- messageManager,
629
- toolManager,
630
- taskManager: this.taskManager,
631
- logger: this.logger,
632
- workdir: this.workdir,
633
- systemPrompt: configuration.systemPrompt,
634
- subagentType: configuration.name, // Use configuration name as subagent type for restored instances
635
- hookManager: this.hookManager,
636
- permissionManager: this.parentToolManager.getPermissionManager(),
637
- getGatewayConfig: this.getGatewayConfig,
638
- getModelConfig: () => ({
639
- ...parentModelConfig,
640
- agentModel: modelToUse,
641
- }),
642
- getMaxInputTokens: this.getMaxInputTokens,
643
- getLanguage: this.getLanguage,
644
- callbacks: {
645
- onUsageAdded: this.onUsageAdded,
646
- },
647
- });
648
-
649
- // Create restored instance
650
- const instance: SubagentInstance = {
651
- subagentId,
652
- configuration,
653
- aiManager,
654
- messageManager,
655
- toolManager,
656
- status: "completed", // Restored sessions are considered completed
657
- messages: sessionData.messages,
658
- subagentType: configuration.name, // Use configuration name as subagent type for restored instances
659
- };
660
-
661
- // IMPORTANT: Store instance in map BEFORE calling setMessages
662
- // This ensures the callback can find the instance
663
- this.instances.set(subagentId, instance);
664
-
665
- messageManager.initializeFromSession(sessionData);
666
- } catch (error) {
667
- this.logger?.warn(
668
- `Failed to restore subagent session ${subagentId}:`,
669
- error,
670
- );
671
- // Continue with other sessions even if one fails
672
- }
673
- }
674
- }
675
-
676
566
  /**
677
567
  * Create subagent callbacks for a specific subagent ID
678
568
  * Extracted to reuse in both create and restore flows
@@ -715,6 +605,20 @@ export class SubagentManager {
715
605
  },
716
606
 
717
607
  onToolBlockUpdated: (params: AgentToolBlockUpdateParams) => {
608
+ // Track last two tool names when a tool starts running
609
+ if (params.stage === "running" && params.name) {
610
+ const instance = this.instances.get(subagentId);
611
+ if (instance) {
612
+ // Add to lastTools if it's different from the last one or we want to show duplicates
613
+ // Based on "ToolA, ToolB" requirement, we'll just keep the last two
614
+ instance.lastTools.push(params.name);
615
+ if (instance.lastTools.length > 2) {
616
+ instance.lastTools.shift();
617
+ }
618
+ instance.onUpdate?.();
619
+ }
620
+ }
621
+
718
622
  // Forward tool block updates to parent via SubagentManager callbacks
719
623
  if (this.callbacks?.onSubagentToolBlockUpdated) {
720
624
  this.callbacks.onSubagentToolBlockUpdated(subagentId, params);
@@ -726,6 +630,8 @@ export class SubagentManager {
726
630
  const instance = this.instances.get(subagentId);
727
631
  if (instance) {
728
632
  instance.messages = messages;
633
+ // Trigger the onUpdate callback if provided
634
+ instance.onUpdate?.();
729
635
  // Forward subagent message changes to parent via callbacks
730
636
  if (this.callbacks?.onSubagentMessagesChange) {
731
637
  this.callbacks.onSubagentMessagesChange(subagentId, messages);
@@ -733,26 +639,17 @@ export class SubagentManager {
733
639
  }
734
640
  },
735
641
 
736
- onSessionIdChange: (newSessionId: string) => {
737
- // Update the subagent block with the new session ID
738
- this.parentMessageManager.updateSubagentBlock(subagentId, {
739
- sessionId: newSessionId,
740
- });
741
- },
742
-
743
642
  onLatestTotalTokensChange: (tokens: number) => {
643
+ const instance = this.instances.get(subagentId);
644
+ if (instance) {
645
+ // Trigger the onUpdate callback if provided
646
+ instance.onUpdate?.();
647
+ }
744
648
  // Forward latest total tokens to parent via SubagentManager callbacks
745
649
  if (this.callbacks?.onSubagentLatestTotalTokensChange) {
746
650
  this.callbacks.onSubagentLatestTotalTokensChange(subagentId, tokens);
747
651
  }
748
652
  },
749
-
750
- onFileHistoryBlockAdded: (
751
- snapshots: import("../types/reversion.js").FileSnapshot[],
752
- ) => {
753
- // Forward file history blocks to parent MessageManager
754
- this.parentMessageManager.addFileHistoryBlock(snapshots);
755
- },
756
653
  };
757
654
  }
758
655
  }