zidane 7.2.5 → 8.0.4

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 (376) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +34 -6
  3. package/dist/{acp-D-tVyUjw.js → acp-DQDhi2dj.js} +230 -77
  4. package/dist/acp-DQDhi2dj.js.map +1 -0
  5. package/dist/acp-cli.d.ts +1 -1
  6. package/dist/acp-cli.js +9 -66
  7. package/dist/acp-cli.js.map +1 -1
  8. package/dist/acp.d.ts +10 -590
  9. package/dist/acp.d.ts.map +1 -1
  10. package/dist/acp.js +3 -3
  11. package/dist/{agent-D0zZnGbt.js → agent-DSLNmBp0.js} +14401 -13214
  12. package/dist/agent-DSLNmBp0.js.map +1 -0
  13. package/dist/agent.d.ts +1 -1
  14. package/dist/agent.js +3 -2
  15. package/dist/aliasing-lVFbm81n.js +217 -0
  16. package/dist/aliasing-lVFbm81n.js.map +1 -0
  17. package/dist/{anthropic-C2aole0G.js → anthropic-CD00LhUP.js} +1442 -1264
  18. package/dist/anthropic-CD00LhUP.js.map +1 -0
  19. package/dist/artifacts-CV2hUKnL.js.map +1 -1
  20. package/dist/{atomic-write-B7zr1ddF.js → atomic-write-Ijgf2Mhc.js} +13 -3
  21. package/dist/atomic-write-Ijgf2Mhc.js.map +1 -0
  22. package/dist/call-tool-dHlbGOIY.js +70 -0
  23. package/dist/call-tool-dHlbGOIY.js.map +1 -0
  24. package/dist/chat/pure.d.ts +372 -6
  25. package/dist/chat/pure.d.ts.map +1 -1
  26. package/dist/chat/pure.js +35 -244
  27. package/dist/chat/pure.js.map +1 -1
  28. package/dist/{approvals-DKaJZYlm.js → chat-JPJJ8uu5.js} +15879 -14631
  29. package/dist/chat-JPJJ8uu5.js.map +1 -0
  30. package/dist/chat.d.ts +4 -6
  31. package/dist/chat.js +23 -12
  32. package/dist/cli-shared-C-YCaglL.js +159 -0
  33. package/dist/cli-shared-C-YCaglL.js.map +1 -0
  34. package/dist/client-core-BQSyDALT.d.ts +2115 -0
  35. package/dist/client-core-BQSyDALT.d.ts.map +1 -0
  36. package/dist/codemode-worker.d.ts +1 -0
  37. package/dist/codemode-worker.js +155 -0
  38. package/dist/codemode-worker.js.map +1 -0
  39. package/dist/codemode.d.ts +352 -0
  40. package/dist/codemode.d.ts.map +1 -0
  41. package/dist/codemode.js +5 -0
  42. package/dist/connection-Dr-Js4yZ.d.ts +28 -0
  43. package/dist/connection-Dr-Js4yZ.d.ts.map +1 -0
  44. package/dist/connection-loop-gdh22LTI.js +112 -0
  45. package/dist/connection-loop-gdh22LTI.js.map +1 -0
  46. package/dist/context-segments-BHKlagL6.js +1016 -0
  47. package/dist/context-segments-BHKlagL6.js.map +1 -0
  48. package/dist/{context-transport-T_6AYIvy.js → context-transport-DSMTUsK1.js} +3 -3
  49. package/dist/{context-transport-T_6AYIvy.js.map → context-transport-DSMTUsK1.js.map} +1 -1
  50. package/dist/contexts/daytona.d.ts +19 -8
  51. package/dist/contexts/daytona.d.ts.map +1 -1
  52. package/dist/contexts/daytona.js +1 -440
  53. package/dist/contexts/docker.d.ts +36 -7
  54. package/dist/contexts/docker.d.ts.map +1 -1
  55. package/dist/contexts/docker.js +1 -456
  56. package/dist/contexts/e2b.d.ts +68 -13
  57. package/dist/contexts/e2b.d.ts.map +1 -1
  58. package/dist/contexts/e2b.js +1 -382
  59. package/dist/contexts/sandbox.d.ts +1 -1
  60. package/dist/contexts/sandbox.js +5 -0
  61. package/dist/contexts/sandbox.js.map +1 -1
  62. package/dist/{contexts-CRE9pw07.js → contexts-d7UolYot.js} +1059 -524
  63. package/dist/contexts-d7UolYot.js.map +1 -0
  64. package/dist/contexts.d.ts +3 -3
  65. package/dist/contexts.js +1 -1
  66. package/dist/cred-keys-GpBg71cP.js +105 -0
  67. package/dist/cred-keys-GpBg71cP.js.map +1 -0
  68. package/dist/{auth-BG2rVKSt.js → credentials-JttEuRON.js} +55 -206
  69. package/dist/credentials-JttEuRON.js.map +1 -0
  70. package/dist/{cursor-iojRXHF9.js → cursor-CdjaQTBW.js} +3 -3
  71. package/dist/{cursor-iojRXHF9.js.map → cursor-CdjaQTBW.js.map} +1 -1
  72. package/dist/daemon-cli.d.ts +1 -1
  73. package/dist/daemon-cli.js +3 -2
  74. package/dist/daemon-cli.js.map +1 -1
  75. package/dist/daytona-DG3omipZ.js +513 -0
  76. package/dist/daytona-DG3omipZ.js.map +1 -0
  77. package/dist/defaults-Cx3zvDOV.js +23 -0
  78. package/dist/defaults-Cx3zvDOV.js.map +1 -0
  79. package/dist/dispatcher-BMlu2BmZ.js +4941 -0
  80. package/dist/dispatcher-BMlu2BmZ.js.map +1 -0
  81. package/dist/docker-BZhMMJUp.js +727 -0
  82. package/dist/docker-BZhMMJUp.js.map +1 -0
  83. package/dist/e2b-CEVOdRib.js +480 -0
  84. package/dist/e2b-CEVOdRib.js.map +1 -0
  85. package/dist/edit-utils-EGosADZq.js +234 -0
  86. package/dist/edit-utils-EGosADZq.js.map +1 -0
  87. package/dist/environment-8wsKE2Ln.js +214 -0
  88. package/dist/environment-8wsKE2Ln.js.map +1 -0
  89. package/dist/eval-BNcvle_J.js +1595 -0
  90. package/dist/eval-BNcvle_J.js.map +1 -0
  91. package/dist/eval.d.ts +2 -2
  92. package/dist/eval.js +1 -1489
  93. package/dist/executor-Cx5-8yqi.js +621 -0
  94. package/dist/executor-Cx5-8yqi.js.map +1 -0
  95. package/dist/extension-authoring-DyGGmq9V.js +385 -0
  96. package/dist/extension-authoring-DyGGmq9V.js.map +1 -0
  97. package/dist/extensions.d.ts +2 -2
  98. package/dist/extensions.js +3 -3
  99. package/dist/{fetch-url-DX75gpL0.js → fetch-url-Ct2TjDWt.js} +4 -4
  100. package/dist/{fetch-url-DX75gpL0.js.map → fetch-url-Ct2TjDWt.js.map} +1 -1
  101. package/dist/{format-BF745pLL.js → format-C2NQEVUH.js} +39 -2
  102. package/dist/format-C2NQEVUH.js.map +1 -0
  103. package/dist/format-bytes-C7tfc3Zm.js +55 -0
  104. package/dist/format-bytes-C7tfc3Zm.js.map +1 -0
  105. package/dist/headless-C_rNUuYC.js +960 -0
  106. package/dist/headless-C_rNUuYC.js.map +1 -0
  107. package/dist/headless.d.ts +1 -1
  108. package/dist/headless.js +1 -413
  109. package/dist/{history-search-C4bk1Wz_.js → history-search-CcYzF8-0.js} +7 -3
  110. package/dist/history-search-CcYzF8-0.js.map +1 -0
  111. package/dist/history-search-worker.d.ts +1 -1
  112. package/dist/history-search-worker.js +1 -1
  113. package/dist/host-D7NrI03V.js +378 -0
  114. package/dist/host-D7NrI03V.js.map +1 -0
  115. package/dist/host-signals-BcP5QKUD.js +118 -0
  116. package/dist/host-signals-BcP5QKUD.js.map +1 -0
  117. package/dist/identity-CwfO8K-t.js +26 -0
  118. package/dist/identity-CwfO8K-t.js.map +1 -0
  119. package/dist/{index-BgN1T3lk.d.ts → index-BGe1fQT7.d.ts} +10987 -9320
  120. package/dist/index-BGe1fQT7.d.ts.map +1 -0
  121. package/dist/index-BVQsIKGn.d.ts +448 -0
  122. package/dist/index-BVQsIKGn.d.ts.map +1 -0
  123. package/dist/{index-C6NEd5y_.d.ts → index-CHABJd-L.d.ts} +3386 -502
  124. package/dist/index-CHABJd-L.d.ts.map +1 -0
  125. package/dist/{index-C37_ppWv.d.ts → index-D6a7qXpg.d.ts} +2 -3
  126. package/dist/index-D6a7qXpg.d.ts.map +1 -0
  127. package/dist/index.d.ts +6 -6
  128. package/dist/index.js +27 -23
  129. package/dist/{completion-core-BRo86R_L.js → inline-ghost-BVRQeGoL.js} +50 -2
  130. package/dist/inline-ghost-BVRQeGoL.js.map +1 -0
  131. package/dist/ipc/remote.d.ts +313 -0
  132. package/dist/ipc/remote.d.ts.map +1 -0
  133. package/dist/ipc/remote.js +4 -0
  134. package/dist/ipc.d.ts +6 -964
  135. package/dist/ipc.js +403 -4
  136. package/dist/ipc.js.map +1 -0
  137. package/dist/{isolation-Kfoof0MM.d.ts → isolation-D9L650W5.d.ts} +100 -15
  138. package/dist/isolation-D9L650W5.d.ts.map +1 -0
  139. package/dist/json-rpc--HXQti1E.js +350 -0
  140. package/dist/json-rpc--HXQti1E.js.map +1 -0
  141. package/dist/links-j8rED1sR.js +603 -0
  142. package/dist/links-j8rED1sR.js.map +1 -0
  143. package/dist/local-terminal-D8QThwo_.js +252 -0
  144. package/dist/local-terminal-D8QThwo_.js.map +1 -0
  145. package/dist/{logger--C1VQjwJ.js → logger-Gtqlayx8.js} +199 -173
  146. package/dist/logger-Gtqlayx8.js.map +1 -0
  147. package/dist/logs-view-CWG8aUAG.js.map +1 -1
  148. package/dist/{mcp-CgNd-f3F.js → mcp-_wecH9Qe.js} +608 -562
  149. package/dist/mcp-_wecH9Qe.js.map +1 -0
  150. package/dist/mcp.d.ts +2 -2
  151. package/dist/mcp.js +2 -2
  152. package/dist/media-sniff-UKijEaiQ.js.map +1 -1
  153. package/dist/{messages-2jcOA_5R.js → messages-BqiwcD2Q.js} +1017 -1014
  154. package/dist/messages-BqiwcD2Q.js.map +1 -0
  155. package/dist/{oauth-DxHO17JE.js → oauth-Px_pBB9W.js} +5 -5
  156. package/dist/{oauth-DxHO17JE.js.map → oauth-Px_pBB9W.js.map} +1 -1
  157. package/dist/{openai-compat-Dy56weK8.js → openai-compat-CFrpH7uq.js} +432 -589
  158. package/dist/openai-compat-CFrpH7uq.js.map +1 -0
  159. package/dist/orchestration-BCGmB8Ps.js +511 -0
  160. package/dist/orchestration-BCGmB8Ps.js.map +1 -0
  161. package/dist/output/stream-json.d.ts +1 -2
  162. package/dist/output/stream-json.d.ts.map +1 -1
  163. package/dist/output/stream-json.js +1 -1
  164. package/dist/output/terminal.d.ts +1 -2
  165. package/dist/output/terminal.d.ts.map +1 -1
  166. package/dist/output/terminal.js +13 -7
  167. package/dist/output/terminal.js.map +1 -1
  168. package/dist/{plugins-gbI7SBA6.js → plugins-DS7mahzZ.js} +20 -101
  169. package/dist/plugins-DS7mahzZ.js.map +1 -0
  170. package/dist/plugins.d.ts +1 -1
  171. package/dist/plugins.js +1 -2
  172. package/dist/prelude-Cxo4t74B.js +517 -0
  173. package/dist/prelude-Cxo4t74B.js.map +1 -0
  174. package/dist/presets.d.ts +1 -1
  175. package/dist/presets.js +1 -2
  176. package/dist/project-root-B8zADr4o.js +167 -0
  177. package/dist/project-root-B8zADr4o.js.map +1 -0
  178. package/dist/project-user-paths-BSgtrRxl.js +58 -0
  179. package/dist/project-user-paths-BSgtrRxl.js.map +1 -0
  180. package/dist/{prompts-Pqc3JdW_.d.ts → prompts-DYcvqShf.d.ts} +14 -11
  181. package/dist/prompts-DYcvqShf.d.ts.map +1 -0
  182. package/dist/protocol-DnhqqSmE.js +13 -0
  183. package/dist/protocol-DnhqqSmE.js.map +1 -0
  184. package/dist/providers/anthropic.d.ts +2 -2
  185. package/dist/providers/anthropic.js +1 -2
  186. package/dist/providers/arcee.d.ts +1 -1
  187. package/dist/providers/arcee.js +1 -1
  188. package/dist/providers/baseten.d.ts +1 -1
  189. package/dist/providers/baseten.js +1 -1
  190. package/dist/providers/cerebras.d.ts +1 -1
  191. package/dist/providers/cerebras.js +3 -2
  192. package/dist/providers/cerebras.js.map +1 -1
  193. package/dist/providers/local.d.ts +1 -1
  194. package/dist/providers/local.js +1 -1
  195. package/dist/providers/openai-compat.d.ts +1 -1
  196. package/dist/providers/openai-compat.js +1 -2
  197. package/dist/providers/openai.d.ts +1 -1
  198. package/dist/providers/openai.js +15 -12
  199. package/dist/providers/openai.js.map +1 -1
  200. package/dist/providers/openrouter.d.ts +1 -1
  201. package/dist/providers/openrouter.js +3 -2
  202. package/dist/providers/openrouter.js.map +1 -1
  203. package/dist/providers/xai.d.ts +1 -1
  204. package/dist/providers/xai.js +3 -3
  205. package/dist/{providers-BVhnFyiY.js → providers-XUR7F4Hi.js} +4 -4
  206. package/dist/{providers-BVhnFyiY.js.map → providers-XUR7F4Hi.js.map} +1 -1
  207. package/dist/providers.d.ts +1 -1
  208. package/dist/providers.js +4 -4
  209. package/dist/registry-D6ShBWJU.js +5234 -0
  210. package/dist/registry-D6ShBWJU.js.map +1 -0
  211. package/dist/remote-hosts-BtA_9ZGp.js +97 -0
  212. package/dist/remote-hosts-BtA_9ZGp.js.map +1 -0
  213. package/dist/restate.d.ts +2 -3
  214. package/dist/restate.d.ts.map +1 -1
  215. package/dist/restate.js +631 -572
  216. package/dist/restate.js.map +1 -1
  217. package/dist/run-summary-CklLiZ4y.js +288 -0
  218. package/dist/run-summary-CklLiZ4y.js.map +1 -0
  219. package/dist/{sandbox-BH0WQkXn.d.ts → sandbox-C5dR5DwQ.d.ts} +6 -3
  220. package/dist/sandbox-C5dR5DwQ.d.ts.map +1 -0
  221. package/dist/session/sqlite.d.ts +1 -2
  222. package/dist/session/sqlite.d.ts.map +1 -1
  223. package/dist/session/sqlite.js +1 -616
  224. package/dist/session-factory-BUGY5P40.js +1576 -0
  225. package/dist/session-factory-BUGY5P40.js.map +1 -0
  226. package/dist/{session-BqRP1hS7.js → session-xh9JXu6i.js} +94 -52
  227. package/dist/session-xh9JXu6i.js.map +1 -0
  228. package/dist/session.d.ts +1 -1
  229. package/dist/session.js +2 -2
  230. package/dist/settings-keys-Ckf29NeM.js +677 -0
  231. package/dist/settings-keys-Ckf29NeM.js.map +1 -0
  232. package/dist/shell-path-Dqaoow2J.js +412 -0
  233. package/dist/shell-path-Dqaoow2J.js.map +1 -0
  234. package/dist/{skills-BC3dodWp.js → skills-DKqZJm4X.js} +430 -470
  235. package/dist/skills-DKqZJm4X.js.map +1 -0
  236. package/dist/skills.d.ts +2 -2
  237. package/dist/skills.js +1 -2
  238. package/dist/source-mPExaUm8.js +381 -0
  239. package/dist/source-mPExaUm8.js.map +1 -0
  240. package/dist/sqlite-DV1DQzN2.js +698 -0
  241. package/dist/sqlite-DV1DQzN2.js.map +1 -0
  242. package/dist/{src-CkNQKFKr.js → src-BRMYRJib.js} +903 -1121
  243. package/dist/src-BRMYRJib.js.map +1 -0
  244. package/dist/{stdio-DPpBDPx5.js → stdio-C6YyoW3I.js} +4 -4
  245. package/dist/{stdio-DPpBDPx5.js.map → stdio-C6YyoW3I.js.map} +1 -1
  246. package/dist/{stdio-loader-Bbx19dMZ.js → stdio-loader-C830Vg-Z.js} +4 -4
  247. package/dist/{stdio-loader-Bbx19dMZ.js.map → stdio-loader-C830Vg-Z.js.map} +1 -1
  248. package/dist/system-prompt-MNVHatSD.d.ts.map +1 -1
  249. package/dist/timeout-DEaqslLO.d.ts.map +1 -1
  250. package/dist/todo-edits-Dn251YUP.js +1790 -0
  251. package/dist/todo-edits-Dn251YUP.js.map +1 -0
  252. package/dist/{turn-operations-DrbJlpq9.js → tool-formatters-IQFDrdrs.js} +1456 -2120
  253. package/dist/tool-formatters-IQFDrdrs.js.map +1 -0
  254. package/dist/tool-state-Dku6YqAB.js +989 -0
  255. package/dist/tool-state-Dku6YqAB.js.map +1 -0
  256. package/dist/tools/fetch-url.d.ts +1 -2
  257. package/dist/tools/fetch-url.d.ts.map +1 -1
  258. package/dist/tools/fetch-url.js +1 -2
  259. package/dist/tools/prompts.d.ts +1 -1
  260. package/dist/tools/prompts.js +7 -7
  261. package/dist/tools/prompts.js.map +1 -1
  262. package/dist/tools/web-search.d.ts +1 -2
  263. package/dist/tools/web-search.d.ts.map +1 -1
  264. package/dist/tools/web-search.js +2 -2
  265. package/dist/tools/web-search.js.map +1 -1
  266. package/dist/tools-_DnT3AGM.js +578 -0
  267. package/dist/tools-_DnT3AGM.js.map +1 -0
  268. package/dist/tools.d.ts +3 -3
  269. package/dist/tools.js +4 -2
  270. package/dist/tui.d.ts +534 -517
  271. package/dist/tui.d.ts.map +1 -1
  272. package/dist/tui.js +34933 -27243
  273. package/dist/tui.js.map +1 -1
  274. package/dist/turn-operations-B8sokwi5.js +333 -0
  275. package/dist/turn-operations-B8sokwi5.js.map +1 -0
  276. package/dist/turn-operations-DQX5ZZni.d.ts +3089 -0
  277. package/dist/turn-operations-DQX5ZZni.d.ts.map +1 -0
  278. package/dist/types-C7ZFOztE.d.ts +132 -0
  279. package/dist/types-C7ZFOztE.d.ts.map +1 -0
  280. package/dist/types-CH5XpIHw.js +114 -0
  281. package/dist/types-CH5XpIHw.js.map +1 -0
  282. package/dist/{types-CWgENVLY.js → types-Cu0ZGDu9.js} +10 -6
  283. package/dist/types-Cu0ZGDu9.js.map +1 -0
  284. package/dist/types-IqegbJEE.d.ts +624 -0
  285. package/dist/types-IqegbJEE.d.ts.map +1 -0
  286. package/dist/types.d.ts +4 -4
  287. package/dist/types.js +1 -1
  288. package/dist/url-D8HCl65b.js +1259 -0
  289. package/dist/url-D8HCl65b.js.map +1 -0
  290. package/dist/wasm-BVT-j5cl.js +33 -0
  291. package/dist/wasm-BVT-j5cl.js.map +1 -0
  292. package/dist/workspace-exec-stream-SV2R9d5S.js +355 -0
  293. package/dist/workspace-exec-stream-SV2R9d5S.js.map +1 -0
  294. package/dist/ws-CIHMvlSh.js +952 -0
  295. package/dist/ws-CIHMvlSh.js.map +1 -0
  296. package/dist/ws-client-N7NxzvQp.js +230 -0
  297. package/dist/ws-client-N7NxzvQp.js.map +1 -0
  298. package/dist/{xai-CazjStOK.js → xai-CvJTeLrI.js} +165 -5
  299. package/dist/xai-CvJTeLrI.js.map +1 -0
  300. package/dist/xdg-zweed0-t.js +117 -0
  301. package/dist/xdg-zweed0-t.js.map +1 -0
  302. package/docs/ACP.md +52 -0
  303. package/docs/ARCHITECTURE.md +103 -6
  304. package/docs/ARTIFACTS.md +8 -4
  305. package/docs/CHAT.md +165 -14
  306. package/docs/EXECUTION_CONTEXT.md +8 -0
  307. package/docs/EXTENSIONS.md +123 -2
  308. package/docs/HEADLESS.md +2 -0
  309. package/docs/IPC.md +775 -64
  310. package/docs/MACHINES.md +456 -0
  311. package/docs/PANELS.md +3 -0
  312. package/docs/README.md +4 -0
  313. package/docs/RESTATE.md +3 -2
  314. package/docs/RUN_IN_BACKGROUND.md +10 -2
  315. package/docs/SERVER.md +1085 -0
  316. package/docs/SKILL.md +6 -1
  317. package/docs/TERMINAL.md +75 -0
  318. package/docs/TUI.md +65 -14
  319. package/package.json +58 -26
  320. package/scripts/eval.ts +4 -0
  321. package/dist/acp-D-tVyUjw.js.map +0 -1
  322. package/dist/agent-D0zZnGbt.js.map +0 -1
  323. package/dist/anthropic-C2aole0G.js.map +0 -1
  324. package/dist/approvals-DKaJZYlm.js.map +0 -1
  325. package/dist/atomic-write-B7zr1ddF.js.map +0 -1
  326. package/dist/auth-BG2rVKSt.js.map +0 -1
  327. package/dist/completion-core-BRo86R_L.js.map +0 -1
  328. package/dist/contexts/daytona.js.map +0 -1
  329. package/dist/contexts/docker.js.map +0 -1
  330. package/dist/contexts/e2b.js.map +0 -1
  331. package/dist/contexts-CRE9pw07.js.map +0 -1
  332. package/dist/eval.js.map +0 -1
  333. package/dist/format-BF745pLL.js.map +0 -1
  334. package/dist/glob-DluQFSw1.js +0 -270
  335. package/dist/glob-DluQFSw1.js.map +0 -1
  336. package/dist/headless.js.map +0 -1
  337. package/dist/history-search-C4bk1Wz_.js.map +0 -1
  338. package/dist/index-BgN1T3lk.d.ts.map +0 -1
  339. package/dist/index-C37_ppWv.d.ts.map +0 -1
  340. package/dist/index-C6NEd5y_.d.ts.map +0 -1
  341. package/dist/ipc-BKVIIOqH.js +0 -331
  342. package/dist/ipc-BKVIIOqH.js.map +0 -1
  343. package/dist/ipc.d.ts.map +0 -1
  344. package/dist/isolation-Kfoof0MM.d.ts.map +0 -1
  345. package/dist/json-rpc-rSWMUh9b.js +0 -192
  346. package/dist/json-rpc-rSWMUh9b.js.map +0 -1
  347. package/dist/logger--C1VQjwJ.js.map +0 -1
  348. package/dist/mcp-CgNd-f3F.js.map +0 -1
  349. package/dist/messages-2jcOA_5R.js.map +0 -1
  350. package/dist/openai-compat-Dy56weK8.js.map +0 -1
  351. package/dist/output-cap-C9zukcCj.js +0 -100
  352. package/dist/output-cap-C9zukcCj.js.map +0 -1
  353. package/dist/panels-B6gRk4bf.js +0 -789
  354. package/dist/panels-B6gRk4bf.js.map +0 -1
  355. package/dist/plugins-gbI7SBA6.js.map +0 -1
  356. package/dist/prompts-Pqc3JdW_.d.ts.map +0 -1
  357. package/dist/run-summary-BU_NgwUT.js +0 -269
  358. package/dist/run-summary-BU_NgwUT.js.map +0 -1
  359. package/dist/sandbox-BH0WQkXn.d.ts.map +0 -1
  360. package/dist/session/sqlite.js.map +0 -1
  361. package/dist/session-BqRP1hS7.js.map +0 -1
  362. package/dist/shell-path-DfdXrGg9.js +0 -1549
  363. package/dist/shell-path-DfdXrGg9.js.map +0 -1
  364. package/dist/skills-BC3dodWp.js.map +0 -1
  365. package/dist/src-CkNQKFKr.js.map +0 -1
  366. package/dist/todo-edits-UOwgxGKu.js +0 -528
  367. package/dist/todo-edits-UOwgxGKu.js.map +0 -1
  368. package/dist/tool-formatters-DoRLnfuD.d.ts +0 -759
  369. package/dist/tool-formatters-DoRLnfuD.d.ts.map +0 -1
  370. package/dist/transcript-anchors-BeudMbXx.d.ts +0 -1762
  371. package/dist/transcript-anchors-BeudMbXx.d.ts.map +0 -1
  372. package/dist/turn-operations-C8bMBDl_.d.ts +0 -1258
  373. package/dist/turn-operations-C8bMBDl_.d.ts.map +0 -1
  374. package/dist/turn-operations-DrbJlpq9.js.map +0 -1
  375. package/dist/types-CWgENVLY.js.map +0 -1
  376. package/dist/xai-CazjStOK.js.map +0 -1
