macro-agent 0.1.0 → 0.1.1

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 (337) hide show
  1. package/.claude/settings.local.json +3 -1
  2. package/.sudocode/specs.jsonl +4 -0
  3. package/CLAUDE.md +16 -14
  4. package/README.md +11 -29
  5. package/dist/acp/macro-agent.d.ts +15 -0
  6. package/dist/acp/macro-agent.d.ts.map +1 -1
  7. package/dist/acp/macro-agent.js +131 -35
  8. package/dist/acp/macro-agent.js.map +1 -1
  9. package/dist/acp/types.d.ts +32 -1
  10. package/dist/acp/types.d.ts.map +1 -1
  11. package/dist/acp/types.js.map +1 -1
  12. package/dist/agent/agent-manager.d.ts +65 -1
  13. package/dist/agent/agent-manager.d.ts.map +1 -1
  14. package/dist/agent/agent-manager.js +464 -183
  15. package/dist/agent/agent-manager.js.map +1 -1
  16. package/dist/agent/types.d.ts +1 -1
  17. package/dist/agent/types.d.ts.map +1 -1
  18. package/dist/api/server.d.ts +3 -0
  19. package/dist/api/server.d.ts.map +1 -1
  20. package/dist/api/server.js +37 -6
  21. package/dist/api/server.js.map +1 -1
  22. package/dist/auth/index.d.ts +2 -0
  23. package/dist/auth/index.d.ts.map +1 -0
  24. package/dist/auth/index.js +2 -0
  25. package/dist/auth/index.js.map +1 -0
  26. package/dist/auth/token.d.ts +41 -0
  27. package/dist/auth/token.d.ts.map +1 -0
  28. package/dist/auth/token.js +73 -0
  29. package/dist/auth/token.js.map +1 -0
  30. package/dist/cli/acp.d.ts +2 -23
  31. package/dist/cli/acp.d.ts.map +1 -1
  32. package/dist/cli/acp.js +127 -61
  33. package/dist/cli/acp.js.map +1 -1
  34. package/dist/cli/index.js +147 -15
  35. package/dist/cli/index.js.map +1 -1
  36. package/dist/cli/mcp.d.ts +6 -0
  37. package/dist/cli/mcp.d.ts.map +1 -1
  38. package/dist/cli/mcp.js +268 -181
  39. package/dist/cli/mcp.js.map +1 -1
  40. package/dist/cli/parse-args.d.ts +20 -0
  41. package/dist/cli/parse-args.d.ts.map +1 -0
  42. package/dist/cli/parse-args.js +43 -0
  43. package/dist/cli/parse-args.js.map +1 -0
  44. package/dist/cli/stable-instance-id.d.ts +8 -0
  45. package/dist/cli/stable-instance-id.d.ts.map +1 -0
  46. package/dist/cli/stable-instance-id.js +14 -0
  47. package/dist/cli/stable-instance-id.js.map +1 -0
  48. package/dist/config/project-config.d.ts +74 -7
  49. package/dist/config/project-config.d.ts.map +1 -1
  50. package/dist/config/project-config.js +123 -20
  51. package/dist/config/project-config.js.map +1 -1
  52. package/dist/map/adapter/acp-over-map.d.ts +17 -0
  53. package/dist/map/adapter/acp-over-map.d.ts.map +1 -1
  54. package/dist/map/adapter/acp-over-map.js +384 -23
  55. package/dist/map/adapter/acp-over-map.js.map +1 -1
  56. package/dist/map/adapter/connection-manager.d.ts.map +1 -1
  57. package/dist/map/adapter/connection-manager.js +3 -0
  58. package/dist/map/adapter/connection-manager.js.map +1 -1
  59. package/dist/map/adapter/event-log.d.ts +87 -0
  60. package/dist/map/adapter/event-log.d.ts.map +1 -0
  61. package/dist/map/adapter/event-log.js +122 -0
  62. package/dist/map/adapter/event-log.js.map +1 -0
  63. package/dist/map/adapter/event-translator.js +6 -6
  64. package/dist/map/adapter/event-translator.js.map +1 -1
  65. package/dist/map/adapter/extensions/agent-lifecycle.d.ts +82 -0
  66. package/dist/map/adapter/extensions/agent-lifecycle.d.ts.map +1 -0
  67. package/dist/map/adapter/extensions/agent-lifecycle.js +164 -0
  68. package/dist/map/adapter/extensions/agent-lifecycle.js.map +1 -0
  69. package/dist/map/adapter/extensions/index.d.ts +10 -1
  70. package/dist/map/adapter/extensions/index.d.ts.map +1 -1
  71. package/dist/map/adapter/extensions/index.js +34 -0
  72. package/dist/map/adapter/extensions/index.js.map +1 -1
  73. package/dist/map/adapter/extensions/mcp-bridge.d.ts +57 -0
  74. package/dist/map/adapter/extensions/mcp-bridge.d.ts.map +1 -0
  75. package/dist/map/adapter/extensions/mcp-bridge.js +745 -0
  76. package/dist/map/adapter/extensions/mcp-bridge.js.map +1 -0
  77. package/dist/map/adapter/extensions/rename.d.ts +29 -0
  78. package/dist/map/adapter/extensions/rename.d.ts.map +1 -0
  79. package/dist/map/adapter/extensions/rename.js +49 -0
  80. package/dist/map/adapter/extensions/rename.js.map +1 -0
  81. package/dist/map/adapter/extensions/task.d.ts.map +1 -1
  82. package/dist/map/adapter/extensions/task.js +10 -0
  83. package/dist/map/adapter/extensions/task.js.map +1 -1
  84. package/dist/map/adapter/extensions/update-metadata.d.ts +29 -0
  85. package/dist/map/adapter/extensions/update-metadata.d.ts.map +1 -0
  86. package/dist/map/adapter/extensions/update-metadata.js +67 -0
  87. package/dist/map/adapter/extensions/update-metadata.js.map +1 -0
  88. package/dist/map/adapter/index.d.ts +2 -1
  89. package/dist/map/adapter/index.d.ts.map +1 -1
  90. package/dist/map/adapter/index.js +8 -2
  91. package/dist/map/adapter/index.js.map +1 -1
  92. package/dist/map/adapter/interface.d.ts +2 -0
  93. package/dist/map/adapter/interface.d.ts.map +1 -1
  94. package/dist/map/adapter/map-adapter.d.ts +3 -0
  95. package/dist/map/adapter/map-adapter.d.ts.map +1 -1
  96. package/dist/map/adapter/map-adapter.js +258 -35
  97. package/dist/map/adapter/map-adapter.js.map +1 -1
  98. package/dist/map/adapter/subscription-manager.d.ts.map +1 -1
  99. package/dist/map/adapter/subscription-manager.js +5 -1
  100. package/dist/map/adapter/subscription-manager.js.map +1 -1
  101. package/dist/map/adapter/types.d.ts +2 -0
  102. package/dist/map/adapter/types.d.ts.map +1 -1
  103. package/dist/mcp/map-client.d.ts +39 -0
  104. package/dist/mcp/map-client.d.ts.map +1 -0
  105. package/dist/mcp/map-client.js +129 -0
  106. package/dist/mcp/map-client.js.map +1 -0
  107. package/dist/mcp/mcp-server.d.ts +14 -0
  108. package/dist/mcp/mcp-server.d.ts.map +1 -1
  109. package/dist/mcp/mcp-server.js +113 -85
  110. package/dist/mcp/mcp-server.js.map +1 -1
  111. package/dist/mcp/types.d.ts +9 -1
  112. package/dist/mcp/types.d.ts.map +1 -1
  113. package/dist/mcp/types.js.map +1 -1
  114. package/dist/metrics/metrics.js +1 -1
  115. package/dist/metrics/metrics.js.map +1 -1
  116. package/dist/roles/capabilities.d.ts +3 -1
  117. package/dist/roles/capabilities.d.ts.map +1 -1
  118. package/dist/roles/capabilities.js +17 -7
  119. package/dist/roles/capabilities.js.map +1 -1
  120. package/dist/roles/config-loader.d.ts +6 -6
  121. package/dist/roles/config-loader.d.ts.map +1 -1
  122. package/dist/roles/config-loader.js +6 -6
  123. package/dist/roles/config-loader.js.map +1 -1
  124. package/dist/roles/registry.d.ts +2 -2
  125. package/dist/roles/registry.js +2 -2
  126. package/dist/server/combined-server.d.ts +20 -0
  127. package/dist/server/combined-server.d.ts.map +1 -1
  128. package/dist/server/combined-server.js +107 -8
  129. package/dist/server/combined-server.js.map +1 -1
  130. package/dist/store/event-store.d.ts +2 -1
  131. package/dist/store/event-store.d.ts.map +1 -1
  132. package/dist/store/event-store.js +69 -20
  133. package/dist/store/event-store.js.map +1 -1
  134. package/dist/store/types/agents.d.ts +18 -0
  135. package/dist/store/types/agents.d.ts.map +1 -1
  136. package/dist/store/types/events.d.ts +1 -1
  137. package/dist/store/types/events.d.ts.map +1 -1
  138. package/dist/task/backend/index.d.ts +47 -29
  139. package/dist/task/backend/index.d.ts.map +1 -1
  140. package/dist/task/backend/index.js +109 -71
  141. package/dist/task/backend/index.js.map +1 -1
  142. package/dist/task/backend/memory.d.ts +1 -0
  143. package/dist/task/backend/memory.d.ts.map +1 -1
  144. package/dist/task/backend/memory.js +3 -0
  145. package/dist/task/backend/memory.js.map +1 -1
  146. package/dist/task/backend/opentasks/backend.d.ts +140 -0
  147. package/dist/task/backend/opentasks/backend.d.ts.map +1 -0
  148. package/dist/task/backend/opentasks/backend.js +1023 -0
  149. package/dist/task/backend/opentasks/backend.js.map +1 -0
  150. package/dist/task/backend/opentasks/client.d.ts +337 -0
  151. package/dist/task/backend/opentasks/client.d.ts.map +1 -0
  152. package/dist/task/backend/opentasks/client.js +225 -0
  153. package/dist/task/backend/opentasks/client.js.map +1 -0
  154. package/dist/task/backend/opentasks/daemon-manager.d.ts +89 -0
  155. package/dist/task/backend/opentasks/daemon-manager.d.ts.map +1 -0
  156. package/dist/task/backend/opentasks/daemon-manager.js +195 -0
  157. package/dist/task/backend/opentasks/daemon-manager.js.map +1 -0
  158. package/dist/task/backend/opentasks/index.d.ts +21 -0
  159. package/dist/task/backend/opentasks/index.d.ts.map +1 -0
  160. package/dist/task/backend/opentasks/index.js +21 -0
  161. package/dist/task/backend/opentasks/index.js.map +1 -0
  162. package/dist/task/backend/opentasks/mapping.d.ts +48 -0
  163. package/dist/task/backend/opentasks/mapping.d.ts.map +1 -0
  164. package/dist/task/backend/opentasks/mapping.js +77 -0
  165. package/dist/task/backend/opentasks/mapping.js.map +1 -0
  166. package/dist/task/backend/types.d.ts +33 -53
  167. package/dist/task/backend/types.d.ts.map +1 -1
  168. package/dist/task/backend/types.js +7 -11
  169. package/dist/task/backend/types.js.map +1 -1
  170. package/dist/task/backend/unified-tool-provider.d.ts +57 -0
  171. package/dist/task/backend/unified-tool-provider.d.ts.map +1 -0
  172. package/dist/task/backend/unified-tool-provider.js +623 -0
  173. package/dist/task/backend/unified-tool-provider.js.map +1 -0
  174. package/dist/teams/team-loader.d.ts +2 -2
  175. package/dist/teams/team-loader.js +3 -3
  176. package/dist/teams/team-loader.js.map +1 -1
  177. package/dist/teams/team-runtime.d.ts.map +1 -1
  178. package/dist/teams/team-runtime.js +2 -0
  179. package/dist/teams/team-runtime.js.map +1 -1
  180. package/docs/architecture.md +7 -6
  181. package/docs/configuration.md +26 -62
  182. package/docs/implementation-details.md +5 -5
  183. package/docs/implementation-summary.md +17 -17
  184. package/docs/plan-self-driving-support.md +4 -4
  185. package/docs/spec-self-driving-support.md +10 -10
  186. package/docs/team-templates.md +2 -2
  187. package/docs/teams.md +3 -3
  188. package/docs/troubleshooting.md +10 -11
  189. package/package.json +6 -4
  190. package/src/__tests__/e2e/agent-spawn-visibility.e2e.test.ts +761 -0
  191. package/src/__tests__/e2e/full-agent-conflict-resolution.e2e.test.ts +2 -2
  192. package/src/__tests__/e2e/mcp-thin-client-bridge.e2e.test.ts +304 -0
  193. package/src/__tests__/e2e/mcp-tools-available.e2e.test.ts +324 -0
  194. package/src/__tests__/e2e/multi-agent.e2e.test.ts +5 -5
  195. package/src/__tests__/e2e/spawn-session-streaming.e2e.test.ts +563 -0
  196. package/src/acp/__tests__/integration.test.ts +56 -31
  197. package/src/acp/__tests__/macro-agent.test.ts +16 -7
  198. package/src/acp/macro-agent.ts +170 -36
  199. package/src/acp/types.ts +46 -1
  200. package/src/agent/__tests__/agent-manager.test.ts +228 -2
  201. package/src/agent/agent-manager.ts +714 -261
  202. package/src/agent/types.ts +3 -1
  203. package/src/api/server.ts +41 -7
  204. package/src/auth/__tests__/token.test.ts +100 -0
  205. package/src/auth/index.ts +1 -0
  206. package/src/auth/token.ts +82 -0
  207. package/src/cli/__tests__/acp.test.ts +1 -1
  208. package/src/cli/__tests__/stable-instance-id.test.ts +1 -1
  209. package/src/cli/acp.ts +130 -72
  210. package/src/cli/index.ts +120 -14
  211. package/src/cli/mcp.ts +311 -207
  212. package/src/cli/parse-args.ts +54 -0
  213. package/src/cli/stable-instance-id.ts +14 -0
  214. package/src/config/project-config.ts +190 -27
  215. package/src/lifecycle/__tests__/cascade-termination.test.ts +1 -1
  216. package/src/map/adapter/__tests__/acp-over-map-cancel.test.ts +22 -4
  217. package/src/map/adapter/__tests__/acp-over-map-getmodels.test.ts +355 -0
  218. package/src/map/adapter/__tests__/acp-over-map-history.test.ts +263 -0
  219. package/src/map/adapter/__tests__/acp-over-map-persistence.e2e.test.ts +1 -1
  220. package/src/map/adapter/__tests__/event-broadcast.test.ts +420 -0
  221. package/src/map/adapter/__tests__/event-log.test.ts +527 -0
  222. package/src/map/adapter/__tests__/event-translator.test.ts +3 -3
  223. package/src/map/adapter/__tests__/extensions.test.ts +408 -0
  224. package/src/map/adapter/__tests__/map-adapter.test.ts +99 -0
  225. package/src/map/adapter/__tests__/mcp-bridge.test.ts +1187 -0
  226. package/src/map/adapter/__tests__/multi-client-broadcast.test.ts +711 -0
  227. package/src/map/adapter/__tests__/websocket-integration.test.ts +218 -0
  228. package/src/map/adapter/acp-over-map.ts +678 -66
  229. package/src/map/adapter/connection-manager.ts +3 -0
  230. package/src/map/adapter/event-log.ts +208 -0
  231. package/src/map/adapter/event-translator.ts +6 -6
  232. package/src/map/adapter/extensions/agent-lifecycle.ts +267 -0
  233. package/src/map/adapter/extensions/index.ts +60 -0
  234. package/src/map/adapter/extensions/mcp-bridge.ts +995 -0
  235. package/src/map/adapter/extensions/task.ts +11 -0
  236. package/src/map/adapter/extensions/update-metadata.ts +126 -0
  237. package/src/map/adapter/index.ts +28 -0
  238. package/src/map/adapter/interface.ts +2 -0
  239. package/src/map/adapter/map-adapter.ts +312 -47
  240. package/src/map/adapter/subscription-manager.ts +5 -1
  241. package/src/map/adapter/types.ts +2 -0
  242. package/src/mcp/__tests__/map-client.test.ts +386 -0
  243. package/src/mcp/__tests__/mcp-server-thin-client.test.ts +368 -0
  244. package/src/mcp/__tests__/mcp-server.test.ts +100 -1
  245. package/src/mcp/map-client.ts +177 -0
  246. package/src/mcp/mcp-server.ts +191 -100
  247. package/src/mcp/types.ts +6 -1
  248. package/src/metrics/metrics.ts +1 -1
  249. package/src/monitor/__tests__/stale-agent-flow.integration.test.ts +1 -1
  250. package/src/roles/__tests__/config-loader.test.ts +7 -7
  251. package/src/roles/capabilities.ts +17 -7
  252. package/src/roles/config-loader.ts +6 -6
  253. package/src/roles/registry.ts +2 -2
  254. package/src/server/__tests__/combined-server.test.ts +94 -21
  255. package/src/server/combined-server.ts +189 -33
  256. package/src/steering/__tests__/steering-integration.test.ts +1 -1
  257. package/src/store/__tests__/event-store.test.ts +196 -1
  258. package/src/store/__tests__/instance.test.ts +3 -3
  259. package/src/store/event-store.ts +80 -21
  260. package/src/store/types/agents.ts +15 -0
  261. package/src/store/types/events.ts +1 -1
  262. package/src/task/backend/__tests__/create-task-backend.test.ts +225 -0
  263. package/src/task/backend/__tests__/e2e/unified-tool-provider-opentasks.e2e.test.ts +524 -0
  264. package/src/task/backend/__tests__/unified-tool-provider.test.ts +579 -0
  265. package/src/task/backend/index.ts +156 -106
  266. package/src/task/backend/memory.ts +4 -0
  267. package/src/task/backend/opentasks/__tests__/backend.test.ts +968 -0
  268. package/src/task/backend/opentasks/__tests__/daemon-manager.test.ts +406 -0
  269. package/src/task/backend/opentasks/__tests__/mapping.test.ts +84 -0
  270. package/src/task/backend/opentasks/__tests__/opentasks-backend.e2e.test.ts +1338 -0
  271. package/src/task/backend/opentasks/backend.ts +1323 -0
  272. package/src/task/backend/opentasks/client.ts +652 -0
  273. package/src/task/backend/opentasks/daemon-manager.ts +253 -0
  274. package/src/task/backend/opentasks/index.ts +69 -0
  275. package/src/task/backend/opentasks/mapping.ts +94 -0
  276. package/src/task/backend/types.ts +42 -66
  277. package/src/task/backend/unified-tool-provider.ts +779 -0
  278. package/src/teams/__tests__/cross-subsystem.integration.test.ts +1 -1
  279. package/src/teams/team-loader.ts +3 -3
  280. package/src/teams/team-runtime.ts +2 -0
  281. package/test_fixtures/README.md +2 -3
  282. package/test_fixtures/fixtures/index.ts +0 -3
  283. package/test_fixtures/fixtures/projects/project-with-specs.ts +7 -149
  284. package/test_fixtures/fixtures/repos/index.ts +1 -3
  285. package/test_fixtures/fixtures/repos/temp-repo-factory.ts +0 -116
  286. package/test_fixtures/fixtures/repos/types.ts +0 -11
  287. package/test_fixtures/harness/__tests__/fixtures.test.ts +10 -102
  288. package/test_fixtures/harness/__tests__/temp-repo-and-simulator.test.ts +0 -33
  289. package/test_fixtures/harness/simulator/agent-simulator.ts +4 -4
  290. package/vitest.config.ts +1 -1
  291. package/vitest.e2e.config.ts +1 -1
  292. package/vitest.setup.ts +1 -30
  293. package/.macro-agent/teams/self-driving/prompts/grinder.md +0 -27
  294. package/.macro-agent/teams/self-driving/prompts/judge.md +0 -27
  295. package/.macro-agent/teams/self-driving/prompts/planner.md +0 -33
  296. package/.macro-agent/teams/self-driving/roles/grinder.yaml +0 -17
  297. package/.macro-agent/teams/self-driving/roles/judge.yaml +0 -24
  298. package/.macro-agent/teams/self-driving/roles/planner.yaml +0 -18
  299. package/.macro-agent/teams/self-driving/team.yaml +0 -103
  300. package/.macro-agent/teams/structured/prompts/developer.md +0 -26
  301. package/.macro-agent/teams/structured/prompts/lead.md +0 -25
  302. package/.macro-agent/teams/structured/prompts/reviewer.md +0 -24
  303. package/.macro-agent/teams/structured/roles/developer.yaml +0 -12
  304. package/.macro-agent/teams/structured/roles/lead.yaml +0 -11
  305. package/.macro-agent/teams/structured/roles/reviewer.yaml +0 -19
  306. package/.macro-agent/teams/structured/team.yaml +0 -89
  307. package/docs/sudocode-integration.md +0 -383
  308. package/src/task/backend/__tests__/backend-parity.test.ts +0 -451
  309. package/src/task/backend/__tests__/tool-provider-edge-cases.test.ts +0 -430
  310. package/src/task/backend/__tests__/tool-provider.test.ts +0 -983
  311. package/src/task/backend/sudocode/__tests__/backend-edge-cases.test.ts +0 -575
  312. package/src/task/backend/sudocode/__tests__/backend.test.ts +0 -1194
  313. package/src/task/backend/sudocode/__tests__/client-integration.test.ts +0 -418
  314. package/src/task/backend/sudocode/__tests__/client.test.ts +0 -345
  315. package/src/task/backend/sudocode/__tests__/e2e/backend.e2e.test.ts +0 -753
  316. package/src/task/backend/sudocode/__tests__/e2e/server-client.e2e.test.ts +0 -680
  317. package/src/task/backend/sudocode/__tests__/e2e-workflow.test.ts +0 -666
  318. package/src/task/backend/sudocode/__tests__/integration/standalone-client.integration.test.ts +0 -396
  319. package/src/task/backend/sudocode/__tests__/integration/sudocode-cli.integration.test.ts +0 -328
  320. package/src/task/backend/sudocode/__tests__/integration/test-utils.ts +0 -175
  321. package/src/task/backend/sudocode/__tests__/mapping-edge-cases.test.ts +0 -265
  322. package/src/task/backend/sudocode/__tests__/server-client.test.ts +0 -675
  323. package/src/task/backend/sudocode/__tests__/sync-policy-edge-cases.test.ts +0 -521
  324. package/src/task/backend/sudocode/__tests__/sync-policy.test.ts +0 -519
  325. package/src/task/backend/sudocode/__tests__/tools.test.ts +0 -471
  326. package/src/task/backend/sudocode/backend.ts +0 -1237
  327. package/src/task/backend/sudocode/client.ts +0 -515
  328. package/src/task/backend/sudocode/index.ts +0 -120
  329. package/src/task/backend/sudocode/mapping.ts +0 -93
  330. package/src/task/backend/sudocode/server-client.ts +0 -522
  331. package/src/task/backend/sudocode/standalone-client.ts +0 -623
  332. package/src/task/backend/sudocode/sync-policy.ts +0 -387
  333. package/src/task/backend/sudocode/tools.ts +0 -896
  334. package/src/task/backend/tool-provider.ts +0 -506
  335. package/test_fixtures/fixtures/sudocode/index.ts +0 -29
  336. package/test_fixtures/fixtures/sudocode/issues.ts +0 -185
  337. package/test_fixtures/fixtures/sudocode/specs.ts +0 -159
