macro-agent 0.0.17 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (338) hide show
  1. package/.claude/settings.local.json +3 -1
  2. package/.sudocode/specs.jsonl +4 -0
  3. package/CLAUDE.md +16 -14
  4. package/README.md +11 -29
  5. package/dist/acp/macro-agent.d.ts +17 -0
  6. package/dist/acp/macro-agent.d.ts.map +1 -1
  7. package/dist/acp/macro-agent.js +183 -55
  8. package/dist/acp/macro-agent.js.map +1 -1
  9. package/dist/acp/types.d.ts +32 -1
  10. package/dist/acp/types.d.ts.map +1 -1
  11. package/dist/acp/types.js.map +1 -1
  12. package/dist/agent/agent-manager.d.ts +65 -1
  13. package/dist/agent/agent-manager.d.ts.map +1 -1
  14. package/dist/agent/agent-manager.js +464 -183
  15. package/dist/agent/agent-manager.js.map +1 -1
  16. package/dist/agent/types.d.ts +1 -1
  17. package/dist/agent/types.d.ts.map +1 -1
  18. package/dist/api/server.d.ts +3 -0
  19. package/dist/api/server.d.ts.map +1 -1
  20. package/dist/api/server.js +37 -6
  21. package/dist/api/server.js.map +1 -1
  22. package/dist/auth/index.d.ts +2 -0
  23. package/dist/auth/index.d.ts.map +1 -0
  24. package/dist/auth/index.js +2 -0
  25. package/dist/auth/index.js.map +1 -0
  26. package/dist/auth/token.d.ts +41 -0
  27. package/dist/auth/token.d.ts.map +1 -0
  28. package/dist/auth/token.js +73 -0
  29. package/dist/auth/token.js.map +1 -0
  30. package/dist/cli/acp.d.ts +2 -23
  31. package/dist/cli/acp.d.ts.map +1 -1
  32. package/dist/cli/acp.js +127 -61
  33. package/dist/cli/acp.js.map +1 -1
  34. package/dist/cli/index.js +147 -15
  35. package/dist/cli/index.js.map +1 -1
  36. package/dist/cli/mcp.d.ts +6 -0
  37. package/dist/cli/mcp.d.ts.map +1 -1
  38. package/dist/cli/mcp.js +268 -181
  39. package/dist/cli/mcp.js.map +1 -1
  40. package/dist/cli/parse-args.d.ts +20 -0
  41. package/dist/cli/parse-args.d.ts.map +1 -0
  42. package/dist/cli/parse-args.js +43 -0
  43. package/dist/cli/parse-args.js.map +1 -0
  44. package/dist/cli/stable-instance-id.d.ts +8 -0
  45. package/dist/cli/stable-instance-id.d.ts.map +1 -0
  46. package/dist/cli/stable-instance-id.js +14 -0
  47. package/dist/cli/stable-instance-id.js.map +1 -0
  48. package/dist/config/project-config.d.ts +74 -7
  49. package/dist/config/project-config.d.ts.map +1 -1
  50. package/dist/config/project-config.js +123 -20
  51. package/dist/config/project-config.js.map +1 -1
  52. package/dist/map/adapter/acp-over-map.d.ts +23 -0
  53. package/dist/map/adapter/acp-over-map.d.ts.map +1 -1
  54. package/dist/map/adapter/acp-over-map.js +482 -55
  55. package/dist/map/adapter/acp-over-map.js.map +1 -1
  56. package/dist/map/adapter/connection-manager.d.ts.map +1 -1
  57. package/dist/map/adapter/connection-manager.js +3 -0
  58. package/dist/map/adapter/connection-manager.js.map +1 -1
  59. package/dist/map/adapter/event-log.d.ts +87 -0
  60. package/dist/map/adapter/event-log.d.ts.map +1 -0
  61. package/dist/map/adapter/event-log.js +122 -0
  62. package/dist/map/adapter/event-log.js.map +1 -0
  63. package/dist/map/adapter/event-translator.js +6 -6
  64. package/dist/map/adapter/event-translator.js.map +1 -1
  65. package/dist/map/adapter/extensions/agent-lifecycle.d.ts +82 -0
  66. package/dist/map/adapter/extensions/agent-lifecycle.d.ts.map +1 -0
  67. package/dist/map/adapter/extensions/agent-lifecycle.js +164 -0
  68. package/dist/map/adapter/extensions/agent-lifecycle.js.map +1 -0
  69. package/dist/map/adapter/extensions/index.d.ts +10 -1
  70. package/dist/map/adapter/extensions/index.d.ts.map +1 -1
  71. package/dist/map/adapter/extensions/index.js +34 -0
  72. package/dist/map/adapter/extensions/index.js.map +1 -1
  73. package/dist/map/adapter/extensions/mcp-bridge.d.ts +57 -0
  74. package/dist/map/adapter/extensions/mcp-bridge.d.ts.map +1 -0
  75. package/dist/map/adapter/extensions/mcp-bridge.js +745 -0
  76. package/dist/map/adapter/extensions/mcp-bridge.js.map +1 -0
  77. package/dist/map/adapter/extensions/rename.d.ts +29 -0
  78. package/dist/map/adapter/extensions/rename.d.ts.map +1 -0
  79. package/dist/map/adapter/extensions/rename.js +49 -0
  80. package/dist/map/adapter/extensions/rename.js.map +1 -0
  81. package/dist/map/adapter/extensions/task.d.ts.map +1 -1
  82. package/dist/map/adapter/extensions/task.js +10 -0
  83. package/dist/map/adapter/extensions/task.js.map +1 -1
  84. package/dist/map/adapter/extensions/update-metadata.d.ts +29 -0
  85. package/dist/map/adapter/extensions/update-metadata.d.ts.map +1 -0
  86. package/dist/map/adapter/extensions/update-metadata.js +67 -0
  87. package/dist/map/adapter/extensions/update-metadata.js.map +1 -0
  88. package/dist/map/adapter/index.d.ts +2 -1
  89. package/dist/map/adapter/index.d.ts.map +1 -1
  90. package/dist/map/adapter/index.js +8 -2
  91. package/dist/map/adapter/index.js.map +1 -1
  92. package/dist/map/adapter/interface.d.ts +2 -0
  93. package/dist/map/adapter/interface.d.ts.map +1 -1
  94. package/dist/map/adapter/map-adapter.d.ts +4 -0
  95. package/dist/map/adapter/map-adapter.d.ts.map +1 -1
  96. package/dist/map/adapter/map-adapter.js +302 -30
  97. package/dist/map/adapter/map-adapter.js.map +1 -1
  98. package/dist/map/adapter/subscription-manager.d.ts.map +1 -1
  99. package/dist/map/adapter/subscription-manager.js +5 -1
  100. package/dist/map/adapter/subscription-manager.js.map +1 -1
  101. package/dist/map/adapter/types.d.ts +2 -0
  102. package/dist/map/adapter/types.d.ts.map +1 -1
  103. package/dist/mcp/map-client.d.ts +39 -0
  104. package/dist/mcp/map-client.d.ts.map +1 -0
  105. package/dist/mcp/map-client.js +129 -0
  106. package/dist/mcp/map-client.js.map +1 -0
  107. package/dist/mcp/mcp-server.d.ts +14 -0
  108. package/dist/mcp/mcp-server.d.ts.map +1 -1
  109. package/dist/mcp/mcp-server.js +113 -85
  110. package/dist/mcp/mcp-server.js.map +1 -1
  111. package/dist/mcp/types.d.ts +9 -1
  112. package/dist/mcp/types.d.ts.map +1 -1
  113. package/dist/mcp/types.js.map +1 -1
  114. package/dist/metrics/metrics.js +1 -1
  115. package/dist/metrics/metrics.js.map +1 -1
  116. package/dist/roles/capabilities.d.ts +3 -1
  117. package/dist/roles/capabilities.d.ts.map +1 -1
  118. package/dist/roles/capabilities.js +17 -7
  119. package/dist/roles/capabilities.js.map +1 -1
  120. package/dist/roles/config-loader.d.ts +6 -6
  121. package/dist/roles/config-loader.d.ts.map +1 -1
  122. package/dist/roles/config-loader.js +6 -6
  123. package/dist/roles/config-loader.js.map +1 -1
  124. package/dist/roles/registry.d.ts +2 -2
  125. package/dist/roles/registry.js +2 -2
  126. package/dist/server/combined-server.d.ts +20 -0
  127. package/dist/server/combined-server.d.ts.map +1 -1
  128. package/dist/server/combined-server.js +107 -8
  129. package/dist/server/combined-server.js.map +1 -1
  130. package/dist/store/event-store.d.ts +7 -1
  131. package/dist/store/event-store.d.ts.map +1 -1
  132. package/dist/store/event-store.js +91 -8
  133. package/dist/store/event-store.js.map +1 -1
  134. package/dist/store/types/agents.d.ts +23 -0
  135. package/dist/store/types/agents.d.ts.map +1 -1
  136. package/dist/store/types/events.d.ts +1 -1
  137. package/dist/store/types/events.d.ts.map +1 -1
  138. package/dist/task/backend/index.d.ts +47 -29
  139. package/dist/task/backend/index.d.ts.map +1 -1
  140. package/dist/task/backend/index.js +109 -71
  141. package/dist/task/backend/index.js.map +1 -1
  142. package/dist/task/backend/memory.d.ts +1 -0
  143. package/dist/task/backend/memory.d.ts.map +1 -1
  144. package/dist/task/backend/memory.js +3 -0
  145. package/dist/task/backend/memory.js.map +1 -1
  146. package/dist/task/backend/opentasks/backend.d.ts +140 -0
  147. package/dist/task/backend/opentasks/backend.d.ts.map +1 -0
  148. package/dist/task/backend/opentasks/backend.js +1023 -0
  149. package/dist/task/backend/opentasks/backend.js.map +1 -0
  150. package/dist/task/backend/opentasks/client.d.ts +337 -0
  151. package/dist/task/backend/opentasks/client.d.ts.map +1 -0
  152. package/dist/task/backend/opentasks/client.js +225 -0
  153. package/dist/task/backend/opentasks/client.js.map +1 -0
  154. package/dist/task/backend/opentasks/daemon-manager.d.ts +89 -0
  155. package/dist/task/backend/opentasks/daemon-manager.d.ts.map +1 -0
  156. package/dist/task/backend/opentasks/daemon-manager.js +195 -0
  157. package/dist/task/backend/opentasks/daemon-manager.js.map +1 -0
  158. package/dist/task/backend/opentasks/index.d.ts +21 -0
  159. package/dist/task/backend/opentasks/index.d.ts.map +1 -0
  160. package/dist/task/backend/opentasks/index.js +21 -0
  161. package/dist/task/backend/opentasks/index.js.map +1 -0
  162. package/dist/task/backend/opentasks/mapping.d.ts +48 -0
  163. package/dist/task/backend/opentasks/mapping.d.ts.map +1 -0
  164. package/dist/task/backend/opentasks/mapping.js +77 -0
  165. package/dist/task/backend/opentasks/mapping.js.map +1 -0
  166. package/dist/task/backend/types.d.ts +33 -53
  167. package/dist/task/backend/types.d.ts.map +1 -1
  168. package/dist/task/backend/types.js +7 -11
  169. package/dist/task/backend/types.js.map +1 -1
  170. package/dist/task/backend/unified-tool-provider.d.ts +57 -0
  171. package/dist/task/backend/unified-tool-provider.d.ts.map +1 -0
  172. package/dist/task/backend/unified-tool-provider.js +623 -0
  173. package/dist/task/backend/unified-tool-provider.js.map +1 -0
  174. package/dist/teams/team-loader.d.ts +2 -2
  175. package/dist/teams/team-loader.js +3 -3
  176. package/dist/teams/team-loader.js.map +1 -1
  177. package/dist/teams/team-runtime.d.ts.map +1 -1
  178. package/dist/teams/team-runtime.js +2 -0
  179. package/dist/teams/team-runtime.js.map +1 -1
  180. package/docs/architecture.md +7 -6
  181. package/docs/configuration.md +26 -62
  182. package/docs/implementation-details.md +5 -5
  183. package/docs/implementation-summary.md +17 -17
  184. package/docs/plan-self-driving-support.md +4 -4
  185. package/docs/spec-self-driving-support.md +10 -10
  186. package/docs/team-templates.md +2 -2
  187. package/docs/teams.md +3 -3
  188. package/docs/troubleshooting.md +10 -11
  189. package/package.json +6 -4
  190. package/src/__tests__/e2e/agent-spawn-visibility.e2e.test.ts +761 -0
  191. package/src/__tests__/e2e/full-agent-conflict-resolution.e2e.test.ts +2 -2
  192. package/src/__tests__/e2e/mcp-thin-client-bridge.e2e.test.ts +304 -0
  193. package/src/__tests__/e2e/mcp-tools-available.e2e.test.ts +324 -0
  194. package/src/__tests__/e2e/multi-agent.e2e.test.ts +5 -5
  195. package/src/__tests__/e2e/spawn-session-streaming.e2e.test.ts +563 -0
  196. package/src/acp/__tests__/history.test.ts +8 -4
  197. package/src/acp/__tests__/integration.test.ts +56 -31
  198. package/src/acp/__tests__/macro-agent.test.ts +16 -7
  199. package/src/acp/macro-agent.ts +230 -62
  200. package/src/acp/types.ts +46 -1
  201. package/src/agent/__tests__/agent-manager.test.ts +228 -2
  202. package/src/agent/agent-manager.ts +714 -261
  203. package/src/agent/types.ts +3 -1
  204. package/src/api/server.ts +41 -7
  205. package/src/auth/__tests__/token.test.ts +100 -0
  206. package/src/auth/index.ts +1 -0
  207. package/src/auth/token.ts +82 -0
  208. package/src/cli/__tests__/acp.test.ts +1 -1
  209. package/src/cli/__tests__/stable-instance-id.test.ts +1 -1
  210. package/src/cli/acp.ts +130 -72
  211. package/src/cli/index.ts +120 -14
  212. package/src/cli/mcp.ts +311 -207
  213. package/src/cli/parse-args.ts +54 -0
  214. package/src/cli/stable-instance-id.ts +14 -0
  215. package/src/config/project-config.ts +190 -27
  216. package/src/lifecycle/__tests__/cascade-termination.test.ts +1 -1
  217. package/src/map/adapter/__tests__/acp-over-map-cancel.test.ts +820 -0
  218. package/src/map/adapter/__tests__/acp-over-map-getmodels.test.ts +355 -0
  219. package/src/map/adapter/__tests__/acp-over-map-history.test.ts +724 -2
  220. package/src/map/adapter/__tests__/acp-over-map-persistence.e2e.test.ts +1 -1
  221. package/src/map/adapter/__tests__/event-broadcast.test.ts +420 -0
  222. package/src/map/adapter/__tests__/event-log.test.ts +527 -0
  223. package/src/map/adapter/__tests__/event-translator.test.ts +3 -3
  224. package/src/map/adapter/__tests__/extensions.test.ts +408 -0
  225. package/src/map/adapter/__tests__/map-adapter.test.ts +99 -0
  226. package/src/map/adapter/__tests__/mcp-bridge.test.ts +1187 -0
  227. package/src/map/adapter/__tests__/multi-client-broadcast.test.ts +711 -0
  228. package/src/map/adapter/__tests__/websocket-integration.test.ts +218 -0
  229. package/src/map/adapter/acp-over-map.ts +777 -92
  230. package/src/map/adapter/connection-manager.ts +3 -0
  231. package/src/map/adapter/event-log.ts +208 -0
  232. package/src/map/adapter/event-translator.ts +6 -6
  233. package/src/map/adapter/extensions/agent-lifecycle.ts +267 -0
  234. package/src/map/adapter/extensions/index.ts +60 -0
  235. package/src/map/adapter/extensions/mcp-bridge.ts +995 -0
  236. package/src/map/adapter/extensions/task.ts +11 -0
  237. package/src/map/adapter/extensions/update-metadata.ts +126 -0
  238. package/src/map/adapter/index.ts +28 -0
  239. package/src/map/adapter/interface.ts +2 -0
  240. package/src/map/adapter/map-adapter.ts +373 -38
  241. package/src/map/adapter/subscription-manager.ts +5 -1
  242. package/src/map/adapter/types.ts +2 -0
  243. package/src/mcp/__tests__/map-client.test.ts +386 -0
  244. package/src/mcp/__tests__/mcp-server-thin-client.test.ts +368 -0
  245. package/src/mcp/__tests__/mcp-server.test.ts +100 -1
  246. package/src/mcp/map-client.ts +177 -0
  247. package/src/mcp/mcp-server.ts +191 -100
  248. package/src/mcp/types.ts +6 -1
  249. package/src/metrics/metrics.ts +1 -1
  250. package/src/monitor/__tests__/stale-agent-flow.integration.test.ts +1 -1
  251. package/src/roles/__tests__/config-loader.test.ts +7 -7
  252. package/src/roles/capabilities.ts +17 -7
  253. package/src/roles/config-loader.ts +6 -6
  254. package/src/roles/registry.ts +2 -2
  255. package/src/server/__tests__/combined-server.test.ts +94 -21
  256. package/src/server/combined-server.ts +189 -33
  257. package/src/steering/__tests__/steering-integration.test.ts +1 -1
  258. package/src/store/__tests__/event-store.test.ts +236 -1
  259. package/src/store/__tests__/instance.test.ts +3 -3
  260. package/src/store/event-store.ts +109 -8
  261. package/src/store/types/agents.ts +16 -0
  262. package/src/store/types/events.ts +1 -1
  263. package/src/task/backend/__tests__/create-task-backend.test.ts +225 -0
  264. package/src/task/backend/__tests__/e2e/unified-tool-provider-opentasks.e2e.test.ts +524 -0
  265. package/src/task/backend/__tests__/unified-tool-provider.test.ts +579 -0
  266. package/src/task/backend/index.ts +156 -106
  267. package/src/task/backend/memory.ts +4 -0
  268. package/src/task/backend/opentasks/__tests__/backend.test.ts +968 -0
  269. package/src/task/backend/opentasks/__tests__/daemon-manager.test.ts +406 -0
  270. package/src/task/backend/opentasks/__tests__/mapping.test.ts +84 -0
  271. package/src/task/backend/opentasks/__tests__/opentasks-backend.e2e.test.ts +1338 -0
  272. package/src/task/backend/opentasks/backend.ts +1323 -0
  273. package/src/task/backend/opentasks/client.ts +652 -0
  274. package/src/task/backend/opentasks/daemon-manager.ts +253 -0
  275. package/src/task/backend/opentasks/index.ts +69 -0
  276. package/src/task/backend/opentasks/mapping.ts +94 -0
  277. package/src/task/backend/types.ts +42 -66
  278. package/src/task/backend/unified-tool-provider.ts +779 -0
  279. package/src/teams/__tests__/cross-subsystem.integration.test.ts +1 -1
  280. package/src/teams/team-loader.ts +3 -3
  281. package/src/teams/team-runtime.ts +2 -0
  282. package/test_fixtures/README.md +2 -3
  283. package/test_fixtures/fixtures/index.ts +0 -3
  284. package/test_fixtures/fixtures/projects/project-with-specs.ts +7 -149
  285. package/test_fixtures/fixtures/repos/index.ts +1 -3
  286. package/test_fixtures/fixtures/repos/temp-repo-factory.ts +0 -116
  287. package/test_fixtures/fixtures/repos/types.ts +0 -11
  288. package/test_fixtures/harness/__tests__/fixtures.test.ts +10 -102
  289. package/test_fixtures/harness/__tests__/temp-repo-and-simulator.test.ts +0 -33
  290. package/test_fixtures/harness/simulator/agent-simulator.ts +4 -4
  291. package/vitest.config.ts +1 -1
  292. package/vitest.e2e.config.ts +1 -1
  293. package/vitest.setup.ts +1 -30
  294. package/.macro-agent/teams/self-driving/prompts/grinder.md +0 -27
  295. package/.macro-agent/teams/self-driving/prompts/judge.md +0 -27
  296. package/.macro-agent/teams/self-driving/prompts/planner.md +0 -33
  297. package/.macro-agent/teams/self-driving/roles/grinder.yaml +0 -17
  298. package/.macro-agent/teams/self-driving/roles/judge.yaml +0 -24
  299. package/.macro-agent/teams/self-driving/roles/planner.yaml +0 -18
  300. package/.macro-agent/teams/self-driving/team.yaml +0 -103
  301. package/.macro-agent/teams/structured/prompts/developer.md +0 -26
  302. package/.macro-agent/teams/structured/prompts/lead.md +0 -25
  303. package/.macro-agent/teams/structured/prompts/reviewer.md +0 -24
  304. package/.macro-agent/teams/structured/roles/developer.yaml +0 -12
  305. package/.macro-agent/teams/structured/roles/lead.yaml +0 -11
  306. package/.macro-agent/teams/structured/roles/reviewer.yaml +0 -19
  307. package/.macro-agent/teams/structured/team.yaml +0 -89
  308. package/docs/sudocode-integration.md +0 -383
  309. package/src/task/backend/__tests__/backend-parity.test.ts +0 -451
  310. package/src/task/backend/__tests__/tool-provider-edge-cases.test.ts +0 -430
  311. package/src/task/backend/__tests__/tool-provider.test.ts +0 -983
  312. package/src/task/backend/sudocode/__tests__/backend-edge-cases.test.ts +0 -575
  313. package/src/task/backend/sudocode/__tests__/backend.test.ts +0 -1194
  314. package/src/task/backend/sudocode/__tests__/client-integration.test.ts +0 -418
  315. package/src/task/backend/sudocode/__tests__/client.test.ts +0 -345
  316. package/src/task/backend/sudocode/__tests__/e2e/backend.e2e.test.ts +0 -753
  317. package/src/task/backend/sudocode/__tests__/e2e/server-client.e2e.test.ts +0 -680
  318. package/src/task/backend/sudocode/__tests__/e2e-workflow.test.ts +0 -666
  319. package/src/task/backend/sudocode/__tests__/integration/standalone-client.integration.test.ts +0 -396
  320. package/src/task/backend/sudocode/__tests__/integration/sudocode-cli.integration.test.ts +0 -328
  321. package/src/task/backend/sudocode/__tests__/integration/test-utils.ts +0 -175
  322. package/src/task/backend/sudocode/__tests__/mapping-edge-cases.test.ts +0 -265
  323. package/src/task/backend/sudocode/__tests__/server-client.test.ts +0 -675
  324. package/src/task/backend/sudocode/__tests__/sync-policy-edge-cases.test.ts +0 -521
  325. package/src/task/backend/sudocode/__tests__/sync-policy.test.ts +0 -519
  326. package/src/task/backend/sudocode/__tests__/tools.test.ts +0 -471
  327. package/src/task/backend/sudocode/backend.ts +0 -1237
  328. package/src/task/backend/sudocode/client.ts +0 -515
  329. package/src/task/backend/sudocode/index.ts +0 -120
  330. package/src/task/backend/sudocode/mapping.ts +0 -93
  331. package/src/task/backend/sudocode/server-client.ts +0 -522
  332. package/src/task/backend/sudocode/standalone-client.ts +0 -623
  333. package/src/task/backend/sudocode/sync-policy.ts +0 -387
  334. package/src/task/backend/sudocode/tools.ts +0 -896
  335. package/src/task/backend/tool-provider.ts +0 -506
  336. package/test_fixtures/fixtures/sudocode/index.ts +0 -29
  337. package/test_fixtures/fixtures/sudocode/issues.ts +0 -185
  338. package/test_fixtures/fixtures/sudocode/specs.ts +0 -159