@@ -1,11 +1,11 @@
1
- import { l as toolOutputByteLength, u as toolResultToText } from "./types-CWgENVLY.js";
2
- import { n as shellQuote } from "./shell-quote-BmnhZmdM.js";
3
1
  import { u as errorMessage } from "./errors-DKiMrOKq.js";
4
- import { n as originPinnedFetch } from "./origin-pinned-fetch-CqG8VZIq.js";
2
+ import { d as toolResultToText, u as toolOutputByteLength } from "./types-Cu0ZGDu9.js";
5
3
  import { a as sniffImageMediaTypeFromBase64, n as reconcileImageMediaType, o as sniffMediaTypeFromBase64, r as reconcileMediaType } from "./media-sniff-UKijEaiQ.js";
6
- import { sep } from "node:path";
4
+ import { n as shellQuote } from "./shell-quote-BmnhZmdM.js";
5
+ import { n as originPinnedFetch } from "./origin-pinned-fetch-CqG8VZIq.js";
7
6
  import { Buffer } from "node:buffer";
8
7
  import { mkdirSync } from "node:fs";
8
+ import { sep } from "node:path";
9
9
  //#region src/mcp/oauth-provider.ts
10
10
  /**
11
11
  * In-memory store — primarily for tests, but valid as a no-persistence option
@@ -39,6 +39,7 @@ var McpOAuthProvider = class {
39
39
  onAuthorizationUrl;
40
40
  clientName;
41
41
  _scope;
42
+ _state;
42
43
  codeVerifierValue;
43
44
  constructor(opts) {
44
45
  this.name = opts.name;
@@ -47,6 +48,11 @@ var McpOAuthProvider = class {
47
48
  this.onAuthorizationUrl = opts.onAuthorizationUrl;
48
49
  this.clientName = opts.clientName ?? DEFAULT_CLIENT_NAME;
49
50
  this._scope = opts.scope;
51
+ this._state = opts.state;
52
+ }
53
+ /** The configured `state` (the SDK sends nothing when it is empty). */
54
+ state() {
55
+ return this._state ?? "";
50
56
  }
