wave-agent-sdk 0.19.6 → 0.19.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 (349) hide show
  1. package/dist/agent.d.ts +9 -14
  2. package/dist/agent.js +49 -56
  3. package/dist/constants/goalPrompts.d.ts +0 -1
  4. package/dist/constants/subagents.d.ts +0 -1
  5. package/dist/constants/toolLimits.d.ts +0 -1
  6. package/dist/constants/tools.d.ts +0 -1
  7. package/dist/core/plugin.d.ts +0 -1
  8. package/dist/core/session.d.ts +0 -1
  9. package/dist/index.d.ts +0 -1
  10. package/dist/managers/MemoryRuleManager.d.ts +0 -1
  11. package/dist/managers/aiManager.d.ts +1 -1
  12. package/dist/managers/aiManager.js +26 -9
  13. package/dist/managers/backgroundTaskManager.d.ts +0 -2
  14. package/dist/managers/backgroundTaskManager.js +16 -19
  15. package/dist/managers/bangManager.d.ts +0 -1
  16. package/dist/managers/cronManager.d.ts +0 -1
  17. package/dist/managers/foregroundTaskManager.d.ts +0 -1
  18. package/dist/managers/forkedAgentManager.d.ts +0 -1
  19. package/dist/managers/goalManager.d.ts +0 -1
  20. package/dist/managers/hookManager.d.ts +0 -1
  21. package/dist/managers/liveConfigManager.d.ts +0 -1
  22. package/dist/managers/lspManager.d.ts +0 -1
  23. package/dist/managers/mcpManager.d.ts +0 -1
  24. package/dist/managers/messageManager.d.ts +0 -1
  25. package/dist/managers/messageQueue.d.ts +17 -2
  26. package/dist/managers/messageQueue.js +40 -1
  27. package/dist/managers/permissionManager.d.ts +0 -1
  28. package/dist/managers/planManager.d.ts +0 -1
  29. package/dist/managers/pluginManager.d.ts +0 -1
  30. package/dist/managers/pluginScopeManager.d.ts +0 -1
  31. package/dist/managers/reversionManager.d.ts +0 -1
  32. package/dist/managers/skillManager.d.ts +0 -1
  33. package/dist/managers/slashCommandManager.d.ts +0 -1
  34. package/dist/managers/subagentManager.d.ts +0 -3
  35. package/dist/managers/subagentManager.js +8 -13
  36. package/dist/managers/toolManager.d.ts +0 -1
  37. package/dist/managers/workflowManager.d.ts +1 -2
  38. package/dist/managers/workflowManager.js +4 -4
  39. package/dist/prompts/autoMemory.d.ts +0 -1
  40. package/dist/prompts/autoMemoryExtraction.d.ts +0 -1
  41. package/dist/prompts/index.d.ts +0 -1
  42. package/dist/prompts/planModeReminders.d.ts +0 -1
  43. package/dist/services/GitService.d.ts +0 -1
  44. package/dist/services/MarketplaceService.d.ts +0 -1
  45. package/dist/services/MemoryRuleService.d.ts +0 -1
  46. package/dist/services/aiService.d.ts +0 -1
  47. package/dist/services/authService.d.ts +0 -1
  48. package/dist/services/autoMemoryService.d.ts +0 -1
  49. package/dist/services/configurationService.d.ts +0 -1
  50. package/dist/services/fileWatcher.d.ts +0 -1
  51. package/dist/services/hook.d.ts +0 -1
  52. package/dist/services/initializationService.d.ts +0 -1
  53. package/dist/services/interactionService.d.ts +0 -1
  54. package/dist/services/interactionService.js +10 -0
  55. package/dist/services/jsonlHandler.d.ts +0 -1
  56. package/dist/services/memory.d.ts +0 -1
  57. package/dist/services/pluginLoader.d.ts +0 -1
  58. package/dist/services/remoteSettingsService.d.ts +0 -1
  59. package/dist/services/reversionService.d.ts +0 -1
  60. package/dist/services/session.d.ts +0 -1
  61. package/dist/services/taskManager.d.ts +0 -1
  62. package/dist/services/taskManager.js +8 -1
  63. package/dist/telemetry/events.d.ts +0 -1
  64. package/dist/telemetry/instrumentation.d.ts +0 -1
  65. package/dist/telemetry/sessionTracing.d.ts +0 -1
  66. package/dist/tools/agentTool.d.ts +0 -1
  67. package/dist/tools/askUserQuestion.d.ts +0 -1
  68. package/dist/tools/bashTool.d.ts +0 -1
  69. package/dist/tools/buildTool.d.ts +0 -1
  70. package/dist/tools/cronCreateTool.d.ts +0 -1
  71. package/dist/tools/cronDeleteTool.d.ts +0 -1
  72. package/dist/tools/cronListTool.d.ts +0 -1
  73. package/dist/tools/editTool.d.ts +0 -1
  74. package/dist/tools/enterPlanMode.d.ts +0 -1
  75. package/dist/tools/enterWorktreeTool.d.ts +0 -1
  76. package/dist/tools/exitPlanMode.d.ts +0 -1
  77. package/dist/tools/exitWorktreeTool.d.ts +0 -1
  78. package/dist/tools/globTool.d.ts +0 -1
  79. package/dist/tools/grepTool.d.ts +0 -1
  80. package/dist/tools/lspTool.d.ts +0 -1
  81. package/dist/tools/readTool.d.ts +0 -1
  82. package/dist/tools/skillTool.d.ts +0 -1
  83. package/dist/tools/taskManagementTools.d.ts +0 -1
  84. package/dist/tools/taskStopTool.d.ts +0 -1
  85. package/dist/tools/types.d.ts +0 -1
  86. package/dist/tools/webFetchTool.d.ts +0 -1
  87. package/dist/tools/workflowTool.d.ts +0 -1
  88. package/dist/tools/writeTool.d.ts +0 -1
  89. package/dist/types/agent.d.ts +0 -1
  90. package/dist/types/auth.d.ts +0 -1
  91. package/dist/types/commands.d.ts +0 -1
  92. package/dist/types/config.d.ts +0 -1
  93. package/dist/types/configuration.d.ts +0 -1
  94. package/dist/types/core.d.ts +0 -1
  95. package/dist/types/cron.d.ts +0 -1
  96. package/dist/types/environment.d.ts +0 -1
  97. package/dist/types/fileSearch.d.ts +0 -1
  98. package/dist/types/history.d.ts +0 -1
  99. package/dist/types/hooks.d.ts +0 -1
  100. package/dist/types/index.d.ts +0 -1
  101. package/dist/types/lsp.d.ts +0 -1
  102. package/dist/types/marketplace.d.ts +0 -1
  103. package/dist/types/mcp.d.ts +0 -1
  104. package/dist/types/memoryRule.d.ts +0 -1
  105. package/dist/types/messaging.d.ts +0 -1
  106. package/dist/types/permissions.d.ts +0 -1
  107. package/dist/types/plugins.d.ts +0 -1
  108. package/dist/types/processes.d.ts +0 -1
  109. package/dist/types/reversion.d.ts +0 -1
  110. package/dist/types/session.d.ts +0 -1
  111. package/dist/types/skills.d.ts +0 -1
  112. package/dist/types/tasks.d.ts +0 -1
  113. package/dist/types/telemetry.d.ts +0 -1
  114. package/dist/types/tools.d.ts +0 -1
  115. package/dist/types/workflow.d.ts +0 -1
  116. package/dist/utils/abortUtils.d.ts +0 -1
  117. package/dist/utils/atomicWrite.d.ts +0 -1
  118. package/dist/utils/bashParser.d.ts +0 -1
  119. package/dist/utils/cacheControlUtils.d.ts +0 -1
  120. package/dist/utils/commandArgumentParser.d.ts +0 -1
  121. package/dist/utils/commandPathResolver.d.ts +0 -1
  122. package/dist/utils/configPaths.d.ts +0 -1
  123. package/dist/utils/configValidator.d.ts +0 -1
  124. package/dist/utils/constants.d.ts +0 -1
  125. package/dist/utils/container.d.ts +0 -1
  126. package/dist/utils/containerSetup.d.ts +0 -1
  127. package/dist/utils/containerSetup.js +0 -3
  128. package/dist/utils/convertMessagesForAPI.d.ts +0 -1
  129. package/dist/utils/cronTasks.d.ts +0 -1
  130. package/dist/utils/cronTasksLock.d.ts +0 -1
  131. package/dist/utils/cronToHuman.d.ts +0 -1
  132. package/dist/utils/customCommands.d.ts +0 -1
  133. package/dist/utils/editUtils.d.ts +0 -1
  134. package/dist/utils/fileFormat.d.ts +0 -1
  135. package/dist/utils/fileSearch.d.ts +0 -1
  136. package/dist/utils/fileUtils.d.ts +0 -1
  137. package/dist/utils/gitUtils.d.ts +0 -1
  138. package/dist/utils/globalLogger.d.ts +0 -1
  139. package/dist/utils/groupMessagesByApiRound.d.ts +0 -1
  140. package/dist/utils/hookMatcher.d.ts +0 -1
  141. package/dist/utils/markdownParser.d.ts +0 -1
  142. package/dist/utils/mcpUtils.d.ts +0 -1
  143. package/dist/utils/messageOperations.d.ts +0 -1
  144. package/dist/utils/modelCapabilities.d.ts +0 -1
  145. package/dist/utils/nameGenerator.d.ts +0 -1
  146. package/dist/utils/notificationXml.d.ts +0 -1
  147. package/dist/utils/openaiClient.d.ts +0 -1
  148. package/dist/utils/parseCronExpression.d.ts +0 -1
  149. package/dist/utils/path.d.ts +0 -1
  150. package/dist/utils/pathEncoder.d.ts +0 -1
  151. package/dist/utils/pathSafety.d.ts +0 -1
  152. package/dist/utils/promptHistory.d.ts +0 -1
  153. package/dist/utils/ripgrep.d.ts +0 -1
  154. package/dist/utils/shellResolver.d.ts +0 -1
  155. package/dist/utils/skillParser.d.ts +0 -1
  156. package/dist/utils/stringUtils.d.ts +0 -1
  157. package/dist/utils/subagentParser.d.ts +0 -1
  158. package/dist/utils/taskReminder.d.ts +0 -1
  159. package/dist/utils/tokenCalculation.d.ts +0 -1
  160. package/dist/utils/tokenEstimate.d.ts +0 -1
  161. package/dist/utils/toolResultStorage.d.ts +0 -1
  162. package/dist/utils/worktreeSession.d.ts +0 -1
  163. package/dist/utils/worktreeUtils.d.ts +0 -1
  164. package/dist/workflow/budgetTracker.d.ts +0 -1
  165. package/dist/workflow/concurrencyLimiter.d.ts +0 -1
  166. package/dist/workflow/journal.d.ts +0 -1
  167. package/dist/workflow/progressReporter.d.ts +0 -1
  168. package/dist/workflow/runState.d.ts +0 -1
  169. package/dist/workflow/scriptRuntime.d.ts +0 -1
  170. package/dist/workflow/structuredOutput.d.ts +0 -1
  171. package/dist/workflow/types.d.ts +0 -1
  172. package/dist/workflow/workflowApis.d.ts +0 -1
  173. package/package.json +1 -1
  174. package/src/agent.ts +49 -67
  175. package/src/managers/aiManager.ts +30 -10
  176. package/src/managers/backgroundTaskManager.ts +17 -21
  177. package/src/managers/messageQueue.ts +43 -2
  178. package/src/managers/subagentManager.ts +9 -15
  179. package/src/managers/workflowManager.ts +5 -5
  180. package/src/services/interactionService.ts +11 -0
  181. package/src/services/taskManager.ts +8 -1
  182. package/src/utils/containerSetup.ts +0 -4
  183. package/dist/agent.d.ts.map +0 -1
  184. package/dist/constants/goalPrompts.d.ts.map +0 -1
  185. package/dist/constants/subagents.d.ts.map +0 -1
  186. package/dist/constants/toolLimits.d.ts.map +0 -1
  187. package/dist/constants/tools.d.ts.map +0 -1
  188. package/dist/core/plugin.d.ts.map +0 -1
  189. package/dist/core/session.d.ts.map +0 -1
  190. package/dist/index.d.ts.map +0 -1
  191. package/dist/managers/MemoryRuleManager.d.ts.map +0 -1
  192. package/dist/managers/aiManager.d.ts.map +0 -1
  193. package/dist/managers/backgroundTaskManager.d.ts.map +0 -1
  194. package/dist/managers/bangManager.d.ts.map +0 -1
  195. package/dist/managers/cronManager.d.ts.map +0 -1
  196. package/dist/managers/foregroundTaskManager.d.ts.map +0 -1
  197. package/dist/managers/forkedAgentManager.d.ts.map +0 -1
  198. package/dist/managers/goalManager.d.ts.map +0 -1
  199. package/dist/managers/hookManager.d.ts.map +0 -1
  200. package/dist/managers/liveConfigManager.d.ts.map +0 -1
  201. package/dist/managers/lspManager.d.ts.map +0 -1
  202. package/dist/managers/mcpManager.d.ts.map +0 -1
  203. package/dist/managers/messageManager.d.ts.map +0 -1
  204. package/dist/managers/messageQueue.d.ts.map +0 -1
  205. package/dist/managers/notificationQueue.d.ts +0 -8
  206. package/dist/managers/notificationQueue.d.ts.map +0 -1
  207. package/dist/managers/notificationQueue.js +0 -17
  208. package/dist/managers/permissionManager.d.ts.map +0 -1
  209. package/dist/managers/planManager.d.ts.map +0 -1
  210. package/dist/managers/pluginManager.d.ts.map +0 -1
  211. package/dist/managers/pluginScopeManager.d.ts.map +0 -1
  212. package/dist/managers/reversionManager.d.ts.map +0 -1
  213. package/dist/managers/skillManager.d.ts.map +0 -1
  214. package/dist/managers/slashCommandManager.d.ts.map +0 -1
  215. package/dist/managers/subagentManager.d.ts.map +0 -1
  216. package/dist/managers/toolManager.d.ts.map +0 -1
  217. package/dist/managers/workflowManager.d.ts.map +0 -1
  218. package/dist/prompts/autoMemory.d.ts.map +0 -1
  219. package/dist/prompts/autoMemoryExtraction.d.ts.map +0 -1
  220. package/dist/prompts/index.d.ts.map +0 -1
  221. package/dist/prompts/planModeReminders.d.ts.map +0 -1
  222. package/dist/services/GitService.d.ts.map +0 -1
  223. package/dist/services/MarketplaceService.d.ts.map +0 -1
  224. package/dist/services/MemoryRuleService.d.ts.map +0 -1
  225. package/dist/services/aiService.d.ts.map +0 -1
  226. package/dist/services/authService.d.ts.map +0 -1
  227. package/dist/services/autoMemoryService.d.ts.map +0 -1
  228. package/dist/services/configurationService.d.ts.map +0 -1
  229. package/dist/services/fileWatcher.d.ts.map +0 -1
  230. package/dist/services/hook.d.ts.map +0 -1
  231. package/dist/services/initializationService.d.ts.map +0 -1
  232. package/dist/services/interactionService.d.ts.map +0 -1
  233. package/dist/services/jsonlHandler.d.ts.map +0 -1
  234. package/dist/services/memory.d.ts.map +0 -1
  235. package/dist/services/pluginLoader.d.ts.map +0 -1
  236. package/dist/services/remoteSettingsService.d.ts.map +0 -1
  237. package/dist/services/reversionService.d.ts.map +0 -1
  238. package/dist/services/session.d.ts.map +0 -1
  239. package/dist/services/taskManager.d.ts.map +0 -1
  240. package/dist/telemetry/events.d.ts.map +0 -1
  241. package/dist/telemetry/instrumentation.d.ts.map +0 -1
  242. package/dist/telemetry/sessionTracing.d.ts.map +0 -1
  243. package/dist/tools/agentTool.d.ts.map +0 -1
  244. package/dist/tools/askUserQuestion.d.ts.map +0 -1
  245. package/dist/tools/bashTool.d.ts.map +0 -1
  246. package/dist/tools/buildTool.d.ts.map +0 -1
  247. package/dist/tools/cronCreateTool.d.ts.map +0 -1
  248. package/dist/tools/cronDeleteTool.d.ts.map +0 -1
  249. package/dist/tools/cronListTool.d.ts.map +0 -1
  250. package/dist/tools/editTool.d.ts.map +0 -1
  251. package/dist/tools/enterPlanMode.d.ts.map +0 -1
  252. package/dist/tools/enterWorktreeTool.d.ts.map +0 -1
  253. package/dist/tools/exitPlanMode.d.ts.map +0 -1
  254. package/dist/tools/exitWorktreeTool.d.ts.map +0 -1
  255. package/dist/tools/globTool.d.ts.map +0 -1
  256. package/dist/tools/grepTool.d.ts.map +0 -1
  257. package/dist/tools/lspTool.d.ts.map +0 -1
  258. package/dist/tools/readTool.d.ts.map +0 -1
  259. package/dist/tools/skillTool.d.ts.map +0 -1
  260. package/dist/tools/taskManagementTools.d.ts.map +0 -1
  261. package/dist/tools/taskStopTool.d.ts.map +0 -1
  262. package/dist/tools/types.d.ts.map +0 -1
  263. package/dist/tools/webFetchTool.d.ts.map +0 -1
  264. package/dist/tools/workflowTool.d.ts.map +0 -1
  265. package/dist/tools/writeTool.d.ts.map +0 -1
  266. package/dist/types/agent.d.ts.map +0 -1
  267. package/dist/types/auth.d.ts.map +0 -1
  268. package/dist/types/commands.d.ts.map +0 -1
  269. package/dist/types/config.d.ts.map +0 -1
  270. package/dist/types/configuration.d.ts.map +0 -1
  271. package/dist/types/core.d.ts.map +0 -1
  272. package/dist/types/cron.d.ts.map +0 -1
  273. package/dist/types/environment.d.ts.map +0 -1
  274. package/dist/types/fileSearch.d.ts.map +0 -1
  275. package/dist/types/history.d.ts.map +0 -1
  276. package/dist/types/hooks.d.ts.map +0 -1
  277. package/dist/types/index.d.ts.map +0 -1
  278. package/dist/types/lsp.d.ts.map +0 -1
  279. package/dist/types/marketplace.d.ts.map +0 -1
  280. package/dist/types/mcp.d.ts.map +0 -1
  281. package/dist/types/memoryRule.d.ts.map +0 -1
  282. package/dist/types/messaging.d.ts.map +0 -1
  283. package/dist/types/permissions.d.ts.map +0 -1
  284. package/dist/types/plugins.d.ts.map +0 -1
  285. package/dist/types/processes.d.ts.map +0 -1
  286. package/dist/types/reversion.d.ts.map +0 -1
  287. package/dist/types/session.d.ts.map +0 -1
  288. package/dist/types/skills.d.ts.map +0 -1
  289. package/dist/types/tasks.d.ts.map +0 -1
  290. package/dist/types/telemetry.d.ts.map +0 -1
  291. package/dist/types/tools.d.ts.map +0 -1
  292. package/dist/types/workflow.d.ts.map +0 -1
  293. package/dist/utils/abortUtils.d.ts.map +0 -1
  294. package/dist/utils/atomicWrite.d.ts.map +0 -1
  295. package/dist/utils/bashParser.d.ts.map +0 -1
  296. package/dist/utils/cacheControlUtils.d.ts.map +0 -1
  297. package/dist/utils/commandArgumentParser.d.ts.map +0 -1
  298. package/dist/utils/commandPathResolver.d.ts.map +0 -1
  299. package/dist/utils/configPaths.d.ts.map +0 -1
  300. package/dist/utils/configValidator.d.ts.map +0 -1
  301. package/dist/utils/constants.d.ts.map +0 -1
  302. package/dist/utils/container.d.ts.map +0 -1
  303. package/dist/utils/containerSetup.d.ts.map +0 -1
  304. package/dist/utils/convertMessagesForAPI.d.ts.map +0 -1
  305. package/dist/utils/cronTasks.d.ts.map +0 -1
  306. package/dist/utils/cronTasksLock.d.ts.map +0 -1
  307. package/dist/utils/cronToHuman.d.ts.map +0 -1
  308. package/dist/utils/customCommands.d.ts.map +0 -1
  309. package/dist/utils/editUtils.d.ts.map +0 -1
  310. package/dist/utils/fileFormat.d.ts.map +0 -1
  311. package/dist/utils/fileSearch.d.ts.map +0 -1
  312. package/dist/utils/fileUtils.d.ts.map +0 -1
  313. package/dist/utils/gitUtils.d.ts.map +0 -1
  314. package/dist/utils/globalLogger.d.ts.map +0 -1
  315. package/dist/utils/groupMessagesByApiRound.d.ts.map +0 -1
  316. package/dist/utils/hookMatcher.d.ts.map +0 -1
  317. package/dist/utils/markdownParser.d.ts.map +0 -1
  318. package/dist/utils/mcpUtils.d.ts.map +0 -1
  319. package/dist/utils/messageOperations.d.ts.map +0 -1
  320. package/dist/utils/modelCapabilities.d.ts.map +0 -1
  321. package/dist/utils/nameGenerator.d.ts.map +0 -1
  322. package/dist/utils/notificationXml.d.ts.map +0 -1
  323. package/dist/utils/openaiClient.d.ts.map +0 -1
  324. package/dist/utils/parseCronExpression.d.ts.map +0 -1
  325. package/dist/utils/path.d.ts.map +0 -1
  326. package/dist/utils/pathEncoder.d.ts.map +0 -1
  327. package/dist/utils/pathSafety.d.ts.map +0 -1
  328. package/dist/utils/promptHistory.d.ts.map +0 -1
  329. package/dist/utils/ripgrep.d.ts.map +0 -1
  330. package/dist/utils/shellResolver.d.ts.map +0 -1
  331. package/dist/utils/skillParser.d.ts.map +0 -1
  332. package/dist/utils/stringUtils.d.ts.map +0 -1
  333. package/dist/utils/subagentParser.d.ts.map +0 -1
  334. package/dist/utils/taskReminder.d.ts.map +0 -1
  335. package/dist/utils/tokenCalculation.d.ts.map +0 -1
  336. package/dist/utils/tokenEstimate.d.ts.map +0 -1
  337. package/dist/utils/toolResultStorage.d.ts.map +0 -1
  338. package/dist/utils/worktreeSession.d.ts.map +0 -1
  339. package/dist/utils/worktreeUtils.d.ts.map +0 -1
  340. package/dist/workflow/budgetTracker.d.ts.map +0 -1
  341. package/dist/workflow/concurrencyLimiter.d.ts.map +0 -1
  342. package/dist/workflow/journal.d.ts.map +0 -1
  343. package/dist/workflow/progressReporter.d.ts.map +0 -1
  344. package/dist/workflow/runState.d.ts.map +0 -1
  345. package/dist/workflow/scriptRuntime.d.ts.map +0 -1
  346. package/dist/workflow/structuredOutput.d.ts.map +0 -1
  347. package/dist/workflow/types.d.ts.map +0 -1
  348. package/dist/workflow/workflowApis.d.ts.map +0 -1
  349. package/src/managers/notificationQueue.ts +0 -19
