macro-agent 0.1.1 → 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 (406) hide show
  1. package/.sudocode/issues.jsonl +28 -0
  2. package/.sudocode/specs.jsonl +4 -0
  3. package/CLAUDE.md +9 -3
  4. package/dist/agent/agent-manager.d.ts.map +1 -1
  5. package/dist/agent/agent-manager.js +111 -48
  6. package/dist/agent/agent-manager.js.map +1 -1
  7. package/dist/agent/types.d.ts +7 -0
  8. package/dist/agent/types.d.ts.map +1 -1
  9. package/dist/agent/types.js.map +1 -1
  10. package/dist/api/server.d.ts +5 -1
  11. package/dist/api/server.d.ts.map +1 -1
  12. package/dist/api/server.js +100 -3
  13. package/dist/api/server.js.map +1 -1
  14. package/dist/api/types.d.ts +1 -1
  15. package/dist/api/types.d.ts.map +1 -1
  16. package/dist/cli/acp.d.ts.map +1 -1
  17. package/dist/cli/acp.js +71 -1
  18. package/dist/cli/acp.js.map +1 -1
  19. package/dist/cli/index.js +5 -1
  20. package/dist/cli/index.js.map +1 -1
  21. package/dist/cli/mcp.js +27 -8
  22. package/dist/cli/mcp.js.map +1 -1
  23. package/dist/config/project-config.d.ts +13 -2
  24. package/dist/config/project-config.d.ts.map +1 -1
  25. package/dist/config/project-config.js +12 -2
  26. package/dist/config/project-config.js.map +1 -1
  27. package/dist/index.d.ts +1 -0
  28. package/dist/index.d.ts.map +1 -1
  29. package/dist/index.js +2 -0
  30. package/dist/index.js.map +1 -1
  31. package/dist/lifecycle/handlers/index.d.ts +7 -3
  32. package/dist/lifecycle/handlers/index.d.ts.map +1 -1
  33. package/dist/lifecycle/handlers/index.js +25 -8
  34. package/dist/lifecycle/handlers/index.js.map +1 -1
  35. package/dist/lifecycle/types.d.ts +2 -0
  36. package/dist/lifecycle/types.d.ts.map +1 -1
  37. package/dist/lifecycle/types.js.map +1 -1
  38. package/dist/map/adapter/extensions/index.d.ts +4 -1
  39. package/dist/map/adapter/extensions/index.d.ts.map +1 -1
  40. package/dist/map/adapter/extensions/index.js +27 -0
  41. package/dist/map/adapter/extensions/index.js.map +1 -1
  42. package/dist/map/adapter/extensions/streams.d.ts +95 -0
  43. package/dist/map/adapter/extensions/streams.d.ts.map +1 -0
  44. package/dist/map/adapter/extensions/streams.js +515 -0
  45. package/dist/map/adapter/extensions/streams.js.map +1 -0
  46. package/dist/map/adapter/index.d.ts +1 -1
  47. package/dist/map/adapter/index.d.ts.map +1 -1
  48. package/dist/map/adapter/index.js +3 -1
  49. package/dist/map/adapter/index.js.map +1 -1
  50. package/dist/map/adapter/types.d.ts +1 -1
  51. package/dist/map/adapter/types.d.ts.map +1 -1
  52. package/dist/mcp/mcp-server.d.ts +2 -0
  53. package/dist/mcp/mcp-server.d.ts.map +1 -1
  54. package/dist/mcp/mcp-server.js +12 -3
  55. package/dist/mcp/mcp-server.js.map +1 -1
  56. package/dist/mcp/tools/done.d.ts.map +1 -1
  57. package/dist/mcp/tools/done.js +18 -0
  58. package/dist/mcp/tools/done.js.map +1 -1
  59. package/dist/roles/builtin/coordinator.d.ts.map +1 -1
  60. package/dist/roles/builtin/coordinator.js +2 -1
  61. package/dist/roles/builtin/coordinator.js.map +1 -1
  62. package/dist/roles/builtin/integrator.d.ts.map +1 -1
  63. package/dist/roles/builtin/integrator.js +2 -1
  64. package/dist/roles/builtin/integrator.js.map +1 -1
  65. package/dist/roles/builtin/worker.d.ts.map +1 -1
  66. package/dist/roles/builtin/worker.js +3 -1
  67. package/dist/roles/builtin/worker.js.map +1 -1
  68. package/dist/roles/capabilities.d.ts +6 -0
  69. package/dist/roles/capabilities.d.ts.map +1 -1
  70. package/dist/roles/capabilities.js +10 -0
  71. package/dist/roles/capabilities.js.map +1 -1
  72. package/dist/roles/config-loader.d.ts +1 -1
  73. package/dist/roles/config-loader.d.ts.map +1 -1
  74. package/dist/roles/config-loader.js +3 -2
  75. package/dist/roles/config-loader.js.map +1 -1
  76. package/dist/roles/types.d.ts +3 -1
  77. package/dist/roles/types.d.ts.map +1 -1
  78. package/dist/server/combined-server.d.ts +8 -1
  79. package/dist/server/combined-server.d.ts.map +1 -1
  80. package/dist/server/combined-server.js +6 -2
  81. package/dist/server/combined-server.js.map +1 -1
  82. package/dist/store/event-store.d.ts.map +1 -1
  83. package/dist/store/event-store.js +12 -5
  84. package/dist/store/event-store.js.map +1 -1
  85. package/dist/store/instance.d.ts +1 -1
  86. package/dist/store/instance.d.ts.map +1 -1
  87. package/dist/store/instance.js +2 -2
  88. package/dist/store/instance.js.map +1 -1
  89. package/dist/store/types/agents.d.ts +5 -0
  90. package/dist/store/types/agents.d.ts.map +1 -1
  91. package/dist/task/backend/opentasks/daemon-manager.d.ts.map +1 -1
  92. package/dist/task/backend/opentasks/daemon-manager.js +1 -1
  93. package/dist/task/backend/opentasks/daemon-manager.js.map +1 -1
  94. package/dist/teams/index.d.ts +3 -1
  95. package/dist/teams/index.d.ts.map +1 -1
  96. package/dist/teams/index.js +2 -0
  97. package/dist/teams/index.js.map +1 -1
  98. package/dist/teams/seed-defaults.d.ts +20 -0
  99. package/dist/teams/seed-defaults.d.ts.map +1 -0
  100. package/dist/teams/seed-defaults.js +71 -0
  101. package/dist/teams/seed-defaults.js.map +1 -0
  102. package/dist/teams/team-loader.d.ts +6 -2
  103. package/dist/teams/team-loader.d.ts.map +1 -1
  104. package/dist/teams/team-loader.js +154 -162
  105. package/dist/teams/team-loader.js.map +1 -1
  106. package/dist/teams/team-manager.d.ts +112 -0
  107. package/dist/teams/team-manager.d.ts.map +1 -0
  108. package/dist/teams/team-manager.js +305 -0
  109. package/dist/teams/team-manager.js.map +1 -0
  110. package/dist/teams/team-runtime.d.ts +125 -19
  111. package/dist/teams/team-runtime.d.ts.map +1 -1
  112. package/dist/teams/team-runtime.js +527 -119
  113. package/dist/teams/team-runtime.js.map +1 -1
  114. package/dist/teams/types.d.ts +41 -151
  115. package/dist/teams/types.d.ts.map +1 -1
  116. package/dist/teams/types.js +2 -3
  117. package/dist/teams/types.js.map +1 -1
  118. package/docs/teams.md +73 -0
  119. package/package.json +2 -1
  120. package/references/minimem/.claude/settings.json +7 -0
  121. package/references/minimem/.sudocode/issues.jsonl +18 -0
  122. package/references/minimem/.sudocode/specs.jsonl +1 -0
  123. package/references/minimem/CLAUDE.md +310 -0
  124. package/references/minimem/README.md +562 -0
  125. package/references/minimem/claude-plugin/.claude-plugin/plugin.json +10 -0
  126. package/references/minimem/claude-plugin/.mcp.json +7 -0
  127. package/references/minimem/claude-plugin/README.md +158 -0
  128. package/references/minimem/claude-plugin/commands/recall.md +47 -0
  129. package/references/minimem/claude-plugin/commands/remember.md +41 -0
  130. package/references/minimem/claude-plugin/hooks/__tests__/hooks.test.ts +272 -0
  131. package/references/minimem/claude-plugin/hooks/hooks.json +27 -0
  132. package/references/minimem/claude-plugin/hooks/session-end.sh +86 -0
  133. package/references/minimem/claude-plugin/hooks/session-start.sh +85 -0
  134. package/references/minimem/claude-plugin/skills/memory/SKILL.md +108 -0
  135. package/references/minimem/media/banner.png +0 -0
  136. package/references/minimem/package-lock.json +5373 -0
  137. package/references/minimem/package.json +72 -0
  138. package/references/minimem/scripts/postbuild.js +35 -0
  139. package/references/minimem/src/__tests__/edge-cases.test.ts +371 -0
  140. package/references/minimem/src/__tests__/errors.test.ts +265 -0
  141. package/references/minimem/src/__tests__/helpers.ts +199 -0
  142. package/references/minimem/src/__tests__/internal.test.ts +407 -0
  143. package/references/minimem/src/__tests__/knowledge.test.ts +287 -0
  144. package/references/minimem/src/__tests__/minimem.integration.test.ts +1127 -0
  145. package/references/minimem/src/__tests__/session.test.ts +190 -0
  146. package/references/minimem/src/cli/__tests__/commands.test.ts +759 -0
  147. package/references/minimem/src/cli/commands/__tests__/conflicts.test.ts +141 -0
  148. package/references/minimem/src/cli/commands/append.ts +76 -0
  149. package/references/minimem/src/cli/commands/config.ts +262 -0
  150. package/references/minimem/src/cli/commands/conflicts.ts +413 -0
  151. package/references/minimem/src/cli/commands/daemon.ts +169 -0
  152. package/references/minimem/src/cli/commands/index.ts +12 -0
  153. package/references/minimem/src/cli/commands/init.ts +88 -0
  154. package/references/minimem/src/cli/commands/mcp.ts +177 -0
  155. package/references/minimem/src/cli/commands/push-pull.ts +213 -0
  156. package/references/minimem/src/cli/commands/search.ts +158 -0
  157. package/references/minimem/src/cli/commands/status.ts +84 -0
  158. package/references/minimem/src/cli/commands/sync-init.ts +290 -0
  159. package/references/minimem/src/cli/commands/sync.ts +70 -0
  160. package/references/minimem/src/cli/commands/upsert.ts +197 -0
  161. package/references/minimem/src/cli/config.ts +584 -0
  162. package/references/minimem/src/cli/index.ts +264 -0
  163. package/references/minimem/src/cli/shared.ts +161 -0
  164. package/references/minimem/src/cli/sync/__tests__/central.test.ts +152 -0
  165. package/references/minimem/src/cli/sync/__tests__/conflicts.test.ts +209 -0
  166. package/references/minimem/src/cli/sync/__tests__/daemon.test.ts +118 -0
  167. package/references/minimem/src/cli/sync/__tests__/detection.test.ts +207 -0
  168. package/references/minimem/src/cli/sync/__tests__/integration.test.ts +476 -0
  169. package/references/minimem/src/cli/sync/__tests__/registry.test.ts +363 -0
  170. package/references/minimem/src/cli/sync/__tests__/state.test.ts +255 -0
  171. package/references/minimem/src/cli/sync/__tests__/validation.test.ts +193 -0
  172. package/references/minimem/src/cli/sync/__tests__/watcher.test.ts +178 -0
  173. package/references/minimem/src/cli/sync/central.ts +292 -0
  174. package/references/minimem/src/cli/sync/conflicts.ts +204 -0
  175. package/references/minimem/src/cli/sync/daemon.ts +407 -0
  176. package/references/minimem/src/cli/sync/detection.ts +138 -0
  177. package/references/minimem/src/cli/sync/index.ts +107 -0
  178. package/references/minimem/src/cli/sync/operations.ts +373 -0
  179. package/references/minimem/src/cli/sync/registry.ts +279 -0
  180. package/references/minimem/src/cli/sync/state.ts +355 -0
  181. package/references/minimem/src/cli/sync/validation.ts +206 -0
  182. package/references/minimem/src/cli/sync/watcher.ts +234 -0
  183. package/references/minimem/src/cli/version.ts +34 -0
  184. package/references/minimem/src/core/index.ts +9 -0
  185. package/references/minimem/src/core/indexer.ts +628 -0
  186. package/references/minimem/src/core/searcher.ts +221 -0
  187. package/references/minimem/src/db/schema.ts +183 -0
  188. package/references/minimem/src/db/sqlite-vec.ts +24 -0
  189. package/references/minimem/src/embeddings/__tests__/embeddings.test.ts +431 -0
  190. package/references/minimem/src/embeddings/batch-gemini.ts +392 -0
  191. package/references/minimem/src/embeddings/batch-openai.ts +409 -0
  192. package/references/minimem/src/embeddings/embeddings.ts +434 -0
  193. package/references/minimem/src/index.ts +109 -0
  194. package/references/minimem/src/internal.ts +299 -0
  195. package/references/minimem/src/minimem.ts +1276 -0
  196. package/references/minimem/src/search/__tests__/hybrid.test.ts +247 -0
  197. package/references/minimem/src/search/graph.ts +234 -0
  198. package/references/minimem/src/search/hybrid.ts +151 -0
  199. package/references/minimem/src/search/search.ts +256 -0
  200. package/references/minimem/src/server/__tests__/mcp.test.ts +341 -0
  201. package/references/minimem/src/server/__tests__/tools.test.ts +364 -0
  202. package/references/minimem/src/server/mcp.ts +326 -0
  203. package/references/minimem/src/server/tools.ts +720 -0
  204. package/references/minimem/src/session.ts +460 -0
  205. package/references/minimem/tsconfig.json +19 -0
  206. package/references/minimem/tsup.config.ts +26 -0
  207. package/references/minimem/vitest.config.ts +24 -0
  208. package/references/openteams/.claude/settings.json +6 -0
  209. package/references/openteams/README.md +1 -0
  210. package/references/openteams/SKILL.md +341 -0
  211. package/references/openteams/design.md +411 -0
  212. package/references/openteams/examples/bmad-method/prompts/analyst/ROLE.md +16 -0
  213. package/references/openteams/examples/bmad-method/prompts/analyst/SOUL.md +5 -0
  214. package/references/openteams/examples/bmad-method/prompts/architect/ROLE.md +24 -0
  215. package/references/openteams/examples/bmad-method/prompts/architect/SOUL.md +5 -0
  216. package/references/openteams/examples/bmad-method/prompts/developer/ROLE.md +25 -0
  217. package/references/openteams/examples/bmad-method/prompts/developer/SOUL.md +5 -0
  218. package/references/openteams/examples/bmad-method/prompts/master/ROLE.md +21 -0
  219. package/references/openteams/examples/bmad-method/prompts/master/SOUL.md +5 -0
  220. package/references/openteams/examples/bmad-method/prompts/pm/ROLE.md +20 -0
  221. package/references/openteams/examples/bmad-method/prompts/pm/SOUL.md +5 -0
  222. package/references/openteams/examples/bmad-method/prompts/qa/ROLE.md +17 -0
  223. package/references/openteams/examples/bmad-method/prompts/qa/SOUL.md +5 -0
  224. package/references/openteams/examples/bmad-method/prompts/quick-flow-dev/ROLE.md +23 -0
  225. package/references/openteams/examples/bmad-method/prompts/quick-flow-dev/SOUL.md +5 -0
  226. package/references/openteams/examples/bmad-method/prompts/scrum-master/ROLE.md +27 -0
  227. package/references/openteams/examples/bmad-method/prompts/scrum-master/SOUL.md +5 -0
  228. package/references/openteams/examples/bmad-method/prompts/tech-writer/ROLE.md +21 -0
  229. package/references/openteams/examples/bmad-method/prompts/tech-writer/SOUL.md +5 -0
  230. package/references/openteams/examples/bmad-method/prompts/ux-designer/ROLE.md +16 -0
  231. package/references/openteams/examples/bmad-method/prompts/ux-designer/SOUL.md +5 -0
  232. package/references/openteams/examples/bmad-method/roles/analyst.yaml +9 -0
  233. package/references/openteams/examples/bmad-method/roles/architect.yaml +9 -0
  234. package/references/openteams/examples/bmad-method/roles/developer.yaml +8 -0
  235. package/references/openteams/examples/bmad-method/roles/master.yaml +8 -0
  236. package/references/openteams/examples/bmad-method/roles/pm.yaml +9 -0
  237. package/references/openteams/examples/bmad-method/roles/qa.yaml +8 -0
  238. package/references/openteams/examples/bmad-method/roles/quick-flow-dev.yaml +8 -0
  239. package/references/openteams/examples/bmad-method/roles/scrum-master.yaml +9 -0
  240. package/references/openteams/examples/bmad-method/roles/tech-writer.yaml +8 -0
  241. package/references/openteams/examples/bmad-method/roles/ux-designer.yaml +8 -0
  242. package/references/openteams/examples/bmad-method/team.yaml +161 -0
  243. package/references/openteams/examples/get-shit-done/prompts/codebase-mapper/ROLE.md +17 -0
  244. package/references/openteams/examples/get-shit-done/prompts/codebase-mapper/SOUL.md +5 -0
  245. package/references/openteams/examples/get-shit-done/prompts/debugger/ROLE.md +25 -0
  246. package/references/openteams/examples/get-shit-done/prompts/debugger/SOUL.md +5 -0
  247. package/references/openteams/examples/get-shit-done/prompts/executor/ROLE.md +34 -0
  248. package/references/openteams/examples/get-shit-done/prompts/executor/SOUL.md +5 -0
  249. package/references/openteams/examples/get-shit-done/prompts/integration-checker/ROLE.md +18 -0
  250. package/references/openteams/examples/get-shit-done/prompts/integration-checker/SOUL.md +3 -0
  251. package/references/openteams/examples/get-shit-done/prompts/orchestrator/ROLE.md +42 -0
  252. package/references/openteams/examples/get-shit-done/prompts/orchestrator/SOUL.md +5 -0
  253. package/references/openteams/examples/get-shit-done/prompts/phase-researcher/ROLE.md +15 -0
  254. package/references/openteams/examples/get-shit-done/prompts/phase-researcher/SOUL.md +3 -0
  255. package/references/openteams/examples/get-shit-done/prompts/plan-checker/ROLE.md +17 -0
  256. package/references/openteams/examples/get-shit-done/prompts/plan-checker/SOUL.md +3 -0
  257. package/references/openteams/examples/get-shit-done/prompts/planner/ROLE.md +28 -0
  258. package/references/openteams/examples/get-shit-done/prompts/planner/SOUL.md +5 -0
  259. package/references/openteams/examples/get-shit-done/prompts/project-researcher/ROLE.md +16 -0
  260. package/references/openteams/examples/get-shit-done/prompts/project-researcher/SOUL.md +3 -0
  261. package/references/openteams/examples/get-shit-done/prompts/research-synthesizer/ROLE.md +13 -0
  262. package/references/openteams/examples/get-shit-done/prompts/research-synthesizer/SOUL.md +3 -0
  263. package/references/openteams/examples/get-shit-done/prompts/roadmapper/ROLE.md +14 -0
  264. package/references/openteams/examples/get-shit-done/prompts/roadmapper/SOUL.md +3 -0
  265. package/references/openteams/examples/get-shit-done/prompts/verifier/ROLE.md +19 -0
  266. package/references/openteams/examples/get-shit-done/prompts/verifier/SOUL.md +5 -0
  267. package/references/openteams/examples/get-shit-done/roles/codebase-mapper.yaml +8 -0
  268. package/references/openteams/examples/get-shit-done/roles/debugger.yaml +8 -0
  269. package/references/openteams/examples/get-shit-done/roles/executor.yaml +8 -0
  270. package/references/openteams/examples/get-shit-done/roles/integration-checker.yaml +8 -0
  271. package/references/openteams/examples/get-shit-done/roles/orchestrator.yaml +9 -0
  272. package/references/openteams/examples/get-shit-done/roles/phase-researcher.yaml +7 -0
  273. package/references/openteams/examples/get-shit-done/roles/plan-checker.yaml +8 -0
  274. package/references/openteams/examples/get-shit-done/roles/planner.yaml +8 -0
  275. package/references/openteams/examples/get-shit-done/roles/project-researcher.yaml +8 -0
  276. package/references/openteams/examples/get-shit-done/roles/research-synthesizer.yaml +7 -0
  277. package/references/openteams/examples/get-shit-done/roles/roadmapper.yaml +7 -0
  278. package/references/openteams/examples/get-shit-done/roles/verifier.yaml +8 -0
  279. package/references/openteams/examples/get-shit-done/team.yaml +154 -0
  280. package/references/openteams/package-lock.json +2181 -0
  281. package/references/openteams/package.json +48 -0
  282. package/references/openteams/schema/role.schema.json +125 -0
  283. package/references/openteams/schema/team.schema.json +284 -0
  284. package/references/openteams/src/cli/agent.ts +104 -0
  285. package/references/openteams/src/cli/cli.test.ts +381 -0
  286. package/references/openteams/src/cli/generate.ts +220 -0
  287. package/references/openteams/src/cli/message.ts +241 -0
  288. package/references/openteams/src/cli/task.ts +154 -0
  289. package/references/openteams/src/cli/team.ts +104 -0
  290. package/references/openteams/src/cli/template.ts +207 -0
  291. package/references/openteams/src/cli.ts +45 -0
  292. package/references/openteams/src/db/database.test.ts +185 -0
  293. package/references/openteams/src/db/database.ts +240 -0
  294. package/references/openteams/src/generators/agent-prompt-generator.test.ts +332 -0
  295. package/references/openteams/src/generators/agent-prompt-generator.ts +521 -0
  296. package/references/openteams/src/generators/package-generator.test.ts +129 -0
  297. package/references/openteams/src/generators/package-generator.ts +102 -0
  298. package/references/openteams/src/generators/skill-generator.test.ts +246 -0
  299. package/references/openteams/src/generators/skill-generator.ts +374 -0
  300. package/references/openteams/src/index.ts +104 -0
  301. package/references/openteams/src/services/agent-service.test.ts +158 -0
  302. package/references/openteams/src/services/agent-service.ts +84 -0
  303. package/references/openteams/src/services/communication-service.test.ts +455 -0
  304. package/references/openteams/src/services/communication-service.ts +371 -0
  305. package/references/openteams/src/services/message-service.test.ts +342 -0
  306. package/references/openteams/src/services/message-service.ts +203 -0
  307. package/references/openteams/src/services/task-service.test.ts +434 -0
  308. package/references/openteams/src/services/task-service.ts +239 -0
  309. package/references/openteams/src/services/team-service.test.ts +181 -0
  310. package/references/openteams/src/services/team-service.ts +139 -0
  311. package/references/openteams/src/services/template-service.test.ts +306 -0
  312. package/references/openteams/src/services/template-service.ts +182 -0
  313. package/references/openteams/src/spawner/acp-factory.ts +96 -0
  314. package/references/openteams/src/spawner/interface.ts +31 -0
  315. package/references/openteams/src/spawner/mock.test.ts +93 -0
  316. package/references/openteams/src/spawner/mock.ts +59 -0
  317. package/references/openteams/src/template/loader.test.ts +1319 -0
  318. package/references/openteams/src/template/loader.ts +698 -0
  319. package/references/openteams/src/template/types.ts +200 -0
  320. package/references/openteams/src/types.ts +205 -0
  321. package/references/openteams/tsconfig.json +18 -0
  322. package/references/openteams/vitest.config.ts +9 -0
  323. package/references/skill-tree/.claude/settings.json +6 -0
  324. package/references/skill-tree/.sudocode/issues.jsonl +11 -0
  325. package/references/skill-tree/.sudocode/specs.jsonl +1 -0
  326. package/references/skill-tree/CLAUDE.md +150 -0
  327. package/references/skill-tree/README.md +324 -0
  328. package/references/skill-tree/docs/GAPS_v1.md +221 -0
  329. package/references/skill-tree/docs/INTEGRATION_PLAN.md +467 -0
  330. package/references/skill-tree/docs/TODOS.md +91 -0
  331. package/references/skill-tree/docs/anthropic_skill_guide.md +1364 -0
  332. package/references/skill-tree/docs/design/federated-skill-trees.md +524 -0
  333. package/references/skill-tree/docs/design/multi-agent-sync.md +759 -0
  334. package/references/skill-tree/docs/scraper/BRAINSTORM.md +583 -0
  335. package/references/skill-tree/docs/scraper/POC_PLAN.md +420 -0
  336. package/references/skill-tree/docs/scraper/README.md +170 -0
  337. package/references/skill-tree/examples/basic-usage.ts +190 -0
  338. package/references/skill-tree/package-lock.json +1509 -0
  339. package/references/skill-tree/package.json +66 -0
  340. package/references/skill-tree/scraper/README.md +123 -0
  341. package/references/skill-tree/scraper/docs/DESIGN.md +683 -0
  342. package/references/skill-tree/scraper/docs/PLAN.md +336 -0
  343. package/references/skill-tree/scraper/drizzle.config.ts +10 -0
  344. package/references/skill-tree/scraper/package-lock.json +6329 -0
  345. package/references/skill-tree/scraper/package.json +68 -0
  346. package/references/skill-tree/scraper/test/fixtures/invalid-skill/missing-description.md +7 -0
  347. package/references/skill-tree/scraper/test/fixtures/invalid-skill/missing-name.md +7 -0
  348. package/references/skill-tree/scraper/test/fixtures/minimal-skill/SKILL.md +27 -0
  349. package/references/skill-tree/scraper/test/fixtures/skill-json/SKILL.json +21 -0
  350. package/references/skill-tree/scraper/test/fixtures/skill-with-meta/SKILL.md +54 -0
  351. package/references/skill-tree/scraper/test/fixtures/skill-with-meta/_meta.json +24 -0
  352. package/references/skill-tree/scraper/test/fixtures/valid-skill/SKILL.md +93 -0
  353. package/references/skill-tree/scraper/test/fixtures/valid-skill/_meta.json +22 -0
  354. package/references/skill-tree/scraper/tsup.config.ts +14 -0
  355. package/references/skill-tree/scraper/vitest.config.ts +17 -0
  356. package/references/skill-tree/scripts/convert-to-vitest.ts +166 -0
  357. package/references/skill-tree/skills/skill-writer/SKILL.md +339 -0
  358. package/references/skill-tree/skills/skill-writer/references/examples.md +326 -0
  359. package/references/skill-tree/skills/skill-writer/references/patterns.md +210 -0
  360. package/references/skill-tree/skills/skill-writer/references/quality-checklist.md +123 -0
  361. package/references/skill-tree/test/run-all.ts +106 -0
  362. package/references/skill-tree/test/utils.ts +128 -0
  363. package/references/skill-tree/vitest.config.ts +16 -0
  364. package/src/agent/agent-manager.ts +143 -72
  365. package/src/agent/types.ts +9 -0
  366. package/src/api/__tests__/server.test.ts +203 -4
  367. package/src/api/server.ts +130 -5
  368. package/src/api/types.ts +3 -1
  369. package/src/cli/acp.ts +68 -1
  370. package/src/cli/index.ts +5 -1
  371. package/src/cli/mcp.ts +27 -13
  372. package/src/config/project-config.ts +27 -3
  373. package/src/index.ts +3 -0
  374. package/src/lifecycle/__tests__/handlers.test.ts +53 -0
  375. package/src/lifecycle/handlers/index.ts +25 -8
  376. package/src/lifecycle/types.ts +3 -0
  377. package/src/map/adapter/__tests__/stream-extensions.test.ts +494 -0
  378. package/src/map/adapter/extensions/index.ts +36 -0
  379. package/src/map/adapter/extensions/streams.ts +839 -0
  380. package/src/map/adapter/index.ts +5 -0
  381. package/src/map/adapter/types.ts +8 -1
  382. package/src/mcp/mcp-server.ts +14 -3
  383. package/src/mcp/tools/done.ts +19 -0
  384. package/src/roles/builtin/coordinator.ts +2 -0
  385. package/src/roles/builtin/integrator.ts +2 -0
  386. package/src/roles/builtin/worker.ts +3 -0
  387. package/src/roles/capabilities.ts +11 -0
  388. package/src/roles/config-loader.ts +3 -2
  389. package/src/roles/types.ts +7 -0
  390. package/src/server/combined-server.ts +15 -1
  391. package/src/store/__tests__/event-store-oob.test.ts +109 -0
  392. package/src/store/event-store.ts +13 -3
  393. package/src/store/instance.ts +2 -2
  394. package/src/store/types/agents.ts +5 -0
  395. package/src/task/backend/__tests__/memory-pull-mode.test.ts +153 -0
  396. package/src/task/backend/opentasks/daemon-manager.ts +4 -1
  397. package/src/teams/CLAUDE.md +180 -0
  398. package/src/teams/__tests__/e2e/workspace-isolation.e2e.test.ts +1263 -0
  399. package/src/teams/__tests__/team-manager.test.ts +814 -0
  400. package/src/teams/__tests__/team-system.test.ts +1291 -8
  401. package/src/teams/index.ts +21 -3
  402. package/src/teams/seed-defaults.ts +79 -0
  403. package/src/teams/team-loader.ts +200 -234
  404. package/src/teams/team-manager.ts +387 -0
  405. package/src/teams/team-runtime.ts +590 -121
  406. package/src/teams/types.ts +99 -200