51
57
  /**
52
58
  * ALWAYS returns a URL — the placeholder in non-interactive mode — and
@@ -98,7 +104,7 @@ var McpOAuthProvider = class {
98
104
  this.codeVerifierValue = verifier;
99
105
  }
100
106
  codeVerifier() {
101
- if (!this.codeVerifierValue) throw new Error(`MCP OAuth: code verifier missing for "${this.name}" — the flow must call saveCodeVerifier() before the redirect`);
107
+ if (!this.codeVerifierValue) throw new Error(`MCP OAuth: code verifier missing for "${this.name}". the flow must call saveCodeVerifier() before the redirect`);
102
108
  return this.codeVerifierValue;
103
109
  }
104
110
  async redirectToAuthorization(url) {
@@ -190,157 +196,6 @@ function hasAuthorizationHeader(headers) {
190
196
  return false;
191
197
  }
192
198
  //#endregion
193
- //#region src/mcp/sse-to-json-fetch.ts
194
- /**
195
- * `fetch` shim that converts streamable-http POST responses with
196
- * `Content-Type: text/event-stream` into synthetic `application/json`
197
- * responses, preserving every original header (notably `mcp-session-id`).
198
- *
199
- * Why this exists
200
- * ----------------
201
- * The MCP SDK's streamable-http transport handles POST responses two ways
202
- * depending on `content-type`:
203
- *
204
- * - `application/json` → `await response.json()` (works on every runtime).
205
- * - `text/event-stream` → `body.pipeThrough(TextDecoderStream)
206
- * .pipeThrough(EventSourceParserStream)`
207
- * and forward each parsed event to `onmessage`.
208
- *
209
- * On Bun + MCP SDK 1.29.x the second pipeline silently drops the parsed
210
- * event: the full `event: message\ndata: {...}\n\n` arrives intact, the
211
- * stream closes, but `onmessage` is never called. Bootstrap then waits the
212
- * full `bootstrapTimeout` (10s default) and the agent loses every tool the
213
- * server would have exposed.
214
- *
215
- * The MCP spec lets clients accept either content type
216
- * (`Accept: application/json, text/event-stream`), so flipping the stream
217
- * to JSON at the boundary is a transparent, server-agnostic workaround.
218
- *
219
- * Scope of the shim
220
- * -----------------
221
- * - POST + `text/event-stream` → drain, parse SSE message events, return
222
- * a synthetic `application/json` response. Single event becomes a JSON
223
- * object (matching the shape the SDK expects from non-streaming
224
- * servers); multiple events become a JSON array (the SDK already
225
- * iterates `Array.isArray(data)` from a JSON response).
226
- * - GET (long-lived `_startOrAuthSse` listener) → untouched. Per-event
227
- * latency matters there and the SSE pipeline isn't always broken on
228
- * GET in the same way (different code path inside Bun's stream impl).
229
- * - 202 / non-SSE / malformed SSE / no body → passthrough.
230
- *
231
- * Runtime gating
232
- * --------------
233
- * `sseToJsonFetchIfNeeded()` only returns a wrapper on Bun. Node + browser
234
- * runtimes get `undefined` and the SDK uses global `fetch` directly — no
235
- * extra buffer-and-redrain on the happy path, and no risk of collapsing a
236
- * future progress-streaming response into a single batched array. If you
237
- * need to apply the shim unconditionally (testing, custom hosts), call
238
- * `sseToJsonFetch()` directly.
239
- *
240
- * Cleanup
241
- * -------
242
- * Remove this file and its `fetch:` injection in `createTransport` once
243
- * either Bun fixes the `pipeThrough` chain or the SDK switches off the
244
- * streaming pipeline by default.
245
- */
246
- /**
247
- * Detect whether we're running on Bun. The `Bun` global is set by the
248
- * runtime itself and isn't faked by Bun's Node-compat layer.
249
- */
250
- function isBunRuntime() {
251
- return typeof globalThis.Bun !== "undefined";
252
- }
253
- /**
254
- * Returns the `sseToJsonFetch` wrapper only on runtimes that need it
255
- * (currently Bun). Returns `undefined` everywhere else, which makes the
256
- * SDK fall back to the global `fetch` and keeps the streaming pipeline
257
- * intact on Node where it works correctly.
258
- *
259
- * Designed as the value to pass directly to `StreamableHTTPClientTransport`'s
260
- * `fetch` option:
261
- *
262
- * new StreamableHTTPClientTransport(url, {
263
- * fetch: sseToJsonFetchIfNeeded(),
264
- * })
265
- */
266
- function sseToJsonFetchIfNeeded() {
267
- return isBunRuntime() ? sseToJsonFetch() : void 0;
268
- }
269
- /**
270
- * Wrap a `fetch` implementation so streamable-http POST responses that come
271
- * back as `text/event-stream` are converted to JSON. Pass the result as
272
- * `opts.fetch` to `StreamableHTTPClientTransport`.
273
- *
274
- * Always-on (i.e. unconditional) — for the runtime-gated entry point,
275
- * use {@link sseToJsonFetchIfNeeded} instead.
276
- */
277
- function sseToJsonFetch(baseFetch = fetch) {
278
- return async function sseToJsonWrappedFetch(input, init) {
279
- const response = await baseFetch(input, init);
280
- if ((init?.method ?? "GET").toString().toUpperCase() !== "POST") return response;
281
- const contentType = response.headers.get("content-type");
282
- if (!contentType || !contentType.includes("text/event-stream")) return response;
283
- if (!response.body) return response;
284
- let raw;
285
- try {
286
- raw = await response.text();
287
- } catch {
288
- return response;
289
- }
290
- const events = parseSseDataEvents(raw);
291
- return synthesizeJsonResponse(response, events.length === 1 ? events[0] : events);
292
- };
293
- }
294
- /**
295
- * Parse a buffered SSE body into the JSON payloads of its `message` events.
296
- *
297
- * Skips:
298
- * - SSE comments (lines starting with `:`).
299
- * - Non-default event types (`event: foo` ≠ `message`). The MCP server
300
- * only ever emits `message` events for JSON-RPC; anything else is out of
301
- * band and the SDK wouldn't have surfaced it to `onmessage` either.
302
- * - Malformed `data:` payloads (anything that fails `JSON.parse`).
303
- */
304
- function parseSseDataEvents(raw) {
305
- const events = [];
306
- for (const block of raw.split(/\r?\n\r?\n/)) {
307
- if (!block.trim()) continue;
308
- const dataLines = [];
309
- let isMessageEvent = true;
310
- for (const line of block.split(/\r?\n/)) {
311
- if (line.startsWith(":")) continue;
312
- if (line.startsWith("event:")) {
313
- const eventType = line.slice(6).trim();
314
- if (eventType && eventType !== "message") isMessageEvent = false;
315
- } else if (line.startsWith("data:")) {
316
- const value = line.slice(5);
317
- dataLines.push(value.startsWith(" ") ? value.slice(1) : value);
318
- }
319
- }
320
- if (!isMessageEvent || dataLines.length === 0) continue;
321
- try {
322
- events.push(JSON.parse(dataLines.join("\n")));
323
- } catch {}
324
- }
325
- return events;
326
- }
327
- /**
328
- * Build a `Response` mirroring the original's status / statusText / headers
329
- * but with a JSON body and `content-type: application/json`. Header
330
- * preservation is the whole point — `mcp-session-id` is set by the server
331
- * on the initialize POST and must round-trip into the SDK's
332
- * `_sessionId` capture (`response.headers.get('mcp-session-id')`).
333
- */
334
- function synthesizeJsonResponse(original, payload) {
335
- const headers = new Headers(original.headers);
336
- headers.set("content-type", "application/json");
337
- return new Response(JSON.stringify(payload), {
338
- status: original.status,
339
- statusText: original.statusText,
340
- headers
341
- });
342
- }
343
- //#endregion
344
199
  //#region src/mcp/tolerant-client.ts
