macro-agent 0.1.0 → 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 (337) 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 +15 -0
  6. package/dist/acp/macro-agent.d.ts.map +1 -1
  7. package/dist/acp/macro-agent.js +131 -35
  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 +17 -0
  53. package/dist/map/adapter/acp-over-map.d.ts.map +1 -1
  54. package/dist/map/adapter/acp-over-map.js +384 -23
  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 +3 -0
  95. package/dist/map/adapter/map-adapter.d.ts.map +1 -1
  96. package/dist/map/adapter/map-adapter.js +258 -35
  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 +2 -1
  131. package/dist/store/event-store.d.ts.map +1 -1
  132. package/dist/store/event-store.js +69 -20
  133. package/dist/store/event-store.js.map +1 -1
  134. package/dist/store/types/agents.d.ts +18 -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__/integration.test.ts +56 -31
  197. package/src/acp/__tests__/macro-agent.test.ts +16 -7
  198. package/src/acp/macro-agent.ts +170 -36
  199. package/src/acp/types.ts +46 -1
  200. package/src/agent/__tests__/agent-manager.test.ts +228 -2
  201. package/src/agent/agent-manager.ts +714 -261
  202. package/src/agent/types.ts +3 -1
  203. package/src/api/server.ts +41 -7
  204. package/src/auth/__tests__/token.test.ts +100 -0
  205. package/src/auth/index.ts +1 -0
  206. package/src/auth/token.ts +82 -0
  207. package/src/cli/__tests__/acp.test.ts +1 -1
  208. package/src/cli/__tests__/stable-instance-id.test.ts +1 -1
  209. package/src/cli/acp.ts +130 -72
  210. package/src/cli/index.ts +120 -14
  211. package/src/cli/mcp.ts +311 -207
  212. package/src/cli/parse-args.ts +54 -0
  213. package/src/cli/stable-instance-id.ts +14 -0
  214. package/src/config/project-config.ts +190 -27
  215. package/src/lifecycle/__tests__/cascade-termination.test.ts +1 -1
  216. package/src/map/adapter/__tests__/acp-over-map-cancel.test.ts +22 -4
  217. package/src/map/adapter/__tests__/acp-over-map-getmodels.test.ts +355 -0
  218. package/src/map/adapter/__tests__/acp-over-map-history.test.ts +263 -0
  219. package/src/map/adapter/__tests__/acp-over-map-persistence.e2e.test.ts +1 -1
  220. package/src/map/adapter/__tests__/event-broadcast.test.ts +420 -0
  221. package/src/map/adapter/__tests__/event-log.test.ts +527 -0
  222. package/src/map/adapter/__tests__/event-translator.test.ts +3 -3
  223. package/src/map/adapter/__tests__/extensions.test.ts +408 -0
  224. package/src/map/adapter/__tests__/map-adapter.test.ts +99 -0
  225. package/src/map/adapter/__tests__/mcp-bridge.test.ts +1187 -0
  226. package/src/map/adapter/__tests__/multi-client-broadcast.test.ts +711 -0
  227. package/src/map/adapter/__tests__/websocket-integration.test.ts +218 -0
  228. package/src/map/adapter/acp-over-map.ts +678 -66
  229. package/src/map/adapter/connection-manager.ts +3 -0
  230. package/src/map/adapter/event-log.ts +208 -0
  231. package/src/map/adapter/event-translator.ts +6 -6
  232. package/src/map/adapter/extensions/agent-lifecycle.ts +267 -0
  233. package/src/map/adapter/extensions/index.ts +60 -0
  234. package/src/map/adapter/extensions/mcp-bridge.ts +995 -0
  235. package/src/map/adapter/extensions/task.ts +11 -0
  236. package/src/map/adapter/extensions/update-metadata.ts +126 -0
  237. package/src/map/adapter/index.ts +28 -0
  238. package/src/map/adapter/interface.ts +2 -0
  239. package/src/map/adapter/map-adapter.ts +312 -47
  240. package/src/map/adapter/subscription-manager.ts +5 -1
  241. package/src/map/adapter/types.ts +2 -0
  242. package/src/mcp/__tests__/map-client.test.ts +386 -0
  243. package/src/mcp/__tests__/mcp-server-thin-client.test.ts +368 -0
  244. package/src/mcp/__tests__/mcp-server.test.ts +100 -1
  245. package/src/mcp/map-client.ts +177 -0
  246. package/src/mcp/mcp-server.ts +191 -100
  247. package/src/mcp/types.ts +6 -1
  248. package/src/metrics/metrics.ts +1 -1
  249. package/src/monitor/__tests__/stale-agent-flow.integration.test.ts +1 -1
  250. package/src/roles/__tests__/config-loader.test.ts +7 -7
  251. package/src/roles/capabilities.ts +17 -7
  252. package/src/roles/config-loader.ts +6 -6
  253. package/src/roles/registry.ts +2 -2
  254. package/src/server/__tests__/combined-server.test.ts +94 -21
  255. package/src/server/combined-server.ts +189 -33
  256. package/src/steering/__tests__/steering-integration.test.ts +1 -1
  257. package/src/store/__tests__/event-store.test.ts +196 -1
  258. package/src/store/__tests__/instance.test.ts +3 -3
  259. package/src/store/event-store.ts +80 -21
  260. package/src/store/types/agents.ts +15 -0
  261. package/src/store/types/events.ts +1 -1
  262. package/src/task/backend/__tests__/create-task-backend.test.ts +225 -0
  263. package/src/task/backend/__tests__/e2e/unified-tool-provider-opentasks.e2e.test.ts +524 -0
  264. package/src/task/backend/__tests__/unified-tool-provider.test.ts +579 -0
  265. package/src/task/backend/index.ts +156 -106
  266. package/src/task/backend/memory.ts +4 -0
  267. package/src/task/backend/opentasks/__tests__/backend.test.ts +968 -0
  268. package/src/task/backend/opentasks/__tests__/daemon-manager.test.ts +406 -0
  269. package/src/task/backend/opentasks/__tests__/mapping.test.ts +84 -0
  270. package/src/task/backend/opentasks/__tests__/opentasks-backend.e2e.test.ts +1338 -0
  271. package/src/task/backend/opentasks/backend.ts +1323 -0
  272. package/src/task/backend/opentasks/client.ts +652 -0
  273. package/src/task/backend/opentasks/daemon-manager.ts +253 -0
  274. package/src/task/backend/opentasks/index.ts +69 -0
  275. package/src/task/backend/opentasks/mapping.ts +94 -0
  276. package/src/task/backend/types.ts +42 -66
  277. package/src/task/backend/unified-tool-provider.ts +779 -0
  278. package/src/teams/__tests__/cross-subsystem.integration.test.ts +1 -1
  279. package/src/teams/team-loader.ts +3 -3
  280. package/src/teams/team-runtime.ts +2 -0
  281. package/test_fixtures/README.md +2 -3
  282. package/test_fixtures/fixtures/index.ts +0 -3
  283. package/test_fixtures/fixtures/projects/project-with-specs.ts +7 -149
  284. package/test_fixtures/fixtures/repos/index.ts +1 -3
  285. package/test_fixtures/fixtures/repos/temp-repo-factory.ts +0 -116
  286. package/test_fixtures/fixtures/repos/types.ts +0 -11
  287. package/test_fixtures/harness/__tests__/fixtures.test.ts +10 -102
  288. package/test_fixtures/harness/__tests__/temp-repo-and-simulator.test.ts +0 -33
  289. package/test_fixtures/harness/simulator/agent-simulator.ts +4 -4
  290. package/vitest.config.ts +1 -1
  291. package/vitest.e2e.config.ts +1 -1
  292. package/vitest.setup.ts +1 -30
  293. package/.macro-agent/teams/self-driving/prompts/grinder.md +0 -27
  294. package/.macro-agent/teams/self-driving/prompts/judge.md +0 -27
  295. package/.macro-agent/teams/self-driving/prompts/planner.md +0 -33
  296. package/.macro-agent/teams/self-driving/roles/grinder.yaml +0 -17
  297. package/.macro-agent/teams/self-driving/roles/judge.yaml +0 -24
  298. package/.macro-agent/teams/self-driving/roles/planner.yaml +0 -18
  299. package/.macro-agent/teams/self-driving/team.yaml +0 -103
  300. package/.macro-agent/teams/structured/prompts/developer.md +0 -26
  301. package/.macro-agent/teams/structured/prompts/lead.md +0 -25
  302. package/.macro-agent/teams/structured/prompts/reviewer.md +0 -24
  303. package/.macro-agent/teams/structured/roles/developer.yaml +0 -12
  304. package/.macro-agent/teams/structured/roles/lead.yaml +0 -11
  305. package/.macro-agent/teams/structured/roles/reviewer.yaml +0 -19
  306. package/.macro-agent/teams/structured/team.yaml +0 -89
  307. package/docs/sudocode-integration.md +0 -383
  308. package/src/task/backend/__tests__/backend-parity.test.ts +0 -451
  309. package/src/task/backend/__tests__/tool-provider-edge-cases.test.ts +0 -430
  310. package/src/task/backend/__tests__/tool-provider.test.ts +0 -983
  311. package/src/task/backend/sudocode/__tests__/backend-edge-cases.test.ts +0 -575
  312. package/src/task/backend/sudocode/__tests__/backend.test.ts +0 -1194
  313. package/src/task/backend/sudocode/__tests__/client-integration.test.ts +0 -418
  314. package/src/task/backend/sudocode/__tests__/client.test.ts +0 -345
  315. package/src/task/backend/sudocode/__tests__/e2e/backend.e2e.test.ts +0 -753
  316. package/src/task/backend/sudocode/__tests__/e2e/server-client.e2e.test.ts +0 -680
  317. package/src/task/backend/sudocode/__tests__/e2e-workflow.test.ts +0 -666
  318. package/src/task/backend/sudocode/__tests__/integration/standalone-client.integration.test.ts +0 -396
  319. package/src/task/backend/sudocode/__tests__/integration/sudocode-cli.integration.test.ts +0 -328
  320. package/src/task/backend/sudocode/__tests__/integration/test-utils.ts +0 -175
  321. package/src/task/backend/sudocode/__tests__/mapping-edge-cases.test.ts +0 -265
  322. package/src/task/backend/sudocode/__tests__/server-client.test.ts +0 -675
  323. package/src/task/backend/sudocode/__tests__/sync-policy-edge-cases.test.ts +0 -521
  324. package/src/task/backend/sudocode/__tests__/sync-policy.test.ts +0 -519
  325. package/src/task/backend/sudocode/__tests__/tools.test.ts +0 -471
  326. package/src/task/backend/sudocode/backend.ts +0 -1237
  327. package/src/task/backend/sudocode/client.ts +0 -515
  328. package/src/task/backend/sudocode/index.ts +0 -120
  329. package/src/task/backend/sudocode/mapping.ts +0 -93
  330. package/src/task/backend/sudocode/server-client.ts +0 -522
  331. package/src/task/backend/sudocode/standalone-client.ts +0 -623
  332. package/src/task/backend/sudocode/sync-policy.ts +0 -387
  333. package/src/task/backend/sudocode/tools.ts +0 -896
  334. package/src/task/backend/tool-provider.ts +0 -506
  335. package/test_fixtures/fixtures/sudocode/index.ts +0 -29
  336. package/test_fixtures/fixtures/sudocode/issues.ts +0 -185
  337. package/test_fixtures/fixtures/sudocode/specs.ts +0 -159
