onbuzz 4.9.13 → 4.10.0

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 (451) hide show
  1. package/node_modules/glob/README.md +31 -5
  2. package/node_modules/glob/dist/commonjs/glob.d.ts +8 -0
  3. package/node_modules/glob/dist/commonjs/glob.d.ts.map +1 -1
  4. package/node_modules/glob/dist/commonjs/glob.js +2 -1
  5. package/node_modules/glob/dist/commonjs/glob.js.map +1 -1
  6. package/node_modules/glob/dist/commonjs/index.min.js +3 -3
  7. package/node_modules/glob/dist/commonjs/index.min.js.map +4 -4
  8. package/node_modules/glob/dist/commonjs/pattern.d.ts +3 -0
  9. package/node_modules/glob/dist/commonjs/pattern.d.ts.map +1 -1
  10. package/node_modules/glob/dist/commonjs/pattern.js +4 -0
  11. package/node_modules/glob/dist/commonjs/pattern.js.map +1 -1
  12. package/node_modules/glob/dist/esm/glob.d.ts +8 -0
  13. package/node_modules/glob/dist/esm/glob.d.ts.map +1 -1
  14. package/node_modules/glob/dist/esm/glob.js +2 -1
  15. package/node_modules/glob/dist/esm/glob.js.map +1 -1
  16. package/node_modules/glob/dist/esm/index.min.js +3 -3
  17. package/node_modules/glob/dist/esm/index.min.js.map +4 -4
  18. package/node_modules/glob/dist/esm/pattern.d.ts +3 -0
  19. package/node_modules/glob/dist/esm/pattern.d.ts.map +1 -1
  20. package/node_modules/glob/dist/esm/pattern.js +4 -0
  21. package/node_modules/glob/dist/esm/pattern.js.map +1 -1
  22. package/node_modules/{@isaacs → glob/node_modules}/balanced-match/README.md +7 -10
  23. package/node_modules/{@isaacs → glob/node_modules}/balanced-match/package.json +7 -18
  24. package/node_modules/{@isaacs → glob/node_modules}/brace-expansion/README.md +3 -6
  25. package/node_modules/{@isaacs → glob/node_modules}/brace-expansion/dist/commonjs/index.js +6 -4
  26. package/node_modules/glob/node_modules/brace-expansion/dist/commonjs/index.js.map +1 -0
  27. package/node_modules/{@isaacs → glob/node_modules}/brace-expansion/dist/esm/index.js +6 -4
  28. package/node_modules/glob/node_modules/brace-expansion/dist/esm/index.js.map +1 -0
  29. package/node_modules/{@isaacs → glob/node_modules}/brace-expansion/package.json +11 -7
  30. package/node_modules/glob/node_modules/minimatch/README.md +76 -1
  31. package/node_modules/glob/node_modules/minimatch/dist/commonjs/assert-valid-pattern.d.ts +1 -1
  32. package/node_modules/glob/node_modules/minimatch/dist/commonjs/assert-valid-pattern.d.ts.map +1 -1
  33. package/node_modules/glob/node_modules/minimatch/dist/commonjs/assert-valid-pattern.js.map +1 -1
  34. package/node_modules/glob/node_modules/minimatch/dist/commonjs/ast.d.ts +4 -2
  35. package/node_modules/glob/node_modules/minimatch/dist/commonjs/ast.d.ts.map +1 -1
  36. package/node_modules/glob/node_modules/minimatch/dist/commonjs/ast.js +309 -55
  37. package/node_modules/glob/node_modules/minimatch/dist/commonjs/ast.js.map +1 -1
  38. package/node_modules/glob/node_modules/minimatch/dist/commonjs/brace-expressions.d.ts.map +1 -1
  39. package/node_modules/glob/node_modules/minimatch/dist/commonjs/brace-expressions.js +2 -4
  40. package/node_modules/glob/node_modules/minimatch/dist/commonjs/brace-expressions.js.map +1 -1
  41. package/node_modules/glob/node_modules/minimatch/dist/commonjs/escape.d.ts +1 -1
  42. package/node_modules/glob/node_modules/minimatch/dist/commonjs/escape.d.ts.map +1 -1
  43. package/node_modules/glob/node_modules/minimatch/dist/commonjs/escape.js +4 -4
  44. package/node_modules/glob/node_modules/minimatch/dist/commonjs/escape.js.map +1 -1
  45. package/node_modules/glob/node_modules/minimatch/dist/commonjs/index.d.ts +81 -1
  46. package/node_modules/glob/node_modules/minimatch/dist/commonjs/index.d.ts.map +1 -1
  47. package/node_modules/glob/node_modules/minimatch/dist/commonjs/index.js +232 -134
  48. package/node_modules/glob/node_modules/minimatch/dist/commonjs/index.js.map +1 -1
  49. package/node_modules/glob/node_modules/minimatch/dist/commonjs/unescape.d.ts +1 -1
  50. package/node_modules/glob/node_modules/minimatch/dist/commonjs/unescape.d.ts.map +1 -1
  51. package/node_modules/glob/node_modules/minimatch/dist/commonjs/unescape.js +8 -8
  52. package/node_modules/glob/node_modules/minimatch/dist/commonjs/unescape.js.map +1 -1
  53. package/node_modules/glob/node_modules/minimatch/dist/esm/assert-valid-pattern.d.ts +1 -1
  54. package/node_modules/glob/node_modules/minimatch/dist/esm/assert-valid-pattern.d.ts.map +1 -1
  55. package/node_modules/glob/node_modules/minimatch/dist/esm/assert-valid-pattern.js.map +1 -1
  56. package/node_modules/glob/node_modules/minimatch/dist/esm/ast.d.ts +4 -2
  57. package/node_modules/glob/node_modules/minimatch/dist/esm/ast.d.ts.map +1 -1
  58. package/node_modules/glob/node_modules/minimatch/dist/esm/ast.js +309 -55
  59. package/node_modules/glob/node_modules/minimatch/dist/esm/ast.js.map +1 -1
  60. package/node_modules/glob/node_modules/minimatch/dist/esm/brace-expressions.d.ts.map +1 -1
  61. package/node_modules/glob/node_modules/minimatch/dist/esm/brace-expressions.js +2 -4
  62. package/node_modules/glob/node_modules/minimatch/dist/esm/brace-expressions.js.map +1 -1
  63. package/node_modules/glob/node_modules/minimatch/dist/esm/escape.d.ts +1 -1
  64. package/node_modules/glob/node_modules/minimatch/dist/esm/escape.d.ts.map +1 -1
  65. package/node_modules/glob/node_modules/minimatch/dist/esm/escape.js +4 -4
  66. package/node_modules/glob/node_modules/minimatch/dist/esm/escape.js.map +1 -1
  67. package/node_modules/glob/node_modules/minimatch/dist/esm/index.d.ts +81 -1
  68. package/node_modules/glob/node_modules/minimatch/dist/esm/index.d.ts.map +1 -1
  69. package/node_modules/glob/node_modules/minimatch/dist/esm/index.js +232 -134
  70. package/node_modules/glob/node_modules/minimatch/dist/esm/index.js.map +1 -1
  71. package/node_modules/glob/node_modules/minimatch/dist/esm/unescape.d.ts +1 -1
  72. package/node_modules/glob/node_modules/minimatch/dist/esm/unescape.d.ts.map +1 -1
  73. package/node_modules/glob/node_modules/minimatch/dist/esm/unescape.js +8 -8
  74. package/node_modules/glob/node_modules/minimatch/dist/esm/unescape.js.map +1 -1
  75. package/node_modules/glob/node_modules/minimatch/package.json +17 -11
  76. package/node_modules/glob/package.json +10 -13
  77. package/node_modules/minipass/LICENSE.md +55 -0
  78. package/node_modules/minipass/dist/commonjs/index.d.ts +12 -16
  79. package/node_modules/minipass/dist/commonjs/index.d.ts.map +1 -1
  80. package/node_modules/minipass/dist/commonjs/index.js +13 -3
  81. package/node_modules/minipass/dist/commonjs/index.js.map +1 -1
  82. package/node_modules/minipass/dist/esm/index.d.ts +12 -16
  83. package/node_modules/minipass/dist/esm/index.d.ts.map +1 -1
  84. package/node_modules/minipass/dist/esm/index.js +3 -1
  85. package/node_modules/minipass/dist/esm/index.js.map +1 -1
  86. package/node_modules/minipass/package.json +9 -14
  87. package/node_modules/path-scurry/node_modules/lru-cache/README.md +96 -10
  88. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/diagnostics-channel-browser.d.ts.map +1 -0
  89. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/diagnostics-channel-browser.js.map +1 -0
  90. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/diagnostics-channel.d.ts +5 -0
  91. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/diagnostics-channel.js +7 -0
  92. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/index.d.ts +1400 -0
  93. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/index.d.ts.map +1 -0
  94. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/index.js +1726 -0
  95. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/index.js.map +1 -0
  96. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/index.min.js +2 -0
  97. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/index.min.js.map +7 -0
  98. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/perf.d.ts +12 -0
  99. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/perf.d.ts.map +1 -0
  100. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/perf.js +10 -0
  101. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/perf.js.map +1 -0
  102. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/diagnostics-channel-cjs.cjs.map +1 -0
  103. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/diagnostics-channel-cjs.d.cts.map +1 -0
  104. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/diagnostics-channel.d.ts +5 -0
  105. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/diagnostics-channel.js +7 -0
  106. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/index.d.ts +109 -32
  107. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/index.d.ts.map +1 -1
  108. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/index.js +334 -197
  109. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/index.js.map +1 -1
  110. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/index.min.js +1 -1
  111. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/index.min.js.map +4 -4
  112. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/diagnostics-channel-node.d.ts.map +1 -0
  113. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/diagnostics-channel-node.js.map +1 -0
  114. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/diagnostics-channel.d.ts +5 -0
  115. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/diagnostics-channel.js +9 -0
  116. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/index.d.ts +1400 -0
  117. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/index.d.ts.map +1 -0
  118. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/index.js +1726 -0
  119. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/index.js.map +1 -0
  120. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/index.min.js +2 -0
  121. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/index.min.js.map +7 -0
  122. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/perf.d.ts +12 -0
  123. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/perf.d.ts.map +1 -0
  124. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/perf.js +10 -0
  125. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/perf.js.map +1 -0
  126. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/perf.d.ts +12 -0
  127. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/perf.d.ts.map +1 -0
  128. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/perf.js +10 -0
  129. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/perf.js.map +1 -0
  130. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/diagnostics-channel-browser.d.ts.map +1 -0
  131. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/diagnostics-channel-browser.js.map +1 -0
  132. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/diagnostics-channel.d.ts +5 -0
  133. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/diagnostics-channel.js +4 -0
  134. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/index.d.ts +1400 -0
  135. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/index.d.ts.map +1 -0
  136. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/index.js +1722 -0
  137. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/index.js.map +1 -0
  138. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/index.min.js +2 -0
  139. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/index.min.js.map +7 -0
  140. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/perf.d.ts +12 -0
  141. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/perf.d.ts.map +1 -0
  142. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/perf.js +7 -0
  143. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/perf.js.map +1 -0
  144. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/diagnostics-channel-esm.d.mts.map +1 -0
  145. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/diagnostics-channel-esm.mjs.map +1 -0
  146. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/diagnostics-channel.d.ts +5 -0
  147. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/diagnostics-channel.js +19 -0
  148. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/index.d.ts +109 -32
  149. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/index.d.ts.map +1 -1
  150. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/index.js +333 -196
  151. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/index.js.map +1 -1
  152. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/index.min.js +1 -1
  153. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/index.min.js.map +4 -4
  154. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/diagnostics-channel-node.d.ts.map +1 -0
  155. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/diagnostics-channel-node.js.map +1 -0
  156. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/diagnostics-channel.d.ts +5 -0
  157. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/diagnostics-channel.js +6 -0
  158. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/index.d.ts +1400 -0
  159. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/index.d.ts.map +1 -0
  160. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/index.js +1722 -0
  161. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/index.js.map +1 -0
  162. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/index.min.js +2 -0
  163. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/index.min.js.map +7 -0
  164. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/perf.d.ts +12 -0
  165. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/perf.d.ts.map +1 -0
  166. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/perf.js +7 -0
  167. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/perf.js.map +1 -0
  168. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/perf.d.ts +12 -0
  169. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/perf.d.ts.map +1 -0
  170. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/perf.js +7 -0
  171. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/perf.js.map +1 -0
  172. package/node_modules/path-scurry/node_modules/lru-cache/package.json +71 -18
  173. package/node_modules/path-scurry/package.json +8 -24
  174. package/package.json +1 -1
  175. package/scripts/debug-balance-probe.mjs +35 -35
  176. package/scripts/push-image.sh +43 -43
  177. package/scripts/setup-acr.sh +65 -65
  178. package/scripts/verify-optional-deps.js +96 -1
  179. package/src/__tests__/composioCliFlags.test.js +239 -239
  180. package/src/analyzers/CSSAnalyzer.js +298 -297
  181. package/src/analyzers/ConfigValidator.js +691 -690
  182. package/src/analyzers/ESLintAnalyzer.js +320 -320
  183. package/src/analyzers/JavaScriptAnalyzer.js +260 -261
  184. package/src/analyzers/PrettierFormatter.js +246 -247
  185. package/src/analyzers/PythonAnalyzer.js +283 -283
  186. package/src/analyzers/SecurityAnalyzer.js +729 -729
  187. package/src/analyzers/SparrowAnalyzer.js +341 -341
  188. package/src/analyzers/TypeScriptAnalyzer.js +247 -247
  189. package/src/analyzers/__tests__/CSSAnalyzer.test.js +41 -41
  190. package/src/analyzers/__tests__/ConfigValidator.test.js +362 -362
  191. package/src/analyzers/__tests__/JavaScriptAnalyzer.test.js +40 -40
  192. package/src/analyzers/__tests__/PythonAnalyzer.test.js +205 -208
  193. package/src/analyzers/__tests__/SecurityAnalyzer.test.js +303 -303
  194. package/src/analyzers/__tests__/TypeScriptAnalyzer.test.js +187 -187
  195. package/src/analyzers/codeCloneDetector/analyzer.js +344 -344
  196. package/src/analyzers/codeCloneDetector/detector.js +250 -250
  197. package/src/analyzers/codeCloneDetector/index.js +194 -192
  198. package/src/analyzers/codeCloneDetector/parser.js +199 -199
  199. package/src/core/__tests__/agentPool.test.js +866 -866
  200. package/src/core/__tests__/agentPoolAutoResume.test.js +209 -209
  201. package/src/core/__tests__/agentPoolWakeOnMessage.test.js +315 -315
  202. package/src/core/__tests__/agentScheduler.emptyResponseChatStall.test.js +213 -213
  203. package/src/core/__tests__/agentScheduler.errorCategorisation.test.js +246 -246
  204. package/src/core/__tests__/agentScheduler.firstChunkTimeout.test.js +138 -138
  205. package/src/core/__tests__/agentScheduler.modeTransitions.test.js +233 -233
  206. package/src/core/__tests__/agentScheduler.nativePromptPick.test.js +319 -319
  207. package/src/core/__tests__/agentScheduler.taskLifecycleInstruction.test.js +78 -78
  208. package/src/core/__tests__/agentScheduler.visualizer.test.js +258 -258
  209. package/src/core/__tests__/flowCheckpointStore.test.js +140 -140
  210. package/src/core/__tests__/flowEndToEnd.test.js +565 -565
  211. package/src/core/__tests__/flowFieldMapping.test.js +188 -189
  212. package/src/core/__tests__/flowLintClientMirror.test.js +96 -98
  213. package/src/core/__tests__/flowSavePayload.test.js +170 -169
  214. package/src/core/__tests__/flowTemplates.test.js +311 -311
  215. package/src/core/__tests__/flowVersionStore.test.js +123 -123
  216. package/src/core/__tests__/messageProcessor.test.js +669 -669
  217. package/src/core/__tests__/stateManager.test.js +0 -1
  218. package/src/core/agentPool.js +2474 -2475
  219. package/src/core/agentScheduler.js +1 -4
  220. package/src/core/contextManager.js +708 -708
  221. package/src/core/flowExecutor.js +1510 -1510
  222. package/src/core/flowFieldMapping.js +136 -138
  223. package/src/core/messageProcessor.js +953 -954
  224. package/src/core/orchestrator.js +593 -595
  225. package/src/core/stateManager.js +1765 -1752
  226. package/src/index.js +1221 -1221
  227. package/src/interfaces/__tests__/archivedAgentDelete.test.js +207 -207
  228. package/src/interfaces/__tests__/bulkAgentRoute.test.js +361 -361
  229. package/src/interfaces/__tests__/imageServing.test.js +228 -228
  230. package/src/interfaces/__tests__/remoteSessionAuth.test.js +308 -308
  231. package/src/interfaces/__tests__/videoJobsRoutes.test.js +178 -179
  232. package/src/interfaces/__tests__/webServer.marketplace.test.js +629 -629
  233. package/src/interfaces/schedulerRoutes.js +50 -50
  234. package/src/interfaces/terminal/__tests__/smoke/connection.test.js +341 -350
  235. package/src/interfaces/terminal/__tests__/smoke/enhancements.test.js +156 -156
  236. package/src/interfaces/terminal/__tests__/smoke/imports.test.js +325 -330
  237. package/src/interfaces/terminal/__tests__/smoke/tools.test.js +385 -388
  238. package/src/interfaces/terminal/api/session.js +265 -266
  239. package/src/interfaces/terminal/api/websocket.js +496 -497
  240. package/src/interfaces/terminal/components/AgentCreator.js +691 -705
  241. package/src/interfaces/terminal/components/AgentEditor.js +676 -678
  242. package/src/interfaces/terminal/components/AgentSwitcher.js +331 -330
  243. package/src/interfaces/terminal/components/ErrorPanel.js +263 -264
  244. package/src/interfaces/terminal/components/Header.js +28 -28
  245. package/src/interfaces/terminal/components/Layout.js +598 -603
  246. package/src/interfaces/terminal/components/MessageList.js +280 -281
  247. package/src/interfaces/terminal/components/SettingsPanel.js +410 -415
  248. package/src/interfaces/terminal/components/StatusBar.js +2 -0
  249. package/src/interfaces/terminal/index.js +168 -168
  250. package/src/interfaces/terminal/state/useAgentControl.js +496 -496
  251. package/src/interfaces/terminal/state/useAgents.js +537 -537
  252. package/src/interfaces/terminal/state/useMessages.js +629 -630
  253. package/src/interfaces/terminal/state/useTools.js +554 -554
  254. package/src/interfaces/terminal/utils/debugLogger.js +44 -44
  255. package/src/interfaces/terminal/utils/settingsStorage.js +232 -232
  256. package/src/interfaces/webServer.js +7578 -7579
  257. package/src/interfaces/webServer.js.bak +7046 -7046
  258. package/src/modules/fileExplorer/__tests__/zipDownload.test.js +237 -237
  259. package/src/modules/fileExplorer/controller.js +470 -469
  260. package/src/modules/fileExplorer/routes.js +285 -286
  261. package/src/modules/widget/__tests__/isDisabled.test.js +41 -41
  262. package/src/modules/widget/__tests__/routes.test.js +677 -678
  263. package/src/modules/widget/__tests__/runtime.test.js +401 -401
  264. package/src/modules/widget/__tests__/versioning.test.js +309 -309
  265. package/src/modules/widget/__tests__/webComponentRuntime.test.js +565 -565
  266. package/src/modules/widget/__tests__/widgetTool.test.js +316 -316
  267. package/src/modules/widget/routes.js +435 -435
  268. package/src/modules/widget/runtime/bundle.js +640 -640
  269. package/src/modules/widget/runtime/webComponentBundle.js +470 -470
  270. package/src/modules/widget/schema.js +182 -181
  271. package/src/modules/widget/widgetTool.js +1389 -1389
  272. package/src/services/__tests__/agentActivityService.test.js +401 -402
  273. package/src/services/__tests__/benchmarkService.test.js +184 -184
  274. package/src/services/__tests__/contextInjectionService.test.js +246 -246
  275. package/src/services/__tests__/conversationQuery.test.js +721 -723
  276. package/src/services/__tests__/credentialVault.test.js +469 -469
  277. package/src/services/__tests__/discordService.integration.test.js +638 -639
  278. package/src/services/__tests__/flowContextService.test.js +590 -590
  279. package/src/services/__tests__/memoryService.test.js +1 -1
  280. package/src/services/__tests__/messageSource.test.js +380 -380
  281. package/src/services/__tests__/modelRouterNaming.test.js +111 -111
  282. package/src/services/__tests__/projectDetector.test.js +34 -34
  283. package/src/services/__tests__/promptService.test.js +242 -242
  284. package/src/services/__tests__/telegramService.test.js +941 -941
  285. package/src/services/__tests__/tokenCountingService.test.js +48 -48
  286. package/src/services/agentActivityService.js +419 -420
  287. package/src/services/aiService.js +2997 -3001
  288. package/src/services/apiKeyManager.js +359 -359
  289. package/src/services/benchmarkService.js +196 -196
  290. package/src/services/codebaseKnowledgeService.js +2 -2
  291. package/src/services/composioService.js +738 -738
  292. package/src/services/conversationCompactionService.js +1258 -1257
  293. package/src/services/credentialVault.js +685 -685
  294. package/src/services/discordService.js +792 -793
  295. package/src/services/embeddings/__tests__/azureCustomProvider.test.js +232 -232
  296. package/src/services/embeddings/__tests__/embeddingService.test.js +417 -417
  297. package/src/services/embeddings/__tests__/localProvider.test.js +263 -263
  298. package/src/services/embeddings/autoRecall.js +218 -219
  299. package/src/services/embeddings/indexers/__tests__/agentIndexer.test.js +232 -232
  300. package/src/services/embeddings/indexers/__tests__/memoryIndexer.test.js +418 -418
  301. package/src/services/embeddings/indexers/__tests__/reminisceIndexer.test.js +356 -357
  302. package/src/services/embeddings/indexers/__tests__/skillsIndexer.test.js +145 -145
  303. package/src/services/embeddings/indexers/__tests__/taskIndexer.test.js +146 -146
  304. package/src/services/embeddings/indexers/composioIndexer.js +279 -279
  305. package/src/services/embeddings/providerInterface.js +206 -206
  306. package/src/services/embeddings/providers/localProvider.js +11 -7
  307. package/src/services/embeddings/providers/openaiProvider.js +101 -101
  308. package/src/services/embeddings/vectorStore/inMemoryJsonStore.js +356 -356
  309. package/src/services/errorHandler.js +809 -809
  310. package/src/services/flowContextService.js +586 -586
  311. package/src/services/grounding/MockAdapter.js +125 -125
  312. package/src/services/modelRouterService.js +26 -31
  313. package/src/services/modelsService.js +322 -322
  314. package/src/services/ollamaService.js +452 -452
  315. package/src/services/projectDetector.js +403 -404
  316. package/src/services/promptService.js +418 -418
  317. package/src/services/qualityInspector.js +795 -795
  318. package/src/services/scheduleService.js +726 -726
  319. package/src/services/serviceRegistry.js +386 -386
  320. package/src/services/telegrafBot.js +174 -174
  321. package/src/services/telegramService.js +1972 -1972
  322. package/src/services/visualEditorBridge.js +1033 -1033
  323. package/src/services/visualEditorServer.js +1769 -1774
  324. package/src/services/whatsappService.js +667 -668
  325. package/src/tools/__tests__/agentCommunicationTool.findAgent.test.js +226 -226
  326. package/src/tools/__tests__/agentCommunicationTool.test.js +3 -3
  327. package/src/tools/__tests__/agentDelayTool.test.js +342 -342
  328. package/src/tools/__tests__/baseTool.test.js +3 -3
  329. package/src/tools/__tests__/codeMapTool.test.js +915 -915
  330. package/src/tools/__tests__/fileContentReplaceTool.test.js +309 -309
  331. package/src/tools/__tests__/fileTreeTool.test.js +274 -274
  332. package/src/tools/__tests__/filesystemTool.test.js +815 -815
  333. package/src/tools/__tests__/foundryWebSearchTool.test.js +252 -252
  334. package/src/tools/__tests__/imageTool.validator.test.js +194 -194
  335. package/src/tools/__tests__/jobDoneTool.test.js +580 -581
  336. package/src/tools/__tests__/memoryTool.forgetStale.test.js +272 -272
  337. package/src/tools/__tests__/memoryTool.reminisce.test.js +2 -2
  338. package/src/tools/__tests__/memoryTool.reminisceSemanticSearch.test.js +301 -301
  339. package/src/tools/__tests__/memoryTool.semanticSearch.test.js +405 -405
  340. package/src/tools/__tests__/memoryTool.teamPool.test.js +293 -293
  341. package/src/tools/__tests__/memoryTool.test.js +1 -1
  342. package/src/tools/__tests__/seekTool.test.js +282 -282
  343. package/src/tools/__tests__/skillsTool.search.test.js +164 -164
  344. package/src/tools/__tests__/skillsTool.test.js +226 -226
  345. package/src/tools/__tests__/staticAnalysisTool.test.js +509 -509
  346. package/src/tools/__tests__/taskManagerTool.discipline.test.js +137 -137
  347. package/src/tools/__tests__/taskManagerTool.search.test.js +143 -143
  348. package/src/tools/__tests__/taskManagerTool.test.js +866 -866
  349. package/src/tools/__tests__/terminalTool.test.js +448 -448
  350. package/src/tools/__tests__/toolShapeForgiveness.test.js +259 -260
  351. package/src/tools/__tests__/userPromptTool.test.js +297 -297
  352. package/src/tools/__tests__/videoTool.jobs.test.js +147 -147
  353. package/src/tools/__tests__/webTool.e2e.test.js +609 -603
  354. package/src/tools/__tests__/webTool.unit.test.js +195 -195
  355. package/src/tools/__tests__/webTool.visionModel.test.js +75 -75
  356. package/src/tools/agentCommunicationTool.js +8 -10
  357. package/src/tools/agentDelayTool.js +496 -497
  358. package/src/tools/asyncToolManager.js +602 -603
  359. package/src/tools/baseTool.js +12 -11
  360. package/src/tools/cloneDetectionTool.js +576 -581
  361. package/src/tools/codeMapTool.js +0 -6
  362. package/src/tools/composioTool.js +617 -617
  363. package/src/tools/dependencyResolverTool.js +1211 -1212
  364. package/src/tools/desktop/DesktopTool.js +629 -638
  365. package/src/tools/desktop/__tests__/DesktopTool.e2e.test.js +306 -306
  366. package/src/tools/desktop/__tests__/DesktopTool.test.js +507 -507
  367. package/src/tools/desktop/__tests__/osController.test.js +364 -364
  368. package/src/tools/desktop/osController.js +491 -491
  369. package/src/tools/docxTool.js +623 -623
  370. package/src/tools/excelTool.js +636 -636
  371. package/src/tools/fileContentReplaceTool.js +5 -7
  372. package/src/tools/fileSystemTool.js +12 -19
  373. package/src/tools/fileTreeTool.js +840 -840
  374. package/src/tools/foundryWebSearchTool.js +273 -273
  375. package/src/tools/helpTool.js +198 -198
  376. package/src/tools/imageTool.js +1397 -1397
  377. package/src/tools/importAnalyzerTool.js +1056 -1056
  378. package/src/tools/jobDoneTool.js +495 -495
  379. package/src/tools/memoryTool.js +1 -1
  380. package/src/tools/office/pres/__tests__/presSystem.test.js +365 -365
  381. package/src/tools/office/pres/archetypes/agenda.js +61 -61
  382. package/src/tools/office/pres/archetypes/bentoGrid.js +218 -219
  383. package/src/tools/office/pres/archetypes/bigStat.js +140 -142
  384. package/src/tools/office/pres/archetypes/closing.js +70 -70
  385. package/src/tools/office/pres/archetypes/hero.js +70 -70
  386. package/src/tools/office/pres/archetypes/productHero.js +93 -94
  387. package/src/tools/office/pres/archetypes/table.js +73 -74
  388. package/src/tools/office/pres/backgrounds/orb.js +66 -66
  389. package/src/tools/office/pres/components.js +422 -423
  390. package/src/tools/officeTool.js +441 -441
  391. package/src/tools/pdfTool.js +625 -627
  392. package/src/tools/platformControlTool.js +1081 -1081
  393. package/src/tools/seekTool.js +917 -918
  394. package/src/tools/skillsTool.js +1 -1
  395. package/src/tools/staticAnalysisTool.js +2143 -2146
  396. package/src/tools/taskManagerTool.js +3324 -3324
  397. package/src/tools/terminalTool.js +2615 -2618
  398. package/src/tools/videoTool.js +1303 -1303
  399. package/src/tools/visionTool.js +508 -508
  400. package/src/tools/visualEditorTool.js +1289 -1290
  401. package/src/tools/webTool.js +3368 -3368
  402. package/src/tools/whatsappTool.js +464 -464
  403. package/src/types/__tests__/agent.test.js +499 -499
  404. package/src/types/__tests__/contextReference.test.js +606 -606
  405. package/src/types/__tests__/conversation.test.js +555 -555
  406. package/src/types/__tests__/toolCommand.test.js +584 -584
  407. package/src/types/contextReference.js +974 -971
  408. package/src/types/conversation.js +729 -729
  409. package/src/types/toolCommand.js +746 -746
  410. package/src/utilities/__tests__/attachmentValidator.test.js +80 -80
  411. package/src/utilities/__tests__/auditReport.test.js +328 -328
  412. package/src/utilities/__tests__/directoryAccessManager.test.js +388 -388
  413. package/src/utilities/__tests__/jsonRepair.test.js +103 -104
  414. package/src/utilities/__tests__/modeTransitionReasons.test.js +105 -105
  415. package/src/utilities/__tests__/platformUtils.test.js +80 -87
  416. package/src/utilities/__tests__/structuredFileValidator.test.js +261 -263
  417. package/src/utilities/__tests__/toolConstants.test.js +92 -94
  418. package/src/utilities/__tests__/useIsTouchDevice.detect.test.js +114 -114
  419. package/src/utilities/__tests__/webUiUtilSync.test.js +117 -117
  420. package/src/utilities/attachmentValidator.js +284 -288
  421. package/src/utilities/authCache.js.backup-1779570472481 +121 -121
  422. package/src/utilities/browserStealth.js +631 -630
  423. package/src/utilities/configManager.js +616 -617
  424. package/src/utilities/directoryAccessManager.js +564 -565
  425. package/src/utilities/fileProcessor.js +308 -307
  426. package/src/utilities/humanBehavior.js +454 -453
  427. package/src/utilities/logger.js +479 -479
  428. package/src/utilities/structuredFileValidator.js +696 -699
  429. package/src/utilities/tagParser.js +5 -10
  430. package/src/utilities/userDataDir.js +308 -308
  431. package/node_modules/@isaacs/brace-expansion/dist/commonjs/index.js.map +0 -1
  432. package/node_modules/@isaacs/brace-expansion/dist/esm/index.js.map +0 -1
  433. package/node_modules/minipass/LICENSE +0 -15
  434. /package/node_modules/{@isaacs → glob/node_modules}/balanced-match/LICENSE.md +0 -0
  435. /package/node_modules/{@isaacs → glob/node_modules}/balanced-match/dist/commonjs/index.d.ts +0 -0
  436. /package/node_modules/{@isaacs → glob/node_modules}/balanced-match/dist/commonjs/index.d.ts.map +0 -0
  437. /package/node_modules/{@isaacs → glob/node_modules}/balanced-match/dist/commonjs/index.js +0 -0
  438. /package/node_modules/{@isaacs → glob/node_modules}/balanced-match/dist/commonjs/index.js.map +0 -0
  439. /package/node_modules/{@isaacs → glob/node_modules}/balanced-match/dist/commonjs/package.json +0 -0
  440. /package/node_modules/{@isaacs → glob/node_modules}/balanced-match/dist/esm/index.d.ts +0 -0
  441. /package/node_modules/{@isaacs → glob/node_modules}/balanced-match/dist/esm/index.d.ts.map +0 -0
  442. /package/node_modules/{@isaacs → glob/node_modules}/balanced-match/dist/esm/index.js +0 -0
  443. /package/node_modules/{@isaacs → glob/node_modules}/balanced-match/dist/esm/index.js.map +0 -0
  444. /package/node_modules/{@isaacs → glob/node_modules}/balanced-match/dist/esm/package.json +0 -0
  445. /package/node_modules/{@isaacs → glob/node_modules}/brace-expansion/LICENSE +0 -0
  446. /package/node_modules/{@isaacs → glob/node_modules}/brace-expansion/dist/commonjs/index.d.ts +0 -0
  447. /package/node_modules/{@isaacs → glob/node_modules}/brace-expansion/dist/commonjs/index.d.ts.map +0 -0
  448. /package/node_modules/{@isaacs → glob/node_modules}/brace-expansion/dist/commonjs/package.json +0 -0
  449. /package/node_modules/{@isaacs → glob/node_modules}/brace-expansion/dist/esm/index.d.ts +0 -0
  450. /package/node_modules/{@isaacs → glob/node_modules}/brace-expansion/dist/esm/index.d.ts.map +0 -0
  451. /package/node_modules/{@isaacs → glob/node_modules}/brace-expansion/dist/esm/package.json +0 -0