345
200
  /**
346
201
  * Async factory — builds an instance of the tolerant MCP client. The
@@ -395,155 +250,235 @@ async function createTolerantClient(info, options) {
395
250
  return new TolerantMcpClient(info, options);
396
251
  }
397
252
  //#endregion
398
- //#region src/mcp/index.ts
399
- /**
400
- * Non-owning view of a live MCP connection: the SAME tools and
401
- * instructions, but a no-op `close`. Handed to consumers that must never
402
- * tear down transports they don't own — the loop's per-call
403
- * `ToolContext.mcpConnection` and child agents spawned off the parent's
404
- * connection — because only the owning agent's destroy path may close the
405
- * real thing, and a doc comment alone is too weak a guard. Single shared
406
- * builder so the call sites can't drift when this shape grows a field.
407
- */
408
- function nonOwningMcpView(connection) {
409
- return {
410
- tools: connection.tools,
411
- ...connection.instructions !== void 0 ? { instructions: connection.instructions } : {},
412
- close: async () => {}
413
- };
414
- }
415
- /**
416
- * Recover a discovered tool's `{ server, toolName }` from its namespaced name
417
- * (`mcp_<server>_<tool>`). Both server and tool names can contain underscores,
418
- * so this matches against the known server list by longest server-name prefix
419
- * rather than splitting blindly. Tools that match no configured server (custom
420
- * connectors with a different naming scheme) fall back to
421
- * `{ server: '', toolName: <namespacedName> }`.
422
- */
423
- function deriveMcpToolMeta(namespacedName, servers) {
424
- let best;
425
- for (const server of servers) {
426
- const prefix = `mcp_${server.name}_`;
427
- if (namespacedName.startsWith(prefix) && (!best || server.name.length > best.name.length)) best = server;
428
- }
429
- if (best) return {
430
- server: best.name,
431
- toolName: namespacedName.slice(`mcp_${best.name}_`.length)
432
- };
433
- return {
434
- server: "",
435
- toolName: namespacedName
436
- };
437
- }
253
+ //#region src/mcp/tool-result.ts
438
254
  /**
439
- * Apply an {@link McpToolWrap} to every entry of a discovered-tool map,
440
- * resolving each tool's {@link McpToolMeta} from `servers`. Returns a fresh
441
- * map; the input is untouched. Used by the agent to apply the
442
- * `AgentOptions.mcpToolWrap` hook to discovered tools.
255
+ * Lossy flattener — converts MCP `CallToolResult.content` blocks to a single
256
+ * string. Text blocks are extracted; non-text blocks are JSON-stringified.
257
+ *
258
+ * Use this only at UI / log boundaries that require a string. The agent
259
+ * loop itself routes through {@link normalizeMcpBlocks} so image blocks
260
+ * survive into provider-native tool_result content (Anthropic blocks,
261
+ * OpenAI companion-user-message).
443
262
  */
