macro-agent 0.0.10 → 0.0.12

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 (518) hide show
  1. package/.macro-agent/teams/self-driving/prompts/grinder.md +27 -0
  2. package/.macro-agent/teams/self-driving/prompts/judge.md +27 -0
  3. package/.macro-agent/teams/self-driving/prompts/planner.md +33 -0
  4. package/.macro-agent/teams/self-driving/roles/grinder.yaml +17 -0
  5. package/.macro-agent/teams/self-driving/roles/judge.yaml +24 -0
  6. package/.macro-agent/teams/self-driving/roles/planner.yaml +18 -0
  7. package/.macro-agent/teams/self-driving/team.yaml +103 -0
  8. package/.macro-agent/teams/structured/prompts/developer.md +26 -0
  9. package/.macro-agent/teams/structured/prompts/lead.md +25 -0
  10. package/.macro-agent/teams/structured/prompts/reviewer.md +24 -0
  11. package/.macro-agent/teams/structured/roles/developer.yaml +12 -0
  12. package/.macro-agent/teams/structured/roles/lead.yaml +11 -0
  13. package/.macro-agent/teams/structured/roles/reviewer.yaml +19 -0
  14. package/.macro-agent/teams/structured/team.yaml +89 -0
  15. package/.sudocode/issues.jsonl +56 -51
  16. package/.sudocode/specs.jsonl +8 -1
  17. package/CLAUDE.md +121 -30
  18. package/README.md +60 -3
  19. package/dist/acp/macro-agent.d.ts +4 -0
  20. package/dist/acp/macro-agent.d.ts.map +1 -1
  21. package/dist/acp/macro-agent.js +50 -4
  22. package/dist/acp/macro-agent.js.map +1 -1
  23. package/dist/acp/session-mapper.d.ts +20 -1
  24. package/dist/acp/session-mapper.d.ts.map +1 -1
  25. package/dist/acp/session-mapper.js +90 -1
  26. package/dist/acp/session-mapper.js.map +1 -1
  27. package/dist/acp/types.d.ts +24 -1
  28. package/dist/acp/types.d.ts.map +1 -1
  29. package/dist/acp/types.js.map +1 -1
  30. package/dist/agent/agent-manager.d.ts +40 -1
  31. package/dist/agent/agent-manager.d.ts.map +1 -1
  32. package/dist/agent/agent-manager.js +172 -8
  33. package/dist/agent/agent-manager.js.map +1 -1
  34. package/dist/agent/types.d.ts +22 -0
  35. package/dist/agent/types.d.ts.map +1 -1
  36. package/dist/agent/types.js.map +1 -1
  37. package/dist/agent/wake.d.ts +15 -0
  38. package/dist/agent/wake.d.ts.map +1 -1
  39. package/dist/agent/wake.js +15 -0
  40. package/dist/agent/wake.js.map +1 -1
  41. package/dist/agent-detection/command-builder.d.ts +30 -0
  42. package/dist/agent-detection/command-builder.d.ts.map +1 -0
  43. package/dist/agent-detection/command-builder.js +71 -0
  44. package/dist/agent-detection/command-builder.js.map +1 -0
  45. package/dist/agent-detection/detector.d.ts +84 -0
  46. package/dist/agent-detection/detector.d.ts.map +1 -0
  47. package/dist/agent-detection/detector.js +240 -0
  48. package/dist/agent-detection/detector.js.map +1 -0
  49. package/dist/agent-detection/index.d.ts +12 -0
  50. package/dist/agent-detection/index.d.ts.map +1 -0
  51. package/dist/agent-detection/index.js +14 -0
  52. package/dist/agent-detection/index.js.map +1 -0
  53. package/dist/agent-detection/registry.d.ts +53 -0
  54. package/dist/agent-detection/registry.d.ts.map +1 -0
  55. package/dist/agent-detection/registry.js +177 -0
  56. package/dist/agent-detection/registry.js.map +1 -0
  57. package/dist/agent-detection/types.d.ts +121 -0
  58. package/dist/agent-detection/types.d.ts.map +1 -0
  59. package/dist/agent-detection/types.js +20 -0
  60. package/dist/agent-detection/types.js.map +1 -0
  61. package/dist/api/server.d.ts +5 -1
  62. package/dist/api/server.d.ts.map +1 -1
  63. package/dist/api/server.js +362 -0
  64. package/dist/api/server.js.map +1 -1
  65. package/dist/api/types.d.ts +50 -1
  66. package/dist/api/types.d.ts.map +1 -1
  67. package/dist/cli/acp.d.ts +2 -0
  68. package/dist/cli/acp.d.ts.map +1 -1
  69. package/dist/cli/acp.js +8 -1
  70. package/dist/cli/acp.js.map +1 -1
  71. package/dist/cli/index.js +29 -0
  72. package/dist/cli/index.js.map +1 -1
  73. package/dist/cli/mcp.js +38 -0
  74. package/dist/cli/mcp.js.map +1 -1
  75. package/dist/config/index.d.ts +2 -0
  76. package/dist/config/index.d.ts.map +1 -0
  77. package/dist/config/index.js +2 -0
  78. package/dist/config/index.js.map +1 -0
  79. package/dist/config/project-config.d.ts +46 -0
  80. package/dist/config/project-config.d.ts.map +1 -0
  81. package/dist/config/project-config.js +68 -0
  82. package/dist/config/project-config.js.map +1 -0
  83. package/dist/lifecycle/cascade.d.ts +1 -1
  84. package/dist/lifecycle/cascade.d.ts.map +1 -1
  85. package/dist/lifecycle/handlers/index.d.ts +4 -0
  86. package/dist/lifecycle/handlers/index.d.ts.map +1 -1
  87. package/dist/lifecycle/handlers/index.js +2 -0
  88. package/dist/lifecycle/handlers/index.js.map +1 -1
  89. package/dist/lifecycle/handlers/worker.d.ts +4 -0
  90. package/dist/lifecycle/handlers/worker.d.ts.map +1 -1
  91. package/dist/lifecycle/handlers/worker.js +35 -3
  92. package/dist/lifecycle/handlers/worker.js.map +1 -1
  93. package/dist/mail/conversation-map.d.ts +33 -0
  94. package/dist/mail/conversation-map.d.ts.map +1 -0
  95. package/dist/mail/conversation-map.js +61 -0
  96. package/dist/mail/conversation-map.js.map +1 -0
  97. package/dist/mail/index.d.ts +11 -0
  98. package/dist/mail/index.d.ts.map +1 -0
  99. package/dist/mail/index.js +11 -0
  100. package/dist/mail/index.js.map +1 -0
  101. package/dist/mail/mail-service.d.ts +85 -0
  102. package/dist/mail/mail-service.d.ts.map +1 -0
  103. package/dist/mail/mail-service.js +121 -0
  104. package/dist/mail/mail-service.js.map +1 -0
  105. package/dist/mail/stores/eventstore-conversation-store.d.ts +40 -0
  106. package/dist/mail/stores/eventstore-conversation-store.d.ts.map +1 -0
  107. package/dist/mail/stores/eventstore-conversation-store.js +131 -0
  108. package/dist/mail/stores/eventstore-conversation-store.js.map +1 -0
  109. package/dist/mail/stores/eventstore-participant-store.d.ts +43 -0
  110. package/dist/mail/stores/eventstore-participant-store.d.ts.map +1 -0
  111. package/dist/mail/stores/eventstore-participant-store.js +145 -0
  112. package/dist/mail/stores/eventstore-participant-store.js.map +1 -0
  113. package/dist/mail/stores/eventstore-thread-store.d.ts +46 -0
  114. package/dist/mail/stores/eventstore-thread-store.d.ts.map +1 -0
  115. package/dist/mail/stores/eventstore-thread-store.js +118 -0
  116. package/dist/mail/stores/eventstore-thread-store.js.map +1 -0
  117. package/dist/mail/stores/eventstore-turn-store.d.ts +47 -0
  118. package/dist/mail/stores/eventstore-turn-store.d.ts.map +1 -0
  119. package/dist/mail/stores/eventstore-turn-store.js +153 -0
  120. package/dist/mail/stores/eventstore-turn-store.js.map +1 -0
  121. package/dist/mail/stores/index.d.ts +12 -0
  122. package/dist/mail/stores/index.d.ts.map +1 -0
  123. package/dist/mail/stores/index.js +12 -0
  124. package/dist/mail/stores/index.js.map +1 -0
  125. package/dist/mail/stores/types.d.ts +146 -0
  126. package/dist/mail/stores/types.d.ts.map +1 -0
  127. package/dist/mail/stores/types.js +13 -0
  128. package/dist/mail/stores/types.js.map +1 -0
  129. package/dist/mail/turn-recorder.d.ts +30 -0
  130. package/dist/mail/turn-recorder.d.ts.map +1 -0
  131. package/dist/mail/turn-recorder.js +98 -0
  132. package/dist/mail/turn-recorder.js.map +1 -0
  133. package/dist/map/adapter/acp-over-map.d.ts.map +1 -1
  134. package/dist/map/adapter/acp-over-map.js +32 -2
  135. package/dist/map/adapter/acp-over-map.js.map +1 -1
  136. package/dist/map/adapter/event-translator.d.ts.map +1 -1
  137. package/dist/map/adapter/event-translator.js +4 -0
  138. package/dist/map/adapter/event-translator.js.map +1 -1
  139. package/dist/map/adapter/extensions/agent-detection.d.ts +49 -0
  140. package/dist/map/adapter/extensions/agent-detection.d.ts.map +1 -0
  141. package/dist/map/adapter/extensions/agent-detection.js +91 -0
  142. package/dist/map/adapter/extensions/agent-detection.js.map +1 -0
  143. package/dist/map/adapter/extensions/index.d.ts +10 -1
  144. package/dist/map/adapter/extensions/index.d.ts.map +1 -1
  145. package/dist/map/adapter/extensions/index.js +39 -0
  146. package/dist/map/adapter/extensions/index.js.map +1 -1
  147. package/dist/map/adapter/extensions/resume.d.ts +47 -0
  148. package/dist/map/adapter/extensions/resume.d.ts.map +1 -0
  149. package/dist/map/adapter/extensions/resume.js +59 -0
  150. package/dist/map/adapter/extensions/resume.js.map +1 -0
  151. package/dist/map/adapter/extensions/workspace-files.d.ts +42 -0
  152. package/dist/map/adapter/extensions/workspace-files.d.ts.map +1 -0
  153. package/dist/map/adapter/extensions/workspace-files.js +338 -0
  154. package/dist/map/adapter/extensions/workspace-files.js.map +1 -0
  155. package/dist/map/adapter/mail-handler-adapter.d.ts +27 -0
  156. package/dist/map/adapter/mail-handler-adapter.d.ts.map +1 -0
  157. package/dist/map/adapter/mail-handler-adapter.js +292 -0
  158. package/dist/map/adapter/mail-handler-adapter.js.map +1 -0
  159. package/dist/map/adapter/map-adapter.d.ts +34 -10
  160. package/dist/map/adapter/map-adapter.d.ts.map +1 -1
  161. package/dist/map/adapter/map-adapter.js +110 -14
  162. package/dist/map/adapter/map-adapter.js.map +1 -1
  163. package/dist/map/adapter/rpc-handler.d.ts +4 -1
  164. package/dist/map/adapter/rpc-handler.d.ts.map +1 -1
  165. package/dist/map/adapter/rpc-handler.js +6 -0
  166. package/dist/map/adapter/rpc-handler.js.map +1 -1
  167. package/dist/map/index.d.ts +1 -0
  168. package/dist/map/index.d.ts.map +1 -1
  169. package/dist/map/index.js +2 -0
  170. package/dist/map/index.js.map +1 -1
  171. package/dist/map/types.d.ts +3 -1
  172. package/dist/map/types.d.ts.map +1 -1
  173. package/dist/map/types.js.map +1 -1
  174. package/dist/mcp/mcp-server.d.ts +6 -0
  175. package/dist/mcp/mcp-server.d.ts.map +1 -1
  176. package/dist/mcp/mcp-server.js +45 -0
  177. package/dist/mcp/mcp-server.js.map +1 -1
  178. package/dist/mcp/tools/claim_task.d.ts +35 -0
  179. package/dist/mcp/tools/claim_task.d.ts.map +1 -0
  180. package/dist/mcp/tools/claim_task.js +58 -0
  181. package/dist/mcp/tools/claim_task.js.map +1 -0
  182. package/dist/mcp/tools/done.d.ts +15 -2
  183. package/dist/mcp/tools/done.d.ts.map +1 -1
  184. package/dist/mcp/tools/done.js +45 -10
  185. package/dist/mcp/tools/done.js.map +1 -1
  186. package/dist/mcp/tools/list_claimable_tasks.d.ts +38 -0
  187. package/dist/mcp/tools/list_claimable_tasks.d.ts.map +1 -0
  188. package/dist/mcp/tools/list_claimable_tasks.js +63 -0
  189. package/dist/mcp/tools/list_claimable_tasks.js.map +1 -0
  190. package/dist/mcp/tools/unclaim_task.d.ts +31 -0
  191. package/dist/mcp/tools/unclaim_task.d.ts.map +1 -0
  192. package/dist/mcp/tools/unclaim_task.js +47 -0
  193. package/dist/mcp/tools/unclaim_task.js.map +1 -0
  194. package/dist/metrics/index.d.ts +2 -0
  195. package/dist/metrics/index.d.ts.map +1 -0
  196. package/dist/metrics/index.js +2 -0
  197. package/dist/metrics/index.js.map +1 -0
  198. package/dist/metrics/metrics.d.ts +79 -0
  199. package/dist/metrics/metrics.d.ts.map +1 -0
  200. package/dist/metrics/metrics.js +166 -0
  201. package/dist/metrics/metrics.js.map +1 -0
  202. package/dist/roles/capabilities.d.ts +1 -0
  203. package/dist/roles/capabilities.d.ts.map +1 -1
  204. package/dist/roles/capabilities.js +3 -0
  205. package/dist/roles/capabilities.js.map +1 -1
  206. package/dist/roles/types.d.ts +1 -1
  207. package/dist/roles/types.d.ts.map +1 -1
  208. package/dist/router/channels.d.ts +2 -4
  209. package/dist/router/channels.d.ts.map +1 -1
  210. package/dist/router/channels.js.map +1 -1
  211. package/dist/router/message-router.d.ts +85 -9
  212. package/dist/router/message-router.d.ts.map +1 -1
  213. package/dist/router/message-router.js +203 -14
  214. package/dist/router/message-router.js.map +1 -1
  215. package/dist/router/role-resolver.d.ts +10 -1
  216. package/dist/router/role-resolver.d.ts.map +1 -1
  217. package/dist/router/role-resolver.js +15 -1
  218. package/dist/router/role-resolver.js.map +1 -1
  219. package/dist/router/types.d.ts +30 -1
  220. package/dist/router/types.d.ts.map +1 -1
  221. package/dist/router/types.js.map +1 -1
  222. package/dist/server/combined-server.d.ts +6 -0
  223. package/dist/server/combined-server.d.ts.map +1 -1
  224. package/dist/server/combined-server.js +24 -2
  225. package/dist/server/combined-server.js.map +1 -1
  226. package/dist/store/event-store.d.ts +14 -1
  227. package/dist/store/event-store.d.ts.map +1 -1
  228. package/dist/store/event-store.js +456 -4
  229. package/dist/store/event-store.js.map +1 -1
  230. package/dist/store/types/agents.d.ts +1 -1
  231. package/dist/store/types/agents.d.ts.map +1 -1
  232. package/dist/store/types/conversations.d.ts +91 -0
  233. package/dist/store/types/conversations.d.ts.map +1 -0
  234. package/dist/store/types/conversations.js +8 -0
  235. package/dist/store/types/conversations.js.map +1 -0
  236. package/dist/store/types/events.d.ts +1 -1
  237. package/dist/store/types/events.d.ts.map +1 -1
  238. package/dist/store/types/events.js.map +1 -1
  239. package/dist/store/types/index.d.ts +2 -0
  240. package/dist/store/types/index.d.ts.map +1 -1
  241. package/dist/store/types/index.js +2 -0
  242. package/dist/store/types/index.js.map +1 -1
  243. package/dist/store/types/sessions.d.ts +44 -0
  244. package/dist/store/types/sessions.d.ts.map +1 -0
  245. package/dist/store/types/sessions.js +9 -0
  246. package/dist/store/types/sessions.js.map +1 -0
  247. package/dist/store/types/tasks.d.ts +2 -0
  248. package/dist/store/types/tasks.d.ts.map +1 -1
  249. package/dist/task/backend/memory.d.ts +4 -1
  250. package/dist/task/backend/memory.d.ts.map +1 -1
  251. package/dist/task/backend/memory.js +81 -0
  252. package/dist/task/backend/memory.js.map +1 -1
  253. package/dist/task/backend/types.d.ts +30 -0
  254. package/dist/task/backend/types.d.ts.map +1 -1
  255. package/dist/task/backend/types.js.map +1 -1
  256. package/dist/teams/index.d.ts +4 -0
  257. package/dist/teams/index.d.ts.map +1 -0
  258. package/dist/teams/index.js +4 -0
  259. package/dist/teams/index.js.map +1 -0
  260. package/dist/teams/team-loader.d.ts +20 -0
  261. package/dist/teams/team-loader.d.ts.map +1 -0
  262. package/dist/teams/team-loader.js +293 -0
  263. package/dist/teams/team-loader.js.map +1 -0
  264. package/dist/teams/team-runtime.d.ts +139 -0
  265. package/dist/teams/team-runtime.d.ts.map +1 -0
  266. package/dist/teams/team-runtime.js +613 -0
  267. package/dist/teams/team-runtime.js.map +1 -0
  268. package/dist/teams/types.d.ts +266 -0
  269. package/dist/teams/types.d.ts.map +1 -0
  270. package/dist/teams/types.js +20 -0
  271. package/dist/teams/types.js.map +1 -0
  272. package/dist/trigger/router/trigger-router.d.ts +30 -3
  273. package/dist/trigger/router/trigger-router.d.ts.map +1 -1
  274. package/dist/trigger/router/trigger-router.js +30 -3
  275. package/dist/trigger/router/trigger-router.js.map +1 -1
  276. package/dist/trigger/wake/types.d.ts +31 -5
  277. package/dist/trigger/wake/types.d.ts.map +1 -1
  278. package/dist/trigger/wake/types.js +19 -0
  279. package/dist/trigger/wake/types.js.map +1 -1
  280. package/dist/workspace/dataplane-adapter.d.ts +1 -1
  281. package/dist/workspace/dataplane-adapter.d.ts.map +1 -1
  282. package/dist/workspace/dataplane-adapter.js +1 -1
  283. package/dist/workspace/dataplane-adapter.js.map +1 -1
  284. package/dist/workspace/index.d.ts +1 -1
  285. package/dist/workspace/index.d.ts.map +1 -1
  286. package/dist/workspace/strategies/index.d.ts +6 -0
  287. package/dist/workspace/strategies/index.d.ts.map +1 -0
  288. package/dist/workspace/strategies/index.js +5 -0
  289. package/dist/workspace/strategies/index.js.map +1 -0
  290. package/dist/workspace/strategies/optimistic.d.ts +26 -0
  291. package/dist/workspace/strategies/optimistic.d.ts.map +1 -0
  292. package/dist/workspace/strategies/optimistic.js +121 -0
  293. package/dist/workspace/strategies/optimistic.js.map +1 -0
  294. package/dist/workspace/strategies/queue.d.ts +26 -0
  295. package/dist/workspace/strategies/queue.d.ts.map +1 -0
  296. package/dist/workspace/strategies/queue.js +67 -0
  297. package/dist/workspace/strategies/queue.js.map +1 -0
  298. package/dist/workspace/strategies/registry.d.ts +37 -0
  299. package/dist/workspace/strategies/registry.d.ts.map +1 -0
  300. package/dist/workspace/strategies/registry.js +63 -0
  301. package/dist/workspace/strategies/registry.js.map +1 -0
  302. package/dist/workspace/strategies/trunk.d.ts +20 -0
  303. package/dist/workspace/strategies/trunk.d.ts.map +1 -0
  304. package/dist/workspace/strategies/trunk.js +108 -0
  305. package/dist/workspace/strategies/trunk.js.map +1 -0
  306. package/dist/workspace/strategies/types.d.ts +104 -0
  307. package/dist/workspace/strategies/types.d.ts.map +1 -0
  308. package/dist/workspace/strategies/types.js +11 -0
  309. package/dist/workspace/strategies/types.js.map +1 -0
  310. package/dist/workspace/types.d.ts +1 -1
  311. package/dist/workspace/types.d.ts.map +1 -1
  312. package/dist/workspace/workspace-manager.d.ts +1 -1
  313. package/dist/workspace/workspace-manager.d.ts.map +1 -1
  314. package/docs/implementation-details.md +1127 -0
  315. package/docs/implementation-summary.md +448 -0
  316. package/docs/mail-integration.md +608 -0
  317. package/docs/plan-self-driving-support.md +433 -0
  318. package/docs/spec-self-driving-support.md +462 -0
  319. package/docs/team-templates.md +860 -0
  320. package/docs/teams.md +233 -0
  321. package/package.json +5 -3
  322. package/src/acp/__tests__/integration.test.ts +161 -1
  323. package/src/acp/__tests__/macro-agent.test.ts +95 -0
  324. package/src/acp/__tests__/session-persistence.test.ts +276 -0
  325. package/src/acp/macro-agent.ts +79 -7
  326. package/src/acp/session-mapper.ts +108 -1
  327. package/src/acp/types.ts +33 -1
  328. package/src/agent/agent-manager.ts +278 -6
  329. package/src/agent/types.ts +27 -0
  330. package/src/agent/wake.ts +15 -0
  331. package/src/agent-detection/__tests__/command-builder.test.ts +336 -0
  332. package/src/agent-detection/__tests__/detector.test.ts +768 -0
  333. package/src/agent-detection/__tests__/registry.test.ts +254 -0
  334. package/src/agent-detection/command-builder.ts +90 -0
  335. package/src/agent-detection/detector.ts +307 -0
  336. package/src/agent-detection/index.ts +36 -0
  337. package/src/agent-detection/registry.ts +200 -0
  338. package/src/agent-detection/types.ts +184 -0
  339. package/src/api/__tests__/conversation-api.test.ts +468 -0
  340. package/src/api/server.ts +425 -1
  341. package/src/api/types.ts +64 -1
  342. package/src/cli/acp.ts +9 -1
  343. package/src/cli/index.ts +44 -0
  344. package/src/cli/mcp.ts +47 -0
  345. package/src/config/index.ts +9 -0
  346. package/src/config/project-config.ts +107 -0
  347. package/src/lifecycle/cascade.ts +1 -1
  348. package/src/lifecycle/handlers/index.ts +8 -0
  349. package/src/lifecycle/handlers/worker.ts +48 -3
  350. package/src/mail/__tests__/conversation-lifecycle.test.ts +409 -0
  351. package/src/mail/__tests__/eventstore-stores.test.ts +1073 -0
  352. package/src/mail/__tests__/mail-full-agent.e2e.test.ts +575 -0
  353. package/src/mail/__tests__/mail-integration.test.ts +759 -0
  354. package/src/mail/__tests__/mail-map-protocol.e2e.test.ts +1068 -0
  355. package/src/mail/__tests__/mail-service.test.ts +506 -0
  356. package/src/mail/__tests__/turn-recorder.test.ts +328 -0
  357. package/src/mail/conversation-map.ts +107 -0
  358. package/src/mail/index.ts +25 -0
  359. package/src/mail/mail-service.ts +257 -0
  360. package/src/mail/stores/eventstore-conversation-store.ts +146 -0
  361. package/src/mail/stores/eventstore-participant-store.ts +172 -0
  362. package/src/mail/stores/eventstore-thread-store.ts +129 -0
  363. package/src/mail/stores/eventstore-turn-store.ts +173 -0
  364. package/src/mail/stores/index.ts +12 -0
  365. package/src/mail/stores/types.ts +160 -0
  366. package/src/mail/turn-recorder.ts +124 -0
  367. package/src/map/README.md +79 -0
  368. package/src/map/adapter/__tests__/extensions.test.ts +359 -0
  369. package/src/map/adapter/__tests__/map-adapter.test.ts +90 -0
  370. package/src/map/adapter/__tests__/workspace-files.test.ts +673 -0
  371. package/src/map/adapter/acp-over-map.ts +45 -2
  372. package/src/map/adapter/event-translator.ts +4 -0
  373. package/src/map/adapter/extensions/agent-detection.ts +201 -0
  374. package/src/map/adapter/extensions/index.ts +63 -0
  375. package/src/map/adapter/extensions/resume.ts +114 -0
  376. package/src/map/adapter/extensions/workspace-files.ts +449 -0
  377. package/src/map/adapter/mail-handler-adapter.ts +429 -0
  378. package/src/map/adapter/map-adapter.ts +173 -27
  379. package/src/map/adapter/rpc-handler.ts +8 -1
  380. package/src/map/index.ts +3 -0
  381. package/src/map/types.ts +3 -1
  382. package/src/mcp/mcp-server.ts +67 -0
  383. package/src/mcp/tools/claim_task.ts +86 -0
  384. package/src/mcp/tools/done.ts +59 -10
  385. package/src/mcp/tools/list_claimable_tasks.ts +93 -0
  386. package/src/mcp/tools/unclaim_task.ts +71 -0
  387. package/src/metrics/index.ts +9 -0
  388. package/src/metrics/metrics.ts +280 -0
  389. package/src/roles/capabilities.ts +3 -0
  390. package/src/roles/types.ts +2 -1
  391. package/src/router/README.md +120 -0
  392. package/src/router/__tests__/message-router.test.ts +561 -0
  393. package/src/router/channels.ts +3 -4
  394. package/src/router/message-router.ts +308 -22
  395. package/src/router/role-resolver.ts +22 -1
  396. package/src/router/types.ts +36 -1
  397. package/src/server/combined-server.ts +36 -2
  398. package/src/store/README.md +134 -0
  399. package/src/store/event-store.ts +546 -3
  400. package/src/store/types/agents.ts +1 -1
  401. package/src/store/types/conversations.ts +129 -0
  402. package/src/store/types/events.ts +5 -1
  403. package/src/store/types/index.ts +2 -0
  404. package/src/store/types/sessions.ts +53 -0
  405. package/src/store/types/tasks.ts +3 -0
  406. package/src/task/backend/memory.ts +116 -0
  407. package/src/task/backend/types.ts +43 -0
  408. package/src/teams/__tests__/cross-subsystem.integration.test.ts +983 -0
  409. package/src/teams/__tests__/e2e/team-runtime.e2e.test.ts +553 -0
  410. package/src/teams/__tests__/team-system.test.ts +1280 -0
  411. package/src/teams/index.ts +13 -0
  412. package/src/teams/team-loader.ts +434 -0
  413. package/src/teams/team-runtime.ts +727 -0
  414. package/src/teams/types.ts +377 -0
  415. package/src/trigger/router/trigger-router.ts +30 -3
  416. package/src/trigger/wake/types.ts +32 -5
  417. package/src/trigger/wake/wake-manager.ts +2 -2
  418. package/src/workspace/dataplane-adapter.ts +1 -1
  419. package/src/workspace/index.ts +1 -1
  420. package/src/workspace/strategies/index.ts +18 -0
  421. package/src/workspace/strategies/optimistic.ts +136 -0
  422. package/src/workspace/strategies/queue.ts +81 -0
  423. package/src/workspace/strategies/registry.ts +89 -0
  424. package/src/workspace/strategies/trunk.ts +123 -0
  425. package/src/workspace/strategies/types.ts +145 -0
  426. package/src/workspace/types.ts +1 -1
  427. package/src/workspace/workspace-manager.ts +1 -1
  428. package/.claude/settings.local.json +0 -59
  429. package/dist/map/utils/address-translation.d.ts +0 -99
  430. package/dist/map/utils/address-translation.d.ts.map +0 -1
  431. package/dist/map/utils/address-translation.js +0 -285
  432. package/dist/map/utils/address-translation.js.map +0 -1
  433. package/dist/map/utils/index.d.ts +0 -7
  434. package/dist/map/utils/index.d.ts.map +0 -1
  435. package/dist/map/utils/index.js +0 -7
  436. package/dist/map/utils/index.js.map +0 -1
  437. package/openspec/AGENTS.md +0 -456
  438. package/openspec/changes/archive/2025-12-21-add-mvp-foundation/design.md +0 -128
  439. package/openspec/changes/archive/2025-12-21-add-mvp-foundation/proposal.md +0 -49
  440. package/openspec/changes/archive/2025-12-21-add-mvp-foundation/specs/agent-manager/spec.md +0 -150
  441. package/openspec/changes/archive/2025-12-21-add-mvp-foundation/specs/cli-api/spec.md +0 -258
  442. package/openspec/changes/archive/2025-12-21-add-mvp-foundation/specs/event-store/spec.md +0 -160
  443. package/openspec/changes/archive/2025-12-21-add-mvp-foundation/specs/mcp-tools/spec.md +0 -224
  444. package/openspec/changes/archive/2025-12-21-add-mvp-foundation/specs/message-router/spec.md +0 -153
  445. package/openspec/changes/archive/2025-12-21-add-mvp-foundation/specs/task-manager/spec.md +0 -136
  446. package/openspec/changes/archive/2025-12-21-add-mvp-foundation/tasks.md +0 -147
  447. package/openspec/project.md +0 -31
  448. package/openspec/specs/agent-manager/spec.md +0 -154
  449. package/openspec/specs/cli-api/spec.md +0 -262
  450. package/openspec/specs/event-store/spec.md +0 -164
  451. package/openspec/specs/mcp-tools/spec.md +0 -228
  452. package/openspec/specs/message-router/spec.md +0 -157
  453. package/openspec/specs/task-manager/spec.md +0 -140
  454. package/references/acp-factory-ref/CHANGELOG.md +0 -33
  455. package/references/acp-factory-ref/LICENSE +0 -21
  456. package/references/acp-factory-ref/README.md +0 -341
  457. package/references/acp-factory-ref/package-lock.json +0 -3102
  458. package/references/acp-factory-ref/package.json +0 -96
  459. package/references/acp-factory-ref/python/CHANGELOG.md +0 -33
  460. package/references/acp-factory-ref/python/LICENSE +0 -21
  461. package/references/acp-factory-ref/python/Makefile +0 -57
  462. package/references/acp-factory-ref/python/README.md +0 -253
  463. package/references/acp-factory-ref/python/pyproject.toml +0 -73
  464. package/references/acp-factory-ref/python/tests/__init__.py +0 -0
  465. package/references/acp-factory-ref/python/tests/e2e/__init__.py +0 -1
  466. package/references/acp-factory-ref/python/tests/e2e/test_codex_e2e.py +0 -349
  467. package/references/acp-factory-ref/python/tests/e2e/test_gemini_e2e.py +0 -165
  468. package/references/acp-factory-ref/python/tests/e2e/test_opencode_e2e.py +0 -296
  469. package/references/acp-factory-ref/python/tests/test_client_handler.py +0 -543
  470. package/references/acp-factory-ref/python/tests/test_pushable.py +0 -199
  471. package/references/claude-code-acp/.github/workflows/ci.yml +0 -45
  472. package/references/claude-code-acp/.github/workflows/publish.yml +0 -34
  473. package/references/claude-code-acp/.prettierrc.json +0 -4
  474. package/references/claude-code-acp/CHANGELOG.md +0 -249
  475. package/references/claude-code-acp/LICENSE +0 -222
  476. package/references/claude-code-acp/README.md +0 -53
  477. package/references/claude-code-acp/docs/RELEASES.md +0 -24
  478. package/references/claude-code-acp/eslint.config.js +0 -48
  479. package/references/claude-code-acp/package-lock.json +0 -4570
  480. package/references/claude-code-acp/package.json +0 -88
  481. package/references/claude-code-acp/scripts/release.sh +0 -119
  482. package/references/claude-code-acp/src/acp-agent.ts +0 -2065
  483. package/references/claude-code-acp/src/index.ts +0 -26
  484. package/references/claude-code-acp/src/lib.ts +0 -38
  485. package/references/claude-code-acp/src/mcp-server.ts +0 -911
  486. package/references/claude-code-acp/src/settings.ts +0 -522
  487. package/references/claude-code-acp/src/tests/.claude/commands/quick-math.md +0 -5
  488. package/references/claude-code-acp/src/tests/.claude/commands/say-hello.md +0 -6
  489. package/references/claude-code-acp/src/tests/acp-agent-fork.test.ts +0 -479
  490. package/references/claude-code-acp/src/tests/acp-agent.test.ts +0 -1502
  491. package/references/claude-code-acp/src/tests/extract-lines.test.ts +0 -103
  492. package/references/claude-code-acp/src/tests/fork-session.test.ts +0 -335
  493. package/references/claude-code-acp/src/tests/replace-and-calculate-location.test.ts +0 -334
  494. package/references/claude-code-acp/src/tests/settings.test.ts +0 -617
  495. package/references/claude-code-acp/src/tests/skills-options.test.ts +0 -187
  496. package/references/claude-code-acp/src/tests/tools.test.ts +0 -318
  497. package/references/claude-code-acp/src/tests/typescript-declarations.test.ts +0 -558
  498. package/references/claude-code-acp/src/tools.ts +0 -819
  499. package/references/claude-code-acp/src/utils.ts +0 -171
  500. package/references/claude-code-acp/tsconfig.json +0 -18
  501. package/references/claude-code-acp/vitest.config.ts +0 -19
  502. package/references/multi-agent-protocol/.sudocode/issues.jsonl +0 -82
  503. package/references/multi-agent-protocol/.sudocode/specs.jsonl +0 -9
  504. package/references/multi-agent-protocol/LICENSE +0 -21
  505. package/references/multi-agent-protocol/README.md +0 -113
  506. package/references/multi-agent-protocol/docs/00-design-specification.md +0 -460
  507. package/references/multi-agent-protocol/docs/01-open-questions.md +0 -1050
  508. package/references/multi-agent-protocol/docs/02-wire-protocol.md +0 -296
  509. package/references/multi-agent-protocol/docs/03-streaming-semantics.md +0 -252
  510. package/references/multi-agent-protocol/docs/04-error-handling.md +0 -231
  511. package/references/multi-agent-protocol/docs/05-connection-model.md +0 -244
  512. package/references/multi-agent-protocol/docs/06-visibility-permissions.md +0 -243
  513. package/references/multi-agent-protocol/docs/07-federation.md +0 -259
  514. package/references/multi-agent-protocol/docs/08-macro-agent-migration.md +0 -253
  515. package/references/multi-agent-protocol/package-lock.json +0 -3239
  516. package/references/multi-agent-protocol/package.json +0 -56
  517. package/references/multi-agent-protocol/schema/meta.json +0 -337
  518. package/references/multi-agent-protocol/schema/schema.json +0 -1828