@@ -20,6 +20,7 @@ import type {
20
20
  PromptResponse,
21
21
  CancelNotification,
22
22
  SessionNotification,
23
+ SessionModelState,
23
24
  } from "@agentclientprotocol/sdk";
24
25
  import type { AgentManager } from "../agent/agent-manager.js";
25
26
  import type { EventStore } from "../store/event-store.js";
@@ -61,6 +62,8 @@ import type {
61
62
  RespondToPermissionResponse,
62
63
  CancelPermissionRequest,
63
64
  CancelPermissionResponse,
65
+ SetPermissionModeRequest,
66
+ SetPermissionModeResponse,
64
67
  ResumeAgentRequest,
65
68
  ResumeAgentResponse,
66
69
  GetHistoryRequest,
@@ -75,6 +78,22 @@ import type { RoleRegistry, Capability } from "../roles/types.js";
75
78
  import { AGENT_CAPABILITIES } from "../roles/capabilities.js";
76
79
  import { DefaultRoleRegistry } from "../roles/registry.js";
77
80
 
81
+ // ─────────────────────────────────────────────────────────────────
82
+ // Helpers
83
+ // ─────────────────────────────────────────────────────────────────
84
+
85
+ /** Extract a plain-text output string from `rawOutput` (string | ContentBlock[] | undefined). */
86
+ function extractToolOutput(rawOutput: unknown): string | undefined {
87
+ if (typeof rawOutput === "string") return rawOutput;
88
+ if (Array.isArray(rawOutput)) {
89
+ return rawOutput
90
+ .filter((item: any) => item.type === "text" && typeof item.text === "string")
91
+ .map((item: any) => item.text as string)
92
+ .join("\n") || undefined;
93
+ }
94
+ return undefined;
95
+ }
96
+
78
97
  // ─────────────────────────────────────────────────────────────────
79
98
  // Protocol Constants
80
99
  // ─────────────────────────────────────────────────────────────────
@@ -97,8 +116,10 @@ const SUPPORTED_EXTENSIONS: ACPExtensionMethod[] = [
97
116
  "_macro/checkCapability",
98
117
  "_macro/respondToPermission",
99
118
  "_macro/cancelPermission",
119
+ "_macro/setPermissionMode",
100
120
  "_macro/resume",
101
121
  "_macro/getHistory",
122
+ "_macro/getModels",
102
123
  ];
103
124
 
104
125
  // ─────────────────────────────────────────────────────────────────
@@ -128,6 +149,22 @@ export interface MacroAgentConfig {
128
149
  defaultCwd?: string;
129
150
  }
130
151
 
152
+ // ─────────────────────────────────────────────────────────────────
153
+ // Helpers
154
+ // ─────────────────────────────────────────────────────────────────
155
+
156
+ /**
157
+ * Build a SessionModelState from the acp-factory Session.models (string[]).
158
+ * Returns null if the models array is empty or missing.
159
+ */
160
+ function buildModelState(models: string[]): SessionModelState | null {
161
+ if (!models || models.length === 0) return null;
162
+ return {
163
+ currentModelId: models[0],
164
+ availableModels: models.map((id) => ({ modelId: id, name: id })),
165
+ };
166
+ }
167
+
131
168
  // ─────────────────────────────────────────────────────────────────
132
169
  // MacroAgent Implementation
133
170
  // ─────────────────────────────────────────────────────────────────
@@ -157,7 +194,13 @@ export class MacroAgent implements Agent {
157
194
  /** Accumulates assistant response parts during prompt streaming for history persistence */
158
195
  private promptBuffers: Map<
159
196
  ACPSessionId,
160
- { textChunks: string[]; toolCalls: Record<string, unknown>[] }
197
+ { parts: Array<{ type: "text"; text: string } | ({ type: "tool" } & Record<string, unknown>)> }
198
+ > = new Map();
199
+
200
+ /** Caches tool info (title, name, input) from initial tool_call events per session */
201
+ private toolInfoCaches: Map<
202
+ ACPSessionId,
203
+ Map<string, { title?: string; name?: string; input?: unknown }>
161
204
  > = new Map();
162
205
 
163
206
  constructor(connection: AgentSideConnection, config: MacroAgentConfig) {
@@ -249,6 +292,7 @@ export class MacroAgent implements Agent {
249
292
 
250
293
  return {
251
294
  sessionId: acpSessionId,
295
+ models: buildModelState(spawned.session.models),
252
296
  };
253
297
  }
254
298
 
@@ -259,6 +303,7 @@ export class MacroAgent implements Agent {
259
303
  let acpSessionId = params.sessionId;
260
304
  const cwd = params.cwd ?? this.defaultCwd;
261
305
 
306
+ // DEBUG: Log loadSession params
262
307
  // Extension: If _meta.agentId provided, look up session from agent record
263
308
  // This allows resuming a stopped head manager by MAP agent ID
264
309
  // when the TUI doesn't know the ACP session ID
@@ -292,14 +337,18 @@ export class MacroAgent implements Agent {
292
337
  }
293
338
  this.ensureConversation(acpSessionId, existing.id);
294
339
  await this.emitSessionInfo(acpSessionId);
295
- return {};
340
+ const activeSession = this.agentManager.getSession(existing.id);
341
+ return {
342
+ models: activeSession ? buildModelState(activeSession.models) : null,
343
+ };
296
344
  }
297
345
 
298
346
  // Agent exists but no active session - resume it
299
347
  console.log(
300
348
  `[MacroAgent] loadSession: Resuming stopped agent ${existing.id}`,
301
349
  );
302
- const spawned = await this.agentManager.resume(existing.id);
350
+ const defaultConfig = this.initConfig.defaultSubAgentConfig;
351
+ const spawned = await this.agentManager.resume(existing.id, defaultConfig?.permissionMode);
303
352
 
304
353
  // Create session mapping
305
354
  this.sessionMapper.createMapping(acpSessionId, spawned.id);
@@ -307,16 +356,20 @@ export class MacroAgent implements Agent {
307
356
  this.ensureConversation(acpSessionId, spawned.id);
308
357
  await this.emitSessionInfo(acpSessionId);
309
358
 
310
- return {};
359
+ return {
360
+ models: buildModelState(spawned.session.models),
361
+ };
311
362
  }
312
363
 
313
364
  // No existing agent found - try to get or create with the specific session ID
314
365
  console.log(
315
366
  `[MacroAgent] loadSession: No existing agent for session ${acpSessionId}, creating new`,
316
367
  );
368
+ const defaultConfig = this.initConfig.defaultSubAgentConfig;
317
369
  const spawned = await this.agentManager.getOrCreateHeadManager({
318
370
  cwd,
319
371
  sessionId: acpSessionId,
372
+ permissionMode: defaultConfig?.permissionMode,
320
373
  });
321
374
 
322
375
  // Create session mapping
@@ -325,7 +378,9 @@ export class MacroAgent implements Agent {
325
378
  this.ensureConversation(acpSessionId, spawned.id);
326
379
  await this.emitSessionInfo(acpSessionId);
327
380
 
328
- return {};
381
+ return {
382
+ models: buildModelState(spawned.session.models),
383
+ };
329
384
  }
330
385
 
331
386
  /**
@@ -360,8 +415,9 @@ export class MacroAgent implements Agent {
360
415
  // Mark session as processing (for health monitoring)
361
416
  this.sessionMapper.setProcessing(acpSessionId, true);
362
417
 
363
- // Initialize prompt buffer for history accumulation
364
- this.promptBuffers.set(acpSessionId, { textChunks: [], toolCalls: [] });
418
+ // Initialize prompt buffer and tool info cache for history accumulation
419
+ this.promptBuffers.set(acpSessionId, { parts: [] });
420
+ this.toolInfoCaches.set(acpSessionId, new Map());
365
421
 
366
422
  try {
367
423
  // Stream responses from the agent
@@ -541,6 +597,11 @@ export class MacroAgent implements Agent {
541
597
  params as unknown as CancelPermissionRequest,
542
598
  ) as unknown as Record<string, unknown>;
543
599
 
600
+ case "_macro/setPermissionMode":
601
+ return this.handleSetPermissionMode(
602
+ params as unknown as SetPermissionModeRequest,
603
+ ) as unknown as Record<string, unknown>;
604
+
544
605
  case "_macro/resume":
545
606
  return this.handleResumeAgent(
546
607
  params as unknown as ResumeAgentRequest,
@@ -551,6 +612,11 @@ export class MacroAgent implements Agent {
551
612
  params as unknown as GetHistoryRequest,
552
613
  ) as unknown as Record<string, unknown>;
553
614
 
615
+ case "_macro/getModels":
616
+ return this.handleGetModels(
617
+ params as { sessionId: string },
618
+ ) as unknown as Record<string, unknown>;
619
+
554
620
  default:
555
621
  throw new ACPError(
556
622
  `Unknown extension method: ${method}`,
@@ -769,9 +835,9 @@ export class MacroAgent implements Agent {
769
835
  private async handleForkAgent(
770
836
  params: ForkAgentRequest,
771
837
  ): Promise<ForkAgentResponse> {
772
- const { agentId, name } = params;
838
+ const { agentId, name, prompt, cwd } = params;
773
839
 
774
- // Get the original agent
840
+ // Validate source agent exists
775
841
  const originalAgent = this.agentManager.get(agentId);
776
842
  if (!originalAgent) {
777
843
  throw new ACPError(`Agent not found: ${agentId}`, "AGENT_NOT_FOUND", {
@@ -779,41 +845,48 @@ export class MacroAgent implements Agent {
779
845
  });
780
846
  }
781
847
 
782
- // Check if the agent has an active session we can fork from
783
- const hasSession = this.agentManager.hasActiveSession(agentId);
784
- if (!hasSession) {
848
+ // Check forkable: needs active session or persisted provider_session_id
849
+ if (
850
+ !this.agentManager.hasActiveSession(agentId) &&
851
+ !originalAgent.provider_session_id
852
+ ) {
785
853
  throw new ACPError(
786
- `Agent has no active session to fork: ${agentId}`,
854
+ `Agent has no session to fork: ${agentId}`,
787
855
  "FORK_NOT_SUPPORTED",
788
856
  { agentId },
789
857
  );
790
858
  }
791
859
 
792
- // For now, create a new agent with the same task as a "fork"
793
- // In a full implementation, we would:
794
- // 1. Check if acp-factory supports native fork
795
- // 2. Use loadSession to clone the conversation state
796
- // Since acp-factory doesn't expose these yet, we create a new agent
797
- // with the same task description as a simplified fork
798
-
799
- const taskDescription = name
800
- ? `[Fork of ${agentId}] ${name}`
801
- : `[Fork of ${agentId}] ${originalAgent.task ?? "Forked task"}`;
802
-
803
- const spawned = await this.agentManager.spawn({
804
- task: taskDescription,
805
- parent: originalAgent.parent ?? null,
806
- cwd: this.defaultCwd,
807
- subscribeParent: false,
860
+ // Fork via AgentManager (handles forkWithFlush + new process + loadSession)
861
+ const forked = await this.agentManager.forkAgent(agentId, {
862
+ name,
863
+ cwd: cwd ?? originalAgent.cwd ?? this.defaultCwd,
808
864
  });
809
865
 
810
- // Emit a fork event for tracking (the EventStore records this via spawn)
811
- // In a more complete implementation, we'd add a dedicated "fork" event type
866
+ // Fire-and-forget initial prompt if provided
867
+ if (prompt) {
868
+ (async () => {
869
+ try {
870
+ for await (const _update of this.agentManager.prompt(
871
+ forked.id,
872
+ prompt,
873
+ )) {
874
+ // Drain the async iterable
875
+ }
876
+ } catch (err) {
877
+ console.warn(
878
+ `[MacroAgent] Failed to send initial prompt to forked agent ${forked.id}:`,
879
+ err,
880
+ );
881
+ }
882
+ })();
883
+ }
812
884
 
813
885
  return {
814
- newAgentId: spawned.id,
815
- newSessionId: spawned.session_id,
886
+ newAgentId: forked.id,
887
+ newSessionId: forked.session_id,
816
888
  originalAgentId: agentId,
889
+ providerSessionId: forked.session.id,
817
890
  };
818
891
  }
819
892
 
@@ -1159,6 +1232,41 @@ export class MacroAgent implements Agent {
1159
1232
  }
1160
1233
  }
1161
1234
 
1235
+ /**
1236
+ * Change the permission mode for a running agent at runtime
1237
+ */
1238
+ private async handleSetPermissionMode(
1239
+ params: SetPermissionModeRequest,
1240
+ ): Promise<SetPermissionModeResponse> {
1241
+ const { agentId, permissionMode } = params;
1242
+
1243
+ // Get the current mode before changing
1244
+ const previousMode = this.agentManager.getPermissionMode(
1245
+ agentId as AgentId,
1246
+ );
1247
+
1248
+ // Set the new mode via agent manager
1249
+ const success = this.agentManager.setPermissionMode(
1250
+ agentId as AgentId,
1251
+ permissionMode,
1252
+ );
1253
+
1254
+ if (success) {
1255
+ console.log(
1256
+ `[MacroAgent] Set permission mode for agent ${agentId} from ${previousMode} to ${permissionMode}`,
1257
+ );
1258
+ return {
1259
+ success: true,
1260
+ previousMode: previousMode ?? undefined,
1261
+ };
1262
+ } else {
1263
+ return {
1264
+ success: false,
1265
+ error: `No active session found for agent ${agentId}`,
1266
+ };
1267
+ }
1268
+ }
1269
+
1162
1270
  /**
1163
1271
  * Resume a stopped/failed agent
1164
1272
  */
@@ -1292,7 +1400,7 @@ export class MacroAgent implements Agent {
1292
1400
 
1293
1401
  case "permission_request": {
1294
1402
  // Handle permission_request specially - ACP SDK doesn't recognize it as a session update
1295
- // We need to call requestPermission on the connection to forward to sudocode
1403
+ // We need to call requestPermission on the connection to forward to the client
1296
1404
 
1297
1405
  // Extract permission request data
1298
1406
  const permReq = sessionUpdate as {
@@ -1311,17 +1419,20 @@ export class MacroAgent implements Agent {
1311
1419
  }>;
1312
1420
  };
1313
1421
 
1314
- // Get the agent ID for this session to forward the response back
1315
- const agentId = this.sessionMapper.getAgentId(permReq.sessionId);
1422
+ // Look up agent ID using the ACP session ID (from the client connection),
1423
+ // NOT permReq.sessionId which is the agent's internal session ID.
1424
+ // The session mapper maps ACP session IDs → agent IDs, so using the
1425
+ // internal session ID would fail silently and drop the permission request.
1426
+ const agentId = this.sessionMapper.getAgentId(acpSessionId);
1316
1427
  if (!agentId) {
1317
1428
  console.warn(
1318
- `[MacroAgent] No agent found for session ${permReq.sessionId}, cannot forward permission request`,
1429
+ `[MacroAgent] No agent found for ACP session ${acpSessionId}, cannot forward permission request`,
1319
1430
  );
1320
1431
  return;
1321
1432
  }
1322
1433
 
1323
- // Forward to sudocode via requestPermission RPC
1324
- // This will trigger sudocode's WebSocketClientHandler which will show the prompt
1434
+ // Forward via requestPermission RPC
1435
+ // This will trigger the client's handler which will show the prompt
1325
1436
  try {
1326
1437
  const response = await this.connection.requestPermission({
1327
1438
  sessionId: acpSessionId,
@@ -1396,7 +1507,7 @@ export class MacroAgent implements Agent {
1396
1507
  console.log(`[MacroAgent] Forwarding ${updateType}`);
1397
1508
  }
1398
1509
 
1399
- // Accumulate content for history persistence
1510
+ // Accumulate content for history persistence (preserving text/tool interleaving order)
1400
1511
  const buffer = this.promptBuffers.get(acpSessionId);
1401
1512
  if (buffer) {
1402
1513
  if (updateType === "agent_message_chunk") {
@@ -1404,23 +1515,42 @@ export class MacroAgent implements Agent {
1404
1515
  | { type?: string; text?: string }
1405
1516
  | undefined;
1406
1517
  if (content?.text) {
1407
- buffer.textChunks.push(content.text);
1518
+ const last = buffer.parts[buffer.parts.length - 1];
1519
+ if (last && last.type === "text") {
1520
+ last.text += content.text;
1521
+ } else {
1522
+ buffer.parts.push({ type: "text", text: content.text });
1523
+ }
1408
1524
  }
1409
1525
  } else if (
1410
1526
  updateType === "tool_call" ||
1411
1527
  updateType === "tool_call_update"
1412
1528
  ) {
1529
+ const toolCallId = sessionUpdate.toolCallId as string | undefined;
1413
1530
  const status = sessionUpdate.status as string | undefined;
1414
- if (
1415
- status === "completed" ||
1416
- (updateType === "tool_call" && status !== "running")
1417
- ) {
1418
- buffer.toolCalls.push({
1419
- toolCallId: sessionUpdate.toolCallId,
1420
- title: sessionUpdate.title,
1421
- status: sessionUpdate.status,
1531
+ const meta = sessionUpdate._meta as { claudeCode?: { toolName?: string } } | undefined;
1532
+ const toolInfoCache = this.toolInfoCaches.get(acpSessionId);
1533
+
1534
+ // Cache tool info from initial tool_call events
1535
+ if (updateType === "tool_call" && toolCallId && toolInfoCache) {
1536
+ toolInfoCache.set(toolCallId, {
1537
+ title: sessionUpdate.title as string | undefined,
1538
+ name: meta?.claudeCode?.toolName,
1422
1539
  input: sessionUpdate.rawInput,
1423
- output: sessionUpdate.output,
1540
+ });
1541
+ }
1542
+
1543
+ if (status === "completed" || status === "failed") {
1544
+ // Merge cached info for tool_call_update events that lack title/input
1545
+ const cached = toolCallId ? toolInfoCache?.get(toolCallId) : undefined;
1546
+ buffer.parts.push({
1547
+ type: "tool",
1548
+ toolCallId,
1549
+ title: sessionUpdate.title ?? cached?.title,
1550
+ name: meta?.claudeCode?.toolName ?? cached?.name,
1551
+ status: sessionUpdate.status,
1552
+ input: sessionUpdate.rawInput ?? cached?.input,
1553
+ output: extractToolOutput(sessionUpdate.rawOutput),
1424
1554
  });
1425
1555
  }
1426
1556
  }
@@ -1627,17 +1757,8 @@ export class MacroAgent implements Agent {
1627
1757
  });
1628
1758
  }
1629
1759
 
1630
- // Record assistant turn with accumulated content
1631
- const assistantText = buffer.textChunks.join("");
1632
- const parts: unknown[] = [];
1633
-
1634
- if (assistantText) {
1635
- parts.push({ type: "text", text: assistantText });
1636
- }
1637
-
1638
- for (const tool of buffer.toolCalls) {
1639
- parts.push({ type: "tool", ...tool });
1640
- }
1760
+ // Record assistant turn with accumulated content (parts already in order)
1761
+ const parts = buffer.parts;
1641
1762
 
1642
1763
  if (parts.length > 0) {
1643
1764
  this.eventStore.emit({
@@ -1661,9 +1782,56 @@ export class MacroAgent implements Agent {
1661
1782
  error instanceof Error ? error.message : String(error),
1662
1783
  );
1663
1784
  } finally {
1664
- // Clean up the buffer
1785
+ // Clean up the buffer and tool info cache
1665
1786
  this.promptBuffers.delete(acpSessionId);
1787
+ this.toolInfoCaches.delete(acpSessionId);
1788
+ }
1789
+ }
1790
+
1791
+ /**
1792
+ * Handle _macro/getModels extension — returns the session's available models.
1793
+ * Claude Code populates models asynchronously after session creation
1794
+ * (via _model_state_update notification), so this allows the TUI to poll
1795
+ * for the model list once it's available.
1796
+ *
1797
+ * Returns full model info (modelId + name) since Claude Code uses shorthand
1798
+ * model IDs ("default", "sonnet") that don't match models.dev. The name
1799
+ * field (e.g., "Claude Sonnet 4") enables better model registry matching.
1800
+ */
1801
+ private handleGetModels(params: { sessionId: string }): {
1802
+ currentModelId: string | null;
1803
+ availableModels: Array<{ modelId: string; name: string }>;
1804
+ } {
1805
+ const { sessionId } = params;
1806
+ const agentId = this.sessionMapper.getAgentId(sessionId);
1807
+ if (!agentId) {
1808
+ return { currentModelId: null, availableModels: [] };
1809
+ }
1810
+ const session = this.agentManager.getSession(agentId);
1811
+ if (!session) {
1812
+ return { currentModelId: null, availableModels: [] };
1813
+ }
1814
+ // Try clientHandler's model info store first (from _model_state_update notification)
1815
+ const clientHandler = (session as unknown as {
1816
+ clientHandler?: {
1817
+ getSessionModelInfo?: (id: string) => {
1818
+ currentModelId: string | null;
1819
+ availableModels: Array<{ modelId: string; name: string }>;
1820
+ } | null;
1821
+ };
1822
+ }).clientHandler;
1823
+ const modelInfo = clientHandler?.getSessionModelInfo?.(session.id);
1824
+ if (modelInfo && modelInfo.availableModels.length > 0) {
1825
+ return modelInfo;
1826
+ }
1827
+ // Fall back to Session.models (from initial session response — just IDs)
1828
+ if (session.models && session.models.length > 0) {
1829
+ return {
1830
+ currentModelId: session.models[0],
1831
+ availableModels: session.models.map((id) => ({ modelId: id, name: id })),
1832
+ };
1666
1833
  }
1834
+ return { currentModelId: null, availableModels: [] };
1667
1835
  }
1668
1836
 
1669
1837
  /**
package/src/acp/types.ts CHANGED
@@ -291,6 +291,12 @@ export interface ForkAgentRequest {
291
291
 
292
292
  /** Optional name for the forked agent */
293
293
  name?: string;
294
+
295
+ /** Optional initial prompt to send after fork */
296
+ prompt?: string;
297
+
298
+ /** Optional working directory override */
299
+ cwd?: string;
294
300
  }
295
301
 
296
302
  /**
@@ -305,6 +311,9 @@ export interface ForkAgentResponse {
305
311
 
306
312
  /** Original agent ID (for reference) */
307
313
  originalAgentId: AgentId;
314
+
315
+ /** Provider session ID (Claude Code UUID) for stream connection */
316
+ providerSessionId?: string;
308
317
  }
309
318
 
310
319
  // ─────────────────────────────────────────────────────────────────
@@ -605,6 +614,38 @@ export interface CancelPermissionResponse {
605
614
  error?: string;
606
615
  }
607
616
 
617
+ // ─────────────────────────────────────────────────────────────────
618
+ // ACP Extension: _macro/setPermissionMode
619
+ // ─────────────────────────────────────────────────────────────────
620
+
621
+ /**
622
+ * Request for _macro/setPermissionMode extension
623
+ *
624
+ * Changes the permission mode for a running agent at runtime.
625
+ * Takes effect on the next permission request; in-flight requests use the old mode.
626
+ */
627
+ export interface SetPermissionModeRequest {
628
+ /** Agent ID to change (looked up directly, bypassing session mapper) */
629
+ agentId: string;
630
+
631
+ /** New permission mode */
632
+ permissionMode: ACPPermissionMode;
633
+ }
634
+
635
+ /**
636
+ * Response for _macro/setPermissionMode extension
637
+ */
638
+ export interface SetPermissionModeResponse {
639
+ /** Whether the mode was changed successfully */
640
+ success: boolean;
641
+
642
+ /** The previous permission mode (if success) */
643
+ previousMode?: ACPPermissionMode;
644
+
645
+ /** Error message if success is false */
646
+ error?: string;
647
+ }
648
+
608
649
  // ─────────────────────────────────────────────────────────────────
609
650
  // ACP Extension: _macro/resume
610
651
  // ─────────────────────────────────────────────────────────────────
@@ -691,8 +732,10 @@ export type ACPExtensionMethod =
691
732
  | "_macro/checkCapability"
692
733
  | "_macro/respondToPermission"
693
734
  | "_macro/cancelPermission"
735
+ | "_macro/setPermissionMode"
694
736
  | "_macro/resume"
695
- | "_macro/getHistory";
737
+ | "_macro/getHistory"
738
+ | "_macro/getModels";
696
739
 
697
740
  /**
698
741
  * Map of extension methods to their request types
@@ -713,6 +756,7 @@ export interface ACPExtensionRequests {
713
756
  "_macro/checkCapability": CheckCapabilityRequest;
714
757
  "_macro/respondToPermission": RespondToPermissionRequest;
715
758
  "_macro/cancelPermission": CancelPermissionRequest;
759
+ "_macro/setPermissionMode": SetPermissionModeRequest;
716
760
  "_macro/resume": ResumeAgentRequest;
717
761
  "_macro/getHistory": GetHistoryRequest;
718
762
  }
@@ -736,6 +780,7 @@ export interface ACPExtensionResponses {
736
780
  "_macro/checkCapability": CheckCapabilityResponse;
737
781
  "_macro/respondToPermission": RespondToPermissionResponse;
738
782
  "_macro/cancelPermission": CancelPermissionResponse;
783
+ "_macro/setPermissionMode": SetPermissionModeResponse;
739
784
  "_macro/resume": ResumeAgentResponse;
740
785
  "_macro/getHistory": GetHistoryResponse;
741
786
  }