444
- function wrapDiscoveredMcpTools(tools, wrap, servers) {
445
- const wrapped = {};
446
- for (const [name, def] of Object.entries(tools)) wrapped[name] = wrap(def, deriveMcpToolMeta(name, servers));
447
- return wrapped;
263
+ function resultToString(content) {
264
+ if (!content || !Array.isArray(content)) return "";
265
+ return content.map((block) => {
266
+ if (block && typeof block === "object" && block.type === "text") {
267
+ const text = block.text;
268
+ if (typeof text === "string") return text;
269
+ }
270
+ return JSON.stringify(block);
271
+ }).join("\n");
448
272
  }
449
- const DEFAULT_MCP_BOOTSTRAP_TIMEOUT_MS = 1e4;
450
- const DEFAULT_MCP_CLOSE_TIMEOUT_MS = 5e3;
451
273
  /**
452
- * Fire a hook without letting a listener's rejection escape. A throwing
453
- * listener used to bring down the whole `Promise.all` in
454
- * `connectMcpServers`, defeating the batch's partial-failure tolerance.
455
- *
456
- * Failures are swallowed; under `ZIDANE_DEBUG` they're traced to stderr
457
- * so the misbehaving listener is still discoverable when a host opts in.
274
+ * Resolve the media type for an audio/video content block whose `data` we have.
458
275
  *
459
- * `args` is variadic to match `Hookable.callHook`'s spread shape; in
460
- * practice every `AgentHooks` event takes a single context object.
276
+ * MCP declares `mimeType` on these blocks, but it's frequently missing or
277
+ * imprecise. Strategy: when the declared type is a recognized member of the
278
+ * kind's family, reconcile it against the bytes (override only on a positive
279
+ * different match); otherwise sniff from scratch; otherwise fall back to a
280
+ * sensible default for the kind.
461
281
  */
462
- async function safeCallHook(hooks, event, ...args) {
463
- if (!hooks) return;
464
- try {
465
- await hooks.callHook(event, ...args);
466
- } catch (err) {
467
- if (process.env.ZIDANE_DEBUG) process.stderr.write(`[zidane/mcp] hook "${event}" listener rejected: ${errorMessage(err)}\n`);
468
- }
282
+ function resolveMediaType(kind, declared, b64) {
283
+ const prefix = `${kind}/`;
284
+ const fallback = kind === "audio" ? "audio/wav" : "video/mp4";
285
+ if (typeof declared === "string" && declared.startsWith(prefix)) return reconcileMediaType(kind, declared, b64);
286
+ return sniffMediaTypeFromBase64(kind, b64) ?? fallback;
469
287
  }
470
288
  /**
471
- * Close a transport best-effort. The catch swallows everything — by the
472
- * time we're closing, the bootstrap has already failed and the only thing
473
- * worse than a leaked transport is a leaked transport AND a new error
474
- * masking the original cause.
475
- */
476
- async function closeTransportQuietly(transport) {
477
- if (!transport) return;
478
- try {
479
- await transport.close();
480
- } catch {}
481
- }
482
- /**
483
- * Read the child PID off a stdio transport. Structural — only
484
- * `StdioClientTransport` exposes `pid`; HTTP/SSE transports return `null`.
485
- * Returns `null` once the child has exited (the SDK clears its process
486
- * reference on the child's `close` event), so a kill based on this value
487
- * never targets a recycled PID.
488
- */
489
- function stdioPidOf(transport) {
490
- if (transport?.__contextScoped === true) return null;
491
- const pid = transport?.pid;
492
- return typeof pid === "number" ? pid : null;
493
- }
494
- /**
495
- * Last-resort reaper for a stdio child whose transport close wedged. By the
496
- * time this runs, the graceful path (stdin EOF → SIGTERM escalation inside
497
- * the SDK, where the installed version implements it) has been abandoned —
498
- * SIGKILL is the only signal a hung child can't ignore. ESRCH (already
499
- * exited) is swallowed.
289
+ * Turn an embedded-resource blob into the matching media block, choosing the
290
+ * kind from the declared `mimeType` prefix when recognized, else by sniffing
291
+ * the bytes (image → audio → video). Returns `null` when nothing identifies it
292
+ * as media, so the caller can fall back to the JSON-stringify path.
500
293
  */
501
- function killStdioChild(pid) {
502
- if (pid == null) return;
503
- try {
504
- process.kill(pid, "SIGKILL");
505
- } catch {}
294
+ function mediaBlockFromBlob(mimeType, b64) {
295
+ const image = sniffImageMediaTypeFromBase64(b64);
296
+ const audio = sniffMediaTypeFromBase64("audio", b64);
297
+ const video = sniffMediaTypeFromBase64("video", b64);
298
+ if (mimeType?.startsWith("image/")) {
299
+ if (image) return {
300
+ type: "image",
301
+ mediaType: image,
302
+ data: b64
303
+ };
304
+ if (audio) return {
305
+ type: "audio",
306
+ mediaType: audio,
307
+ data: b64
308
+ };
309
+ if (video) return {
310
+ type: "video",
311
+ mediaType: video,
312
+ data: b64
313
+ };
314
+ return {
315
+ type: "image",
316
+ mediaType: mimeType,
317
+ data: b64
318
+ };
319
+ }
320
+ if (mimeType?.startsWith("audio/")) {
321
+ if (audio) return {
322
+ type: "audio",
323
+ mediaType: reconcileMediaType("audio", mimeType, b64),
324
+ data: b64
325
+ };
326
+ if (image) return {
327
+ type: "image",
328
+ mediaType: image,
329
+ data: b64
330
+ };
331
+ if (video) return {
332
+ type: "video",
333
+ mediaType: video,
334
+ data: b64
335
+ };
336
+ return {
337
+ type: "audio",
338
+ mediaType: mimeType,
339
+ data: b64
340
+ };
341
+ }
342
+ if (mimeType?.startsWith("video/")) {
343
+ if (video) return {
344
+ type: "video",
345
+ mediaType: reconcileMediaType("video", mimeType, b64),
346
+ data: b64
347
+ };
348
+ if (image) return {
349
+ type: "image",
350
+ mediaType: image,
351
+ data: b64
352
+ };
353
+ if (audio) return {
354
+ type: "audio",
355
+ mediaType: audio,
356
+ data: b64
357
+ };
358
+ return {
359
+ type: "video",
360
+ mediaType: mimeType,
361
+ data: b64
362
+ };
363
+ }
364
+ if (image) return {
365
+ type: "image",
366
+ mediaType: image,
367
+ data: b64
368
+ };
369
+ if (audio) return {
370
+ type: "audio",
371
+ mediaType: audio,
372
+ data: b64
373
+ };
374
+ if (video) return {
375
+ type: "video",
376
+ mediaType: video,
377
+ data: b64
378
+ };
379
+ return null;
506
380
  }
507
381
  /**
508
- * Detect failure signatures that point at a transport mismatch and return
509
- * an actionable hint string. Conservative — only matches the signals we've
510
- * actually observed in the wild against MCP servers, so a real outage on
511
- * the right transport doesn't get misdiagnosed.
382
+ * Normalize MCP `CallToolResult.content` to zidane's {@link ToolResultContent[]} shape.
512
383
  *
513
- * Streamable-HTTP against an SSE-only endpoint:
514
- * - The transport opens an `initialize` POST. SSE-only servers respond
515
- * with `405 Method Not Allowed` or `404 Not Found` (their `/sse` GET
516
- * endpoint is the only handler). The SDK wraps these as
517
- * `Error POSTing to endpoint (HTTP 40X): ...`.
518
- * - Some proxies return `400` with a `text/event-stream` mismatch on
519
- * POST; same root cause, same fix.
384
+ * Handles the MCP content block types:
385
+ * - `text` → preserved as `{type:'text', text}`
386
+ * - `image` → preserved as `{type:'image', mediaType, data}` (MCP uses `mimeType`)
387
+ * - `audio` → `{type:'audio', mediaType, data}`; mediaType reconciled/sniffed when
388
+ * the declared `mimeType` is missing or not an `audio/*` type
389
+ * - `video` → `{type:'video', mediaType, data}` (non-standard, by analogy)
390
+ * - `resource` with embedded text → flattened to a text block
391
+ * - `resource` with embedded blob → routed to image/audio/video by sniffing bytes
392
+ * first, with declared `mimeType` as a fallback when bytes are unidentifiable
393
+ * (it's OPTIONAL on EmbeddedResource per the MCP spec, and often imprecise)
394
+ * - Any unrecognized block → JSON-stringified fallback text block (lossy but safe)
395
+ *
396
+ * Returns `null` when the input is not an array — callers should fall back to an empty
397
+ * result in that case.
520
398
  */
521
- function buildTransportMismatchHint(config, err) {
522
- const msg = err.message;
523
- if (config.transport === "streamable-http") {
524
- if (/\bHTTP\s*40[45]\b/i.test(msg) || /\(40[45]\)/.test(msg)) return "This server may not implement the streamable-HTTP transport. Try `transport: 'sse'` (or `sseUrl` instead of `url`) on this server's config.";
525
- }
526
- if (config.transport === "stdio" && config.scope === "context") {
527
- if (/connection closed/i.test(msg) || /before initialization|closed during connect/i.test(msg)) return `The server exited during startup. With \`scope: 'context'\` it runs INSIDE the execution context, so \`${config.command}\` must exist in that image (check \`mcp:warn\` for the child's stderr, e.g. "command not found"). Install the runtime in the context image, or use \`scope: 'host'\` if this server does not need the workspace filesystem.`;
399
+ function normalizeMcpBlocks(content) {
400
+ if (!Array.isArray(content)) return null;
401
+ const out = [];
402
+ for (const raw of content) {
403
+ if (!raw || typeof raw !== "object") continue;
404
+ const block = raw;
405
+ if (block.type === "text" && typeof block.text === "string") {
406
+ out.push({
407
+ type: "text",
408
+ text: block.text
409
+ });
410
+ continue;
411
+ }
412
+ if (block.type === "image" && typeof block.data === "string") {
413
+ const declared = typeof block.mimeType === "string" ? block.mimeType : typeof block.mediaType === "string" ? block.mediaType : "image/png";
414
+ const mediaType = reconcileImageMediaType(declared, block.data);
415
+ out.push({
416
+ type: "image",
417
+ mediaType,
418
+ data: block.data
419
+ });
420
+ continue;
421
+ }
422
+ if (block.type === "audio" && typeof block.data === "string") {
423
+ out.push({
424
+ type: "audio",
425
+ mediaType: resolveMediaType("audio", block.mimeType ?? block.mediaType, block.data),
426
+ data: block.data
427
+ });
428
+ continue;
429
+ }
430
+ if (block.type === "video" && typeof block.data === "string") {
431
+ out.push({
432
+ type: "video",
433
+ mediaType: resolveMediaType("video", block.mimeType ?? block.mediaType, block.data),
434
+ data: block.data
435
+ });
436
+ continue;
437
+ }
438
+ if (block.type === "resource" && block.resource && typeof block.resource === "object") {
439
+ const res = block.resource;
440
+ if (typeof res.text === "string") {
441
+ out.push({
442
+ type: "text",
443
+ text: res.text
444
+ });
445
+ continue;
446
+ }
447
+ if (typeof res.blob === "string") {
448
+ const mediaBlock = mediaBlockFromBlob(typeof res.mimeType === "string" ? res.mimeType : void 0, res.blob);
449
+ if (mediaBlock) {
450
+ out.push(mediaBlock);
451
+ continue;
452
+ }
453
+ }
454
+ }
455
+ out.push({
456
+ type: "text",
457
+ text: JSON.stringify(block)
458
+ });
528
459
  }
529
- return null;
460
+ return out;
530
461
  }
531
462
  /**
532
- * Wrap a bootstrap failure so the surfaced `Error` carries an actionable
533
- * hint while preserving the original message + stack + cause. Returns the
534
- * input error verbatim when no hint applies.
463
+ * Route the MCP result content through the narrowest appropriate zidane shape:
535
464
  *
536
- * `cause` (ES2022) gives hosts that introspect a way back to the raw SDK
537
- * error; the human-readable hint is the new message body that lands in
538
- * logs / hook payloads.
465
+ * - All blocks are `text` → return a joined string (smaller wire payload,
466
+ * string-friendly for hook consumers that don't need structured access).
467
+ * - Any block is non-text → return a structured `ToolResultContent[]`.
468
+ * - Empty / non-array input → return `''`.
539
469
  */
540
- function withTransportMismatchHint(config, err) {
541
- const hint = buildTransportMismatchHint(config, err);
542
- if (!hint) return err;
543
- const augmented = new Error(`${err.message}\nHint: ${hint}`, { cause: err });
544
- if (err.stack) augmented.stack = err.stack;
545
- return augmented;
470
+ function packMcpResult(content) {
471
+ const normalized = normalizeMcpBlocks(content);
472
+ if (!normalized || normalized.length === 0) return "";
473
+ const parts = [];
474
+ for (const block of normalized) {
475
+ if (block.type !== "text") return normalized;
476
+ parts.push(block.text);
477
+ }
478
+ return parts.join("\n");
546
479
  }
480
+ //#endregion
481
+ //#region src/mcp/server-config.ts
547
482
  /**
548
483
  * The config a transport is actually built from.
549
484
  *
@@ -560,69 +495,15 @@ function withTransportMismatchHint(config, err) {
560
495
  */
561
496
  function resolveTransportConfig(config) {
562
497
  return config.preExpanded ? config : expandMcpConfigEnv(config);
563
- }
564
- /**
565
- * Create an Agent Plugin server's `${PLUGIN_DATA}` directory, immediately
566
- * before the process that needs it is spawned.
567
- *
568
- * Creation lives here rather than in the plugin loader because loading is not
569
- * consent. A host must load every plugin it finds on disk just to learn its
570
- * name — identity lives in the manifest — and the user's `enabledPlugins` gate
571
- * only applies afterwards. Creating at load time therefore left a directory
572
- * behind for every plugin the user had switched OFF, on every discovery pass.
573
- * Spawn time is the first moment the answer is unambiguous.
574
- *
575
- * Gated on `preExpanded` AND `env.PLUGIN_DATA`. `preExpanded` is the explicit
576
- * loader-set marker; `env.PLUGIN_DATA` supplies the path. Requiring both keeps
577
- * a hand-written `mcps.json` that happens to set `PLUGIN_DATA` in its own env
578
- * from triggering directory creation off a value we never validated — a
579
- * filesystem side effect should not key off a user-settable name when an
580
- * unambiguous marker is already on the config.
581
- *
582
- * `cwd` is created too when it sits inside the data directory. A plugin may
583
- * legitimately declare `cwd: "${PLUGIN_DATA}/work"`, and spawning with a
584
- * non-existent `cwd` fails with a bare ENOENT. A `cwd` under `PLUGIN_ROOT` is
585
- * NOT created — that path is package content the author was supposed to ship,
586
- * and silently conjuring it would hide the mistake.
587
- *
588
- * Failures are swallowed deliberately: the spawn immediately after produces a
589
- * far better diagnostic than a thrown mkdir, and this must not be the thing
590
- * that takes a server down.
591
- */
592
- function ensurePluginDataDir(config) {
593
- if (!config.preExpanded) return;
594
- const dataDir = config.env?.PLUGIN_DATA;
595
- if (!dataDir) return;
596
- try {
597
- mkdirSync(dataDir, { recursive: true });
598
- if (config.cwd && config.cwd !== dataDir && config.cwd.startsWith(dataDir + sep)) mkdirSync(config.cwd, { recursive: true });
599
- } catch {}
600
- }
601
- /**
602
- * Origin-pinned `fetch` for a remote server that opted into header pinning,
603
- * or `undefined` when the server didn't (the overwhelmingly common case, and
604
- * the one where we leave redirect handling to the platform).
605
- *
606
- * Deliberately does NOT require `config.headers`: the credential most worth
607
- * pinning is often the one the config never mentions. `bootstrapServer`
608
- * attaches an OAuth provider to a FLAGLESS server whenever the host can build
609
- * one and stored tokens exist, and an Agent Plugins `mcp.json` cannot declare
610
- * `auth` at all — so a plugin's remote server routinely ends up with an
611
- * SDK-injected `Authorization` header and an empty `config.headers`.
612
- * `originPinnedFetch` always protects `authorization` for the same reason.
613
- */
614
- function pinnedFetchFor(config, baseFetch) {
615
- if (!config.pinHeaderOrigin || !config.url) return void 0;
616
- return originPinnedFetch(config.url, Object.keys(config.headers ?? {}), baseFetch ?? fetch);
617
- }
618
- function inferTransport(raw) {
619
- if (raw.transport === "stdio" || raw.transport === "sse" || raw.transport === "streamable-http") return raw.transport;
620
- if (raw.type === "stdio" || raw.type === "sse" || raw.type === "streamable-http" || raw.type === "http") return raw.type === "http" ? "streamable-http" : raw.type;
621
- if (raw.command) return "stdio";
622
- if (raw.httpUrl) return "streamable-http";
623
- if (raw.sseUrl) return "sse";
624
- if (raw.url) return "streamable-http";
625
- throw new Error(`Cannot infer MCP transport from config: ${JSON.stringify(raw)}`);
498
+ }
499
+ function inferTransport(raw) {
500
+ if (raw.transport === "stdio" || raw.transport === "sse" || raw.transport === "streamable-http") return raw.transport;
501
+ if (raw.type === "stdio" || raw.type === "sse" || raw.type === "streamable-http" || raw.type === "http") return raw.type === "http" ? "streamable-http" : raw.type;
502
+ if (raw.command) return "stdio";
503
+ if (raw.httpUrl) return "streamable-http";
504
+ if (raw.sseUrl) return "sse";
505
+ if (raw.url) return "streamable-http";
506
+ throw new Error(`Cannot infer MCP transport from config: ${JSON.stringify(raw)}`);
626
507
  }
627
508
  /**
628
509
  * Placeholder grammar for {@link expandEnvPlaceholders}: `${VAR}` and
@@ -652,7 +533,7 @@ function expandEnvPlaceholders(value, env = process.env) {
652
533
  if (resolved !== void 0 && resolved !== "") return resolved;
653
534
  if (fallback !== void 0) return fallback;
654
535
  if (resolved !== void 0) return resolved;
655
- if (process.env.ZIDANE_DEBUG) process.stderr.write(`[zidane/mcp] env placeholder "${match}" is not set — passed through literally\n`);
536
+ if (process.env.ZIDANE_DEBUG) process.stderr.write(`[zidane/mcp] env placeholder "${match}" is not set, passed through as written\n`);
656
537
  return match;
657
538
  });
658
539
  }
@@ -684,6 +565,34 @@ function expandMcpConfigEnv(config, env = process.env) {
684
565
  ...config.headers ? { headers: expandEnvRecord(config.headers, env) } : {}
685
566
  };
686
567
  }
568
+ /**
569
+ * A config from a source that must never read this process's environment
570
+ * (a machine's project file, `docs/MACHINES.md`): every `${VAR}` placeholder
571
+ * resolved NOW to its `:-default`, else to the empty string, so the
572
+ * transport's own expansion has nothing of this process's secrets to put
573
+ * into a command, an env value, a URL or a header.
574
+ */
575
+ function withoutEnvPlaceholders(config) {
576
+ const strip = (value) => {
577
+ let out = value;
578
+ for (let pass = 0; pass < 4 && ENV_PLACEHOLDER_RE.test(out); pass++) {
579
+ ENV_PLACEHOLDER_RE.lastIndex = 0;
580
+ out = out.replace(ENV_PLACEHOLDER_RE, (_match, _name, fallback) => fallback ?? "");
581
+ }
582
+ ENV_PLACEHOLDER_RE.lastIndex = 0;
583
+ return out.replace(ENV_PLACEHOLDER_RE, "");
584
+ };
585
+ const record = (r) => Object.fromEntries(Object.entries(r).map(([k, v]) => [k, strip(v)]));
586
+ return {
587
+ ...config,
588
+ ...config.command !== void 0 ? { command: strip(config.command) } : {},
589
+ ...config.args ? { args: config.args.map((arg) => typeof arg === "string" ? strip(arg) : arg) } : {},
590
+ ...config.env ? { env: record(config.env) } : {},
591
+ ...config.cwd !== void 0 ? { cwd: strip(config.cwd) } : {},
592
+ ...config.url !== void 0 ? { url: strip(config.url) } : {},
593
+ ...config.headers ? { headers: record(config.headers) } : {}
594
+ };
595
+ }
687
596
  function normalizeOne(name, raw) {
688
597
  const transport = inferTransport(raw);
689
598
  const url = raw.url ?? raw.httpUrl ?? raw.sseUrl;
@@ -755,230 +664,212 @@ function normalizeMcpServers(input) {
755
664
  }
756
665
  throw new Error(`Unsupported MCP server config shape: ${typeof input}`);
757
666
  }
667
+ //#endregion
668
+ //#region src/mcp/sse-to-json-fetch.ts
758
669
  /**
759
- * Lossy flattener — converts MCP `CallToolResult.content` blocks to a single
760
- * string. Text blocks are extracted; non-text blocks are JSON-stringified.
670
+ * `fetch` shim that converts streamable-http POST responses with
671
+ * `Content-Type: text/event-stream` into synthetic `application/json`
672
+ * responses, preserving every original header (notably `mcp-session-id`).
761
673
  *
762
- * Use this only at UI / log boundaries that require a string. The agent
763
- * loop itself routes through {@link normalizeMcpBlocks} so image blocks
764
- * survive into provider-native tool_result content (Anthropic blocks,
765
- * OpenAI companion-user-message).
674
+ * Why this exists
675
+ * ----------------
676
+ * The MCP SDK's streamable-http transport handles POST responses two ways
677
+ * depending on `content-type`:
678
+ *
679
+ * - `application/json` → `await response.json()` (works on every runtime).
680
+ * - `text/event-stream` → `body.pipeThrough(TextDecoderStream)
681
+ * .pipeThrough(EventSourceParserStream)`
682
+ * and forward each parsed event to `onmessage`.
683
+ *
684
+ * On Bun + MCP SDK 1.29.x the second pipeline silently drops the parsed
685
+ * event: the full `event: message\ndata: {...}\n\n` arrives intact, the
686
+ * stream closes, but `onmessage` is never called. Bootstrap then waits the
687
+ * full `bootstrapTimeout` (10s default) and the agent loses every tool the
688
+ * server would have exposed.
689
+ *
690
+ * The MCP spec lets clients accept either content type
691
+ * (`Accept: application/json, text/event-stream`), so flipping the stream
692
+ * to JSON at the boundary is a transparent, server-agnostic workaround.
693
+ *
694
+ * Scope of the shim
695
+ * -----------------
696
+ * - POST + `text/event-stream` → drain, parse SSE message events, return
697
+ * a synthetic `application/json` response. Single event becomes a JSON
698
+ * object (matching the shape the SDK expects from non-streaming
699
+ * servers); multiple events become a JSON array (the SDK already
700
+ * iterates `Array.isArray(data)` from a JSON response).
701
+ * - GET (long-lived `_startOrAuthSse` listener) → untouched. Per-event
702
+ * latency matters there and the SSE pipeline isn't always broken on
703
+ * GET in the same way (different code path inside Bun's stream impl).
704
+ * - 202 / non-SSE / malformed SSE / no body → passthrough.
705
+ *
706
+ * Runtime gating
707
+ * --------------
708
+ * `sseToJsonFetchIfNeeded()` only returns a wrapper on Bun. Node + browser
709
+ * runtimes get `undefined` and the SDK uses global `fetch` directly — no
710
+ * extra buffer-and-redrain on the happy path, and no risk of collapsing a
711
+ * future progress-streaming response into a single batched array. If you
712
+ * need to apply the shim unconditionally (testing, custom hosts), call
713
+ * `sseToJsonFetch()` directly.
714
+ *
715
+ * Cleanup
716
+ * -------
717
+ * Remove this file and its `fetch:` injection in `createTransport` once
718
+ * either Bun fixes the `pipeThrough` chain or the SDK switches off the
719
+ * streaming pipeline by default.
766
720
  */
767
- function resultToString(content) {
768
- if (!content || !Array.isArray(content)) return "";
769
- return content.map((block) => {
770
- if (block && typeof block === "object" && block.type === "text") {
771
- const text = block.text;
772
- if (typeof text === "string") return text;
773
- }
774
- return JSON.stringify(block);
775
- }).join("\n");
721
+ /**
722
+ * Detect whether we're running on Bun. The `Bun` global is set by the
723
+ * runtime itself and isn't faked by Bun's Node-compat layer.
724
+ */
725
+ function isBunRuntime() {
726
+ return typeof globalThis.Bun !== "undefined";
776
727
  }
777
728
  /**
778
- * Resolve the media type for an audio/video content block whose `data` we have.
729
+ * Returns the `sseToJsonFetch` wrapper only on runtimes that need it
730
+ * (currently Bun). Returns `undefined` everywhere else, which makes the
731
+ * SDK fall back to the global `fetch` and keeps the streaming pipeline
732
+ * intact on Node where it works correctly.
779
733
  *
780
- * MCP declares `mimeType` on these blocks, but it's frequently missing or
781
- * imprecise. Strategy: when the declared type is a recognized member of the
782
- * kind's family, reconcile it against the bytes (override only on a positive
783
- * different match); otherwise sniff from scratch; otherwise fall back to a
784
- * sensible default for the kind.
734
+ * Designed as the value to pass directly to `StreamableHTTPClientTransport`'s
735
+ * `fetch` option:
736
+ *
737
+ * new StreamableHTTPClientTransport(url, {
738
+ * fetch: sseToJsonFetchIfNeeded(),
739
+ * })
785
740
  */
786
- function resolveMediaType(kind, declared, b64) {
787
- const prefix = `${kind}/`;
788
- const fallback = kind === "audio" ? "audio/wav" : "video/mp4";
789
- if (typeof declared === "string" && declared.startsWith(prefix)) return reconcileMediaType(kind, declared, b64);
790
- return sniffMediaTypeFromBase64(kind, b64) ?? fallback;
741
+ function sseToJsonFetchIfNeeded() {
742
+ return isBunRuntime() ? sseToJsonFetch() : void 0;
791
743
  }
792
744
  /**
793
- * Turn an embedded-resource blob into the matching media block, choosing the
794
- * kind from the declared `mimeType` prefix when recognized, else by sniffing
795
- * the bytes (image → audio → video). Returns `null` when nothing identifies it
796
- * as media, so the caller can fall back to the JSON-stringify path.
745
+ * Wrap a `fetch` implementation so streamable-http POST responses that come
746
+ * back as `text/event-stream` are converted to JSON. Pass the result as
747
+ * `opts.fetch` to `StreamableHTTPClientTransport`.
748
+ *
749
+ * Always-on (i.e. unconditional) — for the runtime-gated entry point,
750
+ * use {@link sseToJsonFetchIfNeeded} instead.
797
751
  */
798
- function mediaBlockFromBlob(mimeType, b64) {
799
- const image = sniffImageMediaTypeFromBase64(b64);
800
- const audio = sniffMediaTypeFromBase64("audio", b64);
801
- const video = sniffMediaTypeFromBase64("video", b64);
802
- if (mimeType?.startsWith("image/")) {
803
- if (image) return {
804
- type: "image",
805
- mediaType: image,
806
- data: b64
807
- };
808
- if (audio) return {
809
- type: "audio",
810
- mediaType: audio,
811
- data: b64
812
- };
813
- if (video) return {
814
- type: "video",
815
- mediaType: video,
816
- data: b64
817
- };
818
- return {
819
- type: "image",
820
- mediaType: mimeType,
821
- data: b64
822
- };
823
- }
824
- if (mimeType?.startsWith("audio/")) {
825
- if (audio) return {
826
- type: "audio",
827
- mediaType: reconcileMediaType("audio", mimeType, b64),
828
- data: b64
829
- };
830
- if (image) return {
831
- type: "image",
832
- mediaType: image,
833
- data: b64
834
- };
835
- if (video) return {
836
- type: "video",
837
- mediaType: video,
838
- data: b64
839
- };
840
- return {
841
- type: "audio",
842
- mediaType: mimeType,
843
- data: b64
844
- };
845
- }
846
- if (mimeType?.startsWith("video/")) {
847
- if (video) return {
848
- type: "video",
849
- mediaType: reconcileMediaType("video", mimeType, b64),
850
- data: b64
851
- };
852
- if (image) return {
853
- type: "image",
854
- mediaType: image,
855
- data: b64
856
- };
857
- if (audio) return {
858
- type: "audio",
859
- mediaType: audio,
860
- data: b64
861
- };
862
- return {
863
- type: "video",
864
- mediaType: mimeType,
865
- data: b64
866
- };
867
- }
868
- if (image) return {
869
- type: "image",
870
- mediaType: image,
871
- data: b64
872
- };
873
- if (audio) return {
874
- type: "audio",
875
- mediaType: audio,
876
- data: b64
877
- };
878
- if (video) return {
879
- type: "video",
880
- mediaType: video,
881
- data: b64
752
+ function sseToJsonFetch(baseFetch = fetch) {
753
+ return async function sseToJsonWrappedFetch(input, init) {
754
+ const response = await baseFetch(input, init);
755
+ if ((init?.method ?? "GET").toString().toUpperCase() !== "POST") return response;
756
+ const contentType = response.headers.get("content-type");
757
+ if (!contentType || !contentType.includes("text/event-stream")) return response;
758
+ if (!response.body) return response;
759
+ let raw;
760
+ try {
761
+ raw = await response.text();
762
+ } catch {
763
+ return response;
764
+ }
765
+ const events = parseSseDataEvents(raw);
766
+ return synthesizeJsonResponse(response, events.length === 1 ? events[0] : events);
882
767
  };
883
- return null;
884
768
  }
885
769
  /**
886
- * Normalize MCP `CallToolResult.content` to zidane's {@link ToolResultContent[]} shape.
887
- *
888
- * Handles the MCP content block types:
889
- * - `text` → preserved as `{type:'text', text}`
890
- * - `image` → preserved as `{type:'image', mediaType, data}` (MCP uses `mimeType`)
891
- * - `audio` → `{type:'audio', mediaType, data}`; mediaType reconciled/sniffed when
892
- * the declared `mimeType` is missing or not an `audio/*` type
893
- * - `video` → `{type:'video', mediaType, data}` (non-standard, by analogy)
894
- * - `resource` with embedded text → flattened to a text block
895
- * - `resource` with embedded blob → routed to image/audio/video by sniffing bytes
896
- * first, with declared `mimeType` as a fallback when bytes are unidentifiable
897
- * (it's OPTIONAL on EmbeddedResource per the MCP spec, and often imprecise)
898
- * - Any unrecognized block → JSON-stringified fallback text block (lossy but safe)
770
+ * Parse a buffered SSE body into the JSON payloads of its `message` events.
899
771
  *
900
- * Returns `null` when the input is not an array — callers should fall back to an empty
901
- * result in that case.
772
+ * Skips:
773
+ * - SSE comments (lines starting with `:`).
774
+ * - Non-default event types (`event: foo` ≠ `message`). The MCP server
775
+ * only ever emits `message` events for JSON-RPC; anything else is out of
776
+ * band and the SDK wouldn't have surfaced it to `onmessage` either.
777
+ * - Malformed `data:` payloads (anything that fails `JSON.parse`).
902
778
  */
903
- function normalizeMcpBlocks(content) {
904
- if (!Array.isArray(content)) return null;
905
- const out = [];
906
- for (const raw of content) {
907
- if (!raw || typeof raw !== "object") continue;
908
- const block = raw;
909
- if (block.type === "text" && typeof block.text === "string") {
910
- out.push({
911
- type: "text",
912
- text: block.text
913
- });
914
- continue;
915
- }
916
- if (block.type === "image" && typeof block.data === "string") {
917
- const mediaType = reconcileImageMediaType(typeof block.mimeType === "string" ? block.mimeType : typeof block.mediaType === "string" ? block.mediaType : "image/png", block.data);
918
- out.push({
919
- type: "image",
920
- mediaType,
921
- data: block.data
922
- });
923
- continue;
924
- }
925
- if (block.type === "audio" && typeof block.data === "string") {
926
- out.push({
927
- type: "audio",
928
- mediaType: resolveMediaType("audio", block.mimeType ?? block.mediaType, block.data),
929
- data: block.data
930
- });
931
- continue;
932
- }
933
- if (block.type === "video" && typeof block.data === "string") {
934
- out.push({
935
- type: "video",
936
- mediaType: resolveMediaType("video", block.mimeType ?? block.mediaType, block.data),
937
- data: block.data
938
- });
939
- continue;
940
- }
941
- if (block.type === "resource" && block.resource && typeof block.resource === "object") {
942
- const res = block.resource;
943
- if (typeof res.text === "string") {
944
- out.push({
945
- type: "text",
946
- text: res.text
947
- });
948
- continue;
949
- }
950
- if (typeof res.blob === "string") {
951
- const mediaBlock = mediaBlockFromBlob(typeof res.mimeType === "string" ? res.mimeType : void 0, res.blob);
952
- if (mediaBlock) {
953
- out.push(mediaBlock);
954
- continue;
955
- }
779
+ function parseSseDataEvents(raw) {
780
+ const events = [];
781
+ for (const block of raw.split(/\r?\n\r?\n/)) {
782
+ if (!block.trim()) continue;
783
+ const dataLines = [];
784
+ let isMessageEvent = true;
785
+ for (const line of block.split(/\r?\n/)) {
786
+ if (line.startsWith(":")) continue;
787
+ if (line.startsWith("event:")) {
788
+ const eventType = line.slice(6).trim();
789
+ if (eventType && eventType !== "message") isMessageEvent = false;
790
+ } else if (line.startsWith("data:")) {
791
+ const value = line.slice(5);
792
+ dataLines.push(value.startsWith(" ") ? value.slice(1) : value);
956
793
  }
957
794
  }
958
- out.push({
959
- type: "text",
960
- text: JSON.stringify(block)
961
- });
795
+ if (!isMessageEvent || dataLines.length === 0) continue;
796
+ try {
797
+ events.push(JSON.parse(dataLines.join("\n")));
798
+ } catch {}
962
799
  }
963
- return out;
800
+ return events;
964
801
  }
965
802
  /**
966
- * Route the MCP result content through the narrowest appropriate zidane shape:
803
+ * Build a `Response` mirroring the original's status / statusText / headers
804
+ * but with a JSON body and `content-type: application/json`. Header
805
+ * preservation is the whole point — `mcp-session-id` is set by the server
806
+ * on the initialize POST and must round-trip into the SDK's
807
+ * `_sessionId` capture (`response.headers.get('mcp-session-id')`).
808
+ */
809
+ function synthesizeJsonResponse(original, payload) {
810
+ const headers = new Headers(original.headers);
811
+ headers.set("content-type", "application/json");
812
+ return new Response(JSON.stringify(payload), {
813
+ status: original.status,
814
+ statusText: original.statusText,
815
+ headers
816
+ });
817
+ }
818
+ //#endregion
819
+ //#region src/mcp/transport.ts
820
+ /**
821
+ * Create an Agent Plugin server's `${PLUGIN_DATA}` directory, immediately
822
+ * before the process that needs it is spawned.
967
823
  *
968
- * - All blocks are `text` → return a joined string (smaller wire payload,
969
- * string-friendly for hook consumers that don't need structured access).
970
- * - Any block is non-text → return a structured `ToolResultContent[]`.
971
- * - Empty / non-array input → return `''`.
824
+ * Creation lives here rather than in the plugin loader because loading is not
825
+ * consent. A host must load every plugin it finds on disk just to learn its
826
+ * name — identity lives in the manifest — and the user's `enabledPlugins` gate
827
+ * only applies afterwards. Creating at load time therefore left a directory
828
+ * behind for every plugin the user had switched OFF, on every discovery pass.
829
+ * Spawn time is the first moment the answer is unambiguous.
830
+ *
831
+ * Gated on `preExpanded` AND `env.PLUGIN_DATA`. `preExpanded` is the explicit
832
+ * loader-set marker; `env.PLUGIN_DATA` supplies the path. Requiring both keeps
833
+ * a hand-written `mcps.json` that happens to set `PLUGIN_DATA` in its own env
834
+ * from triggering directory creation off a value we never validated — a
835
+ * filesystem side effect should not key off a user-settable name when an
836
+ * unambiguous marker is already on the config.
837
+ *
838
+ * `cwd` is created too when it sits inside the data directory. A plugin may
839
+ * legitimately declare `cwd: "${PLUGIN_DATA}/work"`, and spawning with a
840
+ * non-existent `cwd` fails with a bare ENOENT. A `cwd` under `PLUGIN_ROOT` is
841
+ * NOT created — that path is package content the author was supposed to ship,
842
+ * and silently conjuring it would hide the mistake.
843
+ *
844
+ * Failures are swallowed deliberately: the spawn immediately after produces a
845
+ * far better diagnostic than a thrown mkdir, and this must not be the thing
846
+ * that takes a server down.
972
847
  */
973
- function packMcpResult(content) {
974
- const normalized = normalizeMcpBlocks(content);
975
- if (!normalized || normalized.length === 0) return "";
976
- const parts = [];
977
- for (const block of normalized) {
978
- if (block.type !== "text") return normalized;
979
- parts.push(block.text);
980
- }
981
- return parts.join("\n");
848
+ function ensurePluginDataDir(config) {
849
+ if (!config.preExpanded) return;
850
+ const dataDir = config.env?.PLUGIN_DATA;
851
+ if (!dataDir) return;
852
+ try {
853
+ mkdirSync(dataDir, { recursive: true });
854
+ if (config.cwd && config.cwd !== dataDir && config.cwd.startsWith(dataDir + sep)) mkdirSync(config.cwd, { recursive: true });
855
+ } catch {}
856
+ }
857
+ /**
858
+ * Origin-pinned `fetch` for a remote server that opted into header pinning,
859
+ * or `undefined` when the server didn't (the overwhelmingly common case, and
860
+ * the one where we leave redirect handling to the platform).
861
+ *
862
+ * Deliberately does NOT require `config.headers`: the credential most worth
863
+ * pinning is often the one the config never mentions. `bootstrapServer`
864
+ * attaches an OAuth provider to a FLAGLESS server whenever the host can build
865
+ * one and stored tokens exist, and an Agent Plugins `mcp.json` cannot declare
866
+ * `auth` at all — so a plugin's remote server routinely ends up with an
867
+ * SDK-injected `Authorization` header and an empty `config.headers`.
868
+ * `originPinnedFetch` always protects `authorization` for the same reason.
869
+ */
870
+ function pinnedFetchFor(config, baseFetch) {
871
+ if (!config.pinHeaderOrigin || !config.url) return void 0;
872
+ return originPinnedFetch(config.url, Object.keys(config.headers ?? {}), baseFetch ?? fetch);
982
873
  }
983
874
  /**
984
875
  * Create the appropriate MCP transport for a server config.
@@ -1001,7 +892,7 @@ async function createTransport(rawConfig, authProvider, hooks, execution) {
1001
892
  case "stdio": {
1002
893
  if (config.scope === "context") {
1003
894
  if (!execution) throw new Error(`MCP server "${config.name}" requests \`scope: 'context'\`, but no execution context was supplied to the connector. Pass \`execution\` to connectMcpServers(), or use \`scope: 'host'\`.`);
1004
- const { createContextStdioTransport } = await import("./context-transport-T_6AYIvy.js");
895
+ const { createContextStdioTransport } = await import("./context-transport-DSMTUsK1.js");
1005
896
  const executionHandle = await execution.getHandle();
1006
897
  let commandLine = [config.command, ...config.args ?? []].map(shellQuote).join(" ");
1007
898
  let env = config.env;
@@ -1020,7 +911,7 @@ async function createTransport(rawConfig, authProvider, hooks, execution) {
1020
911
  ...hooks ? { hooks } : {}
1021
912
  });
1022
913
  }
1023
- const { StdioClientTransport, getDefaultEnvironment } = await import("./stdio-loader-Bbx19dMZ.js");
914
+ const { StdioClientTransport, getDefaultEnvironment } = await import("./stdio-loader-C830Vg-Z.js");
1024
915
  const mergedEnv = config.env && !config.strictEnv ? {
1025
916
  ...getDefaultEnvironment(),
1026
917
  ...config.env
@@ -1060,7 +951,7 @@ async function createTransport(rawConfig, authProvider, hooks, execution) {
1060
951
  /**
1061
952
  * Split a stdio-transport child's stderr into lines and surface them via `mcp:warn`.
1062
953
  */
1063
- const MAX_MCP_STDERR_LINE_CHARS = 64 * 1024;
954
+ const MAX_MCP_STDERR_LINE_CHARS = 65536;
1064
955
  /**
1065
956
  * Line-splitting core shared by the two stderr pumps: the Node-stream one
1066
957
  * below (host stdio children) and the async-iterable one in
@@ -1113,6 +1004,156 @@ function attachStderrWarnPump(transport, serverName, hooks) {
1113
1004
  stream.on("close", flush);
1114
1005
  stream.on("error", flush);
1115
1006
  }
1007
+ //#endregion
1008
+ //#region src/mcp/index.ts
1009
+ /**
1010
+ * Non-owning view of a live MCP connection: the SAME tools and
1011
+ * instructions, but a no-op `close`. Handed to consumers that must never
1012
+ * tear down transports they don't own — the loop's per-call
1013
+ * `ToolContext.mcpConnection` and child agents spawned off the parent's
1014
+ * connection — because only the owning agent's destroy path may close the
1015
+ * real thing, and a doc comment alone is too weak a guard. Single shared
1016
+ * builder so the call sites can't drift when this shape grows a field.
1017
+ */
1018
+ function nonOwningMcpView(connection) {
1019
+ return {
1020
+ tools: connection.tools,
1021
+ ...connection.instructions !== void 0 ? { instructions: connection.instructions } : {},
1022
+ close: async () => {}
1023
+ };
1024
+ }
1025
+ /**
1026
+ * Recover a discovered tool's `{ server, toolName }` from its namespaced name
1027
+ * (`mcp_<server>_<tool>`). Both server and tool names can contain underscores,
1028
+ * so this matches against the known server list by longest server-name prefix
1029
+ * rather than splitting blindly. Tools that match no configured server (custom
1030
+ * connectors with a different naming scheme) fall back to
1031
+ * `{ server: '', toolName: <namespacedName> }`.
1032
+ */
1033
+ function deriveMcpToolMeta(namespacedName, servers) {
1034
+ let best;
1035
+ for (const server of servers) {
1036
+ const prefix = `mcp_${server.name}_`;
1037
+ if (namespacedName.startsWith(prefix) && (!best || server.name.length > best.name.length)) best = server;
1038
+ }
1039
+ if (best) return {
1040
+ server: best.name,
1041
+ toolName: namespacedName.slice(`mcp_${best.name}_`.length)
1042
+ };
1043
+ return {
1044
+ server: "",
1045
+ toolName: namespacedName
1046
+ };
1047
+ }
1048
+ /**
1049
+ * Apply an {@link McpToolWrap} to every entry of a discovered-tool map,
1050
+ * resolving each tool's {@link McpToolMeta} from `servers`. Returns a fresh
1051
+ * map; the input is untouched. Used by the agent to apply the
1052
+ * `AgentOptions.mcpToolWrap` hook to discovered tools.
1053
+ */
1054
+ function wrapDiscoveredMcpTools(tools, wrap, servers) {
1055
+ const wrapped = {};
1056
+ for (const [name, def] of Object.entries(tools)) wrapped[name] = wrap(def, deriveMcpToolMeta(name, servers));
1057
+ return wrapped;
1058
+ }
1059
+ const DEFAULT_MCP_BOOTSTRAP_TIMEOUT_MS = 1e4;
1060
+ const DEFAULT_MCP_CLOSE_TIMEOUT_MS = 5e3;
1061
+ /**
1062
+ * Fire a hook without letting a listener's rejection escape. A throwing
1063
+ * listener used to bring down the whole `Promise.all` in
1064
+ * `connectMcpServers`, defeating the batch's partial-failure tolerance.
1065
+ *
1066
+ * Failures are swallowed; under `ZIDANE_DEBUG` they're traced to stderr
1067
+ * so the misbehaving listener is still discoverable when a host opts in.
1068
+ *
1069
+ * `args` is variadic to match `Hookable.callHook`'s spread shape; in
1070
+ * practice every `AgentHooks` event takes a single context object.
1071
+ */
1072
+ async function safeCallHook(hooks, event, ...args) {
1073
+ if (!hooks) return;
1074
+ try {
1075
+ await hooks.callHook(event, ...args);
1076
+ } catch (err) {
1077
+ if (process.env.ZIDANE_DEBUG) process.stderr.write(`[zidane/mcp] hook "${event}" listener rejected: ${errorMessage(err)}\n`);
1078
+ }
1079
+ }
1080
+ /**
1081
+ * Close a transport best-effort. The catch swallows everything — by the
1082
+ * time we're closing, the bootstrap has already failed and the only thing
1083
+ * worse than a leaked transport is a leaked transport AND a new error
1084
+ * masking the original cause.
1085
+ */
1086
+ async function closeTransportQuietly(transport) {
1087
+ if (!transport) return;
1088
+ try {
1089
+ await transport.close();
1090
+ } catch {}
1091
+ }
1092
+ /**
1093
+ * Read the child PID off a stdio transport. Structural — only
1094
+ * `StdioClientTransport` exposes `pid`; HTTP/SSE transports return `null`.
1095
+ * Returns `null` once the child has exited (the SDK clears its process
1096
+ * reference on the child's `close` event), so a kill based on this value
1097
+ * never targets a recycled PID.
1098
+ */
1099
+ function stdioPidOf(transport) {
1100
+ if (transport?.__contextScoped === true) return null;
1101
+ const pid = transport?.pid;
1102
+ return typeof pid === "number" ? pid : null;
1103
+ }
1104
+ /**
1105
+ * Last-resort reaper for a stdio child whose transport close wedged. By the
1106
+ * time this runs, the graceful path (stdin EOF → SIGTERM escalation inside
1107
+ * the SDK, where the installed version implements it) has been abandoned —
1108
+ * SIGKILL is the only signal a hung child can't ignore. ESRCH (already
1109
+ * exited) is swallowed.
1110
+ */
1111
+ function killStdioChild(pid) {
1112
+ if (pid == null) return;
1113
+ try {
1114
+ process.kill(pid, "SIGKILL");
1115
+ } catch {}
1116
+ }
1117
+ /**
1118
+ * Detect failure signatures that point at a transport mismatch and return
1119
+ * an actionable hint string. Conservative — only matches the signals we've
1120
+ * actually observed in the wild against MCP servers, so a real outage on
1121
+ * the right transport doesn't get misdiagnosed.
1122
+ *
1123
+ * Streamable-HTTP against an SSE-only endpoint:
1124
+ * - The transport opens an `initialize` POST. SSE-only servers respond
1125
+ * with `405 Method Not Allowed` or `404 Not Found` (their `/sse` GET
1126
+ * endpoint is the only handler). The SDK wraps these as
1127
+ * `Error POSTing to endpoint (HTTP 40X): ...`.
1128
+ * - Some proxies return `400` with a `text/event-stream` mismatch on
1129
+ * POST; same root cause, same fix.
1130
+ */
1131
+ function buildTransportMismatchHint(config, err) {
1132
+ const msg = err.message;
1133
+ if (config.transport === "streamable-http") {
1134
+ if (/\bHTTP\s*40[45]\b/i.test(msg) || /\(40[45]\)/.test(msg)) return "This server may not implement the streamable-HTTP transport. Try `transport: 'sse'` (or `sseUrl` instead of `url`) on this server's config.";
1135
+ }
1136
+ if (config.transport === "stdio" && config.scope === "context") {
1137
+ if (/connection closed/i.test(msg) || /before initialization|closed during connect/i.test(msg)) return `The server exited during startup. With \`scope: 'context'\` it runs INSIDE the execution context, so \`${config.command}\` must exist in that image (check \`mcp:warn\` for the child's stderr, e.g. "command not found"). Install the runtime in the context image, or use \`scope: 'host'\` if this server does not need the workspace filesystem.`;
1138
+ }
1139
+ return null;
1140
+ }
1141
+ /**
1142
+ * Wrap a bootstrap failure so the surfaced `Error` carries an actionable
1143
+ * hint while preserving the original message + stack + cause. Returns the
1144
+ * input error verbatim when no hint applies.
1145
+ *
1146
+ * `cause` (ES2022) gives hosts that introspect a way back to the raw SDK
1147
+ * error; the human-readable hint is the new message body that lands in
1148
+ * logs / hook payloads.
1149
+ */
1150
+ function withTransportMismatchHint(config, err) {
1151
+ const hint = buildTransportMismatchHint(config, err);
1152
+ if (!hint) return err;
1153
+ const augmented = new Error(`${err.message}\nHint: ${hint}`, { cause: err });
1154
+ if (err.stack) augmented.stack = err.stack;
1155
+ return augmented;
1156
+ }
1116
1157
  /**
1117
1158
  * True when an error from `client.connect()` indicates the user needs to
1118
1159
  * (re-)authenticate against the MCP server. Two signal families:
@@ -1314,9 +1355,9 @@ function wrapConnectWithHookFire(connect, fire) {
1314
1355
  */
1315
1356
  async function bootstrapServer(config, _clientFactory, hooks, options) {
1316
1357
  const start = Date.now();
1317
- if (config.enabledTools && config.disabledTools) return emitConfigFailure(config, hooks, `MCP server "${config.name}": enabledTools and disabledTools are mutually exclusive — set one or the other, not both.`);
1358
+ if (config.enabledTools && config.disabledTools) return emitConfigFailure(config, hooks, `MCP server "${config.name}": set enabledTools or disabledTools, not both.`);
1318
1359
  if (config.lazyConnect && (!config.cachedTools || config.cachedTools.length === 0)) return emitConfigFailure(config, hooks, `MCP server "${config.name}": lazyConnect requires cachedTools (the host must pre-populate the tool schemas so they can be advertised without a connection).`);
1319
- if (config.lazyConnect && config.auth === "oauth") return emitConfigFailure(config, hooks, `MCP server "${config.name}": lazyConnect is not compatible with auth: 'oauth' — OAuth servers must complete the handshake at bootstrap so a login affordance can surface before the model commits to a tool call.`);
1360
+ if (config.lazyConnect && config.auth === "oauth") return emitConfigFailure(config, hooks, `MCP server "${config.name}": lazyConnect doesn't work with auth: 'oauth'. OAuth servers must complete the handshake at bootstrap so a login affordance can surface before the model commits to a tool call.`);
1320
1361
  await safeCallHook(hooks, "mcp:bootstrap:start", {
1321
1362
  name: config.name,
1322
1363
  transport: config.transport
@@ -1520,7 +1561,7 @@ function makeLazyConnector(config, _clientFactory, authProvider, hooks, executio
1520
1561
  liveTransport = null;
1521
1562
  (hooks?.callHook("mcp:warn", {
1522
1563
  name: config.name,
1523
- message: "connection closed unexpectedly — will reconnect on next tool call"
1564
+ message: "connection closed, reconnecting on the next tool call"
1524
1565
  }))?.catch(() => {});
1525
1566
  }
1526
1567
  };
@@ -1655,6 +1696,9 @@ async function applyMcpToolFilters(config, tools, hooks) {
1655
1696
  * a fresh empty object schema instead of failing the run. (Fresh per call so a
1656
1697
  * downstream in-place mutation can't bleed across degraded tools.)
1657
1698
  */
1699
+ function isPlainObject(value) {
1700
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1701
+ }
1658
1702
  function toObjectSchema(schema) {
1659
1703
  if (typeof schema === "object" && schema !== null && !Array.isArray(schema)) return schema;
1660
1704
  return {
@@ -1712,7 +1756,8 @@ function buildMcpToolDef(config, getClient, tool, namespacedName, hooks, invalid
1712
1756
  spec: {
1713
1757
  name: namespacedName,
1714
1758
  description: tool.description || "",
1715
- inputSchema: toObjectSchema(tool.inputSchema)
1759
+ inputSchema: toObjectSchema(tool.inputSchema),
1760
+ ...isPlainObject(tool.outputSchema) ? { outputSchema: tool.outputSchema } : {}
1716
1761
  },
1717
1762
  execute: async (input, ctx) => {
1718
1763
  const { turnId, callId, signal } = ctx;
@@ -1784,7 +1829,7 @@ function buildMcpToolDef(config, getClient, tool, namespacedName, hooks, invalid
1784
1829
  }, void 0, {
1785
1830
  timeout,
1786
1831
  signal: rpcSignal
1787
- }), timeout, `MCP tool "${tool.name}" on server "${config.name}" timed out after ${timeout}ms. The server may still be processing the request — verify its effect before retrying, and do not immediately retry the identical call.`, signal);
1832
+ }), timeout, `MCP tool "${tool.name}" on server "${config.name}" timed out after ${timeout}ms. The server may still be processing the request, so check its effect before retrying, and do not immediately retry the identical call.`, signal);
1788
1833
  };
1789
1834
  let result;
1790
1835
  try {
@@ -1803,6 +1848,7 @@ function buildMcpToolDef(config, getClient, tool, namespacedName, hooks, invalid
1803
1848
  const detail = (typeof output === "string" ? output : toolResultToText(output)).trim();
1804
1849
  throw new Error(detail || `MCP tool "${tool.name}" on server "${config.name}" reported an error with no diagnostic content`);
1805
1850
  }
1851
+ if (result.structuredContent !== void 0) ctx.reportStructured?.(result.structuredContent);
1806
1852
  const transformCtx = {
1807
1853
  turnId,
1808
1854
  callId,
@@ -1910,6 +1956,6 @@ async function raceWithTimeoutAndSignal(task, timeoutMs, timeoutMessage, signal)
1910
1956
  }
1911
1957
  }
1912
1958
  //#endregion
1913
- export { hasAuthorizationHeader as _, expandEnvPlaceholders as a, normalizeMcpBlocks as c, resultToString as d, wrapDiscoveredMcpTools as f, createMemoryMcpCredentialStore as g, McpOAuthProvider as h, deriveMcpToolMeta as i, normalizeMcpServers as l, sseToJsonFetchIfNeeded as m, connectMcpServers as n, expandMcpConfigEnv as o, createTolerantClient as p, createStderrLineEmitter as r, nonOwningMcpView as s, attachStderrWarnPump as t, resolveTransportConfig as u, isOAuthTokenRejectionError as v };
1959
+ export { createMemoryMcpCredentialStore as _, attachStderrWarnPump as a, expandEnvPlaceholders as c, resolveTransportConfig as d, withoutEnvPlaceholders as f, McpOAuthProvider as g, createTolerantClient as h, wrapDiscoveredMcpTools as i, expandMcpConfigEnv as l, resultToString as m, deriveMcpToolMeta as n, createStderrLineEmitter as o, normalizeMcpBlocks as p, nonOwningMcpView as r, sseToJsonFetchIfNeeded as s, connectMcpServers as t, normalizeMcpServers as u, hasAuthorizationHeader as v, isOAuthTokenRejectionError as y };
1914
1960
 
1915
- //# sourceMappingURL=mcp-CgNd-f3F.js.map
1961
+ //# sourceMappingURL=mcp-_wecH9Qe.js.map