macro-agent 0.0.17 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (338) hide show
  1. package/.claude/settings.local.json +3 -1
  2. package/.sudocode/specs.jsonl +4 -0
  3. package/CLAUDE.md +16 -14
  4. package/README.md +11 -29
  5. package/dist/acp/macro-agent.d.ts +17 -0
  6. package/dist/acp/macro-agent.d.ts.map +1 -1
  7. package/dist/acp/macro-agent.js +183 -55
  8. package/dist/acp/macro-agent.js.map +1 -1
  9. package/dist/acp/types.d.ts +32 -1
  10. package/dist/acp/types.d.ts.map +1 -1
  11. package/dist/acp/types.js.map +1 -1
  12. package/dist/agent/agent-manager.d.ts +65 -1
  13. package/dist/agent/agent-manager.d.ts.map +1 -1
  14. package/dist/agent/agent-manager.js +464 -183
  15. package/dist/agent/agent-manager.js.map +1 -1
  16. package/dist/agent/types.d.ts +1 -1
  17. package/dist/agent/types.d.ts.map +1 -1
  18. package/dist/api/server.d.ts +3 -0
  19. package/dist/api/server.d.ts.map +1 -1
  20. package/dist/api/server.js +37 -6
  21. package/dist/api/server.js.map +1 -1
  22. package/dist/auth/index.d.ts +2 -0
  23. package/dist/auth/index.d.ts.map +1 -0
  24. package/dist/auth/index.js +2 -0
  25. package/dist/auth/index.js.map +1 -0
  26. package/dist/auth/token.d.ts +41 -0
  27. package/dist/auth/token.d.ts.map +1 -0
  28. package/dist/auth/token.js +73 -0
  29. package/dist/auth/token.js.map +1 -0
  30. package/dist/cli/acp.d.ts +2 -23
  31. package/dist/cli/acp.d.ts.map +1 -1
  32. package/dist/cli/acp.js +127 -61
  33. package/dist/cli/acp.js.map +1 -1
  34. package/dist/cli/index.js +147 -15
  35. package/dist/cli/index.js.map +1 -1
  36. package/dist/cli/mcp.d.ts +6 -0
  37. package/dist/cli/mcp.d.ts.map +1 -1
  38. package/dist/cli/mcp.js +268 -181
  39. package/dist/cli/mcp.js.map +1 -1
  40. package/dist/cli/parse-args.d.ts +20 -0
  41. package/dist/cli/parse-args.d.ts.map +1 -0
  42. package/dist/cli/parse-args.js +43 -0
  43. package/dist/cli/parse-args.js.map +1 -0
  44. package/dist/cli/stable-instance-id.d.ts +8 -0
  45. package/dist/cli/stable-instance-id.d.ts.map +1 -0
  46. package/dist/cli/stable-instance-id.js +14 -0
  47. package/dist/cli/stable-instance-id.js.map +1 -0
  48. package/dist/config/project-config.d.ts +74 -7
  49. package/dist/config/project-config.d.ts.map +1 -1
  50. package/dist/config/project-config.js +123 -20
  51. package/dist/config/project-config.js.map +1 -1
  52. package/dist/map/adapter/acp-over-map.d.ts +23 -0
  53. package/dist/map/adapter/acp-over-map.d.ts.map +1 -1
  54. package/dist/map/adapter/acp-over-map.js +482 -55
  55. package/dist/map/adapter/acp-over-map.js.map +1 -1
  56. package/dist/map/adapter/connection-manager.d.ts.map +1 -1
  57. package/dist/map/adapter/connection-manager.js +3 -0
  58. package/dist/map/adapter/connection-manager.js.map +1 -1
  59. package/dist/map/adapter/event-log.d.ts +87 -0
  60. package/dist/map/adapter/event-log.d.ts.map +1 -0
  61. package/dist/map/adapter/event-log.js +122 -0
  62. package/dist/map/adapter/event-log.js.map +1 -0
  63. package/dist/map/adapter/event-translator.js +6 -6
  64. package/dist/map/adapter/event-translator.js.map +1 -1
  65. package/dist/map/adapter/extensions/agent-lifecycle.d.ts +82 -0
  66. package/dist/map/adapter/extensions/agent-lifecycle.d.ts.map +1 -0
  67. package/dist/map/adapter/extensions/agent-lifecycle.js +164 -0
  68. package/dist/map/adapter/extensions/agent-lifecycle.js.map +1 -0
  69. package/dist/map/adapter/extensions/index.d.ts +10 -1
  70. package/dist/map/adapter/extensions/index.d.ts.map +1 -1
  71. package/dist/map/adapter/extensions/index.js +34 -0
  72. package/dist/map/adapter/extensions/index.js.map +1 -1
  73. package/dist/map/adapter/extensions/mcp-bridge.d.ts +57 -0
  74. package/dist/map/adapter/extensions/mcp-bridge.d.ts.map +1 -0
  75. package/dist/map/adapter/extensions/mcp-bridge.js +745 -0
  76. package/dist/map/adapter/extensions/mcp-bridge.js.map +1 -0
  77. package/dist/map/adapter/extensions/rename.d.ts +29 -0
  78. package/dist/map/adapter/extensions/rename.d.ts.map +1 -0
  79. package/dist/map/adapter/extensions/rename.js +49 -0
  80. package/dist/map/adapter/extensions/rename.js.map +1 -0
  81. package/dist/map/adapter/extensions/task.d.ts.map +1 -1
  82. package/dist/map/adapter/extensions/task.js +10 -0
  83. package/dist/map/adapter/extensions/task.js.map +1 -1
  84. package/dist/map/adapter/extensions/update-metadata.d.ts +29 -0
  85. package/dist/map/adapter/extensions/update-metadata.d.ts.map +1 -0
  86. package/dist/map/adapter/extensions/update-metadata.js +67 -0
  87. package/dist/map/adapter/extensions/update-metadata.js.map +1 -0
  88. package/dist/map/adapter/index.d.ts +2 -1
  89. package/dist/map/adapter/index.d.ts.map +1 -1
  90. package/dist/map/adapter/index.js +8 -2
  91. package/dist/map/adapter/index.js.map +1 -1
  92. package/dist/map/adapter/interface.d.ts +2 -0
  93. package/dist/map/adapter/interface.d.ts.map +1 -1
  94. package/dist/map/adapter/map-adapter.d.ts +4 -0
  95. package/dist/map/adapter/map-adapter.d.ts.map +1 -1
  96. package/dist/map/adapter/map-adapter.js +302 -30
  97. package/dist/map/adapter/map-adapter.js.map +1 -1
  98. package/dist/map/adapter/subscription-manager.d.ts.map +1 -1
  99. package/dist/map/adapter/subscription-manager.js +5 -1
  100. package/dist/map/adapter/subscription-manager.js.map +1 -1
  101. package/dist/map/adapter/types.d.ts +2 -0
  102. package/dist/map/adapter/types.d.ts.map +1 -1
  103. package/dist/mcp/map-client.d.ts +39 -0
  104. package/dist/mcp/map-client.d.ts.map +1 -0
  105. package/dist/mcp/map-client.js +129 -0
  106. package/dist/mcp/map-client.js.map +1 -0
  107. package/dist/mcp/mcp-server.d.ts +14 -0
  108. package/dist/mcp/mcp-server.d.ts.map +1 -1
  109. package/dist/mcp/mcp-server.js +113 -85
  110. package/dist/mcp/mcp-server.js.map +1 -1
  111. package/dist/mcp/types.d.ts +9 -1
  112. package/dist/mcp/types.d.ts.map +1 -1
  113. package/dist/mcp/types.js.map +1 -1
  114. package/dist/metrics/metrics.js +1 -1
  115. package/dist/metrics/metrics.js.map +1 -1
  116. package/dist/roles/capabilities.d.ts +3 -1
  117. package/dist/roles/capabilities.d.ts.map +1 -1
  118. package/dist/roles/capabilities.js +17 -7
  119. package/dist/roles/capabilities.js.map +1 -1
  120. package/dist/roles/config-loader.d.ts +6 -6
  121. package/dist/roles/config-loader.d.ts.map +1 -1
  122. package/dist/roles/config-loader.js +6 -6
  123. package/dist/roles/config-loader.js.map +1 -1
  124. package/dist/roles/registry.d.ts +2 -2
  125. package/dist/roles/registry.js +2 -2
  126. package/dist/server/combined-server.d.ts +20 -0
  127. package/dist/server/combined-server.d.ts.map +1 -1
  128. package/dist/server/combined-server.js +107 -8
  129. package/dist/server/combined-server.js.map +1 -1
  130. package/dist/store/event-store.d.ts +7 -1
  131. package/dist/store/event-store.d.ts.map +1 -1
  132. package/dist/store/event-store.js +91 -8
  133. package/dist/store/event-store.js.map +1 -1
  134. package/dist/store/types/agents.d.ts +23 -0
  135. package/dist/store/types/agents.d.ts.map +1 -1
  136. package/dist/store/types/events.d.ts +1 -1
  137. package/dist/store/types/events.d.ts.map +1 -1
  138. package/dist/task/backend/index.d.ts +47 -29
  139. package/dist/task/backend/index.d.ts.map +1 -1
  140. package/dist/task/backend/index.js +109 -71
  141. package/dist/task/backend/index.js.map +1 -1
  142. package/dist/task/backend/memory.d.ts +1 -0
  143. package/dist/task/backend/memory.d.ts.map +1 -1
  144. package/dist/task/backend/memory.js +3 -0
  145. package/dist/task/backend/memory.js.map +1 -1
  146. package/dist/task/backend/opentasks/backend.d.ts +140 -0
  147. package/dist/task/backend/opentasks/backend.d.ts.map +1 -0
  148. package/dist/task/backend/opentasks/backend.js +1023 -0
  149. package/dist/task/backend/opentasks/backend.js.map +1 -0
  150. package/dist/task/backend/opentasks/client.d.ts +337 -0
  151. package/dist/task/backend/opentasks/client.d.ts.map +1 -0
  152. package/dist/task/backend/opentasks/client.js +225 -0
  153. package/dist/task/backend/opentasks/client.js.map +1 -0
  154. package/dist/task/backend/opentasks/daemon-manager.d.ts +89 -0
  155. package/dist/task/backend/opentasks/daemon-manager.d.ts.map +1 -0
  156. package/dist/task/backend/opentasks/daemon-manager.js +195 -0
  157. package/dist/task/backend/opentasks/daemon-manager.js.map +1 -0
  158. package/dist/task/backend/opentasks/index.d.ts +21 -0
  159. package/dist/task/backend/opentasks/index.d.ts.map +1 -0
  160. package/dist/task/backend/opentasks/index.js +21 -0
  161. package/dist/task/backend/opentasks/index.js.map +1 -0
  162. package/dist/task/backend/opentasks/mapping.d.ts +48 -0
  163. package/dist/task/backend/opentasks/mapping.d.ts.map +1 -0
  164. package/dist/task/backend/opentasks/mapping.js +77 -0
  165. package/dist/task/backend/opentasks/mapping.js.map +1 -0
  166. package/dist/task/backend/types.d.ts +33 -53
  167. package/dist/task/backend/types.d.ts.map +1 -1
  168. package/dist/task/backend/types.js +7 -11
  169. package/dist/task/backend/types.js.map +1 -1
  170. package/dist/task/backend/unified-tool-provider.d.ts +57 -0
  171. package/dist/task/backend/unified-tool-provider.d.ts.map +1 -0
  172. package/dist/task/backend/unified-tool-provider.js +623 -0
  173. package/dist/task/backend/unified-tool-provider.js.map +1 -0
  174. package/dist/teams/team-loader.d.ts +2 -2
  175. package/dist/teams/team-loader.js +3 -3
  176. package/dist/teams/team-loader.js.map +1 -1
  177. package/dist/teams/team-runtime.d.ts.map +1 -1
  178. package/dist/teams/team-runtime.js +2 -0
  179. package/dist/teams/team-runtime.js.map +1 -1
  180. package/docs/architecture.md +7 -6
  181. package/docs/configuration.md +26 -62
  182. package/docs/implementation-details.md +5 -5
  183. package/docs/implementation-summary.md +17 -17
  184. package/docs/plan-self-driving-support.md +4 -4
  185. package/docs/spec-self-driving-support.md +10 -10
  186. package/docs/team-templates.md +2 -2
  187. package/docs/teams.md +3 -3
  188. package/docs/troubleshooting.md +10 -11
  189. package/package.json +6 -4
  190. package/src/__tests__/e2e/agent-spawn-visibility.e2e.test.ts +761 -0
  191. package/src/__tests__/e2e/full-agent-conflict-resolution.e2e.test.ts +2 -2
  192. package/src/__tests__/e2e/mcp-thin-client-bridge.e2e.test.ts +304 -0
  193. package/src/__tests__/e2e/mcp-tools-available.e2e.test.ts +324 -0
  194. package/src/__tests__/e2e/multi-agent.e2e.test.ts +5 -5
  195. package/src/__tests__/e2e/spawn-session-streaming.e2e.test.ts +563 -0
  196. package/src/acp/__tests__/history.test.ts +8 -4
  197. package/src/acp/__tests__/integration.test.ts +56 -31
  198. package/src/acp/__tests__/macro-agent.test.ts +16 -7
  199. package/src/acp/macro-agent.ts +230 -62
  200. package/src/acp/types.ts +46 -1
  201. package/src/agent/__tests__/agent-manager.test.ts +228 -2
  202. package/src/agent/agent-manager.ts +714 -261
  203. package/src/agent/types.ts +3 -1
  204. package/src/api/server.ts +41 -7
  205. package/src/auth/__tests__/token.test.ts +100 -0
  206. package/src/auth/index.ts +1 -0
  207. package/src/auth/token.ts +82 -0
  208. package/src/cli/__tests__/acp.test.ts +1 -1
  209. package/src/cli/__tests__/stable-instance-id.test.ts +1 -1
  210. package/src/cli/acp.ts +130 -72
  211. package/src/cli/index.ts +120 -14
  212. package/src/cli/mcp.ts +311 -207
  213. package/src/cli/parse-args.ts +54 -0
  214. package/src/cli/stable-instance-id.ts +14 -0
  215. package/src/config/project-config.ts +190 -27
  216. package/src/lifecycle/__tests__/cascade-termination.test.ts +1 -1
  217. package/src/map/adapter/__tests__/acp-over-map-cancel.test.ts +820 -0
  218. package/src/map/adapter/__tests__/acp-over-map-getmodels.test.ts +355 -0
  219. package/src/map/adapter/__tests__/acp-over-map-history.test.ts +724 -2
  220. package/src/map/adapter/__tests__/acp-over-map-persistence.e2e.test.ts +1 -1
  221. package/src/map/adapter/__tests__/event-broadcast.test.ts +420 -0
  222. package/src/map/adapter/__tests__/event-log.test.ts +527 -0
  223. package/src/map/adapter/__tests__/event-translator.test.ts +3 -3
  224. package/src/map/adapter/__tests__/extensions.test.ts +408 -0
  225. package/src/map/adapter/__tests__/map-adapter.test.ts +99 -0
  226. package/src/map/adapter/__tests__/mcp-bridge.test.ts +1187 -0
  227. package/src/map/adapter/__tests__/multi-client-broadcast.test.ts +711 -0
  228. package/src/map/adapter/__tests__/websocket-integration.test.ts +218 -0
  229. package/src/map/adapter/acp-over-map.ts +777 -92
  230. package/src/map/adapter/connection-manager.ts +3 -0
  231. package/src/map/adapter/event-log.ts +208 -0
  232. package/src/map/adapter/event-translator.ts +6 -6
  233. package/src/map/adapter/extensions/agent-lifecycle.ts +267 -0
  234. package/src/map/adapter/extensions/index.ts +60 -0
  235. package/src/map/adapter/extensions/mcp-bridge.ts +995 -0
  236. package/src/map/adapter/extensions/task.ts +11 -0
  237. package/src/map/adapter/extensions/update-metadata.ts +126 -0
  238. package/src/map/adapter/index.ts +28 -0
  239. package/src/map/adapter/interface.ts +2 -0
  240. package/src/map/adapter/map-adapter.ts +373 -38
  241. package/src/map/adapter/subscription-manager.ts +5 -1
  242. package/src/map/adapter/types.ts +2 -0
  243. package/src/mcp/__tests__/map-client.test.ts +386 -0
  244. package/src/mcp/__tests__/mcp-server-thin-client.test.ts +368 -0
  245. package/src/mcp/__tests__/mcp-server.test.ts +100 -1
  246. package/src/mcp/map-client.ts +177 -0
  247. package/src/mcp/mcp-server.ts +191 -100
  248. package/src/mcp/types.ts +6 -1
  249. package/src/metrics/metrics.ts +1 -1
  250. package/src/monitor/__tests__/stale-agent-flow.integration.test.ts +1 -1
  251. package/src/roles/__tests__/config-loader.test.ts +7 -7
  252. package/src/roles/capabilities.ts +17 -7
  253. package/src/roles/config-loader.ts +6 -6
  254. package/src/roles/registry.ts +2 -2
  255. package/src/server/__tests__/combined-server.test.ts +94 -21
  256. package/src/server/combined-server.ts +189 -33
  257. package/src/steering/__tests__/steering-integration.test.ts +1 -1
  258. package/src/store/__tests__/event-store.test.ts +236 -1
  259. package/src/store/__tests__/instance.test.ts +3 -3
  260. package/src/store/event-store.ts +109 -8
  261. package/src/store/types/agents.ts +16 -0
  262. package/src/store/types/events.ts +1 -1
  263. package/src/task/backend/__tests__/create-task-backend.test.ts +225 -0
  264. package/src/task/backend/__tests__/e2e/unified-tool-provider-opentasks.e2e.test.ts +524 -0
  265. package/src/task/backend/__tests__/unified-tool-provider.test.ts +579 -0
  266. package/src/task/backend/index.ts +156 -106
  267. package/src/task/backend/memory.ts +4 -0
  268. package/src/task/backend/opentasks/__tests__/backend.test.ts +968 -0
  269. package/src/task/backend/opentasks/__tests__/daemon-manager.test.ts +406 -0
  270. package/src/task/backend/opentasks/__tests__/mapping.test.ts +84 -0
  271. package/src/task/backend/opentasks/__tests__/opentasks-backend.e2e.test.ts +1338 -0
  272. package/src/task/backend/opentasks/backend.ts +1323 -0
  273. package/src/task/backend/opentasks/client.ts +652 -0
  274. package/src/task/backend/opentasks/daemon-manager.ts +253 -0
  275. package/src/task/backend/opentasks/index.ts +69 -0
  276. package/src/task/backend/opentasks/mapping.ts +94 -0
  277. package/src/task/backend/types.ts +42 -66
  278. package/src/task/backend/unified-tool-provider.ts +779 -0
  279. package/src/teams/__tests__/cross-subsystem.integration.test.ts +1 -1
  280. package/src/teams/team-loader.ts +3 -3
  281. package/src/teams/team-runtime.ts +2 -0
  282. package/test_fixtures/README.md +2 -3
  283. package/test_fixtures/fixtures/index.ts +0 -3
  284. package/test_fixtures/fixtures/projects/project-with-specs.ts +7 -149
  285. package/test_fixtures/fixtures/repos/index.ts +1 -3
  286. package/test_fixtures/fixtures/repos/temp-repo-factory.ts +0 -116
  287. package/test_fixtures/fixtures/repos/types.ts +0 -11
  288. package/test_fixtures/harness/__tests__/fixtures.test.ts +10 -102
  289. package/test_fixtures/harness/__tests__/temp-repo-and-simulator.test.ts +0 -33
  290. package/test_fixtures/harness/simulator/agent-simulator.ts +4 -4
  291. package/vitest.config.ts +1 -1
  292. package/vitest.e2e.config.ts +1 -1
  293. package/vitest.setup.ts +1 -30
  294. package/.macro-agent/teams/self-driving/prompts/grinder.md +0 -27
  295. package/.macro-agent/teams/self-driving/prompts/judge.md +0 -27
  296. package/.macro-agent/teams/self-driving/prompts/planner.md +0 -33
  297. package/.macro-agent/teams/self-driving/roles/grinder.yaml +0 -17
  298. package/.macro-agent/teams/self-driving/roles/judge.yaml +0 -24
  299. package/.macro-agent/teams/self-driving/roles/planner.yaml +0 -18
  300. package/.macro-agent/teams/self-driving/team.yaml +0 -103
  301. package/.macro-agent/teams/structured/prompts/developer.md +0 -26
  302. package/.macro-agent/teams/structured/prompts/lead.md +0 -25
  303. package/.macro-agent/teams/structured/prompts/reviewer.md +0 -24
  304. package/.macro-agent/teams/structured/roles/developer.yaml +0 -12
  305. package/.macro-agent/teams/structured/roles/lead.yaml +0 -11
  306. package/.macro-agent/teams/structured/roles/reviewer.yaml +0 -19
  307. package/.macro-agent/teams/structured/team.yaml +0 -89
  308. package/docs/sudocode-integration.md +0 -383
  309. package/src/task/backend/__tests__/backend-parity.test.ts +0 -451
  310. package/src/task/backend/__tests__/tool-provider-edge-cases.test.ts +0 -430
  311. package/src/task/backend/__tests__/tool-provider.test.ts +0 -983
  312. package/src/task/backend/sudocode/__tests__/backend-edge-cases.test.ts +0 -575
  313. package/src/task/backend/sudocode/__tests__/backend.test.ts +0 -1194
  314. package/src/task/backend/sudocode/__tests__/client-integration.test.ts +0 -418
  315. package/src/task/backend/sudocode/__tests__/client.test.ts +0 -345
  316. package/src/task/backend/sudocode/__tests__/e2e/backend.e2e.test.ts +0 -753
  317. package/src/task/backend/sudocode/__tests__/e2e/server-client.e2e.test.ts +0 -680
  318. package/src/task/backend/sudocode/__tests__/e2e-workflow.test.ts +0 -666
  319. package/src/task/backend/sudocode/__tests__/integration/standalone-client.integration.test.ts +0 -396
  320. package/src/task/backend/sudocode/__tests__/integration/sudocode-cli.integration.test.ts +0 -328
  321. package/src/task/backend/sudocode/__tests__/integration/test-utils.ts +0 -175
  322. package/src/task/backend/sudocode/__tests__/mapping-edge-cases.test.ts +0 -265
  323. package/src/task/backend/sudocode/__tests__/server-client.test.ts +0 -675
  324. package/src/task/backend/sudocode/__tests__/sync-policy-edge-cases.test.ts +0 -521
  325. package/src/task/backend/sudocode/__tests__/sync-policy.test.ts +0 -519
  326. package/src/task/backend/sudocode/__tests__/tools.test.ts +0 -471
  327. package/src/task/backend/sudocode/backend.ts +0 -1237
  328. package/src/task/backend/sudocode/client.ts +0 -515
  329. package/src/task/backend/sudocode/index.ts +0 -120
  330. package/src/task/backend/sudocode/mapping.ts +0 -93
  331. package/src/task/backend/sudocode/server-client.ts +0 -522
  332. package/src/task/backend/sudocode/standalone-client.ts +0 -623
  333. package/src/task/backend/sudocode/sync-policy.ts +0 -387
  334. package/src/task/backend/sudocode/tools.ts +0 -896
  335. package/src/task/backend/tool-provider.ts +0 -506
  336. package/test_fixtures/fixtures/sudocode/index.ts +0 -29
  337. package/test_fixtures/fixtures/sudocode/issues.ts +0 -185
  338. package/test_fixtures/fixtures/sudocode/specs.ts +0 -159
