macro-agent 0.1.0 → 0.1.2

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 (660) hide show
  1. package/.claude/settings.local.json +3 -1
  2. package/.sudocode/issues.jsonl +28 -0
  3. package/.sudocode/specs.jsonl +8 -0
  4. package/CLAUDE.md +25 -17
  5. package/README.md +11 -29
  6. package/dist/acp/macro-agent.d.ts +15 -0
  7. package/dist/acp/macro-agent.d.ts.map +1 -1
  8. package/dist/acp/macro-agent.js +131 -35
  9. package/dist/acp/macro-agent.js.map +1 -1
  10. package/dist/acp/types.d.ts +32 -1
  11. package/dist/acp/types.d.ts.map +1 -1
  12. package/dist/acp/types.js.map +1 -1
  13. package/dist/agent/agent-manager.d.ts +65 -1
  14. package/dist/agent/agent-manager.d.ts.map +1 -1
  15. package/dist/agent/agent-manager.js +544 -200
  16. package/dist/agent/agent-manager.js.map +1 -1
  17. package/dist/agent/types.d.ts +8 -1
  18. package/dist/agent/types.d.ts.map +1 -1
  19. package/dist/agent/types.js.map +1 -1
  20. package/dist/api/server.d.ts +8 -1
  21. package/dist/api/server.d.ts.map +1 -1
  22. package/dist/api/server.js +136 -8
  23. package/dist/api/server.js.map +1 -1
  24. package/dist/api/types.d.ts +1 -1
  25. package/dist/api/types.d.ts.map +1 -1
  26. package/dist/auth/index.d.ts +2 -0
  27. package/dist/auth/index.d.ts.map +1 -0
  28. package/dist/auth/index.js +2 -0
  29. package/dist/auth/index.js.map +1 -0
  30. package/dist/auth/token.d.ts +41 -0
  31. package/dist/auth/token.d.ts.map +1 -0
  32. package/dist/auth/token.js +73 -0
  33. package/dist/auth/token.js.map +1 -0
  34. package/dist/cli/acp.d.ts +2 -23
  35. package/dist/cli/acp.d.ts.map +1 -1
  36. package/dist/cli/acp.js +197 -61
  37. package/dist/cli/acp.js.map +1 -1
  38. package/dist/cli/index.js +152 -16
  39. package/dist/cli/index.js.map +1 -1
  40. package/dist/cli/mcp.d.ts +6 -0
  41. package/dist/cli/mcp.d.ts.map +1 -1
  42. package/dist/cli/mcp.js +279 -173
  43. package/dist/cli/mcp.js.map +1 -1
  44. package/dist/cli/parse-args.d.ts +20 -0
  45. package/dist/cli/parse-args.d.ts.map +1 -0
  46. package/dist/cli/parse-args.js +43 -0
  47. package/dist/cli/parse-args.js.map +1 -0
  48. package/dist/cli/stable-instance-id.d.ts +8 -0
  49. package/dist/cli/stable-instance-id.d.ts.map +1 -0
  50. package/dist/cli/stable-instance-id.js +14 -0
  51. package/dist/cli/stable-instance-id.js.map +1 -0
  52. package/dist/config/project-config.d.ts +85 -7
  53. package/dist/config/project-config.d.ts.map +1 -1
  54. package/dist/config/project-config.js +133 -20
  55. package/dist/config/project-config.js.map +1 -1
  56. package/dist/index.d.ts +1 -0
  57. package/dist/index.d.ts.map +1 -1
  58. package/dist/index.js +2 -0
  59. package/dist/index.js.map +1 -1
  60. package/dist/lifecycle/handlers/index.d.ts +7 -3
  61. package/dist/lifecycle/handlers/index.d.ts.map +1 -1
  62. package/dist/lifecycle/handlers/index.js +25 -8
  63. package/dist/lifecycle/handlers/index.js.map +1 -1
  64. package/dist/lifecycle/types.d.ts +2 -0
  65. package/dist/lifecycle/types.d.ts.map +1 -1
  66. package/dist/lifecycle/types.js.map +1 -1
  67. package/dist/map/adapter/acp-over-map.d.ts +17 -0
  68. package/dist/map/adapter/acp-over-map.d.ts.map +1 -1
  69. package/dist/map/adapter/acp-over-map.js +384 -23
  70. package/dist/map/adapter/acp-over-map.js.map +1 -1
  71. package/dist/map/adapter/connection-manager.d.ts.map +1 -1
  72. package/dist/map/adapter/connection-manager.js +3 -0
  73. package/dist/map/adapter/connection-manager.js.map +1 -1
  74. package/dist/map/adapter/event-log.d.ts +87 -0
  75. package/dist/map/adapter/event-log.d.ts.map +1 -0
  76. package/dist/map/adapter/event-log.js +122 -0
  77. package/dist/map/adapter/event-log.js.map +1 -0
  78. package/dist/map/adapter/event-translator.js +6 -6
  79. package/dist/map/adapter/event-translator.js.map +1 -1
  80. package/dist/map/adapter/extensions/agent-lifecycle.d.ts +82 -0
  81. package/dist/map/adapter/extensions/agent-lifecycle.d.ts.map +1 -0
  82. package/dist/map/adapter/extensions/agent-lifecycle.js +164 -0
  83. package/dist/map/adapter/extensions/agent-lifecycle.js.map +1 -0
  84. package/dist/map/adapter/extensions/index.d.ts +13 -1
  85. package/dist/map/adapter/extensions/index.d.ts.map +1 -1
  86. package/dist/map/adapter/extensions/index.js +61 -0
  87. package/dist/map/adapter/extensions/index.js.map +1 -1
  88. package/dist/map/adapter/extensions/mcp-bridge.d.ts +57 -0
  89. package/dist/map/adapter/extensions/mcp-bridge.d.ts.map +1 -0
  90. package/dist/map/adapter/extensions/mcp-bridge.js +745 -0
  91. package/dist/map/adapter/extensions/mcp-bridge.js.map +1 -0
  92. package/dist/map/adapter/extensions/rename.d.ts +29 -0
  93. package/dist/map/adapter/extensions/rename.d.ts.map +1 -0
  94. package/dist/map/adapter/extensions/rename.js +49 -0
  95. package/dist/map/adapter/extensions/rename.js.map +1 -0
  96. package/dist/map/adapter/extensions/streams.d.ts +95 -0
  97. package/dist/map/adapter/extensions/streams.d.ts.map +1 -0
  98. package/dist/map/adapter/extensions/streams.js +515 -0
  99. package/dist/map/adapter/extensions/streams.js.map +1 -0
  100. package/dist/map/adapter/extensions/task.d.ts.map +1 -1
  101. package/dist/map/adapter/extensions/task.js +10 -0
  102. package/dist/map/adapter/extensions/task.js.map +1 -1
  103. package/dist/map/adapter/extensions/update-metadata.d.ts +29 -0
  104. package/dist/map/adapter/extensions/update-metadata.d.ts.map +1 -0
  105. package/dist/map/adapter/extensions/update-metadata.js +67 -0
  106. package/dist/map/adapter/extensions/update-metadata.js.map +1 -0
  107. package/dist/map/adapter/index.d.ts +2 -1
  108. package/dist/map/adapter/index.d.ts.map +1 -1
  109. package/dist/map/adapter/index.js +10 -2
  110. package/dist/map/adapter/index.js.map +1 -1
  111. package/dist/map/adapter/interface.d.ts +2 -0
  112. package/dist/map/adapter/interface.d.ts.map +1 -1
  113. package/dist/map/adapter/map-adapter.d.ts +3 -0
  114. package/dist/map/adapter/map-adapter.d.ts.map +1 -1
  115. package/dist/map/adapter/map-adapter.js +258 -35
  116. package/dist/map/adapter/map-adapter.js.map +1 -1
  117. package/dist/map/adapter/subscription-manager.d.ts.map +1 -1
  118. package/dist/map/adapter/subscription-manager.js +5 -1
  119. package/dist/map/adapter/subscription-manager.js.map +1 -1
  120. package/dist/map/adapter/types.d.ts +3 -1
  121. package/dist/map/adapter/types.d.ts.map +1 -1
  122. package/dist/mcp/map-client.d.ts +39 -0
  123. package/dist/mcp/map-client.d.ts.map +1 -0
  124. package/dist/mcp/map-client.js +129 -0
  125. package/dist/mcp/map-client.js.map +1 -0
  126. package/dist/mcp/mcp-server.d.ts +16 -0
  127. package/dist/mcp/mcp-server.d.ts.map +1 -1
  128. package/dist/mcp/mcp-server.js +125 -88
  129. package/dist/mcp/mcp-server.js.map +1 -1
  130. package/dist/mcp/tools/done.d.ts.map +1 -1
  131. package/dist/mcp/tools/done.js +18 -0
  132. package/dist/mcp/tools/done.js.map +1 -1
  133. package/dist/mcp/types.d.ts +9 -1
  134. package/dist/mcp/types.d.ts.map +1 -1
  135. package/dist/mcp/types.js.map +1 -1
  136. package/dist/metrics/metrics.js +1 -1
  137. package/dist/metrics/metrics.js.map +1 -1
  138. package/dist/roles/builtin/coordinator.d.ts.map +1 -1
  139. package/dist/roles/builtin/coordinator.js +2 -1
  140. package/dist/roles/builtin/coordinator.js.map +1 -1
  141. package/dist/roles/builtin/integrator.d.ts.map +1 -1
  142. package/dist/roles/builtin/integrator.js +2 -1
  143. package/dist/roles/builtin/integrator.js.map +1 -1
  144. package/dist/roles/builtin/worker.d.ts.map +1 -1
  145. package/dist/roles/builtin/worker.js +3 -1
  146. package/dist/roles/builtin/worker.js.map +1 -1
  147. package/dist/roles/capabilities.d.ts +9 -1
  148. package/dist/roles/capabilities.d.ts.map +1 -1
  149. package/dist/roles/capabilities.js +27 -7
  150. package/dist/roles/capabilities.js.map +1 -1
  151. package/dist/roles/config-loader.d.ts +6 -6
  152. package/dist/roles/config-loader.d.ts.map +1 -1
  153. package/dist/roles/config-loader.js +8 -7
  154. package/dist/roles/config-loader.js.map +1 -1
  155. package/dist/roles/registry.d.ts +2 -2
  156. package/dist/roles/registry.js +2 -2
  157. package/dist/roles/types.d.ts +3 -1
  158. package/dist/roles/types.d.ts.map +1 -1
  159. package/dist/server/combined-server.d.ts +28 -1
  160. package/dist/server/combined-server.d.ts.map +1 -1
  161. package/dist/server/combined-server.js +111 -8
  162. package/dist/server/combined-server.js.map +1 -1
  163. package/dist/store/event-store.d.ts +2 -1
  164. package/dist/store/event-store.d.ts.map +1 -1
  165. package/dist/store/event-store.js +80 -24
  166. package/dist/store/event-store.js.map +1 -1
  167. package/dist/store/instance.d.ts +1 -1
  168. package/dist/store/instance.d.ts.map +1 -1
  169. package/dist/store/instance.js +2 -2
  170. package/dist/store/instance.js.map +1 -1
  171. package/dist/store/types/agents.d.ts +23 -0
  172. package/dist/store/types/agents.d.ts.map +1 -1
  173. package/dist/store/types/events.d.ts +1 -1
  174. package/dist/store/types/events.d.ts.map +1 -1
  175. package/dist/task/backend/index.d.ts +47 -29
  176. package/dist/task/backend/index.d.ts.map +1 -1
  177. package/dist/task/backend/index.js +109 -71
  178. package/dist/task/backend/index.js.map +1 -1
  179. package/dist/task/backend/memory.d.ts +1 -0
  180. package/dist/task/backend/memory.d.ts.map +1 -1
  181. package/dist/task/backend/memory.js +3 -0
  182. package/dist/task/backend/memory.js.map +1 -1
  183. package/dist/task/backend/opentasks/backend.d.ts +140 -0
  184. package/dist/task/backend/opentasks/backend.d.ts.map +1 -0
  185. package/dist/task/backend/opentasks/backend.js +1023 -0
  186. package/dist/task/backend/opentasks/backend.js.map +1 -0
  187. package/dist/task/backend/opentasks/client.d.ts +337 -0
  188. package/dist/task/backend/opentasks/client.d.ts.map +1 -0
  189. package/dist/task/backend/opentasks/client.js +225 -0
  190. package/dist/task/backend/opentasks/client.js.map +1 -0
  191. package/dist/task/backend/opentasks/daemon-manager.d.ts +89 -0
  192. package/dist/task/backend/opentasks/daemon-manager.d.ts.map +1 -0
  193. package/dist/task/backend/opentasks/daemon-manager.js +195 -0
  194. package/dist/task/backend/opentasks/daemon-manager.js.map +1 -0
  195. package/dist/task/backend/opentasks/index.d.ts +21 -0
  196. package/dist/task/backend/opentasks/index.d.ts.map +1 -0
  197. package/dist/task/backend/opentasks/index.js +21 -0
  198. package/dist/task/backend/opentasks/index.js.map +1 -0
  199. package/dist/task/backend/opentasks/mapping.d.ts +48 -0
  200. package/dist/task/backend/opentasks/mapping.d.ts.map +1 -0
  201. package/dist/task/backend/opentasks/mapping.js +77 -0
  202. package/dist/task/backend/opentasks/mapping.js.map +1 -0
  203. package/dist/task/backend/types.d.ts +33 -53
  204. package/dist/task/backend/types.d.ts.map +1 -1
  205. package/dist/task/backend/types.js +7 -11
  206. package/dist/task/backend/types.js.map +1 -1
  207. package/dist/task/backend/unified-tool-provider.d.ts +57 -0
  208. package/dist/task/backend/unified-tool-provider.d.ts.map +1 -0
  209. package/dist/task/backend/unified-tool-provider.js +623 -0
  210. package/dist/task/backend/unified-tool-provider.js.map +1 -0
  211. package/dist/teams/index.d.ts +3 -1
  212. package/dist/teams/index.d.ts.map +1 -1
  213. package/dist/teams/index.js +2 -0
  214. package/dist/teams/index.js.map +1 -1
  215. package/dist/teams/seed-defaults.d.ts +20 -0
  216. package/dist/teams/seed-defaults.d.ts.map +1 -0
  217. package/dist/teams/seed-defaults.js +71 -0
  218. package/dist/teams/seed-defaults.js.map +1 -0
  219. package/dist/teams/team-loader.d.ts +7 -3
  220. package/dist/teams/team-loader.d.ts.map +1 -1
  221. package/dist/teams/team-loader.js +156 -164
  222. package/dist/teams/team-loader.js.map +1 -1
  223. package/dist/teams/team-manager.d.ts +112 -0
  224. package/dist/teams/team-manager.d.ts.map +1 -0
  225. package/dist/teams/team-manager.js +305 -0
  226. package/dist/teams/team-manager.js.map +1 -0
  227. package/dist/teams/team-runtime.d.ts +125 -19
  228. package/dist/teams/team-runtime.d.ts.map +1 -1
  229. package/dist/teams/team-runtime.js +529 -119
  230. package/dist/teams/team-runtime.js.map +1 -1
  231. package/dist/teams/types.d.ts +41 -151
  232. package/dist/teams/types.d.ts.map +1 -1
  233. package/dist/teams/types.js +2 -3
  234. package/dist/teams/types.js.map +1 -1
  235. package/docs/architecture.md +7 -6
  236. package/docs/configuration.md +26 -62
  237. package/docs/implementation-details.md +5 -5
  238. package/docs/implementation-summary.md +17 -17
  239. package/docs/plan-self-driving-support.md +4 -4
  240. package/docs/spec-self-driving-support.md +10 -10
  241. package/docs/team-templates.md +2 -2
  242. package/docs/teams.md +76 -3
  243. package/docs/troubleshooting.md +10 -11
  244. package/package.json +7 -4
  245. package/references/minimem/.claude/settings.json +7 -0
  246. package/references/minimem/.sudocode/issues.jsonl +18 -0
  247. package/references/minimem/.sudocode/specs.jsonl +1 -0
  248. package/references/minimem/CLAUDE.md +310 -0
  249. package/references/minimem/README.md +562 -0
  250. package/references/minimem/claude-plugin/.claude-plugin/plugin.json +10 -0
  251. package/references/minimem/claude-plugin/.mcp.json +7 -0
  252. package/references/minimem/claude-plugin/README.md +158 -0
  253. package/references/minimem/claude-plugin/commands/recall.md +47 -0
  254. package/references/minimem/claude-plugin/commands/remember.md +41 -0
  255. package/references/minimem/claude-plugin/hooks/__tests__/hooks.test.ts +272 -0
  256. package/references/minimem/claude-plugin/hooks/hooks.json +27 -0
  257. package/references/minimem/claude-plugin/hooks/session-end.sh +86 -0
  258. package/references/minimem/claude-plugin/hooks/session-start.sh +85 -0
  259. package/references/minimem/claude-plugin/skills/memory/SKILL.md +108 -0
  260. package/references/minimem/media/banner.png +0 -0
  261. package/references/minimem/package-lock.json +5373 -0
  262. package/references/minimem/package.json +72 -0
  263. package/references/minimem/scripts/postbuild.js +35 -0
  264. package/references/minimem/src/__tests__/edge-cases.test.ts +371 -0
  265. package/references/minimem/src/__tests__/errors.test.ts +265 -0
  266. package/references/minimem/src/__tests__/helpers.ts +199 -0
  267. package/references/minimem/src/__tests__/internal.test.ts +407 -0
  268. package/references/minimem/src/__tests__/knowledge.test.ts +287 -0
  269. package/references/minimem/src/__tests__/minimem.integration.test.ts +1127 -0
  270. package/references/minimem/src/__tests__/session.test.ts +190 -0
  271. package/references/minimem/src/cli/__tests__/commands.test.ts +759 -0
  272. package/references/minimem/src/cli/commands/__tests__/conflicts.test.ts +141 -0
  273. package/references/minimem/src/cli/commands/append.ts +76 -0
  274. package/references/minimem/src/cli/commands/config.ts +262 -0
  275. package/references/minimem/src/cli/commands/conflicts.ts +413 -0
  276. package/references/minimem/src/cli/commands/daemon.ts +169 -0
  277. package/references/minimem/src/cli/commands/index.ts +12 -0
  278. package/references/minimem/src/cli/commands/init.ts +88 -0
  279. package/references/minimem/src/cli/commands/mcp.ts +177 -0
  280. package/references/minimem/src/cli/commands/push-pull.ts +213 -0
  281. package/references/minimem/src/cli/commands/search.ts +158 -0
  282. package/references/minimem/src/cli/commands/status.ts +84 -0
  283. package/references/minimem/src/cli/commands/sync-init.ts +290 -0
  284. package/references/minimem/src/cli/commands/sync.ts +70 -0
  285. package/references/minimem/src/cli/commands/upsert.ts +197 -0
  286. package/references/minimem/src/cli/config.ts +584 -0
  287. package/references/minimem/src/cli/index.ts +264 -0
  288. package/references/minimem/src/cli/shared.ts +161 -0
  289. package/references/minimem/src/cli/sync/__tests__/central.test.ts +152 -0
  290. package/references/minimem/src/cli/sync/__tests__/conflicts.test.ts +209 -0
  291. package/references/minimem/src/cli/sync/__tests__/daemon.test.ts +118 -0
  292. package/references/minimem/src/cli/sync/__tests__/detection.test.ts +207 -0
  293. package/references/minimem/src/cli/sync/__tests__/integration.test.ts +476 -0
  294. package/references/minimem/src/cli/sync/__tests__/registry.test.ts +363 -0
  295. package/references/minimem/src/cli/sync/__tests__/state.test.ts +255 -0
  296. package/references/minimem/src/cli/sync/__tests__/validation.test.ts +193 -0
  297. package/references/minimem/src/cli/sync/__tests__/watcher.test.ts +178 -0
  298. package/references/minimem/src/cli/sync/central.ts +292 -0
  299. package/references/minimem/src/cli/sync/conflicts.ts +204 -0
  300. package/references/minimem/src/cli/sync/daemon.ts +407 -0
  301. package/references/minimem/src/cli/sync/detection.ts +138 -0
  302. package/references/minimem/src/cli/sync/index.ts +107 -0
  303. package/references/minimem/src/cli/sync/operations.ts +373 -0
  304. package/references/minimem/src/cli/sync/registry.ts +279 -0
  305. package/references/minimem/src/cli/sync/state.ts +355 -0
  306. package/references/minimem/src/cli/sync/validation.ts +206 -0
  307. package/references/minimem/src/cli/sync/watcher.ts +234 -0
  308. package/references/minimem/src/cli/version.ts +34 -0
  309. package/references/minimem/src/core/index.ts +9 -0
  310. package/references/minimem/src/core/indexer.ts +628 -0
  311. package/references/minimem/src/core/searcher.ts +221 -0
  312. package/references/minimem/src/db/schema.ts +183 -0
  313. package/references/minimem/src/db/sqlite-vec.ts +24 -0
  314. package/references/minimem/src/embeddings/__tests__/embeddings.test.ts +431 -0
  315. package/references/minimem/src/embeddings/batch-gemini.ts +392 -0
  316. package/references/minimem/src/embeddings/batch-openai.ts +409 -0
  317. package/references/minimem/src/embeddings/embeddings.ts +434 -0
  318. package/references/minimem/src/index.ts +109 -0
  319. package/references/minimem/src/internal.ts +299 -0
  320. package/references/minimem/src/minimem.ts +1276 -0
  321. package/references/minimem/src/search/__tests__/hybrid.test.ts +247 -0
  322. package/references/minimem/src/search/graph.ts +234 -0
  323. package/references/minimem/src/search/hybrid.ts +151 -0
  324. package/references/minimem/src/search/search.ts +256 -0
  325. package/references/minimem/src/server/__tests__/mcp.test.ts +341 -0
  326. package/references/minimem/src/server/__tests__/tools.test.ts +364 -0
  327. package/references/minimem/src/server/mcp.ts +326 -0
  328. package/references/minimem/src/server/tools.ts +720 -0
  329. package/references/minimem/src/session.ts +460 -0
  330. package/references/minimem/tsconfig.json +19 -0
  331. package/references/minimem/tsup.config.ts +26 -0
  332. package/references/minimem/vitest.config.ts +24 -0
  333. package/references/openteams/.claude/settings.json +6 -0
  334. package/references/openteams/README.md +1 -0
  335. package/references/openteams/SKILL.md +341 -0
  336. package/references/openteams/design.md +411 -0
  337. package/references/openteams/examples/bmad-method/prompts/analyst/ROLE.md +16 -0
  338. package/references/openteams/examples/bmad-method/prompts/analyst/SOUL.md +5 -0
  339. package/references/openteams/examples/bmad-method/prompts/architect/ROLE.md +24 -0
  340. package/references/openteams/examples/bmad-method/prompts/architect/SOUL.md +5 -0
  341. package/references/openteams/examples/bmad-method/prompts/developer/ROLE.md +25 -0
  342. package/references/openteams/examples/bmad-method/prompts/developer/SOUL.md +5 -0
  343. package/references/openteams/examples/bmad-method/prompts/master/ROLE.md +21 -0
  344. package/references/openteams/examples/bmad-method/prompts/master/SOUL.md +5 -0
  345. package/references/openteams/examples/bmad-method/prompts/pm/ROLE.md +20 -0
  346. package/references/openteams/examples/bmad-method/prompts/pm/SOUL.md +5 -0
  347. package/references/openteams/examples/bmad-method/prompts/qa/ROLE.md +17 -0
  348. package/references/openteams/examples/bmad-method/prompts/qa/SOUL.md +5 -0
  349. package/references/openteams/examples/bmad-method/prompts/quick-flow-dev/ROLE.md +23 -0
  350. package/references/openteams/examples/bmad-method/prompts/quick-flow-dev/SOUL.md +5 -0
  351. package/references/openteams/examples/bmad-method/prompts/scrum-master/ROLE.md +27 -0
  352. package/references/openteams/examples/bmad-method/prompts/scrum-master/SOUL.md +5 -0
  353. package/references/openteams/examples/bmad-method/prompts/tech-writer/ROLE.md +21 -0
  354. package/references/openteams/examples/bmad-method/prompts/tech-writer/SOUL.md +5 -0
  355. package/references/openteams/examples/bmad-method/prompts/ux-designer/ROLE.md +16 -0
  356. package/references/openteams/examples/bmad-method/prompts/ux-designer/SOUL.md +5 -0
  357. package/references/openteams/examples/bmad-method/roles/analyst.yaml +9 -0
  358. package/references/openteams/examples/bmad-method/roles/architect.yaml +9 -0
  359. package/references/openteams/examples/bmad-method/roles/developer.yaml +8 -0
  360. package/references/openteams/examples/bmad-method/roles/master.yaml +8 -0
  361. package/references/openteams/examples/bmad-method/roles/pm.yaml +9 -0
  362. package/references/openteams/examples/bmad-method/roles/qa.yaml +8 -0
  363. package/references/openteams/examples/bmad-method/roles/quick-flow-dev.yaml +8 -0
  364. package/references/openteams/examples/bmad-method/roles/scrum-master.yaml +9 -0
  365. package/references/openteams/examples/bmad-method/roles/tech-writer.yaml +8 -0
  366. package/references/openteams/examples/bmad-method/roles/ux-designer.yaml +8 -0
  367. package/references/openteams/examples/bmad-method/team.yaml +161 -0
  368. package/references/openteams/examples/get-shit-done/prompts/codebase-mapper/ROLE.md +17 -0
  369. package/references/openteams/examples/get-shit-done/prompts/codebase-mapper/SOUL.md +5 -0
  370. package/references/openteams/examples/get-shit-done/prompts/debugger/ROLE.md +25 -0
  371. package/references/openteams/examples/get-shit-done/prompts/debugger/SOUL.md +5 -0
  372. package/references/openteams/examples/get-shit-done/prompts/executor/ROLE.md +34 -0
  373. package/references/openteams/examples/get-shit-done/prompts/executor/SOUL.md +5 -0
  374. package/references/openteams/examples/get-shit-done/prompts/integration-checker/ROLE.md +18 -0
  375. package/references/openteams/examples/get-shit-done/prompts/integration-checker/SOUL.md +3 -0
  376. package/references/openteams/examples/get-shit-done/prompts/orchestrator/ROLE.md +42 -0
  377. package/references/openteams/examples/get-shit-done/prompts/orchestrator/SOUL.md +5 -0
  378. package/references/openteams/examples/get-shit-done/prompts/phase-researcher/ROLE.md +15 -0
  379. package/references/openteams/examples/get-shit-done/prompts/phase-researcher/SOUL.md +3 -0
  380. package/references/openteams/examples/get-shit-done/prompts/plan-checker/ROLE.md +17 -0
  381. package/references/openteams/examples/get-shit-done/prompts/plan-checker/SOUL.md +3 -0
  382. package/references/openteams/examples/get-shit-done/prompts/planner/ROLE.md +28 -0
  383. package/references/openteams/examples/get-shit-done/prompts/planner/SOUL.md +5 -0
  384. package/references/openteams/examples/get-shit-done/prompts/project-researcher/ROLE.md +16 -0
  385. package/references/openteams/examples/get-shit-done/prompts/project-researcher/SOUL.md +3 -0
  386. package/references/openteams/examples/get-shit-done/prompts/research-synthesizer/ROLE.md +13 -0
  387. package/references/openteams/examples/get-shit-done/prompts/research-synthesizer/SOUL.md +3 -0
  388. package/references/openteams/examples/get-shit-done/prompts/roadmapper/ROLE.md +14 -0
  389. package/references/openteams/examples/get-shit-done/prompts/roadmapper/SOUL.md +3 -0
  390. package/references/openteams/examples/get-shit-done/prompts/verifier/ROLE.md +19 -0
  391. package/references/openteams/examples/get-shit-done/prompts/verifier/SOUL.md +5 -0
  392. package/references/openteams/examples/get-shit-done/roles/codebase-mapper.yaml +8 -0
  393. package/references/openteams/examples/get-shit-done/roles/debugger.yaml +8 -0
  394. package/references/openteams/examples/get-shit-done/roles/executor.yaml +8 -0
  395. package/references/openteams/examples/get-shit-done/roles/integration-checker.yaml +8 -0
  396. package/references/openteams/examples/get-shit-done/roles/orchestrator.yaml +9 -0
  397. package/references/openteams/examples/get-shit-done/roles/phase-researcher.yaml +7 -0
  398. package/references/openteams/examples/get-shit-done/roles/plan-checker.yaml +8 -0
  399. package/references/openteams/examples/get-shit-done/roles/planner.yaml +8 -0
  400. package/references/openteams/examples/get-shit-done/roles/project-researcher.yaml +8 -0
  401. package/references/openteams/examples/get-shit-done/roles/research-synthesizer.yaml +7 -0
  402. package/references/openteams/examples/get-shit-done/roles/roadmapper.yaml +7 -0
  403. package/references/openteams/examples/get-shit-done/roles/verifier.yaml +8 -0
  404. package/references/openteams/examples/get-shit-done/team.yaml +154 -0
  405. package/references/openteams/package-lock.json +2181 -0
  406. package/references/openteams/package.json +48 -0
  407. package/references/openteams/schema/role.schema.json +125 -0
  408. package/references/openteams/schema/team.schema.json +284 -0
  409. package/references/openteams/src/cli/agent.ts +104 -0
  410. package/references/openteams/src/cli/cli.test.ts +381 -0
  411. package/references/openteams/src/cli/generate.ts +220 -0
  412. package/references/openteams/src/cli/message.ts +241 -0
  413. package/references/openteams/src/cli/task.ts +154 -0
  414. package/references/openteams/src/cli/team.ts +104 -0
  415. package/references/openteams/src/cli/template.ts +207 -0
  416. package/references/openteams/src/cli.ts +45 -0
  417. package/references/openteams/src/db/database.test.ts +185 -0
  418. package/references/openteams/src/db/database.ts +240 -0
  419. package/references/openteams/src/generators/agent-prompt-generator.test.ts +332 -0
  420. package/references/openteams/src/generators/agent-prompt-generator.ts +521 -0
  421. package/references/openteams/src/generators/package-generator.test.ts +129 -0
  422. package/references/openteams/src/generators/package-generator.ts +102 -0
  423. package/references/openteams/src/generators/skill-generator.test.ts +246 -0
  424. package/references/openteams/src/generators/skill-generator.ts +374 -0
  425. package/references/openteams/src/index.ts +104 -0
  426. package/references/openteams/src/services/agent-service.test.ts +158 -0
  427. package/references/openteams/src/services/agent-service.ts +84 -0
  428. package/references/openteams/src/services/communication-service.test.ts +455 -0
  429. package/references/openteams/src/services/communication-service.ts +371 -0
  430. package/references/openteams/src/services/message-service.test.ts +342 -0
  431. package/references/openteams/src/services/message-service.ts +203 -0
  432. package/references/openteams/src/services/task-service.test.ts +434 -0
  433. package/references/openteams/src/services/task-service.ts +239 -0
  434. package/references/openteams/src/services/team-service.test.ts +181 -0
  435. package/references/openteams/src/services/team-service.ts +139 -0
  436. package/references/openteams/src/services/template-service.test.ts +306 -0
  437. package/references/openteams/src/services/template-service.ts +182 -0
  438. package/references/openteams/src/spawner/acp-factory.ts +96 -0
  439. package/references/openteams/src/spawner/interface.ts +31 -0
  440. package/references/openteams/src/spawner/mock.test.ts +93 -0
  441. package/references/openteams/src/spawner/mock.ts +59 -0
  442. package/references/openteams/src/template/loader.test.ts +1319 -0
  443. package/references/openteams/src/template/loader.ts +698 -0
  444. package/references/openteams/src/template/types.ts +200 -0
  445. package/references/openteams/src/types.ts +205 -0
  446. package/references/openteams/tsconfig.json +18 -0
  447. package/references/openteams/vitest.config.ts +9 -0
  448. package/references/skill-tree/.claude/settings.json +6 -0
  449. package/references/skill-tree/.sudocode/issues.jsonl +11 -0
  450. package/references/skill-tree/.sudocode/specs.jsonl +1 -0
  451. package/references/skill-tree/CLAUDE.md +150 -0
  452. package/references/skill-tree/README.md +324 -0
  453. package/references/skill-tree/docs/GAPS_v1.md +221 -0
  454. package/references/skill-tree/docs/INTEGRATION_PLAN.md +467 -0
  455. package/references/skill-tree/docs/TODOS.md +91 -0
  456. package/references/skill-tree/docs/anthropic_skill_guide.md +1364 -0
  457. package/references/skill-tree/docs/design/federated-skill-trees.md +524 -0
  458. package/references/skill-tree/docs/design/multi-agent-sync.md +759 -0
  459. package/references/skill-tree/docs/scraper/BRAINSTORM.md +583 -0
  460. package/references/skill-tree/docs/scraper/POC_PLAN.md +420 -0
  461. package/references/skill-tree/docs/scraper/README.md +170 -0
  462. package/references/skill-tree/examples/basic-usage.ts +190 -0
  463. package/references/skill-tree/package-lock.json +1509 -0
  464. package/references/skill-tree/package.json +66 -0
  465. package/references/skill-tree/scraper/README.md +123 -0
  466. package/references/skill-tree/scraper/docs/DESIGN.md +683 -0
  467. package/references/skill-tree/scraper/docs/PLAN.md +336 -0
  468. package/references/skill-tree/scraper/drizzle.config.ts +10 -0
  469. package/references/skill-tree/scraper/package-lock.json +6329 -0
  470. package/references/skill-tree/scraper/package.json +68 -0
  471. package/references/skill-tree/scraper/test/fixtures/invalid-skill/missing-description.md +7 -0
  472. package/references/skill-tree/scraper/test/fixtures/invalid-skill/missing-name.md +7 -0
  473. package/references/skill-tree/scraper/test/fixtures/minimal-skill/SKILL.md +27 -0
  474. package/references/skill-tree/scraper/test/fixtures/skill-json/SKILL.json +21 -0
  475. package/references/skill-tree/scraper/test/fixtures/skill-with-meta/SKILL.md +54 -0
  476. package/references/skill-tree/scraper/test/fixtures/skill-with-meta/_meta.json +24 -0
  477. package/references/skill-tree/scraper/test/fixtures/valid-skill/SKILL.md +93 -0
  478. package/references/skill-tree/scraper/test/fixtures/valid-skill/_meta.json +22 -0
  479. package/references/skill-tree/scraper/tsup.config.ts +14 -0
  480. package/references/skill-tree/scraper/vitest.config.ts +17 -0
  481. package/references/skill-tree/scripts/convert-to-vitest.ts +166 -0
  482. package/references/skill-tree/skills/skill-writer/SKILL.md +339 -0
  483. package/references/skill-tree/skills/skill-writer/references/examples.md +326 -0
  484. package/references/skill-tree/skills/skill-writer/references/patterns.md +210 -0
  485. package/references/skill-tree/skills/skill-writer/references/quality-checklist.md +123 -0
  486. package/references/skill-tree/test/run-all.ts +106 -0
  487. package/references/skill-tree/test/utils.ts +128 -0
  488. package/references/skill-tree/vitest.config.ts +16 -0
  489. package/src/__tests__/e2e/agent-spawn-visibility.e2e.test.ts +761 -0
  490. package/src/__tests__/e2e/full-agent-conflict-resolution.e2e.test.ts +2 -2
  491. package/src/__tests__/e2e/mcp-thin-client-bridge.e2e.test.ts +304 -0
  492. package/src/__tests__/e2e/mcp-tools-available.e2e.test.ts +324 -0
  493. package/src/__tests__/e2e/multi-agent.e2e.test.ts +5 -5
  494. package/src/__tests__/e2e/spawn-session-streaming.e2e.test.ts +563 -0
  495. package/src/acp/__tests__/integration.test.ts +56 -31
  496. package/src/acp/__tests__/macro-agent.test.ts +16 -7
  497. package/src/acp/macro-agent.ts +170 -36
  498. package/src/acp/types.ts +46 -1
  499. package/src/agent/__tests__/agent-manager.test.ts +228 -2
  500. package/src/agent/agent-manager.ts +809 -285
  501. package/src/agent/types.ts +12 -1
  502. package/src/api/__tests__/server.test.ts +203 -4
  503. package/src/api/server.ts +169 -10
  504. package/src/api/types.ts +3 -1
  505. package/src/auth/__tests__/token.test.ts +100 -0
  506. package/src/auth/index.ts +1 -0
  507. package/src/auth/token.ts +82 -0
  508. package/src/cli/__tests__/acp.test.ts +1 -1
  509. package/src/cli/__tests__/stable-instance-id.test.ts +1 -1
  510. package/src/cli/acp.ts +197 -72
  511. package/src/cli/index.ts +125 -15
  512. package/src/cli/mcp.ts +315 -197
  513. package/src/cli/parse-args.ts +54 -0
  514. package/src/cli/stable-instance-id.ts +14 -0
  515. package/src/config/project-config.ts +214 -27
  516. package/src/index.ts +3 -0
  517. package/src/lifecycle/__tests__/cascade-termination.test.ts +1 -1
  518. package/src/lifecycle/__tests__/handlers.test.ts +53 -0
  519. package/src/lifecycle/handlers/index.ts +25 -8
  520. package/src/lifecycle/types.ts +3 -0
  521. package/src/map/adapter/__tests__/acp-over-map-cancel.test.ts +22 -4
  522. package/src/map/adapter/__tests__/acp-over-map-getmodels.test.ts +355 -0
  523. package/src/map/adapter/__tests__/acp-over-map-history.test.ts +263 -0
  524. package/src/map/adapter/__tests__/acp-over-map-persistence.e2e.test.ts +1 -1
  525. package/src/map/adapter/__tests__/event-broadcast.test.ts +420 -0
  526. package/src/map/adapter/__tests__/event-log.test.ts +527 -0
  527. package/src/map/adapter/__tests__/event-translator.test.ts +3 -3
  528. package/src/map/adapter/__tests__/extensions.test.ts +408 -0
  529. package/src/map/adapter/__tests__/map-adapter.test.ts +99 -0
  530. package/src/map/adapter/__tests__/mcp-bridge.test.ts +1187 -0
  531. package/src/map/adapter/__tests__/multi-client-broadcast.test.ts +711 -0
  532. package/src/map/adapter/__tests__/stream-extensions.test.ts +494 -0
  533. package/src/map/adapter/__tests__/websocket-integration.test.ts +218 -0
  534. package/src/map/adapter/acp-over-map.ts +678 -66
  535. package/src/map/adapter/connection-manager.ts +3 -0
  536. package/src/map/adapter/event-log.ts +208 -0
  537. package/src/map/adapter/event-translator.ts +6 -6
  538. package/src/map/adapter/extensions/agent-lifecycle.ts +267 -0
  539. package/src/map/adapter/extensions/index.ts +96 -0
  540. package/src/map/adapter/extensions/mcp-bridge.ts +995 -0
  541. package/src/map/adapter/extensions/streams.ts +839 -0
  542. package/src/map/adapter/extensions/task.ts +11 -0
  543. package/src/map/adapter/extensions/update-metadata.ts +126 -0
  544. package/src/map/adapter/index.ts +33 -0
  545. package/src/map/adapter/interface.ts +2 -0
  546. package/src/map/adapter/map-adapter.ts +312 -47
  547. package/src/map/adapter/subscription-manager.ts +5 -1
  548. package/src/map/adapter/types.ts +10 -1
  549. package/src/mcp/__tests__/map-client.test.ts +386 -0
  550. package/src/mcp/__tests__/mcp-server-thin-client.test.ts +368 -0
  551. package/src/mcp/__tests__/mcp-server.test.ts +100 -1
  552. package/src/mcp/map-client.ts +177 -0
  553. package/src/mcp/mcp-server.ts +205 -103
  554. package/src/mcp/tools/done.ts +19 -0
  555. package/src/mcp/types.ts +6 -1
  556. package/src/metrics/metrics.ts +1 -1
  557. package/src/monitor/__tests__/stale-agent-flow.integration.test.ts +1 -1
  558. package/src/roles/__tests__/config-loader.test.ts +7 -7
  559. package/src/roles/builtin/coordinator.ts +2 -0
  560. package/src/roles/builtin/integrator.ts +2 -0
  561. package/src/roles/builtin/worker.ts +3 -0
  562. package/src/roles/capabilities.ts +28 -7
  563. package/src/roles/config-loader.ts +8 -7
  564. package/src/roles/registry.ts +2 -2
  565. package/src/roles/types.ts +7 -0
  566. package/src/server/__tests__/combined-server.test.ts +94 -21
  567. package/src/server/combined-server.ts +203 -33
  568. package/src/steering/__tests__/steering-integration.test.ts +1 -1
  569. package/src/store/__tests__/event-store-oob.test.ts +109 -0
  570. package/src/store/__tests__/event-store.test.ts +196 -1
  571. package/src/store/__tests__/instance.test.ts +3 -3
  572. package/src/store/event-store.ts +92 -23
  573. package/src/store/instance.ts +2 -2
  574. package/src/store/types/agents.ts +20 -0
  575. package/src/store/types/events.ts +1 -1
  576. package/src/task/backend/__tests__/create-task-backend.test.ts +225 -0
  577. package/src/task/backend/__tests__/e2e/unified-tool-provider-opentasks.e2e.test.ts +524 -0
  578. package/src/task/backend/__tests__/memory-pull-mode.test.ts +153 -0
  579. package/src/task/backend/__tests__/unified-tool-provider.test.ts +579 -0
  580. package/src/task/backend/index.ts +156 -106
  581. package/src/task/backend/memory.ts +4 -0
  582. package/src/task/backend/opentasks/__tests__/backend.test.ts +968 -0
  583. package/src/task/backend/opentasks/__tests__/daemon-manager.test.ts +406 -0
  584. package/src/task/backend/opentasks/__tests__/mapping.test.ts +84 -0
  585. package/src/task/backend/opentasks/__tests__/opentasks-backend.e2e.test.ts +1338 -0
  586. package/src/task/backend/opentasks/backend.ts +1323 -0
  587. package/src/task/backend/opentasks/client.ts +652 -0
  588. package/src/task/backend/opentasks/daemon-manager.ts +256 -0
  589. package/src/task/backend/opentasks/index.ts +69 -0
  590. package/src/task/backend/opentasks/mapping.ts +94 -0
  591. package/src/task/backend/types.ts +42 -66
  592. package/src/task/backend/unified-tool-provider.ts +779 -0
  593. package/src/teams/CLAUDE.md +180 -0
  594. package/src/teams/__tests__/cross-subsystem.integration.test.ts +1 -1
  595. package/src/teams/__tests__/e2e/workspace-isolation.e2e.test.ts +1263 -0
  596. package/src/teams/__tests__/team-manager.test.ts +814 -0
  597. package/src/teams/__tests__/team-system.test.ts +1291 -8
  598. package/src/teams/index.ts +21 -3
  599. package/src/teams/seed-defaults.ts +79 -0
  600. package/src/teams/team-loader.ts +202 -236
  601. package/src/teams/team-manager.ts +387 -0
  602. package/src/teams/team-runtime.ts +592 -121
  603. package/src/teams/types.ts +99 -200
  604. package/test_fixtures/README.md +2 -3
  605. package/test_fixtures/fixtures/index.ts +0 -3
  606. package/test_fixtures/fixtures/projects/project-with-specs.ts +7 -149
  607. package/test_fixtures/fixtures/repos/index.ts +1 -3
  608. package/test_fixtures/fixtures/repos/temp-repo-factory.ts +0 -116
  609. package/test_fixtures/fixtures/repos/types.ts +0 -11
  610. package/test_fixtures/harness/__tests__/fixtures.test.ts +10 -102
  611. package/test_fixtures/harness/__tests__/temp-repo-and-simulator.test.ts +0 -33
  612. package/test_fixtures/harness/simulator/agent-simulator.ts +4 -4
  613. package/vitest.config.ts +1 -1
  614. package/vitest.e2e.config.ts +1 -1
  615. package/vitest.setup.ts +1 -30
  616. package/.macro-agent/teams/self-driving/prompts/grinder.md +0 -27
  617. package/.macro-agent/teams/self-driving/prompts/judge.md +0 -27
  618. package/.macro-agent/teams/self-driving/prompts/planner.md +0 -33
  619. package/.macro-agent/teams/self-driving/roles/grinder.yaml +0 -17
  620. package/.macro-agent/teams/self-driving/roles/judge.yaml +0 -24
  621. package/.macro-agent/teams/self-driving/roles/planner.yaml +0 -18
  622. package/.macro-agent/teams/self-driving/team.yaml +0 -103
  623. package/.macro-agent/teams/structured/prompts/developer.md +0 -26
  624. package/.macro-agent/teams/structured/prompts/lead.md +0 -25
  625. package/.macro-agent/teams/structured/prompts/reviewer.md +0 -24
  626. package/.macro-agent/teams/structured/roles/developer.yaml +0 -12
  627. package/.macro-agent/teams/structured/roles/lead.yaml +0 -11
  628. package/.macro-agent/teams/structured/roles/reviewer.yaml +0 -19
  629. package/.macro-agent/teams/structured/team.yaml +0 -89
  630. package/docs/sudocode-integration.md +0 -383
  631. package/src/task/backend/__tests__/backend-parity.test.ts +0 -451
  632. package/src/task/backend/__tests__/tool-provider-edge-cases.test.ts +0 -430
  633. package/src/task/backend/__tests__/tool-provider.test.ts +0 -983
  634. package/src/task/backend/sudocode/__tests__/backend-edge-cases.test.ts +0 -575
  635. package/src/task/backend/sudocode/__tests__/backend.test.ts +0 -1194
  636. package/src/task/backend/sudocode/__tests__/client-integration.test.ts +0 -418
  637. package/src/task/backend/sudocode/__tests__/client.test.ts +0 -345
  638. package/src/task/backend/sudocode/__tests__/e2e/backend.e2e.test.ts +0 -753
  639. package/src/task/backend/sudocode/__tests__/e2e/server-client.e2e.test.ts +0 -680
  640. package/src/task/backend/sudocode/__tests__/e2e-workflow.test.ts +0 -666
  641. package/src/task/backend/sudocode/__tests__/integration/standalone-client.integration.test.ts +0 -396
  642. package/src/task/backend/sudocode/__tests__/integration/sudocode-cli.integration.test.ts +0 -328
  643. package/src/task/backend/sudocode/__tests__/integration/test-utils.ts +0 -175
  644. package/src/task/backend/sudocode/__tests__/mapping-edge-cases.test.ts +0 -265
  645. package/src/task/backend/sudocode/__tests__/server-client.test.ts +0 -675
  646. package/src/task/backend/sudocode/__tests__/sync-policy-edge-cases.test.ts +0 -521
  647. package/src/task/backend/sudocode/__tests__/sync-policy.test.ts +0 -519
  648. package/src/task/backend/sudocode/__tests__/tools.test.ts +0 -471
  649. package/src/task/backend/sudocode/backend.ts +0 -1237
  650. package/src/task/backend/sudocode/client.ts +0 -515
  651. package/src/task/backend/sudocode/index.ts +0 -120
  652. package/src/task/backend/sudocode/mapping.ts +0 -93
  653. package/src/task/backend/sudocode/server-client.ts +0 -522
  654. package/src/task/backend/sudocode/standalone-client.ts +0 -623
  655. package/src/task/backend/sudocode/sync-policy.ts +0 -387
  656. package/src/task/backend/sudocode/tools.ts +0 -896
  657. package/src/task/backend/tool-provider.ts +0 -506
  658. package/test_fixtures/fixtures/sudocode/index.ts +0 -29
  659. package/test_fixtures/fixtures/sudocode/issues.ts +0 -185
  660. package/test_fixtures/fixtures/sudocode/specs.ts +0 -159