@@ -26,6 +26,11 @@ import {
26
26
  createMAPAdapter,
27
27
  createMAPWebSocketHandler,
28
28
  registerWorkspaceFileExtensions,
29
+ registerUpdateMetadataExtension,
30
+ registerMCPBridgeExtensions,
31
+ registerTaskExtensions,
32
+ registerResumeExtension,
33
+ registerAgentLifecycleExtensions,
29
34
  type MAPAdapter,
30
35
  type MAPAdapterServices,
31
36
  type MAPWebSocketHandler,
@@ -33,8 +38,17 @@ import {
33
38
  import type { Agent, AgentId } from "../store/types/index.js";
34
39
  import type { Address, SendOptions } from "../map/types.js";
35
40
  import { createMailService, type MailService } from "../mail/mail-service.js";
36
- import { createConversationMap, type ConversationMap } from "../mail/conversation-map.js";
41
+ import {
42
+ createConversationMap,
43
+ type ConversationMap,
44
+ } from "../mail/conversation-map.js";
37
45
  import { createTurnRecorder } from "../mail/turn-recorder.js";
46
+ import {
47
+ AgentTokenManager,
48
+ generateToken as generateTokenFn,
49
+ secureCompare,
50
+ } from "../auth/token.js";
51
+ import { TaskBackend, TaskToolProvider } from "../task/backend/types.js";
38
52
 
39
53
  // ─────────────────────────────────────────────────────────────────
40
54
  // Types
@@ -49,6 +63,16 @@ export interface CombinedServerServices {
49
63
  capabilityManager?: CapabilityManager;
50
64
  /** Optional activity watcher for event-driven waking */
51
65
  activityWatcher?: ActivityWatcher;
66
+ /** Optional task backend for task tool bridge extensions */
67
+ taskBackend?: TaskBackend;
68
+ /** Optional task tool provider for dynamic task tools in thin-client mode */
69
+ taskToolProvider?: TaskToolProvider;
70
+ /** Mutable context holder for task tool provider agent_id injection */
71
+ taskToolContext?: { agent_id: string };
72
+ /** Per-agent token manager for MCP bridge authentication */
73
+ agentTokenManager?: AgentTokenManager;
74
+ /** Get connected opentasks project paths (for health endpoint) */
75
+ getConnectedProjects?: () => string[];
52
76
  }
53
77
 
54
78
  export interface CombinedServerConfig {
@@ -69,6 +93,12 @@ export interface CombinedServerConfig {
69
93
 
70
94
  /** Disable MAP protocol (default: false - MAP is enabled) */
71
95
  disableMap?: boolean;
96
+
97
+ /** Server token for authentication. Auto-generated if not provided (unless noAuth is true). */
98
+ serverToken?: string;
99
+
100
+ /** Disable authentication entirely (for local development/testing) */
101
+ noAuth?: boolean;
72
102
  }
73
103
 
74
104
  export interface CombinedServer {
@@ -101,6 +131,9 @@ export interface CombinedServer {
101
131
 
102
132
  /** Conversation map (for agent-to-conversation tracking) */
103
133
  readonly conversationMap?: ConversationMap;
134
+
135
+ /** Server token used for authentication (exposed for tests). Undefined when auth is disabled. */
136
+ readonly serverToken?: string;
104
137
  }
105
138
 
106
139
  // ─────────────────────────────────────────────────────────────────
@@ -110,7 +143,10 @@ export interface CombinedServer {
110
143
  /**
111
144
  * Get all descendants of an agent recursively.
112
145
  */
113
- function getDescendantsRecursive(agentId: AgentId, agentManager: AgentManager): AgentId[] {
146
+ function getDescendantsRecursive(
147
+ agentId: AgentId,
148
+ agentManager: AgentManager,
149
+ ): AgentId[] {
114
150
  const descendants: AgentId[] = [];
115
151
  const children = agentManager.getChildren(agentId);
116
152
  for (const child of children) {
@@ -124,7 +160,9 @@ function getDescendantsRecursive(agentId: AgentId, agentManager: AgentManager):
124
160
  * Create MAPAdapterServices from CombinedServerServices.
125
161
  * Wires the internal services to the MAP adapter interface.
126
162
  */
127
- function createMAPServices(services: CombinedServerServices): MAPAdapterServices {
163
+ function createMAPServices(
164
+ services: CombinedServerServices,
165
+ ): MAPAdapterServices {
128
166
  // Create agent source for getAncestors (needs lineage lookup)
129
167
  // RelevanceAgentSource expects getAgent to return null (not undefined) when not found
130
168
  const agentSource: RelevanceAgentSource = {
@@ -137,10 +175,12 @@ function createMAPServices(services: CombinedServerServices): MAPAdapterServices
137
175
  agent
138
176
  ? {
139
177
  id: agent.id,
178
+ name: agent.name,
140
179
  role: agent.role,
141
180
  state: agent.state,
142
181
  parent: agent.parent ?? undefined,
143
182
  createdAt: agent.created_at,
183
+ metadata: agent.metadata,
144
184
  }
145
185
  : undefined;
146
186
 
@@ -152,7 +192,7 @@ function createMAPServices(services: CombinedServerServices): MAPAdapterServices
152
192
  from: AgentId,
153
193
  to: Address,
154
194
  content: string,
155
- options?: SendOptions
195
+ options?: SendOptions,
156
196
  ) => {
157
197
  const result = await services.messageRouter.sendToAddress({
158
198
  from,
@@ -178,7 +218,7 @@ function createMAPServices(services: CombinedServerServices): MAPAdapterServices
178
218
 
179
219
  export function createCombinedServer(
180
220
  services: CombinedServerServices,
181
- config: CombinedServerConfig = {}
221
+ config: CombinedServerConfig = {},
182
222
  ): CombinedServer {
183
223
  const {
184
224
  port = 3001,
@@ -187,8 +227,15 @@ export function createCombinedServer(
187
227
  cors = true,
188
228
  mapPath = "/map",
189
229
  disableMap = false,
230
+ serverToken: configToken,
231
+ noAuth = false,
190
232
  } = config;
191
233
 
234
+ // Resolve server token: disabled > config > env > none (no auth by default)
235
+ const resolvedServerToken = noAuth
236
+ ? undefined
237
+ : (configToken ?? process.env.MACRO_SERVER_SECRET ?? undefined);
238
+
192
239
  // Set up mail service and conversation map (always created, independent of MAP)
193
240
  const mailService = createMailService({ eventStore: services.eventStore });
194
241
  const conversationMap = createConversationMap();
@@ -211,7 +258,7 @@ export function createCombinedServer(
211
258
  // Create Express app with API routes (include mail services)
212
259
  const app = createAPIApp(
213
260
  { ...services, mailService, conversationMap },
214
- { cors }
261
+ { cors, serverToken: resolvedServerToken },
215
262
  );
216
263
 
217
264
  // Create HTTP server with Express
@@ -239,50 +286,144 @@ export function createCombinedServer(
239
286
  };
240
287
  mapAdapter = createMAPAdapter(
241
288
  { name: "macro-agent", version: "1.0.0" },
242
- mapServices
289
+ mapServices,
243
290
  );
244
291
 
245
292
  // Register workspace file extensions for TUI file attachment.
246
293
  // Uses defaultCwd (project root) as the workspace path for all agents,
247
294
  // since the head manager doesn't have an isolated worktree.
248
295
  registerWorkspaceFileExtensions(mapAdapter, {
249
- getWorkspace: () => ({ path: defaultCwd } as any),
296
+ getWorkspace: () => ({ path: defaultCwd }) as any,
250
297
  agentExists: () => true,
251
298
  });
252
299
 
300
+ // Register generic metadata update extension
301
+ registerUpdateMetadataExtension(mapAdapter, {
302
+ getAgent: (id) => services.agentManager.get(id),
303
+ updateAgentMetadata: (id, updates) =>
304
+ services.eventStore.updateAgentMetadata(id, updates),
305
+ });
306
+
307
+ // Register task extensions for direct MAP task management
308
+ if (services.taskBackend) {
309
+ registerTaskExtensions(mapAdapter, {
310
+ taskBackend: services.taskBackend,
311
+ sendMessage: async (from, to, content, options) => {
312
+ const result = await services.messageRouter.sendToAddress({
313
+ from: from as AgentId,
314
+ to,
315
+ content: typeof content === "string" ? content : JSON.stringify(content),
316
+ options: options ? { priority: options.priority as any } : undefined,
317
+ });
318
+ return { delivered: result.delivered };
319
+ },
320
+ });
321
+ }
322
+
323
+ // Register resume extension for restarting stopped agents
324
+ registerResumeExtension(mapAdapter, {
325
+ getAgent: (id) => {
326
+ const agent = services.agentManager.get(id);
327
+ if (!agent) return undefined;
328
+ return { id: agent.id, state: agent.state, session_id: agent.session_id };
329
+ },
330
+ resume: (id) => services.agentManager.resume(id),
331
+ });
332
+
333
+ // Register agent lifecycle extensions (spawn, fork, permission management)
334
+ registerAgentLifecycleExtensions(mapAdapter, {
335
+ getAgent: (id) => services.agentManager.get(id),
336
+ spawn: (opts) => services.agentManager.spawn(opts),
337
+ forkAgent: (id, opts) => services.agentManager.forkAgent(id, opts),
338
+ prompt: (id, msg) => services.agentManager.prompt(id, msg),
339
+ setPermissionMode: (id, mode) =>
340
+ services.agentManager.setPermissionMode(id, mode as any),
341
+ getPermissionMode: (id) => services.agentManager.getPermissionMode(id),
342
+ respondToPermission: (id, reqId, optId) =>
343
+ services.agentManager.respondToPermission(id, reqId, optId),
344
+ onAgentRegistered: (agent) => {
345
+ // Emit agent_registered event to all MAP subscribers
346
+ // mapAdapter is guaranteed non-null here (inside if (!disableMap) block)
347
+ mapAdapter!.emitEvent({
348
+ eventId: `agent-reg-${agent.id}-${Date.now()}`,
349
+ type: "agent_registered" as any,
350
+ timestamp: Date.now(),
351
+ data: agent,
352
+ });
353
+ },
354
+ listHeadManagers: () => services.agentManager.listHeadManagers(),
355
+ defaultCwd,
356
+ });
357
+
358
+ // Register MCP bridge extensions for thin-client MCP subprocesses
359
+ registerMCPBridgeExtensions(mapAdapter, {
360
+ eventStore: services.eventStore,
361
+ agentManager: services.agentManager,
362
+ taskManager: services.taskManager,
363
+ messageRouter: services.messageRouter,
364
+ peerManager: services.peerManager,
365
+ activityWatcher: services.activityWatcher,
366
+ taskBackend: services.taskBackend,
367
+ taskToolProvider: services.taskToolProvider,
368
+ taskToolContext: services.taskToolContext,
369
+ agentTokenManager: services.agentTokenManager,
370
+ });
371
+
253
372
  mapHandler = createMAPWebSocketHandler(mapAdapter);
254
373
  }
255
374
 
256
375
  // Handle upgrade requests - route by path
257
- httpServer.on("upgrade", (request: IncomingMessage, socket: Duplex, head: Buffer) => {
258
- const pathname = new URL(request.url ?? "/", `http://${request.headers.host}`).pathname;
259
-
260
- if (pathname === "/acp") {
261
- acpWss.handleUpgrade(request, socket, head, (ws) => {
262
- acpWss.emit("connection", ws, request);
263
- });
264
- } else if (pathname === "/api/ws") {
265
- apiWss.handleUpgrade(request, socket, head, (ws) => {
266
- apiWss.emit("connection", ws, request);
267
- });
268
- } else if (pathname === mapPath && mapHandler) {
269
- // MAP protocol connection
270
- acpWss.handleUpgrade(request, socket, head, (ws) => {
271
- mapHandler.handleConnection(ws, request);
272
- });
273
- } else {
274
- // Unknown WebSocket path
275
- socket.write("HTTP/1.1 404 Not Found\r\n\r\n");
276
- socket.destroy();
277
- }
278
- });
376
+ httpServer.on(
377
+ "upgrade",
378
+ (request: IncomingMessage, socket: Duplex, head: Buffer) => {
379
+ const parsedUrl = new URL(
380
+ request.url ?? "/",
381
+ `http://${request.headers.host}`,
382
+ );
383
+ const pathname = parsedUrl.pathname;
384
+
385
+ // Validate server token on WebSocket upgrade (skip when auth disabled)
386
+ if (resolvedServerToken) {
387
+ const urlToken = parsedUrl.searchParams.get("token");
388
+ if (!urlToken || !secureCompare(urlToken, resolvedServerToken)) {
389
+ socket.write("HTTP/1.1 401 Unauthorized\r\n\r\n");
390
+ socket.destroy();
391
+ return;
392
+ }
393
+ }
394
+
395
+ if (pathname === "/acp") {
396
+ acpWss.handleUpgrade(request, socket, head, (ws) => {
397
+ acpWss.emit("connection", ws, request);
398
+ });
399
+ } else if (pathname === "/api/ws") {
400
+ apiWss.handleUpgrade(request, socket, head, (ws) => {
401
+ apiWss.emit("connection", ws, request);
402
+ });
403
+ } else if (pathname === mapPath && mapHandler) {
404
+ // MAP protocol connection
405
+ acpWss.handleUpgrade(request, socket, head, (ws) => {
406
+ mapHandler.handleConnection(ws, request);
407
+ });
408
+ } else {
409
+ // Unknown WebSocket path
410
+ socket.write("HTTP/1.1 404 Not Found\r\n\r\n");
411
+ socket.destroy();
412
+ }
413
+ },
414
+ );
279
415
 
280
416
  // Add health endpoint
281
417
  app.get("/health", (_req, res) => {
418
+ const connectedProjects = services.getConnectedProjects?.() ?? [];
282
419
  res.json({
283
420
  status: "ok",
284
421
  acp_connections: acpHandler.getConnectionCount(),
285
422
  map_connections: mapHandler?.getConnectionCount() ?? 0,
423
+ opentasks: {
424
+ connected_projects: connectedProjects,
425
+ project_count: connectedProjects.length,
426
+ },
286
427
  timestamp: Date.now(),
287
428
  });
288
429
  });
@@ -301,13 +442,23 @@ export function createCombinedServer(
301
442
  httpServer.on("error", reject);
302
443
  httpServer.listen(port, host, () => {
303
444
  httpServer.removeListener("error", reject);
445
+ const tokenParam = resolvedServerToken ? `?token=${resolvedServerToken}` : "";
304
446
  console.error(`[combined] Server listening on http://${host}:${port}`);
305
- console.error(`[combined] ACP WebSocket: ws://${host}:${port}/acp`);
447
+ console.error(`[combined] ACP WebSocket: ws://${host}:${port}/acp${tokenParam}`);
306
448
  if (mapHandler) {
307
- console.error(`[combined] MAP WebSocket: ws://${host}:${port}${mapPath}`);
449
+ console.error(
450
+ `[combined] MAP WebSocket: ws://${host}:${port}${mapPath}${tokenParam}`,
451
+ );
308
452
  }
309
- console.error(`[combined] API WebSocket: ws://${host}:${port}/api/ws`);
453
+ console.error(
454
+ `[combined] API WebSocket: ws://${host}:${port}/api/ws${tokenParam}`,
455
+ );
310
456
  console.error(`[combined] REST API: http://${host}:${port}/api/*`);
457
+ if (resolvedServerToken) {
458
+ console.error(`[combined] Server token: ${resolvedServerToken.substring(0, 8)}...`);
459
+ } else {
460
+ console.error(`[combined] Auth: disabled`);
461
+ }
311
462
  resolve();
312
463
  });
313
464
  });
@@ -348,6 +499,10 @@ export function createCombinedServer(
348
499
  }
349
500
 
350
501
  function getUrl(): string {
502
+ const addr = httpServer.address();
503
+ if (addr && typeof addr === "object") {
504
+ return `http://${host}:${addr.port}`;
505
+ }
351
506
  return `http://${host}:${port}`;
352
507
  }
353
508
 
@@ -370,5 +525,6 @@ export function createCombinedServer(
370
525
  mapAdapter,
371
526
  mailService,
372
527
  conversationMap,
528
+ serverToken: resolvedServerToken,
373
529
  };
374
530
  }
@@ -671,7 +671,7 @@ describe("Steering Integration", () => {
671
671
  payload: { status_type: "started" },
672
672
  });
673
673
  eventStore.emit({
674
- type: "terminate",
674
+ type: "stop",
675
675
  source: { agent_id: "stopped-agent" },
676
676
  payload: { agent_id: "stopped-agent", reason: "completed" },
677
677
  });
@@ -170,7 +170,7 @@ describe('EventStore', () => {
170
170
  });
171
171
 
172
172
  store.emit({
173
- type: 'terminate',
173
+ type: 'stop',
174
174
  source: { agent_id: 'agent_1' },
175
175
  payload: { reason: 'completed' },
176
176
  });
@@ -1096,6 +1096,201 @@ describe('Event Archival', () => {
1096
1096
 
1097
1097
  await planStore.close();
1098
1098
  });
1099
+
1100
+ it('should update agent name via updateAgentMetadata', async () => {
1101
+ const metaStore = await createEventStore({ inMemory: true });
1102
+
1103
+ metaStore.emit({
1104
+ type: 'spawn',
1105
+ source: { agent_id: 'agent_meta' },
1106
+ payload: {
1107
+ agent_id: 'agent_meta',
1108
+ session_id: 'sess_meta',
1109
+ task: 'test metadata',
1110
+ },
1111
+ });
1112
+
1113
+ // Name starts undefined
1114
+ expect(metaStore.getAgent('agent_meta')?.name).toBeUndefined();
1115
+
1116
+ // Set name via updateAgentMetadata
1117
+ metaStore.updateAgentMetadata('agent_meta', { name: 'MyAgent' });
1118
+ expect(metaStore.getAgent('agent_meta')?.name).toBe('MyAgent');
1119
+
1120
+ await metaStore.close();
1121
+ });
1122
+
1123
+ it('should update agent metadata via updateAgentMetadata', async () => {
1124
+ const metaStore = await createEventStore({ inMemory: true });
1125
+
1126
+ metaStore.emit({
1127
+ type: 'spawn',
1128
+ source: { agent_id: 'agent_meta2' },
1129
+ payload: {
1130
+ agent_id: 'agent_meta2',
1131
+ session_id: 'sess_meta2',
1132
+ task: 'test metadata field',
1133
+ },
1134
+ });
1135
+
1136
+ // Metadata starts undefined
1137
+ expect(metaStore.getAgent('agent_meta2')?.metadata).toBeUndefined();
1138
+
1139
+ // Set metadata
1140
+ metaStore.updateAgentMetadata('agent_meta2', {
1141
+ metadata: { color: 'blue', priority: 1 },
1142
+ });
1143
+ const after = metaStore.getAgent('agent_meta2');
1144
+ expect(after?.metadata).toEqual({ color: 'blue', priority: 1 });
1145
+
1146
+ await metaStore.close();
1147
+ });
1148
+
1149
+ it('should shallow-merge metadata with existing values', async () => {
1150
+ const metaStore = await createEventStore({ inMemory: true });
1151
+
1152
+ metaStore.emit({
1153
+ type: 'spawn',
1154
+ source: { agent_id: 'agent_merge' },
1155
+ payload: {
1156
+ agent_id: 'agent_merge',
1157
+ session_id: 'sess_merge',
1158
+ task: 'test metadata merge',
1159
+ },
1160
+ });
1161
+
1162
+ // Set initial metadata
1163
+ metaStore.updateAgentMetadata('agent_merge', {
1164
+ metadata: { color: 'blue', size: 'large' },
1165
+ });
1166
+ expect(metaStore.getAgent('agent_merge')?.metadata).toEqual({
1167
+ color: 'blue',
1168
+ size: 'large',
1169
+ });
1170
+
1171
+ // Merge with new metadata — existing keys preserved, new keys added
1172
+ metaStore.updateAgentMetadata('agent_merge', {
1173
+ metadata: { color: 'red', shape: 'circle' },
1174
+ });
1175
+ expect(metaStore.getAgent('agent_merge')?.metadata).toEqual({
1176
+ color: 'red',
1177
+ size: 'large',
1178
+ shape: 'circle',
1179
+ });
1180
+
1181
+ await metaStore.close();
1182
+ });
1183
+
1184
+ it('should update multiple fields in a single updateAgentMetadata call', async () => {
1185
+ const metaStore = await createEventStore({ inMemory: true });
1186
+
1187
+ metaStore.emit({
1188
+ type: 'spawn',
1189
+ source: { agent_id: 'agent_multi' },
1190
+ payload: {
1191
+ agent_id: 'agent_multi',
1192
+ session_id: 'sess_multi',
1193
+ task: 'test multi-field update',
1194
+ },
1195
+ });
1196
+
1197
+ const plan = [
1198
+ { content: 'Step 1', priority: 'high', status: 'pending' },
1199
+ ];
1200
+
1201
+ metaStore.updateAgentMetadata('agent_multi', {
1202
+ name: 'MultiAgent',
1203
+ plan,
1204
+ metadata: { tag: 'test' },
1205
+ });
1206
+
1207
+ const agent = metaStore.getAgent('agent_multi');
1208
+ expect(agent?.name).toBe('MultiAgent');
1209
+ expect(agent?.plan).toEqual(plan);
1210
+ expect(agent?.metadata).toEqual({ tag: 'test' });
1211
+
1212
+ await metaStore.close();
1213
+ });
1214
+
1215
+ it('should notify agent change listeners on metadata update', async () => {
1216
+ const metaStore = await createEventStore({ inMemory: true });
1217
+
1218
+ metaStore.emit({
1219
+ type: 'spawn',
1220
+ source: { agent_id: 'agent_notify' },
1221
+ payload: {
1222
+ agent_id: 'agent_notify',
1223
+ session_id: 'sess_notify',
1224
+ task: 'test notify',
1225
+ },
1226
+ });
1227
+
1228
+ const changes: string[] = [];
1229
+ metaStore.onAgentChange((agentId) => {
1230
+ changes.push(agentId);
1231
+ });
1232
+
1233
+ metaStore.updateAgentMetadata('agent_notify', { name: 'Notified' });
1234
+ expect(changes).toContain('agent_notify');
1235
+
1236
+ await metaStore.close();
1237
+ });
1238
+
1239
+ it('should preserve all out-of-band fields across reload', async () => {
1240
+ const metaStore = await createEventStore({
1241
+ baseDir: testDir,
1242
+ instanceId: 'metadata-reload-test',
1243
+ });
1244
+
1245
+ metaStore.emit({
1246
+ type: 'spawn',
1247
+ source: { agent_id: 'agent_reload' },
1248
+ payload: {
1249
+ agent_id: 'agent_reload',
1250
+ session_id: 'sess_reload',
1251
+ task: 'test reload persistence',
1252
+ },
1253
+ });
1254
+
1255
+ // Set all out-of-band fields
1256
+ const plan = [
1257
+ { content: 'Research', priority: 'high', status: 'completed' },
1258
+ ];
1259
+ metaStore.updateAgentMetadata('agent_reload', {
1260
+ name: 'ReloadAgent',
1261
+ plan,
1262
+ metadata: { version: 2, env: 'test' },
1263
+ });
1264
+
1265
+ // Verify before reload
1266
+ const before = metaStore.getAgent('agent_reload');
1267
+ expect(before?.name).toBe('ReloadAgent');
1268
+ expect(before?.plan).toEqual(plan);
1269
+ expect(before?.metadata).toEqual({ version: 2, env: 'test' });
1270
+
1271
+ // Persist and reload (rebuildViews)
1272
+ await metaStore.persist();
1273
+ await metaStore.reload();
1274
+
1275
+ // All fields should survive
1276
+ const after = metaStore.getAgent('agent_reload');
1277
+ expect(after).toBeDefined();
1278
+ expect(after?.name).toBe('ReloadAgent');
1279
+ expect(after?.plan).toEqual(plan);
1280
+ expect(after?.metadata).toEqual({ version: 2, env: 'test' });
1281
+
1282
+ await metaStore.close();
1283
+ });
1284
+
1285
+ it('should no-op when agent does not exist', async () => {
1286
+ const metaStore = await createEventStore({ inMemory: true });
1287
+
1288
+ // Should not throw
1289
+ metaStore.updateAgentMetadata('nonexistent', { name: 'Ghost' });
1290
+ expect(metaStore.getAgent('nonexistent')).toBeNull();
1291
+
1292
+ await metaStore.close();
1293
+ });
1099
1294
  });
1100
1295
  });
1101
1296
 
@@ -467,10 +467,10 @@ describe('Peer Visibility', () => {
467
467
  it('should filter by whitelist when visibleEventTypes is specified', () => {
468
468
  const visibility: PeerVisibilityConfig = {
469
469
  exportEvents: true,
470
- visibleEventTypes: ['spawn', 'terminate'],
470
+ visibleEventTypes: ['spawn', 'stop'],
471
471
  };
472
472
  expect(isEventTypeVisibleToPeers('spawn', visibility)).toBe(true);
473
- expect(isEventTypeVisibleToPeers('terminate', visibility)).toBe(true);
473
+ expect(isEventTypeVisibleToPeers('stop', visibility)).toBe(true);
474
474
  expect(isEventTypeVisibleToPeers('message', visibility)).toBe(false);
475
475
  });
476
476
  });
@@ -503,7 +503,7 @@ describe('Peer Visibility', () => {
503
503
  { type: 'spawn', source: { agent_id: 'agent-1' }, payload: {} },
504
504
  { type: 'message', source: { agent_id: 'agent-1' }, payload: {} },
505
505
  { type: 'spawn', source: { agent_id: 'agent-2' }, payload: {} },
506
- { type: 'terminate', source: { agent_id: 'agent-2' }, payload: {} },
506
+ { type: 'stop', source: { agent_id: 'agent-2' }, payload: {} },
507
507
  ];
508
508
 
509
509
  it('should return empty array when export is disabled', () => {