@@ -1,866 +1,866 @@
1
- import { jest, describe, test, expect, beforeEach } from '@jest/globals';
2
- import { createMockLogger, createMockConfig } from '../../__test-utils__/mockFactories.js';
3
-
4
- // Mock uuid
5
- let uuidCounter = 0;
6
- jest.unstable_mockModule('uuid', () => ({
7
- v4: jest.fn(() => `mock-uuid-${++uuidCounter}`)
8
- }));
9
-
10
- const { default: TaskManagerTool } = await import('../taskManagerTool.js');
11
-
12
- /**
13
- * Helper: create a tool instance, a fake agent, and context.
14
- */
15
- function createTestSetup() {
16
- const logger = createMockLogger();
17
- const tool = new TaskManagerTool({ description: 'test task manager' });
18
- tool.logger = logger;
19
-
20
- const agent = {
21
- id: 'agent-1',
22
- name: 'Test Agent',
23
- lastActivity: null,
24
- taskList: {
25
- tasks: [],
26
- lastUpdated: new Date().toISOString()
27
- }
28
- };
29
-
30
- const agentPool = {
31
- getAgent: jest.fn().mockResolvedValue(agent),
32
- persistAgentState: jest.fn().mockResolvedValue(undefined)
33
- };
34
-
35
- const context = {
36
- agentId: 'agent-1',
37
- agentName: 'Test Agent',
38
- agentPool,
39
- projectDir: '/tmp/test'
40
- };
41
-
42
- return { tool, agent, agentPool, context, logger };
43
- }
44
-
45
- beforeEach(() => {
46
- uuidCounter = 0;
47
- });
48
-
49
- describe('TaskManagerTool', () => {
50
- // ── constructor ─────────────────────────────────────────────────
51
- describe('constructor', () => {
52
- test('initializes with supported actions, priorities, and statuses', () => {
53
- const tool = new TaskManagerTool();
54
- expect(tool.supportedActions).toContain('create');
55
- expect(tool.supportedActions).toContain('sync');
56
- expect(tool.supportedActions).toContain('analytics');
57
- expect(tool.taskPriorities).toEqual(['urgent', 'high', 'medium', 'low']);
58
- expect(tool.taskStatuses).toContain('pending');
59
- expect(tool.taskStatuses).toContain('completed');
60
- expect(tool.taskTemplates).toHaveProperty('bug-fix');
61
- });
62
- });
63
-
64
- // ── getDescription ──────────────────────────────────────────────
65
- describe('getDescription', () => {
66
- test('returns a non-empty description string', () => {
67
- const tool = new TaskManagerTool();
68
- const desc = tool.getDescription();
69
- expect(typeof desc).toBe('string');
70
- expect(desc.length).toBeGreaterThan(50);
71
- });
72
- });
73
-
74
- // ── parseParameters ─────────────────────────────────────────────
75
- describe('parseParameters', () => {
76
- test('returns rawContent for string input', () => {
77
- const tool = new TaskManagerTool();
78
- expect(tool.parseParameters('hello')).toEqual({ rawContent: 'hello' });
79
- });
80
-
81
- test('unwraps tag-parser format objects', () => {
82
- const tool = new TaskManagerTool();
83
- const result = tool.parseParameters({
84
- action: { value: 'create', attributes: {} },
85
- title: { value: 'My Task', attributes: {} }
86
- });
87
- expect(result.action).toBe('create');
88
- expect(result.title).toBe('My Task');
89
- });
90
-
91
- test('passes through plain objects', () => {
92
- const tool = new TaskManagerTool();
93
- expect(tool.parseParameters({ action: 'list' })).toEqual({ action: 'list' });
94
- });
95
-
96
- test('returns non-object values as-is', () => {
97
- const tool = new TaskManagerTool();
98
- expect(tool.parseParameters(null)).toBeNull();
99
- });
100
- });
101
-
102
- // ── create action ───────────────────────────────────────────────
103
- describe('execute - create', () => {
104
- test('creates a task with title, description, and priority', async () => {
105
- const { tool, agent, context } = createTestSetup();
106
- const result = await tool.execute(
107
- { action: 'create', title: 'Build API', description: 'REST endpoints', priority: 'high' },
108
- context
109
- );
110
- expect(result.success).toBe(true);
111
- expect(result.action).toBe('create');
112
- expect(result.result.task.title).toBe('Build API');
113
- expect(result.result.task.priority).toBe('high');
114
- expect(result.result.task.status).toBe('pending');
115
- expect(agent.taskList.tasks).toHaveLength(1);
116
- });
117
-
118
- test('uses medium as default priority', async () => {
119
- const { tool, context } = createTestSetup();
120
- const result = await tool.execute({ action: 'create', title: 'Default' }, context);
121
- expect(result.result.task.priority).toBe('medium');
122
- });
123
-
124
- test('errors when title is missing', async () => {
125
- const { tool, context } = createTestSetup();
126
- const result = await tool.execute({ action: 'create', priority: 'low' }, context);
127
- expect(result.success).toBe(false);
128
- expect(result.error).toContain('title is required');
129
- });
130
-
131
- test('errors for invalid priority', async () => {
132
- const { tool, context } = createTestSetup();
133
- const result = await tool.execute(
134
- { action: 'create', title: 'Test', priority: 'superurgent' }, context
135
- );
136
- expect(result.success).toBe(false);
137
- expect(result.error).toContain('Invalid priority');
138
- });
139
- });
140
-
141
- // ── actions array format ────────────────────────────────────────
142
- describe('execute - actions array format', () => {
143
- test('unwraps first element of actions array', async () => {
144
- const { tool, context } = createTestSetup();
145
- const result = await tool.execute(
146
- { actions: [{ type: 'create', title: 'From array', priority: 'medium' }] },
147
- context
148
- );
149
- expect(result.success).toBe(true);
150
- expect(result.result.task.title).toBe('From array');
151
- });
152
-
153
- test('deep-unwraps tag-parser wrapped values in actions array', async () => {
154
- const { tool, context } = createTestSetup();
155
- const result = await tool.execute({
156
- actions: [{
157
- type: { value: 'create', attributes: {} },
158
- title: { value: 'Wrapped', attributes: {} },
159
- priority: 'medium'
160
- }]
161
- }, context);
162
- expect(result.success).toBe(true);
163
- expect(result.result.task.title).toBe('Wrapped');
164
- });
165
- });
166
-
167
- // ── update action ───────────────────────────────────────────────
168
- describe('execute - update', () => {
169
- test('updates task status and priority', async () => {
170
- const { tool, agent, context } = createTestSetup();
171
- await tool.execute({ action: 'create', title: 'Task A', priority: 'low' }, context);
172
- const taskId = agent.taskList.tasks[0].id;
173
- const result = await tool.execute(
174
- { action: 'update', taskId, status: 'in_progress', priority: 'high' }, context
175
- );
176
- expect(result.success).toBe(true);
177
- expect(result.result.task.status).toBe('in_progress');
178
- expect(result.result.task.priority).toBe('high');
179
- });
180
-
181
- test('updates title and description', async () => {
182
- const { tool, agent, context } = createTestSetup();
183
- await tool.execute({ action: 'create', title: 'Old', priority: 'medium' }, context);
184
- const taskId = agent.taskList.tasks[0].id;
185
- const result = await tool.execute(
186
- { action: 'update', taskId, title: 'New Title', description: 'Desc' }, context
187
- );
188
- expect(result.result.task.title).toBe('New Title');
189
- expect(result.result.task.description).toBe('Desc');
190
- });
191
-
192
- test('errors when taskId is missing', async () => {
193
- const { tool, context } = createTestSetup();
194
- const result = await tool.execute({ action: 'update', status: 'completed' }, context);
195
- expect(result.success).toBe(false);
196
- expect(result.error).toContain('Task ID is required');
197
- });
198
-
199
- test('errors for non-existent task', async () => {
200
- const { tool, context } = createTestSetup();
201
- const result = await tool.execute({ action: 'update', taskId: 'no-such', status: 'completed' }, context);
202
- expect(result.success).toBe(false);
203
- expect(result.error).toContain('Task not found');
204
- });
205
-
206
- test('errors for invalid status', async () => {
207
- const { tool, agent, context } = createTestSetup();
208
- await tool.execute({ action: 'create', title: 'T', priority: 'medium' }, context);
209
- const taskId = agent.taskList.tasks[0].id;
210
- const result = await tool.execute({ action: 'update', taskId, status: 'badstatus' }, context);
211
- expect(result.success).toBe(false);
212
- expect(result.error).toContain('Invalid status');
213
- });
214
- });
215
-
216
- // ── list action ─────────────────────────────────────────────────
217
- describe('execute - list', () => {
218
- test('lists all tasks with summary counts', async () => {
219
- const { tool, context } = createTestSetup();
220
- await tool.execute({ action: 'create', title: 'T1', priority: 'high' }, context);
221
- await tool.execute({ action: 'create', title: 'T2', priority: 'low' }, context);
222
- const result = await tool.execute({ action: 'list' }, context);
223
- expect(result.success).toBe(true);
224
- expect(result.result.totalTasks).toBe(2);
225
- expect(result.result.summary.pending).toBe(2);
226
- });
227
-
228
- test('filters by status', async () => {
229
- const { tool, agent, context } = createTestSetup();
230
- await tool.execute({ action: 'create', title: 'T1', priority: 'high' }, context);
231
- await tool.execute({ action: 'create', title: 'T2', priority: 'low' }, context);
232
- await tool.execute({ action: 'complete', taskId: agent.taskList.tasks[0].id }, context);
233
- const result = await tool.execute({ action: 'list', status: 'pending' }, context);
234
- expect(result.result.totalTasks).toBe(1);
235
- });
236
-
237
- test('filters by priority', async () => {
238
- const { tool, context } = createTestSetup();
239
- await tool.execute({ action: 'create', title: 'T1', priority: 'high' }, context);
240
- await tool.execute({ action: 'create', title: 'T2', priority: 'low' }, context);
241
- const result = await tool.execute({ action: 'list', priority: 'high' }, context);
242
- expect(result.result.totalTasks).toBe(1);
243
- });
244
-
245
- test('sorts by priority then creation date', async () => {
246
- const { tool, context } = createTestSetup();
247
- await tool.execute({ action: 'create', title: 'Low', priority: 'low' }, context);
248
- await tool.execute({ action: 'create', title: 'High', priority: 'high' }, context);
249
- const result = await tool.execute({ action: 'list' }, context);
250
- expect(result.result.tasks[0].title).toBe('High');
251
- });
252
- });
253
-
254
- // ── complete action ─────────────────────────────────────────────
255
- describe('execute - complete', () => {
256
- test('marks task as completed with timestamp', async () => {
257
- const { tool, agent, context } = createTestSetup();
258
- await tool.execute({ action: 'create', title: 'Finish', priority: 'medium' }, context);
259
- const taskId = agent.taskList.tasks[0].id;
260
- const result = await tool.execute({ action: 'complete', taskId }, context);
261
- expect(result.success).toBe(true);
262
- expect(result.result.task.status).toBe('completed');
263
- expect(result.result.task.completedAt).toBeDefined();
264
- });
265
-
266
- test('auto-completes first in-progress task when no taskId', async () => {
267
- const { tool, agent, context } = createTestSetup();
268
- await tool.execute({ action: 'create', title: 'Auto', priority: 'medium' }, context);
269
- agent.taskList.tasks[0].status = 'in_progress';
270
- const result = await tool.execute({ action: 'complete' }, context);
271
- expect(result.result.task.status).toBe('completed');
272
- });
273
-
274
- test('returns message for already-completed task', async () => {
275
- const { tool, agent, context } = createTestSetup();
276
- await tool.execute({ action: 'create', title: 'Done', priority: 'medium' }, context);
277
- const taskId = agent.taskList.tasks[0].id;
278
- await tool.execute({ action: 'complete', taskId }, context);
279
- const result = await tool.execute({ action: 'complete', taskId }, context);
280
- expect(result.result.message).toContain('already completed');
281
- });
282
-
283
- test('sets TTL to 1 when no pending tasks remain', async () => {
284
- const { tool, agent, context } = createTestSetup();
285
- await tool.execute({ action: 'create', title: 'Only', priority: 'medium' }, context);
286
- await tool.execute({ action: 'complete', taskId: agent.taskList.tasks[0].id }, context);
287
- expect(agent.ttl).toBe(1);
288
- });
289
-
290
- test('includes no-tasks hint in summary', async () => {
291
- const { tool, agent, context } = createTestSetup();
292
- await tool.execute({ action: 'create', title: 'Only', priority: 'medium' }, context);
293
- const result = await tool.execute({ action: 'complete', taskId: agent.taskList.tasks[0].id }, context);
294
- expect(result.summary).toContain('No remaining tasks');
295
- });
296
-
297
- test('errors when no taskId and no in-progress tasks', async () => {
298
- const { tool, context } = createTestSetup();
299
- const result = await tool.execute({ action: 'complete' }, context);
300
- expect(result.success).toBe(false);
301
- expect(result.error).toContain('No task ID provided');
302
- });
303
- });
304
-
305
- // ── cancel action ───────────────────────────────────────────────
306
- describe('execute - cancel', () => {
307
- test('cancels a task with reason', async () => {
308
- const { tool, agent, context } = createTestSetup();
309
- await tool.execute({ action: 'create', title: 'Cancel me', priority: 'medium' }, context);
310
- const taskId = agent.taskList.tasks[0].id;
311
- const result = await tool.execute({ action: 'cancel', taskId, reason: 'Not needed' }, context);
312
- expect(result.success).toBe(true);
313
- expect(result.result.task.status).toBe('cancelled');
314
- expect(result.result.task.cancellationReason).toBe('Not needed');
315
- });
316
-
317
- test('errors when taskId is missing', async () => {
318
- const { tool, context } = createTestSetup();
319
- const result = await tool.execute({ action: 'cancel' }, context);
320
- expect(result.success).toBe(false);
321
- expect(result.error).toContain('Task ID is required');
322
- });
323
- });
324
-
325
- // ── clear action ────────────────────────────────────────────────
326
- describe('execute - clear', () => {
327
- test('removes completed and cancelled tasks', async () => {
328
- const { tool, agent, context } = createTestSetup();
329
- await tool.execute({ action: 'create', title: 'T1', priority: 'medium' }, context);
330
- await tool.execute({ action: 'create', title: 'T2', priority: 'medium' }, context);
331
- await tool.execute({ action: 'create', title: 'T3', priority: 'medium' }, context);
332
- await tool.execute({ action: 'complete', taskId: agent.taskList.tasks[0].id }, context);
333
- await tool.execute({ action: 'cancel', taskId: agent.taskList.tasks[1].id }, context);
334
- const result = await tool.execute({ action: 'clear' }, context);
335
- expect(result.result.removed).toBe(2);
336
- expect(agent.taskList.tasks).toHaveLength(1);
337
- });
338
- });
339
-
340
- // ── depend action ───────────────────────────────────────────────
341
- describe('execute - depend', () => {
342
- test('creates blocking dependency and sets blocked status', async () => {
343
- const { tool, agent, context } = createTestSetup();
344
- await tool.execute({ action: 'create', title: 'T1', priority: 'high' }, context);
345
- await tool.execute({ action: 'create', title: 'T2', priority: 'medium' }, context);
346
- const [tA, tB] = agent.taskList.tasks;
347
- const result = await tool.execute(
348
- { action: 'depend', taskId: tB.id, dependsOn: tA.id, dependencyType: 'blocks' }, context
349
- );
350
- expect(result.success).toBe(true);
351
- expect(result.result.dependency.taskId).toBe(tA.id);
352
- expect(agent.taskList.tasks[1].status).toBe('blocked');
353
- });
354
-
355
- test('returns already-exists for duplicate dependency', async () => {
356
- const { tool, agent, context } = createTestSetup();
357
- await tool.execute({ action: 'create', title: 'T1', priority: 'high' }, context);
358
- await tool.execute({ action: 'create', title: 'T2', priority: 'medium' }, context);
359
- const [tA, tB] = agent.taskList.tasks;
360
- await tool.execute({ action: 'depend', taskId: tB.id, dependsOn: tA.id }, context);
361
- const result = await tool.execute({ action: 'depend', taskId: tB.id, dependsOn: tA.id }, context);
362
- expect(result.result.message).toContain('already exists');
363
- });
364
-
365
- test('errors when both params are missing', async () => {
366
- const { tool, context } = createTestSetup();
367
- const result = await tool.execute({ action: 'depend', taskId: 'x' }, context);
368
- expect(result.success).toBe(false);
369
- });
370
-
371
- test('errors for invalid dependency type', async () => {
372
- const { tool, agent, context } = createTestSetup();
373
- await tool.execute({ action: 'create', title: 'A', priority: 'high' }, context);
374
- await tool.execute({ action: 'create', title: 'B', priority: 'high' }, context);
375
- const [a, b] = agent.taskList.tasks;
376
- const result = await tool.execute(
377
- { action: 'depend', taskId: b.id, dependsOn: a.id, dependencyType: 'invalid' }, context
378
- );
379
- expect(result.success).toBe(false);
380
- expect(result.error).toContain('Invalid dependency type');
381
- });
382
- });
383
-
384
- // ── relate action ───────────────────────────────────────────────
385
- describe('execute - relate', () => {
386
- test('creates a relates-type dependency', async () => {
387
- const { tool, agent, context } = createTestSetup();
388
- await tool.execute({ action: 'create', title: 'T1', priority: 'high' }, context);
389
- await tool.execute({ action: 'create', title: 'T2', priority: 'medium' }, context);
390
- const [tA, tB] = agent.taskList.tasks;
391
- const result = await tool.execute(
392
- { action: 'relate', taskId: tB.id, dependsOn: tA.id }, context
393
- );
394
- expect(result.result.dependency.type).toBe('relates');
395
- });
396
- });
397
-
398
- // ── subtask action ──────────────────────────────────────────────
399
- describe('execute - subtask', () => {
400
- test('creates subtask under parent', async () => {
401
- const { tool, agent, context } = createTestSetup();
402
- await tool.execute({ action: 'create', title: 'Parent', priority: 'high' }, context);
403
- const parentId = agent.taskList.tasks[0].id;
404
- const result = await tool.execute(
405
- { action: 'subtask', parentTaskId: parentId, title: 'Sub 1', priority: 'medium' }, context
406
- );
407
- expect(result.success).toBe(true);
408
- expect(result.result.subtask.isSubtask).toBe(true);
409
- expect(result.result.subtask.parentTaskId).toBe(parentId);
410
- expect(agent.taskList.tasks[0].subtasks).toContain(result.result.subtask.id);
411
- });
412
-
413
- test('errors when parent not found', async () => {
414
- const { tool, context } = createTestSetup();
415
- const result = await tool.execute(
416
- { action: 'subtask', parentTaskId: 'no-such', title: 'Sub' }, context
417
- );
418
- expect(result.success).toBe(false);
419
- expect(result.error).toContain('Parent task not found');
420
- });
421
-
422
- test('errors when parentTaskId or title missing', async () => {
423
- const { tool, context } = createTestSetup();
424
- const result = await tool.execute({ action: 'subtask', title: 'Sub' }, context);
425
- expect(result.success).toBe(false);
426
- expect(result.error).toContain('required');
427
- });
428
- });
429
-
430
- // ── sync action ─────────────────────────────────────────────────
431
- describe('execute - sync', () => {
432
- test('syncs task list creating new tasks', async () => {
433
- const { tool, context } = createTestSetup();
434
- const result = await tool.execute({
435
- action: 'sync',
436
- tasks: [
437
- { title: 'A', status: 'completed', priority: 'high' },
438
- { title: 'B', status: 'in_progress', priority: 'medium' },
439
- { title: 'C', status: 'pending', priority: 'low' }
440
- ]
441
- }, context);
442
- expect(result.success).toBe(true);
443
- expect(result.result.summary.total).toBe(3);
444
- expect(result.result.summary.created).toBe(3);
445
- });
446
-
447
- test('updates existing tasks by matching title', async () => {
448
- const { tool, context } = createTestSetup();
449
- await tool.execute({ action: 'create', title: 'Existing', priority: 'low' }, context);
450
- const result = await tool.execute({
451
- action: 'sync',
452
- tasks: [{ title: 'Existing', status: 'completed', priority: 'high' }]
453
- }, context);
454
- expect(result.result.summary.updated).toBe(1);
455
- expect(result.result.summary.created).toBe(0);
456
- });
457
-
458
- test('parses JSON string tasks', async () => {
459
- const { tool, context } = createTestSetup();
460
- const result = await tool.execute({
461
- action: 'sync',
462
- tasks: JSON.stringify([{ title: 'JSON', status: 'pending', priority: 'medium' }])
463
- }, context);
464
- expect(result.success).toBe(true);
465
- });
466
-
467
- test('errors for empty tasks array', async () => {
468
- const { tool, context } = createTestSetup();
469
- const result = await tool.execute({ action: 'sync', tasks: [] }, context);
470
- expect(result.success).toBe(false);
471
- expect(result.error).toContain('empty');
472
- });
473
-
474
- test('errors for invalid status', async () => {
475
- const { tool, context } = createTestSetup();
476
- const result = await tool.execute({
477
- action: 'sync',
478
- tasks: [{ title: 'Bad', status: 'oops', priority: 'medium' }]
479
- }, context);
480
- expect(result.success).toBe(false);
481
- expect(result.error).toContain('Invalid status');
482
- });
483
-
484
- // ── Destructive-sync guardrail ───────────────────────────────────
485
- // Real production failure: post-compaction, an agent forgot it had
486
- // a 9-task plan and called sync with 4 unrelated tasks → all 9
487
- // were silently dropped. These tests pin the guardrail.
488
-
489
- describe('REGRESSION: destructive-sync guardrail', () => {
490
- // Helper: seed the agent with two pending + one in_progress task,
491
- // then attempt to sync with a totally different list.
492
- async function setupAgentWithPlan() {
493
- const { tool, context } = createTestSetup();
494
- await tool.execute({
495
- action: 'sync',
496
- tasks: [
497
- { title: 'Char select bg', status: 'in_progress', priority: 'high' },
498
- { title: 'Board art', status: 'pending', priority: 'high' },
499
- { title: 'Dice animation', status: 'pending', priority: 'medium' },
500
- ],
501
- }, context);
502
- return { tool, context };
503
- }
504
-
505
- test('REFUSES sync that would drop pending/in_progress tasks without confirmReplace', async () => {
506
- const { tool, context } = await setupAgentWithPlan();
507
- const result = await tool.execute({
508
- action: 'sync',
509
- tasks: [
510
- { title: 'Add Settings UI', status: 'pending', priority: 'high' },
511
- { title: 'Add Settings logic', status: 'pending', priority: 'high' },
512
- ],
513
- }, context);
514
- expect(result.success).toBe(false);
515
- expect(result.error).toMatch(/Sync would drop 3 non-terminal task/);
516
- // Must name the at-risk tasks so the agent can see them.
517
- expect(result.error).toContain('Char select bg');
518
- expect(result.error).toContain('Board art');
519
- expect(result.error).toContain('Dice animation');
520
- // Must explain the escape hatch.
521
- expect(result.error).toContain('confirmReplace: true');
522
- });
523
-
524
- test('PROCEEDS when confirmReplace=true is explicitly set', async () => {
525
- const { tool, context } = await setupAgentWithPlan();
526
- const result = await tool.execute({
527
- action: 'sync',
528
- confirmReplace: true,
529
- tasks: [
530
- { title: 'Add Settings UI', status: 'pending', priority: 'high' },
531
- ],
532
- }, context);
533
- expect(result.success).toBe(true);
534
- expect(result.result.summary.total).toBe(1);
535
- expect(result.result.summary.removed).toBe(3);
536
- });
537
-
538
- test('does NOT trigger when the incoming list keeps every open task (rename only)', async () => {
539
- const { tool, context } = await setupAgentWithPlan();
540
- // Update statuses, but keep all titles. No drops.
541
- const result = await tool.execute({
542
- action: 'sync',
543
- tasks: [
544
- { title: 'Char select bg', status: 'completed', priority: 'high' },
545
- { title: 'Board art', status: 'in_progress', priority: 'high' },
546
- { title: 'Dice animation', status: 'pending', priority: 'medium' },
547
- ],
548
- }, context);
549
- expect(result.success).toBe(true);
550
- expect(result.result.summary.updated).toBe(3);
551
- expect(result.result.summary.removed).toBe(0);
552
- });
553
-
554
- test('does NOT trigger when only completed/cancelled tasks would be dropped', async () => {
555
- const { tool, context } = createTestSetup();
556
- // Seed with one done task + one pending.
557
- await tool.execute({
558
- action: 'sync',
559
- tasks: [
560
- { title: 'Already done', status: 'completed', priority: 'low' },
561
- { title: 'Still going', status: 'pending', priority: 'high' },
562
- ],
563
- }, context);
564
- // New sync drops the completed one but keeps the pending one.
565
- const result = await tool.execute({
566
- action: 'sync',
567
- tasks: [{ title: 'Still going', status: 'in_progress', priority: 'high' }],
568
- }, context);
569
- // No guardrail trip — completed task is safe to drop.
570
- expect(result.success).toBe(true);
571
- });
572
-
573
- test('error response includes droppedTasks metadata for programmatic recovery', async () => {
574
- const { tool, context } = await setupAgentWithPlan();
575
- const result = await tool.execute({
576
- action: 'sync',
577
- tasks: [{ title: 'New thing', status: 'pending', priority: 'high' }],
578
- }, context);
579
- expect(result.success).toBe(false);
580
- // BaseTool's execute() catches the thrown Error; the message
581
- // carries the human-readable hint. We assert on the hint
582
- // contents — that's what the agent sees.
583
- expect(result.error).toMatch(/3 non-terminal task/);
584
- });
585
-
586
- test('REGRESSION: the exact Talisman failure scenario is now blocked', async () => {
587
- // Reproduce the production failure: agent has a 9-task plan
588
- // reflecting the user's UI revision request. Post-compaction,
589
- // agent loses context and tries to sync a 4-task Settings plan.
590
- const { tool, context } = createTestSetup();
591
- await tool.execute({
592
- action: 'sync',
593
- tasks: [
594
- { title: 'Explore current code and image assets', status: 'in_progress', priority: 'high' },
595
- { title: 'Generate character select background', status: 'pending', priority: 'high' },
596
- { title: 'Generate board space art for all nodes', status: 'pending', priority: 'high' },
597
- { title: 'Generate adventure card art', status: 'pending', priority: 'medium' },
598
- { title: 'Fix board to rectangular layout', status: 'pending', priority: 'high' },
599
- { title: 'Fix character select sticky buttons', status: 'pending', priority: 'medium' },
600
- { title: 'Fix dice animation and combat', status: 'pending', priority: 'high' },
601
- { title: 'Remove all emojis from UI', status: 'pending', priority: 'medium' },
602
- { title: 'Test and verify all changes', status: 'pending', priority: 'medium' },
603
- ],
604
- }, context);
605
- // The agent now (mistakenly) tries to sync a Settings plan.
606
- const result = await tool.execute({
607
- action: 'sync',
608
- tasks: [
609
- { title: 'Add Settings UI to index.html', status: 'pending', priority: 'high' },
610
- { title: 'Add Settings logic to game.js', status: 'pending', priority: 'high' },
611
- { title: 'Wire Settings to title screen', status: 'pending', priority: 'medium' },
612
- { title: 'Test Settings persistence', status: 'pending', priority: 'medium' },
613
- ],
614
- }, context);
615
- expect(result.success).toBe(false);
616
- // ALL 9 original tasks must be named so the agent sees them.
617
- expect(result.error).toContain('Explore current code');
618
- expect(result.error).toContain('Generate character select background');
619
- expect(result.error).toContain('Fix dice animation and combat');
620
- expect(result.error).toContain('Remove all emojis from UI');
621
- expect(result.error).toMatch(/Sync would drop 9 non-terminal task/);
622
- });
623
- });
624
-
625
- test('enforces only one in_progress task', async () => {
626
- const { tool, agent, context } = createTestSetup();
627
- await tool.execute({
628
- action: 'sync',
629
- tasks: [
630
- { title: 'A', status: 'in_progress', priority: 'high' },
631
- { title: 'B', status: 'in_progress', priority: 'medium' }
632
- ]
633
- }, context);
634
- const ipCount = agent.taskList.tasks.filter(t => t.status === 'in_progress').length;
635
- expect(ipCount).toBe(1);
636
- });
637
-
638
- test('auto-sets first pending to in_progress when none active', async () => {
639
- const { tool, agent, context } = createTestSetup();
640
- await tool.execute({
641
- action: 'sync',
642
- tasks: [
643
- { title: 'A', status: 'pending', priority: 'high' },
644
- { title: 'B', status: 'pending', priority: 'medium' }
645
- ]
646
- }, context);
647
- const ipTasks = agent.taskList.tasks.filter(t => t.status === 'in_progress');
648
- expect(ipTasks).toHaveLength(1);
649
- });
650
- });
651
-
652
- // ── template action ─────────────────────────────────────────────
653
- describe('execute - template', () => {
654
- test('lists available templates', async () => {
655
- const { tool, context } = createTestSetup();
656
- const result = await tool.execute({ action: 'template', mode: 'list' }, context);
657
- expect(result.success).toBe(true);
658
- expect(result.result.builtInTemplates.length).toBeGreaterThan(0);
659
- });
660
-
661
- test('applies a built-in template', async () => {
662
- const { tool, agent, context } = createTestSetup();
663
- const result = await tool.execute(
664
- { action: 'template', mode: 'apply', templateId: 'bug-fix' }, context
665
- );
666
- expect(result.success).toBe(true);
667
- expect(result.result.tasksCreated).toBeGreaterThan(0);
668
- expect(agent.taskList.tasks.length).toBeGreaterThan(0);
669
- });
670
-
671
- test('errors for non-existent template', async () => {
672
- const { tool, context } = createTestSetup();
673
- const result = await tool.execute(
674
- { action: 'template', mode: 'apply', templateId: 'nope' }, context
675
- );
676
- expect(result.success).toBe(false);
677
- expect(result.error).toContain('Template not found');
678
- });
679
-
680
- test('creates a custom template', async () => {
681
- const { tool, agent, context } = createTestSetup();
682
- const result = await tool.execute({
683
- action: 'template',
684
- mode: 'create',
685
- customTemplate: {
686
- name: 'My Workflow',
687
- description: 'Custom',
688
- tasks: [{ title: 'Step 1' }, { title: 'Step 2' }]
689
- }
690
- }, context);
691
- expect(result.success).toBe(true);
692
- expect(agent.customTemplates).toHaveLength(1);
693
- });
694
-
695
- test('errors when custom template has no tasks', async () => {
696
- const { tool, context } = createTestSetup();
697
- const result = await tool.execute({
698
- action: 'template',
699
- mode: 'create',
700
- customTemplate: { name: 'Empty', tasks: [] }
701
- }, context);
702
- expect(result.success).toBe(false);
703
- expect(result.error).toContain('requires name and at least one task');
704
- });
705
-
706
- test('suggests templates based on patterns', async () => {
707
- const { tool, agent, context } = createTestSetup();
708
- agent.taskList.tasks.push({
709
- id: 'bug-task', title: 'Fix login bug', status: 'pending',
710
- priority: 'high', createdAt: new Date().toISOString()
711
- });
712
- const result = await tool.execute({ action: 'template', mode: 'suggest' }, context);
713
- expect(result.success).toBe(true);
714
- expect(result.result.suggestions.length).toBeGreaterThan(0);
715
- });
716
-
717
- test('errors for invalid template mode', async () => {
718
- const { tool, context } = createTestSetup();
719
- const result = await tool.execute({ action: 'template', mode: 'invalid' }, context);
720
- expect(result.success).toBe(false);
721
- expect(result.error).toContain('Invalid template mode');
722
- });
723
- });
724
-
725
- // ── progress action ─────────────────────────────────────────────
726
- describe('execute - progress', () => {
727
- test('updates task progress with stage and note', async () => {
728
- const { tool, agent, context } = createTestSetup();
729
- await tool.execute({ action: 'create', title: 'Dev', priority: 'high' }, context);
730
- const taskId = agent.taskList.tasks[0].id;
731
- const result = await tool.execute({
732
- action: 'progress', mode: 'update', taskId,
733
- stage: 'in_development', note: 'Started coding'
734
- }, context);
735
- expect(result.success).toBe(true);
736
- expect(result.result.task.progress.stage).toBe('in_development');
737
- expect(result.result.task.progress.notes).toHaveLength(1);
738
- });
739
-
740
- test('setting percentage to 100 completes task', async () => {
741
- const { tool, agent, context } = createTestSetup();
742
- await tool.execute({ action: 'create', title: 'Pct', priority: 'high' }, context);
743
- const taskId = agent.taskList.tasks[0].id;
744
- await tool.execute({ action: 'progress', mode: 'update', taskId, percentage: 100 }, context);
745
- expect(agent.taskList.tasks[0].status).toBe('completed');
746
- expect(agent.taskList.tasks[0].progress.stage).toBe('completed');
747
- });
748
-
749
- test('gets progress overview', async () => {
750
- const { tool, context } = createTestSetup();
751
- await tool.execute({ action: 'create', title: 'T1', priority: 'high' }, context);
752
- const result = await tool.execute({ action: 'progress', mode: 'overview' }, context);
753
- expect(result.success).toBe(true);
754
- expect(result.result.overview).toBeDefined();
755
- });
756
-
757
- test('calculates progress from subtasks', async () => {
758
- const { tool, agent, context } = createTestSetup();
759
- await tool.execute({ action: 'create', title: 'Parent', priority: 'high' }, context);
760
- const parentId = agent.taskList.tasks[0].id;
761
- await tool.execute({ action: 'subtask', parentTaskId: parentId, title: 'Sub1' }, context);
762
- const result = await tool.execute({ action: 'progress', mode: 'calculate', taskId: parentId }, context);
763
- expect(result.result.calculationMethod).toBe('subtasks');
764
- });
765
-
766
- test('errors for invalid stage', async () => {
767
- const { tool, agent, context } = createTestSetup();
768
- await tool.execute({ action: 'create', title: 'T', priority: 'high' }, context);
769
- const taskId = agent.taskList.tasks[0].id;
770
- const result = await tool.execute({ action: 'progress', mode: 'update', taskId, stage: 'nope' }, context);
771
- expect(result.success).toBe(false);
772
- expect(result.error).toContain('Invalid progress stage');
773
- });
774
-
775
- test('errors for invalid progress mode', async () => {
776
- const { tool, context } = createTestSetup();
777
- const result = await tool.execute({ action: 'progress', mode: 'invalid' }, context);
778
- expect(result.success).toBe(false);
779
- });
780
- });
781
-
782
- // ── prioritize action ───────────────────────────────────────────
783
- describe('execute - prioritize', () => {
784
- test('auto-prioritizes tasks', async () => {
785
- const { tool, context } = createTestSetup();
786
- await tool.execute({ action: 'create', title: 'T1', priority: 'low' }, context);
787
- await tool.execute({ action: 'create', title: 'T2', priority: 'medium' }, context);
788
- const result = await tool.execute({ action: 'prioritize', mode: 'auto' }, context);
789
- expect(result.success).toBe(true);
790
- });
791
-
792
- test('analyzes specific task priority', async () => {
793
- const { tool, agent, context } = createTestSetup();
794
- await tool.execute({ action: 'create', title: 'Analyze', priority: 'medium' }, context);
795
- const taskId = agent.taskList.tasks[0].id;
796
- const result = await tool.execute({ action: 'prioritize', mode: 'analyze', taskId }, context);
797
- expect(result.success).toBe(true);
798
- expect(result.result.task.priorityScore).toBeDefined();
799
- });
800
-
801
- test('balance mode without scheduler returns message', async () => {
802
- const { tool, context } = createTestSetup();
803
- const result = await tool.execute({ action: 'prioritize', mode: 'balance' }, context);
804
- expect(result.success).toBe(true);
805
- expect(result.result.message).toContain('scheduler');
806
- });
807
-
808
- test('errors for invalid mode', async () => {
809
- const { tool, context } = createTestSetup();
810
- const result = await tool.execute({ action: 'prioritize', mode: 'xyz' }, context);
811
- expect(result.success).toBe(false);
812
- });
813
- });
814
-
815
- // ── analytics action ────────────────────────────────────────────
816
- describe('execute - analytics', () => {
817
- test('generates summary analytics', async () => {
818
- const { tool, context } = createTestSetup();
819
- await tool.execute({ action: 'create', title: 'T1', priority: 'high' }, context);
820
- const result = await tool.execute({ action: 'analytics', mode: 'summary' }, context);
821
- expect(result.success).toBe(true);
822
- expect(result.result.generatedAt).toBeDefined();
823
- });
824
-
825
- test('errors for invalid analytics mode', async () => {
826
- const { tool, context } = createTestSetup();
827
- const result = await tool.execute({ action: 'analytics', mode: 'invalid' }, context);
828
- expect(result.success).toBe(false);
829
- expect(result.error).toContain('Invalid analytics mode');
830
- });
831
- });
832
-
833
- // ── error handling ──────────────────────────────────────────────
834
- describe('execute - error handling', () => {
835
- test('fails when agentId is missing', async () => {
836
- const { tool } = createTestSetup();
837
- const result = await tool.execute({ action: 'list' }, { agentPool: {} });
838
- expect(result.success).toBe(false);
839
- expect(result.error).toContain('Agent ID is required');
840
- });
841
-
842
- test('fails when agent is not found', async () => {
843
- const { tool } = createTestSetup();
844
- const pool = { getAgent: jest.fn().mockResolvedValue(null) };
845
- const result = await tool.execute({ action: 'list' }, { agentId: 'x', agentPool: pool });
846
- expect(result.success).toBe(false);
847
- expect(result.error).toContain('Agent not found');
848
- });
849
-
850
- test('fails for unsupported action', async () => {
851
- const { tool, context } = createTestSetup();
852
- const result = await tool.execute({ action: 'fly' }, context);
853
- expect(result.success).toBe(false);
854
- expect(result.error).toContain('Unsupported action');
855
- });
856
-
857
- test('initializes taskList on agent if missing', async () => {
858
- const { tool, context, agentPool } = createTestSetup();
859
- const bare = { id: 'agent-1', name: 'Bare' };
860
- agentPool.getAgent.mockResolvedValue(bare);
861
- const result = await tool.execute({ action: 'list' }, context);
862
- expect(result.success).toBe(true);
863
- expect(bare.taskList).toBeDefined();
864
- });
865
- });
866
- });
1
+ import { jest, describe, test, expect, beforeEach } from '@jest/globals';
2
+ import { createMockLogger } from '../../__test-utils__/mockFactories.js';
3
+
4
+ // Mock uuid
5
+ let uuidCounter = 0;
6
+ jest.unstable_mockModule('uuid', () => ({
7
+ v4: jest.fn(() => `mock-uuid-${++uuidCounter}`)
8
+ }));
9
+
10
+ const { default: TaskManagerTool } = await import('../taskManagerTool.js');
11
+
12
+ /**
13
+ * Helper: create a tool instance, a fake agent, and context.
14
+ */
15
+ function createTestSetup() {
16
+ const logger = createMockLogger();
17
+ const tool = new TaskManagerTool({ description: 'test task manager' });
18
+ tool.logger = logger;
19
+
20
+ const agent = {
21
+ id: 'agent-1',
22
+ name: 'Test Agent',
23
+ lastActivity: null,
24
+ taskList: {
25
+ tasks: [],
26
+ lastUpdated: new Date().toISOString()
27
+ }
28
+ };
29
+
30
+ const agentPool = {
31
+ getAgent: jest.fn().mockResolvedValue(agent),
32
+ persistAgentState: jest.fn().mockResolvedValue(undefined)
33
+ };
34
+
35
+ const context = {
36
+ agentId: 'agent-1',
37
+ agentName: 'Test Agent',
38
+ agentPool,
39
+ projectDir: '/tmp/test'
40
+ };
41
+
42
+ return { tool, agent, agentPool, context, logger };
43
+ }
44
+
45
+ beforeEach(() => {
46
+ uuidCounter = 0;
47
+ });
48
+
49
+ describe('TaskManagerTool', () => {
50
+ // ── constructor ─────────────────────────────────────────────────
51
+ describe('constructor', () => {
52
+ test('initializes with supported actions, priorities, and statuses', () => {
53
+ const tool = new TaskManagerTool();
54
+ expect(tool.supportedActions).toContain('create');
55
+ expect(tool.supportedActions).toContain('sync');
56
+ expect(tool.supportedActions).toContain('analytics');
57
+ expect(tool.taskPriorities).toEqual(['urgent', 'high', 'medium', 'low']);
58
+ expect(tool.taskStatuses).toContain('pending');
59
+ expect(tool.taskStatuses).toContain('completed');
60
+ expect(tool.taskTemplates).toHaveProperty('bug-fix');
61
+ });
62
+ });
63
+
64
+ // ── getDescription ──────────────────────────────────────────────
65
+ describe('getDescription', () => {
66
+ test('returns a non-empty description string', () => {
67
+ const tool = new TaskManagerTool();
68
+ const desc = tool.getDescription();
69
+ expect(typeof desc).toBe('string');
70
+ expect(desc.length).toBeGreaterThan(50);
71
+ });
72
+ });
73
+
74
+ // ── parseParameters ─────────────────────────────────────────────
75
+ describe('parseParameters', () => {
76
+ test('returns rawContent for string input', () => {
77
+ const tool = new TaskManagerTool();
78
+ expect(tool.parseParameters('hello')).toEqual({ rawContent: 'hello' });
79
+ });
80
+
81
+ test('unwraps tag-parser format objects', () => {
82
+ const tool = new TaskManagerTool();
83
+ const result = tool.parseParameters({
84
+ action: { value: 'create', attributes: {} },
85
+ title: { value: 'My Task', attributes: {} }
86
+ });
87
+ expect(result.action).toBe('create');
88
+ expect(result.title).toBe('My Task');
89
+ });
90
+
91
+ test('passes through plain objects', () => {
92
+ const tool = new TaskManagerTool();
93
+ expect(tool.parseParameters({ action: 'list' })).toEqual({ action: 'list' });
94
+ });
95
+
96
+ test('returns non-object values as-is', () => {
97
+ const tool = new TaskManagerTool();
98
+ expect(tool.parseParameters(null)).toBeNull();
99
+ });
100
+ });
101
+
102
+ // ── create action ───────────────────────────────────────────────
103
+ describe('execute - create', () => {
104
+ test('creates a task with title, description, and priority', async () => {
105
+ const { tool, agent, context } = createTestSetup();
106
+ const result = await tool.execute(
107
+ { action: 'create', title: 'Build API', description: 'REST endpoints', priority: 'high' },
108
+ context
109
+ );
110
+ expect(result.success).toBe(true);
111
+ expect(result.action).toBe('create');
112
+ expect(result.result.task.title).toBe('Build API');
113
+ expect(result.result.task.priority).toBe('high');
114
+ expect(result.result.task.status).toBe('pending');
115
+ expect(agent.taskList.tasks).toHaveLength(1);
116
+ });
117
+
118
+ test('uses medium as default priority', async () => {
119
+ const { tool, context } = createTestSetup();
120
+ const result = await tool.execute({ action: 'create', title: 'Default' }, context);
121
+ expect(result.result.task.priority).toBe('medium');
122
+ });
123
+
124
+ test('errors when title is missing', async () => {
125
+ const { tool, context } = createTestSetup();
126
+ const result = await tool.execute({ action: 'create', priority: 'low' }, context);
127
+ expect(result.success).toBe(false);
128
+ expect(result.error).toContain('title is required');
129
+ });
130
+
131
+ test('errors for invalid priority', async () => {
132
+ const { tool, context } = createTestSetup();
133
+ const result = await tool.execute(
134
+ { action: 'create', title: 'Test', priority: 'superurgent' }, context
135
+ );
136
+ expect(result.success).toBe(false);
137
+ expect(result.error).toContain('Invalid priority');
138
+ });
139
+ });
140
+
141
+ // ── actions array format ────────────────────────────────────────
142
+ describe('execute - actions array format', () => {
143
+ test('unwraps first element of actions array', async () => {
144
+ const { tool, context } = createTestSetup();
145
+ const result = await tool.execute(
146
+ { actions: [{ type: 'create', title: 'From array', priority: 'medium' }] },
147
+ context
148
+ );
149
+ expect(result.success).toBe(true);
150
+ expect(result.result.task.title).toBe('From array');
151
+ });
152
+
153
+ test('deep-unwraps tag-parser wrapped values in actions array', async () => {
154
+ const { tool, context } = createTestSetup();
155
+ const result = await tool.execute({
156
+ actions: [{
157
+ type: { value: 'create', attributes: {} },
158
+ title: { value: 'Wrapped', attributes: {} },
159
+ priority: 'medium'
160
+ }]
161
+ }, context);
162
+ expect(result.success).toBe(true);
163
+ expect(result.result.task.title).toBe('Wrapped');
164
+ });
165
+ });
166
+
167
+ // ── update action ───────────────────────────────────────────────
168
+ describe('execute - update', () => {
169
+ test('updates task status and priority', async () => {
170
+ const { tool, agent, context } = createTestSetup();
171
+ await tool.execute({ action: 'create', title: 'Task A', priority: 'low' }, context);
172
+ const taskId = agent.taskList.tasks[0].id;
173
+ const result = await tool.execute(
174
+ { action: 'update', taskId, status: 'in_progress', priority: 'high' }, context
175
+ );
176
+ expect(result.success).toBe(true);
177
+ expect(result.result.task.status).toBe('in_progress');
178
+ expect(result.result.task.priority).toBe('high');
179
+ });
180
+
181
+ test('updates title and description', async () => {
182
+ const { tool, agent, context } = createTestSetup();
183
+ await tool.execute({ action: 'create', title: 'Old', priority: 'medium' }, context);
184
+ const taskId = agent.taskList.tasks[0].id;
185
+ const result = await tool.execute(
186
+ { action: 'update', taskId, title: 'New Title', description: 'Desc' }, context
187
+ );
188
+ expect(result.result.task.title).toBe('New Title');
189
+ expect(result.result.task.description).toBe('Desc');
190
+ });
191
+
192
+ test('errors when taskId is missing', async () => {
193
+ const { tool, context } = createTestSetup();
194
+ const result = await tool.execute({ action: 'update', status: 'completed' }, context);
195
+ expect(result.success).toBe(false);
196
+ expect(result.error).toContain('Task ID is required');
197
+ });
198
+
199
+ test('errors for non-existent task', async () => {
200
+ const { tool, context } = createTestSetup();
201
+ const result = await tool.execute({ action: 'update', taskId: 'no-such', status: 'completed' }, context);
202
+ expect(result.success).toBe(false);
203
+ expect(result.error).toContain('Task not found');
204
+ });
205
+
206
+ test('errors for invalid status', async () => {
207
+ const { tool, agent, context } = createTestSetup();
208
+ await tool.execute({ action: 'create', title: 'T', priority: 'medium' }, context);
209
+ const taskId = agent.taskList.tasks[0].id;
210
+ const result = await tool.execute({ action: 'update', taskId, status: 'badstatus' }, context);
211
+ expect(result.success).toBe(false);
212
+ expect(result.error).toContain('Invalid status');
213
+ });
214
+ });
215
+
216
+ // ── list action ─────────────────────────────────────────────────
217
+ describe('execute - list', () => {
218
+ test('lists all tasks with summary counts', async () => {
219
+ const { tool, context } = createTestSetup();
220
+ await tool.execute({ action: 'create', title: 'T1', priority: 'high' }, context);
221
+ await tool.execute({ action: 'create', title: 'T2', priority: 'low' }, context);
222
+ const result = await tool.execute({ action: 'list' }, context);
223
+ expect(result.success).toBe(true);
224
+ expect(result.result.totalTasks).toBe(2);
225
+ expect(result.result.summary.pending).toBe(2);
226
+ });
227
+
228
+ test('filters by status', async () => {
229
+ const { tool, agent, context } = createTestSetup();
230
+ await tool.execute({ action: 'create', title: 'T1', priority: 'high' }, context);
231
+ await tool.execute({ action: 'create', title: 'T2', priority: 'low' }, context);
232
+ await tool.execute({ action: 'complete', taskId: agent.taskList.tasks[0].id }, context);
233
+ const result = await tool.execute({ action: 'list', status: 'pending' }, context);
234
+ expect(result.result.totalTasks).toBe(1);
235
+ });
236
+
237
+ test('filters by priority', async () => {
238
+ const { tool, context } = createTestSetup();
239
+ await tool.execute({ action: 'create', title: 'T1', priority: 'high' }, context);
240
+ await tool.execute({ action: 'create', title: 'T2', priority: 'low' }, context);
241
+ const result = await tool.execute({ action: 'list', priority: 'high' }, context);
242
+ expect(result.result.totalTasks).toBe(1);
243
+ });
244
+
245
+ test('sorts by priority then creation date', async () => {
246
+ const { tool, context } = createTestSetup();
247
+ await tool.execute({ action: 'create', title: 'Low', priority: 'low' }, context);
248
+ await tool.execute({ action: 'create', title: 'High', priority: 'high' }, context);
249
+ const result = await tool.execute({ action: 'list' }, context);
250
+ expect(result.result.tasks[0].title).toBe('High');
251
+ });
252
+ });
253
+
254
+ // ── complete action ─────────────────────────────────────────────
255
+ describe('execute - complete', () => {
256
+ test('marks task as completed with timestamp', async () => {
257
+ const { tool, agent, context } = createTestSetup();
258
+ await tool.execute({ action: 'create', title: 'Finish', priority: 'medium' }, context);
259
+ const taskId = agent.taskList.tasks[0].id;
260
+ const result = await tool.execute({ action: 'complete', taskId }, context);
261
+ expect(result.success).toBe(true);
262
+ expect(result.result.task.status).toBe('completed');
263
+ expect(result.result.task.completedAt).toBeDefined();
264
+ });
265
+
266
+ test('auto-completes first in-progress task when no taskId', async () => {
267
+ const { tool, agent, context } = createTestSetup();
268
+ await tool.execute({ action: 'create', title: 'Auto', priority: 'medium' }, context);
269
+ agent.taskList.tasks[0].status = 'in_progress';
270
+ const result = await tool.execute({ action: 'complete' }, context);
271
+ expect(result.result.task.status).toBe('completed');
272
+ });
273
+
274
+ test('returns message for already-completed task', async () => {
275
+ const { tool, agent, context } = createTestSetup();
276
+ await tool.execute({ action: 'create', title: 'Done', priority: 'medium' }, context);
277
+ const taskId = agent.taskList.tasks[0].id;
278
+ await tool.execute({ action: 'complete', taskId }, context);
279
+ const result = await tool.execute({ action: 'complete', taskId }, context);
280
+ expect(result.result.message).toContain('already completed');
281
+ });
282
+
283
+ test('sets TTL to 1 when no pending tasks remain', async () => {
284
+ const { tool, agent, context } = createTestSetup();
285
+ await tool.execute({ action: 'create', title: 'Only', priority: 'medium' }, context);
286
+ await tool.execute({ action: 'complete', taskId: agent.taskList.tasks[0].id }, context);
287
+ expect(agent.ttl).toBe(1);
288
+ });
289
+
290
+ test('includes no-tasks hint in summary', async () => {
291
+ const { tool, agent, context } = createTestSetup();
292
+ await tool.execute({ action: 'create', title: 'Only', priority: 'medium' }, context);
293
+ const result = await tool.execute({ action: 'complete', taskId: agent.taskList.tasks[0].id }, context);
294
+ expect(result.summary).toContain('No remaining tasks');
295
+ });
296
+
297
+ test('errors when no taskId and no in-progress tasks', async () => {
298
+ const { tool, context } = createTestSetup();
299
+ const result = await tool.execute({ action: 'complete' }, context);
300
+ expect(result.success).toBe(false);
301
+ expect(result.error).toContain('No task ID provided');
302
+ });
303
+ });
304
+
305
+ // ── cancel action ───────────────────────────────────────────────
306
+ describe('execute - cancel', () => {
307
+ test('cancels a task with reason', async () => {
308
+ const { tool, agent, context } = createTestSetup();
309
+ await tool.execute({ action: 'create', title: 'Cancel me', priority: 'medium' }, context);
310
+ const taskId = agent.taskList.tasks[0].id;
311
+ const result = await tool.execute({ action: 'cancel', taskId, reason: 'Not needed' }, context);
312
+ expect(result.success).toBe(true);
313
+ expect(result.result.task.status).toBe('cancelled');
314
+ expect(result.result.task.cancellationReason).toBe('Not needed');
315
+ });
316
+
317
+ test('errors when taskId is missing', async () => {
318
+ const { tool, context } = createTestSetup();
319
+ const result = await tool.execute({ action: 'cancel' }, context);
320
+ expect(result.success).toBe(false);
321
+ expect(result.error).toContain('Task ID is required');
322
+ });
323
+ });
324
+
325
+ // ── clear action ────────────────────────────────────────────────
326
+ describe('execute - clear', () => {
327
+ test('removes completed and cancelled tasks', async () => {
328
+ const { tool, agent, context } = createTestSetup();
329
+ await tool.execute({ action: 'create', title: 'T1', priority: 'medium' }, context);
330
+ await tool.execute({ action: 'create', title: 'T2', priority: 'medium' }, context);
331
+ await tool.execute({ action: 'create', title: 'T3', priority: 'medium' }, context);
332
+ await tool.execute({ action: 'complete', taskId: agent.taskList.tasks[0].id }, context);
333
+ await tool.execute({ action: 'cancel', taskId: agent.taskList.tasks[1].id }, context);
334
+ const result = await tool.execute({ action: 'clear' }, context);
335
+ expect(result.result.removed).toBe(2);
336
+ expect(agent.taskList.tasks).toHaveLength(1);
337
+ });
338
+ });
339
+
340
+ // ── depend action ───────────────────────────────────────────────
341
+ describe('execute - depend', () => {
342
+ test('creates blocking dependency and sets blocked status', async () => {
343
+ const { tool, agent, context } = createTestSetup();
344
+ await tool.execute({ action: 'create', title: 'T1', priority: 'high' }, context);
345
+ await tool.execute({ action: 'create', title: 'T2', priority: 'medium' }, context);
346
+ const [tA, tB] = agent.taskList.tasks;
347
+ const result = await tool.execute(
348
+ { action: 'depend', taskId: tB.id, dependsOn: tA.id, dependencyType: 'blocks' }, context
349
+ );
350
+ expect(result.success).toBe(true);
351
+ expect(result.result.dependency.taskId).toBe(tA.id);
352
+ expect(agent.taskList.tasks[1].status).toBe('blocked');
353
+ });
354
+
355
+ test('returns already-exists for duplicate dependency', async () => {
356
+ const { tool, agent, context } = createTestSetup();
357
+ await tool.execute({ action: 'create', title: 'T1', priority: 'high' }, context);
358
+ await tool.execute({ action: 'create', title: 'T2', priority: 'medium' }, context);
359
+ const [tA, tB] = agent.taskList.tasks;
360
+ await tool.execute({ action: 'depend', taskId: tB.id, dependsOn: tA.id }, context);
361
+ const result = await tool.execute({ action: 'depend', taskId: tB.id, dependsOn: tA.id }, context);
362
+ expect(result.result.message).toContain('already exists');
363
+ });
364
+
365
+ test('errors when both params are missing', async () => {
366
+ const { tool, context } = createTestSetup();
367
+ const result = await tool.execute({ action: 'depend', taskId: 'x' }, context);
368
+ expect(result.success).toBe(false);
369
+ });
370
+
371
+ test('errors for invalid dependency type', async () => {
372
+ const { tool, agent, context } = createTestSetup();
373
+ await tool.execute({ action: 'create', title: 'A', priority: 'high' }, context);
374
+ await tool.execute({ action: 'create', title: 'B', priority: 'high' }, context);
375
+ const [a, b] = agent.taskList.tasks;
376
+ const result = await tool.execute(
377
+ { action: 'depend', taskId: b.id, dependsOn: a.id, dependencyType: 'invalid' }, context
378
+ );
379
+ expect(result.success).toBe(false);
380
+ expect(result.error).toContain('Invalid dependency type');
381
+ });
382
+ });
383
+
384
+ // ── relate action ───────────────────────────────────────────────
385
+ describe('execute - relate', () => {
386
+ test('creates a relates-type dependency', async () => {
387
+ const { tool, agent, context } = createTestSetup();
388
+ await tool.execute({ action: 'create', title: 'T1', priority: 'high' }, context);
389
+ await tool.execute({ action: 'create', title: 'T2', priority: 'medium' }, context);
390
+ const [tA, tB] = agent.taskList.tasks;
391
+ const result = await tool.execute(
392
+ { action: 'relate', taskId: tB.id, dependsOn: tA.id }, context
393
+ );
394
+ expect(result.result.dependency.type).toBe('relates');
395
+ });
396
+ });
397
+
398
+ // ── subtask action ──────────────────────────────────────────────
399
+ describe('execute - subtask', () => {
400
+ test('creates subtask under parent', async () => {
401
+ const { tool, agent, context } = createTestSetup();
402
+ await tool.execute({ action: 'create', title: 'Parent', priority: 'high' }, context);
403
+ const parentId = agent.taskList.tasks[0].id;
404
+ const result = await tool.execute(
405
+ { action: 'subtask', parentTaskId: parentId, title: 'Sub 1', priority: 'medium' }, context
406
+ );
407
+ expect(result.success).toBe(true);
408
+ expect(result.result.subtask.isSubtask).toBe(true);
409
+ expect(result.result.subtask.parentTaskId).toBe(parentId);
410
+ expect(agent.taskList.tasks[0].subtasks).toContain(result.result.subtask.id);
411
+ });
412
+
413
+ test('errors when parent not found', async () => {
414
+ const { tool, context } = createTestSetup();
415
+ const result = await tool.execute(
416
+ { action: 'subtask', parentTaskId: 'no-such', title: 'Sub' }, context
417
+ );
418
+ expect(result.success).toBe(false);
419
+ expect(result.error).toContain('Parent task not found');
420
+ });
421
+
422
+ test('errors when parentTaskId or title missing', async () => {
423
+ const { tool, context } = createTestSetup();
424
+ const result = await tool.execute({ action: 'subtask', title: 'Sub' }, context);
425
+ expect(result.success).toBe(false);
426
+ expect(result.error).toContain('required');
427
+ });
428
+ });
429
+
430
+ // ── sync action ─────────────────────────────────────────────────
431
+ describe('execute - sync', () => {
432
+ test('syncs task list creating new tasks', async () => {
433
+ const { tool, context } = createTestSetup();
434
+ const result = await tool.execute({
435
+ action: 'sync',
436
+ tasks: [
437
+ { title: 'A', status: 'completed', priority: 'high' },
438
+ { title: 'B', status: 'in_progress', priority: 'medium' },
439
+ { title: 'C', status: 'pending', priority: 'low' }
440
+ ]
441
+ }, context);
442
+ expect(result.success).toBe(true);
443
+ expect(result.result.summary.total).toBe(3);
444
+ expect(result.result.summary.created).toBe(3);
445
+ });
446
+
447
+ test('updates existing tasks by matching title', async () => {
448
+ const { tool, context } = createTestSetup();
449
+ await tool.execute({ action: 'create', title: 'Existing', priority: 'low' }, context);
450
+ const result = await tool.execute({
451
+ action: 'sync',
452
+ tasks: [{ title: 'Existing', status: 'completed', priority: 'high' }]
453
+ }, context);
454
+ expect(result.result.summary.updated).toBe(1);
455
+ expect(result.result.summary.created).toBe(0);
456
+ });
457
+
458
+ test('parses JSON string tasks', async () => {
459
+ const { tool, context } = createTestSetup();
460
+ const result = await tool.execute({
461
+ action: 'sync',
462
+ tasks: JSON.stringify([{ title: 'JSON', status: 'pending', priority: 'medium' }])
463
+ }, context);
464
+ expect(result.success).toBe(true);
465
+ });
466
+
467
+ test('errors for empty tasks array', async () => {
468
+ const { tool, context } = createTestSetup();
469
+ const result = await tool.execute({ action: 'sync', tasks: [] }, context);
470
+ expect(result.success).toBe(false);
471
+ expect(result.error).toContain('empty');
472
+ });
473
+
474
+ test('errors for invalid status', async () => {
475
+ const { tool, context } = createTestSetup();
476
+ const result = await tool.execute({
477
+ action: 'sync',
478
+ tasks: [{ title: 'Bad', status: 'oops', priority: 'medium' }]
479
+ }, context);
480
+ expect(result.success).toBe(false);
481
+ expect(result.error).toContain('Invalid status');
482
+ });
483
+
484
+ // ── Destructive-sync guardrail ───────────────────────────────────
485
+ // Real production failure: post-compaction, an agent forgot it had
486
+ // a 9-task plan and called sync with 4 unrelated tasks → all 9
487
+ // were silently dropped. These tests pin the guardrail.
488
+
489
+ describe('REGRESSION: destructive-sync guardrail', () => {
490
+ // Helper: seed the agent with two pending + one in_progress task,
491
+ // then attempt to sync with a totally different list.
492
+ async function setupAgentWithPlan() {
493
+ const { tool, context } = createTestSetup();
494
+ await tool.execute({
495
+ action: 'sync',
496
+ tasks: [
497
+ { title: 'Char select bg', status: 'in_progress', priority: 'high' },
498
+ { title: 'Board art', status: 'pending', priority: 'high' },
499
+ { title: 'Dice animation', status: 'pending', priority: 'medium' },
500
+ ],
501
+ }, context);
502
+ return { tool, context };
503
+ }
504
+
505
+ test('REFUSES sync that would drop pending/in_progress tasks without confirmReplace', async () => {
506
+ const { tool, context } = await setupAgentWithPlan();
507
+ const result = await tool.execute({
508
+ action: 'sync',
509
+ tasks: [
510
+ { title: 'Add Settings UI', status: 'pending', priority: 'high' },
511
+ { title: 'Add Settings logic', status: 'pending', priority: 'high' },
512
+ ],
513
+ }, context);
514
+ expect(result.success).toBe(false);
515
+ expect(result.error).toMatch(/Sync would drop 3 non-terminal task/);
516
+ // Must name the at-risk tasks so the agent can see them.
517
+ expect(result.error).toContain('Char select bg');
518
+ expect(result.error).toContain('Board art');
519
+ expect(result.error).toContain('Dice animation');
520
+ // Must explain the escape hatch.
521
+ expect(result.error).toContain('confirmReplace: true');
522
+ });
523
+
524
+ test('PROCEEDS when confirmReplace=true is explicitly set', async () => {
525
+ const { tool, context } = await setupAgentWithPlan();
526
+ const result = await tool.execute({
527
+ action: 'sync',
528
+ confirmReplace: true,
529
+ tasks: [
530
+ { title: 'Add Settings UI', status: 'pending', priority: 'high' },
531
+ ],
532
+ }, context);
533
+ expect(result.success).toBe(true);
534
+ expect(result.result.summary.total).toBe(1);
535
+ expect(result.result.summary.removed).toBe(3);
536
+ });
537
+
538
+ test('does NOT trigger when the incoming list keeps every open task (rename only)', async () => {
539
+ const { tool, context } = await setupAgentWithPlan();
540
+ // Update statuses, but keep all titles. No drops.
541
+ const result = await tool.execute({
542
+ action: 'sync',
543
+ tasks: [
544
+ { title: 'Char select bg', status: 'completed', priority: 'high' },
545
+ { title: 'Board art', status: 'in_progress', priority: 'high' },
546
+ { title: 'Dice animation', status: 'pending', priority: 'medium' },
547
+ ],
548
+ }, context);
549
+ expect(result.success).toBe(true);
550
+ expect(result.result.summary.updated).toBe(3);
551
+ expect(result.result.summary.removed).toBe(0);
552
+ });
553
+
554
+ test('does NOT trigger when only completed/cancelled tasks would be dropped', async () => {
555
+ const { tool, context } = createTestSetup();
556
+ // Seed with one done task + one pending.
557
+ await tool.execute({
558
+ action: 'sync',
559
+ tasks: [
560
+ { title: 'Already done', status: 'completed', priority: 'low' },
561
+ { title: 'Still going', status: 'pending', priority: 'high' },
562
+ ],
563
+ }, context);
564
+ // New sync drops the completed one but keeps the pending one.
565
+ const result = await tool.execute({
566
+ action: 'sync',
567
+ tasks: [{ title: 'Still going', status: 'in_progress', priority: 'high' }],
568
+ }, context);
569
+ // No guardrail trip — completed task is safe to drop.
570
+ expect(result.success).toBe(true);
571
+ });
572
+
573
+ test('error response includes droppedTasks metadata for programmatic recovery', async () => {
574
+ const { tool, context } = await setupAgentWithPlan();
575
+ const result = await tool.execute({
576
+ action: 'sync',
577
+ tasks: [{ title: 'New thing', status: 'pending', priority: 'high' }],
578
+ }, context);
579
+ expect(result.success).toBe(false);
580
+ // BaseTool's execute() catches the thrown Error; the message
581
+ // carries the human-readable hint. We assert on the hint
582
+ // contents — that's what the agent sees.
583
+ expect(result.error).toMatch(/3 non-terminal task/);
584
+ });
585
+
586
+ test('REGRESSION: the exact Talisman failure scenario is now blocked', async () => {
587
+ // Reproduce the production failure: agent has a 9-task plan
588
+ // reflecting the user's UI revision request. Post-compaction,
589
+ // agent loses context and tries to sync a 4-task Settings plan.
590
+ const { tool, context } = createTestSetup();
591
+ await tool.execute({
592
+ action: 'sync',
593
+ tasks: [
594
+ { title: 'Explore current code and image assets', status: 'in_progress', priority: 'high' },
595
+ { title: 'Generate character select background', status: 'pending', priority: 'high' },
596
+ { title: 'Generate board space art for all nodes', status: 'pending', priority: 'high' },
597
+ { title: 'Generate adventure card art', status: 'pending', priority: 'medium' },
598
+ { title: 'Fix board to rectangular layout', status: 'pending', priority: 'high' },
599
+ { title: 'Fix character select sticky buttons', status: 'pending', priority: 'medium' },
600
+ { title: 'Fix dice animation and combat', status: 'pending', priority: 'high' },
601
+ { title: 'Remove all emojis from UI', status: 'pending', priority: 'medium' },
602
+ { title: 'Test and verify all changes', status: 'pending', priority: 'medium' },
603
+ ],
604
+ }, context);
605
+ // The agent now (mistakenly) tries to sync a Settings plan.
606
+ const result = await tool.execute({
607
+ action: 'sync',
608
+ tasks: [
609
+ { title: 'Add Settings UI to index.html', status: 'pending', priority: 'high' },
610
+ { title: 'Add Settings logic to game.js', status: 'pending', priority: 'high' },
611
+ { title: 'Wire Settings to title screen', status: 'pending', priority: 'medium' },
612
+ { title: 'Test Settings persistence', status: 'pending', priority: 'medium' },
613
+ ],
614
+ }, context);
615
+ expect(result.success).toBe(false);
616
+ // ALL 9 original tasks must be named so the agent sees them.
617
+ expect(result.error).toContain('Explore current code');
618
+ expect(result.error).toContain('Generate character select background');
619
+ expect(result.error).toContain('Fix dice animation and combat');
620
+ expect(result.error).toContain('Remove all emojis from UI');
621
+ expect(result.error).toMatch(/Sync would drop 9 non-terminal task/);
622
+ });
623
+ });
624
+
625
+ test('enforces only one in_progress task', async () => {
626
+ const { tool, agent, context } = createTestSetup();
627
+ await tool.execute({
628
+ action: 'sync',
629
+ tasks: [
630
+ { title: 'A', status: 'in_progress', priority: 'high' },
631
+ { title: 'B', status: 'in_progress', priority: 'medium' }
632
+ ]
633
+ }, context);
634
+ const ipCount = agent.taskList.tasks.filter(t => t.status === 'in_progress').length;
635
+ expect(ipCount).toBe(1);
636
+ });
637
+
638
+ test('auto-sets first pending to in_progress when none active', async () => {
639
+ const { tool, agent, context } = createTestSetup();
640
+ await tool.execute({
641
+ action: 'sync',
642
+ tasks: [
643
+ { title: 'A', status: 'pending', priority: 'high' },
644
+ { title: 'B', status: 'pending', priority: 'medium' }
645
+ ]
646
+ }, context);
647
+ const ipTasks = agent.taskList.tasks.filter(t => t.status === 'in_progress');
648
+ expect(ipTasks).toHaveLength(1);
649
+ });
650
+ });
651
+
652
+ // ── template action ─────────────────────────────────────────────
653
+ describe('execute - template', () => {
654
+ test('lists available templates', async () => {
655
+ const { tool, context } = createTestSetup();
656
+ const result = await tool.execute({ action: 'template', mode: 'list' }, context);
657
+ expect(result.success).toBe(true);
658
+ expect(result.result.builtInTemplates.length).toBeGreaterThan(0);
659
+ });
660
+
661
+ test('applies a built-in template', async () => {
662
+ const { tool, agent, context } = createTestSetup();
663
+ const result = await tool.execute(
664
+ { action: 'template', mode: 'apply', templateId: 'bug-fix' }, context
665
+ );
666
+ expect(result.success).toBe(true);
667
+ expect(result.result.tasksCreated).toBeGreaterThan(0);
668
+ expect(agent.taskList.tasks.length).toBeGreaterThan(0);
669
+ });
670
+
671
+ test('errors for non-existent template', async () => {
672
+ const { tool, context } = createTestSetup();
673
+ const result = await tool.execute(
674
+ { action: 'template', mode: 'apply', templateId: 'nope' }, context
675
+ );
676
+ expect(result.success).toBe(false);
677
+ expect(result.error).toContain('Template not found');
678
+ });
679
+
680
+ test('creates a custom template', async () => {
681
+ const { tool, agent, context } = createTestSetup();
682
+ const result = await tool.execute({
683
+ action: 'template',
684
+ mode: 'create',
685
+ customTemplate: {
686
+ name: 'My Workflow',
687
+ description: 'Custom',
688
+ tasks: [{ title: 'Step 1' }, { title: 'Step 2' }]
689
+ }
690
+ }, context);
691
+ expect(result.success).toBe(true);
692
+ expect(agent.customTemplates).toHaveLength(1);
693
+ });
694
+
695
+ test('errors when custom template has no tasks', async () => {
696
+ const { tool, context } = createTestSetup();
697
+ const result = await tool.execute({
698
+ action: 'template',
699
+ mode: 'create',
700
+ customTemplate: { name: 'Empty', tasks: [] }
701
+ }, context);
702
+ expect(result.success).toBe(false);
703
+ expect(result.error).toContain('requires name and at least one task');
704
+ });
705
+
706
+ test('suggests templates based on patterns', async () => {
707
+ const { tool, agent, context } = createTestSetup();
708
+ agent.taskList.tasks.push({
709
+ id: 'bug-task', title: 'Fix login bug', status: 'pending',
710
+ priority: 'high', createdAt: new Date().toISOString()
711
+ });
712
+ const result = await tool.execute({ action: 'template', mode: 'suggest' }, context);
713
+ expect(result.success).toBe(true);
714
+ expect(result.result.suggestions.length).toBeGreaterThan(0);
715
+ });
716
+
717
+ test('errors for invalid template mode', async () => {
718
+ const { tool, context } = createTestSetup();
719
+ const result = await tool.execute({ action: 'template', mode: 'invalid' }, context);
720
+ expect(result.success).toBe(false);
721
+ expect(result.error).toContain('Invalid template mode');
722
+ });
723
+ });
724
+
725
+ // ── progress action ─────────────────────────────────────────────
726
+ describe('execute - progress', () => {
727
+ test('updates task progress with stage and note', async () => {
728
+ const { tool, agent, context } = createTestSetup();
729
+ await tool.execute({ action: 'create', title: 'Dev', priority: 'high' }, context);
730
+ const taskId = agent.taskList.tasks[0].id;
731
+ const result = await tool.execute({
732
+ action: 'progress', mode: 'update', taskId,
733
+ stage: 'in_development', note: 'Started coding'
734
+ }, context);
735
+ expect(result.success).toBe(true);
736
+ expect(result.result.task.progress.stage).toBe('in_development');
737
+ expect(result.result.task.progress.notes).toHaveLength(1);
738
+ });
739
+
740
+ test('setting percentage to 100 completes task', async () => {
741
+ const { tool, agent, context } = createTestSetup();
742
+ await tool.execute({ action: 'create', title: 'Pct', priority: 'high' }, context);
743
+ const taskId = agent.taskList.tasks[0].id;
744
+ await tool.execute({ action: 'progress', mode: 'update', taskId, percentage: 100 }, context);
745
+ expect(agent.taskList.tasks[0].status).toBe('completed');
746
+ expect(agent.taskList.tasks[0].progress.stage).toBe('completed');
747
+ });
748
+
749
+ test('gets progress overview', async () => {
750
+ const { tool, context } = createTestSetup();
751
+ await tool.execute({ action: 'create', title: 'T1', priority: 'high' }, context);
752
+ const result = await tool.execute({ action: 'progress', mode: 'overview' }, context);
753
+ expect(result.success).toBe(true);
754
+ expect(result.result.overview).toBeDefined();
755
+ });
756
+
757
+ test('calculates progress from subtasks', async () => {
758
+ const { tool, agent, context } = createTestSetup();
759
+ await tool.execute({ action: 'create', title: 'Parent', priority: 'high' }, context);
760
+ const parentId = agent.taskList.tasks[0].id;
761
+ await tool.execute({ action: 'subtask', parentTaskId: parentId, title: 'Sub1' }, context);
762
+ const result = await tool.execute({ action: 'progress', mode: 'calculate', taskId: parentId }, context);
763
+ expect(result.result.calculationMethod).toBe('subtasks');
764
+ });
765
+
766
+ test('errors for invalid stage', async () => {
767
+ const { tool, agent, context } = createTestSetup();
768
+ await tool.execute({ action: 'create', title: 'T', priority: 'high' }, context);
769
+ const taskId = agent.taskList.tasks[0].id;
770
+ const result = await tool.execute({ action: 'progress', mode: 'update', taskId, stage: 'nope' }, context);
771
+ expect(result.success).toBe(false);
772
+ expect(result.error).toContain('Invalid progress stage');
773
+ });
774
+
775
+ test('errors for invalid progress mode', async () => {
776
+ const { tool, context } = createTestSetup();
777
+ const result = await tool.execute({ action: 'progress', mode: 'invalid' }, context);
778
+ expect(result.success).toBe(false);
779
+ });
780
+ });
781
+
782
+ // ── prioritize action ───────────────────────────────────────────
783
+ describe('execute - prioritize', () => {
784
+ test('auto-prioritizes tasks', async () => {
785
+ const { tool, context } = createTestSetup();
786
+ await tool.execute({ action: 'create', title: 'T1', priority: 'low' }, context);
787
+ await tool.execute({ action: 'create', title: 'T2', priority: 'medium' }, context);
788
+ const result = await tool.execute({ action: 'prioritize', mode: 'auto' }, context);
789
+ expect(result.success).toBe(true);
790
+ });
791
+
792
+ test('analyzes specific task priority', async () => {
793
+ const { tool, agent, context } = createTestSetup();
794
+ await tool.execute({ action: 'create', title: 'Analyze', priority: 'medium' }, context);
795
+ const taskId = agent.taskList.tasks[0].id;
796
+ const result = await tool.execute({ action: 'prioritize', mode: 'analyze', taskId }, context);
797
+ expect(result.success).toBe(true);
798
+ expect(result.result.task.priorityScore).toBeDefined();
799
+ });
800
+
801
+ test('balance mode without scheduler returns message', async () => {
802
+ const { tool, context } = createTestSetup();
803
+ const result = await tool.execute({ action: 'prioritize', mode: 'balance' }, context);
804
+ expect(result.success).toBe(true);
805
+ expect(result.result.message).toContain('scheduler');
806
+ });
807
+
808
+ test('errors for invalid mode', async () => {
809
+ const { tool, context } = createTestSetup();
810
+ const result = await tool.execute({ action: 'prioritize', mode: 'xyz' }, context);
811
+ expect(result.success).toBe(false);
812
+ });
813
+ });
814
+
815
+ // ── analytics action ────────────────────────────────────────────
816
+ describe('execute - analytics', () => {
817
+ test('generates summary analytics', async () => {
818
+ const { tool, context } = createTestSetup();
819
+ await tool.execute({ action: 'create', title: 'T1', priority: 'high' }, context);
820
+ const result = await tool.execute({ action: 'analytics', mode: 'summary' }, context);
821
+ expect(result.success).toBe(true);
822
+ expect(result.result.generatedAt).toBeDefined();
823
+ });
824
+
825
+ test('errors for invalid analytics mode', async () => {
826
+ const { tool, context } = createTestSetup();
827
+ const result = await tool.execute({ action: 'analytics', mode: 'invalid' }, context);
828
+ expect(result.success).toBe(false);
829
+ expect(result.error).toContain('Invalid analytics mode');
830
+ });
831
+ });
832
+
833
+ // ── error handling ──────────────────────────────────────────────
834
+ describe('execute - error handling', () => {
835
+ test('fails when agentId is missing', async () => {
836
+ const { tool } = createTestSetup();
837
+ const result = await tool.execute({ action: 'list' }, { agentPool: {} });
838
+ expect(result.success).toBe(false);
839
+ expect(result.error).toContain('Agent ID is required');
840
+ });
841
+
842
+ test('fails when agent is not found', async () => {
843
+ const { tool } = createTestSetup();
844
+ const pool = { getAgent: jest.fn().mockResolvedValue(null) };
845
+ const result = await tool.execute({ action: 'list' }, { agentId: 'x', agentPool: pool });
846
+ expect(result.success).toBe(false);
847
+ expect(result.error).toContain('Agent not found');
848
+ });
849
+
850
+ test('fails for unsupported action', async () => {
851
+ const { tool, context } = createTestSetup();
852
+ const result = await tool.execute({ action: 'fly' }, context);
853
+ expect(result.success).toBe(false);
854
+ expect(result.error).toContain('Unsupported action');
855
+ });
856
+
857
+ test('initializes taskList on agent if missing', async () => {
858
+ const { tool, context, agentPool } = createTestSetup();
859
+ const bare = { id: 'agent-1', name: 'Bare' };
860
+ agentPool.getAgent.mockResolvedValue(bare);
861
+ const result = await tool.execute({ action: 'list' }, context);
862
+ expect(result.success).toBe(true);
863
+ expect(bare.taskList).toBeDefined();
864
+ });
865
+ });
866
+ });