@@ -1,983 +0,0 @@
1
- /**
2
- * Tests for InMemoryTaskToolProvider
3
- */
4
-
5
- import { describe, it, expect, beforeEach, afterEach } from "vitest";
6
- import { createEventStore, type EventStore } from "../../../store/event-store.js";
7
- import { createInMemoryTaskBackend, type InMemoryTaskBackend } from "../memory.js";
8
- import {
9
- InMemoryTaskToolProvider,
10
- createTaskToolProvider,
11
- type TaskToolContext,
12
- } from "../tool-provider.js";
13
-
14
- describe("InMemoryTaskToolProvider", () => {
15
- let eventStore: EventStore;
16
- let backend: InMemoryTaskBackend;
17
- let provider: InMemoryTaskToolProvider;
18
- const testAgentId = "agent_test";
19
-
20
- const getContext = (): TaskToolContext => ({
21
- agent_id: testAgentId,
22
- });
23
-
24
- beforeEach(async () => {
25
- eventStore = await createEventStore({ inMemory: true });
26
- backend = createInMemoryTaskBackend(eventStore);
27
- provider = createTaskToolProvider(backend, getContext);
28
- });
29
-
30
- afterEach(async () => {
31
- await eventStore.close();
32
- });
33
-
34
- // ─────────────────────────────────────────────────────────────────────────────
35
- // Basic Tests
36
- // ─────────────────────────────────────────────────────────────────────────────
37
-
38
- describe("getTools", () => {
39
- it("should return all task tools", () => {
40
- const tools = provider.getTools();
41
- expect(tools.length).toBeGreaterThan(0);
42
-
43
- const toolNames = tools.map((t) => t.name);
44
- expect(toolNames).toContain("create_task");
45
- expect(toolNames).toContain("get_task");
46
- expect(toolNames).toContain("list_tasks");
47
- expect(toolNames).toContain("list_ready_tasks");
48
- expect(toolNames).toContain("get_task_blockers");
49
- expect(toolNames).toContain("update_task_status");
50
- expect(toolNames).toContain("add_blocker");
51
- expect(toolNames).toContain("remove_blocker");
52
- expect(toolNames).toContain("assign_task");
53
- expect(toolNames).toContain("complete_task");
54
- });
55
-
56
- it("should have valid schema for each tool", () => {
57
- const tools = provider.getTools();
58
-
59
- for (const tool of tools) {
60
- expect(tool.name).toBeDefined();
61
- expect(tool.description).toBeDefined();
62
- expect(tool.schema).toBeDefined();
63
- expect(tool.handler).toBeDefined();
64
- expect(typeof tool.handler).toBe("function");
65
- }
66
- });
67
- });
68
-
69
- describe("getExcludedTools", () => {
70
- it("should return excluded tool names", () => {
71
- const excluded = provider.getExcludedTools();
72
- expect(excluded).toContain("create_task");
73
- expect(excluded).toContain("get_task");
74
- });
75
- });
76
-
77
- // ─────────────────────────────────────────────────────────────────────────────
78
- // Tool Handler Tests
79
- // ─────────────────────────────────────────────────────────────────────────────
80
-
81
- describe("create_task handler", () => {
82
- it("should create a task", async () => {
83
- const tools = provider.getTools();
84
- const createTask = tools.find((t) => t.name === "create_task")!;
85
-
86
- const result = (await createTask.handler({
87
- description: "Test task",
88
- })) as { task_id: string; status: string };
89
-
90
- expect(result.task_id).toBeDefined();
91
- expect(result.status).toBe("pending");
92
- });
93
-
94
- it("should create a subtask with parent", async () => {
95
- const tools = provider.getTools();
96
- const createTask = tools.find((t) => t.name === "create_task")!;
97
-
98
- const parent = (await createTask.handler({
99
- description: "Parent task",
100
- })) as { task_id: string };
101
-
102
- const child = (await createTask.handler({
103
- description: "Child task",
104
- parent_task: parent.task_id,
105
- })) as { task_id: string };
106
-
107
- expect(child.task_id).toBeDefined();
108
-
109
- // Verify parent-child relationship
110
- const getTask = tools.find((t) => t.name === "get_task")!;
111
- const childDetails = (await getTask.handler({
112
- task_id: child.task_id,
113
- })) as { parent_task: string };
114
-
115
- expect(childDetails.parent_task).toBe(parent.task_id);
116
- });
117
- });
118
-
119
- describe("get_task handler", () => {
120
- it("should get task details", async () => {
121
- const tools = provider.getTools();
122
- const createTask = tools.find((t) => t.name === "create_task")!;
123
- const getTask = tools.find((t) => t.name === "get_task")!;
124
-
125
- const created = (await createTask.handler({
126
- description: "Test task",
127
- })) as { task_id: string };
128
-
129
- const result = (await getTask.handler({
130
- task_id: created.task_id,
131
- })) as { id: string; description: string; status: string };
132
-
133
- expect(result.id).toBe(created.task_id);
134
- expect(result.description).toBe("Test task");
135
- expect(result.status).toBe("pending");
136
- });
137
-
138
- it("should throw for non-existent task", async () => {
139
- const tools = provider.getTools();
140
- const getTask = tools.find((t) => t.name === "get_task")!;
141
-
142
- await expect(
143
- getTask.handler({ task_id: "nonexistent" })
144
- ).rejects.toThrow("Task not found");
145
- });
146
- });
147
-
148
- describe("list_tasks handler", () => {
149
- it("should list all tasks", async () => {
150
- const tools = provider.getTools();
151
- const createTask = tools.find((t) => t.name === "create_task")!;
152
- const listTasks = tools.find((t) => t.name === "list_tasks")!;
153
-
154
- await createTask.handler({ description: "Task 1" });
155
- await createTask.handler({ description: "Task 2" });
156
-
157
- const result = (await listTasks.handler({})) as {
158
- tasks: Array<{ id: string }>;
159
- total: number;
160
- };
161
-
162
- expect(result.tasks).toHaveLength(2);
163
- expect(result.total).toBe(2);
164
- });
165
-
166
- it("should filter by status", async () => {
167
- const tools = provider.getTools();
168
- const createTask = tools.find((t) => t.name === "create_task")!;
169
- const assignTask = tools.find((t) => t.name === "assign_task")!;
170
- const listTasks = tools.find((t) => t.name === "list_tasks")!;
171
-
172
- const task1 = (await createTask.handler({
173
- description: "Task 1",
174
- })) as { task_id: string };
175
- await createTask.handler({ description: "Task 2" });
176
-
177
- await assignTask.handler({ task_id: task1.task_id });
178
-
179
- const result = (await listTasks.handler({ status: "assigned" })) as {
180
- tasks: Array<{ id: string }>;
181
- total: number;
182
- };
183
-
184
- expect(result.tasks).toHaveLength(1);
185
- expect(result.tasks[0].id).toBe(task1.task_id);
186
- });
187
- });
188
-
189
- describe("list_ready_tasks handler", () => {
190
- it("should list only unblocked tasks", async () => {
191
- const tools = provider.getTools();
192
- const createTask = tools.find((t) => t.name === "create_task")!;
193
- const addBlocker = tools.find((t) => t.name === "add_blocker")!;
194
- const listReady = tools.find((t) => t.name === "list_ready_tasks")!;
195
-
196
- const task1 = (await createTask.handler({
197
- description: "Blocker task",
198
- })) as { task_id: string };
199
- const task2 = (await createTask.handler({
200
- description: "Blocked task",
201
- })) as { task_id: string };
202
-
203
- await addBlocker.handler({
204
- task_id: task2.task_id,
205
- blocker_id: task1.task_id,
206
- });
207
-
208
- const result = (await listReady.handler({})) as {
209
- tasks: Array<{ id: string }>;
210
- total: number;
211
- };
212
-
213
- // Only task1 should be ready
214
- expect(result.tasks).toHaveLength(1);
215
- expect(result.tasks[0].id).toBe(task1.task_id);
216
- });
217
- });
218
-
219
- describe("get_task_blockers handler", () => {
220
- it("should return blockers for a task", async () => {
221
- const tools = provider.getTools();
222
- const createTask = tools.find((t) => t.name === "create_task")!;
223
- const addBlocker = tools.find((t) => t.name === "add_blocker")!;
224
- const getBlockers = tools.find((t) => t.name === "get_task_blockers")!;
225
-
226
- const blocker = (await createTask.handler({
227
- description: "Blocker",
228
- })) as { task_id: string };
229
- const blocked = (await createTask.handler({
230
- description: "Blocked",
231
- })) as { task_id: string };
232
-
233
- await addBlocker.handler({
234
- task_id: blocked.task_id,
235
- blocker_id: blocker.task_id,
236
- });
237
-
238
- const result = (await getBlockers.handler({
239
- task_id: blocked.task_id,
240
- })) as {
241
- task_id: string;
242
- blockers: Array<{ id: string }>;
243
- isBlocked: boolean;
244
- };
245
-
246
- expect(result.blockers).toHaveLength(1);
247
- expect(result.blockers[0].id).toBe(blocker.task_id);
248
- expect(result.isBlocked).toBe(true);
249
- });
250
- });
251
-
252
- describe("add_blocker handler", () => {
253
- it("should add a blocker to a task", async () => {
254
- const tools = provider.getTools();
255
- const createTask = tools.find((t) => t.name === "create_task")!;
256
- const addBlocker = tools.find((t) => t.name === "add_blocker")!;
257
- const getTask = tools.find((t) => t.name === "get_task")!;
258
-
259
- const blocker = (await createTask.handler({
260
- description: "Blocker",
261
- })) as { task_id: string };
262
- const blocked = (await createTask.handler({
263
- description: "Blocked",
264
- })) as { task_id: string };
265
-
266
- const result = (await addBlocker.handler({
267
- task_id: blocked.task_id,
268
- blocker_id: blocker.task_id,
269
- })) as { added: boolean };
270
-
271
- expect(result.added).toBe(true);
272
-
273
- const taskDetails = (await getTask.handler({
274
- task_id: blocked.task_id,
275
- })) as { isBlocked: boolean; blockers: string[] };
276
-
277
- expect(taskDetails.isBlocked).toBe(true);
278
- expect(taskDetails.blockers).toContain(blocker.task_id);
279
- });
280
- });
281
-
282
- describe("remove_blocker handler", () => {
283
- it("should remove a blocker from a task", async () => {
284
- const tools = provider.getTools();
285
- const createTask = tools.find((t) => t.name === "create_task")!;
286
- const addBlocker = tools.find((t) => t.name === "add_blocker")!;
287
- const removeBlocker = tools.find((t) => t.name === "remove_blocker")!;
288
- const getTask = tools.find((t) => t.name === "get_task")!;
289
-
290
- const blocker = (await createTask.handler({
291
- description: "Blocker",
292
- })) as { task_id: string };
293
- const blocked = (await createTask.handler({
294
- description: "Blocked",
295
- })) as { task_id: string };
296
-
297
- await addBlocker.handler({
298
- task_id: blocked.task_id,
299
- blocker_id: blocker.task_id,
300
- });
301
-
302
- const result = (await removeBlocker.handler({
303
- task_id: blocked.task_id,
304
- blocker_id: blocker.task_id,
305
- })) as { removed: boolean };
306
-
307
- expect(result.removed).toBe(true);
308
-
309
- const taskDetails = (await getTask.handler({
310
- task_id: blocked.task_id,
311
- })) as { isBlocked: boolean; blockers: string[] };
312
-
313
- expect(taskDetails.isBlocked).toBe(false);
314
- expect(taskDetails.blockers).not.toContain(blocker.task_id);
315
- });
316
- });
317
-
318
- describe("assign_task handler", () => {
319
- it("should assign task to calling agent by default", async () => {
320
- const tools = provider.getTools();
321
- const createTask = tools.find((t) => t.name === "create_task")!;
322
- const assignTask = tools.find((t) => t.name === "assign_task")!;
323
- const getTask = tools.find((t) => t.name === "get_task")!;
324
-
325
- const task = (await createTask.handler({
326
- description: "Test task",
327
- })) as { task_id: string };
328
-
329
- const result = (await assignTask.handler({
330
- task_id: task.task_id,
331
- })) as { assigned_agent: string };
332
-
333
- expect(result.assigned_agent).toBe(testAgentId);
334
-
335
- const taskDetails = (await getTask.handler({
336
- task_id: task.task_id,
337
- })) as { assigned_agent: string; status: string };
338
-
339
- expect(taskDetails.assigned_agent).toBe(testAgentId);
340
- expect(taskDetails.status).toBe("assigned");
341
- });
342
-
343
- it("should assign task to specified agent", async () => {
344
- const tools = provider.getTools();
345
- const createTask = tools.find((t) => t.name === "create_task")!;
346
- const assignTask = tools.find((t) => t.name === "assign_task")!;
347
-
348
- const task = (await createTask.handler({
349
- description: "Test task",
350
- })) as { task_id: string };
351
-
352
- const result = (await assignTask.handler({
353
- task_id: task.task_id,
354
- agent_id: "agent_other",
355
- })) as { assigned_agent: string };
356
-
357
- expect(result.assigned_agent).toBe("agent_other");
358
- });
359
- });
360
-
361
- describe("complete_task handler", () => {
362
- it("should complete a task", async () => {
363
- const tools = provider.getTools();
364
- const createTask = tools.find((t) => t.name === "create_task")!;
365
- const completeTask = tools.find((t) => t.name === "complete_task")!;
366
- const getTask = tools.find((t) => t.name === "get_task")!;
367
-
368
- const task = (await createTask.handler({
369
- description: "Test task",
370
- })) as { task_id: string };
371
-
372
- // Start task first
373
- await backend.start(task.task_id);
374
-
375
- const result = (await completeTask.handler({
376
- task_id: task.task_id,
377
- summary: "Task completed successfully",
378
- })) as { completed: boolean };
379
-
380
- expect(result.completed).toBe(true);
381
-
382
- const taskDetails = (await getTask.handler({
383
- task_id: task.task_id,
384
- })) as { status: string };
385
-
386
- expect(taskDetails.status).toBe("completed");
387
- });
388
- });
389
-
390
- describe("update_task_status handler", () => {
391
- it("should update task status", async () => {
392
- const tools = provider.getTools();
393
- const createTask = tools.find((t) => t.name === "create_task")!;
394
- const updateStatus = tools.find((t) => t.name === "update_task_status")!;
395
- const getTask = tools.find((t) => t.name === "get_task")!;
396
-
397
- const task = (await createTask.handler({
398
- description: "Test task",
399
- })) as { task_id: string };
400
-
401
- const result = (await updateStatus.handler({
402
- task_id: task.task_id,
403
- status: "in_progress",
404
- })) as { status: string };
405
-
406
- expect(result.status).toBe("in_progress");
407
-
408
- const taskDetails = (await getTask.handler({
409
- task_id: task.task_id,
410
- })) as { status: string };
411
-
412
- expect(taskDetails.status).toBe("in_progress");
413
- });
414
- });
415
-
416
- // ─────────────────────────────────────────────────────────────────────────────
417
- // Error Handling Tests
418
- // ─────────────────────────────────────────────────────────────────────────────
419
-
420
- describe("error handling", () => {
421
- it("get_task should throw for non-existent task", async () => {
422
- const tools = provider.getTools();
423
- const getTask = tools.find((t) => t.name === "get_task")!;
424
-
425
- await expect(
426
- getTask.handler({ task_id: "task_invalid" })
427
- ).rejects.toThrow("Task not found");
428
- });
429
-
430
- it("assign_task should throw for non-existent task", async () => {
431
- const tools = provider.getTools();
432
- const assignTask = tools.find((t) => t.name === "assign_task")!;
433
-
434
- await expect(
435
- assignTask.handler({ task_id: "task_invalid" })
436
- ).rejects.toThrow();
437
- });
438
-
439
- it("complete_task should throw for non-existent task", async () => {
440
- const tools = provider.getTools();
441
- const completeTask = tools.find((t) => t.name === "complete_task")!;
442
-
443
- await expect(
444
- completeTask.handler({ task_id: "task_invalid" })
445
- ).rejects.toThrow();
446
- });
447
-
448
- it("add_blocker should throw for non-existent task", async () => {
449
- const tools = provider.getTools();
450
- const addBlocker = tools.find((t) => t.name === "add_blocker")!;
451
-
452
- await expect(
453
- addBlocker.handler({
454
- task_id: "task_invalid",
455
- blocker_id: "task_also_invalid",
456
- })
457
- ).rejects.toThrow();
458
- });
459
-
460
- it("remove_blocker should throw for non-existent task", async () => {
461
- const tools = provider.getTools();
462
- const removeBlocker = tools.find((t) => t.name === "remove_blocker")!;
463
-
464
- await expect(
465
- removeBlocker.handler({
466
- task_id: "task_invalid",
467
- blocker_id: "task_also_invalid",
468
- })
469
- ).rejects.toThrow();
470
- });
471
-
472
- it("get_task_blockers should throw for non-existent task", async () => {
473
- const tools = provider.getTools();
474
- const getBlockers = tools.find((t) => t.name === "get_task_blockers")!;
475
-
476
- await expect(
477
- getBlockers.handler({ task_id: "task_invalid" })
478
- ).rejects.toThrow();
479
- });
480
-
481
- it("update_task_status should throw for non-existent task", async () => {
482
- const tools = provider.getTools();
483
- const updateStatus = tools.find((t) => t.name === "update_task_status")!;
484
-
485
- await expect(
486
- updateStatus.handler({ task_id: "task_invalid", status: "in_progress" })
487
- ).rejects.toThrow();
488
- });
489
- });
490
-
491
- // ─────────────────────────────────────────────────────────────────────────────
492
- // Edge Case Tests
493
- // ─────────────────────────────────────────────────────────────────────────────
494
-
495
- describe("edge cases", () => {
496
- it("list_tasks should return empty array when no tasks exist", async () => {
497
- const tools = provider.getTools();
498
- const listTasks = tools.find((t) => t.name === "list_tasks")!;
499
-
500
- const result = (await listTasks.handler({})) as {
501
- tasks: Array<{ id: string }>;
502
- total: number;
503
- };
504
-
505
- expect(result.tasks).toEqual([]);
506
- expect(result.total).toBe(0);
507
- });
508
-
509
- it("list_ready_tasks should return empty array when no tasks exist", async () => {
510
- const tools = provider.getTools();
511
- const listReady = tools.find((t) => t.name === "list_ready_tasks")!;
512
-
513
- const result = (await listReady.handler({})) as {
514
- tasks: Array<{ id: string }>;
515
- total: number;
516
- };
517
-
518
- expect(result.tasks).toEqual([]);
519
- expect(result.total).toBe(0);
520
- });
521
-
522
- it("get_task_blockers should return empty array for unblocked task", async () => {
523
- const tools = provider.getTools();
524
- const createTask = tools.find((t) => t.name === "create_task")!;
525
- const getBlockers = tools.find((t) => t.name === "get_task_blockers")!;
526
-
527
- const task = (await createTask.handler({
528
- description: "Unblocked task",
529
- })) as { task_id: string };
530
-
531
- const result = (await getBlockers.handler({
532
- task_id: task.task_id,
533
- })) as {
534
- blockers: Array<{ id: string }>;
535
- isBlocked: boolean;
536
- };
537
-
538
- expect(result.blockers).toEqual([]);
539
- expect(result.isBlocked).toBe(false);
540
- });
541
-
542
- it("create_task should include created_at timestamp", async () => {
543
- const tools = provider.getTools();
544
- const createTask = tools.find((t) => t.name === "create_task")!;
545
-
546
- const result = (await createTask.handler({
547
- description: "Task with timestamp",
548
- })) as { task_id: string; created_at: number };
549
-
550
- expect(result.created_at).toBeDefined();
551
- expect(typeof result.created_at).toBe("number");
552
- });
553
-
554
- it("get_task should return all expected fields", async () => {
555
- const tools = provider.getTools();
556
- const createTask = tools.find((t) => t.name === "create_task")!;
557
- const getTask = tools.find((t) => t.name === "get_task")!;
558
-
559
- const created = (await createTask.handler({
560
- description: "Full details task",
561
- })) as { task_id: string };
562
-
563
- const result = (await getTask.handler({
564
- task_id: created.task_id,
565
- })) as Record<string, unknown>;
566
-
567
- expect(result).toHaveProperty("id");
568
- expect(result).toHaveProperty("description");
569
- expect(result).toHaveProperty("status");
570
- expect(result).toHaveProperty("isBlocked");
571
- expect(result).toHaveProperty("blockers");
572
- expect(result).toHaveProperty("created_at");
573
- });
574
-
575
- it("list_tasks should include isBlocked field", async () => {
576
- const tools = provider.getTools();
577
- const createTask = tools.find((t) => t.name === "create_task")!;
578
- const listTasks = tools.find((t) => t.name === "list_tasks")!;
579
-
580
- await createTask.handler({ description: "Task" });
581
-
582
- const result = (await listTasks.handler({ include_blocked: true })) as {
583
- tasks: Array<{ id: string; isBlocked: boolean }>;
584
- };
585
-
586
- expect(result.tasks[0]).toHaveProperty("isBlocked");
587
- });
588
- });
589
-
590
- // ─────────────────────────────────────────────────────────────────────────────
591
- // Complex Filter Tests
592
- // ─────────────────────────────────────────────────────────────────────────────
593
-
594
- describe("complex filtering", () => {
595
- it("list_tasks should filter by parent_task", async () => {
596
- const tools = provider.getTools();
597
- const createTask = tools.find((t) => t.name === "create_task")!;
598
- const listTasks = tools.find((t) => t.name === "list_tasks")!;
599
-
600
- const parent = (await createTask.handler({
601
- description: "Parent",
602
- })) as { task_id: string };
603
-
604
- await createTask.handler({
605
- description: "Child 1",
606
- parent_task: parent.task_id,
607
- });
608
- await createTask.handler({
609
- description: "Child 2",
610
- parent_task: parent.task_id,
611
- });
612
- await createTask.handler({ description: "Orphan" });
613
-
614
- const result = (await listTasks.handler({
615
- parent_task: parent.task_id,
616
- include_blocked: true,
617
- })) as {
618
- tasks: Array<{ id: string }>;
619
- total: number;
620
- };
621
-
622
- expect(result.total).toBe(2);
623
- });
624
-
625
- it("list_tasks should filter root_only tasks", async () => {
626
- const tools = provider.getTools();
627
- const createTask = tools.find((t) => t.name === "create_task")!;
628
- const listTasks = tools.find((t) => t.name === "list_tasks")!;
629
-
630
- const parent = (await createTask.handler({
631
- description: "Parent",
632
- })) as { task_id: string };
633
-
634
- await createTask.handler({
635
- description: "Child",
636
- parent_task: parent.task_id,
637
- });
638
-
639
- const result = (await listTasks.handler({
640
- root_only: true,
641
- include_blocked: true,
642
- })) as {
643
- tasks: Array<{ id: string }>;
644
- total: number;
645
- };
646
-
647
- expect(result.total).toBe(1);
648
- expect(result.tasks[0].id).toBe(parent.task_id);
649
- });
650
-
651
- it("list_tasks should filter by assigned_agent", async () => {
652
- const tools = provider.getTools();
653
- const createTask = tools.find((t) => t.name === "create_task")!;
654
- const assignTask = tools.find((t) => t.name === "assign_task")!;
655
- const listTasks = tools.find((t) => t.name === "list_tasks")!;
656
-
657
- const task1 = (await createTask.handler({
658
- description: "Task 1",
659
- })) as { task_id: string };
660
- const task2 = (await createTask.handler({
661
- description: "Task 2",
662
- })) as { task_id: string };
663
- await createTask.handler({ description: "Task 3" });
664
-
665
- await assignTask.handler({ task_id: task1.task_id, agent_id: "agent_a" });
666
- await assignTask.handler({ task_id: task2.task_id, agent_id: "agent_b" });
667
-
668
- const result = (await listTasks.handler({
669
- assigned_agent: "agent_a",
670
- include_blocked: true,
671
- })) as {
672
- tasks: Array<{ id: string }>;
673
- total: number;
674
- };
675
-
676
- expect(result.total).toBe(1);
677
- expect(result.tasks[0].id).toBe(task1.task_id);
678
- });
679
-
680
- it("list_ready_tasks should filter by assigned_agent", async () => {
681
- const tools = provider.getTools();
682
- const createTask = tools.find((t) => t.name === "create_task")!;
683
- const assignTask = tools.find((t) => t.name === "assign_task")!;
684
- const listReady = tools.find((t) => t.name === "list_ready_tasks")!;
685
-
686
- const task1 = (await createTask.handler({
687
- description: "Task 1",
688
- })) as { task_id: string };
689
- await createTask.handler({ description: "Task 2" });
690
-
691
- await assignTask.handler({ task_id: task1.task_id, agent_id: "agent_a" });
692
-
693
- const result = (await listReady.handler({
694
- assigned_agent: "agent_a",
695
- })) as {
696
- tasks: Array<{ id: string }>;
697
- total: number;
698
- };
699
-
700
- expect(result.total).toBe(1);
701
- expect(result.tasks[0].id).toBe(task1.task_id);
702
- });
703
- });
704
-
705
- // ─────────────────────────────────────────────────────────────────────────────
706
- // Context Switching Tests
707
- // ─────────────────────────────────────────────────────────────────────────────
708
-
709
- describe("context switching", () => {
710
- it("should use different agent contexts", async () => {
711
- let currentAgent = "agent_a";
712
- const dynamicContext = () => ({ agent_id: currentAgent });
713
- const dynamicProvider = createTaskToolProvider(backend, dynamicContext);
714
- const tools = dynamicProvider.getTools();
715
- const createTask = tools.find((t) => t.name === "create_task")!;
716
- const getTask = tools.find((t) => t.name === "get_task")!;
717
-
718
- // Create task as agent_a
719
- const task1 = (await createTask.handler({
720
- description: "Task by A",
721
- })) as { task_id: string };
722
-
723
- // Switch to agent_b
724
- currentAgent = "agent_b";
725
-
726
- // Create task as agent_b
727
- const task2 = (await createTask.handler({
728
- description: "Task by B",
729
- })) as { task_id: string };
730
-
731
- // Verify created_by is different
732
- const details1 = await backend.get(task1.task_id);
733
- const details2 = await backend.get(task2.task_id);
734
-
735
- expect(details1!.created_by).toBe("agent_a");
736
- expect(details2!.created_by).toBe("agent_b");
737
- });
738
-
739
- it("assign_task should default to current context agent", async () => {
740
- let currentAgent = "agent_context";
741
- const dynamicContext = () => ({ agent_id: currentAgent });
742
- const dynamicProvider = createTaskToolProvider(backend, dynamicContext);
743
- const tools = dynamicProvider.getTools();
744
- const createTask = tools.find((t) => t.name === "create_task")!;
745
- const assignTask = tools.find((t) => t.name === "assign_task")!;
746
-
747
- const task = (await createTask.handler({
748
- description: "Task",
749
- })) as { task_id: string };
750
-
751
- const result = (await assignTask.handler({
752
- task_id: task.task_id,
753
- })) as { assigned_agent: string };
754
-
755
- expect(result.assigned_agent).toBe("agent_context");
756
- });
757
- });
758
-
759
- // ─────────────────────────────────────────────────────────────────────────────
760
- // Full Workflow Integration Tests
761
- // ─────────────────────────────────────────────────────────────────────────────
762
-
763
- describe("full workflow integration", () => {
764
- it("should handle complete task lifecycle via tools", async () => {
765
- const tools = provider.getTools();
766
- const createTask = tools.find((t) => t.name === "create_task")!;
767
- const assignTask = tools.find((t) => t.name === "assign_task")!;
768
- const updateStatus = tools.find((t) => t.name === "update_task_status")!;
769
- const completeTask = tools.find((t) => t.name === "complete_task")!;
770
- const getTask = tools.find((t) => t.name === "get_task")!;
771
-
772
- // Create
773
- const task = (await createTask.handler({
774
- description: "Lifecycle test task",
775
- })) as { task_id: string };
776
-
777
- // Assign
778
- await assignTask.handler({ task_id: task.task_id });
779
-
780
- // Start
781
- await updateStatus.handler({
782
- task_id: task.task_id,
783
- status: "in_progress",
784
- });
785
-
786
- // Complete
787
- await completeTask.handler({
788
- task_id: task.task_id,
789
- summary: "Done!",
790
- });
791
-
792
- // Verify
793
- const details = (await getTask.handler({
794
- task_id: task.task_id,
795
- })) as { status: string; completed_at: number };
796
-
797
- expect(details.status).toBe("completed");
798
- expect(details.completed_at).toBeDefined();
799
- });
800
-
801
- it("should handle dependency-based workflow via tools", async () => {
802
- const tools = provider.getTools();
803
- const createTask = tools.find((t) => t.name === "create_task")!;
804
- const addBlocker = tools.find((t) => t.name === "add_blocker")!;
805
- const listReady = tools.find((t) => t.name === "list_ready_tasks")!;
806
- const updateStatus = tools.find((t) => t.name === "update_task_status")!;
807
- const completeTask = tools.find((t) => t.name === "complete_task")!;
808
-
809
- // Create tasks with dependencies
810
- const taskA = (await createTask.handler({
811
- description: "Task A (first)",
812
- })) as { task_id: string };
813
- const taskB = (await createTask.handler({
814
- description: "Task B (depends on A)",
815
- })) as { task_id: string };
816
- const taskC = (await createTask.handler({
817
- description: "Task C (depends on B)",
818
- })) as { task_id: string };
819
-
820
- await addBlocker.handler({
821
- task_id: taskB.task_id,
822
- blocker_id: taskA.task_id,
823
- });
824
- await addBlocker.handler({
825
- task_id: taskC.task_id,
826
- blocker_id: taskB.task_id,
827
- });
828
-
829
- // Only A should be ready
830
- let ready = (await listReady.handler({})) as {
831
- tasks: Array<{ id: string }>;
832
- };
833
- expect(ready.tasks).toHaveLength(1);
834
- expect(ready.tasks[0].id).toBe(taskA.task_id);
835
-
836
- // Complete A
837
- await updateStatus.handler({
838
- task_id: taskA.task_id,
839
- status: "in_progress",
840
- });
841
- await completeTask.handler({ task_id: taskA.task_id });
842
-
843
- // Now B should be ready
844
- ready = (await listReady.handler({})) as {
845
- tasks: Array<{ id: string }>;
846
- };
847
- expect(ready.tasks).toHaveLength(1);
848
- expect(ready.tasks[0].id).toBe(taskB.task_id);
849
-
850
- // Complete B
851
- await updateStatus.handler({
852
- task_id: taskB.task_id,
853
- status: "in_progress",
854
- });
855
- await completeTask.handler({ task_id: taskB.task_id });
856
-
857
- // Now C should be ready
858
- ready = (await listReady.handler({})) as {
859
- tasks: Array<{ id: string }>;
860
- };
861
- expect(ready.tasks).toHaveLength(1);
862
- expect(ready.tasks[0].id).toBe(taskC.task_id);
863
- });
864
-
865
- it("should handle parent-child task relationships via tools", async () => {
866
- const tools = provider.getTools();
867
- const createTask = tools.find((t) => t.name === "create_task")!;
868
- const listTasks = tools.find((t) => t.name === "list_tasks")!;
869
- const getTask = tools.find((t) => t.name === "get_task")!;
870
-
871
- // Create parent
872
- const parent = (await createTask.handler({
873
- description: "Parent task",
874
- })) as { task_id: string };
875
-
876
- // Create children
877
- const child1 = (await createTask.handler({
878
- description: "Child 1",
879
- parent_task: parent.task_id,
880
- })) as { task_id: string };
881
- const child2 = (await createTask.handler({
882
- description: "Child 2",
883
- parent_task: parent.task_id,
884
- })) as { task_id: string };
885
-
886
- // Verify parent-child relationships
887
- const parentDetails = (await getTask.handler({
888
- task_id: parent.task_id,
889
- })) as { id: string };
890
- const child1Details = (await getTask.handler({
891
- task_id: child1.task_id,
892
- })) as { parent_task: string };
893
- const child2Details = (await getTask.handler({
894
- task_id: child2.task_id,
895
- })) as { parent_task: string };
896
-
897
- expect(child1Details.parent_task).toBe(parent.task_id);
898
- expect(child2Details.parent_task).toBe(parent.task_id);
899
-
900
- // Filter by parent
901
- const children = (await listTasks.handler({
902
- parent_task: parent.task_id,
903
- include_blocked: true,
904
- })) as {
905
- tasks: Array<{ id: string }>;
906
- total: number;
907
- };
908
-
909
- expect(children.total).toBe(2);
910
- });
911
-
912
- it("should handle blocker completion unblocking workflow", async () => {
913
- const tools = provider.getTools();
914
- const createTask = tools.find((t) => t.name === "create_task")!;
915
- const addBlocker = tools.find((t) => t.name === "add_blocker")!;
916
- const getBlockers = tools.find((t) => t.name === "get_task_blockers")!;
917
- const updateStatus = tools.find((t) => t.name === "update_task_status")!;
918
- const completeTask = tools.find((t) => t.name === "complete_task")!;
919
-
920
- const blocker = (await createTask.handler({
921
- description: "Blocker",
922
- })) as { task_id: string };
923
- const blocked = (await createTask.handler({
924
- description: "Blocked",
925
- })) as { task_id: string };
926
-
927
- await addBlocker.handler({
928
- task_id: blocked.task_id,
929
- blocker_id: blocker.task_id,
930
- });
931
-
932
- // Initially blocked
933
- let blockerStatus = (await getBlockers.handler({
934
- task_id: blocked.task_id,
935
- })) as { isBlocked: boolean; blockers: Array<{ isCompleted: boolean }> };
936
-
937
- expect(blockerStatus.isBlocked).toBe(true);
938
- expect(blockerStatus.blockers[0].isCompleted).toBe(false);
939
-
940
- // Complete blocker
941
- await updateStatus.handler({
942
- task_id: blocker.task_id,
943
- status: "in_progress",
944
- });
945
- await completeTask.handler({ task_id: blocker.task_id });
946
-
947
- // Now unblocked
948
- blockerStatus = (await getBlockers.handler({
949
- task_id: blocked.task_id,
950
- })) as { isBlocked: boolean; blockers: Array<{ isCompleted: boolean }> };
951
-
952
- expect(blockerStatus.isBlocked).toBe(false);
953
- expect(blockerStatus.blockers[0].isCompleted).toBe(true);
954
- });
955
- });
956
-
957
- // ─────────────────────────────────────────────────────────────────────────────
958
- // Schema Validation Tests
959
- // ─────────────────────────────────────────────────────────────────────────────
960
-
961
- describe("schema structure", () => {
962
- it("each tool should have proper JSON schema structure", () => {
963
- const tools = provider.getTools();
964
-
965
- for (const tool of tools) {
966
- expect(tool.schema).toHaveProperty("type");
967
- expect(tool.schema.type).toBe("object");
968
- expect(tool.schema).toHaveProperty("properties");
969
- }
970
- });
971
-
972
- it("required fields should be specified in schema", () => {
973
- const tools = provider.getTools();
974
-
975
- const toolsWithRequired = ["create_task", "get_task", "get_task_blockers"];
976
-
977
- for (const toolName of toolsWithRequired) {
978
- const tool = tools.find((t) => t.name === toolName)!;
979
- expect(tool.schema).toHaveProperty("required");
980
- }
981
- });
982
- });
983
- });