@@ -0,0 +1,575 @@
1
+ /**
2
+ * Mail Full Agent E2E Tests
3
+ *
4
+ * Tests that real agent spawning via agentManager.spawn() correctly
5
+ * creates mail conversations, wires the ConversationMap, and cleans
6
+ * up on terminate.
7
+ *
8
+ * REQUIRES: RUN_FULL_AGENT_TESTS=true environment variable (and authenticated Claude Code)
9
+ *
10
+ * Run with:
11
+ * RUN_FULL_AGENT_TESTS=true npx vitest run --config vitest.e2e.config.ts src/mail/__tests__/mail-full-agent.e2e.test.ts
12
+ */
13
+
14
+ import { describe, it, expect, beforeEach, afterEach } from "vitest";
15
+ import { WebSocket } from "ws";
16
+ import * as fs from "fs";
17
+ import * as path from "path";
18
+ import * as os from "os";
19
+ import { execSync } from "child_process";
20
+ import { createEventStore, type EventStore } from "../../store/event-store.js";
21
+ import {
22
+ createAgentManager,
23
+ type AgentManager,
24
+ } from "../../agent/agent-manager.js";
25
+ import {
26
+ createTaskManager,
27
+ type TaskManager,
28
+ } from "../../task/task-manager.js";
29
+ import {
30
+ createMessageRouter,
31
+ type MessageRouter,
32
+ } from "../../router/message-router.js";
33
+ import {
34
+ createCombinedServer,
35
+ type CombinedServer,
36
+ type CombinedServerServices,
37
+ } from "../../server/combined-server.js";
38
+ import type { MailService } from "../mail-service.js";
39
+ import type { ConversationMap } from "../conversation-map.js";
40
+ import type { AgentId } from "../../store/types/index.js";
41
+
42
+ // ─────────────────────────────────────────────────────────────────
43
+ // Test Configuration
44
+ // ─────────────────────────────────────────────────────────────────
45
+
46
+ const RUN_FULL_AGENT = !!process.env.RUN_FULL_AGENT_TESTS;
47
+ const testFn = RUN_FULL_AGENT ? it : it.skip;
48
+
49
+ const TIMEOUT = {
50
+ SPAWN: 60000,
51
+ MULTI_SPAWN: 180000,
52
+ HIERARCHY: 120000,
53
+ };
54
+
55
+ function log(message: string): void {
56
+ console.log(`[Mail E2E] ${message}`);
57
+ }
58
+
59
+ function getRandomPort(): number {
60
+ return 10000 + Math.floor(Math.random() * 50000);
61
+ }
62
+
63
+ /**
64
+ * Create an isolated test git repo.
65
+ */
66
+ function createTestRepo(): { path: string; cleanup: () => void } {
67
+ const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "mail-agent-e2e-"));
68
+ const repoPath = path.join(tmpDir, "test-repo");
69
+ fs.mkdirSync(repoPath);
70
+ execSync("git init", { cwd: repoPath, stdio: "pipe" });
71
+ execSync('git config user.email "test@test.com"', { cwd: repoPath, stdio: "pipe" });
72
+ execSync('git config user.name "Test User"', { cwd: repoPath, stdio: "pipe" });
73
+ fs.writeFileSync(path.join(repoPath, "README.md"), "# Test Repo\n");
74
+ execSync("git add -A", { cwd: repoPath, stdio: "pipe" });
75
+ execSync('git commit -m "Initial commit"', { cwd: repoPath, stdio: "pipe" });
76
+
77
+ return {
78
+ path: repoPath,
79
+ cleanup: () => fs.rmSync(tmpDir, { recursive: true, force: true }),
80
+ };
81
+ }
82
+
83
+ // ─────────────────────────────────────────────────────────────────
84
+ // ACP WebSocket Client
85
+ // ─────────────────────────────────────────────────────────────────
86
+
87
+ interface JsonRpcMessage {
88
+ jsonrpc: "2.0";
89
+ id?: number;
90
+ method?: string;
91
+ params?: unknown;
92
+ result?: unknown;
93
+ error?: { code: number; message: string; data?: unknown };
94
+ }
95
+
96
+ class ACPTestClient {
97
+ private ws!: WebSocket;
98
+ private waiters: Map<number, (msg: JsonRpcMessage) => void> = new Map();
99
+ private nextId = 1;
100
+ private url: string;
101
+
102
+ constructor(url: string) {
103
+ this.url = url;
104
+ }
105
+
106
+ async connect(): Promise<void> {
107
+ this.ws = new WebSocket(this.url);
108
+ return new Promise((resolve, reject) => {
109
+ const timeout = setTimeout(() => reject(new Error("Connection timeout")), 10000);
110
+ this.ws.on("open", () => { clearTimeout(timeout); resolve(); });
111
+ this.ws.on("error", (err) => { clearTimeout(timeout); reject(err); });
112
+ this.ws.on("message", (data: Buffer) => {
113
+ try {
114
+ const msg = JSON.parse(data.toString()) as JsonRpcMessage;
115
+ if (msg.id !== undefined) {
116
+ const waiter = this.waiters.get(msg.id);
117
+ if (waiter) {
118
+ this.waiters.delete(msg.id);
119
+ waiter(msg);
120
+ }
121
+ }
122
+ } catch { /* ignore */ }
123
+ });
124
+ });
125
+ }
126
+
127
+ async request(method: string, params?: unknown, timeoutMs = 30000): Promise<JsonRpcMessage> {
128
+ const id = this.nextId++;
129
+ return new Promise((resolve, reject) => {
130
+ const timeout = setTimeout(() => {
131
+ this.waiters.delete(id);
132
+ reject(new Error(`Timeout waiting for ${method}`));
133
+ }, timeoutMs);
134
+ this.waiters.set(id, (response) => {
135
+ clearTimeout(timeout);
136
+ resolve(response);
137
+ });
138
+ this.ws.send(JSON.stringify({ jsonrpc: "2.0", method, params, id }));
139
+ });
140
+ }
141
+
142
+ async initialize(): Promise<JsonRpcMessage> {
143
+ return this.request("initialize", {
144
+ protocolVersion: 1,
145
+ capabilities: {},
146
+ clientInfo: { name: "mail-e2e-test", version: "1.0.0" },
147
+ });
148
+ }
149
+
150
+ async newSession(params: { cwd?: string } = {}): Promise<JsonRpcMessage> {
151
+ return this.request("session/new", { mcpServers: [], ...params }, 60000);
152
+ }
153
+
154
+ async spawnAgent(parentId: string, task: string): Promise<JsonRpcMessage> {
155
+ return this.request("_macro/spawnAgent", {
156
+ task_description: task,
157
+ parentId,
158
+ }, 60000);
159
+ }
160
+
161
+ close(): void {
162
+ if (this.ws?.readyState === WebSocket.OPEN) {
163
+ this.ws.close();
164
+ }
165
+ }
166
+ }
167
+
168
+ // ─────────────────────────────────────────────────────────────────
169
+ // Tests
170
+ // ─────────────────────────────────────────────────────────────────
171
+
172
+ describe("Mail Full Agent E2E", () => {
173
+ let eventStore: EventStore;
174
+ let agentManager: AgentManager;
175
+ let taskManager: TaskManager;
176
+ let messageRouter: MessageRouter;
177
+ let server: CombinedServer;
178
+ let mailService: MailService;
179
+ let conversationMap: ConversationMap;
180
+ let testRepo: { path: string; cleanup: () => void };
181
+ let testPort: number;
182
+ const clients: ACPTestClient[] = [];
183
+
184
+ beforeEach(async () => {
185
+ if (!RUN_FULL_AGENT) {
186
+ log("Skipping: RUN_FULL_AGENT_TESTS not set");
187
+ return;
188
+ }
189
+
190
+ testRepo = createTestRepo();
191
+ testPort = getRandomPort();
192
+
193
+ // File-based EventStore (required for MCP subprocess access)
194
+ const instanceId = `mail-e2e-${Date.now()}`;
195
+ eventStore = await createEventStore({
196
+ instanceId,
197
+ baseDir: testRepo.path,
198
+ });
199
+ messageRouter = createMessageRouter(eventStore);
200
+ taskManager = createTaskManager(eventStore);
201
+ agentManager = createAgentManager(eventStore, messageRouter, {
202
+ defaultPermissionMode: "auto-approve",
203
+ defaultCwd: testRepo.path,
204
+ });
205
+
206
+ // CombinedServer auto-wires: mailService, conversationMap, turnRecorder
207
+ const services: CombinedServerServices = {
208
+ eventStore,
209
+ agentManager,
210
+ taskManager,
211
+ messageRouter,
212
+ };
213
+
214
+ server = createCombinedServer(services, {
215
+ port: testPort,
216
+ host: "localhost",
217
+ defaultCwd: testRepo.path,
218
+ });
219
+ await server.start();
220
+
221
+ // Get the auto-wired mail services
222
+ mailService = server.mailService!;
223
+ conversationMap = server.conversationMap!;
224
+
225
+ expect(mailService).toBeDefined();
226
+ expect(conversationMap).toBeDefined();
227
+
228
+ log(`Server started on port ${testPort}`);
229
+ });
230
+
231
+ afterEach(async () => {
232
+ if (!RUN_FULL_AGENT) return;
233
+
234
+ for (const client of clients) {
235
+ client.close();
236
+ }
237
+ clients.length = 0;
238
+
239
+ // Terminate all agents
240
+ try {
241
+ const heads = agentManager.listHeadManagers();
242
+ for (const head of heads) {
243
+ try {
244
+ await agentManager.terminate(head.id, "test_cleanup");
245
+ } catch { /* ignore */ }
246
+ }
247
+ } catch { /* ignore */ }
248
+
249
+ await server?.stop().catch(() => {});
250
+ await agentManager?.close();
251
+ await eventStore?.close();
252
+ testRepo?.cleanup();
253
+ log("Cleanup complete");
254
+ });
255
+
256
+ describe("Spawn creates mail conversations", () => {
257
+ testFn(
258
+ "creates task conversation when spawning child agent",
259
+ async () => {
260
+ const client = new ACPTestClient(`ws://localhost:${testPort}/acp`);
261
+ clients.push(client);
262
+
263
+ await client.connect();
264
+ await client.initialize();
265
+
266
+ // Create head manager session
267
+ const sessionResult = await client.newSession({ cwd: testRepo.path });
268
+ expect(sessionResult.error).toBeUndefined();
269
+ log("Head manager session created");
270
+
271
+ const headManagers = agentManager.listHeadManagers();
272
+ expect(headManagers.length).toBeGreaterThanOrEqual(1);
273
+ const headId = headManagers[0].id;
274
+ log(`Head manager: ${headId}`);
275
+
276
+ // Verify head manager has a session conversation in conversationMap
277
+ const sessionConvId = conversationMap.getSessionConversation(headId);
278
+ // Note: session conversation is created by POST /api/init, not by ACP session/new
279
+ // The ACP path doesn't create session conversations — that's fine for this test
280
+
281
+ // Spawn child agent
282
+ const spawnResult = await client.spawnAgent(headId, "Worker: process test data");
283
+ expect(spawnResult.error).toBeUndefined();
284
+ const childId = (spawnResult.result as any).agentId;
285
+ expect(childId).toBeDefined();
286
+ log(`Child spawned: ${childId}`);
287
+
288
+ // Verify task conversation was created for child
289
+ const childConvId = conversationMap.getAgentConversation(childId);
290
+ expect(childConvId).toBeDefined();
291
+ log(`Child task conversation: ${childConvId}`);
292
+
293
+ // Verify conversation properties
294
+ const childConv = mailService.getConversation(childConvId!);
295
+ expect(childConv).not.toBeNull();
296
+ expect(childConv!.type).toBe("task");
297
+ expect(childConv!.status).toBe("active");
298
+ expect(childConv!.subject).toBe("Worker: process test data");
299
+ log(`Conversation type=${childConv!.type}, status=${childConv!.status}`);
300
+
301
+ // Verify participants: parent + child
302
+ const participants = mailService.listParticipants(childConvId!);
303
+ expect(participants).toHaveLength(2);
304
+ const participantIds = participants.map((p) => p.id);
305
+ expect(participantIds).toContain(headId);
306
+ expect(participantIds).toContain(childId);
307
+
308
+ // Verify roles
309
+ const parentParticipant = participants.find((p) => p.id === headId);
310
+ const childParticipant = participants.find((p) => p.id === childId);
311
+ expect(parentParticipant!.role).toBe("initiator");
312
+ expect(childParticipant!.role).toBe("worker");
313
+ log("Participants verified: initiator + worker");
314
+ },
315
+ { timeout: TIMEOUT.SPAWN },
316
+ );
317
+
318
+ testFn(
319
+ "creates conversation tree with correct parentConversationId for 3-level hierarchy",
320
+ async () => {
321
+ const client = new ACPTestClient(`ws://localhost:${testPort}/acp`);
322
+ clients.push(client);
323
+
324
+ await client.connect();
325
+ await client.initialize();
326
+
327
+ // Level 0: head manager
328
+ const sessionResult = await client.newSession({ cwd: testRepo.path });
329
+ expect(sessionResult.error).toBeUndefined();
330
+ const headId = agentManager.listHeadManagers()[0].id;
331
+ log(`Level 0 (Head): ${headId}`);
332
+
333
+ // Level 1: team lead
334
+ const level1Result = await client.spawnAgent(headId, "Team Lead: coordinate workers");
335
+ expect(level1Result.error).toBeUndefined();
336
+ const level1Id = (level1Result.result as any).agentId;
337
+ log(`Level 1 (Lead): ${level1Id}`);
338
+
339
+ // Level 2: worker (child of Level 1)
340
+ const level2Result = await client.spawnAgent(level1Id, "Worker: execute task");
341
+ expect(level2Result.error).toBeUndefined();
342
+ const level2Id = (level2Result.result as any).agentId;
343
+ log(`Level 2 (Worker): ${level2Id}`);
344
+
345
+ // Verify conversation chain
346
+ const level1ConvId = conversationMap.getAgentConversation(level1Id)!;
347
+ const level2ConvId = conversationMap.getAgentConversation(level2Id)!;
348
+ expect(level1ConvId).toBeDefined();
349
+ expect(level2ConvId).toBeDefined();
350
+
351
+ const level1Conv = mailService.getConversation(level1ConvId);
352
+ const level2Conv = mailService.getConversation(level2ConvId);
353
+
354
+ // Level 2's parent conversation should be Level 1's task conversation
355
+ expect(level2Conv!.parentConversationId).toBe(level1ConvId);
356
+ log(`Level 2 parentConversationId → Level 1 task conv: correct`);
357
+
358
+ // Level 1's parent conversation should be head manager's conversation (if it exists)
359
+ const headConvId =
360
+ conversationMap.getAgentConversation(headId) ??
361
+ conversationMap.getSessionConversation(headId);
362
+ if (headConvId) {
363
+ expect(level1Conv!.parentConversationId).toBe(headConvId);
364
+ log(`Level 1 parentConversationId → Head conv: correct`);
365
+ } else {
366
+ // Head manager has no conversation (ACP path doesn't create one)
367
+ expect(level1Conv!.parentConversationId).toBeUndefined();
368
+ log(`Level 1 parentConversationId → undefined (no head conv): correct`);
369
+ }
370
+
371
+ // Verify total conversations
372
+ const allConvs = mailService.listConversations({ type: "task" });
373
+ expect(allConvs.length).toBeGreaterThanOrEqual(2);
374
+ log(`Total task conversations: ${allConvs.length}`);
375
+ },
376
+ { timeout: TIMEOUT.HIERARCHY },
377
+ );
378
+
379
+ testFn(
380
+ "creates separate task conversations for multiple children",
381
+ async () => {
382
+ const client = new ACPTestClient(`ws://localhost:${testPort}/acp`);
383
+ clients.push(client);
384
+
385
+ await client.connect();
386
+ await client.initialize();
387
+
388
+ await client.newSession({ cwd: testRepo.path });
389
+ const headId = agentManager.listHeadManagers()[0].id;
390
+
391
+ // Spawn two children
392
+ const child1Result = await client.spawnAgent(headId, "Worker A: first task");
393
+ const child2Result = await client.spawnAgent(headId, "Worker B: second task");
394
+ expect(child1Result.error).toBeUndefined();
395
+ expect(child2Result.error).toBeUndefined();
396
+
397
+ const child1Id = (child1Result.result as any).agentId;
398
+ const child2Id = (child2Result.result as any).agentId;
399
+ log(`Child 1: ${child1Id}, Child 2: ${child2Id}`);
400
+
401
+ // Verify separate conversations
402
+ const conv1Id = conversationMap.getAgentConversation(child1Id)!;
403
+ const conv2Id = conversationMap.getAgentConversation(child2Id)!;
404
+ expect(conv1Id).toBeDefined();
405
+ expect(conv2Id).toBeDefined();
406
+ expect(conv1Id).not.toBe(conv2Id);
407
+
408
+ const conv1 = mailService.getConversation(conv1Id);
409
+ const conv2 = mailService.getConversation(conv2Id);
410
+ expect(conv1!.subject).toBe("Worker A: first task");
411
+ expect(conv2!.subject).toBe("Worker B: second task");
412
+
413
+ // Both should have same parent conversation
414
+ expect(conv1!.parentConversationId).toBe(conv2!.parentConversationId);
415
+ log("Both children share same parentConversationId");
416
+
417
+ // Each conversation should have 2 participants (parent + child)
418
+ expect(mailService.listParticipants(conv1Id)).toHaveLength(2);
419
+ expect(mailService.listParticipants(conv2Id)).toHaveLength(2);
420
+ log("Each conversation has 2 participants");
421
+ },
422
+ { timeout: TIMEOUT.MULTI_SPAWN },
423
+ );
424
+ });
425
+
426
+ describe("Terminate cleans up mail conversations", () => {
427
+ testFn(
428
+ "closes task conversation on agent terminate",
429
+ async () => {
430
+ const client = new ACPTestClient(`ws://localhost:${testPort}/acp`);
431
+ clients.push(client);
432
+
433
+ await client.connect();
434
+ await client.initialize();
435
+ await client.newSession({ cwd: testRepo.path });
436
+ const headId = agentManager.listHeadManagers()[0].id;
437
+
438
+ // Spawn child
439
+ const childResult = await client.spawnAgent(headId, "Ephemeral worker");
440
+ const childId = (childResult.result as any).agentId;
441
+ const childConvId = conversationMap.getAgentConversation(childId)!;
442
+
443
+ // Verify conversation is active
444
+ expect(mailService.getConversation(childConvId)!.status).toBe("active");
445
+ log(`Child conv ${childConvId} is active`);
446
+
447
+ // Terminate the child
448
+ await agentManager.terminate(childId, "completed");
449
+ log(`Child ${childId} terminated`);
450
+
451
+ // Verify conversation is closed
452
+ const closedConv = mailService.getConversation(childConvId);
453
+ expect(closedConv!.status).toBe("completed");
454
+ log(`Child conv status: ${closedConv!.status}`);
455
+
456
+ // Verify agent removed from conversationMap
457
+ expect(conversationMap.getAgentConversation(childId)).toBeUndefined();
458
+ log("Agent removed from conversationMap");
459
+ },
460
+ { timeout: TIMEOUT.SPAWN },
461
+ );
462
+
463
+ testFn(
464
+ "cascade terminate closes all descendant conversations",
465
+ async () => {
466
+ const client = new ACPTestClient(`ws://localhost:${testPort}/acp`);
467
+ clients.push(client);
468
+
469
+ await client.connect();
470
+ await client.initialize();
471
+ await client.newSession({ cwd: testRepo.path });
472
+ const headId = agentManager.listHeadManagers()[0].id;
473
+
474
+ // Build 3-level hierarchy
475
+ const level1Result = await client.spawnAgent(headId, "Middle manager");
476
+ const level1Id = (level1Result.result as any).agentId;
477
+ const level1ConvId = conversationMap.getAgentConversation(level1Id)!;
478
+
479
+ const level2Result = await client.spawnAgent(level1Id, "Leaf worker");
480
+ const level2Id = (level2Result.result as any).agentId;
481
+ const level2ConvId = conversationMap.getAgentConversation(level2Id)!;
482
+
483
+ // All active
484
+ expect(mailService.getConversation(level1ConvId)!.status).toBe("active");
485
+ expect(mailService.getConversation(level2ConvId)!.status).toBe("active");
486
+ log("All conversations active");
487
+
488
+ // Terminate head manager (cascade terminates level1 and level2)
489
+ await agentManager.terminate(headId, "completed");
490
+ log("Head manager terminated (cascade)");
491
+
492
+ // Wait a moment for cascade to complete
493
+ await new Promise((r) => setTimeout(r, 500));
494
+
495
+ // Both descendant conversations should be closed
496
+ expect(mailService.getConversation(level1ConvId)!.status).not.toBe("active");
497
+ expect(mailService.getConversation(level2ConvId)!.status).not.toBe("active");
498
+ log(`Level 1 conv: ${mailService.getConversation(level1ConvId)!.status}`);
499
+ log(`Level 2 conv: ${mailService.getConversation(level2ConvId)!.status}`);
500
+
501
+ // Agents removed from conversationMap
502
+ expect(conversationMap.getAgentConversation(level1Id)).toBeUndefined();
503
+ expect(conversationMap.getAgentConversation(level2Id)).toBeUndefined();
504
+ log("All agents removed from conversationMap");
505
+ },
506
+ { timeout: TIMEOUT.HIERARCHY },
507
+ );
508
+ });
509
+
510
+ describe("Mail conversations visible via MAP protocol", () => {
511
+ testFn(
512
+ "task conversations created by spawn appear in mail/list",
513
+ async () => {
514
+ // ACP client for spawning
515
+ const acpClient = new ACPTestClient(`ws://localhost:${testPort}/acp`);
516
+ clients.push(acpClient);
517
+
518
+ await acpClient.connect();
519
+ await acpClient.initialize();
520
+ await acpClient.newSession({ cwd: testRepo.path });
521
+ const headId = agentManager.listHeadManagers()[0].id;
522
+
523
+ // Spawn a child
524
+ const childResult = await acpClient.spawnAgent(headId, "Visible worker");
525
+ expect(childResult.error).toBeUndefined();
526
+ const childId = (childResult.result as any).agentId;
527
+ log(`Child spawned: ${childId}`);
528
+
529
+ // MAP client to query conversations
530
+ const mapWs = new WebSocket(`ws://localhost:${testPort}/map`);
531
+ await new Promise<void>((resolve, reject) => {
532
+ const timeout = setTimeout(() => reject(new Error("MAP timeout")), 5000);
533
+ mapWs.on("open", () => { clearTimeout(timeout); resolve(); });
534
+ mapWs.on("error", (e) => { clearTimeout(timeout); reject(e); });
535
+ });
536
+
537
+ // Send mail/list via MAP
538
+ const listId = 1;
539
+ mapWs.send(JSON.stringify({
540
+ jsonrpc: "2.0",
541
+ method: "mail/list",
542
+ params: { type: "task" },
543
+ id: listId,
544
+ }));
545
+
546
+ const response = await new Promise<any>((resolve, reject) => {
547
+ const timeout = setTimeout(() => reject(new Error("mail/list timeout")), 10000);
548
+ mapWs.on("message", (data: Buffer) => {
549
+ const msg = JSON.parse(data.toString());
550
+ if (msg.id === listId) {
551
+ clearTimeout(timeout);
552
+ resolve(msg);
553
+ }
554
+ });
555
+ });
556
+
557
+ expect(response.error).toBeUndefined();
558
+ expect(response.result.conversations.length).toBeGreaterThanOrEqual(1);
559
+
560
+ // Find our child's conversation
561
+ const childConvId = conversationMap.getAgentConversation(childId);
562
+ const found = response.result.conversations.find(
563
+ (c: any) => c.id === childConvId,
564
+ );
565
+ expect(found).toBeDefined();
566
+ expect(found.type).toBe("task");
567
+ expect(found.status).toBe("active");
568
+ log(`Task conversation found via MAP mail/list`);
569
+
570
+ mapWs.close();
571
+ },
572
+ { timeout: TIMEOUT.SPAWN },
573
+ );
574
+ });
575
+ });