@@ -65,6 +65,7 @@ import {
65
65
  type ScopeId,
66
66
  } from "../types.js";
67
67
  import type { AgentId } from "../../store/types/index.js";
68
+ import type { AgentStopReason } from "../../agent/types.js";
68
69
  import {
69
70
  createConnectionManager,
70
71
  type ConnectionManager,
@@ -92,6 +93,7 @@ import type {
92
93
  } from "../federation/types.js";
93
94
  import { ACPOverMAPHandler, type ACPEnvelope } from "./acp-over-map.js";
94
95
  import { createMailHandlers } from "./mail-handler-adapter.js";
96
+ import { EventLog, dotToUnderscore } from "./event-log.js";
95
97
 
96
98
  // =============================================================================
97
99
  // Connection Session
@@ -227,7 +229,8 @@ export class MAPAdapterImpl implements MAPAdapter {
227
229
  /** Sequence numbers per subscription for proper event ordering */
228
230
  private readonly subscriptionSequences: Map<SubscriptionId, number> =
229
231
  new Map();
230
-
232
+ /** In-memory event log for map/replay support */
233
+ private readonly eventLog: EventLog;
231
234
  private running = false;
232
235
 
233
236
  constructor(
@@ -248,10 +251,95 @@ export class MAPAdapterImpl implements MAPAdapter {
248
251
  eventStore: services.eventStore,
249
252
  taskManager: services.taskManager,
250
253
  defaultCwd: services.defaultCwd,
254
+ // Note: agent_registered events are now emitted by the lifecycle
255
+ // listener below, which covers ALL spawn paths (ACP, MCP, etc.)
251
256
  });
252
257
  console.error("[MAPAdapter] ACP-over-MAP handler initialized");
253
258
  }
254
259
 
260
+ // Listen for agent lifecycle events so spawns/stops happening in THIS
261
+ // process (main server) emit MAP events to subscribers immediately.
262
+ if (services.agentManager) {
263
+ services.agentManager.onLifecycleEvent((event) => {
264
+ if (event.type === "spawned" && event.agent) {
265
+ const agentId = event.agent.id as AgentId;
266
+ this.emitEvent({
267
+ eventId: ulid(),
268
+ type: "agent_registered" as MAPEventType,
269
+ timestamp: Date.now(),
270
+ agentId,
271
+ data: {
272
+ agentId: event.agent.id,
273
+ name: event.agent.name ?? "",
274
+ role: event.agent.role ?? "worker",
275
+ state: event.agent.state ?? "spawning",
276
+ parent: event.agent.parent ?? undefined,
277
+ metadata: event.agent.metadata ?? {},
278
+ },
279
+ });
280
+ } else if (event.type === "stopped" && event.agent) {
281
+ const agentId = event.agent.id as AgentId;
282
+ this.emitEvent({
283
+ eventId: ulid(),
284
+ type: "agent_state_changed" as MAPEventType,
285
+ timestamp: Date.now(),
286
+ agentId,
287
+ data: {
288
+ agentId: event.agent.id,
289
+ current: "stopped",
290
+ previous: "running",
291
+ reason: event.reason,
292
+ },
293
+ });
294
+ this.emitEvent({
295
+ eventId: ulid(),
296
+ type: "agent_unregistered" as MAPEventType,
297
+ timestamp: Date.now(),
298
+ agentId,
299
+ data: {
300
+ agentId: event.agent.id,
301
+ reason: event.reason,
302
+ },
303
+ });
304
+ }
305
+ });
306
+ }
307
+
308
+ // Listen for EventStore task changes to emit MAP task events.
309
+ // Task mutations from MCP bridge handlers run in-process, so
310
+ // onTaskChange fires immediately when tasks are created/updated.
311
+ if (services.eventStore) {
312
+ services.eventStore.onTaskChange((taskId, task) => {
313
+ if (!task) return;
314
+ // Map task status to MAP event type
315
+ const eventTypeMap: Record<string, string> = {
316
+ pending: "task_created",
317
+ assigned: "task_assigned",
318
+ in_progress: "task_assigned",
319
+ completed: "task_completed",
320
+ failed: "task_failed",
321
+ // OpenTasks statuses (defensive — normally mapped before reaching here)
322
+ closed: "task_completed",
323
+ open: "task_created",
324
+ };
325
+ const mapEventType = eventTypeMap[task.status];
326
+ if (mapEventType) {
327
+ this.emitEvent({
328
+ eventId: ulid(),
329
+ type: mapEventType as MAPEventType,
330
+ timestamp: Date.now(),
331
+ agentId: (task.assigned_agent ?? undefined) as AgentId | undefined,
332
+ data: {
333
+ taskId,
334
+ description: task.description,
335
+ status: task.status,
336
+ assignee: task.assigned_agent,
337
+ },
338
+ });
339
+ }
340
+ });
341
+ }
342
+
255
343
  // Initialize connection manager
256
344
  this.connections = createConnectionManager({
257
345
  limits: config.limits,
@@ -267,6 +355,11 @@ export class MAPAdapterImpl implements MAPAdapter {
267
355
  getDescendants: services.getDescendants,
268
356
  });
269
357
 
358
+ // Initialize event log for replay support
359
+ this.eventLog = new EventLog({
360
+ maxSize: config.limits?.maxReplayBufferSize ?? 10_000,
361
+ });
362
+
270
363
  // Forward connection events
271
364
  this.connections.onEvent((event) => {
272
365
  if (event.type === "participant.connected") {
@@ -541,6 +634,17 @@ export class MAPAdapterImpl implements MAPAdapter {
541
634
  // ===========================================================================
542
635
 
543
636
  emitEvent(event: EventNotification): void {
637
+ // Log event for replay support
638
+ this.eventLog.append({
639
+ eventId: event.eventId,
640
+ timestamp: event.timestamp,
641
+ type: event.type,
642
+ data: event.data,
643
+ causedBy: event.causedBy,
644
+ agentId: event.agentId,
645
+ scopeId: event.scopeId,
646
+ });
647
+
544
648
  // Match event against subscriptions
545
649
  const { subscriptions: matchingSubs } = this.subscriptions.match(event);
546
650
 
@@ -702,6 +806,9 @@ export class MAPAdapterImpl implements MAPAdapter {
702
806
  "map/unsubscribe": async (params) =>
703
807
  this.handleUnsubscribe(participantId, params),
704
808
 
809
+ // Replay
810
+ "map/replay": async (params) => this.handleReplay(participantId, params),
811
+
705
812
  // Messaging
706
813
  "map/send": async (params, ctx) =>
707
814
  this.handleSend(participantId, params, ctx),
@@ -712,6 +819,10 @@ export class MAPAdapterImpl implements MAPAdapter {
712
819
  "map/agents/get": async (params) =>
713
820
  this.handleGetAgent(participantId, params),
714
821
 
822
+ // Agent lifecycle
823
+ "map/agents/stop": async (params) =>
824
+ this.handleStopAgent(participantId, params),
825
+
715
826
  // Scope queries
716
827
  "map/scopes/list": async () => this.handleListScopes(participantId),
717
828
  "map/scopes/get": async (params) =>
@@ -761,9 +872,11 @@ export class MAPAdapterImpl implements MAPAdapter {
761
872
  keyof ConnectedParticipant["capabilities"]
762
873
  > = {
763
874
  "map/subscribe": "canSubscribe",
875
+ "map/replay": "canSubscribe",
764
876
  "map/send": "canMessage",
765
877
  "map/agents/list": "canQuery",
766
878
  "map/agents/get": "canQuery",
879
+ "map/agents/stop": "canStop",
767
880
  "map/scopes/list": "canQuery",
768
881
  "map/scopes/get": "canQuery",
769
882
  "map/scopes/create": "canManageScopes",
@@ -808,16 +921,27 @@ export class MAPAdapterImpl implements MAPAdapter {
808
921
  break;
809
922
  }
810
923
 
811
- const result = await session.rpcHandler.process(value, {
812
- participantId: session.participantId,
813
- capabilities: participant.capabilities,
814
- signal: session.abortController.signal,
924
+ // Process messages concurrently (same pattern as toad reference).
925
+ // The message loop must not block on any handler, otherwise
926
+ // session/cancel can't be delivered while session/prompt is running.
927
+ // The stream writer queues writes safely, so concurrent responses
928
+ // don't interleave.
929
+ const processAndRespond = async () => {
930
+ const result = await session.rpcHandler.process(value, {
931
+ participantId: session.participantId,
932
+ capabilities: participant.capabilities,
933
+ signal: session.abortController.signal,
934
+ });
935
+
936
+ if (result.type === "response") {
937
+ await this.sendToSession(session, result.response);
938
+ }
939
+ };
940
+ processAndRespond().catch((err) => {
941
+ if (!session.abortController.signal.aborted) {
942
+ console.error(`[MAPAdapter] Error processing message:`, err);
943
+ }
815
944
  });
816
-
817
- // Send response if needed
818
- if (result.type === "response") {
819
- await this.sendToSession(session, result.response);
820
- }
821
945
  }
822
946
  } catch (error) {
823
947
  if (!session.abortController.signal.aborted) {
@@ -871,12 +995,20 @@ export class MAPAdapterImpl implements MAPAdapter {
871
995
  const connectParams = params as
872
996
  | {
873
997
  type?: ParticipantType;
998
+ participantType?: ParticipantType;
874
999
  name?: string;
875
1000
  credentials?: AuthCredentials;
1001
+ capabilities?: {
1002
+ observation?: { canObserve?: boolean; canQuery?: boolean };
1003
+ messaging?: { canSend?: boolean; canReceive?: boolean; canBroadcast?: boolean };
1004
+ lifecycle?: { canSpawn?: boolean; canRegister?: boolean; canUnregister?: boolean; canSteer?: boolean; canStop?: boolean };
1005
+ scopes?: { canCreateScopes?: boolean; canManageScopes?: boolean };
1006
+ management?: { canManageTasks?: boolean; canManageLifecycle?: boolean };
1007
+ };
876
1008
  }
877
1009
  | undefined;
878
1010
 
879
- const requestedType = connectParams?.type ?? participant.type;
1011
+ const requestedType = connectParams?.type ?? connectParams?.participantType ?? participant.type;
880
1012
  const credentials = connectParams?.credentials;
881
1013
 
882
1014
  // Handle authentication if handler is configured and credentials provided
@@ -905,15 +1037,41 @@ export class MAPAdapterImpl implements MAPAdapter {
905
1037
  };
906
1038
  }
907
1039
 
908
- // No auth handler or no credentials - use default capabilities for the type
1040
+ // Start with default capabilities for the requested type
1041
+ const baseCapabilities = this.getDefaultCapabilities(requestedType);
1042
+
1043
+ // Merge client-requested capabilities (SDK nested format → flat format).
1044
+ // Clients can request additional capabilities; the server grants them
1045
+ // by merging with the type defaults.
1046
+ if (connectParams?.capabilities) {
1047
+ const req = connectParams.capabilities;
1048
+ const requested: Partial<ParticipantCapabilities> = {};
1049
+ if (req.observation?.canQuery !== undefined) requested.canQuery = req.observation.canQuery;
1050
+ if (req.observation?.canObserve !== undefined) requested.canSubscribe = req.observation.canObserve;
1051
+ if (req.messaging?.canSend !== undefined) requested.canMessage = req.messaging.canSend;
1052
+ if (req.lifecycle?.canSpawn !== undefined) requested.canSpawn = req.lifecycle.canSpawn;
1053
+ if (req.lifecycle?.canStop !== undefined) requested.canStop = req.lifecycle.canStop;
1054
+ if (req.scopes?.canManageScopes !== undefined) requested.canManageScopes = req.scopes.canManageScopes;
1055
+ if (req.management?.canManageTasks !== undefined) requested.canManageTasks = req.management.canManageTasks;
1056
+ if (req.management?.canManageLifecycle !== undefined) requested.canManageLifecycle = req.management.canManageLifecycle;
1057
+ const mergedCapabilities = { ...baseCapabilities, ...requested };
1058
+ this.connections.updateCapabilities(participantId, mergedCapabilities);
1059
+
1060
+ const updatedParticipant = this.connections.getParticipant(participantId);
1061
+ return {
1062
+ participantId: participant.id,
1063
+ capabilities: updatedParticipant?.capabilities ?? mergedCapabilities,
1064
+ };
1065
+ }
1066
+
1067
+ // No client-requested capabilities — apply type defaults if type changed
909
1068
  if (requestedType !== participant.type) {
910
- const defaultCapabilities = this.getDefaultCapabilities(requestedType);
911
- this.connections.updateCapabilities(participantId, defaultCapabilities);
1069
+ this.connections.updateCapabilities(participantId, baseCapabilities);
912
1070
 
913
1071
  const updatedParticipant = this.connections.getParticipant(participantId);
914
1072
  return {
915
1073
  participantId: participant.id,
916
- capabilities: updatedParticipant?.capabilities ?? defaultCapabilities,
1074
+ capabilities: updatedParticipant?.capabilities ?? baseCapabilities,
917
1075
  };
918
1076
  }
919
1077
 
@@ -941,6 +1099,7 @@ export class MAPAdapterImpl implements MAPAdapter {
941
1099
  canManageScopes: true,
942
1100
  canManageTasks: true,
943
1101
  canManageFederation: true,
1102
+ canManageLifecycle: true,
944
1103
  }
945
1104
  );
946
1105
  case "gateway":
@@ -953,6 +1112,7 @@ export class MAPAdapterImpl implements MAPAdapter {
953
1112
  canManageScopes: false,
954
1113
  canManageTasks: false,
955
1114
  canManageFederation: true,
1115
+ canManageLifecycle: false,
956
1116
  };
957
1117
  case "client":
958
1118
  default:
@@ -965,6 +1125,7 @@ export class MAPAdapterImpl implements MAPAdapter {
965
1125
  canStop: false,
966
1126
  canManageScopes: false,
967
1127
  canManageTasks: false,
1128
+ canManageLifecycle: false,
968
1129
  }
969
1130
  );
970
1131
  }
@@ -1015,6 +1176,69 @@ export class MAPAdapterImpl implements MAPAdapter {
1015
1176
  return { success: true };
1016
1177
  }
1017
1178
 
1179
+ private async handleReplay(
1180
+ _participantId: ParticipantId,
1181
+ params: unknown,
1182
+ ): Promise<{
1183
+ events: Array<{
1184
+ eventId: string;
1185
+ timestamp: number;
1186
+ causedBy?: string[];
1187
+ event: { id: string; type: string; timestamp: number; data: unknown };
1188
+ }>;
1189
+ hasMore: boolean;
1190
+ }> {
1191
+ const { afterEventId, fromTimestamp, toTimestamp, filter, limit } =
1192
+ (params ?? {}) as {
1193
+ afterEventId?: string;
1194
+ fromTimestamp?: number;
1195
+ toTimestamp?: number;
1196
+ filter?: {
1197
+ eventTypes?: string[];
1198
+ fromAgents?: string[];
1199
+ agents?: string[];
1200
+ scopes?: string[];
1201
+ };
1202
+ limit?: number;
1203
+ };
1204
+
1205
+ // Translate SDK filter field names to internal format
1206
+ // SDK uses 'fromAgents', macro-agent uses 'agents'
1207
+ const internalFilter = filter
1208
+ ? {
1209
+ eventTypes: filter.eventTypes,
1210
+ agents: (filter.fromAgents ?? filter.agents) as string[] | undefined,
1211
+ scopes: filter.scopes,
1212
+ }
1213
+ : undefined;
1214
+
1215
+ const result = this.eventLog.query({
1216
+ afterEventId,
1217
+ fromTimestamp,
1218
+ toTimestamp,
1219
+ filter: internalFilter,
1220
+ limit,
1221
+ });
1222
+
1223
+ // Transform events to SDK response format
1224
+ const events = result.events.map((e) => ({
1225
+ eventId: e.eventId,
1226
+ timestamp: e.timestamp,
1227
+ ...(e.causedBy && { causedBy: e.causedBy }),
1228
+ event: {
1229
+ id: e.eventId,
1230
+ type: dotToUnderscore(e.type),
1231
+ timestamp: e.timestamp,
1232
+ data: e.data,
1233
+ },
1234
+ }));
1235
+
1236
+ return {
1237
+ events,
1238
+ hasMore: result.hasMore,
1239
+ };
1240
+ }
1241
+
1018
1242
  private async handleSend(
1019
1243
  participantId: ParticipantId,
1020
1244
  params: unknown,
@@ -1104,6 +1328,27 @@ export class MAPAdapterImpl implements MAPAdapter {
1104
1328
  );
1105
1329
  }
1106
1330
 
1331
+ // Emit client→agent ACP request as a message_sent event so other
1332
+ // TUI clients can observe user prompts and other ACP operations.
1333
+ // Include from/to at top level of data for extractACPFromMessageEvent()
1334
+ // which reads data.from/data.to for targetAgent resolution.
1335
+ this.emitEvent({
1336
+ eventId: ulid(),
1337
+ type: "message_sent" as MAPEventType,
1338
+ timestamp: Date.now(),
1339
+ agentId: targetAgentId,
1340
+ data: {
1341
+ from: participantId,
1342
+ to: targetAgentId,
1343
+ message: {
1344
+ id: `acp-req-${Date.now()}`,
1345
+ from: participantId,
1346
+ to: targetAgentId,
1347
+ payload: envelope,
1348
+ },
1349
+ },
1350
+ });
1351
+
1107
1352
  // Create notification emitter to stream session updates
1108
1353
  const emitNotification = (notification: ACPEnvelope) => {
1109
1354
  this.emitEvent({
@@ -1112,6 +1357,8 @@ export class MAPAdapterImpl implements MAPAdapter {
1112
1357
  timestamp: Date.now(),
1113
1358
  agentId: targetAgentId,
1114
1359
  data: {
1360
+ from: targetAgentId,
1361
+ to: participantId,
1115
1362
  message: {
1116
1363
  id: `acp-notif-${Date.now()}`,
1117
1364
  from: targetAgentId,
@@ -1126,32 +1373,77 @@ export class MAPAdapterImpl implements MAPAdapter {
1126
1373
  acp: acp as ACPEnvelope["acp"],
1127
1374
  acpContext: acpContext as ACPEnvelope["acpContext"],
1128
1375
  };
1129
- const responseEnvelope = await this.acpOverMapHandler.processRequest(
1130
- targetAgentId,
1131
- acpEnvelope,
1132
- emitNotification,
1133
- );
1134
1376
 
1135
- console.error(`[ACP-over-MAP] Request processed - method=${acp.method}`);
1136
-
1137
- // Emit response event to the participant's subscriptions
1138
- // SDK's stream.ts checks for "message_delivered" (underscore) not "message.delivered" (dot)
1139
- // Use underscore format for ACP-over-MAP compatibility
1140
- const participant = this.connections.getParticipant(participantId);
1141
- if (participant) {
1142
- this.emitEvent({
1143
- eventId: ulid(),
1144
- type: "message_delivered" as MAPEventType, // Cast needed - SDK expects underscore format
1145
- timestamp: Date.now(),
1146
- agentId: targetAgentId, // Must be at top level for subscription matching
1147
- data: {
1148
- message: {
1149
- id: `acp-resp-${Date.now()}`,
1377
+ // Helper to emit the final ACP response to the participant via subscription
1378
+ const emitResponse = (responseEnvelope: ACPEnvelope) => {
1379
+ const participant = this.connections.getParticipant(participantId);
1380
+ if (participant) {
1381
+ this.emitEvent({
1382
+ eventId: ulid(),
1383
+ type: "message_delivered" as MAPEventType,
1384
+ timestamp: Date.now(),
1385
+ agentId: targetAgentId,
1386
+ data: {
1150
1387
  from: targetAgentId,
1151
- payload: responseEnvelope,
1388
+ to: participantId,
1389
+ message: {
1390
+ id: `acp-resp-${Date.now()}`,
1391
+ from: targetAgentId,
1392
+ payload: responseEnvelope,
1393
+ },
1152
1394
  },
1153
- },
1154
- });
1395
+ });
1396
+ }
1397
+ };
1398
+
1399
+ // session/prompt can run for minutes — process it asynchronously so the
1400
+ // map/send RPC returns immediately. The ACP response will arrive via
1401
+ // subscription (message_delivered event) when the prompt finishes.
1402
+ // Other ACP methods (initialize, session/new, session/load, etc.) are
1403
+ // fast and can be awaited normally.
1404
+ const isLongRunning = acp.method === "session/prompt";
1405
+
1406
+ if (isLongRunning) {
1407
+ this.acpOverMapHandler
1408
+ .processRequest(targetAgentId, acpEnvelope, emitNotification)
1409
+ .then((responseEnvelope) => {
1410
+ console.error(
1411
+ `[ACP-over-MAP] Async prompt completed - method=${acp.method}`,
1412
+ );
1413
+ emitResponse(responseEnvelope);
1414
+ })
1415
+ .catch((error) => {
1416
+ console.error(`[ACP-over-MAP] Async prompt error:`, error);
1417
+ // Emit error response so the client's ACP pending request resolves
1418
+ emitResponse({
1419
+ acp: {
1420
+ jsonrpc: "2.0",
1421
+ id: acp.id,
1422
+ error: {
1423
+ code: -32603,
1424
+ message:
1425
+ error instanceof Error ? error.message : String(error),
1426
+ },
1427
+ },
1428
+ acpContext: {
1429
+ streamId: acpContext.streamId,
1430
+ sessionId: acpContext.sessionId ?? null,
1431
+ direction: "agent-to-client",
1432
+ },
1433
+ } as ACPEnvelope);
1434
+ });
1435
+ } else {
1436
+ const responseEnvelope =
1437
+ await this.acpOverMapHandler.processRequest(
1438
+ targetAgentId,
1439
+ acpEnvelope,
1440
+ emitNotification,
1441
+ );
1442
+
1443
+ console.error(
1444
+ `[ACP-over-MAP] Request processed - method=${acp.method}`,
1445
+ );
1446
+ emitResponse(responseEnvelope);
1155
1447
  }
1156
1448
 
1157
1449
  return {
@@ -1181,6 +1473,49 @@ export class MAPAdapterImpl implements MAPAdapter {
1181
1473
  return { agent: agent ?? null };
1182
1474
  }
1183
1475
 
1476
+ private async handleStopAgent(
1477
+ _participantId: ParticipantId,
1478
+ params: unknown,
1479
+ ): Promise<{ stopping: boolean; agent?: AgentInfo }> {
1480
+ const { agentId, reason, force } =
1481
+ (params as {
1482
+ agentId?: AgentId;
1483
+ reason?: string;
1484
+ force?: boolean;
1485
+ }) ?? {};
1486
+
1487
+ if (!agentId) {
1488
+ throw RPCError.invalidParams("agentId required");
1489
+ }
1490
+
1491
+ if (!this.services.agentManager) {
1492
+ throw RPCError.internalError("Agent manager not available");
1493
+ }
1494
+
1495
+ // Abort any active ACP streams for this agent
1496
+ if (this.acpOverMapHandler) {
1497
+ this.acpOverMapHandler.abortStreamsForAgent(agentId);
1498
+ }
1499
+
1500
+ // Terminate the agent
1501
+ try {
1502
+ await this.services.agentManager.terminate(
1503
+ agentId,
1504
+ (reason ?? "cancelled") as AgentStopReason,
1505
+ );
1506
+ } catch (error) {
1507
+ console.error(`[MAPAdapter] Error stopping agent ${agentId}:`, error);
1508
+ throw RPCError.internalError(
1509
+ `Failed to stop agent: ${error instanceof Error ? error.message : String(error)}`,
1510
+ );
1511
+ }
1512
+
1513
+ // Note: agent_state_changed and agent_unregistered events are now
1514
+ // emitted by the lifecycle listener (covers all stop paths).
1515
+
1516
+ return { stopping: true };
1517
+ }
1518
+
1184
1519
  private async handleListScopes(
1185
1520
  participantId: ParticipantId,
1186
1521
  ): Promise<{ scopes: ScopeInfo[] }> {
@@ -400,8 +400,12 @@ export class SubscriptionManagerImpl implements SubscriptionManager {
400
400
  }
401
401
 
402
402
  // Event type filter (OR within array)
403
+ // Normalize between underscore format (SDK: "agent_registered") and
404
+ // dot format (internal: "agent.registered") so subscriptions match
405
+ // regardless of which convention the client uses.
403
406
  if (filter.eventTypes && filter.eventTypes.length > 0) {
404
- if (!filter.eventTypes.includes(event.type as MAPEventType)) {
407
+ const normalizedEventType = event.type.replace(/\./g, "_");
408
+ if (!filter.eventTypes.some((ft) => ft.replace(/\./g, "_") === normalizedEventType)) {
405
409
  return false;
406
410
  }
407
411
  }
@@ -88,6 +88,8 @@ export interface ParticipantCapabilities {
88
88
  canManageTasks?: boolean;
89
89
  /** Can manage federation connections (connect/disconnect peers) */
90
90
  canManageFederation?: boolean;
91
+ /** Can manage agent lifecycle (resume, spawn, fork, permissions) */
92
+ canManageLifecycle?: boolean;
91
93
  }
92
94
 
93
95
  /**