package/src/agent.ts CHANGED
@@ -10,7 +10,6 @@ import { BangManager } from "./managers/bangManager.js";
10
10
  import { CronManager } from "./managers/cronManager.js";
11
11
  import { GoalManager } from "./managers/goalManager.js";
12
12
  import { BackgroundTaskManager } from "./managers/backgroundTaskManager.js";
13
- import { NotificationQueue } from "./managers/notificationQueue.js";
14
13
  import { MessageQueue, type QueuedMessage } from "./managers/messageQueue.js";
15
14
  import { SlashCommandManager } from "./managers/slashCommandManager.js";
16
15
  import { PluginManager } from "./managers/pluginManager.js";
@@ -77,9 +76,8 @@ export class Agent {
77
76
  private goalManager: GoalManager; // Add goal manager instance
78
77
  private hookManager: HookManager; // Add hooks manager instance
79
78
  private reversionManager: ReversionManager;
80
- private notificationQueue: NotificationQueue; // Add notification queue instance
81
- private messageQueue: MessageQueue; // Add message queue instance
82
- private pendingNotificationPromises: Promise<void>[] = []; // Track pending notification processing
79
+ private messageQueue: MessageQueue; // Unified queue for messages, bang commands, and notifications
80
+ private dispatchPromise: Promise<void> | null = null; // Track current dispatch for teardown
83
81
  private memoryRuleManager: MemoryRuleManager; // Add memory rule manager instance
84
82
  private liveConfigManager: LiveConfigManager; // Add live configuration manager
85
83
  private taskManager: TaskManager;
@@ -207,25 +205,8 @@ export class Agent {
207
205
  this.bangManager = this.container.get("BangManager")!;
208
206
  this.cronManager = this.container.get("CronManager")!;
209
207
  this.goalManager = this.container.get("GoalManager")!;
210
- this.notificationQueue = this.container.get("NotificationQueue")!;
211
208
  this.messageQueue = this.container.get("MessageQueue")!;
212
209
 
213
- // Wire up notification queue to trigger AI when notifications arrive while idle
214
- this.notificationQueue.onNotificationsEnqueued = () => {
215
- // If the AI is NOT loading (idle), trigger a new AI cycle to process notifications
216
- if (!this.aiManager.isLoading) {
217
- const pendingPromise = this.processPendingNotifications().catch(
218
- (error) => {
219
- this.logger?.error(
220
- "Failed to process pending notifications:",
221
- error,
222
- );
223
- },
224
- );
225
- this.pendingNotificationPromises.push(pendingPromise);
226
- }
227
- };
228
-
229
210
  // Wire up CWD change callback from AIManager to sync Agent's workdir
230
211
  this.aiManager.setOnCwdChange((newCwd) => {
231
212
  this.workdir = newCwd;
@@ -233,18 +214,20 @@ export class Agent {
233
214
  this.options.callbacks?.onWorkdirChange?.(newCwd);
234
215
  });
235
216
 
236
- // Wire up message queue to process when agent becomes idle
237
- this.messageQueue.onMessageEnqueued = () => this.tryDequeue();
217
+ // Wire up message queue to process when agent becomes idle.
218
+ // Both user messages and background task notifications enqueue here,
219
+ // unified through a single dispatch path (tryDispatch).
220
+ this.messageQueue.onMessageEnqueued = () => this.tryDispatch();
238
221
 
239
222
  // Wire up AI loading changes to process queue when AI becomes idle
240
223
  this.aiManager.onLoadingChange = (loading: boolean) => {
241
- if (!loading) this.tryDequeue();
224
+ if (!loading) this.tryDispatch();
242
225
  };
243
226
 
244
227
  // Wire up bang manager callback for command running changes
245
228
  this.bangManager.onCommandRunningChange = (running: boolean) => {
246
229
  this.options.callbacks?.onCommandRunningChange?.(running);
247
- if (!running) this.tryDequeue();
230
+ if (!running) this.tryDispatch();
248
231
  };
249
232
 
250
233
  // Set initial permission mode if provided
@@ -346,9 +329,11 @@ export class Agent {
346
329
  return this.bangManager?.isCommandRunning ?? false;
347
330
  }
348
331
 
349
- /** Get queued messages */
332
+ /** Get queued user-facing messages (excludes background notifications) */
350
333
  public get queuedMessages(): QueuedMessage[] {
351
- return this.messageQueue.getQueue();
334
+ return this.messageQueue
335
+ .getQueue()
336
+ .filter((m) => m.type !== "notification");
352
337
  }
353
338
 
354
339
  /** Get goal status string */
@@ -421,28 +406,31 @@ export class Agent {
421
406
  }
422
407
 
423
408
  /**
424
- * Unified dequeue trigger — checks state machine before processing.
425
- * Called from onMessageEnqueued, onLoadingChange(false), and
426
- * onCommandRunningChange(false).
409
+ * Unified dispatch trigger — checks state machine before processing.
410
+ * Handles user messages, bang commands, and background task notifications
411
+ * through a single serialized path. Called from onMessageEnqueued,
412
+ * onLoadingChange(false), and onCommandRunningChange(false).
427
413
  */
428
- private tryDequeue(): void {
414
+ private tryDispatch(): void {
429
415
  if (this.messageQueue.state !== "idle") return;
430
416
  if (!this.messageQueue.hasPending()) return;
431
417
  if (this.aiManager.isLoading || this.isCommandRunning) return;
432
418
 
433
419
  this.messageQueue.transitionTo("dispatching");
434
- this.processQueuedMessage()
420
+ this.dispatchPromise = this.processQueuedMessage()
435
421
  .catch((error) => {
436
422
  this.logger?.error("Failed to process queued message:", error);
437
423
  })
438
424
  .finally(() => {
439
425
  this.messageQueue.transitionTo("idle");
440
- this.tryDequeue(); // Re-check after processing
426
+ this.dispatchPromise = null;
427
+ this.tryDispatch(); // Re-check after processing
441
428
  });
442
429
  }
443
430
 
444
431
  /**
445
- * Process the next queued message when the agent becomes idle.
432
+ * Process the next queued item when the agent becomes idle.
433
+ * Handles three item types: user messages, bang commands, and notifications.
446
434
  */
447
435
  private async processQueuedMessage(): Promise<void> {
448
436
  const next = this.messageQueue.dequeue();
@@ -454,6 +442,25 @@ export class Agent {
454
442
  if (next.type === "bang") {
455
443
  await this.bangManager?.executeCommand(next.content);
456
444
  await this.messageManager.saveSession();
445
+ } else if (next.type === "notification") {
446
+ // Drain all pending notifications and batch them into a single AI turn
447
+ const allNotifications = [
448
+ next.content,
449
+ ...this.messageQueue.drainNotifications(),
450
+ ];
451
+ for (const xml of allNotifications) {
452
+ const block = parseTaskNotificationXml(xml);
453
+ if (block) {
454
+ this.messageManager.addNotificationMessage({
455
+ taskId: block.taskId,
456
+ taskType: block.taskType,
457
+ status: block.status,
458
+ summary: block.summary,
459
+ outputFile: block.outputFile,
460
+ });
461
+ }
462
+ }
463
+ await this.aiManager.sendAIMessage({ recursionDepth: 0 });
457
464
  } else {
458
465
  await this.sendMessage(next.content, next.images);
459
466
  }
@@ -681,31 +688,6 @@ export class Agent {
681
688
  this.aiManager.abortAIMessage();
682
689
  }
683
690
 
684
- /**
685
- * Process pending background task notifications by injecting them as user messages
686
- * and triggering a new AI response cycle.
687
- */
688
- private async processPendingNotifications(): Promise<void> {
689
- const notifications = this.notificationQueue.dequeueAll();
690
- if (notifications.length === 0) return;
691
-
692
- for (const notification of notifications) {
693
- const block = parseTaskNotificationXml(notification);
694
- if (block) {
695
- this.messageManager.addNotificationMessage({
696
- taskId: block.taskId,
697
- taskType: block.taskType,
698
- status: block.status,
699
- summary: block.summary,
700
- outputFile: block.outputFile,
701
- });
702
- }
703
- }
704
-
705
- // Trigger AI to process the notifications
706
- await this.aiManager.sendAIMessage({ recursionDepth: 0 });
707
- }
708
-
709
691
  /** Execute bash command (bang command) */
710
692
  public async bang(command: string): Promise<void> {
711
693
  // If the agent is busy, enqueue the bang command
@@ -866,8 +848,9 @@ export class Agent {
866
848
  /** Unified interrupt method, interrupts both AI messages and command execution */
867
849
  public abortMessage(): void {
868
850
  if (this.aiManager.isLoading || this.isCommandRunning) {
869
- // Clear queue first to prevent processQueuedMessage from dequeuing
870
- // when abortAIMessage triggers onLoadingChange(false)
851
+ // Clear user-facing queue items first to prevent processQueuedMessage
852
+ // from dequeuing when abortAIMessage triggers onLoadingChange(false).
853
+ // Notifications are preserved so background task results aren't lost.
871
854
  this.messageQueue.clear();
872
855
  this.options.callbacks?.onQueuedMessagesChange?.(this.queuedMessages);
873
856
  }
@@ -957,15 +940,14 @@ export class Agent {
957
940
  }).catch(() => {});
958
941
  await shutdownTelemetry();
959
942
 
960
- // Clear notification callback first to prevent any late triggers from
943
+ // Clear message queue callback first to prevent any late triggers from
961
944
  // starting async work during teardown
962
- this.notificationQueue.onNotificationsEnqueued = undefined;
945
+ this.messageQueue.onMessageEnqueued = undefined;
963
946
 
964
- // Await any pending notification processing to prevent race conditions
947
+ // Await any pending dispatch to prevent race conditions
965
948
  // with test teardown (e.g., V8 coverage stream cleanup)
966
- if (this.pendingNotificationPromises.length > 0) {
967
- await Promise.allSettled(this.pendingNotificationPromises);
968
- this.pendingNotificationPromises = [];
949
+ if (this.dispatchPromise) {
950
+ await this.dispatchPromise.catch(() => {});
969
951
  }
970
952
 
971
953
  // Fire SessionEnd hooks (fire-and-forget, don't block shutdown)
@@ -41,7 +41,7 @@ import { Container } from "../utils/container.js";
41
41
  import type { WorktreeSession } from "../utils/worktreeSession.js";
42
42
  import { recoverTruncatedJson } from "../utils/stringUtils.js";
43
43
  import { ConfigurationService } from "../services/configurationService.js";
44
- import type { NotificationQueue } from "./notificationQueue.js";
44
+ import type { MessageQueue } from "./messageQueue.js";
45
45
 
46
46
  import { logger } from "../utils/globalLogger.js";
47
47
  import {
@@ -115,6 +115,7 @@ export interface AIManagerOptions {
115
115
 
116
116
  export class AIManager {
117
117
  public isLoading: boolean = false;
118
+ private turnGeneration = 0; // Guards against concurrent sendAIMessage turns
118
119
  private abortController: AbortController | null = null;
119
120
  onLoadingChange?: (loading: boolean) => void;
120
121
  private toolAbortController: AbortController | null = null;
@@ -397,6 +398,10 @@ export class AIManager {
397
398
  }
398
399
 
399
400
  public abortAIMessage(): void {
401
+ // Bump generation so the in-flight turn's end-of-turn setIsLoading(false)
402
+ // is skipped (generation mismatch), preventing it from racing with this abort.
403
+ this.turnGeneration++;
404
+
400
405
  // Interrupt AI service
401
406
  if (this.abortController) {
402
407
  try {
@@ -818,6 +823,15 @@ export class AIManager {
818
823
  const { recursionDepth = 0, model, allowedRules, maxTokens } = options;
819
824
  let turnOffset = recursionDepth;
820
825
 
826
+ // Reserve this turn synchronously before any async work. Bumping the
827
+ // generation invalidates any in-flight turn's end-of-turn cleanup, and
828
+ // recording myGeneration lets us skip cleanup if a newer turn (or abort)
829
+ // has superseded us. setIsLoading(true) here also closes the "idle but
830
+ // previous turn not finished" race window by marking us busy immediately.
831
+ this.turnGeneration++;
832
+ const myGeneration = this.turnGeneration;
833
+ this.setIsLoading(true);
834
+
821
835
  outer: while (true) {
822
836
  let shouldRestart = false;
823
837
 
@@ -839,9 +853,8 @@ export class AIManager {
839
853
  }).catch(() => {}); // Non-blocking
840
854
  }
841
855
 
842
- // Set loading state early for the initial call, before any async work
856
+ // Apply allowed rules for the initial call
843
857
  if (turnOffset === 0) {
844
- this.setIsLoading(true);
845
858
  if (allowedRules && allowedRules.length > 0) {
846
859
  this.permissionManager?.addTemporaryRules(allowedRules);
847
860
  }
@@ -1375,8 +1388,6 @@ export class AIManager {
1375
1388
 
1376
1389
  // Save session in each iteration to ensure message persistence
1377
1390
  await this.messageManager.saveSession();
1378
- // Set loading to false first
1379
- this.setIsLoading(false);
1380
1391
 
1381
1392
  // Clear temporary rules
1382
1393
  this.permissionManager?.clearTemporaryRules();
@@ -1475,11 +1486,11 @@ export class AIManager {
1475
1486
 
1476
1487
  // Inject pending notifications from background tasks (after Stop hooks,
1477
1488
  // aligned with Claude Code which fires Stop hooks unconditionally)
1478
- const notificationQueue = this.container.has("NotificationQueue")
1479
- ? this.container.get<NotificationQueue>("NotificationQueue")
1489
+ const messageQueue = this.container.has("MessageQueue")
1490
+ ? this.container.get<MessageQueue>("MessageQueue")
1480
1491
  : undefined;
1481
- if (notificationQueue && notificationQueue.hasPending()) {
1482
- const notifications = notificationQueue.dequeueAll();
1492
+ if (messageQueue && messageQueue.hasNotifications()) {
1493
+ const notifications = messageQueue.drainNotifications();
1483
1494
  for (const notification of notifications) {
1484
1495
  const block = parseTaskNotificationXml(notification);
1485
1496
  if (block) {
@@ -1498,7 +1509,16 @@ export class AIManager {
1498
1509
  }
1499
1510
  }
1500
1511
 
1501
- if (!shouldRestart) break outer;
1512
+ if (!shouldRestart) {
1513
+ // Release loading state only after ALL cleanup is done (session save,
1514
+ // Stop hooks, notification injection). Generation check ensures a
1515
+ // superseded (aborted or newer) turn doesn't clobber the current
1516
+ // loading state.
1517
+ if (myGeneration === this.turnGeneration) {
1518
+ this.setIsLoading(false);
1519
+ }
1520
+ break outer;
1521
+ }
1502
1522
  }
1503
1523
  }
1504
1524
 
@@ -6,7 +6,7 @@ import { BackgroundTask, BackgroundShell } from "../types/processes.js";
6
6
  import { stripAnsiColors } from "../utils/stringUtils.js";
7
7
  import { logger } from "../utils/globalLogger.js";
8
8
  import { Container } from "../utils/container.js";
9
- import { NotificationQueue } from "./notificationQueue.js";
9
+ import { MessageQueue } from "./messageQueue.js";
10
10
  import { resolveShellPath } from "../utils/shellResolver.js";
11
11
 
12
12
  export interface BackgroundTaskManagerCallbacks {
@@ -32,10 +32,6 @@ export class BackgroundTaskManager {
32
32
  this.workdir = options.workdir;
33
33
  }
34
34
 
35
- private get notificationQueue(): NotificationQueue {
36
- return this.container.get<NotificationQueue>("NotificationQueue")!;
37
- }
38
-
39
35
  /**
40
36
  * Fire the onBackgroundTasksChange callback so UI consumers refresh.
41
37
  * Public so other managers (e.g. WorkflowManager) can trigger a refresh
@@ -178,13 +174,13 @@ export class BackgroundTaskManager {
178
174
 
179
175
  // Skip notification if task was manually killed (user/agent-initiated stop)
180
176
  if (!wasKilled) {
181
- const notificationQueue = this.container.has("NotificationQueue")
182
- ? this.container.get<NotificationQueue>("NotificationQueue")
177
+ const messageQueue = this.container.has("MessageQueue")
178
+ ? this.container.get<MessageQueue>("MessageQueue")
183
179
  : undefined;
184
- if (notificationQueue) {
180
+ if (messageQueue) {
185
181
  const statusStr = shell.status;
186
182
  const summary = `Command "${command}" ${statusStr} with exit code ${code ?? 0}`;
187
- notificationQueue.enqueue(
183
+ messageQueue.enqueueNotification(
188
184
  `<task-notification>\n<task-id>${id}</task-id>\n<task-type>shell</task-type>\n<output-file>${logPath}</output-file>\n<status>${statusStr}</status>\n<summary>${summary}</summary>\n</task-notification>`,
189
185
  );
190
186
  }
@@ -208,12 +204,12 @@ export class BackgroundTaskManager {
208
204
  this.notifyTasksChange();
209
205
 
210
206
  // Enqueue error notification
211
- const notificationQueue = this.container.has("NotificationQueue")
212
- ? this.container.get<NotificationQueue>("NotificationQueue")
207
+ const messageQueue = this.container.has("MessageQueue")
208
+ ? this.container.get<MessageQueue>("MessageQueue")
213
209
  : undefined;
214
- if (notificationQueue) {
210
+ if (messageQueue) {
215
211
  const summary = `Command "${command}" failed with error: ${stripAnsiColors(error.message)}`;
216
- notificationQueue.enqueue(
212
+ messageQueue.enqueueNotification(
217
213
  `<task-notification>\n<task-id>${id}</task-id>\n<task-type>shell</task-type>\n<output-file>${logPath}</output-file>\n<status>failed</status>\n<summary>${summary}</summary>\n</task-notification>`,
218
214
  );
219
215
  }
@@ -337,13 +333,13 @@ export class BackgroundTaskManager {
337
333
 
338
334
  // Skip notification if task was manually killed (user/agent-initiated stop)
339
335
  if (!wasKilled) {
340
- const notificationQueue = this.container.has("NotificationQueue")
341
- ? this.container.get<NotificationQueue>("NotificationQueue")
336
+ const messageQueue = this.container.has("MessageQueue")
337
+ ? this.container.get<MessageQueue>("MessageQueue")
342
338
  : undefined;
343
- if (notificationQueue) {
339
+ if (messageQueue) {
344
340
  const statusStr = shell.status;
345
341
  const summary = `Command "${command}" ${statusStr} with exit code ${code ?? 0}`;
346
- notificationQueue.enqueue(
342
+ messageQueue.enqueueNotification(
347
343
  `<task-notification>\n<task-id>${id}</task-id>\n<task-type>shell</task-type>\n<output-file>${logPath}</output-file>\n<status>${statusStr}</status>\n<summary>${summary}</summary>\n</task-notification>`,
348
344
  );
349
345
  }
@@ -364,12 +360,12 @@ export class BackgroundTaskManager {
364
360
  this.notifyTasksChange();
365
361
 
366
362
  // Enqueue error notification
367
- const notificationQueue = this.container.has("NotificationQueue")
368
- ? this.container.get<NotificationQueue>("NotificationQueue")
363
+ const messageQueue = this.container.has("MessageQueue")
364
+ ? this.container.get<MessageQueue>("MessageQueue")
369
365
  : undefined;
370
- if (notificationQueue) {
366
+ if (messageQueue) {
371
367
  const summary = `Command "${command}" failed with error: ${stripAnsiColors(error.message)}`;
372
- notificationQueue.enqueue(
368
+ messageQueue.enqueueNotification(
373
369
  `<task-notification>\n<task-id>${id}</task-id>\n<task-type>shell</task-type>\n<output-file>${logPath}</output-file>\n<status>failed</status>\n<summary>${summary}</summary>\n</task-notification>`,
374
370
  );
375
371
  }
@@ -2,7 +2,7 @@ export type QueueState = "idle" | "dispatching" | "running";
2
2
 
3
3
  export interface QueuedMessage {
4
4
  id?: string;
5
- type?: "message" | "bang";
5
+ type?: "message" | "bang" | "notification";
6
6
  content: string;
7
7
  images?: Array<{ path: string; mimeType: string }>;
8
8
  longTextMap?: Record<string, string>;
@@ -44,8 +44,12 @@ export class MessageQueue {
44
44
  return this.queue.shift() ?? null;
45
45
  }
46
46
 
47
+ /**
48
+ * Clear user-facing items (messages, bang commands) but preserve pending
49
+ * notifications so background task results are not lost on abort.
50
+ */
47
51
  clear(): void {
48
- this.queue = [];
52
+ this.queue = this.queue.filter((m) => m.type === "notification");
49
53
  this._state = "idle";
50
54
  }
51
55
 
@@ -113,4 +117,41 @@ export class MessageQueue {
113
117
  this.queue = remaining;
114
118
  return editable;
115
119
  }
120
+
121
+ /**
122
+ * Enqueue a background task notification (XML string). Notifications are
123
+ * not user-editable and not surfaced in the queued-messages UI.
124
+ */
125
+ enqueueNotification(xml: string): void {
126
+ this.queue.push({
127
+ id: `mq-${this.nextId++}`,
128
+ type: "notification",
129
+ content: xml,
130
+ editable: false,
131
+ });
132
+ this.onMessageEnqueued?.();
133
+ }
134
+
135
+ /** Whether any notification items are pending. */
136
+ hasNotifications(): boolean {
137
+ return this.queue.some((m) => m.type === "notification");
138
+ }
139
+
140
+ /**
141
+ * Drain all notification items, returning their XML content. Non-notification
142
+ * items (messages, bang commands) are preserved in the queue.
143
+ */
144
+ drainNotifications(): string[] {
145
+ const notifications: string[] = [];
146
+ const remaining: QueuedMessage[] = [];
147
+ for (const msg of this.queue) {
148
+ if (msg.type === "notification") {
149
+ notifications.push(msg.content);
150
+ } else {
151
+ remaining.push(msg);
152
+ }
153
+ }
154
+ this.queue = remaining;
155
+ return notifications;
156
+ }
116
157
  }
@@ -19,7 +19,7 @@ import {
19
19
  createAbortPromise,
20
20
  } from "../utils/abortUtils.js";
21
21
  import { BackgroundTaskManager } from "./backgroundTaskManager.js";
22
- import { NotificationQueue } from "./notificationQueue.js";
22
+ import { MessageQueue } from "./messageQueue.js";
23
23
  import { logger } from "../utils/globalLogger.js";
24
24
  import {
25
25
  UserMessageParams,
@@ -81,7 +81,6 @@ export interface SubagentInstance {
81
81
  toolManager: ToolManager;
82
82
  permissionManager: PermissionManager;
83
83
  backgroundTaskManager: BackgroundTaskManager;
84
- notificationQueue: NotificationQueue;
85
84
  status: "initializing" | "active" | "completed" | "error" | "aborted";
86
85
  messages: Message[];
87
86
  usedTools: {
@@ -393,10 +392,6 @@ export class SubagentManager {
393
392
  });
394
393
  subagentContainer.register("AIManager", aiManager);
395
394
 
396
- // Create isolated NotificationQueue for the subagent/forked agent
397
- const subagentNotificationQueue = new NotificationQueue();
398
- subagentContainer.register("NotificationQueue", subagentNotificationQueue);
399
-
400
395
  // Create isolated BackgroundTaskManager for the subagent/forked agent
401
396
  const subagentBackgroundTaskManager = new BackgroundTaskManager(
402
397
  subagentContainer,
@@ -417,7 +412,6 @@ export class SubagentManager {
417
412
  toolManager,
418
413
  permissionManager: subagentPermissionManager,
419
414
  backgroundTaskManager: subagentBackgroundTaskManager,
420
- notificationQueue: subagentNotificationQueue,
421
415
  status: "initializing",
422
416
  messages: [],
423
417
  usedTools: [], // Initialize usedTools
@@ -657,12 +651,12 @@ export class SubagentManager {
657
651
  }
658
652
  // Skip notification if task was already stopped (e.g. by main agent shutdown)
659
653
  if (!wasAlreadyKilled) {
660
- const notificationQueue = this.container.has("NotificationQueue")
661
- ? this.container.get<NotificationQueue>("NotificationQueue")
654
+ const messageQueue = this.container.has("MessageQueue")
655
+ ? this.container.get<MessageQueue>("MessageQueue")
662
656
  : undefined;
663
- if (notificationQueue) {
657
+ if (messageQueue) {
664
658
  const summary = `Agent task "${instance.description}" completed`;
665
- notificationQueue.enqueue(
659
+ messageQueue.enqueueNotification(
666
660
  `<task-notification>\n<task-id>${instance.backgroundTaskId}</task-id>\n<task-type>agent</task-type>\n<status>completed</status>\n<summary>${summary}</summary>\n</task-notification>`,
667
661
  );
668
662
  }
@@ -694,14 +688,14 @@ export class SubagentManager {
694
688
  }
695
689
  // Skip notification if task was already stopped (e.g. by main agent shutdown)
696
690
  if (!wasAlreadyKilled) {
697
- const notificationQueue = this.container.has("NotificationQueue")
698
- ? this.container.get<NotificationQueue>("NotificationQueue")
691
+ const messageQueue = this.container.has("MessageQueue")
692
+ ? this.container.get<MessageQueue>("MessageQueue")
699
693
  : undefined;
700
- if (notificationQueue) {
694
+ if (messageQueue) {
701
695
  const errorMsg =
702
696
  error instanceof Error ? error.message : String(error);
703
697
  const summary = `Agent task "${instance.description}" failed: ${errorMsg}`;
704
- notificationQueue.enqueue(
698
+ messageQueue.enqueueNotification(
705
699
  `<task-notification>\n<task-id>${instance.backgroundTaskId}</task-id>\n<task-type>agent</task-type>\n<status>failed</status>\n<summary>${summary}</summary>\n</task-notification>`,
706
700
  );
707
701
  }
@@ -4,7 +4,7 @@ import * as fs from "fs";
4
4
  import * as path from "path";
5
5
  import { Container } from "../utils/container.js";
6
6
  import { BackgroundTaskManager } from "./backgroundTaskManager.js";
7
- import { NotificationQueue } from "./notificationQueue.js";
7
+ import { MessageQueue } from "./messageQueue.js";
8
8
  import { SubagentManager } from "./subagentManager.js";
9
9
  import { taskNotificationToXml } from "../utils/notificationXml.js";
10
10
  import { ConcurrencyLimiter } from "../workflow/concurrencyLimiter.js";
@@ -49,8 +49,8 @@ export class WorkflowManager {
49
49
  return this.container.get<BackgroundTaskManager>("BackgroundTaskManager")!;
50
50
  }
51
51
 
52
- private get notificationQueue(): NotificationQueue {
53
- return this.container.get<NotificationQueue>("NotificationQueue")!;
52
+ private get messageQueue(): MessageQueue {
53
+ return this.container.get<MessageQueue>("MessageQueue")!;
54
54
  }
55
55
 
56
56
  private get subagentManager(): SubagentManager {
@@ -295,7 +295,7 @@ export class WorkflowManager {
295
295
 
296
296
  // Enqueue completion notification
297
297
  const journalPath = journal.filePath;
298
- this.notificationQueue.enqueue(
298
+ this.messageQueue.enqueueNotification(
299
299
  taskNotificationToXml({
300
300
  type: "task_notification",
301
301
  taskId: runId,
@@ -337,7 +337,7 @@ export class WorkflowManager {
337
337
  task.endTime = Date.now();
338
338
  }
339
339
 
340
- this.notificationQueue.enqueue(
340
+ this.messageQueue.enqueueNotification(
341
341
  taskNotificationToXml({
342
342
  type: "task_notification",
343
343
  taskId: runId,
@@ -71,6 +71,13 @@ export class InteractionService {
71
71
  })),
72
72
  });
73
73
 
74
+ // Reserve loading state synchronously before any async work (hooks).
75
+ // This closes the race window where a concurrent notification/message
76
+ // could see isLoading=false during the await below and start a second
77
+ // sendAIMessage. sendAIMessage's generation guard is the backstop, but
78
+ // reserving here prevents the race at its source.
79
+ aiManager.setIsLoading(true);
80
+
74
81
  // Execute UserPromptSubmit hooks after adding the user message
75
82
  if (hookManager) {
76
83
  try {
@@ -104,6 +111,8 @@ export class InteractionService {
104
111
  "UserPromptSubmit hook blocked prompt processing with error:",
105
112
  processResult.errorMessage,
106
113
  );
114
+ // Release the loading state we reserved above
115
+ aiManager.setIsLoading(false);
107
116
  return; // Don't send to AI
108
117
  }
109
118
  } catch (error) {
@@ -116,6 +125,8 @@ export class InteractionService {
116
125
  await aiManager.sendAIMessage();
117
126
  } catch (error) {
118
127
  console.error("Failed to add user message:", error);
128
+ // Ensure loading state is released on error
129
+ aiManager.setIsLoading(false);
119
130
  // Loading state will be automatically updated by the useEffect that watches messages
120
131
  }
121
132
  }
@@ -7,6 +7,13 @@ import { logger } from "../utils/globalLogger.js";
7
7
  import { Container } from "../utils/container.js";
8
8
  import type { MessageManager } from "../managers/messageManager.js";
9
9
 
10
+ function byIdAsc(a: Task, b: Task) {
11
+ const aNum = parseInt(a.id, 10);
12
+ const bNum = parseInt(b.id, 10);
13
+ if (!isNaN(aNum) && !isNaN(bNum)) return aNum - bNum;
14
+ return a.id.localeCompare(b.id);
15
+ }
16
+
10
17
  export class TaskManager extends EventEmitter {
11
18
  private readonly baseDir: string;
12
19
  private taskListId: string;
@@ -234,7 +241,7 @@ export class TaskManager extends EventEmitter {
234
241
  }),
235
242
  );
236
243
 
237
- return tasks.filter((t): t is Task => t !== null);
244
+ return tasks.filter((t): t is Task => t !== null).sort(byIdAsc);
238
245
  } catch (error) {
239
246
  if ((error as NodeJS.ErrnoException).code === "ENOENT") {
240
247
  return [];
@@ -1,7 +1,6 @@
1
1
  import { Container } from "./container.js";
2
2
  import { ForegroundTaskManager } from "../managers/foregroundTaskManager.js";
3
3
  import { BackgroundTaskManager } from "../managers/backgroundTaskManager.js";
4
- import { NotificationQueue } from "../managers/notificationQueue.js";
5
4
  import { MessageQueue } from "../managers/messageQueue.js";
6
5
  import { TaskManager } from "../services/taskManager.js";
7
6
  import { MessageManager } from "../managers/messageManager.js";
@@ -92,9 +91,6 @@ export function setupAgentContainer(
92
91
  // container so it is isolated per session, not shared process-wide.
93
92
  container.register("WorktreeSession", null);
94
93
 
95
- const notificationQueue = new NotificationQueue();
96
- container.register("NotificationQueue", notificationQueue);
97
-
98
94
  const messageQueue = new MessageQueue();
99
95
  container.register("MessageQueue", messageQueue);
100
96
 
@@ -1 +0,0 @@
1
- {"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"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"}