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,451 +0,0 @@
1
- /**
2
- * Backend Parity Tests
3
- *
4
- * Tests that verify InMemoryTaskBackend and SudocodeTaskBackend produce
5
- * identical results for the same operations. This ensures the pluggable
6
- * backend contract is maintained.
7
- *
8
- * @module task/backend/__tests__/backend-parity.test
9
- * @see s-8472 Pluggable Task Backend Integration
10
- * @see s-1zcx Multi-Agent Orchestration Testing Strategy
11
- */
12
-
13
- import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
14
- import { createEventStore, type EventStore } from "../../../store/event-store.js";
15
- import { InMemoryTaskBackend, createInMemoryTaskBackend } from "../memory.js";
16
- import {
17
- SudocodeTaskBackend,
18
- createSudocodeTaskBackend,
19
- } from "../sudocode/backend.js";
20
- import type { SudocodeClient, Issue, IssueChangeCallback } from "../sudocode/client.js";
21
- import type { TaskBackend, ExtendedTask } from "../types.js";
22
-
23
- // Mock SudocodeClient that mimics InMemory behavior
24
- function createMinimalMockClient(): SudocodeClient {
25
- const issues = new Map<string, Issue>();
26
- const issueBlockers = new Map<string, Issue[]>();
27
- const issueBlocking = new Map<string, Issue[]>();
28
- const issueChangeCallbacks: IssueChangeCallback[] = [];
29
-
30
- return {
31
- getIssue: vi.fn(async (id: string) => issues.get(id) ?? null),
32
- createIssue: vi.fn(async (data: Partial<Issue>) => {
33
- const id = `i-${Date.now()}`;
34
- const issue: Issue = {
35
- id,
36
- uuid: `uuid-${id}`,
37
- title: data.title ?? "Test Issue",
38
- status: data.status ?? "open",
39
- priority: data.priority ?? 2,
40
- created_at: new Date().toISOString(),
41
- updated_at: new Date().toISOString(),
42
- };
43
- issues.set(id, issue);
44
- return issue;
45
- }),
46
- updateIssue: vi.fn(async (id: string, updates: Partial<Issue>) => {
47
- const issue = issues.get(id);
48
- if (!issue) throw new Error(`Issue not found: ${id}`);
49
- Object.assign(issue, updates);
50
- issue.updated_at = new Date().toISOString();
51
- return issue;
52
- }),
53
- getReadyIssues: vi.fn(async () => {
54
- return Array.from(issues.values()).filter((i) => {
55
- const blockers = issueBlockers.get(i.id) ?? [];
56
- return blockers.every((b) => b.status === "closed");
57
- });
58
- }),
59
- getBlockers: vi.fn(async (id: string) => issueBlockers.get(id) ?? []),
60
- getBlocking: vi.fn(async (id: string) => issueBlocking.get(id) ?? []),
61
- createLink: vi.fn(async (from: string, to: string, type: string) => {
62
- if (type === "blocks") {
63
- const fromIssue = issues.get(from);
64
- if (!fromIssue) throw new Error(`Issue not found: ${from}`);
65
- const existing = issueBlockers.get(to) ?? [];
66
- issueBlockers.set(to, [...existing, fromIssue]);
67
- const blocking = issueBlocking.get(from) ?? [];
68
- issueBlocking.set(from, [...blocking, issues.get(to)!]);
69
- }
70
- }),
71
- removeLink: vi.fn(async (from: string, to: string, type: string) => {
72
- if (type === "blocks") {
73
- const blockers = issueBlockers.get(to) ?? [];
74
- issueBlockers.set(to, blockers.filter((b) => b.id !== from));
75
- const blocking = issueBlocking.get(from) ?? [];
76
- issueBlocking.set(from, blocking.filter((b) => b.id !== to));
77
- }
78
- }),
79
- onIssueChange: vi.fn((callback: IssueChangeCallback) => {
80
- issueChangeCallbacks.push(callback);
81
- return () => {
82
- const idx = issueChangeCallbacks.indexOf(callback);
83
- if (idx >= 0) issueChangeCallbacks.splice(idx, 1);
84
- };
85
- }),
86
- close: vi.fn(),
87
- isReady: vi.fn(() => true),
88
- } as unknown as SudocodeClient;
89
- }
90
-
91
- describe("Backend Parity", () => {
92
- let memoryEventStore: EventStore;
93
- let sudocodeEventStore: EventStore;
94
- let memoryBackend: InMemoryTaskBackend;
95
- let sudocodeBackend: SudocodeTaskBackend;
96
- let mockClient: SudocodeClient;
97
- const testAgentId = "agent_test";
98
-
99
- beforeEach(async () => {
100
- memoryEventStore = await createEventStore({ inMemory: true });
101
- sudocodeEventStore = await createEventStore({ inMemory: true });
102
- mockClient = createMinimalMockClient();
103
-
104
- memoryBackend = createInMemoryTaskBackend(memoryEventStore);
105
- sudocodeBackend = createSudocodeTaskBackend(
106
- sudocodeEventStore,
107
- mockClient,
108
- { syncStatus: false }
109
- );
110
- });
111
-
112
- afterEach(async () => {
113
- sudocodeBackend.close();
114
- await memoryEventStore.close();
115
- await sudocodeEventStore.close();
116
- });
117
-
118
- /**
119
- * Run the same operation on both backends and compare results
120
- */
121
- async function runOnBoth<T>(
122
- operation: (backend: TaskBackend) => Promise<T>
123
- ): Promise<{ memory: T; sudocode: T }> {
124
- const [memory, sudocode] = await Promise.all([
125
- operation(memoryBackend),
126
- operation(sudocodeBackend),
127
- ]);
128
- return { memory, sudocode };
129
- }
130
-
131
- /**
132
- * Compare task objects, ignoring timestamps and IDs
133
- */
134
- function compareTasks(a: ExtendedTask | null, b: ExtendedTask | null): void {
135
- if (!a && !b) return;
136
- expect(a).not.toBeNull();
137
- expect(b).not.toBeNull();
138
- if (!a || !b) return;
139
-
140
- expect(a.description).toBe(b.description);
141
- expect(a.status).toBe(b.status);
142
- expect(a.isBlocked).toBe(b.isBlocked);
143
- expect(a.assigned_agent).toBe(b.assigned_agent);
144
- }
145
-
146
- describe("create operations", () => {
147
- it("should produce tasks with same fields", async () => {
148
- const memoryTask = await memoryBackend.create({
149
- description: "Test task",
150
- created_by: testAgentId,
151
- });
152
- const sudocodeTask = await sudocodeBackend.create({
153
- description: "Test task",
154
- created_by: testAgentId,
155
- });
156
-
157
- compareTasks(memoryTask, sudocodeTask);
158
- });
159
-
160
- it("should handle subtasks the same way", async () => {
161
- const memParent = await memoryBackend.create({
162
- description: "Parent",
163
- created_by: testAgentId,
164
- });
165
- const sudParent = await sudocodeBackend.create({
166
- description: "Parent",
167
- created_by: testAgentId,
168
- });
169
-
170
- const memChild = await memoryBackend.createSubtask(memParent.id, {
171
- description: "Child",
172
- created_by: testAgentId,
173
- });
174
- const sudChild = await sudocodeBackend.createSubtask(sudParent.id, {
175
- description: "Child",
176
- created_by: testAgentId,
177
- });
178
-
179
- expect(memChild.parent_task).toBe(memParent.id);
180
- expect(sudChild.parent_task).toBe(sudParent.id);
181
- });
182
- });
183
-
184
- describe("status transitions", () => {
185
- it("should follow same transition rules", async () => {
186
- // Create tasks
187
- const memTask = await memoryBackend.create({
188
- description: "Test",
189
- created_by: testAgentId,
190
- });
191
- const sudTask = await sudocodeBackend.create({
192
- description: "Test",
193
- created_by: testAgentId,
194
- });
195
-
196
- // Assign
197
- await memoryBackend.assign(memTask.id, "agent_a");
198
- await sudocodeBackend.assign(sudTask.id, "agent_a");
199
-
200
- const memAfterAssign = await memoryBackend.get(memTask.id);
201
- const sudAfterAssign = await sudocodeBackend.get(sudTask.id);
202
- compareTasks(memAfterAssign, sudAfterAssign);
203
-
204
- // Start
205
- await memoryBackend.start(memTask.id);
206
- await sudocodeBackend.start(sudTask.id);
207
-
208
- const memAfterStart = await memoryBackend.get(memTask.id);
209
- const sudAfterStart = await sudocodeBackend.get(sudTask.id);
210
- compareTasks(memAfterStart, sudAfterStart);
211
-
212
- // Complete
213
- await memoryBackend.complete(memTask.id);
214
- await sudocodeBackend.complete(sudTask.id);
215
-
216
- const memAfterComplete = await memoryBackend.get(memTask.id);
217
- const sudAfterComplete = await sudocodeBackend.get(sudTask.id);
218
- compareTasks(memAfterComplete, sudAfterComplete);
219
- });
220
-
221
- it("should reject same invalid transitions", async () => {
222
- // Create and complete a task
223
- const memTask = await memoryBackend.create({
224
- description: "Test",
225
- created_by: testAgentId,
226
- });
227
- const sudTask = await sudocodeBackend.create({
228
- description: "Test",
229
- created_by: testAgentId,
230
- });
231
-
232
- await memoryBackend.start(memTask.id);
233
- await sudocodeBackend.start(sudTask.id);
234
- await memoryBackend.complete(memTask.id);
235
- await sudocodeBackend.complete(sudTask.id);
236
-
237
- // Try to start completed task - both should reject
238
- await expect(memoryBackend.start(memTask.id)).rejects.toThrow(
239
- "Invalid status transition"
240
- );
241
- await expect(sudocodeBackend.start(sudTask.id)).rejects.toThrow(
242
- "Invalid status transition"
243
- );
244
- });
245
- });
246
-
247
- describe("delete behavior", () => {
248
- it("both backends throw 'not supported' on delete", async () => {
249
- const memTask = await memoryBackend.create({
250
- description: "Test",
251
- created_by: testAgentId,
252
- });
253
- const sudTask = await sudocodeBackend.create({
254
- description: "Test",
255
- created_by: testAgentId,
256
- });
257
-
258
- // Both backends should throw "not supported"
259
- await expect(memoryBackend.delete(memTask.id)).rejects.toThrow(
260
- "not supported"
261
- );
262
- await expect(sudocodeBackend.delete(sudTask.id)).rejects.toThrow(
263
- "not supported"
264
- );
265
- });
266
-
267
- it("delete throws for all task states", async () => {
268
- // Test that delete throws regardless of task state
269
- const pendingTask = await sudocodeBackend.create({
270
- description: "Pending",
271
- created_by: testAgentId,
272
- });
273
-
274
- const completedTask = await sudocodeBackend.create({
275
- description: "Completed",
276
- created_by: testAgentId,
277
- });
278
- await sudocodeBackend.start(completedTask.id);
279
- await sudocodeBackend.complete(completedTask.id);
280
-
281
- // Both should throw
282
- await expect(sudocodeBackend.delete(pendingTask.id)).rejects.toThrow(
283
- "not supported"
284
- );
285
- await expect(sudocodeBackend.delete(completedTask.id)).rejects.toThrow(
286
- "not supported"
287
- );
288
- });
289
- });
290
-
291
- describe("blocker operations", () => {
292
- it("should handle blockers identically", async () => {
293
- const memBlocker = await memoryBackend.create({
294
- description: "Blocker",
295
- created_by: testAgentId,
296
- });
297
- const memBlocked = await memoryBackend.create({
298
- description: "Blocked",
299
- created_by: testAgentId,
300
- });
301
-
302
- const sudBlocker = await sudocodeBackend.create({
303
- description: "Blocker",
304
- created_by: testAgentId,
305
- });
306
- const sudBlocked = await sudocodeBackend.create({
307
- description: "Blocked",
308
- created_by: testAgentId,
309
- });
310
-
311
- await memoryBackend.addBlocker(memBlocked.id, memBlocker.id);
312
- await sudocodeBackend.addBlocker(sudBlocked.id, sudBlocker.id);
313
-
314
- const memBlockedTask = await memoryBackend.get(memBlocked.id);
315
- const sudBlockedTask = await sudocodeBackend.get(sudBlocked.id);
316
-
317
- expect(memBlockedTask?.isBlocked).toBe(true);
318
- expect(sudBlockedTask?.isBlocked).toBe(true);
319
- });
320
-
321
- it("should unblock when blocker completes", async () => {
322
- const memBlocker = await memoryBackend.create({
323
- description: "Blocker",
324
- created_by: testAgentId,
325
- });
326
- const memBlocked = await memoryBackend.create({
327
- description: "Blocked",
328
- created_by: testAgentId,
329
- });
330
-
331
- const sudBlocker = await sudocodeBackend.create({
332
- description: "Blocker",
333
- created_by: testAgentId,
334
- });
335
- const sudBlocked = await sudocodeBackend.create({
336
- description: "Blocked",
337
- created_by: testAgentId,
338
- });
339
-
340
- await memoryBackend.addBlocker(memBlocked.id, memBlocker.id);
341
- await sudocodeBackend.addBlocker(sudBlocked.id, sudBlocker.id);
342
-
343
- // Complete blockers
344
- await memoryBackend.start(memBlocker.id);
345
- await memoryBackend.complete(memBlocker.id);
346
- await sudocodeBackend.start(sudBlocker.id);
347
- await sudocodeBackend.complete(sudBlocker.id);
348
-
349
- const memBlockedTask = await memoryBackend.get(memBlocked.id);
350
- const sudBlockedTask = await sudocodeBackend.get(sudBlocked.id);
351
-
352
- expect(memBlockedTask?.isBlocked).toBe(false);
353
- expect(sudBlockedTask?.isBlocked).toBe(false);
354
- });
355
- });
356
-
357
- describe("listReady operations", () => {
358
- it("should return same tasks for identical blocker configurations", async () => {
359
- // Create tasks in both backends
360
- const memReady = await memoryBackend.create({
361
- description: "Ready",
362
- created_by: testAgentId,
363
- });
364
- const memBlocker = await memoryBackend.create({
365
- description: "Blocker",
366
- created_by: testAgentId,
367
- });
368
- const memBlocked = await memoryBackend.create({
369
- description: "Blocked",
370
- created_by: testAgentId,
371
- });
372
-
373
- const sudReady = await sudocodeBackend.create({
374
- description: "Ready",
375
- created_by: testAgentId,
376
- });
377
- const sudBlocker = await sudocodeBackend.create({
378
- description: "Blocker",
379
- created_by: testAgentId,
380
- });
381
- const sudBlocked = await sudocodeBackend.create({
382
- description: "Blocked",
383
- created_by: testAgentId,
384
- });
385
-
386
- await memoryBackend.addBlocker(memBlocked.id, memBlocker.id);
387
- await sudocodeBackend.addBlocker(sudBlocked.id, sudBlocker.id);
388
-
389
- const memReadyTasks = await memoryBackend.listReady();
390
- const sudReadyTasks = await sudocodeBackend.listReady();
391
-
392
- // Should have same number of ready tasks
393
- expect(memReadyTasks.length).toBe(sudReadyTasks.length);
394
-
395
- // Ready and Blocker should be in ready lists
396
- expect(memReadyTasks.map((t) => t.description)).toContain("Ready");
397
- expect(memReadyTasks.map((t) => t.description)).toContain("Blocker");
398
- expect(memReadyTasks.map((t) => t.description)).not.toContain("Blocked");
399
-
400
- expect(sudReadyTasks.map((t) => t.description)).toContain("Ready");
401
- expect(sudReadyTasks.map((t) => t.description)).toContain("Blocker");
402
- expect(sudReadyTasks.map((t) => t.description)).not.toContain("Blocked");
403
- });
404
- });
405
-
406
- describe("subtask status operations", () => {
407
- it("should aggregate subtask status identically", async () => {
408
- const memParent = await memoryBackend.create({
409
- description: "Parent",
410
- created_by: testAgentId,
411
- });
412
- const sudParent = await sudocodeBackend.create({
413
- description: "Parent",
414
- created_by: testAgentId,
415
- });
416
-
417
- // Create subtasks
418
- const memChild1 = await memoryBackend.createSubtask(memParent.id, {
419
- description: "Child 1",
420
- created_by: testAgentId,
421
- });
422
- const memChild2 = await memoryBackend.createSubtask(memParent.id, {
423
- description: "Child 2",
424
- created_by: testAgentId,
425
- });
426
-
427
- const sudChild1 = await sudocodeBackend.createSubtask(sudParent.id, {
428
- description: "Child 1",
429
- created_by: testAgentId,
430
- });
431
- const sudChild2 = await sudocodeBackend.createSubtask(sudParent.id, {
432
- description: "Child 2",
433
- created_by: testAgentId,
434
- });
435
-
436
- // Complete one child
437
- await memoryBackend.start(memChild1.id);
438
- await memoryBackend.complete(memChild1.id);
439
- await sudocodeBackend.start(sudChild1.id);
440
- await sudocodeBackend.complete(sudChild1.id);
441
-
442
- const memStatus = await memoryBackend.getSubtaskStatus(memParent.id);
443
- const sudStatus = await sudocodeBackend.getSubtaskStatus(sudParent.id);
444
-
445
- expect(memStatus.total).toBe(sudStatus.total);
446
- expect(memStatus.completed).toBe(sudStatus.completed);
447
- expect(memStatus.pending).toBe(sudStatus.pending);
448
- expect(memStatus.allCompleted).toBe(sudStatus.allCompleted);
449
- });
450
- });
451
- });