@@ -1,1237 +0,0 @@
1
- /**
2
- * SudocodeTaskBackend Implementation
3
- *
4
- * Implements TaskBackend using EventStore for local task storage and
5
- * SudocodeClient for external issue data and dependency tracking.
6
- *
7
- * @module task/backend/sudocode/backend
8
- * @see s-8472 Pluggable Task Backend Integration with Sudocode
9
- * @see i-2gwa 7A.5: Implement SudocodeTaskBackend core
10
- */
11
-
12
- import { nanoid } from "nanoid";
13
- import type { EventStore } from "../../../store/event-store.js";
14
- import type {
15
- Task,
16
- TaskStatus,
17
- AgentId,
18
- TaskId,
19
- AgentHistoryEntry,
20
- } from "../../../store/types/index.js";
21
- import type {
22
- TaskBackend,
23
- ExtendedTask,
24
- CreateTaskOptions,
25
- UpdateTaskOptions,
26
- TaskFilter,
27
- TaskOutputs,
28
- TaskError,
29
- SubtaskStatus,
30
- AssignOptions,
31
- TaskChangeCallback,
32
- TaskChangeEvent,
33
- Unsubscribe,
34
- } from "../types.js";
35
- import type { SudocodeClient, IssueChangeCallback } from "./client.js";
36
- import { mapSudocodeStatus, isIssueComplete } from "./mapping.js";
37
- import type { SyncPolicy, SyncEventCallback, SyncEvent } from "./sync-policy.js";
38
- import {
39
- SyncPolicyEngine,
40
- defaultSyncPolicy,
41
- createSyncPolicyEngine,
42
- } from "./sync-policy.js";
43
-
44
- // Valid status transitions
45
- const VALID_STATUS_TRANSITIONS: Record<TaskStatus, TaskStatus[]> = {
46
- pending: ["assigned", "in_progress", "failed"],
47
- assigned: ["in_progress", "pending", "failed"],
48
- in_progress: ["completed", "failed", "pending"],
49
- completed: [],
50
- failed: ["pending"],
51
- };
52
-
53
- /**
54
- * Error thrown by SudocodeTaskBackend operations
55
- */
56
- export class SudocodeTaskBackendError extends Error {
57
- constructor(
58
- message: string,
59
- public readonly code: string,
60
- public readonly taskId?: TaskId
61
- ) {
62
- super(message);
63
- this.name = "SudocodeTaskBackendError";
64
- }
65
- }
66
-
67
- /**
68
- * SudocodeTaskBackend Configuration
69
- */
70
- export interface SudocodeTaskBackendConfig {
71
- /** Path to sudocode project root */
72
- projectPath?: string;
73
-
74
- /** Whether to sync task status with issue status */
75
- syncStatus?: boolean;
76
-
77
- /** Whether to auto-close issues when tasks complete */
78
- autoCloseIssues?: boolean;
79
-
80
- /** Sync policy configuration */
81
- syncPolicy?: Partial<SyncPolicy>;
82
- }
83
-
84
- const DEFAULT_CONFIG: Required<Omit<SudocodeTaskBackendConfig, "syncPolicy">> = {
85
- projectPath: process.cwd(),
86
- syncStatus: true,
87
- autoCloseIssues: false,
88
- };
89
-
90
- /**
91
- * SudocodeTaskBackend implements TaskBackend using:
92
- * - EventStore for local task storage and events
93
- * - SudocodeClient for issue data and dependency tracking
94
- *
95
- * Key features:
96
- * - Tasks can be bound to sudocode issues via external_id
97
- * - isBlocked computed from sudocode's issue blockers
98
- * - listReady uses sudocode's ready issues API
99
- * - Status changes can optionally sync to issues
100
- */
101
- export class SudocodeTaskBackend implements TaskBackend {
102
- private readonly config: Required<Omit<SudocodeTaskBackendConfig, "syncPolicy">>;
103
- private readonly tasksByIssue: Map<string, Set<TaskId>> = new Map();
104
- private readonly issueByTask: Map<TaskId, string> = new Map();
105
- private readonly syncEngine: SyncPolicyEngine;
106
- private issueChangeUnsubscribe?: Unsubscribe;
107
-
108
- constructor(
109
- private readonly eventStore: EventStore,
110
- private readonly client: SudocodeClient,
111
- config?: SudocodeTaskBackendConfig
112
- ) {
113
- this.config = { ...DEFAULT_CONFIG, ...config };
114
- this.syncEngine = createSyncPolicyEngine(
115
- config?.syncPolicy ?? {},
116
- this
117
- );
118
- this.rebuildIndex();
119
- this.subscribeToIssueChanges();
120
- }
121
-
122
- // ─────────────────────────────────────────────────────────────────────────────
123
- // Index Management
124
- // ─────────────────────────────────────────────────────────────────────────────
125
-
126
- /**
127
- * Rebuild the task-by-issue index from existing tasks
128
- */
129
- private rebuildIndex(): void {
130
- this.tasksByIssue.clear();
131
- this.issueByTask.clear();
132
- const tasks = this.eventStore.listTasks();
133
-
134
- for (const task of tasks) {
135
- const externalId = this.getTaskExternalId(task);
136
- if (externalId) {
137
- this.addToIndex(externalId, task.id);
138
- }
139
- }
140
- }
141
-
142
- /**
143
- * Add a task to the issue index
144
- */
145
- private addToIndex(issueId: string, taskId: TaskId): void {
146
- // Update issue -> tasks map
147
- if (!this.tasksByIssue.has(issueId)) {
148
- this.tasksByIssue.set(issueId, new Set());
149
- }
150
- this.tasksByIssue.get(issueId)!.add(taskId);
151
-
152
- // Update task -> issue map
153
- this.issueByTask.set(taskId, issueId);
154
- }
155
-
156
- /**
157
- * Remove a task from the issue index
158
- */
159
- private removeFromIndex(issueId: string, taskId: TaskId): void {
160
- // Update issue -> tasks map
161
- const tasks = this.tasksByIssue.get(issueId);
162
- if (tasks) {
163
- tasks.delete(taskId);
164
- if (tasks.size === 0) {
165
- this.tasksByIssue.delete(issueId);
166
- }
167
- }
168
-
169
- // Update task -> issue map
170
- this.issueByTask.delete(taskId);
171
- }
172
-
173
- /**
174
- * Get tasks bound to an issue
175
- */
176
- getTasksByIssue(issueId: string): TaskId[] {
177
- return Array.from(this.tasksByIssue.get(issueId) ?? []);
178
- }
179
-
180
- /**
181
- * Get the issue a task is bound to
182
- */
183
- getIssueForTask(taskId: TaskId): string | undefined {
184
- return this.issueByTask.get(taskId);
185
- }
186
-
187
- /**
188
- * Bind a task to a sudocode issue.
189
- * @param taskId Task ID to bind
190
- * @param issueId Issue ID to bind to
191
- * @throws If task or issue not found
192
- */
193
- async bindToIssue(taskId: TaskId, issueId: string): Promise<void> {
194
- const task = this.eventStore.getTask(taskId);
195
- if (!task) {
196
- throw new SudocodeTaskBackendError(
197
- `Task not found: ${taskId}`,
198
- "TASK_NOT_FOUND",
199
- taskId
200
- );
201
- }
202
-
203
- // Verify issue exists
204
- const issue = await this.client.getIssue(issueId);
205
- if (!issue) {
206
- throw new SudocodeTaskBackendError(
207
- `Issue not found: ${issueId}`,
208
- "ISSUE_NOT_FOUND"
209
- );
210
- }
211
-
212
- // Check if already bound to a different issue
213
- const currentIssue = this.getIssueForTask(taskId);
214
- if (currentIssue && currentIssue !== issueId) {
215
- // Remove from old index
216
- this.removeFromIndex(currentIssue, taskId);
217
- }
218
-
219
- // Update task with external_id
220
- this.eventStore.emit({
221
- type: "task",
222
- source: { agent_id: task.assigned_agent ?? task.created_by },
223
- payload: {
224
- task_id: taskId,
225
- action: "status_change",
226
- details: {
227
- outputs: { external_id: issueId },
228
- },
229
- },
230
- });
231
-
232
- // Update index
233
- this.addToIndex(issueId, taskId);
234
- }
235
-
236
- /**
237
- * Unbind a task from its current issue.
238
- * @param taskId Task ID to unbind
239
- * @throws If task not found
240
- */
241
- async unbindFromIssue(taskId: TaskId): Promise<void> {
242
- const task = this.eventStore.getTask(taskId);
243
- if (!task) {
244
- throw new SudocodeTaskBackendError(
245
- `Task not found: ${taskId}`,
246
- "TASK_NOT_FOUND",
247
- taskId
248
- );
249
- }
250
-
251
- const issueId = this.getIssueForTask(taskId);
252
- if (!issueId) {
253
- // Not bound, nothing to do
254
- return;
255
- }
256
-
257
- // Clear external_id in task outputs
258
- this.eventStore.emit({
259
- type: "task",
260
- source: { agent_id: task.assigned_agent ?? task.created_by },
261
- payload: {
262
- task_id: taskId,
263
- action: "status_change",
264
- details: {
265
- outputs: { external_id: null },
266
- },
267
- },
268
- });
269
-
270
- // Remove from index
271
- this.removeFromIndex(issueId, taskId);
272
- }
273
-
274
- // ─────────────────────────────────────────────────────────────────────────────
275
- // Issue Change Subscription
276
- // ─────────────────────────────────────────────────────────────────────────────
277
-
278
- /**
279
- * Subscribe to sudocode issue changes for status sync
280
- */
281
- private subscribeToIssueChanges(): void {
282
- if (!this.config.syncStatus) return;
283
-
284
- const callback: IssueChangeCallback = (event) => {
285
- // Handle via sync policy engine
286
- this.syncEngine.handleIssueChange(event).catch(() => {
287
- // Ignore errors from sync engine
288
- });
289
-
290
- // Additionally handle status mapping for non-closed status changes
291
- // (closed status is handled by the sync engine based on policy)
292
- if (
293
- event.type === "status_changed" &&
294
- event.issue &&
295
- event.issue.status !== "closed"
296
- ) {
297
- const taskIds = this.getTasksByIssue(event.issueId);
298
- const newStatus = mapSudocodeStatus(event.issue.status);
299
-
300
- for (const taskId of taskIds) {
301
- const task = this.eventStore.getTask(taskId);
302
- if (task && task.status !== newStatus) {
303
- // Only update if the task isn't in a terminal state
304
- // Also preserve "assigned" status - it's a macro-agent concept
305
- // that will transition to in_progress when start() is called
306
- if (
307
- task.status !== "completed" &&
308
- task.status !== "failed" &&
309
- task.status !== "assigned"
310
- ) {
311
- // Emit status change event
312
- this.eventStore.emit({
313
- type: "task",
314
- source: { agent_id: task.assigned_agent ?? task.created_by },
315
- payload: {
316
- task_id: taskId,
317
- action: "status_change",
318
- details: { status: newStatus },
319
- },
320
- });
321
- }
322
- }
323
- }
324
- }
325
- };
326
-
327
- this.issueChangeUnsubscribe = this.client.onIssueChange(callback);
328
- }
329
-
330
- // ─────────────────────────────────────────────────────────────────────────────
331
- // Lifecycle
332
- // ─────────────────────────────────────────────────────────────────────────────
333
-
334
- async create(options: CreateTaskOptions): Promise<ExtendedTask> {
335
- const taskId = `task_${nanoid(12)}`;
336
-
337
- // Validate parent task exists if specified
338
- if (options.parent_task) {
339
- const parent = this.eventStore.getTask(options.parent_task);
340
- if (!parent) {
341
- throw new SudocodeTaskBackendError(
342
- `Parent task not found: ${options.parent_task}`,
343
- "PARENT_TASK_NOT_FOUND",
344
- options.parent_task
345
- );
346
- }
347
- }
348
-
349
- // Validate external_id (issue) exists if specified
350
- if (options.external_id) {
351
- const issue = await this.client.getIssue(options.external_id);
352
- if (!issue) {
353
- throw new SudocodeTaskBackendError(
354
- `Issue not found: ${options.external_id}`,
355
- "ISSUE_NOT_FOUND"
356
- );
357
- }
358
- }
359
-
360
- // Emit task created event
361
- this.eventStore.emit({
362
- type: "task",
363
- source: { agent_id: options.created_by },
364
- payload: {
365
- task_id: taskId,
366
- action: "created",
367
- details: {
368
- description: options.description,
369
- parent_task: options.parent_task,
370
- external_id: options.external_id,
371
- },
372
- },
373
- });
374
-
375
- // Update parent's subtasks array if this is a subtask
376
- if (options.parent_task) {
377
- this.eventStore.emit({
378
- type: "task",
379
- source: { agent_id: options.created_by },
380
- payload: {
381
- task_id: options.parent_task,
382
- action: "status_change",
383
- details: {
384
- subtask_added: taskId,
385
- },
386
- },
387
- });
388
- }
389
-
390
- // Add to issue index if bound
391
- if (options.external_id) {
392
- this.addToIndex(options.external_id, taskId);
393
- }
394
-
395
- const task = this.eventStore.getTask(taskId)!;
396
- return this.toExtendedTask(task);
397
- }
398
-
399
- async get(id: TaskId): Promise<ExtendedTask | null> {
400
- const task = this.eventStore.getTask(id);
401
- if (!task) return null;
402
- return this.toExtendedTask(task);
403
- }
404
-
405
- async update(id: TaskId, updates: UpdateTaskOptions): Promise<ExtendedTask> {
406
- const task = this.eventStore.getTask(id);
407
- if (!task) {
408
- throw new SudocodeTaskBackendError(
409
- `Task not found: ${id}`,
410
- "TASK_NOT_FOUND",
411
- id
412
- );
413
- }
414
-
415
- const source = task.assigned_agent ?? task.created_by;
416
-
417
- // Handle status update with validation
418
- if (updates.status !== undefined) {
419
- const validTransitions = VALID_STATUS_TRANSITIONS[task.status];
420
- if (!validTransitions.includes(updates.status)) {
421
- throw new SudocodeTaskBackendError(
422
- `Invalid status transition: ${task.status} -> ${updates.status}`,
423
- "INVALID_STATUS_TRANSITION",
424
- id
425
- );
426
- }
427
-
428
- this.eventStore.emit({
429
- type: "task",
430
- source: { agent_id: source },
431
- payload: {
432
- task_id: id,
433
- action: "status_change",
434
- details: { status: updates.status },
435
- },
436
- });
437
- }
438
-
439
- // Handle other updates
440
- if (updates.outputs !== undefined) {
441
- this.eventStore.emit({
442
- type: "task",
443
- source: { agent_id: source },
444
- payload: {
445
- task_id: id,
446
- action: "status_change",
447
- details: { outputs: updates.outputs },
448
- },
449
- });
450
- }
451
-
452
- if (updates.artifacts !== undefined) {
453
- this.eventStore.emit({
454
- type: "task",
455
- source: { agent_id: source },
456
- payload: {
457
- task_id: id,
458
- action: "status_change",
459
- details: { artifacts: updates.artifacts },
460
- },
461
- });
462
- }
463
-
464
- if (updates.description !== undefined) {
465
- this.eventStore.emit({
466
- type: "task",
467
- source: { agent_id: source },
468
- payload: {
469
- task_id: id,
470
- action: "status_change",
471
- details: { description: updates.description },
472
- },
473
- });
474
- }
475
-
476
- const updated = this.eventStore.getTask(id)!;
477
- return this.toExtendedTask(updated);
478
- }
479
-
480
- async delete(_id: TaskId): Promise<void> {
481
- // Tasks are immutable in event-sourced system - no delete operation
482
- // This matches InMemoryTaskBackend behavior for backend parity
483
- throw new SudocodeTaskBackendError(
484
- "Delete operation not supported - tasks are immutable",
485
- "NOT_SUPPORTED"
486
- );
487
- }
488
-
489
- // ─────────────────────────────────────────────────────────────────────────────
490
- // Status Transitions
491
- // ─────────────────────────────────────────────────────────────────────────────
492
-
493
- async assign(
494
- id: TaskId,
495
- agentId: AgentId,
496
- options?: AssignOptions
497
- ): Promise<void> {
498
- const task = this.eventStore.getTask(id);
499
- if (!task) {
500
- throw new SudocodeTaskBackendError(
501
- `Task not found: ${id}`,
502
- "TASK_NOT_FOUND",
503
- id
504
- );
505
- }
506
-
507
- this.eventStore.emit({
508
- type: "task",
509
- source: { agent_id: agentId },
510
- payload: {
511
- task_id: id,
512
- action: "assigned",
513
- details: {
514
- agent_id: agentId,
515
- role: options?.role,
516
- },
517
- },
518
- });
519
-
520
- // Note: We don't sync to sudocode on assign because "assigned" is a
521
- // macro-agent concept (task has a worker but work hasn't started).
522
- // Sudocode "in_progress" is synced when start() is called.
523
- }
524
-
525
- async unassign(id: TaskId): Promise<void> {
526
- const task = this.eventStore.getTask(id);
527
- if (!task) {
528
- throw new SudocodeTaskBackendError(
529
- `Task not found: ${id}`,
530
- "TASK_NOT_FOUND",
531
- id
532
- );
533
- }
534
-
535
- if (!task.assigned_agent) {
536
- throw new SudocodeTaskBackendError(
537
- `Task is not assigned: ${id}`,
538
- "TASK_NOT_ASSIGNED",
539
- id
540
- );
541
- }
542
-
543
- this.eventStore.emit({
544
- type: "task",
545
- source: { agent_id: task.assigned_agent },
546
- payload: {
547
- task_id: id,
548
- action: "unassigned",
549
- details: {
550
- agent_id: task.assigned_agent,
551
- },
552
- },
553
- });
554
- }
555
-
556
- async start(id: TaskId): Promise<void> {
557
- const task = this.eventStore.getTask(id);
558
- if (!task) {
559
- throw new SudocodeTaskBackendError(
560
- `Task not found: ${id}`,
561
- "TASK_NOT_FOUND",
562
- id
563
- );
564
- }
565
-
566
- const validTransitions = VALID_STATUS_TRANSITIONS[task.status];
567
- if (!validTransitions.includes("in_progress")) {
568
- throw new SudocodeTaskBackendError(
569
- `Invalid status transition: ${task.status} -> in_progress`,
570
- "INVALID_STATUS_TRANSITION",
571
- id
572
- );
573
- }
574
-
575
- this.eventStore.emit({
576
- type: "task",
577
- source: { agent_id: task.assigned_agent ?? task.created_by },
578
- payload: {
579
- task_id: id,
580
- action: "status_change",
581
- details: { status: "in_progress" },
582
- },
583
- });
584
-
585
- // Update issue status
586
- const externalId = this.getTaskExternalId(task);
587
- if (externalId && this.config.syncStatus) {
588
- try {
589
- await this.client.updateIssue(externalId, { status: "in_progress" });
590
- } catch {
591
- // Ignore errors syncing to sudocode
592
- }
593
- }
594
- }
595
-
596
- async complete(id: TaskId, outputs?: TaskOutputs): Promise<void> {
597
- const task = this.eventStore.getTask(id);
598
- if (!task) {
599
- throw new SudocodeTaskBackendError(
600
- `Task not found: ${id}`,
601
- "TASK_NOT_FOUND",
602
- id
603
- );
604
- }
605
-
606
- const validTransitions = VALID_STATUS_TRANSITIONS[task.status];
607
- if (!validTransitions.includes("completed")) {
608
- throw new SudocodeTaskBackendError(
609
- `Invalid status transition: ${task.status} -> completed`,
610
- "INVALID_STATUS_TRANSITION",
611
- id
612
- );
613
- }
614
-
615
- // Add outputs if provided
616
- if (outputs) {
617
- if (outputs.data) {
618
- this.eventStore.emit({
619
- type: "task",
620
- source: { agent_id: task.assigned_agent ?? task.created_by },
621
- payload: {
622
- task_id: id,
623
- action: "status_change",
624
- details: { outputs: outputs.data },
625
- },
626
- });
627
- }
628
- if (outputs.artifacts) {
629
- this.eventStore.emit({
630
- type: "task",
631
- source: { agent_id: task.assigned_agent ?? task.created_by },
632
- payload: {
633
- task_id: id,
634
- action: "status_change",
635
- details: { artifacts: outputs.artifacts },
636
- },
637
- });
638
- }
639
- }
640
-
641
- this.eventStore.emit({
642
- type: "task",
643
- source: { agent_id: task.assigned_agent ?? task.created_by },
644
- payload: {
645
- task_id: id,
646
- action: "completed",
647
- details: {},
648
- },
649
- });
650
-
651
- // Optionally close the issue
652
- const externalId = this.getTaskExternalId(task);
653
- if (externalId && this.config.autoCloseIssues) {
654
- try {
655
- await this.client.updateIssue(externalId, { status: "closed" });
656
- } catch {
657
- // Ignore errors syncing to sudocode
658
- }
659
- }
660
- }
661
-
662
- async fail(id: TaskId, error: TaskError): Promise<void> {
663
- const task = this.eventStore.getTask(id);
664
- if (!task) {
665
- throw new SudocodeTaskBackendError(
666
- `Task not found: ${id}`,
667
- "TASK_NOT_FOUND",
668
- id
669
- );
670
- }
671
-
672
- const validTransitions = VALID_STATUS_TRANSITIONS[task.status];
673
- if (!validTransitions.includes("failed")) {
674
- throw new SudocodeTaskBackendError(
675
- `Invalid status transition: ${task.status} -> failed`,
676
- "INVALID_STATUS_TRANSITION",
677
- id
678
- );
679
- }
680
-
681
- // Store error info in outputs
682
- this.eventStore.emit({
683
- type: "task",
684
- source: { agent_id: task.assigned_agent ?? task.created_by },
685
- payload: {
686
- task_id: id,
687
- action: "status_change",
688
- details: {
689
- outputs: {
690
- error: {
691
- message: error.message,
692
- code: error.code,
693
- details: error.details,
694
- },
695
- },
696
- },
697
- },
698
- });
699
-
700
- this.eventStore.emit({
701
- type: "task",
702
- source: { agent_id: task.assigned_agent ?? task.created_by },
703
- payload: {
704
- task_id: id,
705
- action: "failed",
706
- details: {},
707
- },
708
- });
709
- }
710
-
711
- // ─────────────────────────────────────────────────────────────────────────────
712
- // Queries
713
- // ─────────────────────────────────────────────────────────────────────────────
714
-
715
- async list(filter?: TaskFilter): Promise<ExtendedTask[]> {
716
- let tasks = this.eventStore.listTasks();
717
-
718
- if (filter) {
719
- // Filter by status
720
- if (filter.status) {
721
- const statuses = Array.isArray(filter.status)
722
- ? filter.status
723
- : [filter.status];
724
- tasks = tasks.filter((t) => statuses.includes(t.status));
725
- }
726
-
727
- if (filter.assigned_agent) {
728
- tasks = tasks.filter((t) => t.assigned_agent === filter.assigned_agent);
729
- }
730
-
731
- if (filter.parent_task) {
732
- tasks = tasks.filter((t) => t.parent_task === filter.parent_task);
733
- }
734
-
735
- if (filter.created_by) {
736
- tasks = tasks.filter((t) => t.created_by === filter.created_by);
737
- }
738
-
739
- if (filter.rootTasksOnly) {
740
- tasks = tasks.filter((t) => !t.parent_task);
741
- }
742
- }
743
-
744
- // Convert to ExtendedTask (async because of issue lookups)
745
- const extended = await Promise.all(
746
- tasks.map((t) => this.toExtendedTask(t))
747
- );
748
-
749
- // Filter blocked if needed
750
- if (!filter?.includeBlocked) {
751
- return extended.filter((t) => !t.isBlocked);
752
- }
753
-
754
- return extended;
755
- }
756
-
757
- async listReady(filter?: TaskFilter): Promise<ExtendedTask[]> {
758
- // Get pending/assigned tasks with isBlocked computed
759
- const tasks = await this.list({
760
- ...filter,
761
- status: filter?.status ?? ["pending", "assigned"],
762
- includeBlocked: true, // We'll filter manually
763
- });
764
-
765
- // Filter to ready tasks
766
- // isBlocked already checks both local and sudocode blockers,
767
- // respecting local task completion over stale sudocode state.
768
- const readyTasks: ExtendedTask[] = [];
769
-
770
- for (const task of tasks) {
771
- if (task.isBlocked) {
772
- continue;
773
- }
774
-
775
- // For bound tasks, also check issue status directly
776
- // (excludes tasks bound to issues with "blocked" status)
777
- if (task.external_id) {
778
- try {
779
- const issue = await this.client.getIssue(task.external_id);
780
- if (issue && issue.status === "blocked") {
781
- continue;
782
- }
783
- } catch {
784
- // If we can't fetch issue, include the task
785
- }
786
- }
787
-
788
- readyTasks.push(task);
789
- }
790
-
791
- return readyTasks;
792
- }
793
-
794
- async getChildren(parentId: TaskId): Promise<ExtendedTask[]> {
795
- const tasks = this.eventStore.listTasks();
796
- const children = tasks.filter((t) => t.parent_task === parentId);
797
- return Promise.all(children.map((t) => this.toExtendedTask(t)));
798
- }
799
-
800
- async getSubtaskStatus(parentId: TaskId): Promise<SubtaskStatus> {
801
- const children = await this.getChildren(parentId);
802
-
803
- const status: SubtaskStatus = {
804
- total: children.length,
805
- pending: 0,
806
- assigned: 0,
807
- in_progress: 0,
808
- completed: 0,
809
- failed: 0,
810
- allCompleted: false,
811
- anyFailed: false,
812
- };
813
-
814
- for (const task of children) {
815
- switch (task.status) {
816
- case "pending":
817
- status.pending++;
818
- break;
819
- case "assigned":
820
- status.assigned++;
821
- break;
822
- case "in_progress":
823
- status.in_progress++;
824
- break;
825
- case "completed":
826
- status.completed++;
827
- break;
828
- case "failed":
829
- status.failed++;
830
- break;
831
- }
832
- }
833
-
834
- status.allCompleted =
835
- status.total > 0 && status.completed === status.total;
836
- status.anyFailed = status.failed > 0;
837
-
838
- return status;
839
- }
840
-
841
- // ─────────────────────────────────────────────────────────────────────────────
842
- // Hierarchy
843
- // ─────────────────────────────────────────────────────────────────────────────
844
-
845
- async createSubtask(
846
- parentId: TaskId,
847
- options: CreateTaskOptions
848
- ): Promise<ExtendedTask> {
849
- return this.create({
850
- ...options,
851
- parent_task: parentId,
852
- });
853
- }
854
-
855
- // ─────────────────────────────────────────────────────────────────────────────
856
- // Dependencies
857
- // ─────────────────────────────────────────────────────────────────────────────
858
-
859
- async addBlocker(taskId: TaskId, blockerId: TaskId): Promise<void> {
860
- const task = this.eventStore.getTask(taskId);
861
- if (!task) {
862
- throw new SudocodeTaskBackendError(
863
- `Task not found: ${taskId}`,
864
- "TASK_NOT_FOUND",
865
- taskId
866
- );
867
- }
868
-
869
- const blocker = this.eventStore.getTask(blockerId);
870
- if (!blocker) {
871
- throw new SudocodeTaskBackendError(
872
- `Blocker task not found: ${blockerId}`,
873
- "TASK_NOT_FOUND",
874
- blockerId
875
- );
876
- }
877
-
878
- // Always track locally via EventStore
879
- this.eventStore.emit({
880
- type: "task",
881
- source: { agent_id: task.assigned_agent ?? task.created_by },
882
- payload: {
883
- task_id: taskId,
884
- action: "blocker_added",
885
- details: { blocker_id: blockerId },
886
- },
887
- });
888
-
889
- // If both tasks are bound to sudocode issues, create a sudocode relationship
890
- const taskIssueId = this.getIssueForTask(taskId);
891
- const blockerIssueId = this.getIssueForTask(blockerId);
892
- if (taskIssueId && blockerIssueId) {
893
- try {
894
- // In sudocode, "A blocks B" means A must complete before B
895
- // So we create: blockerIssue blocks taskIssue
896
- await this.client.createLink(blockerIssueId, taskIssueId, "blocks");
897
- } catch {
898
- // Log but don't fail - local tracking is the source of truth
899
- }
900
- }
901
- }
902
-
903
- async removeBlocker(taskId: TaskId, blockerId: TaskId): Promise<void> {
904
- const task = this.eventStore.getTask(taskId);
905
- if (!task) {
906
- throw new SudocodeTaskBackendError(
907
- `Task not found: ${taskId}`,
908
- "TASK_NOT_FOUND",
909
- taskId
910
- );
911
- }
912
-
913
- // Always update local EventStore tracking
914
- this.eventStore.emit({
915
- type: "task",
916
- source: { agent_id: task.assigned_agent ?? task.created_by },
917
- payload: {
918
- task_id: taskId,
919
- action: "blocker_removed",
920
- details: { blocker_id: blockerId },
921
- },
922
- });
923
-
924
- // If both tasks are bound to sudocode issues, remove the sudocode relationship
925
- const taskIssueId = this.getIssueForTask(taskId);
926
- const blockerIssueId = this.getIssueForTask(blockerId);
927
- if (taskIssueId && blockerIssueId) {
928
- try {
929
- await this.client.removeLink(blockerIssueId, taskIssueId, "blocks");
930
- } catch {
931
- // Log but don't fail - local tracking is the source of truth
932
- }
933
- }
934
- }
935
-
936
- async getBlockers(taskId: TaskId): Promise<ExtendedTask[]> {
937
- const task = this.eventStore.getTask(taskId);
938
- if (!task) {
939
- throw new SudocodeTaskBackendError(
940
- `Task not found: ${taskId}`,
941
- "TASK_NOT_FOUND",
942
- taskId
943
- );
944
- }
945
-
946
- // Track blockers by ID to avoid duplicates
947
- const blockerMap = new Map<TaskId, ExtendedTask>();
948
-
949
- // Get local task blockers
950
- const localBlockerIds = task.blockers ?? [];
951
- for (const blockerId of localBlockerIds) {
952
- const blocker = this.eventStore.getTask(blockerId);
953
- if (blocker && !blockerMap.has(blockerId)) {
954
- blockerMap.set(blockerId, await this.toExtendedTask(blocker));
955
- }
956
- }
957
-
958
- // If task is bound to an issue, also get sudocode blockers
959
- const taskIssueId = this.getIssueForTask(taskId);
960
- if (taskIssueId) {
961
- try {
962
- const issueBlockers = await this.client.getBlockers(taskIssueId);
963
- for (const issueBlocker of issueBlockers) {
964
- // Find tasks bound to this blocking issue
965
- const blockerTaskIds = this.getTasksByIssue(issueBlocker.id);
966
- for (const blockerTaskId of blockerTaskIds) {
967
- if (!blockerMap.has(blockerTaskId)) {
968
- const blockerTask = this.eventStore.getTask(blockerTaskId);
969
- if (blockerTask) {
970
- blockerMap.set(
971
- blockerTaskId,
972
- await this.toExtendedTask(blockerTask)
973
- );
974
- }
975
- }
976
- }
977
- }
978
- } catch {
979
- // Ignore errors fetching sudocode blockers - local is source of truth
980
- }
981
- }
982
-
983
- return Array.from(blockerMap.values());
984
- }
985
-
986
- async getBlocking(taskId: TaskId): Promise<ExtendedTask[]> {
987
- const task = this.eventStore.getTask(taskId);
988
- if (!task) {
989
- throw new SudocodeTaskBackendError(
990
- `Task not found: ${taskId}`,
991
- "TASK_NOT_FOUND",
992
- taskId
993
- );
994
- }
995
-
996
- // Track blocked tasks by ID to avoid duplicates
997
- const blockingMap = new Map<TaskId, ExtendedTask>();
998
-
999
- // Find all local tasks that have this task in their blockers
1000
- const allTasks = this.eventStore.listTasks();
1001
- const localBlocking = allTasks.filter((t) => t.blockers?.includes(taskId));
1002
- for (const blockedTask of localBlocking) {
1003
- if (!blockingMap.has(blockedTask.id)) {
1004
- blockingMap.set(blockedTask.id, await this.toExtendedTask(blockedTask));
1005
- }
1006
- }
1007
-
1008
- // If task is bound to an issue, also get sudocode blocking
1009
- const taskIssueId = this.getIssueForTask(taskId);
1010
- if (taskIssueId) {
1011
- try {
1012
- const issueBlocking = await this.client.getBlocking(taskIssueId);
1013
- for (const blockedIssue of issueBlocking) {
1014
- // Find tasks bound to this blocked issue
1015
- const blockedTaskIds = this.getTasksByIssue(blockedIssue.id);
1016
- for (const blockedTaskId of blockedTaskIds) {
1017
- if (!blockingMap.has(blockedTaskId)) {
1018
- const blockedTask = this.eventStore.getTask(blockedTaskId);
1019
- if (blockedTask) {
1020
- blockingMap.set(
1021
- blockedTaskId,
1022
- await this.toExtendedTask(blockedTask)
1023
- );
1024
- }
1025
- }
1026
- }
1027
- }
1028
- } catch {
1029
- // Ignore errors fetching sudocode blocking - local is source of truth
1030
- }
1031
- }
1032
-
1033
- return Array.from(blockingMap.values());
1034
- }
1035
-
1036
- // ─────────────────────────────────────────────────────────────────────────────
1037
- // History
1038
- // ─────────────────────────────────────────────────────────────────────────────
1039
-
1040
- async getAgentHistory(taskId: TaskId): Promise<AgentHistoryEntry[]> {
1041
- const task = this.eventStore.getTask(taskId);
1042
- if (!task) {
1043
- throw new SudocodeTaskBackendError(
1044
- `Task not found: ${taskId}`,
1045
- "TASK_NOT_FOUND",
1046
- taskId
1047
- );
1048
- }
1049
-
1050
- return task.agent_history ?? [];
1051
- }
1052
-
1053
- // ─────────────────────────────────────────────────────────────────────────────
1054
- // Event Subscriptions
1055
- // ─────────────────────────────────────────────────────────────────────────────
1056
-
1057
- onTaskChange(callback: TaskChangeCallback): Unsubscribe;
1058
- onTaskChange(taskId: TaskId, callback: TaskChangeCallback): Unsubscribe;
1059
- onTaskChange(
1060
- callbackOrTaskId: TaskChangeCallback | TaskId,
1061
- maybeCallback?: TaskChangeCallback
1062
- ): Unsubscribe {
1063
- const filterTaskId =
1064
- typeof callbackOrTaskId === "string" ? callbackOrTaskId : undefined;
1065
- const callback =
1066
- typeof callbackOrTaskId === "function"
1067
- ? callbackOrTaskId
1068
- : maybeCallback!;
1069
-
1070
- // Wrap EventStore's onTaskChange
1071
- return this.eventStore.onTaskChange((taskId, task) => {
1072
- if (filterTaskId && taskId !== filterTaskId) {
1073
- return;
1074
- }
1075
-
1076
- // Build TaskChangeEvent using sync conversion (checks local blockers only)
1077
- const event: TaskChangeEvent = {
1078
- type: task ? "updated" : "deleted",
1079
- taskId,
1080
- task: task ? this.toExtendedTaskSync(task) : ({} as ExtendedTask),
1081
- };
1082
-
1083
- callback(event);
1084
- });
1085
- }
1086
-
1087
- // ─────────────────────────────────────────────────────────────────────────────
1088
- // Sync Policy
1089
- // ─────────────────────────────────────────────────────────────────────────────
1090
-
1091
- /**
1092
- * Get the current sync policy
1093
- */
1094
- getSyncPolicy(): SyncPolicy {
1095
- return this.syncEngine.getPolicy();
1096
- }
1097
-
1098
- /**
1099
- * Subscribe to sync events from the policy engine
1100
- */
1101
- onSyncEvent(callback: SyncEventCallback): Unsubscribe {
1102
- return this.syncEngine.onSyncEvent(callback);
1103
- }
1104
-
1105
- // ─────────────────────────────────────────────────────────────────────────────
1106
- // Cleanup
1107
- // ─────────────────────────────────────────────────────────────────────────────
1108
-
1109
- /**
1110
- * Close the backend and release resources
1111
- */
1112
- close(): void {
1113
- if (this.issueChangeUnsubscribe) {
1114
- this.issueChangeUnsubscribe();
1115
- }
1116
- }
1117
-
1118
- // ─────────────────────────────────────────────────────────────────────────────
1119
- // Private Helpers
1120
- // ─────────────────────────────────────────────────────────────────────────────
1121
-
1122
- /**
1123
- * Get the external_id from a task (if stored in outputs)
1124
- */
1125
- private getTaskExternalId(task: Task): string | undefined {
1126
- // First check in-memory index (populated during create and rebuildIndex)
1127
- const fromIndex = this.issueByTask.get(task.id);
1128
- if (fromIndex) {
1129
- return fromIndex;
1130
- }
1131
- // Fall back to checking outputs (for persistence after restart via bindToIssue)
1132
- const outputs = task.outputs as Record<string, unknown> | undefined;
1133
- if (outputs?.external_id && typeof outputs.external_id === "string") {
1134
- return outputs.external_id;
1135
- }
1136
- return undefined;
1137
- }
1138
-
1139
- /**
1140
- * Convert a Task to ExtendedTask with computed isBlocked field.
1141
- * Checks both local blockers and sudocode issue blockers.
1142
- *
1143
- * For sudocode issue blockers, a blocker is considered resolved if:
1144
- * 1. The issue itself is closed/completed, OR
1145
- * 2. Any local task bound to that issue is completed (local takes precedence)
1146
- */
1147
- private async toExtendedTask(task: Task): Promise<ExtendedTask> {
1148
- let isBlocked = false;
1149
-
1150
- // Check local blockers first
1151
- const localBlockerIds = task.blockers ?? [];
1152
- for (const blockerId of localBlockerIds) {
1153
- const blocker = this.eventStore.getTask(blockerId);
1154
- if (blocker && blocker.status !== "completed") {
1155
- isBlocked = true;
1156
- break;
1157
- }
1158
- }
1159
-
1160
- // Check sudocode issue blockers if task is bound
1161
- if (!isBlocked) {
1162
- const externalId = this.getTaskExternalId(task);
1163
- if (externalId) {
1164
- try {
1165
- const issueBlockers = await this.client.getBlockers(externalId);
1166
- for (const blocker of issueBlockers) {
1167
- // Check if any local task bound to this blocking issue is completed
1168
- // Local task completion takes precedence over issue status
1169
- const localTasksForBlocker = this.getTasksByIssue(blocker.id);
1170
- const hasCompletedLocalTask = localTasksForBlocker.some(
1171
- (taskId) => {
1172
- const blockerTask = this.eventStore.getTask(taskId);
1173
- return blockerTask && blockerTask.status === "completed";
1174
- }
1175
- );
1176
-
1177
- // Blocker is unresolved if:
1178
- // - No local task is completed for this issue, AND
1179
- // - The issue itself is not complete
1180
- if (!hasCompletedLocalTask && !isIssueComplete(blocker.status)) {
1181
- isBlocked = true;
1182
- break;
1183
- }
1184
- }
1185
- } catch {
1186
- // If we can't fetch blockers, assume not blocked
1187
- }
1188
- }
1189
- }
1190
-
1191
- return {
1192
- ...task,
1193
- isBlocked,
1194
- external_id: this.getTaskExternalId(task),
1195
- };
1196
- }
1197
-
1198
- /**
1199
- * Synchronous version of toExtendedTask that only checks local blockers.
1200
- * Used in callbacks where async is not possible.
1201
- * Note: Does not check sudocode issue blockers - only local task blockers.
1202
- */
1203
- private toExtendedTaskSync(task: Task): ExtendedTask {
1204
- let isBlocked = false;
1205
-
1206
- // Check local blockers only (sync operation)
1207
- const localBlockerIds = task.blockers ?? [];
1208
- for (const blockerId of localBlockerIds) {
1209
- const blocker = this.eventStore.getTask(blockerId);
1210
- if (blocker && blocker.status !== "completed") {
1211
- isBlocked = true;
1212
- break;
1213
- }
1214
- }
1215
-
1216
- return {
1217
- ...task,
1218
- isBlocked,
1219
- external_id: this.getTaskExternalId(task),
1220
- };
1221
- }
1222
- }
1223
-
1224
- /**
1225
- * Create a SudocodeTaskBackend instance.
1226
- *
1227
- * @param eventStore - EventStore for local task storage
1228
- * @param client - SudocodeClient for issue access
1229
- * @param config - Optional configuration
1230
- */
1231
- export function createSudocodeTaskBackend(
1232
- eventStore: EventStore,
1233
- client: SudocodeClient,
1234
- config?: SudocodeTaskBackendConfig
1235
- ): SudocodeTaskBackend {
1236
- return new SudocodeTaskBackend(eventStore, client, config);
1237
- }