macro-agent 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (660) hide show
  1. package/.claude/settings.local.json +3 -1
  2. package/.sudocode/issues.jsonl +28 -0
  3. package/.sudocode/specs.jsonl +8 -0
  4. package/CLAUDE.md +25 -17
  5. package/README.md +11 -29
  6. package/dist/acp/macro-agent.d.ts +15 -0
  7. package/dist/acp/macro-agent.d.ts.map +1 -1
  8. package/dist/acp/macro-agent.js +131 -35
  9. package/dist/acp/macro-agent.js.map +1 -1
  10. package/dist/acp/types.d.ts +32 -1
  11. package/dist/acp/types.d.ts.map +1 -1
  12. package/dist/acp/types.js.map +1 -1
  13. package/dist/agent/agent-manager.d.ts +65 -1
  14. package/dist/agent/agent-manager.d.ts.map +1 -1
  15. package/dist/agent/agent-manager.js +544 -200
  16. package/dist/agent/agent-manager.js.map +1 -1
  17. package/dist/agent/types.d.ts +8 -1
  18. package/dist/agent/types.d.ts.map +1 -1
  19. package/dist/agent/types.js.map +1 -1
  20. package/dist/api/server.d.ts +8 -1
  21. package/dist/api/server.d.ts.map +1 -1
  22. package/dist/api/server.js +136 -8
  23. package/dist/api/server.js.map +1 -1
  24. package/dist/api/types.d.ts +1 -1
  25. package/dist/api/types.d.ts.map +1 -1
  26. package/dist/auth/index.d.ts +2 -0
  27. package/dist/auth/index.d.ts.map +1 -0
  28. package/dist/auth/index.js +2 -0
  29. package/dist/auth/index.js.map +1 -0
  30. package/dist/auth/token.d.ts +41 -0
  31. package/dist/auth/token.d.ts.map +1 -0
  32. package/dist/auth/token.js +73 -0
  33. package/dist/auth/token.js.map +1 -0
  34. package/dist/cli/acp.d.ts +2 -23
  35. package/dist/cli/acp.d.ts.map +1 -1
  36. package/dist/cli/acp.js +197 -61
  37. package/dist/cli/acp.js.map +1 -1
  38. package/dist/cli/index.js +152 -16
  39. package/dist/cli/index.js.map +1 -1
  40. package/dist/cli/mcp.d.ts +6 -0
  41. package/dist/cli/mcp.d.ts.map +1 -1
  42. package/dist/cli/mcp.js +279 -173
  43. package/dist/cli/mcp.js.map +1 -1
  44. package/dist/cli/parse-args.d.ts +20 -0
  45. package/dist/cli/parse-args.d.ts.map +1 -0
  46. package/dist/cli/parse-args.js +43 -0
  47. package/dist/cli/parse-args.js.map +1 -0
  48. package/dist/cli/stable-instance-id.d.ts +8 -0
  49. package/dist/cli/stable-instance-id.d.ts.map +1 -0
  50. package/dist/cli/stable-instance-id.js +14 -0
  51. package/dist/cli/stable-instance-id.js.map +1 -0
  52. package/dist/config/project-config.d.ts +85 -7
  53. package/dist/config/project-config.d.ts.map +1 -1
  54. package/dist/config/project-config.js +133 -20
  55. package/dist/config/project-config.js.map +1 -1
  56. package/dist/index.d.ts +1 -0
  57. package/dist/index.d.ts.map +1 -1
  58. package/dist/index.js +2 -0
  59. package/dist/index.js.map +1 -1
  60. package/dist/lifecycle/handlers/index.d.ts +7 -3
  61. package/dist/lifecycle/handlers/index.d.ts.map +1 -1
  62. package/dist/lifecycle/handlers/index.js +25 -8
  63. package/dist/lifecycle/handlers/index.js.map +1 -1
  64. package/dist/lifecycle/types.d.ts +2 -0
  65. package/dist/lifecycle/types.d.ts.map +1 -1
  66. package/dist/lifecycle/types.js.map +1 -1
  67. package/dist/map/adapter/acp-over-map.d.ts +17 -0
  68. package/dist/map/adapter/acp-over-map.d.ts.map +1 -1
  69. package/dist/map/adapter/acp-over-map.js +384 -23
  70. package/dist/map/adapter/acp-over-map.js.map +1 -1
  71. package/dist/map/adapter/connection-manager.d.ts.map +1 -1
  72. package/dist/map/adapter/connection-manager.js +3 -0
  73. package/dist/map/adapter/connection-manager.js.map +1 -1
  74. package/dist/map/adapter/event-log.d.ts +87 -0
  75. package/dist/map/adapter/event-log.d.ts.map +1 -0
  76. package/dist/map/adapter/event-log.js +122 -0
  77. package/dist/map/adapter/event-log.js.map +1 -0
  78. package/dist/map/adapter/event-translator.js +6 -6
  79. package/dist/map/adapter/event-translator.js.map +1 -1
  80. package/dist/map/adapter/extensions/agent-lifecycle.d.ts +82 -0
  81. package/dist/map/adapter/extensions/agent-lifecycle.d.ts.map +1 -0
  82. package/dist/map/adapter/extensions/agent-lifecycle.js +164 -0
  83. package/dist/map/adapter/extensions/agent-lifecycle.js.map +1 -0
  84. package/dist/map/adapter/extensions/index.d.ts +13 -1
  85. package/dist/map/adapter/extensions/index.d.ts.map +1 -1
  86. package/dist/map/adapter/extensions/index.js +61 -0
  87. package/dist/map/adapter/extensions/index.js.map +1 -1
  88. package/dist/map/adapter/extensions/mcp-bridge.d.ts +57 -0
  89. package/dist/map/adapter/extensions/mcp-bridge.d.ts.map +1 -0
  90. package/dist/map/adapter/extensions/mcp-bridge.js +745 -0
  91. package/dist/map/adapter/extensions/mcp-bridge.js.map +1 -0
  92. package/dist/map/adapter/extensions/rename.d.ts +29 -0
  93. package/dist/map/adapter/extensions/rename.d.ts.map +1 -0
  94. package/dist/map/adapter/extensions/rename.js +49 -0
  95. package/dist/map/adapter/extensions/rename.js.map +1 -0
  96. package/dist/map/adapter/extensions/streams.d.ts +95 -0
  97. package/dist/map/adapter/extensions/streams.d.ts.map +1 -0
  98. package/dist/map/adapter/extensions/streams.js +515 -0
  99. package/dist/map/adapter/extensions/streams.js.map +1 -0
  100. package/dist/map/adapter/extensions/task.d.ts.map +1 -1
  101. package/dist/map/adapter/extensions/task.js +10 -0
  102. package/dist/map/adapter/extensions/task.js.map +1 -1
  103. package/dist/map/adapter/extensions/update-metadata.d.ts +29 -0
  104. package/dist/map/adapter/extensions/update-metadata.d.ts.map +1 -0
  105. package/dist/map/adapter/extensions/update-metadata.js +67 -0
  106. package/dist/map/adapter/extensions/update-metadata.js.map +1 -0
  107. package/dist/map/adapter/index.d.ts +2 -1
  108. package/dist/map/adapter/index.d.ts.map +1 -1
  109. package/dist/map/adapter/index.js +10 -2
  110. package/dist/map/adapter/index.js.map +1 -1
  111. package/dist/map/adapter/interface.d.ts +2 -0
  112. package/dist/map/adapter/interface.d.ts.map +1 -1
  113. package/dist/map/adapter/map-adapter.d.ts +3 -0
  114. package/dist/map/adapter/map-adapter.d.ts.map +1 -1
  115. package/dist/map/adapter/map-adapter.js +258 -35
  116. package/dist/map/adapter/map-adapter.js.map +1 -1
  117. package/dist/map/adapter/subscription-manager.d.ts.map +1 -1
  118. package/dist/map/adapter/subscription-manager.js +5 -1
  119. package/dist/map/adapter/subscription-manager.js.map +1 -1
  120. package/dist/map/adapter/types.d.ts +3 -1
  121. package/dist/map/adapter/types.d.ts.map +1 -1
  122. package/dist/mcp/map-client.d.ts +39 -0
  123. package/dist/mcp/map-client.d.ts.map +1 -0
  124. package/dist/mcp/map-client.js +129 -0
  125. package/dist/mcp/map-client.js.map +1 -0
  126. package/dist/mcp/mcp-server.d.ts +16 -0
  127. package/dist/mcp/mcp-server.d.ts.map +1 -1
  128. package/dist/mcp/mcp-server.js +125 -88
  129. package/dist/mcp/mcp-server.js.map +1 -1
  130. package/dist/mcp/tools/done.d.ts.map +1 -1
  131. package/dist/mcp/tools/done.js +18 -0
  132. package/dist/mcp/tools/done.js.map +1 -1
  133. package/dist/mcp/types.d.ts +9 -1
  134. package/dist/mcp/types.d.ts.map +1 -1
  135. package/dist/mcp/types.js.map +1 -1
  136. package/dist/metrics/metrics.js +1 -1
  137. package/dist/metrics/metrics.js.map +1 -1
  138. package/dist/roles/builtin/coordinator.d.ts.map +1 -1
  139. package/dist/roles/builtin/coordinator.js +2 -1
  140. package/dist/roles/builtin/coordinator.js.map +1 -1
  141. package/dist/roles/builtin/integrator.d.ts.map +1 -1
  142. package/dist/roles/builtin/integrator.js +2 -1
  143. package/dist/roles/builtin/integrator.js.map +1 -1
  144. package/dist/roles/builtin/worker.d.ts.map +1 -1
  145. package/dist/roles/builtin/worker.js +3 -1
  146. package/dist/roles/builtin/worker.js.map +1 -1
  147. package/dist/roles/capabilities.d.ts +9 -1
  148. package/dist/roles/capabilities.d.ts.map +1 -1
  149. package/dist/roles/capabilities.js +27 -7
  150. package/dist/roles/capabilities.js.map +1 -1
  151. package/dist/roles/config-loader.d.ts +6 -6
  152. package/dist/roles/config-loader.d.ts.map +1 -1
  153. package/dist/roles/config-loader.js +8 -7
  154. package/dist/roles/config-loader.js.map +1 -1
  155. package/dist/roles/registry.d.ts +2 -2
  156. package/dist/roles/registry.js +2 -2
  157. package/dist/roles/types.d.ts +3 -1
  158. package/dist/roles/types.d.ts.map +1 -1
  159. package/dist/server/combined-server.d.ts +28 -1
  160. package/dist/server/combined-server.d.ts.map +1 -1
  161. package/dist/server/combined-server.js +111 -8
  162. package/dist/server/combined-server.js.map +1 -1
  163. package/dist/store/event-store.d.ts +2 -1
  164. package/dist/store/event-store.d.ts.map +1 -1
  165. package/dist/store/event-store.js +80 -24
  166. package/dist/store/event-store.js.map +1 -1
  167. package/dist/store/instance.d.ts +1 -1
  168. package/dist/store/instance.d.ts.map +1 -1
  169. package/dist/store/instance.js +2 -2
  170. package/dist/store/instance.js.map +1 -1
  171. package/dist/store/types/agents.d.ts +23 -0
  172. package/dist/store/types/agents.d.ts.map +1 -1
  173. package/dist/store/types/events.d.ts +1 -1
  174. package/dist/store/types/events.d.ts.map +1 -1
  175. package/dist/task/backend/index.d.ts +47 -29
  176. package/dist/task/backend/index.d.ts.map +1 -1
  177. package/dist/task/backend/index.js +109 -71
  178. package/dist/task/backend/index.js.map +1 -1
  179. package/dist/task/backend/memory.d.ts +1 -0
  180. package/dist/task/backend/memory.d.ts.map +1 -1
  181. package/dist/task/backend/memory.js +3 -0
  182. package/dist/task/backend/memory.js.map +1 -1
  183. package/dist/task/backend/opentasks/backend.d.ts +140 -0
  184. package/dist/task/backend/opentasks/backend.d.ts.map +1 -0
  185. package/dist/task/backend/opentasks/backend.js +1023 -0
  186. package/dist/task/backend/opentasks/backend.js.map +1 -0
  187. package/dist/task/backend/opentasks/client.d.ts +337 -0
  188. package/dist/task/backend/opentasks/client.d.ts.map +1 -0
  189. package/dist/task/backend/opentasks/client.js +225 -0
  190. package/dist/task/backend/opentasks/client.js.map +1 -0
  191. package/dist/task/backend/opentasks/daemon-manager.d.ts +89 -0
  192. package/dist/task/backend/opentasks/daemon-manager.d.ts.map +1 -0
  193. package/dist/task/backend/opentasks/daemon-manager.js +195 -0
  194. package/dist/task/backend/opentasks/daemon-manager.js.map +1 -0
  195. package/dist/task/backend/opentasks/index.d.ts +21 -0
  196. package/dist/task/backend/opentasks/index.d.ts.map +1 -0
  197. package/dist/task/backend/opentasks/index.js +21 -0
  198. package/dist/task/backend/opentasks/index.js.map +1 -0
  199. package/dist/task/backend/opentasks/mapping.d.ts +48 -0
  200. package/dist/task/backend/opentasks/mapping.d.ts.map +1 -0
  201. package/dist/task/backend/opentasks/mapping.js +77 -0
  202. package/dist/task/backend/opentasks/mapping.js.map +1 -0
  203. package/dist/task/backend/types.d.ts +33 -53
  204. package/dist/task/backend/types.d.ts.map +1 -1
  205. package/dist/task/backend/types.js +7 -11
  206. package/dist/task/backend/types.js.map +1 -1
  207. package/dist/task/backend/unified-tool-provider.d.ts +57 -0
  208. package/dist/task/backend/unified-tool-provider.d.ts.map +1 -0
  209. package/dist/task/backend/unified-tool-provider.js +623 -0
  210. package/dist/task/backend/unified-tool-provider.js.map +1 -0
  211. package/dist/teams/index.d.ts +3 -1
  212. package/dist/teams/index.d.ts.map +1 -1
  213. package/dist/teams/index.js +2 -0
  214. package/dist/teams/index.js.map +1 -1
  215. package/dist/teams/seed-defaults.d.ts +20 -0
  216. package/dist/teams/seed-defaults.d.ts.map +1 -0
  217. package/dist/teams/seed-defaults.js +71 -0
  218. package/dist/teams/seed-defaults.js.map +1 -0
  219. package/dist/teams/team-loader.d.ts +7 -3
  220. package/dist/teams/team-loader.d.ts.map +1 -1
  221. package/dist/teams/team-loader.js +156 -164
  222. package/dist/teams/team-loader.js.map +1 -1
  223. package/dist/teams/team-manager.d.ts +112 -0
  224. package/dist/teams/team-manager.d.ts.map +1 -0
  225. package/dist/teams/team-manager.js +305 -0
  226. package/dist/teams/team-manager.js.map +1 -0
  227. package/dist/teams/team-runtime.d.ts +125 -19
  228. package/dist/teams/team-runtime.d.ts.map +1 -1
  229. package/dist/teams/team-runtime.js +529 -119
  230. package/dist/teams/team-runtime.js.map +1 -1
  231. package/dist/teams/types.d.ts +41 -151
  232. package/dist/teams/types.d.ts.map +1 -1
  233. package/dist/teams/types.js +2 -3
  234. package/dist/teams/types.js.map +1 -1
  235. package/docs/architecture.md +7 -6
  236. package/docs/configuration.md +26 -62
  237. package/docs/implementation-details.md +5 -5
  238. package/docs/implementation-summary.md +17 -17
  239. package/docs/plan-self-driving-support.md +4 -4
  240. package/docs/spec-self-driving-support.md +10 -10
  241. package/docs/team-templates.md +2 -2
  242. package/docs/teams.md +76 -3
  243. package/docs/troubleshooting.md +10 -11
  244. package/package.json +7 -4
  245. package/references/minimem/.claude/settings.json +7 -0
  246. package/references/minimem/.sudocode/issues.jsonl +18 -0
  247. package/references/minimem/.sudocode/specs.jsonl +1 -0
  248. package/references/minimem/CLAUDE.md +310 -0
  249. package/references/minimem/README.md +562 -0
  250. package/references/minimem/claude-plugin/.claude-plugin/plugin.json +10 -0
  251. package/references/minimem/claude-plugin/.mcp.json +7 -0
  252. package/references/minimem/claude-plugin/README.md +158 -0
  253. package/references/minimem/claude-plugin/commands/recall.md +47 -0
  254. package/references/minimem/claude-plugin/commands/remember.md +41 -0
  255. package/references/minimem/claude-plugin/hooks/__tests__/hooks.test.ts +272 -0
  256. package/references/minimem/claude-plugin/hooks/hooks.json +27 -0
  257. package/references/minimem/claude-plugin/hooks/session-end.sh +86 -0
  258. package/references/minimem/claude-plugin/hooks/session-start.sh +85 -0
  259. package/references/minimem/claude-plugin/skills/memory/SKILL.md +108 -0
  260. package/references/minimem/media/banner.png +0 -0
  261. package/references/minimem/package-lock.json +5373 -0
  262. package/references/minimem/package.json +72 -0
  263. package/references/minimem/scripts/postbuild.js +35 -0
  264. package/references/minimem/src/__tests__/edge-cases.test.ts +371 -0
  265. package/references/minimem/src/__tests__/errors.test.ts +265 -0
  266. package/references/minimem/src/__tests__/helpers.ts +199 -0
  267. package/references/minimem/src/__tests__/internal.test.ts +407 -0
  268. package/references/minimem/src/__tests__/knowledge.test.ts +287 -0
  269. package/references/minimem/src/__tests__/minimem.integration.test.ts +1127 -0
  270. package/references/minimem/src/__tests__/session.test.ts +190 -0
  271. package/references/minimem/src/cli/__tests__/commands.test.ts +759 -0
  272. package/references/minimem/src/cli/commands/__tests__/conflicts.test.ts +141 -0
  273. package/references/minimem/src/cli/commands/append.ts +76 -0
  274. package/references/minimem/src/cli/commands/config.ts +262 -0
  275. package/references/minimem/src/cli/commands/conflicts.ts +413 -0
  276. package/references/minimem/src/cli/commands/daemon.ts +169 -0
  277. package/references/minimem/src/cli/commands/index.ts +12 -0
  278. package/references/minimem/src/cli/commands/init.ts +88 -0
  279. package/references/minimem/src/cli/commands/mcp.ts +177 -0
  280. package/references/minimem/src/cli/commands/push-pull.ts +213 -0
  281. package/references/minimem/src/cli/commands/search.ts +158 -0
  282. package/references/minimem/src/cli/commands/status.ts +84 -0
  283. package/references/minimem/src/cli/commands/sync-init.ts +290 -0
  284. package/references/minimem/src/cli/commands/sync.ts +70 -0
  285. package/references/minimem/src/cli/commands/upsert.ts +197 -0
  286. package/references/minimem/src/cli/config.ts +584 -0
  287. package/references/minimem/src/cli/index.ts +264 -0
  288. package/references/minimem/src/cli/shared.ts +161 -0
  289. package/references/minimem/src/cli/sync/__tests__/central.test.ts +152 -0
  290. package/references/minimem/src/cli/sync/__tests__/conflicts.test.ts +209 -0
  291. package/references/minimem/src/cli/sync/__tests__/daemon.test.ts +118 -0
  292. package/references/minimem/src/cli/sync/__tests__/detection.test.ts +207 -0
  293. package/references/minimem/src/cli/sync/__tests__/integration.test.ts +476 -0
  294. package/references/minimem/src/cli/sync/__tests__/registry.test.ts +363 -0
  295. package/references/minimem/src/cli/sync/__tests__/state.test.ts +255 -0
  296. package/references/minimem/src/cli/sync/__tests__/validation.test.ts +193 -0
  297. package/references/minimem/src/cli/sync/__tests__/watcher.test.ts +178 -0
  298. package/references/minimem/src/cli/sync/central.ts +292 -0
  299. package/references/minimem/src/cli/sync/conflicts.ts +204 -0
  300. package/references/minimem/src/cli/sync/daemon.ts +407 -0
  301. package/references/minimem/src/cli/sync/detection.ts +138 -0
  302. package/references/minimem/src/cli/sync/index.ts +107 -0
  303. package/references/minimem/src/cli/sync/operations.ts +373 -0
  304. package/references/minimem/src/cli/sync/registry.ts +279 -0
  305. package/references/minimem/src/cli/sync/state.ts +355 -0
  306. package/references/minimem/src/cli/sync/validation.ts +206 -0
  307. package/references/minimem/src/cli/sync/watcher.ts +234 -0
  308. package/references/minimem/src/cli/version.ts +34 -0
  309. package/references/minimem/src/core/index.ts +9 -0
  310. package/references/minimem/src/core/indexer.ts +628 -0
  311. package/references/minimem/src/core/searcher.ts +221 -0
  312. package/references/minimem/src/db/schema.ts +183 -0
  313. package/references/minimem/src/db/sqlite-vec.ts +24 -0
  314. package/references/minimem/src/embeddings/__tests__/embeddings.test.ts +431 -0
  315. package/references/minimem/src/embeddings/batch-gemini.ts +392 -0
  316. package/references/minimem/src/embeddings/batch-openai.ts +409 -0
  317. package/references/minimem/src/embeddings/embeddings.ts +434 -0
  318. package/references/minimem/src/index.ts +109 -0
  319. package/references/minimem/src/internal.ts +299 -0
  320. package/references/minimem/src/minimem.ts +1276 -0
  321. package/references/minimem/src/search/__tests__/hybrid.test.ts +247 -0
  322. package/references/minimem/src/search/graph.ts +234 -0
  323. package/references/minimem/src/search/hybrid.ts +151 -0
  324. package/references/minimem/src/search/search.ts +256 -0
  325. package/references/minimem/src/server/__tests__/mcp.test.ts +341 -0
  326. package/references/minimem/src/server/__tests__/tools.test.ts +364 -0
  327. package/references/minimem/src/server/mcp.ts +326 -0
  328. package/references/minimem/src/server/tools.ts +720 -0
  329. package/references/minimem/src/session.ts +460 -0
  330. package/references/minimem/tsconfig.json +19 -0
  331. package/references/minimem/tsup.config.ts +26 -0
  332. package/references/minimem/vitest.config.ts +24 -0
  333. package/references/openteams/.claude/settings.json +6 -0
  334. package/references/openteams/README.md +1 -0
  335. package/references/openteams/SKILL.md +341 -0
  336. package/references/openteams/design.md +411 -0
  337. package/references/openteams/examples/bmad-method/prompts/analyst/ROLE.md +16 -0
  338. package/references/openteams/examples/bmad-method/prompts/analyst/SOUL.md +5 -0
  339. package/references/openteams/examples/bmad-method/prompts/architect/ROLE.md +24 -0
  340. package/references/openteams/examples/bmad-method/prompts/architect/SOUL.md +5 -0
  341. package/references/openteams/examples/bmad-method/prompts/developer/ROLE.md +25 -0
  342. package/references/openteams/examples/bmad-method/prompts/developer/SOUL.md +5 -0
  343. package/references/openteams/examples/bmad-method/prompts/master/ROLE.md +21 -0
  344. package/references/openteams/examples/bmad-method/prompts/master/SOUL.md +5 -0
  345. package/references/openteams/examples/bmad-method/prompts/pm/ROLE.md +20 -0
  346. package/references/openteams/examples/bmad-method/prompts/pm/SOUL.md +5 -0
  347. package/references/openteams/examples/bmad-method/prompts/qa/ROLE.md +17 -0
  348. package/references/openteams/examples/bmad-method/prompts/qa/SOUL.md +5 -0
  349. package/references/openteams/examples/bmad-method/prompts/quick-flow-dev/ROLE.md +23 -0
  350. package/references/openteams/examples/bmad-method/prompts/quick-flow-dev/SOUL.md +5 -0
  351. package/references/openteams/examples/bmad-method/prompts/scrum-master/ROLE.md +27 -0
  352. package/references/openteams/examples/bmad-method/prompts/scrum-master/SOUL.md +5 -0
  353. package/references/openteams/examples/bmad-method/prompts/tech-writer/ROLE.md +21 -0
  354. package/references/openteams/examples/bmad-method/prompts/tech-writer/SOUL.md +5 -0
  355. package/references/openteams/examples/bmad-method/prompts/ux-designer/ROLE.md +16 -0
  356. package/references/openteams/examples/bmad-method/prompts/ux-designer/SOUL.md +5 -0
  357. package/references/openteams/examples/bmad-method/roles/analyst.yaml +9 -0
  358. package/references/openteams/examples/bmad-method/roles/architect.yaml +9 -0
  359. package/references/openteams/examples/bmad-method/roles/developer.yaml +8 -0
  360. package/references/openteams/examples/bmad-method/roles/master.yaml +8 -0
  361. package/references/openteams/examples/bmad-method/roles/pm.yaml +9 -0
  362. package/references/openteams/examples/bmad-method/roles/qa.yaml +8 -0
  363. package/references/openteams/examples/bmad-method/roles/quick-flow-dev.yaml +8 -0
  364. package/references/openteams/examples/bmad-method/roles/scrum-master.yaml +9 -0
  365. package/references/openteams/examples/bmad-method/roles/tech-writer.yaml +8 -0
  366. package/references/openteams/examples/bmad-method/roles/ux-designer.yaml +8 -0
  367. package/references/openteams/examples/bmad-method/team.yaml +161 -0
  368. package/references/openteams/examples/get-shit-done/prompts/codebase-mapper/ROLE.md +17 -0
  369. package/references/openteams/examples/get-shit-done/prompts/codebase-mapper/SOUL.md +5 -0
  370. package/references/openteams/examples/get-shit-done/prompts/debugger/ROLE.md +25 -0
  371. package/references/openteams/examples/get-shit-done/prompts/debugger/SOUL.md +5 -0
  372. package/references/openteams/examples/get-shit-done/prompts/executor/ROLE.md +34 -0
  373. package/references/openteams/examples/get-shit-done/prompts/executor/SOUL.md +5 -0
  374. package/references/openteams/examples/get-shit-done/prompts/integration-checker/ROLE.md +18 -0
  375. package/references/openteams/examples/get-shit-done/prompts/integration-checker/SOUL.md +3 -0
  376. package/references/openteams/examples/get-shit-done/prompts/orchestrator/ROLE.md +42 -0
  377. package/references/openteams/examples/get-shit-done/prompts/orchestrator/SOUL.md +5 -0
  378. package/references/openteams/examples/get-shit-done/prompts/phase-researcher/ROLE.md +15 -0
  379. package/references/openteams/examples/get-shit-done/prompts/phase-researcher/SOUL.md +3 -0
  380. package/references/openteams/examples/get-shit-done/prompts/plan-checker/ROLE.md +17 -0
  381. package/references/openteams/examples/get-shit-done/prompts/plan-checker/SOUL.md +3 -0
  382. package/references/openteams/examples/get-shit-done/prompts/planner/ROLE.md +28 -0
  383. package/references/openteams/examples/get-shit-done/prompts/planner/SOUL.md +5 -0
  384. package/references/openteams/examples/get-shit-done/prompts/project-researcher/ROLE.md +16 -0
  385. package/references/openteams/examples/get-shit-done/prompts/project-researcher/SOUL.md +3 -0
  386. package/references/openteams/examples/get-shit-done/prompts/research-synthesizer/ROLE.md +13 -0
  387. package/references/openteams/examples/get-shit-done/prompts/research-synthesizer/SOUL.md +3 -0
  388. package/references/openteams/examples/get-shit-done/prompts/roadmapper/ROLE.md +14 -0
  389. package/references/openteams/examples/get-shit-done/prompts/roadmapper/SOUL.md +3 -0
  390. package/references/openteams/examples/get-shit-done/prompts/verifier/ROLE.md +19 -0
  391. package/references/openteams/examples/get-shit-done/prompts/verifier/SOUL.md +5 -0
  392. package/references/openteams/examples/get-shit-done/roles/codebase-mapper.yaml +8 -0
  393. package/references/openteams/examples/get-shit-done/roles/debugger.yaml +8 -0
  394. package/references/openteams/examples/get-shit-done/roles/executor.yaml +8 -0
  395. package/references/openteams/examples/get-shit-done/roles/integration-checker.yaml +8 -0
  396. package/references/openteams/examples/get-shit-done/roles/orchestrator.yaml +9 -0
  397. package/references/openteams/examples/get-shit-done/roles/phase-researcher.yaml +7 -0
  398. package/references/openteams/examples/get-shit-done/roles/plan-checker.yaml +8 -0
  399. package/references/openteams/examples/get-shit-done/roles/planner.yaml +8 -0
  400. package/references/openteams/examples/get-shit-done/roles/project-researcher.yaml +8 -0
  401. package/references/openteams/examples/get-shit-done/roles/research-synthesizer.yaml +7 -0
  402. package/references/openteams/examples/get-shit-done/roles/roadmapper.yaml +7 -0
  403. package/references/openteams/examples/get-shit-done/roles/verifier.yaml +8 -0
  404. package/references/openteams/examples/get-shit-done/team.yaml +154 -0
  405. package/references/openteams/package-lock.json +2181 -0
  406. package/references/openteams/package.json +48 -0
  407. package/references/openteams/schema/role.schema.json +125 -0
  408. package/references/openteams/schema/team.schema.json +284 -0
  409. package/references/openteams/src/cli/agent.ts +104 -0
  410. package/references/openteams/src/cli/cli.test.ts +381 -0
  411. package/references/openteams/src/cli/generate.ts +220 -0
  412. package/references/openteams/src/cli/message.ts +241 -0
  413. package/references/openteams/src/cli/task.ts +154 -0
  414. package/references/openteams/src/cli/team.ts +104 -0
  415. package/references/openteams/src/cli/template.ts +207 -0
  416. package/references/openteams/src/cli.ts +45 -0
  417. package/references/openteams/src/db/database.test.ts +185 -0
  418. package/references/openteams/src/db/database.ts +240 -0
  419. package/references/openteams/src/generators/agent-prompt-generator.test.ts +332 -0
  420. package/references/openteams/src/generators/agent-prompt-generator.ts +521 -0
  421. package/references/openteams/src/generators/package-generator.test.ts +129 -0
  422. package/references/openteams/src/generators/package-generator.ts +102 -0
  423. package/references/openteams/src/generators/skill-generator.test.ts +246 -0
  424. package/references/openteams/src/generators/skill-generator.ts +374 -0
  425. package/references/openteams/src/index.ts +104 -0
  426. package/references/openteams/src/services/agent-service.test.ts +158 -0
  427. package/references/openteams/src/services/agent-service.ts +84 -0
  428. package/references/openteams/src/services/communication-service.test.ts +455 -0
  429. package/references/openteams/src/services/communication-service.ts +371 -0
  430. package/references/openteams/src/services/message-service.test.ts +342 -0
  431. package/references/openteams/src/services/message-service.ts +203 -0
  432. package/references/openteams/src/services/task-service.test.ts +434 -0
  433. package/references/openteams/src/services/task-service.ts +239 -0
  434. package/references/openteams/src/services/team-service.test.ts +181 -0
  435. package/references/openteams/src/services/team-service.ts +139 -0
  436. package/references/openteams/src/services/template-service.test.ts +306 -0
  437. package/references/openteams/src/services/template-service.ts +182 -0
  438. package/references/openteams/src/spawner/acp-factory.ts +96 -0
  439. package/references/openteams/src/spawner/interface.ts +31 -0
  440. package/references/openteams/src/spawner/mock.test.ts +93 -0
  441. package/references/openteams/src/spawner/mock.ts +59 -0
  442. package/references/openteams/src/template/loader.test.ts +1319 -0
  443. package/references/openteams/src/template/loader.ts +698 -0
  444. package/references/openteams/src/template/types.ts +200 -0
  445. package/references/openteams/src/types.ts +205 -0
  446. package/references/openteams/tsconfig.json +18 -0
  447. package/references/openteams/vitest.config.ts +9 -0
  448. package/references/skill-tree/.claude/settings.json +6 -0
  449. package/references/skill-tree/.sudocode/issues.jsonl +11 -0
  450. package/references/skill-tree/.sudocode/specs.jsonl +1 -0
  451. package/references/skill-tree/CLAUDE.md +150 -0
  452. package/references/skill-tree/README.md +324 -0
  453. package/references/skill-tree/docs/GAPS_v1.md +221 -0
  454. package/references/skill-tree/docs/INTEGRATION_PLAN.md +467 -0
  455. package/references/skill-tree/docs/TODOS.md +91 -0
  456. package/references/skill-tree/docs/anthropic_skill_guide.md +1364 -0
  457. package/references/skill-tree/docs/design/federated-skill-trees.md +524 -0
  458. package/references/skill-tree/docs/design/multi-agent-sync.md +759 -0
  459. package/references/skill-tree/docs/scraper/BRAINSTORM.md +583 -0
  460. package/references/skill-tree/docs/scraper/POC_PLAN.md +420 -0
  461. package/references/skill-tree/docs/scraper/README.md +170 -0
  462. package/references/skill-tree/examples/basic-usage.ts +190 -0
  463. package/references/skill-tree/package-lock.json +1509 -0
  464. package/references/skill-tree/package.json +66 -0
  465. package/references/skill-tree/scraper/README.md +123 -0
  466. package/references/skill-tree/scraper/docs/DESIGN.md +683 -0
  467. package/references/skill-tree/scraper/docs/PLAN.md +336 -0
  468. package/references/skill-tree/scraper/drizzle.config.ts +10 -0
  469. package/references/skill-tree/scraper/package-lock.json +6329 -0
  470. package/references/skill-tree/scraper/package.json +68 -0
  471. package/references/skill-tree/scraper/test/fixtures/invalid-skill/missing-description.md +7 -0
  472. package/references/skill-tree/scraper/test/fixtures/invalid-skill/missing-name.md +7 -0
  473. package/references/skill-tree/scraper/test/fixtures/minimal-skill/SKILL.md +27 -0
  474. package/references/skill-tree/scraper/test/fixtures/skill-json/SKILL.json +21 -0
  475. package/references/skill-tree/scraper/test/fixtures/skill-with-meta/SKILL.md +54 -0
  476. package/references/skill-tree/scraper/test/fixtures/skill-with-meta/_meta.json +24 -0
  477. package/references/skill-tree/scraper/test/fixtures/valid-skill/SKILL.md +93 -0
  478. package/references/skill-tree/scraper/test/fixtures/valid-skill/_meta.json +22 -0
  479. package/references/skill-tree/scraper/tsup.config.ts +14 -0
  480. package/references/skill-tree/scraper/vitest.config.ts +17 -0
  481. package/references/skill-tree/scripts/convert-to-vitest.ts +166 -0
  482. package/references/skill-tree/skills/skill-writer/SKILL.md +339 -0
  483. package/references/skill-tree/skills/skill-writer/references/examples.md +326 -0
  484. package/references/skill-tree/skills/skill-writer/references/patterns.md +210 -0
  485. package/references/skill-tree/skills/skill-writer/references/quality-checklist.md +123 -0
  486. package/references/skill-tree/test/run-all.ts +106 -0
  487. package/references/skill-tree/test/utils.ts +128 -0
  488. package/references/skill-tree/vitest.config.ts +16 -0
  489. package/src/__tests__/e2e/agent-spawn-visibility.e2e.test.ts +761 -0
  490. package/src/__tests__/e2e/full-agent-conflict-resolution.e2e.test.ts +2 -2
  491. package/src/__tests__/e2e/mcp-thin-client-bridge.e2e.test.ts +304 -0
  492. package/src/__tests__/e2e/mcp-tools-available.e2e.test.ts +324 -0
  493. package/src/__tests__/e2e/multi-agent.e2e.test.ts +5 -5
  494. package/src/__tests__/e2e/spawn-session-streaming.e2e.test.ts +563 -0
  495. package/src/acp/__tests__/integration.test.ts +56 -31
  496. package/src/acp/__tests__/macro-agent.test.ts +16 -7
  497. package/src/acp/macro-agent.ts +170 -36
  498. package/src/acp/types.ts +46 -1
  499. package/src/agent/__tests__/agent-manager.test.ts +228 -2
  500. package/src/agent/agent-manager.ts +809 -285
  501. package/src/agent/types.ts +12 -1
  502. package/src/api/__tests__/server.test.ts +203 -4
  503. package/src/api/server.ts +169 -10
  504. package/src/api/types.ts +3 -1
  505. package/src/auth/__tests__/token.test.ts +100 -0
  506. package/src/auth/index.ts +1 -0
  507. package/src/auth/token.ts +82 -0
  508. package/src/cli/__tests__/acp.test.ts +1 -1
  509. package/src/cli/__tests__/stable-instance-id.test.ts +1 -1
  510. package/src/cli/acp.ts +197 -72
  511. package/src/cli/index.ts +125 -15
  512. package/src/cli/mcp.ts +315 -197
  513. package/src/cli/parse-args.ts +54 -0
  514. package/src/cli/stable-instance-id.ts +14 -0
  515. package/src/config/project-config.ts +214 -27
  516. package/src/index.ts +3 -0
  517. package/src/lifecycle/__tests__/cascade-termination.test.ts +1 -1
  518. package/src/lifecycle/__tests__/handlers.test.ts +53 -0
  519. package/src/lifecycle/handlers/index.ts +25 -8
  520. package/src/lifecycle/types.ts +3 -0
  521. package/src/map/adapter/__tests__/acp-over-map-cancel.test.ts +22 -4
  522. package/src/map/adapter/__tests__/acp-over-map-getmodels.test.ts +355 -0
  523. package/src/map/adapter/__tests__/acp-over-map-history.test.ts +263 -0
  524. package/src/map/adapter/__tests__/acp-over-map-persistence.e2e.test.ts +1 -1
  525. package/src/map/adapter/__tests__/event-broadcast.test.ts +420 -0
  526. package/src/map/adapter/__tests__/event-log.test.ts +527 -0
  527. package/src/map/adapter/__tests__/event-translator.test.ts +3 -3
  528. package/src/map/adapter/__tests__/extensions.test.ts +408 -0
  529. package/src/map/adapter/__tests__/map-adapter.test.ts +99 -0
  530. package/src/map/adapter/__tests__/mcp-bridge.test.ts +1187 -0
  531. package/src/map/adapter/__tests__/multi-client-broadcast.test.ts +711 -0
  532. package/src/map/adapter/__tests__/stream-extensions.test.ts +494 -0
  533. package/src/map/adapter/__tests__/websocket-integration.test.ts +218 -0
  534. package/src/map/adapter/acp-over-map.ts +678 -66
  535. package/src/map/adapter/connection-manager.ts +3 -0
  536. package/src/map/adapter/event-log.ts +208 -0
  537. package/src/map/adapter/event-translator.ts +6 -6
  538. package/src/map/adapter/extensions/agent-lifecycle.ts +267 -0
  539. package/src/map/adapter/extensions/index.ts +96 -0
  540. package/src/map/adapter/extensions/mcp-bridge.ts +995 -0
  541. package/src/map/adapter/extensions/streams.ts +839 -0
  542. package/src/map/adapter/extensions/task.ts +11 -0
  543. package/src/map/adapter/extensions/update-metadata.ts +126 -0
  544. package/src/map/adapter/index.ts +33 -0
  545. package/src/map/adapter/interface.ts +2 -0
  546. package/src/map/adapter/map-adapter.ts +312 -47
  547. package/src/map/adapter/subscription-manager.ts +5 -1
  548. package/src/map/adapter/types.ts +10 -1
  549. package/src/mcp/__tests__/map-client.test.ts +386 -0
  550. package/src/mcp/__tests__/mcp-server-thin-client.test.ts +368 -0
  551. package/src/mcp/__tests__/mcp-server.test.ts +100 -1
  552. package/src/mcp/map-client.ts +177 -0
  553. package/src/mcp/mcp-server.ts +205 -103
  554. package/src/mcp/tools/done.ts +19 -0
  555. package/src/mcp/types.ts +6 -1
  556. package/src/metrics/metrics.ts +1 -1
  557. package/src/monitor/__tests__/stale-agent-flow.integration.test.ts +1 -1
  558. package/src/roles/__tests__/config-loader.test.ts +7 -7
  559. package/src/roles/builtin/coordinator.ts +2 -0
  560. package/src/roles/builtin/integrator.ts +2 -0
  561. package/src/roles/builtin/worker.ts +3 -0
  562. package/src/roles/capabilities.ts +28 -7
  563. package/src/roles/config-loader.ts +8 -7
  564. package/src/roles/registry.ts +2 -2
  565. package/src/roles/types.ts +7 -0
  566. package/src/server/__tests__/combined-server.test.ts +94 -21
  567. package/src/server/combined-server.ts +203 -33
  568. package/src/steering/__tests__/steering-integration.test.ts +1 -1
  569. package/src/store/__tests__/event-store-oob.test.ts +109 -0
  570. package/src/store/__tests__/event-store.test.ts +196 -1
  571. package/src/store/__tests__/instance.test.ts +3 -3
  572. package/src/store/event-store.ts +92 -23
  573. package/src/store/instance.ts +2 -2
  574. package/src/store/types/agents.ts +20 -0
  575. package/src/store/types/events.ts +1 -1
  576. package/src/task/backend/__tests__/create-task-backend.test.ts +225 -0
  577. package/src/task/backend/__tests__/e2e/unified-tool-provider-opentasks.e2e.test.ts +524 -0
  578. package/src/task/backend/__tests__/memory-pull-mode.test.ts +153 -0
  579. package/src/task/backend/__tests__/unified-tool-provider.test.ts +579 -0
  580. package/src/task/backend/index.ts +156 -106
  581. package/src/task/backend/memory.ts +4 -0
  582. package/src/task/backend/opentasks/__tests__/backend.test.ts +968 -0
  583. package/src/task/backend/opentasks/__tests__/daemon-manager.test.ts +406 -0
  584. package/src/task/backend/opentasks/__tests__/mapping.test.ts +84 -0
  585. package/src/task/backend/opentasks/__tests__/opentasks-backend.e2e.test.ts +1338 -0
  586. package/src/task/backend/opentasks/backend.ts +1323 -0
  587. package/src/task/backend/opentasks/client.ts +652 -0
  588. package/src/task/backend/opentasks/daemon-manager.ts +256 -0
  589. package/src/task/backend/opentasks/index.ts +69 -0
  590. package/src/task/backend/opentasks/mapping.ts +94 -0
  591. package/src/task/backend/types.ts +42 -66
  592. package/src/task/backend/unified-tool-provider.ts +779 -0
  593. package/src/teams/CLAUDE.md +180 -0
  594. package/src/teams/__tests__/cross-subsystem.integration.test.ts +1 -1
  595. package/src/teams/__tests__/e2e/workspace-isolation.e2e.test.ts +1263 -0
  596. package/src/teams/__tests__/team-manager.test.ts +814 -0
  597. package/src/teams/__tests__/team-system.test.ts +1291 -8
  598. package/src/teams/index.ts +21 -3
  599. package/src/teams/seed-defaults.ts +79 -0
  600. package/src/teams/team-loader.ts +202 -236
  601. package/src/teams/team-manager.ts +387 -0
  602. package/src/teams/team-runtime.ts +592 -121
  603. package/src/teams/types.ts +99 -200
  604. package/test_fixtures/README.md +2 -3
  605. package/test_fixtures/fixtures/index.ts +0 -3
  606. package/test_fixtures/fixtures/projects/project-with-specs.ts +7 -149
  607. package/test_fixtures/fixtures/repos/index.ts +1 -3
  608. package/test_fixtures/fixtures/repos/temp-repo-factory.ts +0 -116
  609. package/test_fixtures/fixtures/repos/types.ts +0 -11
  610. package/test_fixtures/harness/__tests__/fixtures.test.ts +10 -102
  611. package/test_fixtures/harness/__tests__/temp-repo-and-simulator.test.ts +0 -33
  612. package/test_fixtures/harness/simulator/agent-simulator.ts +4 -4
  613. package/vitest.config.ts +1 -1
  614. package/vitest.e2e.config.ts +1 -1
  615. package/vitest.setup.ts +1 -30
  616. package/.macro-agent/teams/self-driving/prompts/grinder.md +0 -27
  617. package/.macro-agent/teams/self-driving/prompts/judge.md +0 -27
  618. package/.macro-agent/teams/self-driving/prompts/planner.md +0 -33
  619. package/.macro-agent/teams/self-driving/roles/grinder.yaml +0 -17
  620. package/.macro-agent/teams/self-driving/roles/judge.yaml +0 -24
  621. package/.macro-agent/teams/self-driving/roles/planner.yaml +0 -18
  622. package/.macro-agent/teams/self-driving/team.yaml +0 -103
  623. package/.macro-agent/teams/structured/prompts/developer.md +0 -26
  624. package/.macro-agent/teams/structured/prompts/lead.md +0 -25
  625. package/.macro-agent/teams/structured/prompts/reviewer.md +0 -24
  626. package/.macro-agent/teams/structured/roles/developer.yaml +0 -12
  627. package/.macro-agent/teams/structured/roles/lead.yaml +0 -11
  628. package/.macro-agent/teams/structured/roles/reviewer.yaml +0 -19
  629. package/.macro-agent/teams/structured/team.yaml +0 -89
  630. package/docs/sudocode-integration.md +0 -383
  631. package/src/task/backend/__tests__/backend-parity.test.ts +0 -451
  632. package/src/task/backend/__tests__/tool-provider-edge-cases.test.ts +0 -430
  633. package/src/task/backend/__tests__/tool-provider.test.ts +0 -983
  634. package/src/task/backend/sudocode/__tests__/backend-edge-cases.test.ts +0 -575
  635. package/src/task/backend/sudocode/__tests__/backend.test.ts +0 -1194
  636. package/src/task/backend/sudocode/__tests__/client-integration.test.ts +0 -418
  637. package/src/task/backend/sudocode/__tests__/client.test.ts +0 -345
  638. package/src/task/backend/sudocode/__tests__/e2e/backend.e2e.test.ts +0 -753
  639. package/src/task/backend/sudocode/__tests__/e2e/server-client.e2e.test.ts +0 -680
  640. package/src/task/backend/sudocode/__tests__/e2e-workflow.test.ts +0 -666
  641. package/src/task/backend/sudocode/__tests__/integration/standalone-client.integration.test.ts +0 -396
  642. package/src/task/backend/sudocode/__tests__/integration/sudocode-cli.integration.test.ts +0 -328
  643. package/src/task/backend/sudocode/__tests__/integration/test-utils.ts +0 -175
  644. package/src/task/backend/sudocode/__tests__/mapping-edge-cases.test.ts +0 -265
  645. package/src/task/backend/sudocode/__tests__/server-client.test.ts +0 -675
  646. package/src/task/backend/sudocode/__tests__/sync-policy-edge-cases.test.ts +0 -521
  647. package/src/task/backend/sudocode/__tests__/sync-policy.test.ts +0 -519
  648. package/src/task/backend/sudocode/__tests__/tools.test.ts +0 -471
  649. package/src/task/backend/sudocode/backend.ts +0 -1237
  650. package/src/task/backend/sudocode/client.ts +0 -515
  651. package/src/task/backend/sudocode/index.ts +0 -120
  652. package/src/task/backend/sudocode/mapping.ts +0 -93
  653. package/src/task/backend/sudocode/server-client.ts +0 -522
  654. package/src/task/backend/sudocode/standalone-client.ts +0 -623
  655. package/src/task/backend/sudocode/sync-policy.ts +0 -387
  656. package/src/task/backend/sudocode/tools.ts +0 -896
  657. package/src/task/backend/tool-provider.ts +0 -506
  658. package/test_fixtures/fixtures/sudocode/index.ts +0 -29
  659. package/test_fixtures/fixtures/sudocode/issues.ts +0 -185
  660. package/test_fixtures/fixtures/sudocode/specs.ts +0 -159
