wave-agent-sdk 0.5.0 → 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 (169) hide show
  1. package/dist/agent.d.ts +14 -11
  2. package/dist/agent.d.ts.map +1 -1
  3. package/dist/agent.js +64 -151
  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/constants/tools.d.ts +4 -1
  8. package/dist/constants/tools.d.ts.map +1 -1
  9. package/dist/constants/tools.js +4 -1
  10. package/dist/index.d.ts +1 -0
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +1 -0
  13. package/dist/managers/aiManager.d.ts +2 -5
  14. package/dist/managers/aiManager.d.ts.map +1 -1
  15. package/dist/managers/aiManager.js +43 -48
  16. package/dist/managers/backgroundTaskManager.d.ts.map +1 -1
  17. package/dist/managers/backgroundTaskManager.js +63 -53
  18. package/dist/managers/foregroundTaskManager.d.ts.map +1 -1
  19. package/dist/managers/foregroundTaskManager.js +3 -2
  20. package/dist/managers/mcpManager.d.ts.map +1 -1
  21. package/dist/managers/messageManager.d.ts +13 -27
  22. package/dist/managers/messageManager.d.ts.map +1 -1
  23. package/dist/managers/messageManager.js +94 -89
  24. package/dist/managers/permissionManager.d.ts.map +1 -1
  25. package/dist/managers/permissionManager.js +25 -15
  26. package/dist/managers/planManager.d.ts +1 -1
  27. package/dist/managers/planManager.d.ts.map +1 -1
  28. package/dist/managers/planManager.js +2 -2
  29. package/dist/managers/reversionManager.d.ts.map +1 -1
  30. package/dist/managers/reversionManager.js +23 -2
  31. package/dist/managers/slashCommandManager.d.ts +3 -0
  32. package/dist/managers/slashCommandManager.d.ts.map +1 -1
  33. package/dist/managers/slashCommandManager.js +8 -3
  34. package/dist/managers/subagentManager.d.ts +8 -14
  35. package/dist/managers/subagentManager.d.ts.map +1 -1
  36. package/dist/managers/subagentManager.js +46 -112
  37. package/dist/managers/toolManager.d.ts +11 -0
  38. package/dist/managers/toolManager.d.ts.map +1 -1
  39. package/dist/managers/toolManager.js +20 -2
  40. package/dist/{constants/prompts.d.ts → prompts/index.d.ts} +17 -15
  41. package/dist/prompts/index.d.ts.map +1 -0
  42. package/dist/prompts/index.js +309 -0
  43. package/dist/services/aiService.d.ts +0 -1
  44. package/dist/services/aiService.d.ts.map +1 -1
  45. package/dist/services/aiService.js +4 -140
  46. package/dist/services/memory.d.ts +0 -3
  47. package/dist/services/memory.d.ts.map +1 -1
  48. package/dist/services/memory.js +0 -59
  49. package/dist/services/session.d.ts +15 -1
  50. package/dist/services/session.d.ts.map +1 -1
  51. package/dist/services/session.js +57 -1
  52. package/dist/services/taskManager.d.ts +25 -0
  53. package/dist/services/taskManager.d.ts.map +1 -0
  54. package/dist/services/taskManager.js +164 -0
  55. package/dist/tools/askUserQuestion.d.ts.map +1 -1
  56. package/dist/tools/askUserQuestion.js +39 -25
  57. package/dist/tools/bashTool.d.ts.map +1 -1
  58. package/dist/tools/bashTool.js +13 -11
  59. package/dist/tools/editTool.d.ts.map +1 -1
  60. package/dist/tools/editTool.js +2 -1
  61. package/dist/tools/exitPlanMode.d.ts.map +1 -1
  62. package/dist/tools/exitPlanMode.js +26 -2
  63. package/dist/tools/globTool.d.ts.map +1 -1
  64. package/dist/tools/globTool.js +8 -2
  65. package/dist/tools/grepTool.d.ts.map +1 -1
  66. package/dist/tools/grepTool.js +17 -6
  67. package/dist/tools/lsTool.d.ts.map +1 -1
  68. package/dist/tools/lsTool.js +3 -1
  69. package/dist/tools/readTool.d.ts.map +1 -1
  70. package/dist/tools/readTool.js +16 -1
  71. package/dist/tools/taskManagementTools.d.ts +6 -0
  72. package/dist/tools/taskManagementTools.d.ts.map +1 -0
  73. package/dist/tools/taskManagementTools.js +461 -0
  74. package/dist/tools/taskOutputTool.d.ts.map +1 -1
  75. package/dist/tools/taskOutputTool.js +32 -8
  76. package/dist/tools/taskStopTool.d.ts.map +1 -1
  77. package/dist/tools/taskStopTool.js +7 -1
  78. package/dist/tools/taskTool.d.ts.map +1 -1
  79. package/dist/tools/taskTool.js +37 -2
  80. package/dist/tools/types.d.ts +11 -0
  81. package/dist/tools/types.d.ts.map +1 -1
  82. package/dist/tools/writeTool.d.ts.map +1 -1
  83. package/dist/tools/writeTool.js +9 -1
  84. package/dist/types/index.d.ts +1 -0
  85. package/dist/types/index.d.ts.map +1 -1
  86. package/dist/types/index.js +1 -0
  87. package/dist/types/messaging.d.ts +2 -18
  88. package/dist/types/messaging.d.ts.map +1 -1
  89. package/dist/types/processes.d.ts +16 -6
  90. package/dist/types/processes.d.ts.map +1 -1
  91. package/dist/types/tasks.d.ts +13 -0
  92. package/dist/types/tasks.d.ts.map +1 -0
  93. package/dist/types/tasks.js +1 -0
  94. package/dist/types/tools.d.ts +4 -1
  95. package/dist/types/tools.d.ts.map +1 -1
  96. package/dist/utils/builtinSubagents.d.ts.map +1 -1
  97. package/dist/utils/builtinSubagents.js +59 -44
  98. package/dist/utils/cacheControlUtils.d.ts.map +1 -1
  99. package/dist/utils/cacheControlUtils.js +18 -12
  100. package/dist/utils/constants.d.ts +0 -4
  101. package/dist/utils/constants.d.ts.map +1 -1
  102. package/dist/utils/constants.js +0 -4
  103. package/dist/utils/convertMessagesForAPI.js +2 -2
  104. package/dist/utils/editUtils.d.ts.map +1 -1
  105. package/dist/utils/editUtils.js +2 -2
  106. package/dist/utils/gitUtils.d.ts +7 -0
  107. package/dist/utils/gitUtils.d.ts.map +1 -0
  108. package/dist/utils/gitUtils.js +24 -0
  109. package/dist/utils/messageOperations.d.ts +3 -58
  110. package/dist/utils/messageOperations.d.ts.map +1 -1
  111. package/dist/utils/messageOperations.js +4 -146
  112. package/dist/utils/nameGenerator.d.ts +1 -1
  113. package/dist/utils/nameGenerator.d.ts.map +1 -1
  114. package/dist/utils/nameGenerator.js +19 -3
  115. package/package.json +1 -1
  116. package/src/agent.ts +86 -183
  117. package/src/constants/subagents.ts +4 -0
  118. package/src/constants/tools.ts +4 -1
  119. package/src/index.ts +1 -0
  120. package/src/managers/aiManager.ts +63 -70
  121. package/src/managers/backgroundTaskManager.ts +58 -54
  122. package/src/managers/foregroundTaskManager.ts +3 -2
  123. package/src/managers/mcpManager.ts +6 -3
  124. package/src/managers/messageManager.ts +126 -142
  125. package/src/managers/permissionManager.ts +32 -21
  126. package/src/managers/planManager.ts +2 -2
  127. package/src/managers/reversionManager.ts +26 -2
  128. package/src/managers/slashCommandManager.ts +12 -3
  129. package/src/managers/subagentManager.ts +60 -144
  130. package/src/managers/toolManager.ts +32 -2
  131. package/src/prompts/index.ts +366 -0
  132. package/src/services/aiService.ts +3 -145
  133. package/src/services/memory.ts +0 -72
  134. package/src/services/session.ts +73 -0
  135. package/src/services/taskManager.ts +195 -0
  136. package/src/tools/askUserQuestion.ts +51 -29
  137. package/src/tools/bashTool.ts +15 -17
  138. package/src/tools/editTool.ts +3 -1
  139. package/src/tools/exitPlanMode.ts +27 -3
  140. package/src/tools/globTool.ts +10 -2
  141. package/src/tools/grepTool.ts +17 -6
  142. package/src/tools/lsTool.ts +3 -1
  143. package/src/tools/readTool.ts +17 -1
  144. package/src/tools/taskManagementTools.ts +516 -0
  145. package/src/tools/taskOutputTool.ts +34 -12
  146. package/src/tools/taskStopTool.ts +7 -1
  147. package/src/tools/taskTool.ts +45 -1
  148. package/src/tools/types.ts +12 -0
  149. package/src/tools/writeTool.ts +9 -2
  150. package/src/types/index.ts +1 -0
  151. package/src/types/messaging.ts +1 -21
  152. package/src/types/processes.ts +18 -7
  153. package/src/types/tasks.ts +13 -0
  154. package/src/types/tools.ts +4 -1
  155. package/src/utils/builtinSubagents.ts +81 -45
  156. package/src/utils/cacheControlUtils.ts +26 -18
  157. package/src/utils/constants.ts +0 -5
  158. package/src/utils/convertMessagesForAPI.ts +2 -2
  159. package/src/utils/editUtils.ts +2 -6
  160. package/src/utils/gitUtils.ts +24 -0
  161. package/src/utils/messageOperations.ts +6 -229
  162. package/src/utils/nameGenerator.ts +20 -3
  163. package/dist/constants/prompts.d.ts.map +0 -1
  164. package/dist/constants/prompts.js +0 -118
  165. package/dist/tools/todoWriteTool.d.ts +0 -6
  166. package/dist/tools/todoWriteTool.d.ts.map +0 -1
  167. package/dist/tools/todoWriteTool.js +0 -220
  168. package/src/constants/prompts.ts +0 -155
  169. package/src/tools/todoWriteTool.ts +0 -257
