macro-agent 0.0.17 → 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 (338) 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 +17 -0
  6. package/dist/acp/macro-agent.d.ts.map +1 -1
  7. package/dist/acp/macro-agent.js +183 -55
  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 +23 -0
  53. package/dist/map/adapter/acp-over-map.d.ts.map +1 -1
  54. package/dist/map/adapter/acp-over-map.js +482 -55
  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 +4 -0
  95. package/dist/map/adapter/map-adapter.d.ts.map +1 -1
  96. package/dist/map/adapter/map-adapter.js +302 -30
  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 +7 -1
  131. package/dist/store/event-store.d.ts.map +1 -1
  132. package/dist/store/event-store.js +91 -8
  133. package/dist/store/event-store.js.map +1 -1
  134. package/dist/store/types/agents.d.ts +23 -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__/history.test.ts +8 -4
  197. package/src/acp/__tests__/integration.test.ts +56 -31
  198. package/src/acp/__tests__/macro-agent.test.ts +16 -7
  199. package/src/acp/macro-agent.ts +230 -62
  200. package/src/acp/types.ts +46 -1
  201. package/src/agent/__tests__/agent-manager.test.ts +228 -2
  202. package/src/agent/agent-manager.ts +714 -261
  203. package/src/agent/types.ts +3 -1
  204. package/src/api/server.ts +41 -7
  205. package/src/auth/__tests__/token.test.ts +100 -0
  206. package/src/auth/index.ts +1 -0
  207. package/src/auth/token.ts +82 -0
  208. package/src/cli/__tests__/acp.test.ts +1 -1
  209. package/src/cli/__tests__/stable-instance-id.test.ts +1 -1
  210. package/src/cli/acp.ts +130 -72
  211. package/src/cli/index.ts +120 -14
  212. package/src/cli/mcp.ts +311 -207
  213. package/src/cli/parse-args.ts +54 -0
  214. package/src/cli/stable-instance-id.ts +14 -0
  215. package/src/config/project-config.ts +190 -27
  216. package/src/lifecycle/__tests__/cascade-termination.test.ts +1 -1
  217. package/src/map/adapter/__tests__/acp-over-map-cancel.test.ts +820 -0
  218. package/src/map/adapter/__tests__/acp-over-map-getmodels.test.ts +355 -0
  219. package/src/map/adapter/__tests__/acp-over-map-history.test.ts +724 -2
  220. package/src/map/adapter/__tests__/acp-over-map-persistence.e2e.test.ts +1 -1
  221. package/src/map/adapter/__tests__/event-broadcast.test.ts +420 -0
  222. package/src/map/adapter/__tests__/event-log.test.ts +527 -0
  223. package/src/map/adapter/__tests__/event-translator.test.ts +3 -3
  224. package/src/map/adapter/__tests__/extensions.test.ts +408 -0
  225. package/src/map/adapter/__tests__/map-adapter.test.ts +99 -0
  226. package/src/map/adapter/__tests__/mcp-bridge.test.ts +1187 -0
  227. package/src/map/adapter/__tests__/multi-client-broadcast.test.ts +711 -0
  228. package/src/map/adapter/__tests__/websocket-integration.test.ts +218 -0
  229. package/src/map/adapter/acp-over-map.ts +777 -92
  230. package/src/map/adapter/connection-manager.ts +3 -0
  231. package/src/map/adapter/event-log.ts +208 -0
  232. package/src/map/adapter/event-translator.ts +6 -6
  233. package/src/map/adapter/extensions/agent-lifecycle.ts +267 -0
  234. package/src/map/adapter/extensions/index.ts +60 -0
  235. package/src/map/adapter/extensions/mcp-bridge.ts +995 -0
  236. package/src/map/adapter/extensions/task.ts +11 -0
  237. package/src/map/adapter/extensions/update-metadata.ts +126 -0
  238. package/src/map/adapter/index.ts +28 -0
  239. package/src/map/adapter/interface.ts +2 -0
  240. package/src/map/adapter/map-adapter.ts +373 -38
  241. package/src/map/adapter/subscription-manager.ts +5 -1
  242. package/src/map/adapter/types.ts +2 -0
  243. package/src/mcp/__tests__/map-client.test.ts +386 -0
  244. package/src/mcp/__tests__/mcp-server-thin-client.test.ts +368 -0
  245. package/src/mcp/__tests__/mcp-server.test.ts +100 -1
  246. package/src/mcp/map-client.ts +177 -0
  247. package/src/mcp/mcp-server.ts +191 -100
  248. package/src/mcp/types.ts +6 -1
  249. package/src/metrics/metrics.ts +1 -1
  250. package/src/monitor/__tests__/stale-agent-flow.integration.test.ts +1 -1
  251. package/src/roles/__tests__/config-loader.test.ts +7 -7
  252. package/src/roles/capabilities.ts +17 -7
  253. package/src/roles/config-loader.ts +6 -6
  254. package/src/roles/registry.ts +2 -2
  255. package/src/server/__tests__/combined-server.test.ts +94 -21
  256. package/src/server/combined-server.ts +189 -33
  257. package/src/steering/__tests__/steering-integration.test.ts +1 -1
  258. package/src/store/__tests__/event-store.test.ts +236 -1
  259. package/src/store/__tests__/instance.test.ts +3 -3
  260. package/src/store/event-store.ts +109 -8
  261. package/src/store/types/agents.ts +16 -0
  262. package/src/store/types/events.ts +1 -1
  263. package/src/task/backend/__tests__/create-task-backend.test.ts +225 -0
  264. package/src/task/backend/__tests__/e2e/unified-tool-provider-opentasks.e2e.test.ts +524 -0
  265. package/src/task/backend/__tests__/unified-tool-provider.test.ts +579 -0
  266. package/src/task/backend/index.ts +156 -106
  267. package/src/task/backend/memory.ts +4 -0
  268. package/src/task/backend/opentasks/__tests__/backend.test.ts +968 -0
  269. package/src/task/backend/opentasks/__tests__/daemon-manager.test.ts +406 -0
  270. package/src/task/backend/opentasks/__tests__/mapping.test.ts +84 -0
  271. package/src/task/backend/opentasks/__tests__/opentasks-backend.e2e.test.ts +1338 -0
  272. package/src/task/backend/opentasks/backend.ts +1323 -0
  273. package/src/task/backend/opentasks/client.ts +652 -0
  274. package/src/task/backend/opentasks/daemon-manager.ts +253 -0
  275. package/src/task/backend/opentasks/index.ts +69 -0
  276. package/src/task/backend/opentasks/mapping.ts +94 -0
  277. package/src/task/backend/types.ts +42 -66
  278. package/src/task/backend/unified-tool-provider.ts +779 -0
  279. package/src/teams/__tests__/cross-subsystem.integration.test.ts +1 -1
  280. package/src/teams/team-loader.ts +3 -3
  281. package/src/teams/team-runtime.ts +2 -0
  282. package/test_fixtures/README.md +2 -3
  283. package/test_fixtures/fixtures/index.ts +0 -3
  284. package/test_fixtures/fixtures/projects/project-with-specs.ts +7 -149
  285. package/test_fixtures/fixtures/repos/index.ts +1 -3
  286. package/test_fixtures/fixtures/repos/temp-repo-factory.ts +0 -116
  287. package/test_fixtures/fixtures/repos/types.ts +0 -11
  288. package/test_fixtures/harness/__tests__/fixtures.test.ts +10 -102
  289. package/test_fixtures/harness/__tests__/temp-repo-and-simulator.test.ts +0 -33
  290. package/test_fixtures/harness/simulator/agent-simulator.ts +4 -4
  291. package/vitest.config.ts +1 -1
  292. package/vitest.e2e.config.ts +1 -1
  293. package/vitest.setup.ts +1 -30
  294. package/.macro-agent/teams/self-driving/prompts/grinder.md +0 -27
  295. package/.macro-agent/teams/self-driving/prompts/judge.md +0 -27
  296. package/.macro-agent/teams/self-driving/prompts/planner.md +0 -33
  297. package/.macro-agent/teams/self-driving/roles/grinder.yaml +0 -17
  298. package/.macro-agent/teams/self-driving/roles/judge.yaml +0 -24
  299. package/.macro-agent/teams/self-driving/roles/planner.yaml +0 -18
  300. package/.macro-agent/teams/self-driving/team.yaml +0 -103
  301. package/.macro-agent/teams/structured/prompts/developer.md +0 -26
  302. package/.macro-agent/teams/structured/prompts/lead.md +0 -25
  303. package/.macro-agent/teams/structured/prompts/reviewer.md +0 -24
  304. package/.macro-agent/teams/structured/roles/developer.yaml +0 -12
  305. package/.macro-agent/teams/structured/roles/lead.yaml +0 -11
  306. package/.macro-agent/teams/structured/roles/reviewer.yaml +0 -19
  307. package/.macro-agent/teams/structured/team.yaml +0 -89
  308. package/docs/sudocode-integration.md +0 -383
  309. package/src/task/backend/__tests__/backend-parity.test.ts +0 -451
  310. package/src/task/backend/__tests__/tool-provider-edge-cases.test.ts +0 -430
  311. package/src/task/backend/__tests__/tool-provider.test.ts +0 -983
  312. package/src/task/backend/sudocode/__tests__/backend-edge-cases.test.ts +0 -575
  313. package/src/task/backend/sudocode/__tests__/backend.test.ts +0 -1194
  314. package/src/task/backend/sudocode/__tests__/client-integration.test.ts +0 -418
  315. package/src/task/backend/sudocode/__tests__/client.test.ts +0 -345
  316. package/src/task/backend/sudocode/__tests__/e2e/backend.e2e.test.ts +0 -753
  317. package/src/task/backend/sudocode/__tests__/e2e/server-client.e2e.test.ts +0 -680
  318. package/src/task/backend/sudocode/__tests__/e2e-workflow.test.ts +0 -666
  319. package/src/task/backend/sudocode/__tests__/integration/standalone-client.integration.test.ts +0 -396
  320. package/src/task/backend/sudocode/__tests__/integration/sudocode-cli.integration.test.ts +0 -328
  321. package/src/task/backend/sudocode/__tests__/integration/test-utils.ts +0 -175
  322. package/src/task/backend/sudocode/__tests__/mapping-edge-cases.test.ts +0 -265
  323. package/src/task/backend/sudocode/__tests__/server-client.test.ts +0 -675
  324. package/src/task/backend/sudocode/__tests__/sync-policy-edge-cases.test.ts +0 -521
  325. package/src/task/backend/sudocode/__tests__/sync-policy.test.ts +0 -519
  326. package/src/task/backend/sudocode/__tests__/tools.test.ts +0 -471
  327. package/src/task/backend/sudocode/backend.ts +0 -1237
  328. package/src/task/backend/sudocode/client.ts +0 -515
  329. package/src/task/backend/sudocode/index.ts +0 -120
  330. package/src/task/backend/sudocode/mapping.ts +0 -93
  331. package/src/task/backend/sudocode/server-client.ts +0 -522
  332. package/src/task/backend/sudocode/standalone-client.ts +0 -623
  333. package/src/task/backend/sudocode/sync-policy.ts +0 -387
  334. package/src/task/backend/sudocode/tools.ts +0 -896
  335. package/src/task/backend/tool-provider.ts +0 -506
  336. package/test_fixtures/fixtures/sudocode/index.ts +0 -29
  337. package/test_fixtures/fixtures/sudocode/issues.ts +0 -185
  338. 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
- });