@@ -8,6 +8,11 @@
8
8
  */
9
9
 
10
10
  import { nanoid } from "nanoid";
11
+ import {
12
+ uniqueNamesGenerator,
13
+ adjectives,
14
+ animals,
15
+ } from "unique-names-generator";
11
16
  import {
12
17
  AgentFactory,
13
18
  type Session,
@@ -50,6 +55,7 @@ import {
50
55
  type CascadeAgentManager,
51
56
  } from "../lifecycle/cascade.js";
52
57
  import type { HealthCheckService } from "../monitor/health-check-service.js";
58
+ import { AgentTokenManager } from "../auth/token.js";
53
59
 
54
60
  // ─────────────────────────────────────────────────────────────────
55
61
  // Helper Functions
@@ -103,8 +109,12 @@ export interface AgentManager {
103
109
 
104
110
  /**
105
111
  * Resume a stopped agent by loading its existing session.
112
+ * @param permissionMode - Optional permission mode override (defaults to the agent manager's default)
106
113
  */
107
- resume(agentId: AgentId): Promise<SpawnedAgent>;
114
+ resume(
115
+ agentId: AgentId,
116
+ permissionMode?: PermissionMode,
117
+ ): Promise<SpawnedAgent>;
108
118
 
109
119
  /**
110
120
  * Continue a terminated agent by spawning a new agent with the same
@@ -117,7 +127,20 @@ export interface AgentManager {
117
127
  */
118
128
  continueAgent(
119
129
  agentId: AgentId,
120
- options?: ContinueAgentOptions
130
+ options?: ContinueAgentOptions,
131
+ ): Promise<SpawnedAgent>;
132
+
133
+ /**
134
+ * Fork an agent's session, creating a new agent with the same
135
+ * conversation history. Uses forkWithFlush for active sessions
136
+ * or loadSession for stopped agents with persisted sessions.
137
+ *
138
+ * @param sourceAgentId - ID of the agent to fork from
139
+ * @param options - Fork options (name, prompt, cwd)
140
+ */
141
+ forkAgent(
142
+ sourceAgentId: AgentId,
143
+ options?: { name?: string; prompt?: string; cwd?: string },
121
144
  ): Promise<SpawnedAgent>;
122
145
 
123
146
  // ── Queries ────────────────────────────────────────────────────
@@ -143,7 +166,7 @@ export interface AgentManager {
143
166
  */
144
167
  getHierarchy(
145
168
  agentId: AgentId,
146
- options?: HierarchyOptions
169
+ options?: HierarchyOptions,
147
170
  ): AgentHierarchy | null;
148
171
 
149
172
  // ── Head Manager ───────────────────────────────────────────────
@@ -166,7 +189,7 @@ export interface AgentManager {
166
189
  */
167
190
  prompt(
168
191
  agentId: AgentId,
169
- message: string
192
+ message: string,
170
193
  ): AsyncIterable<ExtendedSessionUpdate>;
171
194
 
172
195
  /**
@@ -186,7 +209,7 @@ export interface AgentManager {
186
209
  maxFollowUps?: number;
187
210
  /** Callback for each update during prompting */
188
211
  onUpdate?: (update: ExtendedSessionUpdate) => void;
189
- }
212
+ },
190
213
  ): Promise<{
191
214
  doneCalled: boolean;
192
215
  doneStatus?: string;
@@ -235,7 +258,7 @@ export interface AgentManager {
235
258
  respondToPermission(
236
259
  agentId: AgentId,
237
260
  requestId: string,
238
- optionId: string
261
+ optionId: string,
239
262
  ): boolean;
240
263
 
241
264
  /**
@@ -247,6 +270,24 @@ export interface AgentManager {
247
270
  */
248
271
  cancelPermission(agentId: AgentId, requestId: string): boolean;
249
272
 
273
+ /**
274
+ * Change the permission mode for a running agent at runtime.
275
+ * Takes effect on the next permission request.
276
+ *
277
+ * @param agentId - Agent ID to change permission mode for
278
+ * @param mode - New permission mode
279
+ * @returns true if the mode was changed successfully
280
+ */
281
+ setPermissionMode(agentId: AgentId, mode: PermissionMode): boolean;
282
+
283
+ /**
284
+ * Get the current permission mode for a running agent.
285
+ *
286
+ * @param agentId - Agent ID to query
287
+ * @returns The current permission mode, or null if no active session
288
+ */
289
+ getPermissionMode(agentId: AgentId): PermissionMode | null;
290
+
250
291
  // ── Lifecycle Callbacks ────────────────────────────────────────
251
292
 
252
293
  /**
@@ -260,15 +301,22 @@ export interface AgentManager {
260
301
  * Set a spawn interceptor that transforms SpawnAgentOptions before spawning.
261
302
  * Used by TeamRuntime to inject team topics, prompts, MCP servers, and env vars.
262
303
  */
263
- setSpawnInterceptor(
264
- interceptor: SpawnInterceptor | null
265
- ): void;
304
+ setSpawnInterceptor(interceptor: SpawnInterceptor | null): void;
266
305
 
267
306
  /**
268
307
  * Get the RoleRegistry used by this AgentManager.
269
308
  */
270
309
  getRoleRegistry(): RoleRegistry;
271
310
 
311
+ // ── OpenTasks Socket Path (Late Binding) ─────────────────────
312
+
313
+ /**
314
+ * Set the runtime OpenTasks socket path for propagation to child agents.
315
+ * Used when createTaskBackend() discovers the daemon socket path after
316
+ * AgentManager is already created.
317
+ */
318
+ setOpenTasksSocketPath(socketPath: string): void;
319
+
272
320
  // ── Mail Services (Late Binding) ─────────────────────────────
273
321
 
274
322
  /**
@@ -277,7 +325,7 @@ export interface AgentManager {
277
325
  */
278
326
  setMailServices(
279
327
  mailService: import("../mail/mail-service.js").MailService,
280
- conversationMap: import("../mail/conversation-map.js").ConversationMap
328
+ conversationMap: import("../mail/conversation-map.js").ConversationMap,
281
329
  ): void;
282
330
 
283
331
  // ── Cleanup ────────────────────────────────────────────────────
@@ -335,6 +383,38 @@ export interface AgentManagerConfig {
335
383
  * Required when mailService is provided.
336
384
  */
337
385
  conversationMap?: import("../mail/conversation-map.js").ConversationMap;
386
+
387
+ /**
388
+ * Optional server URL for MCP thin-client mode.
389
+ * When set, spawned agents use ephemeral MAP WebSocket calls instead
390
+ * of creating local service stacks.
391
+ */
392
+ serverUrl?: string;
393
+
394
+ /**
395
+ * Server authentication token for MCP thin-client connections.
396
+ * Passed to spawned agents as MACRO_SERVER_TOKEN env var.
397
+ */
398
+ serverToken?: string;
399
+
400
+ /**
401
+ * Per-agent token manager for MCP bridge authentication.
402
+ * When provided, generates a unique token per agent at spawn time
403
+ * and revokes it on terminate.
404
+ */
405
+ agentTokenManager?: AgentTokenManager;
406
+
407
+ /**
408
+ * Task backend type to propagate to child MCP subprocesses.
409
+ * Sourced from merged config. Falls back to MACRO_TASK_BACKEND env var.
410
+ */
411
+ taskBackend?: string;
412
+
413
+ /**
414
+ * OpenTasks socket path to propagate to child MCP subprocesses.
415
+ * Sourced from merged config. Falls back to OPENTASKS_SOCKET_PATH env var.
416
+ */
417
+ openTasksSocketPath?: string;
338
418
  }
339
419
 
340
420
  // ─────────────────────────────────────────────────────────────────
@@ -346,7 +426,7 @@ export interface AgentManagerConfig {
346
426
  * Used by TeamRuntime to inject team-specific configuration.
347
427
  */
348
428
  export type SpawnInterceptor = (
349
- options: SpawnAgentOptions
429
+ options: SpawnAgentOptions,
350
430
  ) => SpawnAgentOptions | Promise<SpawnAgentOptions>;
351
431
 
352
432
  // ─────────────────────────────────────────────────────────────────
@@ -356,7 +436,7 @@ export type SpawnInterceptor = (
356
436
  export function createAgentManager(
357
437
  eventStore: EventStore,
358
438
  messageRouter: MessageRouter,
359
- config: AgentManagerConfig = {}
439
+ config: AgentManagerConfig = {},
360
440
  ): AgentManager {
361
441
  const {
362
442
  defaultPermissionMode = "auto-approve",
@@ -367,8 +447,16 @@ export function createAgentManager(
367
447
  healthCheckService,
368
448
  mailService: initialMailService,
369
449
  conversationMap: initialConversationMap,
450
+ serverUrl,
451
+ serverToken,
452
+ agentTokenManager,
453
+ taskBackend: configTaskBackend,
454
+ openTasksSocketPath: initialOpenTasksSocketPath,
370
455
  } = config;
371
456
 
457
+ // Mutable OpenTasks socket path (support late binding via setOpenTasksSocketPath)
458
+ let configOpenTasksSocketPath = initialOpenTasksSocketPath;
459
+
372
460
  // Mutable mail services (support late binding via setMailServices)
373
461
  let mailService = initialMailService;
374
462
  let conversationMap = initialConversationMap;
@@ -385,11 +473,95 @@ export function createAgentManager(
385
473
  // Lifecycle event listeners
386
474
  const lifecycleListeners = new Set<AgentLifecycleCallback>();
387
475
 
476
+ // Shutdown guard — prevents spawns during close()
477
+ let isShuttingDown = false;
478
+
479
+ // ─────────────────────────────────────────────────────────────────
480
+ // MCP Server Config
481
+ // ─────────────────────────────────────────────────────────────────
482
+
483
+ /**
484
+ * Build the macro-agent MCP server config for a Claude Code agent session.
485
+ * Used by spawn(), resume(), and forkAgent() to ensure every agent gets
486
+ * access to the macro-agent coordination tools.
487
+ */
488
+ function buildMacroAgentMcp(opts: {
489
+ agentId: string;
490
+ parentId: string;
491
+ taskId: string;
492
+ cwd: string;
493
+ permissionMode: string;
494
+ lineage?: string[];
495
+ sessionId?: string;
496
+ streamId?: string;
497
+ }) {
498
+ // Common env vars for both thin-client and legacy modes
499
+ const env = [
500
+ { name: "MACRO_AGENT_ID", value: opts.agentId },
501
+ { name: "MACRO_PARENT_ID", value: opts.parentId },
502
+ { name: "MACRO_TASK_ID", value: opts.taskId },
503
+ { name: "MACRO_AGENT_CWD", value: opts.cwd },
504
+ { name: "MACRO_PERMISSION_MODE", value: opts.permissionMode },
505
+ {
506
+ name: "MACRO_TASK_BACKEND",
507
+ value: configTaskBackend ?? process.env.MACRO_TASK_BACKEND ?? "",
508
+ },
509
+ {
510
+ name: "OPENTASKS_SOCKET_PATH",
511
+ value: configOpenTasksSocketPath ?? process.env.OPENTASKS_SOCKET_PATH ?? "",
512
+ },
513
+ // Pass streamId so MCP subprocess can include it in lifecycle context
514
+ // (WorkspaceManager is not available in subprocess)
515
+ { name: "MACRO_STREAM_ID", value: opts.streamId ?? "" },
516
+ ];
517
+
518
+ if (serverUrl) {
519
+ // Thin-client mode: forward tool calls to main server via MAP WebSocket
520
+ env.push(
521
+ { name: "MACRO_SERVER_URL", value: serverUrl },
522
+ {
523
+ name: "MACRO_AGENT_LINEAGE",
524
+ value: JSON.stringify(opts.lineage ?? []),
525
+ },
526
+ { name: "MACRO_SESSION_ID", value: opts.sessionId ?? "" },
527
+ );
528
+
529
+ // Auth tokens for thin-client connections
530
+ if (serverToken) {
531
+ env.push({ name: "MACRO_SERVER_TOKEN", value: serverToken });
532
+ }
533
+ if (agentTokenManager) {
534
+ const agentToken = agentTokenManager.createToken(opts.agentId);
535
+ env.push({ name: "MACRO_AGENT_TOKEN", value: agentToken });
536
+ }
537
+ } else {
538
+ // Legacy mode: create local service stack with shared SQLite
539
+ env.push(
540
+ { name: "MACRO_INSTANCE_ID", value: eventStore.instanceId },
541
+ { name: "MACRO_BASE_DIR", value: eventStore.baseDir },
542
+ );
543
+ }
544
+
545
+ return {
546
+ name: "macro-agent",
547
+ command: "npx",
548
+ args: ["multiagent-mcp"],
549
+ env,
550
+ };
551
+ }
552
+
388
553
  // ─────────────────────────────────────────────────────────────────
389
554
  // Lifecycle
390
555
  // ─────────────────────────────────────────────────────────────────
391
556
 
392
557
  async function spawn(rawOptions: SpawnAgentOptions): Promise<SpawnedAgent> {
558
+ if (isShuttingDown) {
559
+ throw new AgentManagerError(
560
+ "Cannot spawn agent during shutdown",
561
+ "SHUTDOWN_IN_PROGRESS",
562
+ );
563
+ }
564
+
393
565
  // Apply spawn interceptor if set (used by TeamRuntime for team context injection)
394
566
  const options = spawnInterceptor
395
567
  ? await spawnInterceptor(rawOptions)
@@ -409,9 +581,11 @@ export function createAgentManager(
409
581
  interactionPatterns,
410
582
  // Workspace-related fields (Phase 2)
411
583
  role,
584
+ team_instance,
412
585
  streamId,
413
586
  streamConfig,
414
587
  dataplaneTaskId,
588
+ capabilities,
415
589
  } = options;
416
590
 
417
591
  // Generate IDs upfront (including session_id so we can persist before starting MCP)
@@ -426,7 +600,7 @@ export function createAgentManager(
426
600
  throw new AgentManagerError(
427
601
  `Parent agent not found: ${parent}`,
428
602
  "AGENT_NOT_FOUND",
429
- parent
603
+ parent,
430
604
  );
431
605
  }
432
606
 
@@ -437,8 +611,12 @@ export function createAgentManager(
437
611
 
438
612
  // Accept either the specific capability (e.g., agent.spawn.grinder)
439
613
  // or the generic agent.spawn.custom as a fallback for non-built-in roles
440
- const hasSpecific = roleRegistry.hasCapability(parentRole, requiredCapability);
441
- const hasGeneric = requiredCapability !== AGENT_CAPABILITIES.SPAWN_CUSTOM &&
614
+ const hasSpecific = roleRegistry.hasCapability(
615
+ parentRole,
616
+ requiredCapability,
617
+ );
618
+ const hasGeneric =
619
+ requiredCapability !== AGENT_CAPABILITIES.SPAWN_CUSTOM &&
442
620
  roleRegistry.hasCapability(parentRole, AGENT_CAPABILITIES.SPAWN_CUSTOM);
443
621
 
444
622
  if (!hasSpecific && !hasGeneric) {
@@ -446,7 +624,7 @@ export function createAgentManager(
446
624
  `Parent agent with role '${parentRole}' does not have capability to spawn '${childRole}' agents. ` +
447
625
  `Required capability: ${requiredCapability}`,
448
626
  "CAPABILITY_DENIED",
449
- parent
627
+ parent,
450
628
  );
451
629
  }
452
630
  }
@@ -502,11 +680,20 @@ export function createAgentManager(
502
680
  task_id: taskId,
503
681
  parent: parent ?? null,
504
682
  role: role ?? undefined,
683
+ team_instance: team_instance ?? undefined,
505
684
  config: agentConfig ?? {},
506
685
  cwd,
507
686
  },
508
687
  });
509
688
 
689
+ // Generate a human-readable default name
690
+ const generatedName = uniqueNamesGenerator({
691
+ dictionaries: [adjectives, animals],
692
+ separator: "-",
693
+ length: 2,
694
+ });
695
+ eventStore.updateAgentMetadata(agentId as AgentId, { name: generatedName });
696
+
510
697
  // Persist immediately so MCP server subprocess can read the agent
511
698
  await eventStore.persist();
512
699
 
@@ -514,10 +701,10 @@ export function createAgentManager(
514
701
  const verifyAgent = eventStore.getAgent(agentId);
515
702
  const allAgents = eventStore.listAgents();
516
703
  console.error(
517
- `[AgentManager] After persist: agent ${agentId} exists = ${!!verifyAgent}, total agents = ${allAgents.length}, instancePath = ${eventStore.instancePath}`
704
+ `[AgentManager] After persist: agent ${agentId} exists = ${!!verifyAgent}, total agents = ${allAgents.length}, instancePath = ${eventStore.instancePath}`,
518
705
  );
519
706
  console.error(
520
- `[AgentManager] All agent IDs: ${allAgents.map((a) => a.id).join(", ")}`
707
+ `[AgentManager] All agent IDs: ${allAgents.map((a) => a.id).join(", ")}`,
521
708
  );
522
709
 
523
710
  try {
@@ -527,195 +714,242 @@ export function createAgentManager(
527
714
  env: agentConfig?.env,
528
715
  });
529
716
 
530
- // Build MCP server configuration for the macro-agent MCP server
531
- // Note: McpServerStdio doesn't have a 'type' field - stdio is the implicit default
532
- // when neither 'type: http' nor 'type: sse' is specified
533
- const macroAgentMcp = {
534
- name: "macro-agent",
535
- command: "npx",
536
- args: ["multiagent-mcp"],
537
- env: [
538
- { name: "MACRO_AGENT_ID", value: agentId },
539
- { name: "MACRO_PARENT_ID", value: parent ?? "" },
540
- { name: "MACRO_TASK_ID", value: taskId },
541
- { name: "MACRO_AGENT_CWD", value: cwd },
542
- { name: "MACRO_INSTANCE_ID", value: eventStore.instanceId },
543
- { name: "MACRO_BASE_DIR", value: eventStore.baseDir },
544
- ],
545
- };
717
+ try {
718
+ // ─────────────────────────────────────────────────────────────────
719
+ // Workspace Creation (before session, so cwd reflects worktree)
720
+ // ─────────────────────────────────────────────────────────────────
721
+ let workspace: Workspace | undefined;
722
+ let resolvedStreamId = streamId;
546
723
 
547
- // Combine with any user-provided MCP servers
548
- // Note: Like macroAgentMcp, user MCP servers use stdio (no 'type' field)
549
- const userMcpServers =
550
- agentConfig?.mcpServers?.map((s) => ({
551
- name: s.name,
552
- command: s.command,
553
- args: s.args ?? [],
554
- env: s.env
555
- ? Object.entries(s.env).map(([name, value]) => ({ name, value }))
556
- : [],
557
- })) ?? [];
724
+ if (workspaceManager && role) {
725
+ try {
726
+ workspace = await createWorkspaceForRole(
727
+ workspaceManager,
728
+ agentId,
729
+ role,
730
+ {
731
+ streamId,
732
+ streamConfig,
733
+ dataplaneTaskId,
734
+ capabilities,
735
+ cwd,
736
+ },
737
+ );
738
+
739
+ if (workspace) {
740
+ agentWorkspaces.set(agentId, workspace);
741
+ resolvedStreamId = workspace.streamId;
742
+
743
+ // Create and claim a dataplane task so the worktree gets a real
744
+ // worker branch. Without this, the worktree stays in detached
745
+ // HEAD and done() would detect "HEAD" instead of the actual
746
+ // worker branch name (e.g. worker/<agentId>/<taskId>).
747
+ if (workspace.role === "worker" && workspace.streamId) {
748
+ try {
749
+ const dpTaskId = workspaceManager.createTask(
750
+ workspace.streamId,
751
+ { title: task ?? `Task for ${agentId}` },
752
+ );
753
+ workspaceManager.claimTask(dpTaskId, agentId, workspace.path);
754
+ } catch (claimErr) {
755
+ console.error(
756
+ `[AgentManager] Failed to create/claim dataplane task for ${agentId}:`,
757
+ claimErr,
758
+ );
759
+ // Continue without a branch — worktree stays in detached HEAD
760
+ }
761
+ }
558
762
 
559
- // Create session with MCP servers
560
- // Note: The MCP server subprocess will start here and look for the agent
561
- // in EventStore. We already persisted the spawn event above.
562
- const session = await handle.createSession(cwd, {
563
- mcpServers: [macroAgentMcp, ...userMcpServers],
564
- });
763
+ // Register with parent coordinator if applicable
764
+ const isChildRole = role === "worker" || role === "integrator" ||
765
+ (capabilities && (capabilities.includes("workspace.worktree") || capabilities.includes("workspace.integrate")));
766
+ if (parent && isChildRole) {
767
+ const parentWorkspace = agentWorkspaces.get(parent);
768
+ if (parentWorkspace?.role === "coordinator") {
769
+ workspaceManager.registerChildWorkspace(
770
+ parent,
771
+ agentId,
772
+ workspace.path,
773
+ );
774
+ }
775
+ }
776
+ }
777
+ } catch (wsError) {
778
+ console.error(
779
+ `[AgentManager] Failed to create workspace for ${agentId}: ${wsError}`,
780
+ );
781
+ // Continue without workspace — don't fail the spawn
782
+ }
783
+ }
565
784
 
566
- // Emit started status (session is ready)
567
- // Include the provider's session ID (e.g., Claude Code UUID) so
568
- // it can be used for handle.loadSession() during resume
569
- eventStore.emit({
570
- type: "status",
571
- source: { agent_id: agentId },
572
- payload: {
573
- status_type: "started",
574
- summary: "Agent session started",
575
- provider_session_id: session.id,
576
- },
577
- });
785
+ // Use workspace path as the agent's working directory when available.
786
+ // This ensures the agent process, MCP subprocess (MACRO_AGENT_CWD), and
787
+ // done handler all use the worktree path instead of the repo root.
788
+ const effectiveCwd = workspace?.path ?? cwd;
578
789
 
579
- // Persist the status event
580
- await eventStore.persist();
790
+ // Update agent's cwd in EventStore so resume() also uses workspace path
791
+ if (workspace) {
792
+ eventStore.updateAgentMetadata(agentId as AgentId, { cwd: effectiveCwd });
793
+ await eventStore.persist();
794
+ }
581
795
 
582
- // Set up default subscriptions via MessageRouter
583
- messageRouter.setupDefaultSubscriptions({
584
- agent_id: agentId,
585
- parent_id: parent ?? undefined,
586
- task_id: taskId,
587
- subscribe_parent: subscribeParent,
588
- additional_topics: topics,
589
- role: role ?? undefined,
590
- });
796
+ const macroAgentMcp = buildMacroAgentMcp({
797
+ agentId,
798
+ parentId: parent ?? "",
799
+ taskId,
800
+ cwd: effectiveCwd,
801
+ permissionMode,
802
+ lineage: parentAgent?.lineage
803
+ ? [...parentAgent.lineage, parent!]
804
+ : [],
805
+ sessionId,
806
+ streamId,
807
+ });
591
808
 
592
- // ─────────────────────────────────────────────────────────────────
593
- // Mail: Create task conversation for this agent
594
- // ─────────────────────────────────────────────────────────────────
595
- if (mailService && conversationMap) {
596
- try {
597
- const parentConversationId = parent
598
- ? conversationMap.getAgentConversation(parent) ??
599
- conversationMap.getSessionConversation(parent)
809
+ // Combine with any user-provided MCP servers
810
+ // Note: Like macroAgentMcp, user MCP servers use stdio (no 'type' field)
811
+ const userMcpServers =
812
+ agentConfig?.mcpServers?.map((s) => ({
813
+ name: s.name,
814
+ command: s.command,
815
+ args: s.args ?? [],
816
+ env: s.env
817
+ ? Object.entries(s.env).map(([name, value]) => ({ name, value }))
818
+ : [],
819
+ })) ?? [];
820
+
821
+ // Create session with MCP servers
822
+ // Note: The MCP server subprocess will start here and look for the agent
823
+ // in EventStore. We already persisted the spawn event above.
824
+ //
825
+ // When permissionMode is "interactive", we strip settingSources so that
826
+ // the Claude Code subprocess doesn't read pre-approved tool rules from
827
+ // the user's ~/.claude/settings.local.json. This ensures ALL tool calls
828
+ // go through the canUseTool → requestPermission ACP flow.
829
+ const agentMeta =
830
+ permissionMode === "interactive"
831
+ ? { claudeCode: { options: { settingSources: [] } } }
600
832
  : undefined;
833
+ const session = await handle.createSession(effectiveCwd, {
834
+ mcpServers: [macroAgentMcp, ...userMcpServers],
835
+ ...(agentMeta && { agentMeta }),
836
+ });
601
837
 
602
- const { conversationId: taskConvId } =
603
- mailService.createConversation({
604
- type: "task",
605
- subject: task?.slice(0, 80),
606
- createdBy: parent ?? agentId,
607
- parentConversationId: parentConversationId,
608
- });
838
+ // Emit started status (session is ready)
839
+ // Include the provider's session ID (e.g., Claude Code UUID) so
840
+ // it can be used for handle.loadSession() during resume
841
+ eventStore.emit({
842
+ type: "status",
843
+ source: { agent_id: agentId },
844
+ payload: {
845
+ status_type: "started",
846
+ summary: "Agent session started",
847
+ provider_session_id: session.id,
848
+ },
849
+ });
609
850
 
610
- // Join parent and child as participants
611
- if (parent) {
851
+ // Persist the status event
852
+ await eventStore.persist();
853
+
854
+ // Set up default subscriptions via MessageRouter
855
+ messageRouter.setupDefaultSubscriptions({
856
+ agent_id: agentId,
857
+ parent_id: parent ?? undefined,
858
+ task_id: taskId,
859
+ subscribe_parent: subscribeParent,
860
+ additional_topics: topics,
861
+ role: role ?? undefined,
862
+ });
863
+
864
+ // ─────────────────────────────────────────────────────────────────
865
+ // Mail: Create task conversation for this agent
866
+ // ─────────────────────────────────────────────────────────────────
867
+ if (mailService && conversationMap) {
868
+ try {
869
+ const parentConversationId = parent
870
+ ? (conversationMap.getAgentConversation(parent) ??
871
+ conversationMap.getSessionConversation(parent))
872
+ : undefined;
873
+
874
+ const { conversationId: taskConvId } =
875
+ mailService.createConversation({
876
+ type: "task",
877
+ subject: task?.slice(0, 80),
878
+ createdBy: parent ?? agentId,
879
+ parentConversationId: parentConversationId,
880
+ });
881
+
882
+ // Join parent and child as participants
883
+ if (parent) {
884
+ mailService.joinConversation({
885
+ conversationId: taskConvId,
886
+ participantId: parent,
887
+ participantType: "agent",
888
+ role: "initiator",
889
+ agentId: parent,
890
+ });
891
+ }
612
892
  mailService.joinConversation({
613
893
  conversationId: taskConvId,
614
- participantId: parent,
894
+ participantId: agentId,
615
895
  participantType: "agent",
616
- role: "initiator",
617
- agentId: parent,
896
+ role: "worker",
897
+ agentId,
618
898
  });
619
- }
620
- mailService.joinConversation({
621
- conversationId: taskConvId,
622
- participantId: agentId,
623
- participantType: "agent",
624
- role: "worker",
625
- agentId,
626
- });
627
899
 
628
- conversationMap.setAgentConversation(agentId, taskConvId);
629
- } catch (err) {
630
- // Never fail spawn due to mail errors
631
- console.warn(
632
- `[AgentManager] Failed to create task conversation for ${agentId}:`,
633
- err
634
- );
900
+ conversationMap.setAgentConversation(agentId, taskConvId);
901
+ } catch (err) {
902
+ // Never fail spawn due to mail errors
903
+ console.warn(
904
+ `[AgentManager] Failed to create task conversation for ${agentId}:`,
905
+ err,
906
+ );
907
+ }
635
908
  }
636
- }
637
909
 
638
- // Track active session
639
- const activeSession: ActiveSession = {
640
- agentId,
641
- handle,
642
- session,
643
- createdAt: Date.now(),
644
- isPrompting: false,
645
- };
646
- activeSessions.set(agentId, activeSession);
910
+ // Track active session
911
+ const activeSession: ActiveSession = {
912
+ agentId,
913
+ handle,
914
+ session,
915
+ createdAt: Date.now(),
916
+ isPrompting: false,
917
+ };
918
+ activeSessions.set(agentId, activeSession);
647
919
 
648
- // Get the agent from materialized view
649
- const agent = eventStore.getAgent(agentId)!;
920
+ // Get the agent from materialized view
921
+ const agent = eventStore.getAgent(agentId)!;
650
922
 
651
- // ─────────────────────────────────────────────────────────────────
652
- // Workspace Creation (Phase 2)
653
- // ─────────────────────────────────────────────────────────────────
654
- let workspace: Workspace | undefined;
655
- let resolvedStreamId = streamId;
923
+ // Notify lifecycle listeners
924
+ notifyLifecycle({ type: "spawned", agent });
925
+ notifyLifecycle({ type: "started", agent });
656
926
 
657
- if (workspaceManager && role) {
658
- try {
659
- workspace = await createWorkspaceForRole(
660
- workspaceManager,
661
- agentId,
662
- role,
663
- {
664
- streamId,
665
- streamConfig,
666
- dataplaneTaskId,
667
- cwd,
668
- }
669
- );
670
-
671
- if (workspace) {
672
- agentWorkspaces.set(agentId, workspace);
673
- resolvedStreamId = workspace.streamId;
674
-
675
- // Register with parent coordinator if applicable
676
- if (
677
- parent &&
678
- (role === "worker" || role === "integrator")
679
- ) {
680
- const parentWorkspace = agentWorkspaces.get(parent);
681
- if (parentWorkspace?.role === "coordinator") {
682
- workspaceManager.registerChildWorkspace(
683
- parent,
684
- agentId,
685
- workspace.path
686
- );
687
- }
688
- }
689
- }
690
- } catch (wsError) {
691
- console.error(
692
- `[AgentManager] Failed to create workspace for ${agentId}: ${wsError}`
693
- );
694
- // Continue without workspace - don't fail the spawn
927
+ // Start health monitoring for coordinators
928
+ if (healthCheckService && role === "coordinator") {
929
+ healthCheckService.startForCoordinator(agentId);
695
930
  }
696
- }
697
-
698
- // Notify lifecycle listeners
699
- notifyLifecycle({ type: "spawned", agent });
700
- notifyLifecycle({ type: "started", agent });
701
931
 
702
- // Start health monitoring for coordinators
703
- if (healthCheckService && role === "coordinator") {
704
- healthCheckService.startForCoordinator(agentId);
932
+ return {
933
+ id: agentId,
934
+ session_id: sessionId, // Macro-agent's own session ID for ACP protocol mapping
935
+ agent,
936
+ session,
937
+ workspace,
938
+ streamId: resolvedStreamId,
939
+ };
940
+ } catch (handleError) {
941
+ // Close the spawned process to prevent orphaning
942
+ try {
943
+ await handle.close();
944
+ } catch {
945
+ // Ignore errors during cleanup
946
+ }
947
+ throw handleError;
705
948
  }
706
-
707
- return {
708
- id: agentId,
709
- session_id: sessionId, // Macro-agent's own session ID for ACP protocol mapping
710
- agent,
711
- session,
712
- workspace,
713
- streamId: resolvedStreamId,
714
- };
715
949
  } catch (error) {
716
950
  // Clean up the spawn event we already emitted
717
951
  eventStore.emit({
718
- type: "terminate",
952
+ type: "stop",
719
953
  source: { agent_id: agentId },
720
954
  payload: {
721
955
  reason: "failed",
@@ -726,21 +960,21 @@ export function createAgentManager(
726
960
  throw new AgentManagerError(
727
961
  `Failed to spawn agent: ${error}`,
728
962
  "SPAWN_FAILED",
729
- agentId
963
+ agentId,
730
964
  );
731
965
  }
732
966
  }
733
967
 
734
968
  async function terminate(
735
969
  agentId: AgentId,
736
- reason: AgentStopReason
970
+ reason: AgentStopReason,
737
971
  ): Promise<void> {
738
972
  const agent = eventStore.getAgent(agentId);
739
973
  if (!agent) {
740
974
  throw new AgentManagerError(
741
975
  `Agent not found: ${agentId}`,
742
976
  "AGENT_NOT_FOUND",
743
- agentId
977
+ agentId,
744
978
  );
745
979
  }
746
980
 
@@ -769,12 +1003,17 @@ export function createAgentManager(
769
1003
  agentWorkspaces.delete(agentId);
770
1004
  } catch (wsError) {
771
1005
  console.error(
772
- `[AgentManager] Failed to deallocate workspace for ${agentId}: ${wsError}`
1006
+ `[AgentManager] Failed to deallocate workspace for ${agentId}: ${wsError}`,
773
1007
  );
774
1008
  // Continue with termination even if workspace cleanup fails
775
1009
  }
776
1010
  }
777
1011
 
1012
+ // Revoke agent authentication token
1013
+ if (agentTokenManager) {
1014
+ agentTokenManager.revokeToken(agentId);
1015
+ }
1016
+
778
1017
  // ─────────────────────────────────────────────────────────────────
779
1018
  // Mail: Close task conversation on terminate
780
1019
  // ─────────────────────────────────────────────────────────────────
@@ -789,8 +1028,7 @@ export function createAgentManager(
789
1028
  });
790
1029
  }
791
1030
  // Close any peer conversations
792
- const peerConvIds =
793
- conversationMap.closePeerConversationsFor(agentId);
1031
+ const peerConvIds = conversationMap.closePeerConversationsFor(agentId);
794
1032
  for (const peerConvId of peerConvIds) {
795
1033
  mailService.closeConversation({
796
1034
  conversationId: peerConvId,
@@ -802,14 +1040,14 @@ export function createAgentManager(
802
1040
  } catch (err) {
803
1041
  console.warn(
804
1042
  `[AgentManager] Failed to close conversation for ${agentId}:`,
805
- err
1043
+ err,
806
1044
  );
807
1045
  }
808
1046
  }
809
1047
 
810
- // Emit terminate event
1048
+ // Emit stop event
811
1049
  eventStore.emit({
812
- type: "terminate",
1050
+ type: "stop",
813
1051
  source: { agent_id: agentId },
814
1052
  payload: {
815
1053
  agent_id: agentId,
@@ -876,19 +1114,30 @@ export function createAgentManager(
876
1114
  child.id,
877
1115
  agentId,
878
1116
  cascadeAdapter,
879
- workspaceProvider
1117
+ workspaceProvider,
880
1118
  );
881
1119
  }
882
1120
  }
883
1121
  }
884
1122
 
885
- async function resume(agentId: AgentId): Promise<SpawnedAgent> {
1123
+ async function resume(
1124
+ agentId: AgentId,
1125
+ overridePermissionMode?: PermissionMode,
1126
+ ): Promise<SpawnedAgent> {
1127
+ if (isShuttingDown) {
1128
+ throw new AgentManagerError(
1129
+ "Cannot resume agent during shutdown",
1130
+ "SHUTDOWN_IN_PROGRESS",
1131
+ agentId,
1132
+ );
1133
+ }
1134
+
886
1135
  const agent = eventStore.getAgent(agentId);
887
1136
  if (!agent) {
888
1137
  throw new AgentManagerError(
889
1138
  `Agent not found: ${agentId}`,
890
1139
  "AGENT_NOT_FOUND",
891
- agentId
1140
+ agentId,
892
1141
  );
893
1142
  }
894
1143
 
@@ -897,66 +1146,261 @@ export function createAgentManager(
897
1146
  throw new AgentManagerError(
898
1147
  `Agent already has active session: ${agentId}`,
899
1148
  "ALREADY_RUNNING",
900
- agentId
1149
+ agentId,
901
1150
  );
902
1151
  }
903
1152
 
1153
+ const permissionMode = overridePermissionMode ?? defaultPermissionMode;
1154
+
904
1155
  // Spawn new process
905
1156
  const handle = await AgentFactory.spawn(defaultAgentType, {
906
- permissionMode: defaultPermissionMode,
1157
+ permissionMode,
907
1158
  });
908
1159
 
909
- const agentCwd = agent.cwd ?? defaultCwd;
910
- let session;
1160
+ try {
1161
+ const agentCwd = agent.cwd ?? defaultCwd;
1162
+ let session;
911
1163
 
912
- if (agent.provider_session_id) {
913
- // Load existing session using the provider's session ID (e.g., Claude Code UUID)
914
- session = await handle.loadSession(agent.provider_session_id, agentCwd);
915
- } else {
916
- // No provider session ID available (agent predates this feature or wasn't persisted).
917
- // Create a new session instead of loading with the macro-agent session_id
918
- // which is not a valid provider session ID (e.g., Claude Code expects UUIDs).
919
- session = await handle.createSession(agentCwd);
1164
+ // When interactive mode, strip settings to prevent auto-approval
1165
+ const resumeAgentMeta =
1166
+ permissionMode === "interactive"
1167
+ ? { claudeCode: { options: { settingSources: [] } } }
1168
+ : undefined;
1169
+
1170
+ const macroAgentMcp = buildMacroAgentMcp({
1171
+ agentId,
1172
+ parentId: agent.parent ?? "",
1173
+ taskId: agent.task_id ?? "",
1174
+ cwd: agentCwd,
1175
+ permissionMode,
1176
+ lineage: agent.lineage ?? [],
1177
+ sessionId: agent.session_id ?? "",
1178
+ });
1179
+ const mcpServers = [macroAgentMcp];
1180
+
1181
+ if (agent.provider_session_id) {
1182
+ // Load existing session using the provider's session ID (e.g., Claude Code UUID)
1183
+ // Note: loadSession's TS type for mcpServers is { name, uri }[] but
1184
+ // the underlying ACP protocol accepts full McpServerStdio. The JS
1185
+ // implementation passes mcpServers through to the connection unchanged.
1186
+ session = await handle.loadSession(
1187
+ agent.provider_session_id,
1188
+ agentCwd,
1189
+ mcpServers as any,
1190
+ resumeAgentMeta ? { agentMeta: resumeAgentMeta } : undefined,
1191
+ );
1192
+ } else {
1193
+ // No provider session ID available (agent predates this feature or wasn't persisted).
1194
+ // Create a new session instead of loading with the macro-agent session_id
1195
+ // which is not a valid provider session ID (e.g., Claude Code expects UUIDs).
1196
+ session = await handle.createSession(agentCwd, {
1197
+ mcpServers,
1198
+ ...(resumeAgentMeta && { agentMeta: resumeAgentMeta }),
1199
+ });
1200
+
1201
+ // Store the provider session ID for future resumes
1202
+ eventStore.emit({
1203
+ type: "status",
1204
+ source: { agent_id: agentId },
1205
+ payload: {
1206
+ status_type: "started",
1207
+ summary: "Agent session created (no provider session to resume)",
1208
+ provider_session_id: session.id,
1209
+ },
1210
+ });
1211
+ }
1212
+
1213
+ // Track active session
1214
+ const activeSession: ActiveSession = {
1215
+ agentId,
1216
+ handle,
1217
+ session,
1218
+ createdAt: Date.now(),
1219
+ isPrompting: false,
1220
+ };
1221
+ activeSessions.set(agentId, activeSession);
920
1222
 
921
- // Store the provider session ID for future resumes
1223
+ // Emit status event for resume
922
1224
  eventStore.emit({
923
1225
  type: "status",
924
1226
  source: { agent_id: agentId },
925
1227
  payload: {
926
1228
  status_type: "started",
927
- summary: "Agent session created (no provider session to resume)",
1229
+ summary: "Agent session resumed",
928
1230
  provider_session_id: session.id,
929
1231
  },
930
1232
  });
1233
+
1234
+ return {
1235
+ id: agentId,
1236
+ session_id: agent.session_id, // Macro-agent's own session ID
1237
+ agent: eventStore.getAgent(agentId)!,
1238
+ session,
1239
+ };
1240
+ } catch (handleError) {
1241
+ // Close the spawned process to prevent orphaning
1242
+ try {
1243
+ await handle.close();
1244
+ } catch {
1245
+ // Ignore errors during cleanup
1246
+ }
1247
+ throw handleError;
931
1248
  }
1249
+ }
932
1250
 
933
- // Track active session
934
- const activeSession: ActiveSession = {
935
- agentId,
936
- handle,
937
- session,
938
- createdAt: Date.now(),
939
- isPrompting: false,
940
- };
941
- activeSessions.set(agentId, activeSession);
1251
+ // ─────────────────────────────────────────────────────────────────
1252
+ // Fork
1253
+ // ─────────────────────────────────────────────────────────────────
942
1254
 
943
- // Emit status event for resume
1255
+ async function forkAgent(
1256
+ sourceAgentId: AgentId,
1257
+ options?: { name?: string; prompt?: string; cwd?: string },
1258
+ ): Promise<SpawnedAgent> {
1259
+ if (isShuttingDown) {
1260
+ throw new AgentManagerError(
1261
+ "Cannot fork agent during shutdown",
1262
+ "SHUTDOWN_IN_PROGRESS",
1263
+ sourceAgentId,
1264
+ );
1265
+ }
1266
+
1267
+ const sourceAgent = eventStore.getAgent(sourceAgentId);
1268
+ if (!sourceAgent) {
1269
+ throw new AgentManagerError(
1270
+ `Agent not found: ${sourceAgentId}`,
1271
+ "AGENT_NOT_FOUND",
1272
+ sourceAgentId,
1273
+ );
1274
+ }
1275
+
1276
+ // Need either an active session or a persisted provider_session_id
1277
+ const activeSession = activeSessions.get(sourceAgentId);
1278
+ if (!activeSession && !sourceAgent.provider_session_id) {
1279
+ throw new AgentManagerError(
1280
+ `Agent has no session to fork: ${sourceAgentId}`,
1281
+ "FORK_NOT_SUPPORTED",
1282
+ sourceAgentId,
1283
+ );
1284
+ }
1285
+
1286
+ // Generate new IDs
1287
+ const agentId = `agent_${nanoid(12)}`;
1288
+ const taskId = `task_${nanoid(12)}`;
1289
+ const sessionId = `session_${nanoid(12)}`;
1290
+ const cwd = options?.cwd ?? sourceAgent.cwd ?? defaultCwd;
1291
+
1292
+ // Emit spawn event with fork metadata
944
1293
  eventStore.emit({
945
- type: "status",
946
- source: { agent_id: agentId },
1294
+ type: "spawn",
1295
+ source: { agent_id: sourceAgentId },
947
1296
  payload: {
948
- status_type: "started",
949
- summary: "Agent session resumed",
950
- provider_session_id: session.id,
1297
+ agent_id: agentId,
1298
+ session_id: sessionId,
1299
+ task: options?.name ?? `[Fork of ${sourceAgentId}]`,
1300
+ task_id: taskId,
1301
+ parent: sourceAgent.parent ?? null,
1302
+ role: sourceAgent.role ?? undefined,
1303
+ team_instance: sourceAgent.team_instance ?? undefined,
1304
+ config: {},
1305
+ cwd,
1306
+ metadata: { fork_of: sourceAgentId },
951
1307
  },
952
1308
  });
953
1309
 
954
- return {
955
- id: agentId,
956
- session_id: agent.session_id, // Macro-agent's own session ID
957
- agent: eventStore.getAgent(agentId)!,
958
- session,
959
- };
1310
+ // Generate a human-readable name
1311
+ const generatedName = uniqueNamesGenerator({
1312
+ dictionaries: [adjectives, animals],
1313
+ separator: "-",
1314
+ length: 2,
1315
+ });
1316
+ eventStore.updateAgentMetadata(agentId as AgentId, { name: generatedName });
1317
+ await eventStore.persist();
1318
+
1319
+ // Get the provider session ID to fork from
1320
+ let forkedProviderSessionId: string;
1321
+ if (activeSession) {
1322
+ // Active session: fork with flush to ensure data is persisted
1323
+ const forkedSession = await activeSession.session.forkWithFlush();
1324
+ forkedProviderSessionId = forkedSession.id;
1325
+ } else {
1326
+ // Stopped agent: use the persisted provider session ID directly
1327
+ forkedProviderSessionId = sourceAgent.provider_session_id!;
1328
+ }
1329
+
1330
+ // Spawn a new process
1331
+ const handle = await AgentFactory.spawn(defaultAgentType, {
1332
+ permissionMode: defaultPermissionMode,
1333
+ });
1334
+
1335
+ try {
1336
+ const macroAgentMcp = buildMacroAgentMcp({
1337
+ agentId,
1338
+ parentId: sourceAgent.parent ?? "",
1339
+ taskId,
1340
+ cwd,
1341
+ permissionMode: defaultPermissionMode,
1342
+ lineage: sourceAgent.lineage ?? [],
1343
+ sessionId,
1344
+ });
1345
+
1346
+ // Load the forked session on the new process with correct MCP config.
1347
+ // Note: loadSession's TS type for mcpServers is { name, uri }[] but
1348
+ // the underlying ACP protocol accepts full McpServerStdio. The JS
1349
+ // implementation passes mcpServers through to the connection unchanged.
1350
+ const session = await handle.loadSession(forkedProviderSessionId, cwd, [
1351
+ macroAgentMcp,
1352
+ ] as any);
1353
+
1354
+ // Emit started status with provider session ID
1355
+ eventStore.emit({
1356
+ type: "status",
1357
+ source: { agent_id: agentId },
1358
+ payload: {
1359
+ status_type: "started",
1360
+ summary: "Agent session started (forked)",
1361
+ provider_session_id: session.id,
1362
+ },
1363
+ });
1364
+ await eventStore.persist();
1365
+
1366
+ // Set up message router subscriptions
1367
+ messageRouter.setupDefaultSubscriptions({
1368
+ agent_id: agentId,
1369
+ parent_id: sourceAgent.parent ?? undefined,
1370
+ task_id: taskId,
1371
+ subscribe_parent: false,
1372
+ additional_topics: [],
1373
+ role: sourceAgent.role ?? undefined,
1374
+ });
1375
+
1376
+ // Track active session
1377
+ const newActiveSession: ActiveSession = {
1378
+ agentId,
1379
+ handle,
1380
+ session,
1381
+ createdAt: Date.now(),
1382
+ isPrompting: false,
1383
+ };
1384
+ activeSessions.set(agentId, newActiveSession);
1385
+
1386
+ const agent = eventStore.getAgent(agentId)!;
1387
+ notifyLifecycle({ type: "spawned", agent });
1388
+ notifyLifecycle({ type: "started", agent });
1389
+
1390
+ return {
1391
+ id: agentId,
1392
+ session_id: sessionId,
1393
+ agent,
1394
+ session,
1395
+ };
1396
+ } catch (handleError) {
1397
+ try {
1398
+ await handle.close();
1399
+ } catch {
1400
+ // Ignore errors during cleanup
1401
+ }
1402
+ throw handleError;
1403
+ }
960
1404
  }
961
1405
 
962
1406
  // ─────────────────────────────────────────────────────────────────
@@ -994,7 +1438,7 @@ export function createAgentManager(
994
1438
 
995
1439
  function getHierarchy(
996
1440
  agentId: AgentId,
997
- options?: HierarchyOptions
1441
+ options?: HierarchyOptions,
998
1442
  ): AgentHierarchy | null {
999
1443
  const agent = eventStore.getAgent(agentId);
1000
1444
  if (!agent) return null;
@@ -1035,7 +1479,7 @@ export function createAgentManager(
1035
1479
  // ─────────────────────────────────────────────────────────────────
1036
1480
 
1037
1481
  async function getOrCreateHeadManager(
1038
- options: HeadManagerOptions
1482
+ options: HeadManagerOptions,
1039
1483
  ): Promise<SpawnedAgent> {
1040
1484
  const {
1041
1485
  cwd,
@@ -1107,14 +1551,14 @@ export function createAgentManager(
1107
1551
 
1108
1552
  async function* prompt(
1109
1553
  agentId: AgentId,
1110
- message: string
1554
+ message: string,
1111
1555
  ): AsyncIterable<ExtendedSessionUpdate> {
1112
1556
  const activeSession = activeSessions.get(agentId);
1113
1557
  if (!activeSession) {
1114
1558
  throw new AgentManagerError(
1115
1559
  `No active session for agent: ${agentId}`,
1116
1560
  "SESSION_NOT_FOUND",
1117
- agentId
1561
+ agentId,
1118
1562
  );
1119
1563
  }
1120
1564
 
@@ -1139,7 +1583,7 @@ export function createAgentManager(
1139
1583
  maxFollowUps?: number;
1140
1584
  throwOnMaxExceeded?: boolean;
1141
1585
  onUpdate?: (update: ExtendedSessionUpdate) => void;
1142
- }
1586
+ },
1143
1587
  ): Promise<{
1144
1588
  doneCalled: boolean;
1145
1589
  doneStatus?: string;
@@ -1156,7 +1600,10 @@ export function createAgentManager(
1156
1600
  // Helper to check if done() was called by looking for status events
1157
1601
  // The done() MCP tool emits status events with status_type completed/failed
1158
1602
  // and includes signal: "WORKER_DONE" in the details
1159
- const checkDoneCalled = async (): Promise<{ called: boolean; status?: string }> => {
1603
+ const checkDoneCalled = async (): Promise<{
1604
+ called: boolean;
1605
+ status?: string;
1606
+ }> => {
1160
1607
  // Reload from disk to see events from MCP subprocess
1161
1608
  await eventStore.reload();
1162
1609
  const statusEvents = eventStore.query({ type: "status" });
@@ -1166,14 +1613,15 @@ export function createAgentManager(
1166
1613
  (e) =>
1167
1614
  e.source?.agent_id === agentId &&
1168
1615
  (e.payload?.status_type === "completed" ||
1169
- e.payload?.status_type === "failed" ||
1170
- (e.payload?.details as Record<string, unknown>)?.signal === "WORKER_DONE")
1616
+ e.payload?.status_type === "failed" ||
1617
+ (e.payload?.details as Record<string, unknown>)?.signal ===
1618
+ "WORKER_DONE"),
1171
1619
  );
1172
1620
 
1173
1621
  if (agentCompletedStatus) {
1174
1622
  return {
1175
1623
  called: true,
1176
- status: agentCompletedStatus.payload?.status_type as string
1624
+ status: agentCompletedStatus.payload?.status_type as string,
1177
1625
  };
1178
1626
  }
1179
1627
 
@@ -1213,7 +1661,8 @@ Call done() NOW with status "completed" if your work is finished, or "blocked" i
1213
1661
 
1214
1662
  for (let i = 0; i < maxFollowUps; i++) {
1215
1663
  followUpCount++;
1216
- const followUpMessage = followUpMessages[Math.min(i, followUpMessages.length - 1)];
1664
+ const followUpMessage =
1665
+ followUpMessages[Math.min(i, followUpMessages.length - 1)];
1217
1666
 
1218
1667
  // Send follow-up prompt
1219
1668
  for await (const update of prompt(agentId, followUpMessage)) {
@@ -1238,7 +1687,7 @@ Call done() NOW with status "completed" if your work is finished, or "blocked" i
1238
1687
  if (throwOnMaxExceeded) {
1239
1688
  throw new Error(
1240
1689
  `Agent ${agentId} did not call done() after ${maxFollowUps} follow-up attempts. ` +
1241
- `Total prompts sent: ${1 + followUpCount}. Consider increasing maxFollowUps or investigating agent behavior.`
1690
+ `Total prompts sent: ${1 + followUpCount}. Consider increasing maxFollowUps or investigating agent behavior.`,
1242
1691
  );
1243
1692
  }
1244
1693
 
@@ -1293,12 +1742,12 @@ Call done() NOW with status "completed" if your work is finished, or "blocked" i
1293
1742
  function respondToPermission(
1294
1743
  agentId: AgentId,
1295
1744
  requestId: string,
1296
- optionId: string
1745
+ optionId: string,
1297
1746
  ): boolean {
1298
1747
  const activeSession = activeSessions.get(agentId);
1299
1748
  if (!activeSession) {
1300
1749
  console.warn(
1301
- `[AgentManager] Cannot respond to permission: no active session for agent ${agentId}`
1750
+ `[AgentManager] Cannot respond to permission: no active session for agent ${agentId}`,
1302
1751
  );
1303
1752
  return false;
1304
1753
  }
@@ -1306,13 +1755,13 @@ Call done() NOW with status "completed" if your work is finished, or "blocked" i
1306
1755
  try {
1307
1756
  activeSession.session.respondToPermission(requestId, optionId);
1308
1757
  console.log(
1309
- `[AgentManager] Responded to permission ${requestId} for agent ${agentId} with ${optionId}`
1758
+ `[AgentManager] Responded to permission ${requestId} for agent ${agentId} with ${optionId}`,
1310
1759
  );
1311
1760
  return true;
1312
1761
  } catch (err) {
1313
1762
  console.error(
1314
1763
  `[AgentManager] Error responding to permission ${requestId}:`,
1315
- err
1764
+ err,
1316
1765
  );
1317
1766
  return false;
1318
1767
  }
@@ -1322,7 +1771,7 @@ Call done() NOW with status "completed" if your work is finished, or "blocked" i
1322
1771
  const activeSession = activeSessions.get(agentId);
1323
1772
  if (!activeSession) {
1324
1773
  console.warn(
1325
- `[AgentManager] Cannot cancel permission: no active session for agent ${agentId}`
1774
+ `[AgentManager] Cannot cancel permission: no active session for agent ${agentId}`,
1326
1775
  );
1327
1776
  return false;
1328
1777
  }
@@ -1330,18 +1779,50 @@ Call done() NOW with status "completed" if your work is finished, or "blocked" i
1330
1779
  try {
1331
1780
  activeSession.session.cancelPermission(requestId);
1332
1781
  console.log(
1333
- `[AgentManager] Cancelled permission ${requestId} for agent ${agentId}`
1782
+ `[AgentManager] Cancelled permission ${requestId} for agent ${agentId}`,
1334
1783
  );
1335
1784
  return true;
1336
1785
  } catch (err) {
1337
1786
  console.error(
1338
1787
  `[AgentManager] Error cancelling permission ${requestId}:`,
1339
- err
1788
+ err,
1340
1789
  );
1341
1790
  return false;
1342
1791
  }
1343
1792
  }
1344
1793
 
1794
+ function setPermissionMode(agentId: AgentId, mode: PermissionMode): boolean {
1795
+ const activeSession = activeSessions.get(agentId);
1796
+ if (!activeSession) {
1797
+ console.warn(
1798
+ `[AgentManager] Cannot set permission mode: no active session for agent ${agentId}`,
1799
+ );
1800
+ return false;
1801
+ }
1802
+
1803
+ try {
1804
+ activeSession.handle.setPermissionMode(mode);
1805
+ console.log(
1806
+ `[AgentManager] Set permission mode for agent ${agentId} to ${mode}`,
1807
+ );
1808
+ return true;
1809
+ } catch (err) {
1810
+ console.error(
1811
+ `[AgentManager] Error setting permission mode for agent ${agentId}:`,
1812
+ err,
1813
+ );
1814
+ return false;
1815
+ }
1816
+ }
1817
+
1818
+ function getPermissionMode(agentId: AgentId): PermissionMode | null {
1819
+ const activeSession = activeSessions.get(agentId);
1820
+ if (!activeSession) {
1821
+ return null;
1822
+ }
1823
+ return activeSession.handle.getPermissionMode();
1824
+ }
1825
+
1345
1826
  // ─────────────────────────────────────────────────────────────────
1346
1827
  // Lifecycle Callbacks
1347
1828
  // ─────────────────────────────────────────────────────────────────
@@ -1361,13 +1842,21 @@ Call done() NOW with status "completed" if your work is finished, or "blocked" i
1361
1842
  }
1362
1843
  }
1363
1844
 
1845
+ // ─────────────────────────────────────────────────────────────────
1846
+ // OpenTasks Socket Path (Late Binding)
1847
+ // ─────────────────────────────────────────────────────────────────
1848
+
1849
+ function setOpenTasksSocketPath(socketPath: string): void {
1850
+ configOpenTasksSocketPath = socketPath;
1851
+ }
1852
+
1364
1853
  // ─────────────────────────────────────────────────────────────────
1365
1854
  // Mail Services (Late Binding)
1366
1855
  // ─────────────────────────────────────────────────────────────────
1367
1856
 
1368
1857
  function setMailServices(
1369
1858
  ms: import("../mail/mail-service.js").MailService,
1370
- cm: import("../mail/conversation-map.js").ConversationMap
1859
+ cm: import("../mail/conversation-map.js").ConversationMap,
1371
1860
  ): void {
1372
1861
  mailService = ms;
1373
1862
  conversationMap = cm;
@@ -1378,6 +1867,9 @@ Call done() NOW with status "completed" if your work is finished, or "blocked" i
1378
1867
  // ─────────────────────────────────────────────────────────────────
1379
1868
 
1380
1869
  async function close(): Promise<void> {
1870
+ // Prevent new spawns/resumes from racing with cleanup
1871
+ isShuttingDown = true;
1872
+
1381
1873
  // Stop all health checks
1382
1874
  if (healthCheckService) {
1383
1875
  healthCheckService.stopAll();
@@ -1393,7 +1885,7 @@ Call done() NOW with status "completed" if your work is finished, or "blocked" i
1393
1885
  } catch {
1394
1886
  // Ignore errors during cleanup
1395
1887
  }
1396
- })()
1888
+ })(),
1397
1889
  );
1398
1890
  }
1399
1891
 
@@ -1416,14 +1908,14 @@ Call done() NOW with status "completed" if your work is finished, or "blocked" i
1416
1908
  */
1417
1909
  async function continueAgent(
1418
1910
  agentId: AgentId,
1419
- options?: ContinueAgentOptions
1911
+ options?: ContinueAgentOptions,
1420
1912
  ): Promise<SpawnedAgent> {
1421
1913
  const agent = eventStore.getAgent(agentId);
1422
1914
  if (!agent) {
1423
1915
  throw new AgentManagerError(
1424
1916
  `Agent not found: ${agentId}`,
1425
1917
  "AGENT_NOT_FOUND",
1426
- agentId
1918
+ agentId,
1427
1919
  );
1428
1920
  }
1429
1921
 
@@ -1443,7 +1935,9 @@ Call done() NOW with status "completed" if your work is finished, or "blocked" i
1443
1935
 
1444
1936
  if (events.length > 0) {
1445
1937
  contextLines.push("## Prior Session Context");
1446
- contextLines.push(`Continuing from agent ${agentId} (${events.length} events).`);
1938
+ contextLines.push(
1939
+ `Continuing from agent ${agentId} (${events.length} events).`,
1940
+ );
1447
1941
  for (const event of events.slice(-20)) {
1448
1942
  const summary = event.payload?.summary;
1449
1943
  if (summary && typeof summary === "string") {
@@ -1456,9 +1950,7 @@ Call done() NOW with status "completed" if your work is finished, or "blocked" i
1456
1950
 
1457
1951
  // Spawn a continuation agent with same role, task, and context
1458
1952
  const taskDescription =
1459
- options?.task ??
1460
- agent.task ??
1461
- `Continue work from ${agentId}`;
1953
+ options?.task ?? agent.task ?? `Continue work from ${agentId}`;
1462
1954
 
1463
1955
  const newAgent = await spawn({
1464
1956
  task: taskDescription,
@@ -1487,6 +1979,7 @@ Call done() NOW with status "completed" if your work is finished, or "blocked" i
1487
1979
  terminate,
1488
1980
  resume,
1489
1981
  continueAgent,
1982
+ forkAgent,
1490
1983
  get,
1491
1984
  list,
1492
1985
  getChildren,
@@ -1502,9 +1995,12 @@ Call done() NOW with status "completed" if your work is finished, or "blocked" i
1502
1995
  isProcessRunning,
1503
1996
  respondToPermission,
1504
1997
  cancelPermission,
1998
+ setPermissionMode,
1999
+ getPermissionMode,
1505
2000
  onLifecycleEvent,
1506
2001
  setSpawnInterceptor,
1507
2002
  getRoleRegistry,
2003
+ setOpenTasksSocketPath,
1508
2004
  setMailServices,
1509
2005
  close,
1510
2006
  };
@@ -1518,84 +2014,112 @@ interface CreateWorkspaceOptions {
1518
2014
  streamId?: string;
1519
2015
  streamConfig?: import("../workspace/types.js").StreamConfig;
1520
2016
  dataplaneTaskId?: string;
2017
+ capabilities?: string[];
1521
2018
  cwd: string;
1522
2019
  }
1523
2020
 
1524
2021
  /**
1525
- * Create a workspace for an agent based on their role.
2022
+ * Create a workspace for an agent based on their capabilities.
2023
+ *
2024
+ * Dispatches on workspace capabilities (workspace.stream, workspace.integrate,
2025
+ * workspace.worktree) rather than role names. This allows team-defined roles
2026
+ * (e.g., "developer" extending "worker") to get proper workspace allocation
2027
+ * by inheriting workspace capabilities from their base role.
2028
+ *
2029
+ * Falls back to role-name matching for backward compatibility when no
2030
+ * capabilities are provided.
1526
2031
  *
1527
2032
  * @param workspaceManager - WorkspaceManager instance
1528
2033
  * @param agentId - Agent ID
1529
- * @param role - Agent role (e.g., 'worker', 'coordinator', 'integrator')
1530
- * @param options - Additional options
2034
+ * @param role - Agent role name (used for logging and fallback)
2035
+ * @param options - Workspace options including capabilities
1531
2036
  * @returns Created workspace or undefined
1532
2037
  */
1533
2038
  async function createWorkspaceForRole(
1534
2039
  workspaceManager: WorkspaceManager,
1535
2040
  agentId: AgentId,
1536
2041
  role: string,
1537
- options: CreateWorkspaceOptions
2042
+ options: CreateWorkspaceOptions,
1538
2043
  ): Promise<Workspace | undefined> {
1539
- const { streamId, streamConfig, dataplaneTaskId } = options;
2044
+ const { streamId, streamConfig, dataplaneTaskId, capabilities } = options;
2045
+
2046
+ // Capability-based dispatch (preferred — works for team-defined roles)
2047
+ if (capabilities && capabilities.length > 0) {
2048
+ if (capabilities.includes("workspace.stream")) {
2049
+ // Coordinator pattern: create integration stream.
2050
+ // In team mode, stream is managed by TeamRuntime.setupWorkspaceIntegration(),
2051
+ // so missing streamConfig is expected — return silently.
2052
+ if (!streamConfig) {
2053
+ return undefined;
2054
+ }
2055
+ const newStreamId = workspaceManager.createIntegrationStream(agentId, streamConfig);
2056
+ return workspaceManager.createCoordinatorWorkspace(agentId, newStreamId);
2057
+
2058
+ } else if (capabilities.includes("workspace.integrate")) {
2059
+ // Integrator pattern: join existing stream
2060
+ if (!streamId) {
2061
+ console.warn(
2062
+ `[AgentManager] ${role} ${agentId} has workspace.integrate but no streamId, skipping workspace`,
2063
+ );
2064
+ return undefined;
2065
+ }
2066
+ return workspaceManager.createIntegratorWorkspace(agentId, streamId);
1540
2067
 
2068
+ } else if (capabilities.includes("workspace.worktree")) {
2069
+ // Worker pattern: create worktree in stream
2070
+ if (!streamId) {
2071
+ console.warn(
2072
+ `[AgentManager] ${role} ${agentId} has workspace.worktree but no streamId, skipping workspace`,
2073
+ );
2074
+ return undefined;
2075
+ }
2076
+ const taskId = dataplaneTaskId ?? agentId;
2077
+ return workspaceManager.createWorkerWorkspace(agentId, taskId, streamId);
2078
+ }
2079
+
2080
+ // Has capabilities but no workspace capability — no workspace needed
2081
+ return undefined;
2082
+ }
2083
+
2084
+ // Fallback: role-name dispatch (backward compatibility for non-team spawns)
1541
2085
  switch (role) {
1542
2086
  case "coordinator": {
1543
- // Coordinators create a new integration stream
1544
2087
  if (!streamConfig) {
1545
2088
  console.warn(
1546
- `[AgentManager] Coordinator ${agentId} spawn missing streamConfig, skipping workspace`
2089
+ `[AgentManager] Coordinator ${agentId} spawn missing streamConfig, skipping workspace`,
1547
2090
  );
1548
2091
  return undefined;
1549
2092
  }
1550
-
1551
- const newStreamId = workspaceManager.createIntegrationStream(
1552
- agentId,
1553
- streamConfig
1554
- );
1555
-
2093
+ const newStreamId = workspaceManager.createIntegrationStream(agentId, streamConfig);
1556
2094
  return workspaceManager.createCoordinatorWorkspace(agentId, newStreamId);
1557
2095
  }
1558
2096
 
1559
2097
  case "integrator": {
1560
- // Integrators join an existing stream
1561
2098
  if (!streamId) {
1562
2099
  console.warn(
1563
- `[AgentManager] Integrator ${agentId} spawn missing streamId, skipping workspace`
2100
+ `[AgentManager] Integrator ${agentId} spawn missing streamId, skipping workspace`,
1564
2101
  );
1565
2102
  return undefined;
1566
2103
  }
1567
-
1568
2104
  return workspaceManager.createIntegratorWorkspace(agentId, streamId);
1569
2105
  }
1570
2106
 
1571
2107
  case "worker":
1572
2108
  case "worker.resolver": {
1573
- // Workers need streamId and either dataplaneTaskId or create a new task
1574
2109
  if (!streamId) {
1575
2110
  console.warn(
1576
- `[AgentManager] Worker ${agentId} spawn missing streamId, skipping workspace`
1577
- );
1578
- return undefined;
1579
- }
1580
-
1581
- // Use provided task ID or skip (task should be created separately)
1582
- const taskId = dataplaneTaskId;
1583
- if (!taskId) {
1584
- console.warn(
1585
- `[AgentManager] Worker ${agentId} spawn missing dataplaneTaskId, skipping workspace`
2111
+ `[AgentManager] Worker ${agentId} spawn missing streamId, skipping workspace`,
1586
2112
  );
1587
2113
  return undefined;
1588
2114
  }
1589
-
2115
+ const taskId = dataplaneTaskId ?? agentId;
1590
2116
  return workspaceManager.createWorkerWorkspace(agentId, taskId, streamId);
1591
2117
  }
1592
2118
 
1593
2119
  case "monitor":
1594
- // Monitors don't need workspaces
1595
2120
  return undefined;
1596
2121
 
1597
2122
  default:
1598
- // Unknown role - no workspace
1599
2123
  return undefined;
1600
2124
  }
1601
2125
  }