@@ -3,19 +3,13 @@ import {
3
3
  updateToolBlockInMessage,
4
4
  addErrorBlockToMessage,
5
5
  addUserMessageToMessages,
6
- extractUserInputHistory,
7
6
  addCommandOutputMessage,
8
7
  updateCommandOutputInMessage,
9
8
  completeCommandInMessage,
10
- addSubagentBlockToMessage,
11
- updateSubagentBlockInMessage,
12
9
  removeLastUserMessage,
13
10
  UserMessageParams,
14
- type AddSubagentBlockParams,
15
- type UpdateSubagentBlockParams,
16
11
  type AgentToolBlockUpdateParams,
17
12
  } from "../utils/messageOperations.js";
18
- import type { SubagentConfiguration } from "../utils/subagentParser.js";
19
13
  import type { Logger, Message, Usage, SlashCommand } from "../types/index.js";
20
14
  import { join } from "path";
21
15
  import {
@@ -33,7 +27,6 @@ export interface MessageManagerCallbacks {
33
27
  onMessagesChange?: (messages: Message[]) => void;
34
28
  onSessionIdChange?: (sessionId: string) => void;
35
29
  onLatestTotalTokensChange?: (latestTotalTokens: number) => void;
36
- onUserInputHistoryChange?: (history: string[]) => void;
37
30
  onUsagesChange?: (usages: Usage[]) => void;
38
31
  // Incremental callback
39
32
  onUserMessageAdded?: (params: UserMessageParams) => void;
@@ -45,7 +38,7 @@ export interface MessageManagerCallbacks {
45
38
  onAssistantReasoningUpdated?: (chunk: string, accumulated: string) => void;
46
39
  onToolBlockUpdated?: (params: AgentToolBlockUpdateParams) => void;
47
40
  onErrorBlockAdded?: (error: string) => void;
48
- onCompressBlockAdded?: (insertIndex: number, content: string) => void;
41
+ onCompressBlockAdded?: (content: string) => void;
49
42
  onCompressionStateChange?: (isCompressing: boolean) => void;
50
43
  onMemoryBlockAdded?: (
51
44
  content: string,
@@ -61,18 +54,8 @@ export interface MessageManagerCallbacks {
61
54
  onInfoBlockAdded?: (content: string) => void;
62
55
  // Rewind callbacks
63
56
  onShowRewind?: () => void;
64
- // Subagent callbacks
65
- onSubAgentBlockAdded?: (
66
- subagentId: string,
67
- parameters: {
68
- description: string;
69
- prompt: string;
70
- subagent_type: string;
71
- },
72
- ) => void;
73
- onSubAgentBlockUpdated?: (
74
- subagentId: string,
75
- status: "active" | "completed" | "error" | "aborted",
57
+ onFileHistoryBlockAdded?: (
58
+ snapshots: import("../types/reversion.js").FileSnapshot[],
76
59
  ) => void;
77
60
  }
78
61
 
@@ -88,9 +71,10 @@ export interface MessageManagerOptions {
88
71
  export class MessageManager {
89
72
  // Private state properties
90
73
  private sessionId: string;
74
+ private rootSessionId: string;
75
+ private parentSessionId?: string;
91
76
  private messages: Message[];
92
77
  private latestTotalTokens: number;
93
- private userInputHistory: string[];
94
78
  private workdir: string;
95
79
  private encodedWorkdir: string; // Cached encoded workdir
96
80
  private logger?: Logger; // Add optional logger property
@@ -104,9 +88,9 @@ export class MessageManager {
104
88
 
105
89
  constructor(options: MessageManagerOptions) {
106
90
  this.sessionId = generateSessionId();
91
+ this.rootSessionId = this.sessionId;
107
92
  this.messages = [];
108
93
  this.latestTotalTokens = 0;
109
- this.userInputHistory = [];
110
94
  this.workdir = options.workdir;
111
95
  this.encodedWorkdir = pathEncoder.encodeSync(this.workdir); // Cache encoded workdir
112
96
  this.callbacks = options.callbacks;
@@ -125,6 +109,14 @@ export class MessageManager {
125
109
  return this.sessionId;
126
110
  }
127
111
 
112
+ public getRootSessionId(): string {
113
+ return this.rootSessionId;
114
+ }
115
+
116
+ public getParentSessionId(): string | undefined {
117
+ return this.parentSessionId;
118
+ }
119
+
128
120
  public getMessages(): Message[] {
129
121
  return [...this.messages];
130
122
  }
@@ -133,10 +125,6 @@ export class MessageManager {
133
125
  return this.latestTotalTokens;
134
126
  }
135
127
 
136
- public getUserInputHistory(): string[] {
137
- return [...this.userInputHistory];
138
- }
139
-
140
128
  public getWorkdir(): string {
141
129
  return this.workdir;
142
130
  }
@@ -247,6 +235,8 @@ export class MessageManager {
247
235
  unsavedMessages, // Only append new messages
248
236
  this.workdir,
249
237
  this.sessionType,
238
+ this.rootSessionId,
239
+ this.parentSessionId,
250
240
  );
251
241
 
252
242
  // Update the saved message count
@@ -264,18 +254,13 @@ export class MessageManager {
264
254
  }
265
255
  }
266
256
 
267
- public setUserInputHistory(userInputHistory: string[]): void {
268
- this.userInputHistory = [...userInputHistory];
269
- this.callbacks.onUserInputHistoryChange?.(this.userInputHistory);
270
- }
271
-
272
257
  /**
273
- * Clear messages and input history
258
+ * Clear messages
274
259
  */
275
260
  public clearMessages(): void {
276
261
  this.setMessages([]);
277
- this.setUserInputHistory([]);
278
262
  this.setSessionId(generateSessionId());
263
+ this.rootSessionId = this.sessionId;
279
264
  this.setlatestTotalTokens(0);
280
265
  this.savedMessageCount = 0; // Reset saved message count
281
266
  }
@@ -297,36 +282,17 @@ export class MessageManager {
297
282
  // Initialize state from session data
298
283
  public initializeFromSession(sessionData: SessionData): void {
299
284
  this.setSessionId(sessionData.id);
285
+ this.rootSessionId = sessionData.rootSessionId || sessionData.id;
286
+ this.parentSessionId = sessionData.parentSessionId;
300
287
  this.setMessages([...sessionData.messages]);
301
288
  this.updateFilesInContext(sessionData.messages);
302
289
  this.setlatestTotalTokens(sessionData.metadata.latestTotalTokens);
303
290
 
304
- // Extract user input history from session messages
305
- this.setUserInputHistory(extractUserInputHistory(sessionData.messages));
306
-
307
291
  // Set saved message count to the number of loaded messages since they're already saved
308
292
  // This must be done after setSessionId which resets it to 0
309
293
  this.savedMessageCount = sessionData.messages.length;
310
294
  }
311
295
 
312
- // Add to input history
313
- public addToInputHistory(input: string): void {
314
- // Avoid adding duplicate inputs
315
- if (
316
- this.userInputHistory.length > 0 &&
317
- this.userInputHistory[this.userInputHistory.length - 1] === input
318
- ) {
319
- return;
320
- }
321
- // Limit history records, keep the latest 100
322
- this.setUserInputHistory([...this.userInputHistory, input].slice(-100));
323
- }
324
-
325
- // Clear input history
326
- public clearInputHistory(): void {
327
- this.setUserInputHistory([]);
328
- }
329
-
330
296
  // Encapsulated message operation functions
331
297
  public addUserMessage(params: UserMessageParams): void {
332
298
  const newMessages = addUserMessageToMessages({
@@ -402,17 +368,7 @@ export class MessageManager {
402
368
  public updateToolBlock(params: AgentToolBlockUpdateParams): void {
403
369
  const newMessages = updateToolBlockInMessage({
404
370
  messages: this.messages,
405
- id: params.id,
406
- parameters: params.parameters,
407
- result: params.result,
408
- success: params.success,
409
- error: params.error,
410
- stage: params.stage,
411
- name: params.name,
412
- shortResult: params.shortResult,
413
- images: params.images,
414
- compactParams: params.compactParams,
415
- parametersChunk: params.parametersChunk,
371
+ ...params,
416
372
  });
417
373
  this.setMessages(newMessages);
418
374
  this.callbacks.onToolBlockUpdated?.(params);
@@ -442,14 +398,14 @@ export class MessageManager {
442
398
  }
443
399
 
444
400
  /**
445
- * Compress messages and update session, delete compressed messages, only keep compressed messages and subsequent messages
401
+ * Compress messages and update session, delete compressed messages, only keep compressed messages and last 3 messages
446
402
  */
447
403
  public compressMessagesAndUpdateSession(
448
- insertIndex: number,
449
404
  compressedContent: string,
450
405
  usage?: Usage,
451
406
  ): void {
452
- const currentMessages = this.messages;
407
+ // Get last 3 messages to preserve
408
+ const lastThreeMessages = this.messages.slice(-3);
453
409
 
454
410
  // Create compressed message
455
411
  const compressMessage: Message = {
@@ -464,24 +420,24 @@ export class MessageManager {
464
420
  ...(usage && { usage }),
465
421
  };
466
422
 
467
- // Convert negative index to positive index
468
- const actualIndex =
469
- insertIndex < 0 ? currentMessages.length + insertIndex : insertIndex;
423
+ // Build new message array: keep the compressed message and last 3 messages
424
+ const newMessages: Message[] = [compressMessage, ...lastThreeMessages];
470
425
 
471
- // Build new message array: keep compressed message and all messages from actualIndex onwards
472
- const newMessages: Message[] = [
473
- compressMessage,
474
- ...currentMessages.slice(actualIndex),
475
- ];
476
-
477
- // Update sessionId
426
+ // Update sessionId and parentSessionId
427
+ const oldSessionId = this.sessionId;
478
428
  this.setSessionId(generateSessionId());
429
+ this.parentSessionId = oldSessionId;
430
+
431
+ // Trigger task list update if this is the main session to ensure continuity
432
+ if (this.sessionType === "main") {
433
+ this.callbacks.onSessionIdChange?.(this.sessionId);
434
+ }
479
435
 
480
436
  // Set new message list
481
437
  this.setMessages(newMessages);
482
438
 
483
- // Trigger compression callback, insertIndex remains unchanged
484
- this.callbacks.onCompressBlockAdded?.(insertIndex, compressedContent);
439
+ // Trigger compression callback
440
+ this.callbacks.onCompressBlockAdded?.(compressedContent);
485
441
  }
486
442
 
487
443
  public addFileHistoryBlock(
@@ -496,6 +452,7 @@ export class MessageManager {
496
452
  snapshots,
497
453
  } as unknown as import("../types/index.js").MessageBlock);
498
454
  this.setMessages([...this.messages]);
455
+ this.callbacks.onFileHistoryBlockAdded?.(snapshots);
499
456
  }
500
457
  }
501
458
 
@@ -529,58 +486,6 @@ export class MessageManager {
529
486
  this.callbacks.onCompleteCommandMessage?.(command, exitCode);
530
487
  }
531
488
 
532
- // Subagent block methods
533
- public addSubagentBlock(
534
- subagentId: string,
535
- subagentName: string,
536
- sessionId: string,
537
- configuration: SubagentConfiguration,
538
- status: "active" | "completed" | "error" | "aborted" = "active",
539
- parameters: {
540
- description: string;
541
- prompt: string;
542
- subagent_type: string;
543
- },
544
- runInBackground?: boolean,
545
- ): void {
546
- const params: AddSubagentBlockParams = {
547
- messages: this.messages,
548
- subagentId,
549
- subagentName,
550
- sessionId,
551
- status,
552
- configuration,
553
- runInBackground,
554
- };
555
- const updatedMessages = addSubagentBlockToMessage(params);
556
- this.setMessages(updatedMessages);
557
- this.callbacks.onSubAgentBlockAdded?.(params.subagentId, parameters);
558
- }
559
-
560
- public updateSubagentBlock(
561
- subagentId: string,
562
- updates: Partial<{
563
- status: "active" | "completed" | "error" | "aborted";
564
- sessionId: string;
565
- runInBackground: boolean;
566
- }>,
567
- ): void {
568
- const updatedMessages = updateSubagentBlockInMessage(
569
- this.messages,
570
- subagentId,
571
- updates,
572
- );
573
- this.setMessages(updatedMessages);
574
- const params: UpdateSubagentBlockParams = {
575
- messages: this.messages,
576
- subagentId,
577
- status: updates.status || "active",
578
- };
579
- if (updates.status) {
580
- this.callbacks.onSubAgentBlockUpdated?.(params.subagentId, params.status);
581
- }
582
- }
583
-
584
489
  /**
585
490
  * Trigger usage change callback with all usage data from assistant messages
586
491
  */
@@ -703,6 +608,14 @@ export class MessageManager {
703
608
  this.setMessages(newMessages);
704
609
  }
705
610
 
611
+ public async getFullMessageThread(): Promise<{
612
+ messages: Message[];
613
+ sessionIds: string[];
614
+ }> {
615
+ const { loadFullMessageThread } = await import("../services/session.js");
616
+ return loadFullMessageThread(this.sessionId, this.workdir);
617
+ }
618
+
706
619
  /**
707
620
  * Truncate history to a specific index and revert file changes.
708
621
  * @param index - The index of the user message to truncate to.
@@ -712,30 +625,101 @@ export class MessageManager {
712
625
  index: number,
713
626
  reversionManager?: import("./reversionManager.js").ReversionManager,
714
627
  ): Promise<void> {
715
- if (index < 0 || index >= this.messages.length) {
628
+ const { messages, sessionIds } = await this.getFullMessageThread();
629
+
630
+ if (index < 0 || index >= messages.length) {
716
631
  throw new Error(`Invalid message index: ${index}`);
717
632
  }
718
633
 
719
- // Identify messages to be removed
720
- const messagesToRemove = this.messages.slice(index);
634
+ // Find which session the index belongs to
635
+ let targetSessionId = this.sessionId;
636
+ let targetIndexInSession = index;
637
+
638
+ // We need to be careful here because loadFullMessageThread might have removed "compress" blocks
639
+ // Let's re-calculate based on the actual messages returned.
640
+ // Actually, it's easier to just load sessions one by one again or keep track of counts.
641
+
642
+ // For simplicity, let's assume we want to truncate the WHOLE thread.
643
+ // If the index is in a previous session, we need to:
644
+ // 1. Load that session.
645
+ // 2. Truncate it.
646
+ // 3. Make it the current session.
647
+ // 4. Delete/Invalidate subsequent sessions.
648
+
649
+ // To correctly map 'index' to a session, we need to know the message count of each session
650
+ // as they appear in the concatenated 'messages' array.
651
+
652
+ let remainingIndex = index;
653
+ const { loadSessionFromJsonl } = await import("../services/session.js");
654
+
655
+ for (const sid of sessionIds) {
656
+ const sessionData = await loadSessionFromJsonl(sid, this.workdir);
657
+ if (!sessionData) continue;
658
+
659
+ const sessionMessages = sessionData.messages;
660
+ // If this is not the first session in the thread, it might have a compress block at the start
661
+ // that was removed in getFullMessageThread.
662
+ const hasCompressBlock = sessionMessages[0]?.blocks.some(
663
+ (b) => b.type === "compress",
664
+ );
665
+ const effectiveMessages =
666
+ hasCompressBlock && sid !== sessionIds[0]
667
+ ? sessionMessages.slice(1)
668
+ : sessionMessages;
669
+
670
+ if (remainingIndex < effectiveMessages.length) {
671
+ targetSessionId = sid;
672
+ targetIndexInSession = hasCompressBlock
673
+ ? remainingIndex + 1
674
+ : remainingIndex;
675
+ break;
676
+ }
677
+ remainingIndex -= effectiveMessages.length;
678
+ }
679
+
680
+ // Load the target session to perform truncation
681
+ const targetSessionData = await loadSessionFromJsonl(
682
+ targetSessionId,
683
+ this.workdir,
684
+ );
685
+ if (!targetSessionData)
686
+ throw new Error(`Target session ${targetSessionId} not found`);
687
+
688
+ // Identify messages to be removed (from the whole thread)
689
+ const messagesToRemove = messages.slice(index);
721
690
  const messageIdsToRemove = messagesToRemove
722
691
  .map((m) => m.id as string)
723
692
  .filter((id) => !!id);
724
693
 
725
694
  // Revert file changes if manager is provided
726
695
  if (reversionManager && messageIdsToRemove.length > 0) {
727
- await reversionManager.revertTo(messageIdsToRemove, this.messages);
696
+ await reversionManager.revertTo(messageIdsToRemove, messages);
728
697
  }
729
698
 
730
- // Truncate messages in memory
731
- const newMessages = this.messages.slice(0, index);
732
- this.setMessages(newMessages);
699
+ // Truncate messages in the target session
700
+ const newMessagesInSession = targetSessionData.messages.slice(
701
+ 0,
702
+ targetIndexInSession,
703
+ );
733
704
 
734
- // Update persistence: rewrite the session file
735
- await this.rewriteSessionFile(newMessages);
705
+ // Update target session file
706
+ this.sessionId = targetSessionId;
707
+ this.rootSessionId = targetSessionData.rootSessionId || targetSessionId;
708
+ this.parentSessionId = targetSessionData.parentSessionId;
709
+ this.transcriptPath = this.computeTranscriptPath();
710
+
711
+ await this.rewriteSessionFile(newMessagesInSession);
712
+
713
+ // Update in-memory messages to the truncated session messages
714
+ // We do NOT include ancestor messages here to avoid exceeding context limits.
715
+ // The 'compress' block at the start of the session (if any) already summarizes them.
716
+ this.setMessages(newMessagesInSession);
736
717
 
737
718
  // Update saved message count
738
- this.savedMessageCount = newMessages.length;
719
+ this.savedMessageCount = newMessagesInSession.length;
720
+
721
+ // Notify session ID change if it changed
722
+ this.callbacks.onSessionIdChange?.(this.sessionId);
739
723
  }
740
724
 
741
725
  /**
@@ -323,40 +323,30 @@ export class PermissionManager {
323
323
  workdir,
324
324
  );
325
325
  if (!isInside) {
326
- this.logger?.warn(
327
- "File operation outside the Safe Zone in acceptEdits mode",
326
+ this.logger?.info(
327
+ "File operation outside the Safe Zone in acceptEdits mode, falling back to manual confirmation",
328
328
  {
329
329
  toolName: context.toolName,
330
330
  targetPath,
331
331
  resolvedPath,
332
332
  },
333
333
  );
334
- return {
335
- behavior: "deny",
336
- message: `Tool '${context.toolName}' attempted to modify a file outside the Safe Zone: ${targetPath}. Operations outside the Safe Zone always require manual confirmation.`,
337
- };
334
+ // Fall through to normal permission check flow to trigger confirmation prompt
335
+ } else {
336
+ this.logger?.debug(
337
+ "Permission automatically accepted for tool in acceptEdits mode",
338
+ {
339
+ toolName: context.toolName,
340
+ },
341
+ );
342
+ return { behavior: "allow" };
338
343
  }
339
344
  }
340
-
341
- this.logger?.debug(
342
- "Permission automatically accepted for tool in acceptEdits mode",
343
- {
344
- toolName: context.toolName,
345
- },
346
- );
347
- return { behavior: "allow" };
348
345
  }
349
346
  }
350
347
 
351
348
  // 1.3 If plan mode, allow Read-only tools and Edit/Write for plan file
352
349
  if (context.permissionMode === "plan") {
353
- if (context.toolName === BASH_TOOL_NAME) {
354
- return {
355
- behavior: "deny",
356
- message: "Bash commands are not allowed in plan mode.",
357
- };
358
- }
359
-
360
350
  const writeTools = [
361
351
  EDIT_TOOL_NAME,
362
352
  MULTI_EDIT_TOOL_NAME,
@@ -492,6 +482,27 @@ export class PermissionManager {
492
482
  suggestedPrefix,
493
483
  };
494
484
 
485
+ // Set hidePersistentOption for out-of-bounds file operations
486
+ const fileTools = [
487
+ EDIT_TOOL_NAME,
488
+ MULTI_EDIT_TOOL_NAME,
489
+ DELETE_FILE_TOOL_NAME,
490
+ WRITE_TOOL_NAME,
491
+ ];
492
+ if (fileTools.includes(toolName)) {
493
+ const targetPath = (toolInput?.file_path || toolInput?.target_file) as
494
+ | string
495
+ | undefined;
496
+ const workdir = toolInput?.workdir as string | undefined;
497
+
498
+ if (targetPath) {
499
+ const { isInside } = this.isInsideSafeZone(targetPath, workdir);
500
+ if (!isInside) {
501
+ context.hidePersistentOption = true;
502
+ }
503
+ }
504
+ }
505
+
495
506
  // Set hidePersistentOption for dangerous or out-of-bounds bash commands
496
507
  if (toolName === BASH_TOOL_NAME && toolInput?.command) {
497
508
  const command = String(toolInput.command);
@@ -17,7 +17,7 @@ export class PlanManager {
17
17
  /**
18
18
  * Ensures the plan directory exists and generates a new plan file path with a random name
19
19
  */
20
- public async getOrGeneratePlanFilePath(): Promise<{
20
+ public async getOrGeneratePlanFilePath(seed?: string): Promise<{
21
21
  path: string;
22
22
  name: string;
23
23
  }> {
@@ -30,7 +30,7 @@ export class PlanManager {
30
30
  );
31
31
  throw error;
32
32
  }
33
- const name = generateRandomName();
33
+ const name = generateRandomName(seed);
34
34
  const filePath = path.join(this.planDir, `${name}.md`);
35
35
  this.logger?.info(`Generated plan file path: ${filePath}`);
36
36
  return { path: filePath, name };
@@ -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
  }
@@ -1,6 +1,7 @@
1
1
  import type { MessageManager } from "./messageManager.js";
2
2
  import type { AIManager } from "./aiManager.js";
3
3
  import type { BackgroundTaskManager } from "./backgroundTaskManager.js";
4
+ import type { TaskManager } from "../services/taskManager.js";
4
5
  import type {
5
6
  SlashCommand,
6
7
  CustomSlashCommand,
@@ -20,7 +21,7 @@ import {
20
21
  } from "../utils/markdownParser.js";
21
22
  import { exec } from "child_process";
22
23
  import { promisify } from "util";
23
- import { INIT_PROMPT } from "../constants/prompts.js";
24
+ import { INIT_PROMPT } from "../prompts/index.js";
24
25
 
25
26
  const execAsync = promisify(exec);
26
27
 
@@ -28,6 +29,7 @@ export interface SlashCommandManagerOptions {
28
29
  messageManager: MessageManager;
29
30
  aiManager: AIManager;
30
31
  backgroundTaskManager: BackgroundTaskManager;
32
+ taskManager: TaskManager;
31
33
  workdir: string;
32
34
  logger?: Logger;
33
35
  }
@@ -38,6 +40,7 @@ export class SlashCommandManager {
38
40
  private messageManager: MessageManager;
39
41
  private aiManager: AIManager;
40
42
  private backgroundTaskManager: BackgroundTaskManager;
43
+ private taskManager: TaskManager;
41
44
  private workdir: string;
42
45
  private logger?: Logger;
43
46
 
@@ -45,6 +48,7 @@ export class SlashCommandManager {
45
48
  this.messageManager = options.messageManager;
46
49
  this.aiManager = options.aiManager;
47
50
  this.backgroundTaskManager = options.backgroundTaskManager;
51
+ this.taskManager = options.taskManager;
48
52
  this.workdir = options.workdir;
49
53
  this.logger = options.logger;
50
54
 
@@ -61,8 +65,13 @@ export class SlashCommandManager {
61
65
  handler: () => {
62
66
  // Clear chat messages
63
67
  this.messageManager.clearMessages();
64
- // Clear terminal screen
65
- process.stdout.write("\x1Bc");
68
+
69
+ // Reset task list if WAVE_TASK_LIST_ID is not set
70
+ if (!process.env.WAVE_TASK_LIST_ID) {
71
+ const newTaskListId = this.messageManager.getRootSessionId();
72
+ this.taskManager.setTaskListId(newTaskListId);
73
+ this.taskManager.emit("tasksChange", newTaskListId);
74
+ }
66
75
  },
67
76
  });
68
77