macro-agent 0.0.10 → 0.0.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (518) hide show
  1. package/.macro-agent/teams/self-driving/prompts/grinder.md +27 -0
  2. package/.macro-agent/teams/self-driving/prompts/judge.md +27 -0
  3. package/.macro-agent/teams/self-driving/prompts/planner.md +33 -0
  4. package/.macro-agent/teams/self-driving/roles/grinder.yaml +17 -0
  5. package/.macro-agent/teams/self-driving/roles/judge.yaml +24 -0
  6. package/.macro-agent/teams/self-driving/roles/planner.yaml +18 -0
  7. package/.macro-agent/teams/self-driving/team.yaml +103 -0
  8. package/.macro-agent/teams/structured/prompts/developer.md +26 -0
  9. package/.macro-agent/teams/structured/prompts/lead.md +25 -0
  10. package/.macro-agent/teams/structured/prompts/reviewer.md +24 -0
  11. package/.macro-agent/teams/structured/roles/developer.yaml +12 -0
  12. package/.macro-agent/teams/structured/roles/lead.yaml +11 -0
  13. package/.macro-agent/teams/structured/roles/reviewer.yaml +19 -0
  14. package/.macro-agent/teams/structured/team.yaml +89 -0
  15. package/.sudocode/issues.jsonl +56 -51
  16. package/.sudocode/specs.jsonl +8 -1
  17. package/CLAUDE.md +121 -30
  18. package/README.md +60 -3
  19. package/dist/acp/macro-agent.d.ts +4 -0
  20. package/dist/acp/macro-agent.d.ts.map +1 -1
  21. package/dist/acp/macro-agent.js +50 -4
  22. package/dist/acp/macro-agent.js.map +1 -1
  23. package/dist/acp/session-mapper.d.ts +20 -1
  24. package/dist/acp/session-mapper.d.ts.map +1 -1
  25. package/dist/acp/session-mapper.js +90 -1
  26. package/dist/acp/session-mapper.js.map +1 -1
  27. package/dist/acp/types.d.ts +24 -1
  28. package/dist/acp/types.d.ts.map +1 -1
  29. package/dist/acp/types.js.map +1 -1
  30. package/dist/agent/agent-manager.d.ts +40 -1
  31. package/dist/agent/agent-manager.d.ts.map +1 -1
  32. package/dist/agent/agent-manager.js +172 -8
  33. package/dist/agent/agent-manager.js.map +1 -1
  34. package/dist/agent/types.d.ts +22 -0
  35. package/dist/agent/types.d.ts.map +1 -1
  36. package/dist/agent/types.js.map +1 -1
  37. package/dist/agent/wake.d.ts +15 -0
  38. package/dist/agent/wake.d.ts.map +1 -1
  39. package/dist/agent/wake.js +15 -0
  40. package/dist/agent/wake.js.map +1 -1
  41. package/dist/agent-detection/command-builder.d.ts +30 -0
  42. package/dist/agent-detection/command-builder.d.ts.map +1 -0
  43. package/dist/agent-detection/command-builder.js +71 -0
  44. package/dist/agent-detection/command-builder.js.map +1 -0
  45. package/dist/agent-detection/detector.d.ts +84 -0
  46. package/dist/agent-detection/detector.d.ts.map +1 -0
  47. package/dist/agent-detection/detector.js +240 -0
  48. package/dist/agent-detection/detector.js.map +1 -0
  49. package/dist/agent-detection/index.d.ts +12 -0
  50. package/dist/agent-detection/index.d.ts.map +1 -0
  51. package/dist/agent-detection/index.js +14 -0
  52. package/dist/agent-detection/index.js.map +1 -0
  53. package/dist/agent-detection/registry.d.ts +53 -0
  54. package/dist/agent-detection/registry.d.ts.map +1 -0
  55. package/dist/agent-detection/registry.js +177 -0
  56. package/dist/agent-detection/registry.js.map +1 -0
  57. package/dist/agent-detection/types.d.ts +121 -0
  58. package/dist/agent-detection/types.d.ts.map +1 -0
  59. package/dist/agent-detection/types.js +20 -0
  60. package/dist/agent-detection/types.js.map +1 -0
  61. package/dist/api/server.d.ts +5 -1
  62. package/dist/api/server.d.ts.map +1 -1
  63. package/dist/api/server.js +362 -0
  64. package/dist/api/server.js.map +1 -1
  65. package/dist/api/types.d.ts +50 -1
  66. package/dist/api/types.d.ts.map +1 -1
  67. package/dist/cli/acp.d.ts +2 -0
  68. package/dist/cli/acp.d.ts.map +1 -1
  69. package/dist/cli/acp.js +8 -1
  70. package/dist/cli/acp.js.map +1 -1
  71. package/dist/cli/index.js +29 -0
  72. package/dist/cli/index.js.map +1 -1
  73. package/dist/cli/mcp.js +38 -0
  74. package/dist/cli/mcp.js.map +1 -1
  75. package/dist/config/index.d.ts +2 -0
  76. package/dist/config/index.d.ts.map +1 -0
  77. package/dist/config/index.js +2 -0
  78. package/dist/config/index.js.map +1 -0
  79. package/dist/config/project-config.d.ts +46 -0
  80. package/dist/config/project-config.d.ts.map +1 -0
  81. package/dist/config/project-config.js +68 -0
  82. package/dist/config/project-config.js.map +1 -0
  83. package/dist/lifecycle/cascade.d.ts +1 -1
  84. package/dist/lifecycle/cascade.d.ts.map +1 -1
  85. package/dist/lifecycle/handlers/index.d.ts +4 -0
  86. package/dist/lifecycle/handlers/index.d.ts.map +1 -1
  87. package/dist/lifecycle/handlers/index.js +2 -0
  88. package/dist/lifecycle/handlers/index.js.map +1 -1
  89. package/dist/lifecycle/handlers/worker.d.ts +4 -0
  90. package/dist/lifecycle/handlers/worker.d.ts.map +1 -1
  91. package/dist/lifecycle/handlers/worker.js +35 -3
  92. package/dist/lifecycle/handlers/worker.js.map +1 -1
  93. package/dist/mail/conversation-map.d.ts +33 -0
  94. package/dist/mail/conversation-map.d.ts.map +1 -0
  95. package/dist/mail/conversation-map.js +61 -0
  96. package/dist/mail/conversation-map.js.map +1 -0
  97. package/dist/mail/index.d.ts +11 -0
  98. package/dist/mail/index.d.ts.map +1 -0
  99. package/dist/mail/index.js +11 -0
  100. package/dist/mail/index.js.map +1 -0
  101. package/dist/mail/mail-service.d.ts +85 -0
  102. package/dist/mail/mail-service.d.ts.map +1 -0
  103. package/dist/mail/mail-service.js +121 -0
  104. package/dist/mail/mail-service.js.map +1 -0
  105. package/dist/mail/stores/eventstore-conversation-store.d.ts +40 -0
  106. package/dist/mail/stores/eventstore-conversation-store.d.ts.map +1 -0
  107. package/dist/mail/stores/eventstore-conversation-store.js +131 -0
  108. package/dist/mail/stores/eventstore-conversation-store.js.map +1 -0
  109. package/dist/mail/stores/eventstore-participant-store.d.ts +43 -0
  110. package/dist/mail/stores/eventstore-participant-store.d.ts.map +1 -0
  111. package/dist/mail/stores/eventstore-participant-store.js +145 -0
  112. package/dist/mail/stores/eventstore-participant-store.js.map +1 -0
  113. package/dist/mail/stores/eventstore-thread-store.d.ts +46 -0
  114. package/dist/mail/stores/eventstore-thread-store.d.ts.map +1 -0
  115. package/dist/mail/stores/eventstore-thread-store.js +118 -0
  116. package/dist/mail/stores/eventstore-thread-store.js.map +1 -0
  117. package/dist/mail/stores/eventstore-turn-store.d.ts +47 -0
  118. package/dist/mail/stores/eventstore-turn-store.d.ts.map +1 -0
  119. package/dist/mail/stores/eventstore-turn-store.js +153 -0
  120. package/dist/mail/stores/eventstore-turn-store.js.map +1 -0
  121. package/dist/mail/stores/index.d.ts +12 -0
  122. package/dist/mail/stores/index.d.ts.map +1 -0
  123. package/dist/mail/stores/index.js +12 -0
  124. package/dist/mail/stores/index.js.map +1 -0
  125. package/dist/mail/stores/types.d.ts +146 -0
  126. package/dist/mail/stores/types.d.ts.map +1 -0
  127. package/dist/mail/stores/types.js +13 -0
  128. package/dist/mail/stores/types.js.map +1 -0
  129. package/dist/mail/turn-recorder.d.ts +30 -0
  130. package/dist/mail/turn-recorder.d.ts.map +1 -0
  131. package/dist/mail/turn-recorder.js +98 -0
  132. package/dist/mail/turn-recorder.js.map +1 -0
  133. package/dist/map/adapter/acp-over-map.d.ts.map +1 -1
  134. package/dist/map/adapter/acp-over-map.js +32 -2
  135. package/dist/map/adapter/acp-over-map.js.map +1 -1
  136. package/dist/map/adapter/event-translator.d.ts.map +1 -1
  137. package/dist/map/adapter/event-translator.js +4 -0
  138. package/dist/map/adapter/event-translator.js.map +1 -1
  139. package/dist/map/adapter/extensions/agent-detection.d.ts +49 -0
  140. package/dist/map/adapter/extensions/agent-detection.d.ts.map +1 -0
  141. package/dist/map/adapter/extensions/agent-detection.js +91 -0
  142. package/dist/map/adapter/extensions/agent-detection.js.map +1 -0
  143. package/dist/map/adapter/extensions/index.d.ts +10 -1
  144. package/dist/map/adapter/extensions/index.d.ts.map +1 -1
  145. package/dist/map/adapter/extensions/index.js +39 -0
  146. package/dist/map/adapter/extensions/index.js.map +1 -1
  147. package/dist/map/adapter/extensions/resume.d.ts +47 -0
  148. package/dist/map/adapter/extensions/resume.d.ts.map +1 -0
  149. package/dist/map/adapter/extensions/resume.js +59 -0
  150. package/dist/map/adapter/extensions/resume.js.map +1 -0
  151. package/dist/map/adapter/extensions/workspace-files.d.ts +42 -0
  152. package/dist/map/adapter/extensions/workspace-files.d.ts.map +1 -0
  153. package/dist/map/adapter/extensions/workspace-files.js +338 -0
  154. package/dist/map/adapter/extensions/workspace-files.js.map +1 -0
  155. package/dist/map/adapter/mail-handler-adapter.d.ts +27 -0
  156. package/dist/map/adapter/mail-handler-adapter.d.ts.map +1 -0
  157. package/dist/map/adapter/mail-handler-adapter.js +292 -0
  158. package/dist/map/adapter/mail-handler-adapter.js.map +1 -0
  159. package/dist/map/adapter/map-adapter.d.ts +34 -10
  160. package/dist/map/adapter/map-adapter.d.ts.map +1 -1
  161. package/dist/map/adapter/map-adapter.js +110 -14
  162. package/dist/map/adapter/map-adapter.js.map +1 -1
  163. package/dist/map/adapter/rpc-handler.d.ts +4 -1
  164. package/dist/map/adapter/rpc-handler.d.ts.map +1 -1
  165. package/dist/map/adapter/rpc-handler.js +6 -0
  166. package/dist/map/adapter/rpc-handler.js.map +1 -1
  167. package/dist/map/index.d.ts +1 -0
  168. package/dist/map/index.d.ts.map +1 -1
  169. package/dist/map/index.js +2 -0
  170. package/dist/map/index.js.map +1 -1
  171. package/dist/map/types.d.ts +3 -1
  172. package/dist/map/types.d.ts.map +1 -1
  173. package/dist/map/types.js.map +1 -1
  174. package/dist/mcp/mcp-server.d.ts +6 -0
  175. package/dist/mcp/mcp-server.d.ts.map +1 -1
  176. package/dist/mcp/mcp-server.js +45 -0
  177. package/dist/mcp/mcp-server.js.map +1 -1
  178. package/dist/mcp/tools/claim_task.d.ts +35 -0
  179. package/dist/mcp/tools/claim_task.d.ts.map +1 -0
  180. package/dist/mcp/tools/claim_task.js +58 -0
  181. package/dist/mcp/tools/claim_task.js.map +1 -0
  182. package/dist/mcp/tools/done.d.ts +15 -2
  183. package/dist/mcp/tools/done.d.ts.map +1 -1
  184. package/dist/mcp/tools/done.js +45 -10
  185. package/dist/mcp/tools/done.js.map +1 -1
  186. package/dist/mcp/tools/list_claimable_tasks.d.ts +38 -0
  187. package/dist/mcp/tools/list_claimable_tasks.d.ts.map +1 -0
  188. package/dist/mcp/tools/list_claimable_tasks.js +63 -0
  189. package/dist/mcp/tools/list_claimable_tasks.js.map +1 -0
  190. package/dist/mcp/tools/unclaim_task.d.ts +31 -0
  191. package/dist/mcp/tools/unclaim_task.d.ts.map +1 -0
  192. package/dist/mcp/tools/unclaim_task.js +47 -0
  193. package/dist/mcp/tools/unclaim_task.js.map +1 -0
  194. package/dist/metrics/index.d.ts +2 -0
  195. package/dist/metrics/index.d.ts.map +1 -0
  196. package/dist/metrics/index.js +2 -0
  197. package/dist/metrics/index.js.map +1 -0
  198. package/dist/metrics/metrics.d.ts +79 -0
  199. package/dist/metrics/metrics.d.ts.map +1 -0
  200. package/dist/metrics/metrics.js +166 -0
  201. package/dist/metrics/metrics.js.map +1 -0
  202. package/dist/roles/capabilities.d.ts +1 -0
  203. package/dist/roles/capabilities.d.ts.map +1 -1
  204. package/dist/roles/capabilities.js +3 -0
  205. package/dist/roles/capabilities.js.map +1 -1
  206. package/dist/roles/types.d.ts +1 -1
  207. package/dist/roles/types.d.ts.map +1 -1
  208. package/dist/router/channels.d.ts +2 -4
  209. package/dist/router/channels.d.ts.map +1 -1
  210. package/dist/router/channels.js.map +1 -1
  211. package/dist/router/message-router.d.ts +85 -9
  212. package/dist/router/message-router.d.ts.map +1 -1
  213. package/dist/router/message-router.js +203 -14
  214. package/dist/router/message-router.js.map +1 -1
  215. package/dist/router/role-resolver.d.ts +10 -1
  216. package/dist/router/role-resolver.d.ts.map +1 -1
  217. package/dist/router/role-resolver.js +15 -1
  218. package/dist/router/role-resolver.js.map +1 -1
  219. package/dist/router/types.d.ts +30 -1
  220. package/dist/router/types.d.ts.map +1 -1
  221. package/dist/router/types.js.map +1 -1
  222. package/dist/server/combined-server.d.ts +6 -0
  223. package/dist/server/combined-server.d.ts.map +1 -1
  224. package/dist/server/combined-server.js +24 -2
  225. package/dist/server/combined-server.js.map +1 -1
  226. package/dist/store/event-store.d.ts +14 -1
  227. package/dist/store/event-store.d.ts.map +1 -1
  228. package/dist/store/event-store.js +456 -4
  229. package/dist/store/event-store.js.map +1 -1
  230. package/dist/store/types/agents.d.ts +1 -1
  231. package/dist/store/types/agents.d.ts.map +1 -1
  232. package/dist/store/types/conversations.d.ts +91 -0
  233. package/dist/store/types/conversations.d.ts.map +1 -0
  234. package/dist/store/types/conversations.js +8 -0
  235. package/dist/store/types/conversations.js.map +1 -0
  236. package/dist/store/types/events.d.ts +1 -1
  237. package/dist/store/types/events.d.ts.map +1 -1
  238. package/dist/store/types/events.js.map +1 -1
  239. package/dist/store/types/index.d.ts +2 -0
  240. package/dist/store/types/index.d.ts.map +1 -1
  241. package/dist/store/types/index.js +2 -0
  242. package/dist/store/types/index.js.map +1 -1
  243. package/dist/store/types/sessions.d.ts +44 -0
  244. package/dist/store/types/sessions.d.ts.map +1 -0
  245. package/dist/store/types/sessions.js +9 -0
  246. package/dist/store/types/sessions.js.map +1 -0
  247. package/dist/store/types/tasks.d.ts +2 -0
  248. package/dist/store/types/tasks.d.ts.map +1 -1
  249. package/dist/task/backend/memory.d.ts +4 -1
  250. package/dist/task/backend/memory.d.ts.map +1 -1
  251. package/dist/task/backend/memory.js +81 -0
  252. package/dist/task/backend/memory.js.map +1 -1
  253. package/dist/task/backend/types.d.ts +30 -0
  254. package/dist/task/backend/types.d.ts.map +1 -1
  255. package/dist/task/backend/types.js.map +1 -1
  256. package/dist/teams/index.d.ts +4 -0
  257. package/dist/teams/index.d.ts.map +1 -0
  258. package/dist/teams/index.js +4 -0
  259. package/dist/teams/index.js.map +1 -0
  260. package/dist/teams/team-loader.d.ts +20 -0
  261. package/dist/teams/team-loader.d.ts.map +1 -0
  262. package/dist/teams/team-loader.js +293 -0
  263. package/dist/teams/team-loader.js.map +1 -0
  264. package/dist/teams/team-runtime.d.ts +139 -0
  265. package/dist/teams/team-runtime.d.ts.map +1 -0
  266. package/dist/teams/team-runtime.js +613 -0
  267. package/dist/teams/team-runtime.js.map +1 -0
  268. package/dist/teams/types.d.ts +266 -0
  269. package/dist/teams/types.d.ts.map +1 -0
  270. package/dist/teams/types.js +20 -0
  271. package/dist/teams/types.js.map +1 -0
  272. package/dist/trigger/router/trigger-router.d.ts +30 -3
  273. package/dist/trigger/router/trigger-router.d.ts.map +1 -1
  274. package/dist/trigger/router/trigger-router.js +30 -3
  275. package/dist/trigger/router/trigger-router.js.map +1 -1
  276. package/dist/trigger/wake/types.d.ts +31 -5
  277. package/dist/trigger/wake/types.d.ts.map +1 -1
  278. package/dist/trigger/wake/types.js +19 -0
  279. package/dist/trigger/wake/types.js.map +1 -1
  280. package/dist/workspace/dataplane-adapter.d.ts +1 -1
  281. package/dist/workspace/dataplane-adapter.d.ts.map +1 -1
  282. package/dist/workspace/dataplane-adapter.js +1 -1
  283. package/dist/workspace/dataplane-adapter.js.map +1 -1
  284. package/dist/workspace/index.d.ts +1 -1
  285. package/dist/workspace/index.d.ts.map +1 -1
  286. package/dist/workspace/strategies/index.d.ts +6 -0
  287. package/dist/workspace/strategies/index.d.ts.map +1 -0
  288. package/dist/workspace/strategies/index.js +5 -0
  289. package/dist/workspace/strategies/index.js.map +1 -0
  290. package/dist/workspace/strategies/optimistic.d.ts +26 -0
  291. package/dist/workspace/strategies/optimistic.d.ts.map +1 -0
  292. package/dist/workspace/strategies/optimistic.js +121 -0
  293. package/dist/workspace/strategies/optimistic.js.map +1 -0
  294. package/dist/workspace/strategies/queue.d.ts +26 -0
  295. package/dist/workspace/strategies/queue.d.ts.map +1 -0
  296. package/dist/workspace/strategies/queue.js +67 -0
  297. package/dist/workspace/strategies/queue.js.map +1 -0
  298. package/dist/workspace/strategies/registry.d.ts +37 -0
  299. package/dist/workspace/strategies/registry.d.ts.map +1 -0
  300. package/dist/workspace/strategies/registry.js +63 -0
  301. package/dist/workspace/strategies/registry.js.map +1 -0
  302. package/dist/workspace/strategies/trunk.d.ts +20 -0
  303. package/dist/workspace/strategies/trunk.d.ts.map +1 -0
  304. package/dist/workspace/strategies/trunk.js +108 -0
  305. package/dist/workspace/strategies/trunk.js.map +1 -0
  306. package/dist/workspace/strategies/types.d.ts +104 -0
  307. package/dist/workspace/strategies/types.d.ts.map +1 -0
  308. package/dist/workspace/strategies/types.js +11 -0
  309. package/dist/workspace/strategies/types.js.map +1 -0
  310. package/dist/workspace/types.d.ts +1 -1
  311. package/dist/workspace/types.d.ts.map +1 -1
  312. package/dist/workspace/workspace-manager.d.ts +1 -1
  313. package/dist/workspace/workspace-manager.d.ts.map +1 -1
  314. package/docs/implementation-details.md +1127 -0
  315. package/docs/implementation-summary.md +448 -0
  316. package/docs/mail-integration.md +608 -0
  317. package/docs/plan-self-driving-support.md +433 -0
  318. package/docs/spec-self-driving-support.md +462 -0
  319. package/docs/team-templates.md +860 -0
  320. package/docs/teams.md +233 -0
  321. package/package.json +5 -3
  322. package/src/acp/__tests__/integration.test.ts +161 -1
  323. package/src/acp/__tests__/macro-agent.test.ts +95 -0
  324. package/src/acp/__tests__/session-persistence.test.ts +276 -0
  325. package/src/acp/macro-agent.ts +79 -7
  326. package/src/acp/session-mapper.ts +108 -1
  327. package/src/acp/types.ts +33 -1
  328. package/src/agent/agent-manager.ts +278 -6
  329. package/src/agent/types.ts +27 -0
  330. package/src/agent/wake.ts +15 -0
  331. package/src/agent-detection/__tests__/command-builder.test.ts +336 -0
  332. package/src/agent-detection/__tests__/detector.test.ts +768 -0
  333. package/src/agent-detection/__tests__/registry.test.ts +254 -0
  334. package/src/agent-detection/command-builder.ts +90 -0
  335. package/src/agent-detection/detector.ts +307 -0
  336. package/src/agent-detection/index.ts +36 -0
  337. package/src/agent-detection/registry.ts +200 -0
  338. package/src/agent-detection/types.ts +184 -0
  339. package/src/api/__tests__/conversation-api.test.ts +468 -0
  340. package/src/api/server.ts +425 -1
  341. package/src/api/types.ts +64 -1
  342. package/src/cli/acp.ts +9 -1
  343. package/src/cli/index.ts +44 -0
  344. package/src/cli/mcp.ts +47 -0
  345. package/src/config/index.ts +9 -0
  346. package/src/config/project-config.ts +107 -0
  347. package/src/lifecycle/cascade.ts +1 -1
  348. package/src/lifecycle/handlers/index.ts +8 -0
  349. package/src/lifecycle/handlers/worker.ts +48 -3
  350. package/src/mail/__tests__/conversation-lifecycle.test.ts +409 -0
  351. package/src/mail/__tests__/eventstore-stores.test.ts +1073 -0
  352. package/src/mail/__tests__/mail-full-agent.e2e.test.ts +575 -0
  353. package/src/mail/__tests__/mail-integration.test.ts +759 -0
  354. package/src/mail/__tests__/mail-map-protocol.e2e.test.ts +1068 -0
  355. package/src/mail/__tests__/mail-service.test.ts +506 -0
  356. package/src/mail/__tests__/turn-recorder.test.ts +328 -0
  357. package/src/mail/conversation-map.ts +107 -0
  358. package/src/mail/index.ts +25 -0
  359. package/src/mail/mail-service.ts +257 -0
  360. package/src/mail/stores/eventstore-conversation-store.ts +146 -0
  361. package/src/mail/stores/eventstore-participant-store.ts +172 -0
  362. package/src/mail/stores/eventstore-thread-store.ts +129 -0
  363. package/src/mail/stores/eventstore-turn-store.ts +173 -0
  364. package/src/mail/stores/index.ts +12 -0
  365. package/src/mail/stores/types.ts +160 -0
  366. package/src/mail/turn-recorder.ts +124 -0
  367. package/src/map/README.md +79 -0
  368. package/src/map/adapter/__tests__/extensions.test.ts +359 -0
  369. package/src/map/adapter/__tests__/map-adapter.test.ts +90 -0
  370. package/src/map/adapter/__tests__/workspace-files.test.ts +673 -0
  371. package/src/map/adapter/acp-over-map.ts +45 -2
  372. package/src/map/adapter/event-translator.ts +4 -0
  373. package/src/map/adapter/extensions/agent-detection.ts +201 -0
  374. package/src/map/adapter/extensions/index.ts +63 -0
  375. package/src/map/adapter/extensions/resume.ts +114 -0
  376. package/src/map/adapter/extensions/workspace-files.ts +449 -0
  377. package/src/map/adapter/mail-handler-adapter.ts +429 -0
  378. package/src/map/adapter/map-adapter.ts +173 -27
  379. package/src/map/adapter/rpc-handler.ts +8 -1
  380. package/src/map/index.ts +3 -0
  381. package/src/map/types.ts +3 -1
  382. package/src/mcp/mcp-server.ts +67 -0
  383. package/src/mcp/tools/claim_task.ts +86 -0
  384. package/src/mcp/tools/done.ts +59 -10
  385. package/src/mcp/tools/list_claimable_tasks.ts +93 -0
  386. package/src/mcp/tools/unclaim_task.ts +71 -0
  387. package/src/metrics/index.ts +9 -0
  388. package/src/metrics/metrics.ts +280 -0
  389. package/src/roles/capabilities.ts +3 -0
  390. package/src/roles/types.ts +2 -1
  391. package/src/router/README.md +120 -0
  392. package/src/router/__tests__/message-router.test.ts +561 -0
  393. package/src/router/channels.ts +3 -4
  394. package/src/router/message-router.ts +308 -22
  395. package/src/router/role-resolver.ts +22 -1
  396. package/src/router/types.ts +36 -1
  397. package/src/server/combined-server.ts +36 -2
  398. package/src/store/README.md +134 -0
  399. package/src/store/event-store.ts +546 -3
  400. package/src/store/types/agents.ts +1 -1
  401. package/src/store/types/conversations.ts +129 -0
  402. package/src/store/types/events.ts +5 -1
  403. package/src/store/types/index.ts +2 -0
  404. package/src/store/types/sessions.ts +53 -0
  405. package/src/store/types/tasks.ts +3 -0
  406. package/src/task/backend/memory.ts +116 -0
  407. package/src/task/backend/types.ts +43 -0
  408. package/src/teams/__tests__/cross-subsystem.integration.test.ts +983 -0
  409. package/src/teams/__tests__/e2e/team-runtime.e2e.test.ts +553 -0
  410. package/src/teams/__tests__/team-system.test.ts +1280 -0
  411. package/src/teams/index.ts +13 -0
  412. package/src/teams/team-loader.ts +434 -0
  413. package/src/teams/team-runtime.ts +727 -0
  414. package/src/teams/types.ts +377 -0
  415. package/src/trigger/router/trigger-router.ts +30 -3
  416. package/src/trigger/wake/types.ts +32 -5
  417. package/src/trigger/wake/wake-manager.ts +2 -2
  418. package/src/workspace/dataplane-adapter.ts +1 -1
  419. package/src/workspace/index.ts +1 -1
  420. package/src/workspace/strategies/index.ts +18 -0
  421. package/src/workspace/strategies/optimistic.ts +136 -0
  422. package/src/workspace/strategies/queue.ts +81 -0
  423. package/src/workspace/strategies/registry.ts +89 -0
  424. package/src/workspace/strategies/trunk.ts +123 -0
  425. package/src/workspace/strategies/types.ts +145 -0
  426. package/src/workspace/types.ts +1 -1
  427. package/src/workspace/workspace-manager.ts +1 -1
  428. package/.claude/settings.local.json +0 -59
  429. package/dist/map/utils/address-translation.d.ts +0 -99
  430. package/dist/map/utils/address-translation.d.ts.map +0 -1
  431. package/dist/map/utils/address-translation.js +0 -285
  432. package/dist/map/utils/address-translation.js.map +0 -1
  433. package/dist/map/utils/index.d.ts +0 -7
  434. package/dist/map/utils/index.d.ts.map +0 -1
  435. package/dist/map/utils/index.js +0 -7
  436. package/dist/map/utils/index.js.map +0 -1
  437. package/openspec/AGENTS.md +0 -456
  438. package/openspec/changes/archive/2025-12-21-add-mvp-foundation/design.md +0 -128
  439. package/openspec/changes/archive/2025-12-21-add-mvp-foundation/proposal.md +0 -49
  440. package/openspec/changes/archive/2025-12-21-add-mvp-foundation/specs/agent-manager/spec.md +0 -150
  441. package/openspec/changes/archive/2025-12-21-add-mvp-foundation/specs/cli-api/spec.md +0 -258
  442. package/openspec/changes/archive/2025-12-21-add-mvp-foundation/specs/event-store/spec.md +0 -160
  443. package/openspec/changes/archive/2025-12-21-add-mvp-foundation/specs/mcp-tools/spec.md +0 -224
  444. package/openspec/changes/archive/2025-12-21-add-mvp-foundation/specs/message-router/spec.md +0 -153
  445. package/openspec/changes/archive/2025-12-21-add-mvp-foundation/specs/task-manager/spec.md +0 -136
  446. package/openspec/changes/archive/2025-12-21-add-mvp-foundation/tasks.md +0 -147
  447. package/openspec/project.md +0 -31
  448. package/openspec/specs/agent-manager/spec.md +0 -154
  449. package/openspec/specs/cli-api/spec.md +0 -262
  450. package/openspec/specs/event-store/spec.md +0 -164
  451. package/openspec/specs/mcp-tools/spec.md +0 -228
  452. package/openspec/specs/message-router/spec.md +0 -157
  453. package/openspec/specs/task-manager/spec.md +0 -140
  454. package/references/acp-factory-ref/CHANGELOG.md +0 -33
  455. package/references/acp-factory-ref/LICENSE +0 -21
  456. package/references/acp-factory-ref/README.md +0 -341
  457. package/references/acp-factory-ref/package-lock.json +0 -3102
  458. package/references/acp-factory-ref/package.json +0 -96
  459. package/references/acp-factory-ref/python/CHANGELOG.md +0 -33
  460. package/references/acp-factory-ref/python/LICENSE +0 -21
  461. package/references/acp-factory-ref/python/Makefile +0 -57
  462. package/references/acp-factory-ref/python/README.md +0 -253
  463. package/references/acp-factory-ref/python/pyproject.toml +0 -73
  464. package/references/acp-factory-ref/python/tests/__init__.py +0 -0
  465. package/references/acp-factory-ref/python/tests/e2e/__init__.py +0 -1
  466. package/references/acp-factory-ref/python/tests/e2e/test_codex_e2e.py +0 -349
  467. package/references/acp-factory-ref/python/tests/e2e/test_gemini_e2e.py +0 -165
  468. package/references/acp-factory-ref/python/tests/e2e/test_opencode_e2e.py +0 -296
  469. package/references/acp-factory-ref/python/tests/test_client_handler.py +0 -543
  470. package/references/acp-factory-ref/python/tests/test_pushable.py +0 -199
  471. package/references/claude-code-acp/.github/workflows/ci.yml +0 -45
  472. package/references/claude-code-acp/.github/workflows/publish.yml +0 -34
  473. package/references/claude-code-acp/.prettierrc.json +0 -4
  474. package/references/claude-code-acp/CHANGELOG.md +0 -249
  475. package/references/claude-code-acp/LICENSE +0 -222
  476. package/references/claude-code-acp/README.md +0 -53
  477. package/references/claude-code-acp/docs/RELEASES.md +0 -24
  478. package/references/claude-code-acp/eslint.config.js +0 -48
  479. package/references/claude-code-acp/package-lock.json +0 -4570
  480. package/references/claude-code-acp/package.json +0 -88
  481. package/references/claude-code-acp/scripts/release.sh +0 -119
  482. package/references/claude-code-acp/src/acp-agent.ts +0 -2065
  483. package/references/claude-code-acp/src/index.ts +0 -26
  484. package/references/claude-code-acp/src/lib.ts +0 -38
  485. package/references/claude-code-acp/src/mcp-server.ts +0 -911
  486. package/references/claude-code-acp/src/settings.ts +0 -522
  487. package/references/claude-code-acp/src/tests/.claude/commands/quick-math.md +0 -5
  488. package/references/claude-code-acp/src/tests/.claude/commands/say-hello.md +0 -6
  489. package/references/claude-code-acp/src/tests/acp-agent-fork.test.ts +0 -479
  490. package/references/claude-code-acp/src/tests/acp-agent.test.ts +0 -1502
  491. package/references/claude-code-acp/src/tests/extract-lines.test.ts +0 -103
  492. package/references/claude-code-acp/src/tests/fork-session.test.ts +0 -335
  493. package/references/claude-code-acp/src/tests/replace-and-calculate-location.test.ts +0 -334
  494. package/references/claude-code-acp/src/tests/settings.test.ts +0 -617
  495. package/references/claude-code-acp/src/tests/skills-options.test.ts +0 -187
  496. package/references/claude-code-acp/src/tests/tools.test.ts +0 -318
  497. package/references/claude-code-acp/src/tests/typescript-declarations.test.ts +0 -558
  498. package/references/claude-code-acp/src/tools.ts +0 -819
  499. package/references/claude-code-acp/src/utils.ts +0 -171
  500. package/references/claude-code-acp/tsconfig.json +0 -18
  501. package/references/claude-code-acp/vitest.config.ts +0 -19
  502. package/references/multi-agent-protocol/.sudocode/issues.jsonl +0 -82
  503. package/references/multi-agent-protocol/.sudocode/specs.jsonl +0 -9
  504. package/references/multi-agent-protocol/LICENSE +0 -21
  505. package/references/multi-agent-protocol/README.md +0 -113
  506. package/references/multi-agent-protocol/docs/00-design-specification.md +0 -460
  507. package/references/multi-agent-protocol/docs/01-open-questions.md +0 -1050
  508. package/references/multi-agent-protocol/docs/02-wire-protocol.md +0 -296
  509. package/references/multi-agent-protocol/docs/03-streaming-semantics.md +0 -252
  510. package/references/multi-agent-protocol/docs/04-error-handling.md +0 -231
  511. package/references/multi-agent-protocol/docs/05-connection-model.md +0 -244
  512. package/references/multi-agent-protocol/docs/06-visibility-permissions.md +0 -243
  513. package/references/multi-agent-protocol/docs/07-federation.md +0 -259
  514. package/references/multi-agent-protocol/docs/08-macro-agent-migration.md +0 -253
  515. package/references/multi-agent-protocol/package-lock.json +0 -3239
  516. package/references/multi-agent-protocol/package.json +0 -56
  517. package/references/multi-agent-protocol/schema/meta.json +0 -337
  518. package/references/multi-agent-protocol/schema/schema.json +0 -1828