@@ -0,0 +1,371 @@
1
+ import type Database from "better-sqlite3";
2
+ import type { EnforcementMode } from "../types";
3
+ import type {
4
+ CommunicationConfig,
5
+ SignalEvent,
6
+ SignalEventRow,
7
+ EmitSignalOptions,
8
+ } from "../template/types";
9
+
10
+ function rowToEvent(row: SignalEventRow): SignalEvent {
11
+ return {
12
+ ...row,
13
+ payload: JSON.parse(row.payload || "{}"),
14
+ };
15
+ }
16
+
17
+ export interface EmitResult {
18
+ event: SignalEvent;
19
+ permitted: boolean;
20
+ enforcement: EnforcementMode;
21
+ }
22
+
23
+ export interface ChannelInfo {
24
+ name: string;
25
+ description: string | null;
26
+ signals: string[];
27
+ }
28
+
29
+ export interface SubscriptionInfo {
30
+ role: string;
31
+ channel: string;
32
+ signal: string | null;
33
+ }
34
+
35
+ export interface PeerRouteInfo {
36
+ from_role: string;
37
+ to_role: string;
38
+ via: string;
39
+ signals: string[];
40
+ }
41
+
42
+ export class CommunicationService {
43
+ constructor(private db: Database.Database) {}
44
+
45
+ /**
46
+ * Apply a CommunicationConfig from a template manifest to a team.
47
+ * Populates channels, signals, subscriptions, emissions, peer routes.
48
+ */
49
+ applyConfig(teamName: string, config: CommunicationConfig): void {
50
+ // Store enforcement mode on the team
51
+ if (config.enforcement) {
52
+ this.db
53
+ .prepare("UPDATE teams SET enforcement = ? WHERE name = ?")
54
+ .run(config.enforcement, teamName);
55
+ }
56
+
57
+ // Channels + signals
58
+ if (config.channels) {
59
+ for (const [name, def] of Object.entries(config.channels)) {
60
+ const result = this.db
61
+ .prepare(
62
+ "INSERT OR IGNORE INTO channels (team_name, name, description) VALUES (?, ?, ?)"
63
+ )
64
+ .run(teamName, name, def.description ?? null);
65
+
66
+ const channelId =
67
+ result.changes > 0
68
+ ? Number(result.lastInsertRowid)
69
+ : (
70
+ this.db
71
+ .prepare(
72
+ "SELECT id FROM channels WHERE team_name = ? AND name = ?"
73
+ )
74
+ .get(teamName, name) as { id: number }
75
+ ).id;
76
+
77
+ const insertSignal = this.db.prepare(
78
+ "INSERT OR IGNORE INTO channel_signals (channel_id, signal) VALUES (?, ?)"
79
+ );
80
+ for (const signal of def.signals) {
81
+ insertSignal.run(channelId, signal);
82
+ }
83
+ }
84
+ }
85
+
86
+ // Subscriptions
87
+ if (config.subscriptions) {
88
+ const insertSub = this.db.prepare(
89
+ "INSERT OR IGNORE INTO subscriptions (team_name, role, channel, signal) VALUES (?, ?, ?, ?)"
90
+ );
91
+ for (const [role, entries] of Object.entries(config.subscriptions)) {
92
+ for (const entry of entries) {
93
+ if (entry.signals && entry.signals.length > 0) {
94
+ for (const signal of entry.signals) {
95
+ insertSub.run(teamName, role, entry.channel, signal);
96
+ }
97
+ } else {
98
+ // Subscribe to all signals in channel
99
+ insertSub.run(teamName, role, entry.channel, null);
100
+ }
101
+ }
102
+ }
103
+ }
104
+
105
+ // Emissions
106
+ if (config.emissions) {
107
+ const insertEmission = this.db.prepare(
108
+ "INSERT OR IGNORE INTO emissions (team_name, role, signal) VALUES (?, ?, ?)"
109
+ );
110
+ for (const [role, signals] of Object.entries(config.emissions)) {
111
+ for (const signal of signals) {
112
+ insertEmission.run(teamName, role, signal);
113
+ }
114
+ }
115
+ }
116
+
117
+ // Peer routes
118
+ if (config.routing?.peers) {
119
+ const insertRoute = this.db.prepare(
120
+ "INSERT INTO peer_routes (team_name, from_role, to_role, via, signals) VALUES (?, ?, ?, ?, ?)"
121
+ );
122
+ for (const peer of config.routing.peers) {
123
+ insertRoute.run(
124
+ teamName,
125
+ peer.from,
126
+ peer.to,
127
+ peer.via,
128
+ JSON.stringify(peer.signals ?? [])
129
+ );
130
+ }
131
+ }
132
+ }
133
+
134
+ // --- Channels ---
135
+
136
+ listChannels(teamName: string): ChannelInfo[] {
137
+ const rows = this.db
138
+ .prepare("SELECT * FROM channels WHERE team_name = ? ORDER BY name")
139
+ .all(teamName) as Array<{ id: number; name: string; description: string | null }>;
140
+
141
+ return rows.map((row) => {
142
+ const signals = this.db
143
+ .prepare("SELECT signal FROM channel_signals WHERE channel_id = ?")
144
+ .all(row.id) as Array<{ signal: string }>;
145
+
146
+ return {
147
+ name: row.name,
148
+ description: row.description,
149
+ signals: signals.map((s) => s.signal),
150
+ };
151
+ });
152
+ }
153
+
154
+ getChannel(teamName: string, channelName: string): ChannelInfo | null {
155
+ const row = this.db
156
+ .prepare("SELECT * FROM channels WHERE team_name = ? AND name = ?")
157
+ .get(teamName, channelName) as
158
+ | { id: number; name: string; description: string | null }
159
+ | undefined;
160
+
161
+ if (!row) return null;
162
+
163
+ const signals = this.db
164
+ .prepare("SELECT signal FROM channel_signals WHERE channel_id = ?")
165
+ .all(row.id) as Array<{ signal: string }>;
166
+
167
+ return {
168
+ name: row.name,
169
+ description: row.description,
170
+ signals: signals.map((s) => s.signal),
171
+ };
172
+ }
173
+
174
+ // --- Subscriptions ---
175
+
176
+ getSubscriptionsForRole(teamName: string, role: string): SubscriptionInfo[] {
177
+ return this.db
178
+ .prepare(
179
+ "SELECT role, channel, signal FROM subscriptions WHERE team_name = ? AND role = ?"
180
+ )
181
+ .all(teamName, role) as SubscriptionInfo[];
182
+ }
183
+
184
+ listSubscriptions(teamName: string): SubscriptionInfo[] {
185
+ return this.db
186
+ .prepare(
187
+ "SELECT role, channel, signal FROM subscriptions WHERE team_name = ? ORDER BY role, channel"
188
+ )
189
+ .all(teamName) as SubscriptionInfo[];
190
+ }
191
+
192
+ // --- Emissions ---
193
+
194
+ getEmissionsForRole(teamName: string, role: string): string[] {
195
+ const rows = this.db
196
+ .prepare(
197
+ "SELECT signal FROM emissions WHERE team_name = ? AND role = ?"
198
+ )
199
+ .all(teamName, role) as Array<{ signal: string }>;
200
+ return rows.map((r) => r.signal);
201
+ }
202
+
203
+ canEmit(teamName: string, role: string, signal: string): boolean {
204
+ // If no emissions are declared for this team, allow all (permissive default)
205
+ const anyEmissions = this.db
206
+ .prepare("SELECT COUNT(*) as count FROM emissions WHERE team_name = ?")
207
+ .get(teamName) as { count: number };
208
+
209
+ if (anyEmissions.count === 0) return true;
210
+
211
+ const row = this.db
212
+ .prepare(
213
+ "SELECT COUNT(*) as count FROM emissions WHERE team_name = ? AND role = ? AND signal = ?"
214
+ )
215
+ .get(teamName, role, signal) as { count: number };
216
+
217
+ return row.count > 0;
218
+ }
219
+
220
+ // --- Peer Routes ---
221
+
222
+ listPeerRoutes(teamName: string): PeerRouteInfo[] {
223
+ const rows = this.db
224
+ .prepare(
225
+ "SELECT from_role, to_role, via, signals FROM peer_routes WHERE team_name = ?"
226
+ )
227
+ .all(teamName) as Array<{
228
+ from_role: string;
229
+ to_role: string;
230
+ via: string;
231
+ signals: string;
232
+ }>;
233
+
234
+ return rows.map((r) => ({
235
+ ...r,
236
+ signals: JSON.parse(r.signals),
237
+ }));
238
+ }
239
+
240
+ getPeerRoutesForRole(teamName: string, role: string): PeerRouteInfo[] {
241
+ const rows = this.db
242
+ .prepare(
243
+ "SELECT from_role, to_role, via, signals FROM peer_routes WHERE team_name = ? AND from_role = ?"
244
+ )
245
+ .all(teamName, role) as Array<{
246
+ from_role: string;
247
+ to_role: string;
248
+ via: string;
249
+ signals: string;
250
+ }>;
251
+
252
+ return rows.map((r) => ({
253
+ ...r,
254
+ signals: JSON.parse(r.signals),
255
+ }));
256
+ }
257
+
258
+ // --- Enforcement ---
259
+
260
+ getEnforcement(teamName: string): EnforcementMode {
261
+ const row = this.db
262
+ .prepare("SELECT enforcement FROM teams WHERE name = ?")
263
+ .get(teamName) as { enforcement: EnforcementMode } | undefined;
264
+ return row?.enforcement ?? "permissive";
265
+ }
266
+
267
+ // --- Signal Events ---
268
+
269
+ emit(options: EmitSignalOptions): EmitResult {
270
+ const enforcement = this.getEnforcement(options.teamName);
271
+ const permitted = this.canEmit(
272
+ options.teamName,
273
+ options.sender,
274
+ options.signal
275
+ );
276
+
277
+ if (!permitted && enforcement === "strict") {
278
+ throw new Error(
279
+ `Role "${options.sender}" is not permitted to emit signal "${options.signal}" (enforcement: strict)`
280
+ );
281
+ }
282
+
283
+ const result = this.db
284
+ .prepare(
285
+ "INSERT INTO signal_events (team_name, channel, signal, sender, payload) VALUES (?, ?, ?, ?, ?)"
286
+ )
287
+ .run(
288
+ options.teamName,
289
+ options.channel,
290
+ options.signal,
291
+ options.sender,
292
+ JSON.stringify(options.payload ?? {})
293
+ );
294
+
295
+ const event = this.getEvent(Number(result.lastInsertRowid))!;
296
+ return { event, permitted, enforcement };
297
+ }
298
+
299
+ listEvents(
300
+ teamName: string,
301
+ filters?: { channel?: string; signal?: string; sender?: string }
302
+ ): SignalEvent[] {
303
+ let sql = "SELECT * FROM signal_events WHERE team_name = ?";
304
+ const params: any[] = [teamName];
305
+
306
+ if (filters?.channel) {
307
+ sql += " AND channel = ?";
308
+ params.push(filters.channel);
309
+ }
310
+ if (filters?.signal) {
311
+ sql += " AND signal = ?";
312
+ params.push(filters.signal);
313
+ }
314
+ if (filters?.sender) {
315
+ sql += " AND sender = ?";
316
+ params.push(filters.sender);
317
+ }
318
+
319
+ sql += " ORDER BY created_at ASC";
320
+
321
+ const rows = this.db.prepare(sql).all(...params) as SignalEventRow[];
322
+ return rows.map(rowToEvent);
323
+ }
324
+
325
+ /**
326
+ * Get events that a role should receive based on its subscriptions.
327
+ */
328
+ getEventsForRole(teamName: string, role: string): SignalEvent[] {
329
+ const subs = this.getSubscriptionsForRole(teamName, role);
330
+ if (subs.length === 0) return [];
331
+
332
+ const allEvents: SignalEvent[] = [];
333
+
334
+ for (const sub of subs) {
335
+ if (sub.signal) {
336
+ // Signal-filtered subscription
337
+ const rows = this.db
338
+ .prepare(
339
+ "SELECT * FROM signal_events WHERE team_name = ? AND channel = ? AND signal = ? ORDER BY created_at ASC"
340
+ )
341
+ .all(teamName, sub.channel, sub.signal) as SignalEventRow[];
342
+ allEvents.push(...rows.map(rowToEvent));
343
+ } else {
344
+ // Full channel subscription
345
+ const rows = this.db
346
+ .prepare(
347
+ "SELECT * FROM signal_events WHERE team_name = ? AND channel = ? ORDER BY created_at ASC"
348
+ )
349
+ .all(teamName, sub.channel) as SignalEventRow[];
350
+ allEvents.push(...rows.map(rowToEvent));
351
+ }
352
+ }
353
+
354
+ // Deduplicate by id and sort by created_at
355
+ const seen = new Set<number>();
356
+ return allEvents
357
+ .filter((e) => {
358
+ if (seen.has(e.id)) return false;
359
+ seen.add(e.id);
360
+ return true;
361
+ })
362
+ .sort((a, b) => a.created_at.localeCompare(b.created_at));
363
+ }
364
+
365
+ private getEvent(id: number): SignalEvent | null {
366
+ const row = this.db
367
+ .prepare("SELECT * FROM signal_events WHERE id = ?")
368
+ .get(id) as SignalEventRow | undefined;
369
+ return row ? rowToEvent(row) : null;
370
+ }
371
+ }
@@ -0,0 +1,342 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from "vitest";
2
+ import type Database from "better-sqlite3";
3
+ import { createInMemoryDatabase } from "../db/database";
4
+ import { MessageService } from "./message-service";
5
+ import { TeamService } from "./team-service";
6
+
7
+ describe("MessageService", () => {
8
+ let db: Database.Database;
9
+ let messageService: MessageService;
10
+ let teamService: TeamService;
11
+
12
+ beforeEach(() => {
13
+ db = createInMemoryDatabase();
14
+ messageService = new MessageService(db);
15
+ teamService = new TeamService(db);
16
+
17
+ teamService.create({ name: "test-team" });
18
+ teamService.addMember("test-team", "alice");
19
+ teamService.addMember("test-team", "bob");
20
+ teamService.addMember("test-team", "charlie");
21
+ });
22
+
23
+ afterEach(() => {
24
+ db.close();
25
+ });
26
+
27
+ describe("team validation", () => {
28
+ it("send throws when team does not exist", () => {
29
+ expect(() =>
30
+ messageService.send({
31
+ teamName: "nonexistent",
32
+ sender: "alice",
33
+ recipient: "bob",
34
+ content: "Hi",
35
+ summary: "Greeting",
36
+ })
37
+ ).toThrow('Team "nonexistent" not found');
38
+ });
39
+
40
+ it("broadcast throws when team does not exist", () => {
41
+ expect(() =>
42
+ messageService.broadcast({
43
+ teamName: "nonexistent",
44
+ sender: "alice",
45
+ content: "Hi",
46
+ summary: "Greeting",
47
+ })
48
+ ).toThrow('Team "nonexistent" not found');
49
+ });
50
+
51
+ it("sendShutdownRequest throws when team does not exist", () => {
52
+ expect(() =>
53
+ messageService.sendShutdownRequest({
54
+ teamName: "nonexistent",
55
+ sender: "lead",
56
+ recipient: "alice",
57
+ })
58
+ ).toThrow('Team "nonexistent" not found');
59
+ });
60
+
61
+ it("send throws when recipient is not a member", () => {
62
+ expect(() =>
63
+ messageService.send({
64
+ teamName: "test-team",
65
+ sender: "alice",
66
+ recipient: "ghost",
67
+ content: "Hi",
68
+ summary: "Greeting",
69
+ })
70
+ ).toThrow('Agent "ghost" is not a member of team "test-team"');
71
+ });
72
+
73
+ it("sendShutdownRequest throws when recipient is not a member", () => {
74
+ expect(() =>
75
+ messageService.sendShutdownRequest({
76
+ teamName: "test-team",
77
+ sender: "lead",
78
+ recipient: "ghost",
79
+ })
80
+ ).toThrow('Agent "ghost" is not a member of team "test-team"');
81
+ });
82
+
83
+ it("sendPlanApprovalResponse throws when recipient is not a member", () => {
84
+ expect(() =>
85
+ messageService.sendPlanApprovalResponse({
86
+ teamName: "test-team",
87
+ sender: "lead",
88
+ recipient: "ghost",
89
+ requestId: "req-1",
90
+ approve: true,
91
+ })
92
+ ).toThrow('Agent "ghost" is not a member of team "test-team"');
93
+ });
94
+ });
95
+
96
+ describe("send", () => {
97
+ it("sends a direct message", () => {
98
+ const msg = messageService.send({
99
+ teamName: "test-team",
100
+ sender: "alice",
101
+ recipient: "bob",
102
+ content: "Hello Bob",
103
+ summary: "Greeting from Alice",
104
+ });
105
+ expect(msg.id).toBeGreaterThan(0);
106
+ expect(msg.type).toBe("message");
107
+ expect(msg.sender).toBe("alice");
108
+ expect(msg.recipient).toBe("bob");
109
+ expect(msg.content).toBe("Hello Bob");
110
+ expect(msg.summary).toBe("Greeting from Alice");
111
+ expect(msg.delivered).toBe(false);
112
+ });
113
+ });
114
+
115
+ describe("broadcast", () => {
116
+ it("sends to all non-shutdown members except sender", () => {
117
+ const msgs = messageService.broadcast({
118
+ teamName: "test-team",
119
+ sender: "alice",
120
+ content: "Team announcement",
121
+ summary: "Important update for team",
122
+ });
123
+ // alice is sender, so only bob and charlie
124
+ expect(msgs).toHaveLength(2);
125
+ expect(msgs.every((m) => m.type === "broadcast")).toBe(true);
126
+ const recipients = msgs.map((m) => m.recipient).sort();
127
+ expect(recipients).toEqual(["bob", "charlie"]);
128
+ });
129
+
130
+ it("skips shut down members", () => {
131
+ teamService.updateMemberStatus("test-team", "charlie", "shutdown");
132
+ const msgs = messageService.broadcast({
133
+ teamName: "test-team",
134
+ sender: "alice",
135
+ content: "Update",
136
+ summary: "Update for active members",
137
+ });
138
+ expect(msgs).toHaveLength(1);
139
+ expect(msgs[0].recipient).toBe("bob");
140
+ });
141
+ });
142
+
143
+ describe("sendShutdownRequest", () => {
144
+ it("creates a shutdown request with request_id", () => {
145
+ const msg = messageService.sendShutdownRequest({
146
+ teamName: "test-team",
147
+ sender: "lead",
148
+ recipient: "alice",
149
+ reason: "Work complete",
150
+ });
151
+ expect(msg.type).toBe("shutdown_request");
152
+ expect(msg.recipient).toBe("alice");
153
+ expect(msg.request_id).toBeTruthy();
154
+ expect(msg.content).toBe("Work complete");
155
+ });
156
+
157
+ it("uses default reason when none provided", () => {
158
+ const msg = messageService.sendShutdownRequest({
159
+ teamName: "test-team",
160
+ sender: "lead",
161
+ recipient: "alice",
162
+ });
163
+ expect(msg.content).toBe("Shutdown requested");
164
+ });
165
+ });
166
+
167
+ describe("sendShutdownResponse", () => {
168
+ it("creates an approval response", () => {
169
+ const req = messageService.sendShutdownRequest({
170
+ teamName: "test-team",
171
+ sender: "lead",
172
+ recipient: "alice",
173
+ });
174
+ const resp = messageService.sendShutdownResponse({
175
+ teamName: "test-team",
176
+ sender: "alice",
177
+ requestId: req.request_id!,
178
+ approve: true,
179
+ });
180
+ expect(resp.type).toBe("shutdown_response");
181
+ expect(resp.approve).toBe(true);
182
+ expect(resp.request_id).toBe(req.request_id);
183
+ });
184
+
185
+ it("creates a rejection response", () => {
186
+ const req = messageService.sendShutdownRequest({
187
+ teamName: "test-team",
188
+ sender: "lead",
189
+ recipient: "bob",
190
+ });
191
+ const resp = messageService.sendShutdownResponse({
192
+ teamName: "test-team",
193
+ sender: "bob",
194
+ requestId: req.request_id!,
195
+ approve: false,
196
+ content: "Still working",
197
+ });
198
+ expect(resp.approve).toBe(false);
199
+ expect(resp.content).toBe("Still working");
200
+ });
201
+ });
202
+
203
+ describe("sendPlanApprovalResponse", () => {
204
+ it("creates a plan approval", () => {
205
+ const msg = messageService.sendPlanApprovalResponse({
206
+ teamName: "test-team",
207
+ sender: "lead",
208
+ recipient: "alice",
209
+ requestId: "plan-123",
210
+ approve: true,
211
+ });
212
+ expect(msg.type).toBe("plan_approval_response");
213
+ expect(msg.approve).toBe(true);
214
+ expect(msg.request_id).toBe("plan-123");
215
+ });
216
+
217
+ it("creates a plan rejection with feedback", () => {
218
+ const msg = messageService.sendPlanApprovalResponse({
219
+ teamName: "test-team",
220
+ sender: "lead",
221
+ recipient: "alice",
222
+ requestId: "plan-456",
223
+ approve: false,
224
+ content: "Need tests",
225
+ });
226
+ expect(msg.approve).toBe(false);
227
+ expect(msg.content).toBe("Need tests");
228
+ });
229
+ });
230
+
231
+ describe("listing", () => {
232
+ it("listForTeam returns all messages", () => {
233
+ messageService.send({
234
+ teamName: "test-team",
235
+ sender: "alice",
236
+ recipient: "bob",
237
+ content: "Hi",
238
+ summary: "Greeting",
239
+ });
240
+ messageService.send({
241
+ teamName: "test-team",
242
+ sender: "bob",
243
+ recipient: "charlie",
244
+ content: "Hello",
245
+ summary: "Reply",
246
+ });
247
+
248
+ const msgs = messageService.listForTeam("test-team");
249
+ expect(msgs).toHaveLength(2);
250
+ });
251
+
252
+ it("listForAgent returns relevant messages", () => {
253
+ messageService.send({
254
+ teamName: "test-team",
255
+ sender: "alice",
256
+ recipient: "bob",
257
+ content: "For Bob",
258
+ summary: "To Bob",
259
+ });
260
+ messageService.send({
261
+ teamName: "test-team",
262
+ sender: "bob",
263
+ recipient: "charlie",
264
+ content: "From Bob",
265
+ summary: "From Bob",
266
+ });
267
+ messageService.send({
268
+ teamName: "test-team",
269
+ sender: "charlie",
270
+ recipient: "alice",
271
+ content: "Not for Bob",
272
+ summary: "To Alice",
273
+ });
274
+
275
+ const msgs = messageService.listForAgent("test-team", "bob");
276
+ expect(msgs).toHaveLength(2); // received one, sent one
277
+ });
278
+
279
+ it("listForAgent only returns broadcasts addressed to that agent", () => {
280
+ // alice broadcasts to bob and charlie (per-recipient rows)
281
+ messageService.broadcast({
282
+ teamName: "test-team",
283
+ sender: "alice",
284
+ content: "Team update",
285
+ summary: "Broadcast",
286
+ });
287
+
288
+ const bobMsgs = messageService.listForAgent("test-team", "bob");
289
+ const bobBroadcasts = bobMsgs.filter((m) => m.type === "broadcast");
290
+ expect(bobBroadcasts).toHaveLength(1);
291
+ expect(bobBroadcasts[0].recipient).toBe("bob");
292
+
293
+ // charlie should only see their own copy
294
+ const charlieMsgs = messageService.listForAgent("test-team", "charlie");
295
+ const charlieBroadcasts = charlieMsgs.filter((m) => m.type === "broadcast");
296
+ expect(charlieBroadcasts).toHaveLength(1);
297
+ expect(charlieBroadcasts[0].recipient).toBe("charlie");
298
+
299
+ // alice (sender) should see both as sender, not as broadcast recipient
300
+ const aliceMsgs = messageService.listForAgent("test-team", "alice");
301
+ const aliceBroadcasts = aliceMsgs.filter((m) => m.type === "broadcast");
302
+ expect(aliceBroadcasts).toHaveLength(2); // sent to bob + charlie
303
+ expect(aliceBroadcasts.every((m) => m.sender === "alice")).toBe(true);
304
+ });
305
+ });
306
+
307
+ describe("delivery tracking", () => {
308
+ it("getUndelivered returns undelivered messages", () => {
309
+ messageService.send({
310
+ teamName: "test-team",
311
+ sender: "alice",
312
+ recipient: "bob",
313
+ content: "Msg 1",
314
+ summary: "First",
315
+ });
316
+ messageService.send({
317
+ teamName: "test-team",
318
+ sender: "alice",
319
+ recipient: "bob",
320
+ content: "Msg 2",
321
+ summary: "Second",
322
+ });
323
+
324
+ const undelivered = messageService.getUndelivered("test-team", "bob");
325
+ expect(undelivered).toHaveLength(2);
326
+ });
327
+
328
+ it("markDelivered removes from undelivered list", () => {
329
+ const msg = messageService.send({
330
+ teamName: "test-team",
331
+ sender: "alice",
332
+ recipient: "bob",
333
+ content: "Msg",
334
+ summary: "Test",
335
+ });
336
+ messageService.markDelivered(msg.id);
337
+
338
+ const undelivered = messageService.getUndelivered("test-team", "bob");
339
+ expect(undelivered).toHaveLength(0);
340
+ });
341
+ });
342
+ });