wave-agent-sdk 0.0.7 → 0.0.10

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 (240) hide show
  1. package/dist/agent.d.ts +105 -24
  2. package/dist/agent.d.ts.map +1 -1
  3. package/dist/agent.js +438 -53
  4. package/dist/index.d.ts +4 -0
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/index.js +4 -0
  7. package/dist/managers/aiManager.d.ts +18 -7
  8. package/dist/managers/aiManager.d.ts.map +1 -1
  9. package/dist/managers/aiManager.js +254 -142
  10. package/dist/managers/backgroundBashManager.d.ts.map +1 -1
  11. package/dist/managers/backgroundBashManager.js +11 -9
  12. package/dist/managers/hookManager.d.ts +6 -6
  13. package/dist/managers/hookManager.d.ts.map +1 -1
  14. package/dist/managers/hookManager.js +81 -39
  15. package/dist/managers/liveConfigManager.d.ts +95 -0
  16. package/dist/managers/liveConfigManager.d.ts.map +1 -0
  17. package/dist/managers/liveConfigManager.js +442 -0
  18. package/dist/managers/lspManager.d.ts +43 -0
  19. package/dist/managers/lspManager.d.ts.map +1 -0
  20. package/dist/managers/lspManager.js +326 -0
  21. package/dist/managers/messageManager.d.ts +41 -24
  22. package/dist/managers/messageManager.d.ts.map +1 -1
  23. package/dist/managers/messageManager.js +184 -73
  24. package/dist/managers/permissionManager.d.ts +66 -0
  25. package/dist/managers/permissionManager.d.ts.map +1 -0
  26. package/dist/managers/permissionManager.js +208 -0
  27. package/dist/managers/skillManager.d.ts +1 -0
  28. package/dist/managers/skillManager.d.ts.map +1 -1
  29. package/dist/managers/skillManager.js +2 -1
  30. package/dist/managers/slashCommandManager.d.ts.map +1 -1
  31. package/dist/managers/slashCommandManager.js +4 -2
  32. package/dist/managers/subagentManager.d.ts +42 -6
  33. package/dist/managers/subagentManager.d.ts.map +1 -1
  34. package/dist/managers/subagentManager.js +213 -62
  35. package/dist/managers/toolManager.d.ts +38 -1
  36. package/dist/managers/toolManager.d.ts.map +1 -1
  37. package/dist/managers/toolManager.js +66 -2
  38. package/dist/services/aiService.d.ts +15 -5
  39. package/dist/services/aiService.d.ts.map +1 -1
  40. package/dist/services/aiService.js +446 -77
  41. package/dist/services/configurationService.d.ts +116 -0
  42. package/dist/services/configurationService.d.ts.map +1 -0
  43. package/dist/services/configurationService.js +585 -0
  44. package/dist/services/fileWatcher.d.ts +69 -0
  45. package/dist/services/fileWatcher.d.ts.map +1 -0
  46. package/dist/services/fileWatcher.js +212 -0
  47. package/dist/services/hook.d.ts +5 -40
  48. package/dist/services/hook.d.ts.map +1 -1
  49. package/dist/services/hook.js +47 -109
  50. package/dist/services/jsonlHandler.d.ts +71 -0
  51. package/dist/services/jsonlHandler.d.ts.map +1 -0
  52. package/dist/services/jsonlHandler.js +236 -0
  53. package/dist/services/memory.d.ts.map +1 -1
  54. package/dist/services/memory.js +33 -11
  55. package/dist/services/session.d.ts +116 -52
  56. package/dist/services/session.d.ts.map +1 -1
  57. package/dist/services/session.js +415 -143
  58. package/dist/tools/bashTool.d.ts.map +1 -1
  59. package/dist/tools/bashTool.js +77 -17
  60. package/dist/tools/deleteFileTool.d.ts.map +1 -1
  61. package/dist/tools/deleteFileTool.js +27 -1
  62. package/dist/tools/editTool.d.ts.map +1 -1
  63. package/dist/tools/editTool.js +33 -8
  64. package/dist/tools/lspTool.d.ts +6 -0
  65. package/dist/tools/lspTool.d.ts.map +1 -0
  66. package/dist/tools/lspTool.js +589 -0
  67. package/dist/tools/multiEditTool.d.ts.map +1 -1
  68. package/dist/tools/multiEditTool.js +30 -10
  69. package/dist/tools/readTool.d.ts.map +1 -1
  70. package/dist/tools/readTool.js +113 -3
  71. package/dist/tools/skillTool.js +2 -2
  72. package/dist/tools/todoWriteTool.d.ts.map +1 -1
  73. package/dist/tools/todoWriteTool.js +23 -0
  74. package/dist/tools/types.d.ts +11 -8
  75. package/dist/tools/types.d.ts.map +1 -1
  76. package/dist/tools/writeTool.d.ts.map +1 -1
  77. package/dist/tools/writeTool.js +30 -15
  78. package/dist/types/commands.d.ts +4 -1
  79. package/dist/types/commands.d.ts.map +1 -1
  80. package/dist/types/config.d.ts +4 -0
  81. package/dist/types/config.d.ts.map +1 -1
  82. package/dist/types/configuration.d.ts +69 -0
  83. package/dist/types/configuration.d.ts.map +1 -0
  84. package/dist/types/configuration.js +8 -0
  85. package/dist/types/core.d.ts +45 -0
  86. package/dist/types/core.d.ts.map +1 -1
  87. package/dist/types/environment.d.ts +83 -0
  88. package/dist/types/environment.d.ts.map +1 -0
  89. package/dist/types/environment.js +21 -0
  90. package/dist/types/fileSearch.d.ts +5 -0
  91. package/dist/types/fileSearch.d.ts.map +1 -0
  92. package/dist/types/fileSearch.js +1 -0
  93. package/dist/types/hooks.d.ts +18 -3
  94. package/dist/types/hooks.d.ts.map +1 -1
  95. package/dist/types/hooks.js +8 -8
  96. package/dist/types/index.d.ts +7 -0
  97. package/dist/types/index.d.ts.map +1 -1
  98. package/dist/types/index.js +7 -0
  99. package/dist/types/lsp.d.ts +90 -0
  100. package/dist/types/lsp.d.ts.map +1 -0
  101. package/dist/types/lsp.js +4 -0
  102. package/dist/types/messaging.d.ts +19 -12
  103. package/dist/types/messaging.d.ts.map +1 -1
  104. package/dist/types/permissions.d.ts +35 -0
  105. package/dist/types/permissions.d.ts.map +1 -0
  106. package/dist/types/permissions.js +12 -0
  107. package/dist/types/session.d.ts +15 -0
  108. package/dist/types/session.d.ts.map +1 -0
  109. package/dist/types/session.js +7 -0
  110. package/dist/types/skills.d.ts +1 -0
  111. package/dist/types/skills.d.ts.map +1 -1
  112. package/dist/types/tools.d.ts +35 -0
  113. package/dist/types/tools.d.ts.map +1 -0
  114. package/dist/types/tools.js +4 -0
  115. package/dist/utils/abortUtils.d.ts +34 -0
  116. package/dist/utils/abortUtils.d.ts.map +1 -0
  117. package/dist/utils/abortUtils.js +92 -0
  118. package/dist/utils/bashHistory.d.ts +4 -0
  119. package/dist/utils/bashHistory.d.ts.map +1 -1
  120. package/dist/utils/bashHistory.js +48 -30
  121. package/dist/utils/builtinSubagents.d.ts +7 -0
  122. package/dist/utils/builtinSubagents.d.ts.map +1 -0
  123. package/dist/utils/builtinSubagents.js +65 -0
  124. package/dist/utils/cacheControlUtils.d.ts +96 -0
  125. package/dist/utils/cacheControlUtils.d.ts.map +1 -0
  126. package/dist/utils/cacheControlUtils.js +324 -0
  127. package/dist/utils/commandPathResolver.d.ts +52 -0
  128. package/dist/utils/commandPathResolver.d.ts.map +1 -0
  129. package/dist/utils/commandPathResolver.js +145 -0
  130. package/dist/utils/configPaths.d.ts +85 -0
  131. package/dist/utils/configPaths.d.ts.map +1 -0
  132. package/dist/utils/configPaths.js +121 -0
  133. package/dist/utils/constants.d.ts +1 -13
  134. package/dist/utils/constants.d.ts.map +1 -1
  135. package/dist/utils/constants.js +2 -14
  136. package/dist/utils/convertMessagesForAPI.d.ts +2 -1
  137. package/dist/utils/convertMessagesForAPI.d.ts.map +1 -1
  138. package/dist/utils/convertMessagesForAPI.js +39 -18
  139. package/dist/utils/customCommands.d.ts.map +1 -1
  140. package/dist/utils/customCommands.js +66 -21
  141. package/dist/utils/fileSearch.d.ts +14 -0
  142. package/dist/utils/fileSearch.d.ts.map +1 -0
  143. package/dist/utils/fileSearch.js +88 -0
  144. package/dist/utils/fileUtils.d.ts +27 -0
  145. package/dist/utils/fileUtils.d.ts.map +1 -0
  146. package/dist/utils/fileUtils.js +145 -0
  147. package/dist/utils/globalLogger.d.ts +88 -0
  148. package/dist/utils/globalLogger.d.ts.map +1 -0
  149. package/dist/utils/globalLogger.js +120 -0
  150. package/dist/utils/largeOutputHandler.d.ts +15 -0
  151. package/dist/utils/largeOutputHandler.d.ts.map +1 -0
  152. package/dist/utils/largeOutputHandler.js +40 -0
  153. package/dist/utils/markdownParser.d.ts.map +1 -1
  154. package/dist/utils/markdownParser.js +1 -17
  155. package/dist/utils/mcpUtils.d.ts.map +1 -1
  156. package/dist/utils/mcpUtils.js +25 -3
  157. package/dist/utils/messageOperations.d.ts +20 -18
  158. package/dist/utils/messageOperations.d.ts.map +1 -1
  159. package/dist/utils/messageOperations.js +30 -38
  160. package/dist/utils/pathEncoder.d.ts +108 -0
  161. package/dist/utils/pathEncoder.d.ts.map +1 -0
  162. package/dist/utils/pathEncoder.js +279 -0
  163. package/dist/utils/subagentParser.d.ts +2 -2
  164. package/dist/utils/subagentParser.d.ts.map +1 -1
  165. package/dist/utils/subagentParser.js +12 -8
  166. package/dist/utils/tokenCalculation.d.ts +26 -0
  167. package/dist/utils/tokenCalculation.d.ts.map +1 -0
  168. package/dist/utils/tokenCalculation.js +36 -0
  169. package/dist/utils/tokenEstimator.d.ts +39 -0
  170. package/dist/utils/tokenEstimator.d.ts.map +1 -0
  171. package/dist/utils/tokenEstimator.js +55 -0
  172. package/package.json +6 -6
  173. package/src/agent.ts +586 -78
  174. package/src/index.ts +4 -0
  175. package/src/managers/aiManager.ts +341 -192
  176. package/src/managers/backgroundBashManager.ts +11 -9
  177. package/src/managers/hookManager.ts +102 -54
  178. package/src/managers/liveConfigManager.ts +634 -0
  179. package/src/managers/lspManager.ts +434 -0
  180. package/src/managers/messageManager.ts +258 -121
  181. package/src/managers/permissionManager.ts +276 -0
  182. package/src/managers/skillManager.ts +3 -1
  183. package/src/managers/slashCommandManager.ts +5 -3
  184. package/src/managers/subagentManager.ts +295 -76
  185. package/src/managers/toolManager.ts +95 -3
  186. package/src/services/aiService.ts +656 -84
  187. package/src/services/configurationService.ts +762 -0
  188. package/src/services/fileWatcher.ts +300 -0
  189. package/src/services/hook.ts +54 -144
  190. package/src/services/jsonlHandler.ts +303 -0
  191. package/src/services/memory.ts +34 -11
  192. package/src/services/session.ts +522 -173
  193. package/src/tools/bashTool.ts +94 -20
  194. package/src/tools/deleteFileTool.ts +38 -1
  195. package/src/tools/editTool.ts +44 -9
  196. package/src/tools/lspTool.ts +760 -0
  197. package/src/tools/multiEditTool.ts +41 -11
  198. package/src/tools/readTool.ts +127 -3
  199. package/src/tools/skillTool.ts +2 -2
  200. package/src/tools/todoWriteTool.ts +33 -1
  201. package/src/tools/types.ts +15 -9
  202. package/src/tools/writeTool.ts +43 -16
  203. package/src/types/commands.ts +6 -1
  204. package/src/types/config.ts +5 -0
  205. package/src/types/configuration.ts +73 -0
  206. package/src/types/core.ts +55 -0
  207. package/src/types/environment.ts +104 -0
  208. package/src/types/fileSearch.ts +4 -0
  209. package/src/types/hooks.ts +32 -16
  210. package/src/types/index.ts +7 -0
  211. package/src/types/lsp.ts +96 -0
  212. package/src/types/messaging.ts +21 -14
  213. package/src/types/permissions.ts +48 -0
  214. package/src/types/session.ts +20 -0
  215. package/src/types/skills.ts +1 -0
  216. package/src/types/tools.ts +38 -0
  217. package/src/utils/abortUtils.ts +118 -0
  218. package/src/utils/bashHistory.ts +55 -31
  219. package/src/utils/builtinSubagents.ts +71 -0
  220. package/src/utils/cacheControlUtils.ts +475 -0
  221. package/src/utils/commandPathResolver.ts +189 -0
  222. package/src/utils/configPaths.ts +163 -0
  223. package/src/utils/constants.ts +2 -17
  224. package/src/utils/convertMessagesForAPI.ts +44 -18
  225. package/src/utils/customCommands.ts +90 -22
  226. package/src/utils/fileSearch.ts +107 -0
  227. package/src/utils/fileUtils.ts +160 -0
  228. package/src/utils/globalLogger.ts +128 -0
  229. package/src/utils/largeOutputHandler.ts +55 -0
  230. package/src/utils/markdownParser.ts +1 -19
  231. package/src/utils/mcpUtils.ts +34 -3
  232. package/src/utils/messageOperations.ts +47 -53
  233. package/src/utils/pathEncoder.ts +394 -0
  234. package/src/utils/subagentParser.ts +13 -9
  235. package/src/utils/tokenCalculation.ts +43 -0
  236. package/src/utils/tokenEstimator.ts +68 -0
  237. package/dist/utils/configResolver.d.ts +0 -38
  238. package/dist/utils/configResolver.d.ts.map +0 -1
  239. package/dist/utils/configResolver.js +0 -106
  240. package/src/utils/configResolver.ts +0 -142