@@ -1,1502 +0,0 @@
1
- import { describe, it, expect, beforeAll, afterAll, beforeEach, afterEach } from "vitest";
2
- import { spawn, spawnSync } from "child_process";
3
- import {
4
- Agent,
5
- AgentSideConnection,
6
- AvailableCommand,
7
- Client,
8
- ClientSideConnection,
9
- ndJsonStream,
10
- NewSessionResponse,
11
- ReadTextFileRequest,
12
- ReadTextFileResponse,
13
- RequestPermissionRequest,
14
- RequestPermissionResponse,
15
- SessionNotification,
16
- WriteTextFileRequest,
17
- WriteTextFileResponse,
18
- } from "@agentclientprotocol/sdk";
19
- import { nodeToWebWritable, nodeToWebReadable } from "../utils.js";
20
- import { markdownEscape, toolInfoFromToolUse, toolUpdateFromToolResult } from "../tools.js";
21
- import { toAcpNotifications, promptToClaude } from "../acp-agent.js";
22
- import { query, SDKAssistantMessage } from "@anthropic-ai/claude-agent-sdk";
23
- import { randomUUID } from "crypto";
24
- import type {
25
- BetaToolResultBlockParam,
26
- BetaToolSearchToolResultBlockParam,
27
- BetaWebSearchToolResultBlockParam,
28
- BetaWebFetchToolResultBlockParam,
29
- BetaCodeExecutionToolResultBlockParam,
30
- } from "@anthropic-ai/sdk/resources/beta.mjs";
31
-
32
- describe.skipIf(!process.env.RUN_INTEGRATION_TESTS)("ACP subprocess integration", () => {
33
- let child: ReturnType<typeof spawn>;
34
-
35
- beforeAll(async () => {
36
- const valid = spawnSync("tsc", { stdio: "inherit" });
37
- if (valid.status) {
38
- throw new Error("failed to compile");
39
- }
40
- // Start the subprocess
41
- child = spawn("npm", ["run", "--silent", "dev"], {
42
- stdio: ["pipe", "pipe", "inherit"],
43
- env: process.env,
44
- });
45
- child.on("error", (error) => {
46
- console.error("Error starting subprocess:", error);
47
- });
48
- child.on("exit", (exit) => {
49
- console.error("Exited with", exit);
50
- });
51
- });
52
-
53
- afterAll(() => {
54
- child.kill();
55
- });
56
-
57
- class TestClient implements Client {
58
- agent: Agent;
59
- files: Map<string, string> = new Map();
60
- receivedText: string = "";
61
- resolveAvailableCommands: (commands: AvailableCommand[]) => void;
62
- availableCommandsPromise: Promise<AvailableCommand[]>;
63
-
64
- constructor(agent: Agent) {
65
- this.agent = agent;
66
- this.resolveAvailableCommands = () => {};
67
- this.availableCommandsPromise = new Promise((resolve) => {
68
- this.resolveAvailableCommands = resolve;
69
- });
70
- }
71
-
72
- takeReceivedText() {
73
- const text = this.receivedText;
74
- this.receivedText = "";
75
- return text;
76
- }
77
-
78
- async requestPermission(params: RequestPermissionRequest): Promise<RequestPermissionResponse> {
79
- const optionId = params.options.find((p) => p.kind === "allow_once")!.optionId;
80
-
81
- return { outcome: { outcome: "selected", optionId } };
82
- }
83
-
84
- async sessionUpdate(params: SessionNotification): Promise<void> {
85
- console.error("RECEIVED", JSON.stringify(params, null, 4));
86
-
87
- switch (params.update.sessionUpdate) {
88
- case "agent_message_chunk": {
89
- if (params.update.content.type === "text") {
90
- this.receivedText += params.update.content.text;
91
- }
92
- break;
93
- }
94
- case "available_commands_update":
95
- this.resolveAvailableCommands(params.update.availableCommands);
96
- break;
97
- default:
98
- break;
99
- }
100
- }
101
-
102
- async writeTextFile(params: WriteTextFileRequest): Promise<WriteTextFileResponse> {
103
- this.files.set(params.path, params.content);
104
- return {};
105
- }
106
-
107
- async readTextFile(params: ReadTextFileRequest): Promise<ReadTextFileResponse> {
108
- const content = this.files.get(params.path) ?? "";
109
- return {
110
- content,
111
- };
112
- }
113
- }
114
-
115
- async function setupTestSession(cwd: string): Promise<{
116
- client: TestClient;
117
- connection: ClientSideConnection;
118
- newSessionResponse: NewSessionResponse;
119
- }> {
120
- let client;
121
- const input = nodeToWebWritable(child.stdin!);
122
- const output = nodeToWebReadable(child.stdout!);
123
- const stream = ndJsonStream(input, output);
124
- const connection = new ClientSideConnection((agent) => {
125
- client = new TestClient(agent);
126
- return client;
127
- }, stream);
128
-
129
- await connection.initialize({
130
- protocolVersion: 1,
131
- clientCapabilities: {
132
- fs: {
133
- readTextFile: true,
134
- writeTextFile: true,
135
- },
136
- },
137
- });
138
-
139
- const newSessionResponse = await connection.newSession({
140
- cwd,
141
- mcpServers: [],
142
- });
143
-
144
- return { client: client!, connection, newSessionResponse };
145
- }
146
-
147
- it("should connect to the ACP subprocess", async () => {
148
- const { client, connection, newSessionResponse } = await setupTestSession("./");
149
-
150
- await connection.prompt({
151
- prompt: [
152
- {
153
- type: "text",
154
- text: "Hello",
155
- },
156
- ],
157
- sessionId: newSessionResponse.sessionId,
158
- });
159
-
160
- expect(client.takeReceivedText()).not.toEqual("");
161
- }, 30000);
162
-
163
- it("should include available commands", async () => {
164
- const { client, connection, newSessionResponse } = await setupTestSession(__dirname);
165
-
166
- const commands = await client.availableCommandsPromise;
167
-
168
- expect(commands).toContainEqual({
169
- name: "quick-math",
170
- description: "10 * 3 = 30 (project)",
171
- input: null,
172
- });
173
- expect(commands).toContainEqual({
174
- name: "say-hello",
175
- description: "Say hello (project)",
176
- input: { hint: "name" },
177
- });
178
-
179
- await connection.prompt({
180
- prompt: [
181
- {
182
- type: "text",
183
- text: "/quick-math",
184
- },
185
- ],
186
- sessionId: newSessionResponse.sessionId,
187
- });
188
-
189
- expect(client.takeReceivedText()).toContain("30");
190
-
191
- await connection.prompt({
192
- prompt: [
193
- {
194
- type: "text",
195
- text: "/say-hello GPT-5",
196
- },
197
- ],
198
- sessionId: newSessionResponse.sessionId,
199
- });
200
-
201
- expect(client.takeReceivedText()).toContain("Hello GPT-5");
202
- }, 30000);
203
-
204
- it("/compact works", async () => {
205
- const { client, connection, newSessionResponse } = await setupTestSession(__dirname);
206
-
207
- const commands = await client.availableCommandsPromise;
208
-
209
- expect(commands).toContainEqual({
210
- description:
211
- "Clear conversation history but keep a summary in context. Optional: /compact [instructions for summarization]",
212
- input: {
213
- hint: "<optional custom summarization instructions>",
214
- },
215
- name: "compact",
216
- });
217
-
218
- // Error case (no previous message)
219
- await connection.prompt({
220
- prompt: [{ type: "text", text: "/compact" }],
221
- sessionId: newSessionResponse.sessionId,
222
- });
223
-
224
- expect(client.takeReceivedText()).toBe("");
225
-
226
- // Send something
227
- await connection.prompt({
228
- prompt: [{ type: "text", text: "Hi" }],
229
- sessionId: newSessionResponse.sessionId,
230
- });
231
- // Clear response
232
- client.takeReceivedText();
233
-
234
- // Test with instruction
235
- await connection.prompt({
236
- prompt: [
237
- {
238
- type: "text",
239
- text: "/compact greeting",
240
- },
241
- ],
242
- sessionId: newSessionResponse.sessionId,
243
- });
244
-
245
- expect(client.takeReceivedText()).toContain("");
246
- }, 120000);
247
- });
248
-
249
- describe("tool conversions", () => {
250
- it("should handle Bash nicely", () => {
251
- const tool_use = {
252
- type: "tool_use",
253
- id: "toolu_01VtsS2mxUFwpBJZYd7BmbC9",
254
- name: "Bash",
255
- input: {
256
- command: "rm README.md.rm",
257
- description: "Delete README.md.rm file",
258
- },
259
- };
260
-
261
- expect(toolInfoFromToolUse(tool_use)).toStrictEqual({
262
- kind: "execute",
263
- title: "`rm README.md.rm`",
264
- content: [
265
- {
266
- content: {
267
- text: "Delete README.md.rm file",
268
- type: "text",
269
- },
270
- type: "content",
271
- },
272
- ],
273
- });
274
- });
275
-
276
- it("should handle Glob nicely", () => {
277
- const tool_use = {
278
- type: "tool_use",
279
- id: "toolu_01VtsS2mxUFwpBJZYd7BmbC9",
280
- name: "Glob",
281
- input: {
282
- pattern: "*/**.ts",
283
- },
284
- };
285
-
286
- expect(toolInfoFromToolUse(tool_use)).toStrictEqual({
287
- kind: "search",
288
- title: "Find `*/**.ts`",
289
- content: [],
290
- locations: [],
291
- });
292
- });
293
-
294
- it("should handle Task tool calls", () => {
295
- const tool_use = {
296
- type: "tool_use",
297
- id: "toolu_01ANYHYDsXcDPKgxhg7us9bj",
298
- name: "Task",
299
- input: {
300
- description: "Handle user's work request",
301
- prompt:
302
- 'The user has asked me to "Create a Task to do the work!" but hasn\'t specified what specific work they want done. I need to:\n\n1. First understand what work needs to be done by examining the current state of the repository\n2. Look at the git status to see what files have been modified\n3. Check if there are any obvious tasks that need completion based on the current state\n4. If the work isn\'t clear from the context, ask the user to specify what work they want accomplished\n\nThe git status shows: "M src/tests/acp-agent.test.ts" - there\'s a modified test file that might need attention.\n\nPlease examine the repository state and determine what work needs to be done, then either complete it or ask the user for clarification on the specific task they want accomplished.',
303
- subagent_type: "general-purpose",
304
- },
305
- };
306
-
307
- expect(toolInfoFromToolUse(tool_use)).toStrictEqual({
308
- kind: "think",
309
- title: "Handle user's work request",
310
- content: [
311
- {
312
- content: {
313
- text: 'The user has asked me to "Create a Task to do the work!" but hasn\'t specified what specific work they want done. I need to:\n\n1. First understand what work needs to be done by examining the current state of the repository\n2. Look at the git status to see what files have been modified\n3. Check if there are any obvious tasks that need completion based on the current state\n4. If the work isn\'t clear from the context, ask the user to specify what work they want accomplished\n\nThe git status shows: "M src/tests/acp-agent.test.ts" - there\'s a modified test file that might need attention.\n\nPlease examine the repository state and determine what work needs to be done, then either complete it or ask the user for clarification on the specific task they want accomplished.',
314
- type: "text",
315
- },
316
- type: "content",
317
- },
318
- ],
319
- });
320
- });
321
-
322
- it("should handle LS tool calls", () => {
323
- const tool_use = {
324
- type: "tool_use",
325
- id: "toolu_01EEqsX7Eb9hpx87KAHVPTey",
326
- name: "LS",
327
- input: {
328
- path: "/Users/test/github/claude-code-acp",
329
- },
330
- };
331
-
332
- expect(toolInfoFromToolUse(tool_use)).toStrictEqual({
333
- kind: "search",
334
- title: "List the `/Users/test/github/claude-code-acp` directory's contents",
335
- content: [],
336
- locations: [],
337
- });
338
- });
339
-
340
- it("should handle Grep tool calls", () => {
341
- const tool_use = {
342
- type: "tool_use",
343
- id: "toolu_016j8oGSD3eAZ9KT62Y7Jsjb",
344
- name: "Grep",
345
- input: {
346
- pattern: ".*",
347
- },
348
- };
349
-
350
- expect(toolInfoFromToolUse(tool_use)).toStrictEqual({
351
- kind: "search",
352
- title: 'grep ".*"',
353
- content: [],
354
- });
355
- });
356
-
357
- it("should handle Write tool calls", () => {
358
- const tool_use = {
359
- type: "tool_use",
360
- id: "toolu_01ABC123XYZ789",
361
- name: "Write",
362
- input: {
363
- file_path: "/Users/test/project/example.txt",
364
- content: "Hello, World!\nThis is test content.",
365
- },
366
- };
367
-
368
- expect(toolInfoFromToolUse(tool_use)).toStrictEqual({
369
- kind: "edit",
370
- title: "Write /Users/test/project/example.txt",
371
- content: [
372
- {
373
- type: "diff",
374
- path: "/Users/test/project/example.txt",
375
- oldText: null,
376
- newText: "Hello, World!\nThis is test content.",
377
- },
378
- ],
379
- locations: [{ path: "/Users/test/project/example.txt" }],
380
- });
381
- });
382
-
383
- it("should handle mcp__acp__Write tool calls", () => {
384
- const tool_use = {
385
- type: "tool_use",
386
- id: "toolu_01GHI789JKL456",
387
- name: "mcp__acp__Write",
388
- input: {
389
- file_path: "/Users/test/project/config.json",
390
- content: '{"version": "1.0.0"}',
391
- },
392
- };
393
-
394
- expect(toolInfoFromToolUse(tool_use)).toStrictEqual({
395
- kind: "edit",
396
- title: "Write /Users/test/project/config.json",
397
- content: [
398
- {
399
- type: "diff",
400
- path: "/Users/test/project/config.json",
401
- oldText: null,
402
- newText: '{"version": "1.0.0"}',
403
- },
404
- ],
405
- locations: [{ path: "/Users/test/project/config.json" }],
406
- });
407
- });
408
-
409
- it("should handle Read tool calls", () => {
410
- const tool_use = {
411
- type: "tool_use",
412
- id: "toolu_01MNO456PQR789",
413
- name: "Read",
414
- input: {
415
- file_path: "/Users/test/project/readme.md",
416
- },
417
- };
418
-
419
- expect(toolInfoFromToolUse(tool_use)).toStrictEqual({
420
- kind: "read",
421
- title: "Read File",
422
- content: [],
423
- locations: [{ path: "/Users/test/project/readme.md", line: 0 }],
424
- });
425
- });
426
-
427
- it("should handle mcp__acp__Read tool calls", () => {
428
- const tool_use = {
429
- type: "tool_use",
430
- id: "toolu_01YZA789BCD123",
431
- name: "mcp__acp__Read",
432
- input: {
433
- file_path: "/Users/test/project/data.json",
434
- },
435
- };
436
-
437
- expect(toolInfoFromToolUse(tool_use)).toStrictEqual({
438
- kind: "read",
439
- title: "Read /Users/test/project/data.json",
440
- content: [],
441
- locations: [{ path: "/Users/test/project/data.json", line: 0 }],
442
- });
443
- });
444
-
445
- it("should handle mcp__acp__Read with limit", () => {
446
- const tool_use = {
447
- type: "tool_use",
448
- id: "toolu_01EFG456HIJ789",
449
- name: "mcp__acp__Read",
450
- input: {
451
- file_path: "/Users/test/project/large.txt",
452
- limit: 100,
453
- },
454
- };
455
-
456
- expect(toolInfoFromToolUse(tool_use)).toStrictEqual({
457
- kind: "read",
458
- title: "Read /Users/test/project/large.txt (1 - 100)",
459
- content: [],
460
- locations: [{ path: "/Users/test/project/large.txt", line: 0 }],
461
- });
462
- });
463
-
464
- it("should handle mcp__acp__Read with offset and limit", () => {
465
- const tool_use = {
466
- type: "tool_use",
467
- id: "toolu_01KLM789NOP456",
468
- name: "mcp__acp__Read",
469
- input: {
470
- file_path: "/Users/test/project/large.txt",
471
- offset: 50,
472
- limit: 100,
473
- },
474
- };
475
-
476
- expect(toolInfoFromToolUse(tool_use)).toStrictEqual({
477
- kind: "read",
478
- title: "Read /Users/test/project/large.txt (51 - 150)",
479
- content: [],
480
- locations: [{ path: "/Users/test/project/large.txt", line: 50 }],
481
- });
482
- });
483
-
484
- it("should handle mcp__acp__Read with only offset", () => {
485
- const tool_use = {
486
- type: "tool_use",
487
- id: "toolu_01QRS123TUV789",
488
- name: "mcp__acp__Read",
489
- input: {
490
- file_path: "/Users/test/project/large.txt",
491
- offset: 200,
492
- },
493
- };
494
-
495
- expect(toolInfoFromToolUse(tool_use)).toStrictEqual({
496
- kind: "read",
497
- title: "Read /Users/test/project/large.txt (from line 201)",
498
- content: [],
499
- locations: [{ path: "/Users/test/project/large.txt", line: 200 }],
500
- });
501
- });
502
-
503
- it("should handle KillBash entries", () => {
504
- const tool_use = {
505
- type: "tool_use",
506
- id: "toolu_01PhLms5fuvmdjy2bb6dfUKT",
507
- name: "KillShell",
508
- input: {
509
- shell_id: "bash_1",
510
- },
511
- };
512
-
513
- expect(toolInfoFromToolUse(tool_use)).toStrictEqual({
514
- kind: "execute",
515
- title: `Kill Process`,
516
- content: [],
517
- });
518
- });
519
-
520
- it("should handle BashOutput entries", () => {
521
- const tool_use = {
522
- type: "tool_use",
523
- id: "toolu_01SJUWPtj1QspgANgtpqGPuN",
524
- name: "BashOutput",
525
- input: {
526
- bash_id: "bash_1",
527
- },
528
- };
529
-
530
- expect(toolInfoFromToolUse(tool_use)).toStrictEqual({
531
- kind: "execute",
532
- title: `Tail Logs`,
533
- content: [],
534
- });
535
- });
536
-
537
- it("should handle plan entries", () => {
538
- const received: SDKAssistantMessage = {
539
- type: "assistant",
540
- message: {
541
- id: "msg_017eNosJgww7F5qD4a8BcAcx",
542
- type: "message",
543
- role: "assistant",
544
- container: null,
545
- model: "claude-sonnet-4-20250514",
546
- content: [
547
- {
548
- type: "tool_use",
549
- id: "toolu_01HaXZ4LfdchSeSR8ygt4zyq",
550
- name: "TodoWrite",
551
- input: {
552
- todos: [
553
- {
554
- content: "Analyze existing test coverage and identify gaps",
555
- status: "in_progress",
556
- activeForm: "Analyzing existing test coverage",
557
- },
558
- {
559
- content: "Add comprehensive edge case tests",
560
- status: "pending",
561
- activeForm: "Adding comprehensive edge case tests",
562
- },
563
- {
564
- content: "Add performance and timing tests",
565
- status: "pending",
566
- activeForm: "Adding performance and timing tests",
567
- },
568
- {
569
- content: "Add error handling and panic behavior tests",
570
- status: "pending",
571
- activeForm: "Adding error handling tests",
572
- },
573
- {
574
- content: "Add concurrent access and race condition tests",
575
- status: "pending",
576
- activeForm: "Adding concurrent access tests",
577
- },
578
- {
579
- content: "Add tests for Each function with various data types",
580
- status: "pending",
581
- activeForm: "Adding Each function tests",
582
- },
583
- {
584
- content: "Add benchmark tests for performance measurement",
585
- status: "pending",
586
- activeForm: "Adding benchmark tests",
587
- },
588
- {
589
- content: "Improve test organization and helper functions",
590
- status: "pending",
591
- activeForm: "Improving test organization",
592
- },
593
- ],
594
- },
595
- },
596
- ],
597
- stop_reason: null,
598
- stop_sequence: null,
599
- usage: {
600
- input_tokens: 6,
601
- cache_creation_input_tokens: 326,
602
- cache_read_input_tokens: 17265,
603
- cache_creation: {
604
- ephemeral_5m_input_tokens: 326,
605
- ephemeral_1h_input_tokens: 0,
606
- },
607
- output_tokens: 1,
608
- service_tier: "standard",
609
- server_tool_use: null,
610
- },
611
- context_management: null,
612
- },
613
- parent_tool_use_id: null,
614
- session_id: "d056596f-e328-41e9-badd-b07122ae5227",
615
- uuid: "b7c3330c-de8f-4bba-ac53-68c7f76ffeb5",
616
- };
617
- expect(
618
- toAcpNotifications(
619
- received.message.content,
620
- received.message.role,
621
- "test",
622
- {},
623
- {} as AgentSideConnection,
624
- console,
625
- ),
626
- ).toStrictEqual([
627
- {
628
- sessionId: "test",
629
- update: {
630
- sessionUpdate: "plan",
631
- entries: [
632
- {
633
- content: "Analyze existing test coverage and identify gaps",
634
- priority: "medium",
635
- status: "in_progress",
636
- },
637
- {
638
- content: "Add comprehensive edge case tests",
639
- priority: "medium",
640
- status: "pending",
641
- },
642
- {
643
- content: "Add performance and timing tests",
644
- priority: "medium",
645
- status: "pending",
646
- },
647
- {
648
- content: "Add error handling and panic behavior tests",
649
- priority: "medium",
650
- status: "pending",
651
- },
652
- {
653
- content: "Add concurrent access and race condition tests",
654
- priority: "medium",
655
- status: "pending",
656
- },
657
- {
658
- content: "Add tests for Each function with various data types",
659
- priority: "medium",
660
- status: "pending",
661
- },
662
- {
663
- content: "Add benchmark tests for performance measurement",
664
- priority: "medium",
665
- status: "pending",
666
- },
667
- {
668
- content: "Improve test organization and helper functions",
669
- priority: "medium",
670
- status: "pending",
671
- },
672
- ],
673
- },
674
- },
675
- ]);
676
- });
677
-
678
- it("should return empty update for successful edit result", () => {
679
- const toolUse = {
680
- type: "tool_use",
681
- id: "toolu_01MNO345",
682
- name: "mcp__acp__Edit",
683
- input: {
684
- file_path: "/Users/test/project/test.txt",
685
- old_string: "old",
686
- new_string: "new",
687
- },
688
- };
689
-
690
- const toolResult = {
691
- content: [
692
- {
693
- type: "text" as const,
694
- text: "not valid json",
695
- },
696
- ],
697
- tool_use_id: "test",
698
- is_error: false,
699
- type: "tool_result" as const,
700
- };
701
-
702
- const update = toolUpdateFromToolResult(toolResult, toolUse);
703
-
704
- // Should return empty object when parsing fails
705
- expect(update).toEqual({});
706
- });
707
-
708
- it("should return content update for edit failure", () => {
709
- const toolUse = {
710
- type: "tool_use",
711
- id: "toolu_01MNO345",
712
- name: "mcp__acp__Edit",
713
- input: {
714
- file_path: "/Users/test/project/test.txt",
715
- old_string: "old",
716
- new_string: "new",
717
- },
718
- };
719
-
720
- const toolResult = {
721
- content: [
722
- {
723
- type: "text" as const,
724
- text: "Failed to find `old_string`",
725
- },
726
- ],
727
- tool_use_id: "test",
728
- is_error: true,
729
- type: "tool_result" as const,
730
- };
731
-
732
- const update = toolUpdateFromToolResult(toolResult, toolUse);
733
-
734
- // Should return empty object when parsing fails
735
- expect(update).toEqual({
736
- content: [
737
- {
738
- content: { type: "text", text: "```\nFailed to find `old_string`\n```" },
739
- type: "content",
740
- },
741
- ],
742
- });
743
- });
744
-
745
- it("should transform tool_reference content to valid ACP content", () => {
746
- const toolUse = {
747
- type: "tool_use",
748
- id: "toolu_01MNO345",
749
- name: "ToolSearch",
750
- input: { query: "test" },
751
- };
752
-
753
- const toolResult: BetaToolResultBlockParam = {
754
- content: [
755
- {
756
- type: "tool_reference",
757
- tool_name: "some_discovered_tool",
758
- },
759
- ],
760
- tool_use_id: "toolu_01MNO345",
761
- is_error: false,
762
- type: "tool_result",
763
- };
764
-
765
- const update = toolUpdateFromToolResult(toolResult, toolUse);
766
-
767
- expect(update).toEqual({
768
- content: [
769
- {
770
- type: "content",
771
- content: { type: "text", text: "Tool: some_discovered_tool" },
772
- },
773
- ],
774
- });
775
- });
776
-
777
- it("should transform web_search_result content to valid ACP content", () => {
778
- const toolUse = {
779
- type: "tool_use",
780
- id: "toolu_01MNO345",
781
- name: "WebSearch",
782
- input: { query: "test" },
783
- };
784
-
785
- const toolResult: BetaWebSearchToolResultBlockParam = {
786
- content: [
787
- {
788
- type: "web_search_result",
789
- title: "Test Result",
790
- url: "https://example.com",
791
- encrypted_content: "...",
792
- page_age: null,
793
- },
794
- ],
795
- tool_use_id: "toolu_01MNO345",
796
- type: "web_search_tool_result",
797
- };
798
-
799
- const update = toolUpdateFromToolResult(toolResult, toolUse);
800
-
801
- expect(update).toEqual({
802
- content: [
803
- {
804
- type: "content",
805
- content: { type: "text", text: "Test Result (https://example.com)" },
806
- },
807
- ],
808
- });
809
- });
810
-
811
- it("should transform web_search_tool_result_error to valid ACP content", () => {
812
- const toolUse = {
813
- type: "tool_use",
814
- id: "toolu_01MNO345",
815
- name: "WebSearch",
816
- input: { query: "test" },
817
- };
818
-
819
- const toolResult: BetaWebSearchToolResultBlockParam = {
820
- content: {
821
- type: "web_search_tool_result_error",
822
- error_code: "unavailable",
823
- },
824
- tool_use_id: "toolu_01MNO345",
825
- type: "web_search_tool_result",
826
- };
827
-
828
- const update = toolUpdateFromToolResult(toolResult, toolUse);
829
-
830
- expect(update).toEqual({
831
- content: [
832
- {
833
- type: "content",
834
- content: { type: "text", text: "Error: unavailable" },
835
- },
836
- ],
837
- });
838
- });
839
-
840
- it("should transform code_execution_result content to valid ACP content", () => {
841
- const toolUse = {
842
- type: "tool_use",
843
- id: "toolu_01MNO345",
844
- name: "CodeExecution",
845
- input: {},
846
- };
847
-
848
- const toolResult: BetaCodeExecutionToolResultBlockParam = {
849
- content: {
850
- type: "code_execution_result",
851
- stdout: "Hello World",
852
- stderr: "",
853
- return_code: 0,
854
- content: [],
855
- },
856
- tool_use_id: "toolu_01MNO345",
857
- type: "code_execution_tool_result",
858
- };
859
-
860
- const update = toolUpdateFromToolResult(toolResult, toolUse);
861
-
862
- expect(update).toEqual({
863
- content: [
864
- {
865
- type: "content",
866
- content: { type: "text", text: "Output: Hello World" },
867
- },
868
- ],
869
- });
870
- });
871
-
872
- it("should transform web_fetch_result content to valid ACP content", () => {
873
- const toolUse = {
874
- type: "tool_use",
875
- id: "toolu_01MNO345",
876
- name: "WebFetch",
877
- input: { url: "https://example.com" },
878
- };
879
-
880
- const toolResult: BetaWebFetchToolResultBlockParam = {
881
- content: {
882
- type: "web_fetch_result",
883
- url: "https://example.com",
884
- content: {
885
- type: "document",
886
- citations: null,
887
- title: null,
888
- source: { type: "text", media_type: "text/plain", data: "Page content here" },
889
- },
890
- },
891
- tool_use_id: "toolu_01MNO345",
892
- type: "web_fetch_tool_result",
893
- };
894
-
895
- const update = toolUpdateFromToolResult(toolResult, toolUse);
896
-
897
- expect(update).toEqual({
898
- content: [
899
- {
900
- type: "content",
901
- content: { type: "text", text: "Fetched: https://example.com" },
902
- },
903
- ],
904
- });
905
- });
906
-
907
- it("should transform tool_search_tool_search_result to valid ACP content", () => {
908
- const toolUse = {
909
- type: "tool_use",
910
- id: "toolu_01MNO345",
911
- name: "ToolSearch",
912
- input: { query: "test" },
913
- };
914
-
915
- const toolResult: BetaToolSearchToolResultBlockParam = {
916
- content: {
917
- type: "tool_search_tool_search_result",
918
- tool_references: [
919
- { type: "tool_reference", tool_name: "tool_a" },
920
- { type: "tool_reference", tool_name: "tool_b" },
921
- ],
922
- },
923
- tool_use_id: "toolu_01MNO345",
924
- type: "tool_search_tool_result",
925
- };
926
-
927
- const update = toolUpdateFromToolResult(toolResult, toolUse);
928
-
929
- expect(update).toEqual({
930
- content: [
931
- {
932
- type: "content",
933
- content: { type: "text", text: "Tools found: tool_a, tool_b" },
934
- },
935
- ],
936
- });
937
- });
938
- });
939
-
940
- describe("escape markdown", () => {
941
- it("should escape markdown characters", () => {
942
- let text = "Hello *world*!";
943
- let escaped = markdownEscape(text);
944
- expect(escaped).toEqual("```\nHello *world*!\n```");
945
-
946
- text = "for example:\n```markdown\nHello *world*!\n```\n";
947
- escaped = markdownEscape(text);
948
- expect(escaped).toEqual("````\nfor example:\n```markdown\nHello *world*!\n```\n````");
949
- });
950
- });
951
-
952
- describe("prompt conversion", () => {
953
- it("should not change built-in slash commands", () => {
954
- const message = promptToClaude({
955
- sessionId: "test",
956
- prompt: [
957
- {
958
- type: "text",
959
- text: "/compact args",
960
- },
961
- ],
962
- });
963
- expect(message.message.content).toEqual([
964
- {
965
- text: "/compact args",
966
- type: "text",
967
- },
968
- ]);
969
- });
970
-
971
- it("should remove MCP prefix from MCP slash commands", () => {
972
- const message = promptToClaude({
973
- sessionId: "test",
974
- prompt: [
975
- {
976
- type: "text",
977
- text: "/mcp:server:name args",
978
- },
979
- ],
980
- });
981
- expect(message.message.content).toEqual([
982
- {
983
- text: "/server:name (MCP) args",
984
- type: "text",
985
- },
986
- ]);
987
- });
988
- });
989
-
990
- describe.skipIf(!process.env.RUN_INTEGRATION_TESTS)("SDK behavior", () => {
991
- it("query has a 'default' model", async () => {
992
- const q = query({ prompt: "hi" });
993
- const models = await q.supportedModels();
994
- const defaultModel = models.find((m) => m.value === "default");
995
- expect(defaultModel).toBeDefined();
996
- }, 10000);
997
-
998
- it("custom session id", async () => {
999
- const sessionId = randomUUID();
1000
- const q = query({
1001
- prompt: "hi",
1002
- options: {
1003
- systemPrompt: { type: "preset", preset: "claude_code" },
1004
- extraArgs: { "session-id": sessionId },
1005
- settingSources: ["user", "project", "local"],
1006
- includePartialMessages: true,
1007
- },
1008
- });
1009
-
1010
- // The SDK may send other events (like hook_started) before init
1011
- // Iterate to find the init event
1012
- let initEvent = null;
1013
- for await (const value of q) {
1014
- if (value.type === "system" && value.subtype === "init") {
1015
- initEvent = value;
1016
- break;
1017
- }
1018
- }
1019
- expect(initEvent).toMatchObject({ type: "system", subtype: "init", session_id: sessionId });
1020
- }, 10000);
1021
- });
1022
-
1023
- describe.skipIf(!process.env.RUN_INTEGRATION_TESTS)("_session/inject e2e", () => {
1024
- let child: ReturnType<typeof spawn>;
1025
-
1026
- beforeAll(async () => {
1027
- const valid = spawnSync("tsc", { stdio: "inherit" });
1028
- if (valid.status) {
1029
- throw new Error("failed to compile");
1030
- }
1031
- child = spawn("npm", ["run", "--silent", "dev"], {
1032
- stdio: ["pipe", "pipe", "inherit"],
1033
- env: process.env,
1034
- });
1035
- child.on("error", (error) => {
1036
- console.error("Error starting subprocess:", error);
1037
- });
1038
- });
1039
-
1040
- afterAll(() => {
1041
- child.kill();
1042
- });
1043
-
1044
- class InjectTestClient implements Client {
1045
- agent: Agent;
1046
- receivedText: string = "";
1047
- messageChunks: string[] = [];
1048
-
1049
- constructor(agent: Agent) {
1050
- this.agent = agent;
1051
- }
1052
-
1053
- takeReceivedText() {
1054
- const text = this.receivedText;
1055
- this.receivedText = "";
1056
- return text;
1057
- }
1058
-
1059
- async requestPermission(params: RequestPermissionRequest): Promise<RequestPermissionResponse> {
1060
- const optionId = params.options.find((p) => p.kind === "allow_once")!.optionId;
1061
- return { outcome: { outcome: "selected", optionId } };
1062
- }
1063
-
1064
- async sessionUpdate(params: SessionNotification): Promise<void> {
1065
- if (params.update.sessionUpdate === "agent_message_chunk") {
1066
- if (params.update.content.type === "text") {
1067
- this.receivedText += params.update.content.text;
1068
- this.messageChunks.push(params.update.content.text);
1069
- }
1070
- }
1071
- }
1072
-
1073
- async writeTextFile(params: WriteTextFileRequest): Promise<WriteTextFileResponse> {
1074
- return {};
1075
- }
1076
-
1077
- async readTextFile(params: ReadTextFileRequest): Promise<ReadTextFileResponse> {
1078
- return { content: "" };
1079
- }
1080
- }
1081
-
1082
- it("should inject message that is processed in next turn", async () => {
1083
- let client: InjectTestClient;
1084
- const input = nodeToWebWritable(child.stdin!);
1085
- const output = nodeToWebReadable(child.stdout!);
1086
- const stream = ndJsonStream(input, output);
1087
- const connection = new ClientSideConnection((agent) => {
1088
- client = new InjectTestClient(agent);
1089
- return client;
1090
- }, stream);
1091
-
1092
- await connection.initialize({
1093
- protocolVersion: 1,
1094
- clientCapabilities: {
1095
- fs: { readTextFile: true, writeTextFile: true },
1096
- },
1097
- });
1098
-
1099
- const { sessionId } = await connection.newSession({
1100
- cwd: "./",
1101
- mcpServers: [],
1102
- });
1103
-
1104
- // First prompt - simple greeting
1105
- await connection.prompt({
1106
- prompt: [{ type: "text", text: "Say hi." }],
1107
- sessionId,
1108
- });
1109
- client!.takeReceivedText(); // Clear first response
1110
-
1111
- // Inject a message into the session (this queues it for the next turn)
1112
- const injectResult = await connection.extMethod("_session/inject", {
1113
- sessionId,
1114
- message: "In your next response, include the word ELEPHANT.",
1115
- });
1116
- expect(injectResult).toEqual({ success: true });
1117
-
1118
- // Second prompt - the injected message should be processed first
1119
- await connection.prompt({
1120
- prompt: [{ type: "text", text: "What animal should you mention?" }],
1121
- sessionId,
1122
- });
1123
-
1124
- // The response should acknowledge the injected instruction
1125
- const responseText = client!.takeReceivedText().toUpperCase();
1126
- expect(responseText).toContain("ELEPHANT");
1127
- }, 60000);
1128
-
1129
- it("should inject ContentBlock array message for next turn", async () => {
1130
- let client: InjectTestClient;
1131
- const input = nodeToWebWritable(child.stdin!);
1132
- const output = nodeToWebReadable(child.stdout!);
1133
- const stream = ndJsonStream(input, output);
1134
- const connection = new ClientSideConnection((agent) => {
1135
- client = new InjectTestClient(agent);
1136
- return client;
1137
- }, stream);
1138
-
1139
- await connection.initialize({
1140
- protocolVersion: 1,
1141
- clientCapabilities: {
1142
- fs: { readTextFile: true, writeTextFile: true },
1143
- },
1144
- });
1145
-
1146
- const { sessionId } = await connection.newSession({
1147
- cwd: "./",
1148
- mcpServers: [],
1149
- });
1150
-
1151
- // First prompt
1152
- await connection.prompt({
1153
- prompt: [{ type: "text", text: "Say hi." }],
1154
- sessionId,
1155
- });
1156
- client!.takeReceivedText(); // Clear first response
1157
-
1158
- // Inject using ContentBlock array format
1159
- const injectResult = await connection.extMethod("_session/inject", {
1160
- sessionId,
1161
- message: [
1162
- { type: "text", text: "In your next response, include the word BANANA." },
1163
- ],
1164
- });
1165
- expect(injectResult).toEqual({ success: true });
1166
-
1167
- // Next prompt triggers processing of injected message
1168
- await connection.prompt({
1169
- prompt: [{ type: "text", text: "What fruit should you mention?" }],
1170
- sessionId,
1171
- });
1172
-
1173
- const responseText = client!.takeReceivedText().toUpperCase();
1174
- expect(responseText).toContain("BANANA");
1175
- }, 60000);
1176
-
1177
- it("should return error for non-existent session", async () => {
1178
- let client: InjectTestClient;
1179
- const input = nodeToWebWritable(child.stdin!);
1180
- const output = nodeToWebReadable(child.stdout!);
1181
- const stream = ndJsonStream(input, output);
1182
- const connection = new ClientSideConnection((agent) => {
1183
- client = new InjectTestClient(agent);
1184
- return client;
1185
- }, stream);
1186
-
1187
- await connection.initialize({
1188
- protocolVersion: 1,
1189
- clientCapabilities: {},
1190
- });
1191
-
1192
- const injectResult = await connection.extMethod("_session/inject", {
1193
- sessionId: "non-existent-session-id",
1194
- message: "test",
1195
- });
1196
-
1197
- expect(injectResult).toEqual({
1198
- success: false,
1199
- error: "Session non-existent-session-id not found",
1200
- });
1201
- }, 30000);
1202
- });
1203
-
1204
- describe("permission requests", () => {
1205
- it("should include title field in tool permission request structure", () => {
1206
- // Test various tool types to ensure title is correctly generated
1207
- const testCases = [
1208
- {
1209
- toolUse: {
1210
- type: "tool_use" as const,
1211
- id: "test-1",
1212
- name: "Write",
1213
- input: { file_path: "/test/file.txt", content: "test" },
1214
- },
1215
- expectedTitlePart: "/test/file.txt",
1216
- },
1217
- {
1218
- toolUse: {
1219
- type: "tool_use" as const,
1220
- id: "test-2",
1221
- name: "Bash",
1222
- input: { command: "ls -la", description: "List files" },
1223
- },
1224
- expectedTitlePart: "`ls -la`",
1225
- },
1226
- {
1227
- toolUse: {
1228
- type: "tool_use" as const,
1229
- id: "test-3",
1230
- name: "mcp__acp__Read",
1231
- input: { file_path: "/test/data.json" },
1232
- },
1233
- expectedTitlePart: "/test/data.json",
1234
- },
1235
- ];
1236
-
1237
- for (const testCase of testCases) {
1238
- // Get the tool info that would be used in requestPermission
1239
- const toolInfo = toolInfoFromToolUse(testCase.toolUse);
1240
-
1241
- // Verify toolInfo has a title
1242
- expect(toolInfo.title).toBeDefined();
1243
- expect(toolInfo.title).toContain(testCase.expectedTitlePart);
1244
-
1245
- // Verify the structure that our fix creates for requestPermission
1246
- const requestStructure = {
1247
- toolCall: {
1248
- toolCallId: testCase.toolUse.id,
1249
- rawInput: testCase.toolUse.input,
1250
- title: toolInfo.title, // This is what commit 1785d86 adds
1251
- },
1252
- };
1253
-
1254
- // Ensure the title field is present and populated
1255
- expect(requestStructure.toolCall.title).toBeDefined();
1256
- expect(requestStructure.toolCall.title).toContain(testCase.expectedTitlePart);
1257
- }
1258
- });
1259
- });
1260
-
1261
- describe("auto-compaction configuration", () => {
1262
- it("should accept compaction config in NewSessionMeta", () => {
1263
- // Test that the CompactionConfig type structure is correct
1264
- const meta: { claudeCode: { compaction: { enabled: boolean; contextTokenThreshold?: number; customInstructions?: string } } } = {
1265
- claudeCode: {
1266
- compaction: {
1267
- enabled: true,
1268
- contextTokenThreshold: 50000,
1269
- customInstructions: "Focus on the key decisions and outcomes",
1270
- },
1271
- },
1272
- };
1273
-
1274
- // Verify the structure
1275
- expect(meta.claudeCode.compaction.enabled).toBe(true);
1276
- expect(meta.claudeCode.compaction.contextTokenThreshold).toBe(50000);
1277
- expect(meta.claudeCode.compaction.customInstructions).toBe(
1278
- "Focus on the key decisions and outcomes",
1279
- );
1280
- });
1281
-
1282
- it("should have sensible defaults for compaction config", () => {
1283
- // When compaction is not configured, it should be disabled
1284
- const metaWithoutCompaction: { claudeCode: { compaction?: unknown } } = {
1285
- claudeCode: {},
1286
- };
1287
- expect(metaWithoutCompaction.claudeCode.compaction).toBeUndefined();
1288
-
1289
- // When compaction is enabled without threshold, default should be used
1290
- const metaWithDefaults: { claudeCode: { compaction: { enabled: boolean; contextTokenThreshold?: number } } } = {
1291
- claudeCode: {
1292
- compaction: {
1293
- enabled: true,
1294
- },
1295
- },
1296
- };
1297
- expect(metaWithDefaults.claudeCode.compaction.enabled).toBe(true);
1298
- expect(metaWithDefaults.claudeCode.compaction.contextTokenThreshold).toBeUndefined();
1299
- // The actual default (100000) is applied in createSession
1300
- });
1301
-
1302
- it("should support minimal compaction config with just enabled flag", () => {
1303
- const minimalConfig: { claudeCode: { compaction: { enabled: boolean } } } = {
1304
- claudeCode: {
1305
- compaction: {
1306
- enabled: false,
1307
- },
1308
- },
1309
- };
1310
-
1311
- expect(minimalConfig.claudeCode.compaction.enabled).toBe(false);
1312
- });
1313
- });
1314
-
1315
- describe("compaction event emission", () => {
1316
- it("compaction_started event should have correct structure", () => {
1317
- const event = {
1318
- sessionUpdate: "compaction_started",
1319
- sessionId: "test-session-123",
1320
- trigger: "auto" as const,
1321
- preTokens: 105000,
1322
- threshold: 100000,
1323
- };
1324
-
1325
- expect(event.sessionUpdate).toBe("compaction_started");
1326
- expect(event.sessionId).toBe("test-session-123");
1327
- expect(event.trigger).toBe("auto");
1328
- expect(event.preTokens).toBe(105000);
1329
- expect(event.threshold).toBe(100000);
1330
- });
1331
-
1332
- it("compaction_started event for manual trigger should not require threshold", () => {
1333
- const event: {
1334
- sessionUpdate: string;
1335
- sessionId: string;
1336
- trigger: "manual";
1337
- preTokens: number;
1338
- threshold?: number;
1339
- } = {
1340
- sessionUpdate: "compaction_started",
1341
- sessionId: "test-session-456",
1342
- trigger: "manual",
1343
- preTokens: 80000,
1344
- };
1345
-
1346
- expect(event.sessionUpdate).toBe("compaction_started");
1347
- expect(event.trigger).toBe("manual");
1348
- expect(event.threshold).toBeUndefined();
1349
- });
1350
-
1351
- it("compaction_completed event should have correct structure", () => {
1352
- const event = {
1353
- sessionUpdate: "compaction_completed",
1354
- sessionId: "test-session-123",
1355
- trigger: "auto" as const,
1356
- preTokens: 105000,
1357
- };
1358
-
1359
- expect(event.sessionUpdate).toBe("compaction_completed");
1360
- expect(event.sessionId).toBe("test-session-123");
1361
- expect(event.trigger).toBe("auto");
1362
- expect(event.preTokens).toBe(105000);
1363
- });
1364
-
1365
- it("compaction_completed event for manual trigger", () => {
1366
- const event = {
1367
- sessionUpdate: "compaction_completed",
1368
- sessionId: "test-session-789",
1369
- trigger: "manual" as const,
1370
- preTokens: 75000,
1371
- };
1372
-
1373
- expect(event.sessionUpdate).toBe("compaction_completed");
1374
- expect(event.trigger).toBe("manual");
1375
- });
1376
- });
1377
-
1378
- describe("compaction event emission via extNotification", () => {
1379
- it("compaction_started should be emitted via extNotification with _ prefix", async () => {
1380
- // This test verifies that compaction events are emitted via extNotification
1381
- // with the _ prefix for SDK version compatibility (SDK 0.12.x vs 0.13.x)
1382
- const { ClaudeAcpAgent } = await import("../acp-agent.js");
1383
-
1384
- const extNotificationCalls: Array<{ method: string; params: any }> = [];
1385
- const mockClient = {
1386
- extNotification: async (method: string, params: any) => {
1387
- extNotificationCalls.push({ method, params });
1388
- },
1389
- } as any;
1390
-
1391
- const agent = new ClaudeAcpAgent(mockClient);
1392
-
1393
- // The expected method name includes _ prefix for SDK compatibility
1394
- // SDK 0.12.x expects "_compaction_started" and strips the prefix
1395
- // SDK 0.13.x sends without prefix but we add it for backwards compatibility
1396
- const expectedMethodName = "_compaction_started";
1397
-
1398
- // Verify the structure of the expected call
1399
- const expectedParams = {
1400
- sessionId: "test-session",
1401
- trigger: "auto",
1402
- preTokens: 105000,
1403
- threshold: 100000,
1404
- };
1405
-
1406
- expect(expectedMethodName).toBe("_compaction_started");
1407
- expect(expectedParams.sessionId).toBe("test-session");
1408
- expect(expectedParams.trigger).toBe("auto");
1409
- expect(expectedParams.preTokens).toBe(105000);
1410
- expect(expectedParams.threshold).toBe(100000);
1411
- });
1412
-
1413
- it("compaction_completed should be emitted via extNotification with _ prefix", async () => {
1414
- // Similar to compaction_started, compaction_completed uses _ prefix
1415
- const expectedMethodName = "_compaction_completed";
1416
-
1417
- const expectedParams = {
1418
- sessionId: "test-session",
1419
- trigger: "auto",
1420
- preTokens: 105000,
1421
- };
1422
-
1423
- expect(expectedMethodName).toBe("_compaction_completed");
1424
- expect(expectedParams.sessionId).toBe("test-session");
1425
- expect(expectedParams.trigger).toBe("auto");
1426
- expect(expectedParams.preTokens).toBe(105000);
1427
- });
1428
-
1429
- it("should use extNotification instead of sessionUpdate for compaction events", () => {
1430
- // Compaction events are NOT part of the standard ACP SessionUpdate schema
1431
- // They must be sent via extNotification to avoid schema validation errors
1432
- // This is a documentation test to verify the design decision
1433
-
1434
- // Standard ACP SessionUpdate types (validated by schema):
1435
- const standardSessionUpdateTypes = [
1436
- "agent_message_chunk",
1437
- "agent_tool_call_progress",
1438
- "tool_call",
1439
- "tool_result",
1440
- "result",
1441
- "available_commands_update",
1442
- ];
1443
-
1444
- // Compaction events (sent via extNotification, not sessionUpdate):
1445
- const compactionEventTypes = ["compaction_started", "compaction_completed"];
1446
-
1447
- // Verify compaction events are NOT in the standard types
1448
- for (const compactionType of compactionEventTypes) {
1449
- expect(standardSessionUpdateTypes).not.toContain(compactionType);
1450
- }
1451
- });
1452
- });
1453
-
1454
- describe("_session/setCompaction extension method", () => {
1455
- it("should return error for non-existent session", async () => {
1456
- const { ClaudeAcpAgent } = await import("../acp-agent.js");
1457
- const mockClient = {} as any;
1458
- const agent = new ClaudeAcpAgent(mockClient);
1459
-
1460
- const result = await agent.extMethod("_session/setCompaction", {
1461
- sessionId: "non-existent-session",
1462
- enabled: true,
1463
- });
1464
-
1465
- expect(result).toEqual({
1466
- success: false,
1467
- error: "Session non-existent-session not found",
1468
- });
1469
- });
1470
-
1471
- it("should accept valid compaction configuration", async () => {
1472
- // Test the structure of valid params
1473
- const params = {
1474
- sessionId: "test-session",
1475
- enabled: true,
1476
- contextTokenThreshold: 50000,
1477
- customInstructions: "Focus on code changes",
1478
- };
1479
-
1480
- expect(params.sessionId).toBe("test-session");
1481
- expect(params.enabled).toBe(true);
1482
- expect(params.contextTokenThreshold).toBe(50000);
1483
- expect(params.customInstructions).toBe("Focus on code changes");
1484
- });
1485
-
1486
- it("should accept minimal compaction configuration", async () => {
1487
- const params: {
1488
- sessionId: string;
1489
- enabled: boolean;
1490
- contextTokenThreshold?: number;
1491
- customInstructions?: string;
1492
- } = {
1493
- sessionId: "test-session",
1494
- enabled: false,
1495
- };
1496
-
1497
- expect(params.sessionId).toBe("test-session");
1498
- expect(params.enabled).toBe(false);
1499
- expect(params.contextTokenThreshold).toBeUndefined();
1500
- expect(params.customInstructions).toBeUndefined();
1501
- });
1502
- });