@@ -1,9 +1,7 @@
1
- import { randomUUID } from "crypto";
2
1
  import {
3
2
  addAssistantMessageToMessages,
4
3
  updateToolBlockInMessage,
5
4
  addErrorBlockToMessage,
6
- addDiffBlockToMessage,
7
5
  addUserMessageToMessages,
8
6
  extractUserInputHistory,
9
7
  addMemoryBlockToMessage,
@@ -18,16 +16,18 @@ import {
18
16
  type UpdateSubagentBlockParams,
19
17
  type AgentToolBlockUpdateParams,
20
18
  } from "../utils/messageOperations.js";
19
+ import type { SubagentConfiguration } from "../utils/subagentParser.js";
21
20
  import type { Logger, Message, Usage } from "../types/index.js";
21
+ import { join } from "path";
22
22
  import {
23
- cleanupExpiredSessions,
24
- getLatestSession,
25
- loadSession,
26
- saveSession,
23
+ appendMessages,
24
+ createSession,
25
+ generateSessionId,
27
26
  SessionData,
28
- getSessionFilePath,
27
+ SESSION_DIR,
29
28
  } from "../services/session.js";
30
29
  import { ChatCompletionMessageFunctionToolCall } from "openai/resources.js";
30
+ import { pathEncoder } from "../utils/pathEncoder.js";
31
31
 
32
32
  export interface MessageManagerCallbacks {
33
33
  onMessagesChange?: (messages: Message[]) => void;
@@ -37,12 +37,13 @@ export interface MessageManagerCallbacks {
37
37
  onUsagesChange?: (usages: Usage[]) => void;
38
38
  // Incremental callback
39
39
  onUserMessageAdded?: (params: UserMessageParams) => void;
40
- onAssistantMessageAdded?: (
41
- content?: string,
42
- toolCalls?: ChatCompletionMessageFunctionToolCall[],
43
- ) => void;
40
+ // MODIFIED: Remove arguments for separation of concerns
41
+ onAssistantMessageAdded?: () => void;
42
+ // NEW: Streaming content callback - FR-001: receives chunk and accumulated content
43
+ onAssistantContentUpdated?: (chunk: string, accumulated: string) => void;
44
+ // NEW: Streaming reasoning callback
45
+ onAssistantReasoningUpdated?: (chunk: string, accumulated: string) => void;
44
46
  onToolBlockUpdated?: (params: AgentToolBlockUpdateParams) => void;
45
- onDiffBlockAdded?: (filePath: string, diffResult: string) => void;
46
47
  onErrorBlockAdded?: (error: string) => void;
47
48
  onCompressBlockAdded?: (insertIndex: number, content: string) => void;
48
49
  onCompressionStateChange?: (isCompressing: boolean) => void;
@@ -67,7 +68,6 @@ export interface MessageManagerCallbacks {
67
68
  ) => void;
68
69
  onSubAgentBlockUpdated?: (
69
70
  subagentId: string,
70
- messages: Message[],
71
71
  status: "active" | "completed" | "error" | "aborted",
72
72
  ) => void;
73
73
  }
@@ -76,13 +76,8 @@ export interface MessageManagerOptions {
76
76
  callbacks: MessageManagerCallbacks;
77
77
  workdir: string;
78
78
  logger?: Logger;
79
-
80
- // New: Optional session directory override
81
- /**
82
- * Custom session directory path
83
- * @default join(homedir(), ".wave", "sessions")
84
- */
85
- sessionDir?: string;
79
+ sessionType?: "main" | "subagent";
80
+ subagentType?: string;
86
81
  }
87
82
 
88
83
  export class MessageManager {
@@ -91,22 +86,30 @@ export class MessageManager {
91
86
  private messages: Message[];
92
87
  private latestTotalTokens: number;
93
88
  private userInputHistory: string[];
94
- private sessionStartTime: string;
95
89
  private workdir: string;
90
+ private encodedWorkdir: string; // Cached encoded workdir
96
91
  private logger?: Logger; // Add optional logger property
97
92
  private callbacks: MessageManagerCallbacks;
98
- private sessionDir?: string; // Add session directory property
93
+ private transcriptPath: string; // Cached transcript path
94
+ private savedMessageCount: number; // Track how many messages have been saved to prevent duplication
95
+ private sessionType: "main" | "subagent";
96
+ private subagentType?: string;
99
97
 
100
98
  constructor(options: MessageManagerOptions) {
101
- this.sessionId = randomUUID();
99
+ this.sessionId = generateSessionId();
102
100
  this.messages = [];
103
101
  this.latestTotalTokens = 0;
104
102
  this.userInputHistory = [];
105
- this.sessionStartTime = new Date().toISOString();
106
103
  this.workdir = options.workdir;
104
+ this.encodedWorkdir = pathEncoder.encodeSync(this.workdir); // Cache encoded workdir
107
105
  this.callbacks = options.callbacks;
108
106
  this.logger = options.logger;
109
- this.sessionDir = options.sessionDir;
107
+ this.savedMessageCount = 0; // Initialize saved message count tracker
108
+ this.sessionType = options.sessionType || "main";
109
+ this.subagentType = options.subagentType;
110
+
111
+ // Compute and cache the transcript path
112
+ this.transcriptPath = this.computeTranscriptPath();
110
113
  }
111
114
 
112
115
  // Getter methods
@@ -126,100 +129,97 @@ export class MessageManager {
126
129
  return [...this.userInputHistory];
127
130
  }
128
131
 
132
+ public getWorkdir(): string {
133
+ return this.workdir;
134
+ }
135
+
136
+ public getSessionDir(): string {
137
+ return SESSION_DIR;
138
+ }
139
+
129
140
  public getTranscriptPath(): string {
130
- return getSessionFilePath(this.sessionId, this.sessionDir);
141
+ return this.transcriptPath;
142
+ }
143
+
144
+ /**
145
+ * Compute the transcript path using cached encoded workdir
146
+ * Called during construction and when sessionId changes
147
+ */
148
+ private computeTranscriptPath(): string {
149
+ const baseDir = join(SESSION_DIR, this.encodedWorkdir);
150
+
151
+ // All sessions now go in the same directory
152
+ // Session type is determined by metadata, not file path
153
+ return join(baseDir, `${this.sessionId}.jsonl`);
131
154
  }
132
155
 
133
156
  // Setter methods, will trigger callbacks
134
157
  public setSessionId(sessionId: string): void {
135
158
  if (this.sessionId !== sessionId) {
136
159
  this.sessionId = sessionId;
160
+ // Reset saved message count for new session
161
+ this.savedMessageCount = 0;
162
+ // Recompute transcript path since session ID changed
163
+ this.transcriptPath = this.computeTranscriptPath();
164
+
137
165
  this.callbacks.onSessionIdChange?.(sessionId);
138
166
  }
139
167
  }
140
168
 
141
- public setMessages(messages: Message[]): void {
142
- this.messages = [...messages];
143
- this.callbacks.onMessagesChange?.([...messages]);
144
- }
145
-
146
169
  /**
147
- * Save current session
170
+ * Create session if needed (async helper)
148
171
  */
149
- public async saveSession(): Promise<void> {
172
+ private async createSessionIfNeeded(): Promise<void> {
150
173
  try {
151
- await saveSession(
152
- this.sessionId,
153
- this.messages,
154
- this.workdir,
155
- this.latestTotalTokens,
156
- this.sessionStartTime,
157
- this.sessionDir,
158
- );
174
+ await createSession(this.sessionId, this.workdir, this.sessionType);
159
175
  } catch (error) {
160
- this.logger?.error("Failed to save session:", error);
176
+ this.logger?.error("Failed to create session:", error);
161
177
  }
162
178
  }
163
179
 
180
+ public setMessages(messages: Message[]): void {
181
+ this.messages = [...messages];
182
+ this.callbacks.onMessagesChange?.([...messages]);
183
+ }
184
+
164
185
  /**
165
- * Handle session restoration logic
186
+ * Save current session
166
187
  */
167
- public async handleSessionRestoration(
168
- restoreSessionId?: string,
169
- continueLastSession?: boolean,
170
- ): Promise<void> {
171
- // Clean up expired sessions first
188
+ public async saveSession(): Promise<void> {
172
189
  try {
173
- await cleanupExpiredSessions(this.workdir, this.sessionDir);
174
- } catch (error) {
175
- this.logger?.warn("Failed to cleanup expired sessions:", error);
176
- }
190
+ // Only save messages that haven't been saved yet
191
+ const unsavedMessages = this.messages.slice(this.savedMessageCount);
177
192
 
178
- if (!restoreSessionId && !continueLastSession) {
179
- return;
180
- }
181
-
182
- try {
183
- let sessionToRestore: SessionData | null = null;
193
+ if (unsavedMessages.length === 0) {
194
+ // No new messages to save
195
+ return;
196
+ }
184
197
 
185
- if (restoreSessionId) {
186
- sessionToRestore = await loadSession(restoreSessionId, this.sessionDir);
187
- if (!sessionToRestore) {
188
- console.error(`Session not found: ${restoreSessionId}`);
189
- process.exit(1);
190
- }
191
- } else if (continueLastSession) {
192
- sessionToRestore = await getLatestSession(
193
- this.workdir,
194
- this.sessionDir,
195
- );
196
- if (!sessionToRestore) {
197
- console.error(
198
- `No previous session found for workdir: ${this.workdir}`,
199
- );
200
- process.exit(1);
201
- }
198
+ // Create session if needed (only when we have messages to save)
199
+ if (this.savedMessageCount === 0) {
200
+ // This is the first time saving messages, so create the session
201
+ await this.createSessionIfNeeded();
202
202
  }
203
203
 
204
- if (sessionToRestore) {
205
- console.log(`Restoring session: ${sessionToRestore.id}`);
204
+ // Use JSONL format for new sessions
205
+ await appendMessages(
206
+ this.sessionId,
207
+ unsavedMessages, // Only append new messages
208
+ this.workdir,
209
+ this.sessionType,
210
+ );
206
211
 
207
- // Initialize from session data
208
- this.initializeFromSession(
209
- sessionToRestore.id,
210
- sessionToRestore.messages,
211
- sessionToRestore.metadata.latestTotalTokens,
212
- );
213
- }
212
+ // Update the saved message count
213
+ this.savedMessageCount = this.messages.length;
214
214
  } catch (error) {
215
- console.error("Failed to restore session:", error);
216
- process.exit(1);
215
+ this.logger?.error("Failed to save session:", error);
217
216
  }
218
217
  }
219
218
 
220
219
  public setlatestTotalTokens(latestTotalTokens: number): void {
221
220
  if (this.latestTotalTokens !== latestTotalTokens) {
222
221
  this.latestTotalTokens = latestTotalTokens;
222
+
223
223
  this.callbacks.onLatestTotalTokensChange?.(latestTotalTokens);
224
224
  }
225
225
  }
@@ -235,23 +235,23 @@ export class MessageManager {
235
235
  public clearMessages(): void {
236
236
  this.setMessages([]);
237
237
  this.setUserInputHistory([]);
238
- this.setSessionId(randomUUID());
238
+ this.setSessionId(generateSessionId());
239
239
  this.setlatestTotalTokens(0);
240
- this.sessionStartTime = new Date().toISOString();
240
+ this.savedMessageCount = 0; // Reset saved message count
241
241
  }
242
242
 
243
243
  // Initialize state from session data
244
- public initializeFromSession(
245
- sessionId: string,
246
- messages: Message[],
247
- latestTotalTokens: number,
248
- ): void {
249
- this.setSessionId(sessionId);
250
- this.setMessages([...messages]);
251
- this.setlatestTotalTokens(latestTotalTokens);
244
+ public initializeFromSession(sessionData: SessionData): void {
245
+ this.setSessionId(sessionData.id);
246
+ this.setMessages([...sessionData.messages]);
247
+ this.setlatestTotalTokens(sessionData.metadata.latestTotalTokens);
252
248
 
253
249
  // Extract user input history from session messages
254
- this.setUserInputHistory(extractUserInputHistory(messages));
250
+ this.setUserInputHistory(extractUserInputHistory(sessionData.messages));
251
+
252
+ // Set saved message count to the number of loaded messages since they're already saved
253
+ // This must be done after setSessionId which resets it to 0
254
+ this.savedMessageCount = sessionData.messages.length;
255
255
  }
256
256
 
257
257
  // Add to input history
@@ -280,21 +280,68 @@ export class MessageManager {
280
280
  });
281
281
  this.setMessages(newMessages);
282
282
  this.callbacks.onUserMessageAdded?.(params);
283
+
284
+ // Note: Subagent-specific callbacks are now handled by SubagentManager
283
285
  }
284
286
 
285
287
  public addAssistantMessage(
286
288
  content?: string,
287
289
  toolCalls?: ChatCompletionMessageFunctionToolCall[],
288
290
  usage?: Usage,
291
+ additionalFields?: Record<string, unknown>,
289
292
  ): void {
293
+ const additionalFieldsRecord = additionalFields
294
+ ? Object.fromEntries(
295
+ Object.entries(additionalFields).filter(
296
+ ([, value]) => value !== undefined,
297
+ ),
298
+ )
299
+ : undefined;
300
+
290
301
  const newMessages = addAssistantMessageToMessages(
291
302
  this.messages,
292
303
  content,
293
304
  toolCalls,
294
305
  usage,
306
+ additionalFieldsRecord,
295
307
  );
296
308
  this.setMessages(newMessages);
297
- this.callbacks.onAssistantMessageAdded?.(content, toolCalls);
309
+ this.callbacks.onAssistantMessageAdded?.();
310
+
311
+ // Note: Subagent-specific callbacks are now handled by SubagentManager
312
+ }
313
+
314
+ public mergeAssistantAdditionalFields(
315
+ additionalFields: Record<string, unknown>,
316
+ ): void {
317
+ if (!additionalFields || Object.keys(additionalFields).length === 0) {
318
+ return;
319
+ }
320
+
321
+ const newMessages = [...this.messages];
322
+ for (let i = newMessages.length - 1; i >= 0; i--) {
323
+ const message = newMessages[i];
324
+ if (message.role === "assistant") {
325
+ const mergedAdditionalFields = {
326
+ ...(message.additionalFields || {}),
327
+ } as Record<string, unknown>;
328
+
329
+ for (const [key, value] of Object.entries(additionalFields)) {
330
+ if (value === undefined) {
331
+ continue;
332
+ }
333
+ mergedAdditionalFields[key] = value;
334
+ }
335
+
336
+ if (Object.keys(mergedAdditionalFields).length === 0) {
337
+ return;
338
+ }
339
+
340
+ message.additionalFields = mergedAdditionalFields;
341
+ this.setMessages(newMessages);
342
+ return;
343
+ }
344
+ }
298
345
  }
299
346
 
300
347
  public updateToolBlock(params: AgentToolBlockUpdateParams): void {
@@ -305,27 +352,17 @@ export class MessageManager {
305
352
  result: params.result,
306
353
  success: params.success,
307
354
  error: params.error,
308
- isRunning: params.isRunning,
355
+ stage: params.stage,
309
356
  name: params.name,
310
357
  shortResult: params.shortResult,
311
358
  images: params.images,
312
359
  compactParams: params.compactParams,
360
+ parametersChunk: params.parametersChunk,
313
361
  });
314
362
  this.setMessages(newMessages);
315
363
  this.callbacks.onToolBlockUpdated?.(params);
316
- }
317
364
 
318
- public addDiffBlock(
319
- filePath: string,
320
- diffResult: Array<{ value: string; added?: boolean; removed?: boolean }>,
321
- ): void {
322
- const newMessages = addDiffBlockToMessage({
323
- messages: this.messages,
324
- path: filePath,
325
- diffResult,
326
- });
327
- this.setMessages(newMessages);
328
- this.callbacks.onDiffBlockAdded?.(filePath, JSON.stringify(diffResult));
365
+ // Note: Subagent-specific callbacks are now handled by SubagentManager
329
366
  }
330
367
 
331
368
  public addErrorBlock(error: string): void {
@@ -343,6 +380,7 @@ export class MessageManager {
343
380
  public compressMessagesAndUpdateSession(
344
381
  insertIndex: number,
345
382
  compressedContent: string,
383
+ usage?: Usage,
346
384
  ): void {
347
385
  const currentMessages = this.messages;
348
386
 
@@ -353,8 +391,10 @@ export class MessageManager {
353
391
  {
354
392
  type: "compress",
355
393
  content: compressedContent,
394
+ sessionId: this.sessionId,
356
395
  },
357
396
  ],
397
+ ...(usage && { usage }),
358
398
  };
359
399
 
360
400
  // Convert negative index to positive index
@@ -368,7 +408,7 @@ export class MessageManager {
368
408
  ];
369
409
 
370
410
  // Update sessionId
371
- this.setSessionId(randomUUID());
411
+ this.setSessionId(generateSessionId());
372
412
 
373
413
  // Set new message list
374
414
  this.setMessages(newMessages);
@@ -428,8 +468,9 @@ export class MessageManager {
428
468
  public addSubagentBlock(
429
469
  subagentId: string,
430
470
  subagentName: string,
471
+ sessionId: string,
472
+ configuration: SubagentConfiguration,
431
473
  status: "active" | "completed" | "error" = "active",
432
- subagentMessages: Message[] = [],
433
474
  parameters: {
434
475
  description: string;
435
476
  prompt: string;
@@ -440,8 +481,9 @@ export class MessageManager {
440
481
  messages: this.messages,
441
482
  subagentId,
442
483
  subagentName,
484
+ sessionId,
443
485
  status,
444
- subagentMessages,
486
+ configuration,
445
487
  };
446
488
  const updatedMessages = addSubagentBlockToMessage(params);
447
489
  this.setMessages(updatedMessages);
@@ -452,7 +494,7 @@ export class MessageManager {
452
494
  subagentId: string,
453
495
  updates: Partial<{
454
496
  status: "active" | "completed" | "error" | "aborted";
455
- messages: Message[];
497
+ sessionId: string;
456
498
  }>,
457
499
  ): void {
458
500
  const updatedMessages = updateSubagentBlockInMessage(
@@ -465,13 +507,8 @@ export class MessageManager {
465
507
  messages: this.messages,
466
508
  subagentId,
467
509
  status: updates.status || "active",
468
- subagentMessages: updates.messages || [],
469
510
  };
470
- this.callbacks.onSubAgentBlockUpdated?.(
471
- params.subagentId,
472
- params.messages,
473
- params.status,
474
- );
511
+ this.callbacks.onSubAgentBlockUpdated?.(params.subagentId, params.status);
475
512
  }
476
513
 
477
514
  /**
@@ -487,6 +524,106 @@ export class MessageManager {
487
524
  this.callbacks.onUsagesChange?.(usages);
488
525
  }
489
526
 
527
+ /**
528
+ * Update the current assistant message content during streaming
529
+ * This method updates the last assistant message's content without creating a new message
530
+ * FR-001: Tracks and provides both chunk (new content) and accumulated (total content)
531
+ */
532
+ public updateCurrentMessageContent(newAccumulatedContent: string): void {
533
+ if (this.messages.length === 0) return;
534
+
535
+ const lastMessage = this.messages[this.messages.length - 1];
536
+ if (lastMessage.role !== "assistant") return;
537
+
538
+ // Get the current content to calculate the chunk
539
+ const textBlockIndex = lastMessage.blocks.findIndex(
540
+ (block) => block.type === "text",
541
+ );
542
+ const currentContent =
543
+ textBlockIndex >= 0
544
+ ? (
545
+ lastMessage.blocks[textBlockIndex] as {
546
+ type: "text";
547
+ content: string;
548
+ }
549
+ ).content || ""
550
+ : "";
551
+
552
+ // Calculate the chunk (new content since last update)
553
+ const chunk = newAccumulatedContent.slice(currentContent.length);
554
+
555
+ if (textBlockIndex >= 0) {
556
+ // Update existing text block
557
+ lastMessage.blocks[textBlockIndex] = {
558
+ type: "text",
559
+ content: newAccumulatedContent,
560
+ };
561
+ } else {
562
+ // Add new text block if none exists
563
+ lastMessage.blocks.push({
564
+ type: "text",
565
+ content: newAccumulatedContent,
566
+ });
567
+ }
568
+
569
+ // FR-001: Trigger callbacks with chunk and accumulated content
570
+ this.callbacks.onAssistantContentUpdated?.(chunk, newAccumulatedContent);
571
+
572
+ // Note: Subagent-specific callbacks are now handled by SubagentManager
573
+
574
+ this.callbacks.onMessagesChange?.([...this.messages]); // Still need to notify of changes
575
+ }
576
+
577
+ /**
578
+ * Update the current assistant message reasoning during streaming
579
+ * This method updates the last assistant message's reasoning content without creating a new message
580
+ */
581
+ public updateCurrentMessageReasoning(newAccumulatedReasoning: string): void {
582
+ if (this.messages.length === 0) return;
583
+
584
+ const lastMessage = this.messages[this.messages.length - 1];
585
+ if (lastMessage.role !== "assistant") return;
586
+
587
+ // Get the current reasoning content to calculate the chunk
588
+ const reasoningBlockIndex = lastMessage.blocks.findIndex(
589
+ (block) => block.type === "reasoning",
590
+ );
591
+ const currentReasoning =
592
+ reasoningBlockIndex >= 0
593
+ ? (
594
+ lastMessage.blocks[reasoningBlockIndex] as {
595
+ type: "reasoning";
596
+ content: string;
597
+ }
598
+ ).content || ""
599
+ : "";
600
+
601
+ // Calculate the chunk (new content since last update)
602
+ const chunk = newAccumulatedReasoning.slice(currentReasoning.length);
603
+
604
+ if (reasoningBlockIndex >= 0) {
605
+ // Update existing reasoning block
606
+ lastMessage.blocks[reasoningBlockIndex] = {
607
+ type: "reasoning",
608
+ content: newAccumulatedReasoning,
609
+ };
610
+ } else {
611
+ // Add new reasoning block if none exists
612
+ lastMessage.blocks.push({
613
+ type: "reasoning",
614
+ content: newAccumulatedReasoning,
615
+ });
616
+ }
617
+
618
+ // Trigger callbacks with chunk and accumulated reasoning content
619
+ this.callbacks.onAssistantReasoningUpdated?.(
620
+ chunk,
621
+ newAccumulatedReasoning,
622
+ );
623
+
624
+ this.callbacks.onMessagesChange?.([...this.messages]); // Still need to notify of changes
625
+ }
626
+
490
627
  /**
491
628
  * Remove the last user message from the conversation
492
629
  * Used for hook error handling when the user prompt needs to be erased