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 +0,0 @@
1
- {"version":3,"file":"atomic-write-B7zr1ddF.js","names":[],"sources":["../src/atomic-write.ts"],"sourcesContent":["/**\n * Synchronous atomic file write helper.\n *\n * Used by the chat-layer state / safelist / credentials / keybindings writers\n * and the OAuth credentials store. The pattern is always the same: write to a\n * sibling temp file, fsync it, then `renameSync` over the target. `rename(2)`\n * is atomic on the same filesystem, so concurrent readers either see the\n * previous file or the new one — never a half-written intermediate. The fsync\n * before the rename ensures the temp file's bytes are on disk before it takes\n * the target's place (without it, a power loss shortly after rename can leave\n * a zero-length or partial target on some filesystems). The temp suffix folds\n * `process.pid` + a per-process monotonic counter so two writers — same\n * process or same-millisecond siblings — can't pick the same tmp path.\n *\n * `ensureDir` runs `mkdirSync(dirname(path), { recursive: true })` before\n * the write so callers don't have to repeat the boilerplate. `mode` is\n * forwarded to the file open (e.g. `0o600` for credentials files).\n */\n\nimport { Buffer } from 'node:buffer'\nimport { closeSync, fsyncSync, mkdirSync, openSync, renameSync, unlinkSync, writeSync } from 'node:fs'\nimport { mkdir, open, rename, rm } from 'node:fs/promises'\nimport { dirname } from 'node:path'\n\nexport interface AtomicWriteOptions {\n /** When set, create the parent directory recursively before writing. */\n ensureDir?: boolean\n /** Forwarded to the file open — typically `0o600` for credentials files. */\n mode?: number\n}\n\nlet tmpCounter = 0\n\nfunction nextTmpPath(path: string): string {\n tmpCounter = (tmpCounter + 1) % Number.MAX_SAFE_INTEGER\n return `${path}.${process.pid}.${tmpCounter.toString(36)}.tmp`\n}\n\n/**\n * Best-effort fsync of the directory containing `path`, so the rename\n * itself (the directory entry) is durable. Wrapped in try/catch: opening\n * a directory for fsync fails on some platforms (notably Windows) and\n * durability of the entry is a nice-to-have on top of the data fsync.\n */\nfunction fsyncDirSync(path: string): void {\n try {\n const fd = openSync(dirname(path), 'r')\n try {\n fsyncSync(fd)\n }\n finally {\n closeSync(fd)\n }\n }\n catch { /* platform doesn't support directory fsync */ }\n}\n\nexport function writeFileAtomic(\n path: string,\n contents: string,\n options: AtomicWriteOptions = {},\n): void {\n if (options.ensureDir)\n mkdirSync(dirname(path), { recursive: true })\n const tmp = nextTmpPath(path)\n try {\n const fd = openSync(tmp, 'w', options.mode)\n try {\n const buf = Buffer.from(contents, 'utf8')\n let offset = 0\n while (offset < buf.length)\n offset += writeSync(fd, buf, offset)\n // Flush data to disk BEFORE the rename — otherwise a crash after\n // rename could leave the target pointing at not-yet-persisted bytes.\n fsyncSync(fd)\n }\n finally {\n closeSync(fd)\n }\n renameSync(tmp, path)\n fsyncDirSync(path)\n }\n catch (err) {\n // Don't leak the temp file when the write or rename throws (disk full,\n // permission flip, target dir removed).\n try {\n unlinkSync(tmp)\n }\n catch { /* never created or already gone */ }\n throw err\n }\n}\n\n/**\n * Async variant of {@link writeFileAtomic} — same tmp-suffix, fsync and\n * cleanup-on-failure semantics over `node:fs/promises`. Used by writers\n * that already live on the async path (e.g. loop persistence blobs).\n */\nexport async function writeFileAtomicAsync(\n path: string,\n contents: string,\n options: AtomicWriteOptions = {},\n): Promise<void> {\n if (options.ensureDir)\n await mkdir(dirname(path), { recursive: true })\n const tmp = nextTmpPath(path)\n try {\n const fh = await open(tmp, 'w', options.mode)\n try {\n await fh.writeFile(contents, 'utf8')\n await fh.sync()\n }\n finally {\n await fh.close()\n }\n await rename(tmp, path)\n try {\n const dh = await open(dirname(path), 'r')\n try {\n await dh.sync()\n }\n finally {\n await dh.close()\n }\n }\n catch { /* platform doesn't support directory fsync */ }\n }\n catch (err) {\n await rm(tmp, { force: true }).catch(() => {})\n throw err\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AA+BA,IAAI,aAAa;AAEjB,SAAS,YAAY,MAAsB;CACzC,cAAc,aAAa,KAAK,OAAO;CACvC,OAAO,GAAG,KAAK,GAAG,QAAQ,IAAI,GAAG,WAAW,SAAS,EAAE,EAAE;AAC3D;;;;;;;AAQA,SAAS,aAAa,MAAoB;CACxC,IAAI;EACF,MAAM,KAAK,SAAS,QAAQ,IAAI,GAAG,GAAG;EACtC,IAAI;GACF,UAAU,EAAE;EACd,UACQ;GACN,UAAU,EAAE;EACd;CACF,QACM,CAAiD;AACzD;AAEA,SAAgB,gBACd,MACA,UACA,UAA8B,CAAC,GACzB;CACN,IAAI,QAAQ,WACV,UAAU,QAAQ,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;CAC9C,MAAM,MAAM,YAAY,IAAI;CAC5B,IAAI;EACF,MAAM,KAAK,SAAS,KAAK,KAAK,QAAQ,IAAI;EAC1C,IAAI;GACF,MAAM,MAAM,OAAO,KAAK,UAAU,MAAM;GACxC,IAAI,SAAS;GACb,OAAO,SAAS,IAAI,QAClB,UAAU,UAAU,IAAI,KAAK,MAAM;GAGrC,UAAU,EAAE;EACd,UACQ;GACN,UAAU,EAAE;EACd;EACA,WAAW,KAAK,IAAI;EACpB,aAAa,IAAI;CACnB,SACO,KAAK;EAGV,IAAI;GACF,WAAW,GAAG;EAChB,QACM,CAAsC;EAC5C,MAAM;CACR;AACF;;;;;;AAOA,eAAsB,qBACpB,MACA,UACA,UAA8B,CAAC,GAChB;CACf,IAAI,QAAQ,WACV,MAAM,MAAM,QAAQ,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;CAChD,MAAM,MAAM,YAAY,IAAI;CAC5B,IAAI;EACF,MAAM,KAAK,MAAM,KAAK,KAAK,KAAK,QAAQ,IAAI;EAC5C,IAAI;GACF,MAAM,GAAG,UAAU,UAAU,MAAM;GACnC,MAAM,GAAG,KAAK;EAChB,UACQ;GACN,MAAM,GAAG,MAAM;EACjB;EACA,MAAM,OAAO,KAAK,IAAI;EACtB,IAAI;GACF,MAAM,KAAK,MAAM,KAAK,QAAQ,IAAI,GAAG,GAAG;GACxC,IAAI;IACF,MAAM,GAAG,KAAK;GAChB,UACQ;IACN,MAAM,GAAG,MAAM;GACjB;EACF,QACM,CAAiD;CACzD,SACO,KAAK;EACV,MAAM,GAAG,KAAK,EAAE,OAAO,KAAK,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC;EAC7C,MAAM;CACR;AACF"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"auth-BG2rVKSt.js","names":["CLIENT_ID","AUTHORIZE_URL","TOKEN_URL","CALLBACK_PORT","CALLBACK_PATH","PROVIDER_NAME","parseAuthorizationInput","exchangeAuthorizationCode","getModels","getModel"],"sources":["../src/chat/oauth-page/server.ts","../src/chat/oauth-page/anthropic.ts","../src/chat/oauth-page/openai-codex.ts","../src/chat/oauth-page/render.ts","../src/chat/oauth-page/index.ts","../src/chat/providers.ts","../src/chat/credentials.ts","../src/chat/auth.ts"],"sourcesContent":["/**\n * Local HTTP callback server primitive shared by the Anthropic + Codex\n * login flows. Owns three concerns:\n *\n * 1. Bind a loopback listener (configurable port + host via\n * `PI_OAUTH_CALLBACK_HOST`, matching pi-ai's semantics).\n * 2. Parse the redirect query string into `{ code, state }` or surface\n * a structured error.\n * 3. Render the result page through the caller-supplied renderer.\n *\n * Each provider clones the lifecycle wrapper (`waitForCode` / `cancelWait`\n * / `close`) so the higher-level login code reads the same on either\n * side. The only thing the server itself decides is the HTML.\n */\n\nimport type { IncomingMessage, Server, ServerResponse } from 'node:http'\nimport type { OAuthCallbackPageRenderer } from './render'\nimport { createServer } from 'node:http'\n\nconst CALLBACK_HOST = process.env.PI_OAUTH_CALLBACK_HOST || '127.0.0.1'\n\nexport interface CallbackServerOptions {\n /** Loopback port. Must match the `redirect_uri` registered with the provider. */\n port: number\n /** Path component of the redirect URI (e.g. `/callback`, `/auth/callback`). */\n path: string\n /**\n * Expected `state` value. The server rejects any callback whose `state`\n * doesn't match, exactly. For Anthropic this is the PKCE verifier; for\n * Codex it's a random 16-byte hex string.\n */\n expectedState: string\n /** Display name passed through to {@link OAuthCallbackPageRenderer}. */\n providerName: string\n /** HTML renderer for both success + error pages. */\n renderPage: OAuthCallbackPageRenderer\n /** Success-page body shown after a clean redirect. */\n successMessage: string\n /**\n * How to react when `server.listen` itself fails (typically `EADDRINUSE`\n * when another instance is mid-flow). `\"reject\"` propagates the error to\n * the awaiting `start` caller; `\"resolveWithStub\"` returns a degraded\n * handle whose `waitForCode` immediately resolves to `null`, so the\n * caller can fall back to manual paste. Anthropic uses `\"reject\"`,\n * Codex uses `\"resolveWithStub\"` to match pi-ai's per-provider behavior.\n */\n onListenError?: 'reject' | 'resolveWithStub'\n}\n\nexport interface CallbackServerHandle {\n /** Fully-qualified redirect URI to advertise to the provider. */\n redirectUri: string\n /**\n * Resolves when the redirect lands (with `{ code, state? }`) or when\n * {@link cancelWait} is invoked (with `null`). Never throws.\n */\n waitForCode: () => Promise<{ code: string, state?: string } | null>\n /** Unblock `waitForCode` early (e.g. manual paste won the race). */\n cancelWait: () => void\n /** Close the listener. Idempotent. */\n close: () => void\n}\n\ninterface PendingResolution {\n settle: (value: { code: string, state?: string } | null) => void\n}\n\nfunction buildRequestHandler(opts: CallbackServerOptions, pending: PendingResolution) {\n return (req: IncomingMessage, res: ServerResponse) => {\n try {\n const url = new URL(req.url || '', 'http://localhost')\n\n if (url.pathname !== opts.path) {\n respondError(res, 404, opts, 'Callback route not found.')\n return\n }\n\n const error = url.searchParams.get('error')\n if (error) {\n respondError(res, 400, opts, `${opts.providerName} authentication did not complete.`, `Error: ${error}`)\n return\n }\n\n const code = url.searchParams.get('code')\n const state = url.searchParams.get('state')\n\n if (!code || !state) {\n respondError(res, 400, opts, 'Missing code or state parameter.')\n return\n }\n\n if (state !== opts.expectedState) {\n respondError(res, 400, opts, 'State mismatch.')\n return\n }\n\n res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' })\n res.end(opts.renderPage({\n kind: 'success',\n provider: opts.providerName,\n message: opts.successMessage,\n }))\n pending.settle({ code, state })\n }\n catch {\n // We intentionally swallow the actual error here: surfacing a stack\n // to the user's browser would leak internals, and the login flow\n // is about to fall back to manual paste anyway.\n respondError(res, 500, opts, 'Internal error while processing the callback.')\n }\n }\n}\n\nfunction respondError(\n res: ServerResponse,\n status: number,\n opts: CallbackServerOptions,\n message: string,\n details?: string,\n) {\n res.writeHead(status, { 'Content-Type': 'text/html; charset=utf-8' })\n res.end(opts.renderPage({\n kind: 'error',\n provider: opts.providerName,\n message,\n details,\n }))\n}\n\nfunction buildStubHandle(redirectUri: string, server: Server | null): CallbackServerHandle {\n return {\n redirectUri,\n waitForCode: async () => null,\n cancelWait: () => {},\n close: () => {\n try { server?.close() }\n catch { /* already closed */ }\n },\n }\n}\n\n/**\n * Start the loopback HTTP callback server. The returned handle is the\n * caller's contract — they `await waitForCode()`, race it against manual\n * paste, then `close()` in a `finally`.\n */\nexport async function startCallbackServer(opts: CallbackServerOptions): Promise<CallbackServerHandle> {\n const onListenError = opts.onListenError ?? 'reject'\n const redirectUri = `http://localhost:${opts.port}${opts.path}`\n\n return new Promise<CallbackServerHandle>((resolve, reject) => {\n let settled = false\n const pending: PendingResolution = {\n settle: () => {},\n }\n const waitPromise = new Promise<{ code: string, state?: string } | null>((resolveWait) => {\n pending.settle = (value) => {\n if (settled)\n return\n settled = true\n resolveWait(value)\n }\n })\n\n const server = createServer(buildRequestHandler(opts, pending))\n\n server.on('error', (err) => {\n if (onListenError === 'resolveWithStub') {\n pending.settle(null)\n resolve(buildStubHandle(redirectUri, server))\n return\n }\n reject(err)\n })\n\n server.listen(opts.port, CALLBACK_HOST, () => {\n resolve({\n redirectUri,\n waitForCode: () => waitPromise,\n cancelWait: () => pending.settle(null),\n close: () => {\n try { server.close() }\n catch { /* already closed */ }\n },\n })\n })\n })\n}\n","/**\n * Anthropic OAuth flow with a customisable callback page.\n *\n * Mirrors `loginAnthropic` from `@earendil-works/pi-ai/oauth` 1:1 except:\n *\n * 1. The HTTP callback server's response HTML routes through\n * {@link OAuthCallbackPageRenderer} instead of pi-ai's baked-in page.\n * 2. The lifecycle is delegated to {@link startCallbackServer} so the\n * Codex sibling and any future loopback provider share one HTTP impl.\n *\n * Refresh + `getApiKey` delegate to `src/providers/oauth-registry` — which\n * wraps pi-ai's canonical `auth.oauth.refresh` flow — those don't touch\n * HTML and there's no upside to forking them.\n *\n * Endpoints / client id / port / scopes verified against pi-ai `0.82.1`\n * (`dist/auth/oauth/anthropic.js`). Re-verify in lockstep on the next\n * `@earendil-works/pi-ai` upgrade, or delete this file once pi-ai exposes\n * a callback-page hook upstream.\n */\n\nimport type {\n OAuthCredentials,\n OAuthLoginCallbacks,\n} from '@earendil-works/pi-ai/oauth'\nimport type { OAuthProviderInterface } from '../../providers/oauth-registry'\nimport type { OAuthCallbackPageRenderer } from './render'\nimport { refreshAnthropicToken } from '../../providers/oauth-registry'\nimport { generatePkce } from './pkce'\nimport { startCallbackServer } from './server'\n\n// Pulled verbatim from pi-ai (verified at 0.82.1) — `dist/auth/oauth/anthropic.js`.\nconst CLIENT_ID = atob('OWQxYzI1MGEtZTYxYi00NGQ5LTg4ZWQtNTk0NGQxOTYyZjVl')\nconst AUTHORIZE_URL = 'https://claude.ai/oauth/authorize'\nconst TOKEN_URL = 'https://platform.claude.com/v1/oauth/token'\nconst CALLBACK_PORT = 53692\nconst CALLBACK_PATH = '/callback'\nconst SCOPES = 'org:create_api_key user:profile user:inference user:sessions:claude_code user:mcp_servers user:file_upload'\nconst PROVIDER_NAME = 'Anthropic'\n\n/**\n * Parse what the user pasted into the manual-code prompt. Accepts:\n * - the bare authorization code\n * - the full `redirect_uri?code=...&state=...` URL\n * - the `code#state` shorthand Anthropic surfaces on the page\n * - a raw query string with `code=...&state=...`\n *\n * Matches pi-ai's parse table so an existing user's muscle memory still works.\n */\nfunction parseAuthorizationInput(input: string): { code?: string, state?: string } {\n const value = input.trim()\n if (!value)\n return {}\n\n try {\n const url = new URL(value)\n return {\n code: url.searchParams.get('code') ?? undefined,\n state: url.searchParams.get('state') ?? undefined,\n }\n }\n catch {\n // fall through to non-URL parsing below\n }\n\n if (value.includes('#')) {\n const [code, state] = value.split('#', 2)\n return { code, state }\n }\n if (value.includes('code=')) {\n const params = new URLSearchParams(value)\n return {\n code: params.get('code') ?? undefined,\n state: params.get('state') ?? undefined,\n }\n }\n return { code: value }\n}\n\nasync function postJson(url: string, body: Record<string, unknown>): Promise<string> {\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Accept': 'application/json',\n },\n body: JSON.stringify(body),\n signal: AbortSignal.timeout(30_000),\n })\n const responseBody = await response.text()\n if (!response.ok)\n throw new Error(`HTTP request failed. status=${response.status}; url=${url}; body=${responseBody}`)\n return responseBody\n}\n\nasync function exchangeAuthorizationCode(\n code: string,\n state: string,\n verifier: string,\n redirectUri: string,\n): Promise<OAuthCredentials> {\n const responseBody = await postJson(TOKEN_URL, {\n grant_type: 'authorization_code',\n client_id: CLIENT_ID,\n code,\n state,\n redirect_uri: redirectUri,\n code_verifier: verifier,\n })\n\n const tokenData = JSON.parse(responseBody) as {\n refresh_token: string\n access_token: string\n expires_in: number\n }\n\n return {\n refresh: tokenData.refresh_token,\n access: tokenData.access_token,\n // -5min so a refresh fires before the upstream expiry; matches pi-ai.\n expires: Date.now() + tokenData.expires_in * 1000 - 5 * 60 * 1000,\n }\n}\n\nexport interface LoginAnthropicWithPageOptions extends Pick<\n OAuthLoginCallbacks,\n 'onAuth' | 'onPrompt' | 'onProgress' | 'onManualCodeInput'\n> {\n renderPage: OAuthCallbackPageRenderer\n}\n\nexport async function loginAnthropicWithCustomPage(\n options: LoginAnthropicWithPageOptions,\n): Promise<OAuthCredentials> {\n const { verifier, challenge } = await generatePkce()\n const server = await startCallbackServer({\n port: CALLBACK_PORT,\n path: CALLBACK_PATH,\n expectedState: verifier,\n providerName: PROVIDER_NAME,\n renderPage: options.renderPage,\n successMessage: 'Anthropic authentication completed. You can close this window.',\n onListenError: 'reject',\n })\n\n let code: string | undefined\n let state: string | undefined\n\n try {\n const authParams = new URLSearchParams({\n code: 'true',\n client_id: CLIENT_ID,\n response_type: 'code',\n redirect_uri: server.redirectUri,\n scope: SCOPES,\n code_challenge: challenge,\n code_challenge_method: 'S256',\n state: verifier,\n })\n\n options.onAuth({\n url: `${AUTHORIZE_URL}?${authParams.toString()}`,\n instructions: 'Complete login in your browser. If the browser is on another machine, paste the final redirect URL here.',\n })\n\n if (options.onManualCodeInput) {\n let manualInput: string | undefined\n let manualError: Error | undefined\n const manualPromise = options\n .onManualCodeInput()\n .then((input) => {\n manualInput = input\n server.cancelWait()\n })\n .catch((err) => {\n manualError = err instanceof Error ? err : new Error(String(err))\n server.cancelWait()\n })\n\n const result = await server.waitForCode()\n if (manualError)\n throw manualError\n\n if (result?.code) {\n code = result.code\n state = result.state\n }\n else if (manualInput) {\n const parsed = parseAuthorizationInput(manualInput)\n if (parsed.state && parsed.state !== verifier)\n throw new Error('OAuth state mismatch')\n code = parsed.code\n state = parsed.state ?? verifier\n }\n\n if (!code) {\n await manualPromise\n if (manualError)\n throw manualError\n if (manualInput) {\n const parsed = parseAuthorizationInput(manualInput)\n if (parsed.state && parsed.state !== verifier)\n throw new Error('OAuth state mismatch')\n code = parsed.code\n state = parsed.state ?? verifier\n }\n }\n }\n else {\n const result = await server.waitForCode()\n if (result?.code) {\n code = result.code\n state = result.state\n }\n }\n\n if (!code) {\n const input = await options.onPrompt({\n message: 'Paste the authorization code or full redirect URL:',\n placeholder: server.redirectUri,\n })\n const parsed = parseAuthorizationInput(input)\n if (parsed.state && parsed.state !== verifier)\n throw new Error('OAuth state mismatch')\n code = parsed.code\n state = parsed.state ?? verifier\n }\n\n if (!code)\n throw new Error('Missing authorization code')\n if (!state)\n throw new Error('Missing OAuth state')\n\n options.onProgress?.('Exchanging authorization code for tokens...')\n return await exchangeAuthorizationCode(code, state, verifier, server.redirectUri)\n }\n finally {\n server.close()\n }\n}\n\n/**\n * Build an `OAuthProviderInterface` that behaves identically to pi-ai's\n * `anthropicOAuthProvider` except for the callback page HTML. Drop this\n * onto `ProviderDescriptor.oauthProvider` to override.\n */\nexport function createAnthropicOAuthProviderWithCustomPage(\n renderPage: OAuthCallbackPageRenderer,\n): OAuthProviderInterface {\n return {\n id: 'anthropic',\n name: 'Anthropic (Claude Pro/Max)',\n usesCallbackServer: true,\n async login(callbacks: OAuthLoginCallbacks) {\n return loginAnthropicWithCustomPage({\n renderPage,\n onAuth: callbacks.onAuth,\n onPrompt: callbacks.onPrompt,\n onProgress: callbacks.onProgress,\n onManualCodeInput: callbacks.onManualCodeInput,\n })\n },\n async refreshToken(credentials: OAuthCredentials) {\n return refreshAnthropicToken(credentials.refresh)\n },\n getApiKey(credentials: OAuthCredentials) {\n return credentials.access\n },\n }\n}\n","/**\n * OpenAI Codex (ChatGPT Plus/Pro) OAuth flow with a customisable callback\n * page. 1:1 mirror of pi-ai's `loginOpenAICodex` (endpoints verified at\n * `0.82.1`, `dist/auth/oauth/openai-codex.js`) except the HTTP callback\n * server routes through {@link OAuthCallbackPageRenderer}.\n *\n * Refresh delegates to `refreshOpenAICodexToken` in\n * `src/providers/oauth-registry`, which wraps pi-ai's canonical\n * `auth.oauth.refresh` flow — Codex's refresh path involves JWT decoding for\n * `accountId`, and we don't want to duplicate it. The HTML override is\n * purely about the landing page.\n */\n\nimport type {\n OAuthCredentials,\n OAuthLoginCallbacks,\n} from '@earendil-works/pi-ai/oauth'\nimport type { OAuthProviderInterface } from '../../providers/oauth-registry'\nimport type { OAuthCallbackPageRenderer } from './render'\nimport { Buffer } from 'node:buffer'\nimport { randomBytes } from 'node:crypto'\nimport { refreshOpenAICodexToken } from '../../providers/oauth-registry'\nimport { generatePkce } from './pkce'\nimport { startCallbackServer } from './server'\n\n// Pulled verbatim from pi-ai (verified at 0.82.1) — `dist/auth/oauth/openai-codex.js`.\nconst CLIENT_ID = 'app_EMoamEEZ73f0CkXaXp7hrann'\nconst AUTHORIZE_URL = 'https://auth.openai.com/oauth/authorize'\nconst TOKEN_URL = 'https://auth.openai.com/oauth/token'\nconst CALLBACK_PORT = 1455\nconst CALLBACK_PATH = '/auth/callback'\nconst SCOPE = 'openid profile email offline_access'\nconst JWT_CLAIM_PATH = 'https://api.openai.com/auth'\nconst PROVIDER_NAME = 'OpenAI'\n\nfunction createState(): string {\n return randomBytes(16).toString('hex')\n}\n\nfunction parseAuthorizationInput(input: string): { code?: string, state?: string } {\n const value = input.trim()\n if (!value)\n return {}\n\n try {\n const url = new URL(value)\n return {\n code: url.searchParams.get('code') ?? undefined,\n state: url.searchParams.get('state') ?? undefined,\n }\n }\n catch {\n // fall through\n }\n\n if (value.includes('#')) {\n const [code, state] = value.split('#', 2)\n return { code, state }\n }\n if (value.includes('code=')) {\n const params = new URLSearchParams(value)\n return {\n code: params.get('code') ?? undefined,\n state: params.get('state') ?? undefined,\n }\n }\n return { code: value }\n}\n\ninterface JwtPayload {\n [key: string]: unknown\n [JWT_CLAIM_PATH]?: { chatgpt_account_id?: string }\n}\n\nfunction decodeJwt(token: string): JwtPayload | null {\n try {\n const parts = token.split('.')\n if (parts.length !== 3)\n return null\n const payload = parts[1] ?? ''\n // JWT segments are base64url (RFC 7515) — `-`/`_` alphabet, no padding.\n // `atob` expects standard base64 and throws on those chars, which made\n // extraction fail for perfectly valid tokens whose payload happened to\n // contain them. `Buffer` handles both alphabets and missing padding.\n const decoded = Buffer.from(payload, 'base64url').toString('utf8')\n return JSON.parse(decoded) as JwtPayload\n }\n catch {\n return null\n }\n}\n\nfunction getAccountId(accessToken: string): string | null {\n const payload = decodeJwt(accessToken)\n const claim = payload?.[JWT_CLAIM_PATH] as { chatgpt_account_id?: string } | undefined\n const accountId = claim?.chatgpt_account_id\n return typeof accountId === 'string' && accountId.length > 0 ? accountId : null\n}\n\ninterface TokenExchangeSuccess {\n type: 'success'\n access: string\n refresh: string\n expires: number\n}\n\ninterface TokenExchangeFailure {\n type: 'failed'\n message: string\n}\n\nasync function exchangeAuthorizationCode(\n code: string,\n verifier: string,\n redirectUri: string,\n): Promise<TokenExchangeSuccess | TokenExchangeFailure> {\n const response = await fetch(TOKEN_URL, {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body: new URLSearchParams({\n grant_type: 'authorization_code',\n client_id: CLIENT_ID,\n code,\n code_verifier: verifier,\n redirect_uri: redirectUri,\n }),\n })\n\n if (!response.ok) {\n const text = await response.text().catch(() => '')\n return {\n type: 'failed',\n message: `OpenAI Codex token exchange failed (${response.status}): ${text || response.statusText}`,\n }\n }\n\n const json = await response.json() as {\n access_token?: string\n refresh_token?: string\n expires_in?: number\n }\n if (!json.access_token || !json.refresh_token || typeof json.expires_in !== 'number') {\n return {\n type: 'failed',\n message: `OpenAI Codex token exchange response missing fields: ${JSON.stringify(json)}`,\n }\n }\n return {\n type: 'success',\n access: json.access_token,\n refresh: json.refresh_token,\n expires: Date.now() + json.expires_in * 1000,\n }\n}\n\nexport interface LoginOpenAICodexWithPageOptions extends Pick<\n OAuthLoginCallbacks,\n 'onAuth' | 'onPrompt' | 'onProgress' | 'onManualCodeInput'\n> {\n renderPage: OAuthCallbackPageRenderer\n /** OAuth `originator` parameter. Defaults to `\"pi\"` to stay observable upstream. */\n originator?: string\n}\n\nexport async function loginOpenAICodexWithCustomPage(\n options: LoginOpenAICodexWithPageOptions,\n): Promise<OAuthCredentials> {\n const { verifier, challenge } = await generatePkce()\n const state = createState()\n const originator = options.originator ?? 'pi'\n\n const server = await startCallbackServer({\n port: CALLBACK_PORT,\n path: CALLBACK_PATH,\n expectedState: state,\n providerName: PROVIDER_NAME,\n renderPage: options.renderPage,\n successMessage: 'OpenAI authentication completed. You can close this window.',\n // Matches pi-ai: Codex falls back to manual paste on port collision.\n onListenError: 'resolveWithStub',\n })\n\n const authUrl = new URL(AUTHORIZE_URL)\n authUrl.searchParams.set('response_type', 'code')\n authUrl.searchParams.set('client_id', CLIENT_ID)\n authUrl.searchParams.set('redirect_uri', server.redirectUri)\n authUrl.searchParams.set('scope', SCOPE)\n authUrl.searchParams.set('code_challenge', challenge)\n authUrl.searchParams.set('code_challenge_method', 'S256')\n authUrl.searchParams.set('state', state)\n authUrl.searchParams.set('id_token_add_organizations', 'true')\n authUrl.searchParams.set('codex_cli_simplified_flow', 'true')\n authUrl.searchParams.set('originator', originator)\n\n options.onAuth({\n url: authUrl.toString(),\n instructions: 'A browser window should open. Complete login to finish.',\n })\n\n let code: string | undefined\n\n try {\n if (options.onManualCodeInput) {\n let manualCode: string | undefined\n let manualError: Error | undefined\n const manualPromise = options\n .onManualCodeInput()\n .then((input) => {\n manualCode = input\n server.cancelWait()\n })\n .catch((err) => {\n manualError = err instanceof Error ? err : new Error(String(err))\n server.cancelWait()\n })\n\n const result = await server.waitForCode()\n if (manualError)\n throw manualError\n\n if (result?.code) {\n code = result.code\n }\n else if (manualCode) {\n const parsed = parseAuthorizationInput(manualCode)\n if (parsed.state && parsed.state !== state)\n throw new Error('State mismatch')\n code = parsed.code\n }\n\n if (!code) {\n await manualPromise\n if (manualError)\n throw manualError\n if (manualCode) {\n const parsed = parseAuthorizationInput(manualCode)\n if (parsed.state && parsed.state !== state)\n throw new Error('State mismatch')\n code = parsed.code\n }\n }\n }\n else {\n const result = await server.waitForCode()\n if (result?.code)\n code = result.code\n }\n\n if (!code) {\n const input = await options.onPrompt({\n message: 'Paste the authorization code (or full redirect URL):',\n })\n const parsed = parseAuthorizationInput(input)\n if (parsed.state && parsed.state !== state)\n throw new Error('State mismatch')\n code = parsed.code\n }\n\n if (!code)\n throw new Error('Missing authorization code')\n\n const tokenResult = await exchangeAuthorizationCode(code, verifier, server.redirectUri)\n if (tokenResult.type !== 'success')\n throw new Error(tokenResult.message)\n\n const accountId = getAccountId(tokenResult.access)\n if (!accountId)\n throw new Error('Failed to extract accountId from token')\n\n return {\n access: tokenResult.access,\n refresh: tokenResult.refresh,\n expires: tokenResult.expires,\n accountId,\n }\n }\n finally {\n server.close()\n }\n}\n\n/**\n * Build an `OAuthProviderInterface` that behaves identically to pi-ai's\n * `openaiCodexOAuthProvider` except for the callback page HTML.\n */\nexport function createOpenAICodexOAuthProviderWithCustomPage(\n renderPage: OAuthCallbackPageRenderer,\n): OAuthProviderInterface {\n return {\n id: 'openai-codex',\n name: 'ChatGPT Plus/Pro (Codex Subscription)',\n usesCallbackServer: true,\n async login(callbacks: OAuthLoginCallbacks) {\n return loginOpenAICodexWithCustomPage({\n renderPage,\n onAuth: callbacks.onAuth,\n onPrompt: callbacks.onPrompt,\n onProgress: callbacks.onProgress,\n onManualCodeInput: callbacks.onManualCodeInput,\n })\n },\n async refreshToken(credentials: OAuthCredentials) {\n return refreshOpenAICodexToken(credentials.refresh)\n },\n getApiKey(credentials: OAuthCredentials) {\n return credentials.access\n },\n }\n}\n","/**\n * HTML rendered by the local OAuth callback server when the browser hits\n * `redirect_uri`. This is the page the user sees the moment authentication\n * finishes — by default pi-ai bakes in its own dark page; this module lets\n * a host swap it for something branded without forking the package.\n *\n * Renderer is a single pure function — `(page) => string` — so a host can\n * inline a literal template, pull from a theme registry, or proxy a static\n * asset built at compile time. Anything that returns a `string`.\n */\n\n/**\n * Outcome rendered on the callback page. `kind` drives status code on the\n * underlying HTTP response (`200` for success, `4xx` for error); the rest\n * is content. `provider` is the human-readable name of the OAuth provider\n * (e.g. `\"Anthropic\"`, `\"OpenAI Codex\"`) so a renderer can swap copy or\n * logos per provider.\n */\nexport interface OAuthCallbackPage {\n kind: 'success' | 'error'\n /** Headline message — usually one sentence. Safe to include user-visible details. */\n message: string\n /** Optional secondary details (e.g. provider error code). Rendered in mono. */\n details?: string\n /** Provider display name passed through from the login function. */\n provider: string\n}\n\nexport type OAuthCallbackPageRenderer = (page: OAuthCallbackPage) => string\n\nfunction escapeHtml(value: string): string {\n return value\n .replaceAll('&', '&amp;')\n .replaceAll('<', '&lt;')\n .replaceAll('>', '&gt;')\n .replaceAll('\"', '&quot;')\n .replaceAll('\\'', '&#39;')\n}\n\n/**\n * Default zidane-themed page. Visually neutral but distinguishable from\n * pi-ai's stock page — dark background, mono headings, no logo (host can\n * pass a custom renderer to add one).\n */\nexport const renderDefaultCallbackPage: OAuthCallbackPageRenderer = (page) => {\n const title = page.kind === 'success'\n ? `Signed in to ${page.provider}`\n : `Could not sign in to ${page.provider}`\n const heading = escapeHtml(title)\n const message = escapeHtml(page.message)\n const details = page.details ? escapeHtml(page.details) : undefined\n\n return `<!doctype html>\n<html lang=\"en\">\n<head>\n <meta charset=\"utf-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n <title>${heading}</title>\n <style>\n :root {\n --text: #f4f4f5;\n --text-dim: #a1a1aa;\n --accent: ${page.kind === 'success' ? '#22d3ee' : '#f87171'};\n --page-bg: #0a0a0a;\n --panel-bg: #131316;\n --border: #27272a;\n --font-sans: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;\n }\n * { box-sizing: border-box; }\n html { color-scheme: dark; }\n body {\n margin: 0;\n min-height: 100vh;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 24px;\n background: var(--page-bg);\n color: var(--text);\n font-family: var(--font-sans);\n }\n main {\n width: 100%;\n max-width: 520px;\n padding: 32px;\n background: var(--panel-bg);\n border: 1px solid var(--border);\n border-radius: 12px;\n }\n .label {\n font-family: var(--font-mono);\n font-size: 12px;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n color: var(--accent);\n margin-bottom: 12px;\n }\n h1 {\n margin: 0 0 12px;\n font-size: 22px;\n font-weight: 600;\n letter-spacing: -0.01em;\n color: var(--text);\n }\n p {\n margin: 0;\n line-height: 1.6;\n color: var(--text-dim);\n font-size: 14px;\n }\n .details {\n margin-top: 18px;\n padding: 12px 14px;\n background: var(--page-bg);\n border: 1px solid var(--border);\n border-radius: 8px;\n font-family: var(--font-mono);\n font-size: 12px;\n color: var(--text-dim);\n white-space: pre-wrap;\n word-break: break-word;\n }\n </style>\n</head>\n<body>\n <main>\n <div class=\"label\">zidane · OAuth · ${escapeHtml(page.provider)}</div>\n <h1>${heading}</h1>\n <p>${message}</p>\n ${details ? `<div class=\"details\">${details}</div>` : ''}\n </main>\n</body>\n</html>`\n}\n\n/**\n * Tiny helper kept private to the module so renderer authors can reuse\n * the same escaping rules as the default theme. Re-exported in case a\n * host wants to embed user-supplied messages safely.\n */\nexport const escapeHtmlForCallbackPage: (value: string) => string = escapeHtml\n","/**\n * Custom OAuth callback page — drop-in replacements for pi-ai's built-in\n * Anthropic + OpenAI Codex providers that route the post-redirect HTML\n * through your own renderer.\n *\n * Why this module exists\n * ----------------------\n * `@earendil-works/pi-ai` (still true at `0.82.1`) bakes its callback page\n * into its Anthropic / OpenAI Codex login flows with no override, so this\n * module forks the two flows in one self-contained place — everything else\n * (token refresh, `getApiKey`, env handling) delegates back to pi-ai's\n * canonical flows via `src/providers/oauth-registry` unchanged. Since pi-ai\n * `0.80.8` the OAuth architecture is `Provider.auth.oauth` + `AuthInteraction`\n * (the old `OAuthLoginCallbacks` registry surface was removed upstream and is\n * now reproduced locally in `src/providers/oauth-registry.ts`).\n *\n * How to use\n * ----------\n *\n * ```ts\n * import { createCustomCallbackOAuthProviders, renderDefaultCallbackPage } from './chat/oauth-page'\n * import { BUILTIN_PROVIDERS } from './chat/providers'\n *\n * const { anthropic, openaiCodex } = createCustomCallbackOAuthProviders(renderDefaultCallbackPage)\n *\n * const providers = {\n * ...BUILTIN_PROVIDERS,\n * anthropic: { ...BUILTIN_PROVIDERS.anthropic, oauthProvider: anthropic },\n * openai: { ...BUILTIN_PROVIDERS.openai, oauthProvider: openaiCodex },\n * }\n * ```\n *\n * Or override per provider with `createAnthropicOAuthProviderWithCustomPage`\n * / `createOpenAICodexOAuthProviderWithCustomPage` directly.\n *\n * Deleting this module\n * --------------------\n * The upstream deletion path now runs through pi-ai's canonical\n * `Provider.auth.oauth` + `CredentialStore` architecture (a page-render hook\n * alone no longer exists as a plan). Migrating means adopting that whole\n * stack in place of `src/providers/oauth.ts`'s credential handling — see\n * `src/providers/oauth-registry.ts` for the seam.\n *\n * Endpoints verified against pi-ai `0.82.1`. Re-verify endpoints + ports on\n * every pi-ai bump (auth URLs, client IDs, callback ports are duplicated\n * verbatim here; see `anthropic.ts` and `openai-codex.ts` for the\n * source-of-truth pointers).\n */\n\nimport type { OAuthProviderInterface } from '../../providers/oauth-registry'\nimport type { OAuthCallbackPageRenderer } from './render'\nimport { registerOAuthProvider } from '../../providers/oauth-registry'\nimport { createAnthropicOAuthProviderWithCustomPage } from './anthropic'\nimport { createCursorOAuthProvider } from './cursor'\nimport { createOpenAICodexOAuthProviderWithCustomPage } from './openai-codex'\nimport { createXaiOAuthProvider } from './xai'\n\nexport type {\n LoginAnthropicWithPageOptions,\n} from './anthropic'\nexport {\n createAnthropicOAuthProviderWithCustomPage,\n loginAnthropicWithCustomPage,\n} from './anthropic'\nexport {\n createCursorOAuthProvider,\n CURSOR_OAUTH_PROVIDER_ID,\n loginCursor,\n refreshCursorToken,\n} from './cursor'\nexport type {\n LoginOpenAICodexWithPageOptions,\n} from './openai-codex'\nexport {\n createOpenAICodexOAuthProviderWithCustomPage,\n loginOpenAICodexWithCustomPage,\n} from './openai-codex'\nexport type {\n OAuthCallbackPage,\n OAuthCallbackPageRenderer,\n} from './render'\nexport {\n escapeHtmlForCallbackPage,\n renderDefaultCallbackPage,\n} from './render'\nexport {\n createXaiOAuthProvider,\n loginXai,\n refreshXaiToken,\n XAI_OAUTH_PROVIDER_ID,\n xaiBaseUrl,\n} from './xai'\nexport type { XaiOAuthCredentials } from './xai'\n\nexport interface CustomCallbackOAuthProviders {\n anthropic: OAuthProviderInterface\n openaiCodex: OAuthProviderInterface\n}\n\n/**\n * Bundle helper — returns both Anthropic + OpenAI Codex providers wired\n * with the same renderer. Hosts that want different renderers per provider\n * should call the individual `create…WithCustomPage` factories instead.\n */\nexport function createCustomCallbackOAuthProviders(\n renderPage: OAuthCallbackPageRenderer,\n): CustomCallbackOAuthProviders {\n return {\n anthropic: createAnthropicOAuthProviderWithCustomPage(renderPage),\n openaiCodex: createOpenAICodexOAuthProviderWithCustomPage(renderPage),\n }\n}\n\nlet cursorRegistered = false\n\n/**\n * Register Cursor with pi-ai's OAuth registry.\n *\n * Unlike Anthropic / Codex, Cursor is **not** built into pi-ai, so\n * `getOAuthApiKey('cursor', …)` (used by `resolveOAuthApiKey` for lazy\n * token refresh) would throw \"Unknown OAuth provider\" until we register it.\n * Call once at startup from both the CLI auth path and the TUI. Idempotent.\n */\nexport function registerCursorOAuthProvider(): OAuthProviderInterface {\n const provider = createCursorOAuthProvider()\n if (!cursorRegistered) {\n registerOAuthProvider(provider)\n cursorRegistered = true\n }\n return provider\n}\n\nlet xaiRegistered = false\n\n/**\n * Register xAI Grok with pi-ai's OAuth registry.\n *\n * pi-ai ships an xAI *API-key* provider, but no OAuth one — so\n * `getOAuthApiKey('xai-oauth', …)` (lazy refresh in `resolveOAuthApiKey`)\n * would throw \"Unknown OAuth provider\" until we register our flow. The\n * callback page routes through the supplied renderer so the post-redirect\n * HTML matches zidane's theme. Call once at startup; idempotent.\n */\nexport function registerXaiOAuthProvider(\n renderPage: OAuthCallbackPageRenderer,\n): OAuthProviderInterface {\n const provider = createXaiOAuthProvider(renderPage)\n if (!xaiRegistered) {\n registerOAuthProvider(provider)\n xaiRegistered = true\n }\n return provider\n}\n","/**\n * Provider registry — the customization seam for the TUI.\n *\n * A {@link ProviderDescriptor} carries everything the TUI needs to know about\n * a provider in one place: how to instantiate it, what to show in the UI,\n * how to detect credentials, how to OAuth (if applicable), and where to look\n * up its models. Hosts can ship their own descriptors instead of the built-in\n * four — see {@link BUILTIN_PROVIDERS}.\n */\n\nimport type { Provider } from '../providers'\nimport type { ModelCatalogEntry } from '../providers/model-catalog'\nimport type { OAuthProviderInterface } from '../providers/oauth-registry'\nimport { getBuiltinModel as getModel, getBuiltinModels as getModels } from '@earendil-works/pi-ai/providers/all'\nimport { anthropic, arcee, baseten, cerebras, cursor, local, mistral, openai, openrouter, xai, zai } from '../providers'\nimport { listModelOverlay, listModels, lookupModel, mergeCatalogEntry } from '../providers/model-catalog'\nimport { ANTHROPIC_EXTRA_MODELS, FAST_MODE_OPTIONS } from './anthropic-models'\nimport { createCustomCallbackOAuthProviders, registerCursorOAuthProvider, registerXaiOAuthProvider, renderDefaultCallbackPage } from './oauth-page'\n\n/**\n * pi-ai's stock Anthropic + Codex OAuth providers bake their own callback\n * HTML; we route both through `renderDefaultCallbackPage` so the post-redirect\n * page matches zidane's theme. The override lives in `src/chat/oauth-page/`\n * — see that folder's `index.ts` for the deletion path once pi-ai exposes\n * a `renderCallbackPage` hook upstream.\n */\nconst { anthropic: anthropicOAuthProvider, openaiCodex: openaiCodexOAuthProvider }\n = createCustomCallbackOAuthProviders(renderDefaultCallbackPage)\n\n/**\n * Cursor isn't built into pi-ai, so registering it makes lazy token refresh\n * (`getOAuthApiKey('cursor', …)` inside `resolveOAuthApiKey`) resolve to our\n * Cursor flow. The poll-based login has no callback server, so no themed page.\n */\nconst cursorOAuthProvider = registerCursorOAuthProvider()\n\n/**\n * xAI Grok isn't built into pi-ai's OAuth registry (only its API-key provider\n * is), so register our loopback-PKCE flow — themed via `renderDefaultCallbackPage`\n * — so lazy token refresh resolves through it.\n */\nconst xaiOAuthProvider = registerXaiOAuthProvider(renderDefaultCallbackPage)\n\n/**\n * Structural model metadata — compatible with pi-ai's `Model` interface but\n * not coupled to it, so hosts can pass either pi-ai-shaped objects or a\n * custom registry of their own.\n *\n * Deliberately a structural duplicate of pi-ai's `Model` rather than a\n * re-export: keeps the public API stable when pi-ai bumps shape, and lets\n * hosts implement their own registry without depending on pi-ai's types.\n *\n * Lives here (rather than `./config`) because `ProviderDescriptor.models`\n * needs it — pushing it to `config.tsx` created an import cycle.\n */\nexport interface ModelInfo {\n id: string\n name?: string\n contextWindow: number\n maxTokens?: number\n reasoning?: boolean\n input?: readonly ('text' | 'image')[]\n cost?: { input: number, output: number, cacheRead?: number, cacheWrite?: number }\n provider?: string\n /**\n * Model-specific toggleable knobs surfaced in the UI (e.g. Anthropic's\n * \"fast mode\"). Each option is an independent boolean the user flips on the\n * active model; enabled options are collected into `StreamOptions.modelOptions`\n * keyed by {@link ModelOption.id} and applied to the wire by the provider.\n * Omitted / empty → the model has no custom options and the picker is hidden.\n */\n options?: readonly ModelOption[]\n}\n\n/**\n * A single toggleable model option. Declarative metadata only — the provider\n * owns the actual request-body translation (keyed on {@link id}). Kept generic\n * (not \"fast mode\"-specific) so new per-model knobs land without a type change.\n */\nexport interface ModelOption {\n /** Stable identifier; the key under which the toggle persists + reaches the provider. */\n id: string\n /** Short label shown in the picker (e.g. \"Fast mode\"). */\n label: string\n /** One-line description of the trade-off (shown in the picker row). */\n description?: string\n /**\n * Optional cost multipliers applied to the model's base rates while the\n * option is enabled. `{ input: 2, output: 2 }` doubles both. Used by the\n * cost estimator so the footer reflects premium-priced modes (fast mode).\n */\n costMultiplier?: { input?: number, output?: number, cacheRead?: number, cacheWrite?: number }\n}\n\n/**\n * Free-form configuration field that a provider needs the user to supply\n * alongside (or instead of) an API key — e.g. a self-hosted base URL, an\n * Azure deployment name, a default model id.\n *\n * The wizard prompts for each field in order. Submitted values are persisted\n * into the apikey credential (as `customFields[key]`) and mirrored into\n * `process.env[envVar]` so the provider's factory can read them via the same\n * env-var convention as `envKey`.\n *\n * Descriptors without an `envKey` use `customFields` as the sole credential\n * mechanism — the wizard skips its API-key step in that case.\n */\nexport interface CustomField {\n /** Storage key inside the credential's `customFields` record. */\n key: string\n /** Human-readable label shown in the wizard step's title. */\n label: string\n /** Env var name to mirror this field into at TUI launch + on submit. */\n envVar: string\n /** Optional placeholder shown in the wizard input. */\n placeholder?: string\n /** Optional helper copy rendered above the input. */\n hint?: string\n /** When true, an empty submission is rejected by the wizard. Default: false. */\n required?: boolean\n}\n\nexport interface ProviderDescriptor {\n /**\n * Unique identifier. Used as the registry key, persisted in `state.json`\n * as the resumed provider, and (by default) as the credential-file key.\n */\n key: string\n /** Display name shown in the TUI's provider picker and labels. */\n label: string\n /**\n * Factory that builds a fresh `Provider` instance. Called on every session\n * activation. Some factories (e.g. zidane's built-in `anthropic`) eagerly\n * resolve credentials at construction time and throw when none are\n * configured — set {@link defaultModel} on the descriptor to avoid the TUI\n * calling the factory before the user has picked + authed a provider.\n */\n factory: () => Provider\n /**\n * Default model id to seed the picker with. When omitted, the TUI falls\n * back to `descriptor.factory().meta.defaultModel`, which constructs the\n * provider — fine for lazy-credential factories, but breaks for factories\n * that throw on missing credentials before the wizard runs. Setting this\n * eagerly lets the TUI render the auth wizard without ever instantiating\n * the provider.\n */\n defaultModel?: string\n /**\n * Env var checked when detecting whether the user has an API key\n * configured for this provider. Optional — set to `undefined` when the\n * provider only supports OAuth (or only credentials via a custom path).\n */\n envKey?: string\n /**\n * Key under which credentials live in `credentials.json`. Defaults to\n * `key`. The only built-in that overrides this is OpenAI Codex\n * (`openai` → `openai-codex`) to stay compatible with the harness\n * provider's existing lookup.\n */\n credentialFileKey?: string\n /** Placeholder shown in the wizard's API-key input. */\n apiKeyPlaceholder?: string\n /**\n * pi-ai (or compat) OAuth provider. When present, the wizard offers an\n * OAuth option in addition to API key.\n */\n oauthProvider?: OAuthProviderInterface\n /**\n * Optional copy appended to the wizard's \"OAuth\" method description.\n * Use to communicate why a user might pick OAuth (e.g. subscription tier,\n * org-wide SSO). Shown after `browser-based sign-in`. Skip the leading\n * space — the wizard adds it. Example: `'Claude Pro/Max subscription'`.\n */\n oauthHint?: string\n /**\n * pi-ai provider id used to look up models in pi-ai's built-in registry.\n * Defaults to `key`. Only Codex differs (`openai` → `openai-codex`).\n */\n piProviderId?: string\n /**\n * Back-compat aliases for persisted model ids whose upstream slug changed.\n * Used for metadata lookups only; aliases are not surfaced as picker rows.\n */\n modelAliases?: Readonly<Record<string, string>>\n /**\n * Override the model list returned for this provider's picker. When set,\n * skips pi-ai's registry entirely. Useful for hosts maintaining their\n * own model catalogue or for custom providers pi-ai doesn't know about.\n */\n models?: readonly ModelInfo[]\n /**\n * Additional free-form fields the wizard should collect from the user\n * (e.g. base URL for a self-hosted endpoint, default model slug for a\n * provider with no fixed catalogue). Each field is persisted in the\n * apikey credential's `customFields` map and mirrored into the matching\n * `envVar` at TUI launch.\n *\n * When `envKey` is also set, the wizard collects custom fields **before**\n * the API key. When `envKey` is omitted, the wizard skips the API-key\n * step entirely — `customFields` becomes the sole credential mechanism\n * (used by the `local` provider, which authenticates via baseURL only).\n */\n customFields?: readonly CustomField[]\n /**\n * Extra models merged AHEAD of pi-ai's registry list (de-duped by id, pi-ai\n * wins on collision). Unlike {@link models} this augments rather than\n * replaces — used to surface freshly-shipped models pi-ai hasn't bundled\n * yet without losing the rest of the registry. Ignored when {@link models}\n * is set.\n */\n extraModels?: readonly ModelInfo[]\n /**\n * Resolve the custom {@link ModelOption}s a given model id supports. Used to\n * attach options (e.g. fast mode) to models that come from pi-ai's registry —\n * which {@link extraModels} can't reach because pi-ai wins the id collision.\n * Returns `undefined` / `[]` for models with no options. Models that already\n * carry `options` (e.g. via {@link extraModels}) keep theirs.\n */\n optionsFor?: (modelId: string) => readonly ModelOption[] | undefined\n /**\n * Env var holding a user-supplied context window (in tokens) for providers\n * that have no model registry to advertise one — chiefly the `local`\n * provider, whose runtime (Ollama, vLLM, …) doesn't expose its loaded\n * model's window over the OpenAI-compat API. When set, {@link getContextWindow}\n * falls back to resolving this env var (via {@link resolveContextWindow})\n * after the registry lookup comes up empty. The value is either a single\n * window (`\"32768\"`, applied to every model) or a per-model map\n * (`\"llama3.1:8b=32768, qwen=128k, 64k\"`, with an optional bare fallback).\n * Pair it with a {@link CustomField} that mirrors the user's value into the\n * same `envVar` so the footer's context indicator and auto-compaction work\n * for local models.\n */\n contextWindowEnvVar?: string\n}\n\n/** Convenience accessor — returns `credentialFileKey ?? key`. */\nexport function credKeyOf(desc: ProviderDescriptor): string {\n return desc.credentialFileKey ?? desc.key\n}\n\n/** Convenience accessor — returns `piProviderId ?? key`. */\nexport function piIdOf(desc: ProviderDescriptor): string {\n return desc.piProviderId ?? desc.key\n}\n\n/**\n * Catalog keys a descriptor answers to, most specific first.\n *\n * Usually one: for every provider except Codex the descriptor `key`, the pi-ai\n * registry id, and the `Provider.name` used on the wire are the same string.\n * Codex is the exception — metadata lives under `openai-codex` while its cost\n * accounting runs under `openai` — so a consumer's single entry has to work\n * under either. The wire side does the same via `aliasCatalogProviders`; if\n * these two disagreed, an override would half-apply (right invoice, wrong\n * context gauge), which is harder to notice than failing outright.\n */\nfunction catalogProvidersOf(desc: ProviderDescriptor): readonly string[] {\n const piId = piIdOf(desc)\n return piId === desc.key ? [piId] : [piId, desc.key]\n}\n\n// ---------------------------------------------------------------------------\n// Built-in descriptors\n// ---------------------------------------------------------------------------\n\nexport const anthropicDescriptor: ProviderDescriptor = {\n key: 'anthropic',\n label: 'Anthropic',\n factory: anthropic,\n defaultModel: 'claude-fable-5-1',\n envKey: 'ANTHROPIC_API_KEY',\n apiKeyPlaceholder: 'sk-ant-…',\n oauthProvider: anthropicOAuthProvider,\n oauthHint: 'Claude Pro/Max subscription',\n extraModels: ANTHROPIC_EXTRA_MODELS,\n optionsFor: id => FAST_MODE_OPTIONS[id] ? [FAST_MODE_OPTIONS[id]] : undefined,\n}\n\n/**\n * Models we have CONFIRMED accept OpenAI's priority service tier\n * (`serviceTier: 'priority'`, wired in `src/providers/openai.ts`) at the\n * published 2.5× multiplier. Deliberately a set rather than \"the current\n * flagship\": priority availability and its multiplier are per-model, so a new\n * default (gpt-6-astra, the 5.6 family) does not inherit the toggle — add the\n * id here once https://platform.openai.com/docs/guides/priority-processing\n * lists it, together with the multiplier its pricing page states. Missing =\n * the picker hides the fast toggle, which is the safe direction: offering it\n * would send a tier the model may reject and would misreport cost.\n */\nconst OPENAI_PRIORITY_TIER_MODELS: ReadonlySet<string> = new Set(['gpt-5.5'])\n\nexport const openaiDescriptor: ProviderDescriptor = {\n key: 'openai',\n label: 'OpenAI',\n factory: openai,\n defaultModel: 'gpt-6-astra',\n envKey: 'OPENAI_CODEX_API_KEY',\n credentialFileKey: 'openai-codex',\n piProviderId: 'openai-codex',\n // Both credential shapes stream: the provider auto-detects the backend —\n // an `sk-…` platform key routes to the standard Responses API\n // (api.openai.com, platform billing), a Codex OAuth JWT (`eyJ…`, via the\n // login flow) to the ChatGPT Codex backend (subscription billing).\n apiKeyPlaceholder: 'sk-… (platform) or eyJ… (Codex)',\n oauthProvider: openaiCodexOAuthProvider,\n // OpenAI publishes `gpt-5.6` as an alias for the Sol flagship; pi-ai's\n // registry only carries the concrete ids, so resolve the shorthand for\n // metadata lookups (context window, reasoning, cost). Wire-side mirror\n // lives in `MODEL_ALIASES` in `src/providers/openai.ts`.\n modelAliases: {\n 'gpt-5.6': 'gpt-5.6-sol',\n },\n optionsFor: id => OPENAI_PRIORITY_TIER_MODELS.has(id)\n ? [{\n id: 'fast',\n label: 'Fast mode',\n description: 'Priority tier at 2.5× pricing',\n costMultiplier: { input: 2.5, output: 2.5, cacheRead: 2.5, cacheWrite: 2.5 },\n }]\n : undefined,\n}\n\nexport const openrouterDescriptor: ProviderDescriptor = {\n key: 'openrouter',\n label: 'OpenRouter',\n factory: openrouter,\n defaultModel: 'anthropic/claude-fable-5.1',\n envKey: 'OPENROUTER_API_KEY',\n apiKeyPlaceholder: 'sk-or-…',\n // Metadata-side mirror of the OpenRouter provider's wire alias (see\n // `MODEL_ALIASES` in `src/providers/openrouter.ts`). Keeps context-window /\n // reasoning lookups resolving for sessions persisted with the old slug.\n modelAliases: {\n 'anthropic/claude-sonnet-4-6': 'anthropic/claude-sonnet-4.6',\n 'anthropic/claude-fable-5-1': 'anthropic/claude-fable-5.1',\n },\n}\n\nexport const cerebrasDescriptor: ProviderDescriptor = {\n key: 'cerebras',\n label: 'Cerebras',\n factory: cerebras,\n // `zai-glm-4.7` was retired from Cerebras' catalog in pi-ai 0.84.3; the\n // registry now lists only `gpt-oss-120b` and `gemma-4-31b`.\n defaultModel: 'gpt-oss-120b',\n envKey: 'CEREBRAS_API_KEY',\n apiKeyPlaceholder: 'csk-…',\n}\n\n/**\n * xAI Grok. Supports BOTH a static API key (`XAI_API_KEY`) and the\n * SuperGrok / X Premium+ OAuth subscription, so the wizard offers both methods.\n * Models come from pi-ai's `xai` registry (OpenAI-compatible endpoint), shared\n * by both auth modes.\n */\nexport const xaiDescriptor: ProviderDescriptor = {\n key: 'xai',\n label: 'xAI Grok',\n factory: xai,\n defaultModel: 'grok-4.6',\n envKey: 'XAI_API_KEY',\n apiKeyPlaceholder: 'xai-…',\n oauthProvider: xaiOAuthProvider,\n oauthHint: 'SuperGrok / X Premium+ subscription',\n // pi-ai stores the OAuth credential under the provider id, distinct from the\n // `xai` descriptor key so the API-key and OAuth credentials never collide.\n credentialFileKey: 'xai-oauth',\n piProviderId: 'xai',\n}\n\n/**\n * Arcee Trinity models (Platform API slugs). pi-ai has no Arcee registry, so we\n * ship a curated list. `reasoning: true` surfaces the effort/thinking UI — the\n * `arcee()` factory round-trips Trinity's `reasoning_content` across turns so the\n * model resumes its chain-of-thought through tool calls.\n *\n * Costs/context per Arcee's published Trinity catalogue.\n */\nconst ARCEE_MODELS: readonly ModelInfo[] = [\n { id: 'trinity-large-thinking', name: 'Trinity Large (Thinking)', contextWindow: 256_000, reasoning: true, input: ['text'], cost: { input: 0.25, output: 0.8 } },\n { id: 'trinity-large-preview', name: 'Trinity Large (Preview)', contextWindow: 128_000, input: ['text'], cost: { input: 0.45, output: 0.15 } },\n { id: 'trinity-mini', name: 'Trinity Mini', contextWindow: 128_000, input: ['text'], cost: { input: 0.045, output: 0.15 } },\n]\n\nexport const arceeDescriptor: ProviderDescriptor = {\n key: 'arcee',\n label: 'Arcee',\n factory: arcee,\n defaultModel: 'trinity-large-thinking',\n envKey: 'ARCEE_API_KEY',\n apiKeyPlaceholder: 'arcee-…',\n models: ARCEE_MODELS,\n}\n\n/**\n * Baseten Model APIs — the curated list that predates pi-ai's `baseten`\n * registry, now merged BEHIND it as `extraModels` (pi-ai wins on shared ids).\n * Mirrors https://docs.baseten.co/inference/model-apis/overview (context\n * windows) and Baseten's published per-MTok rates. Models without a\n * `cost` (GLM 5.1, Nemotron Ultra) have no published rate yet — the footer\n * cost indicator hides for those. `/v1/models` is the live source of truth.\n * Keep the rates here in sync with `EXTRA_COST_RATES` in `../providers/cost.ts`:\n * this table is picker metadata only (`ModelInfo.cost`); turn pricing — and\n * therefore every cost surface — comes from `fillEstimatedCost`, which reads\n * the registry and that table, never this one.\n *\n * All listed models support reasoning (`reasoning_content`); the `baseten()`\n * factory maps the thinking level to `reasoning_effort` / `chat_template_args`\n * per model.\n */\nconst BASETEN_MODELS: readonly ModelInfo[] = [\n { id: 'zai-org/GLM-5.2', name: 'GLM 5.2', contextWindow: 1_000_000, reasoning: true, input: ['text'], cost: { input: 1.4, output: 4.4, cacheRead: 0.14 } },\n { id: 'zai-org/GLM-5.1', name: 'GLM 5.1', contextWindow: 200_000, reasoning: true, input: ['text'] },\n { id: 'zai-org/GLM-5', name: 'GLM 5', contextWindow: 200_000, reasoning: true, input: ['text'], cost: { input: 0.95, output: 3.15, cacheRead: 0.2 } },\n { id: 'zai-org/GLM-4.7', name: 'GLM 4.7', contextWindow: 200_000, reasoning: true, input: ['text'], cost: { input: 0.6, output: 2.2, cacheRead: 0.12 } },\n { id: 'moonshotai/Kimi-K2.6', name: 'Kimi K2.6', contextWindow: 256_000, reasoning: true, input: ['text', 'image'], cost: { input: 1, output: 3.9, cacheRead: 0.2 } },\n { id: 'moonshotai/Kimi-K2.5', name: 'Kimi K2.5', contextWindow: 256_000, reasoning: true, input: ['text', 'image'], cost: { input: 0.6, output: 3, cacheRead: 0.12 } },\n { id: 'deepseek-ai/DeepSeek-V4-Pro', name: 'DeepSeek V4 Pro', contextWindow: 131_000, reasoning: true, input: ['text'], cost: { input: 1.74, output: 3.48, cacheRead: 0.145 } },\n { id: 'nvidia/Nemotron-120B-A12B', name: 'Nemotron Super', contextWindow: 200_000, reasoning: true, input: ['text'], cost: { input: 0.3, output: 0.75, cacheRead: 0.06 } },\n { id: 'nvidia/NVIDIA-Nemotron-3-Ultra-550B-A55B', name: 'Nemotron Ultra', contextWindow: 200_000, reasoning: true, input: ['text'] },\n { id: 'openai/gpt-oss-120b', name: 'GPT-OSS 120B', contextWindow: 128_000, reasoning: true, input: ['text'], cost: { input: 0.1, output: 0.5 } },\n]\n\nexport const basetenDescriptor: ProviderDescriptor = {\n key: 'baseten',\n label: 'Baseten',\n factory: baseten,\n defaultModel: 'zai-org/GLM-5.3',\n envKey: 'BASETEN_API_KEY',\n apiKeyPlaceholder: 'baseten-api-key…',\n // pi-ai bundles a `baseten` registry now (GLM 5.x, Kimi K2.x/K3, DeepSeek\n // V4, Nemotron, gpt-oss); the curated list above stays as `extraModels`\n // so ids it carries that pi-ai does not remain selectable. Bundled wins on\n // overlap, so pi-ai's rates and windows supersede ours as they land.\n piProviderId: 'baseten',\n extraModels: BASETEN_MODELS,\n}\n\n/**\n * Z.ai (Zhipu) GLM Open Platform — the GLM 4.x list that predates pi-ai's\n * `zai` registry, now merged BEHIND it as `extraModels` (pi-ai wins on shared\n * ids). Mirrors https://docs.z.ai/guides/overview/pricing and per-model\n * context windows from the GLM-4.7 overview page. `reasoning: true`\n * surfaces the effort/thinking UI — the `zai()` factory maps the thinking\n * level to Z.ai's `extra_body.thinking` toggle and round-trips\n * `reasoning_content` across turns. GLM-4.7-Flash and GLM-4.5-Flash are\n * Z.ai's zero-cost tiers (`cost` omitted).\n */\nconst ZAI_MODELS: readonly ModelInfo[] = [\n { id: 'glm-4.7-flash', name: 'GLM-4.7-Flash', contextWindow: 200_000, maxTokens: 128_000, reasoning: true, input: ['text'] },\n { id: 'glm-4.7', name: 'GLM-4.7', contextWindow: 200_000, maxTokens: 128_000, reasoning: true, input: ['text'], cost: { input: 0.6, output: 2.2, cacheRead: 0.11 } },\n { id: 'glm-4.6', name: 'GLM-4.6', contextWindow: 200_000, maxTokens: 128_000, reasoning: true, input: ['text'], cost: { input: 0.6, output: 2.2, cacheRead: 0.11 } },\n { id: 'glm-4.5-flash', name: 'GLM-4.5-Flash', contextWindow: 128_000, reasoning: true, input: ['text'] },\n { id: 'glm-4.5-air', name: 'GLM-4.5-Air', contextWindow: 128_000, reasoning: true, input: ['text'], cost: { input: 0.2, output: 1.1, cacheRead: 0.03 } },\n]\n\n/**\n * Mistral La Plateforme. Models come from pi-ai's `mistral` registry (plain\n * slugs, same ids the OpenAI-compatible `/v1/chat/completions` route accepts).\n * The `mistral()` factory maps the thinking level to `reasoning_effort` for the\n * families that document it and parses Mistral's `thinking` content chunks.\n */\nexport const mistralDescriptor: ProviderDescriptor = {\n key: 'mistral',\n label: 'Mistral',\n factory: mistral,\n defaultModel: 'mistral-medium-latest',\n envKey: 'MISTRAL_API_KEY',\n apiKeyPlaceholder: 'mistral-api-key…',\n piProviderId: 'mistral',\n}\n\nexport const zaiDescriptor: ProviderDescriptor = {\n key: 'zai',\n label: 'Z.ai',\n factory: zai,\n defaultModel: 'glm-5.3-flash',\n envKey: 'ZAI_API_KEY',\n apiKeyPlaceholder: 'zai-api-key…',\n // Same shape as Baseten: pi-ai's `zai` registry (GLM 5.x) owns the list,\n // the GLM 4.x entries below ride as extras for sessions pinned to them.\n piProviderId: 'zai',\n extraModels: ZAI_MODELS,\n}\n\n/**\n * Conservative context-window assumption for a user-configured local model.\n * Local runtimes expose no reliable per-model metadata over the OpenAI-compat\n * `/v1/models` endpoint (it returns ids, not context sizes), so we pick a\n * floor that fits the smallest mainstream OSS models (Llama 3.x 8B, Qwen2.5)\n * without over-promising. The footer's context indicator and auto-compaction\n * lean on this — under-estimating is the safe direction (compact early rather\n * than overflow the server's real window).\n *\n * Users running larger-context models (e.g. a 128K Qwen) can raise it via the\n * `LOCAL_LLM_CONTEXT_WINDOW` env var / customField, which supports a single\n * value or a per-model map (resolved by {@link resolveContextWindow}).\n */\nconst LOCAL_DEFAULT_CONTEXT_WINDOW = 8_192\n\n/**\n * Local OpenAI-compatible LLM (Ollama, vLLM, LM Studio, Lemonade, llama.cpp).\n *\n * No fixed base URL or model catalogue — both come from the user via\n * `customFields`. No `envKey`, so the wizard skips the API-key prompt and\n * treats the customFields-only credential as the auth signal (see\n * `applyApiKeyEnv` + `detectAuth`).\n *\n * Vision / cache / reasoning are off by default in the factory — flip them\n * by passing a custom descriptor that calls `local({ capabilities })`.\n *\n * `models` is a getter, not a static list: there's no fixed catalogue to ship,\n * but once the user has configured a default model (mirrored into\n * `LOCAL_LLM_DEFAULT_MODEL` by `applyApiKeyEnv`), we surface it as a\n * single-entry list so the model picker + footer aren't empty. Resolved at\n * access time because the env var is populated at TUI launch, after this\n * module loads. Empty list when unconfigured — the picker hides, the user\n * types a slug at the prompt as before.\n */\nexport const localDescriptor: ProviderDescriptor = {\n key: 'local',\n label: 'Local LLM',\n factory: local,\n get models(): readonly ModelInfo[] {\n const configured = process.env.LOCAL_LLM_DEFAULT_MODEL?.trim()\n if (!configured)\n return []\n // Resolve the window from the user's `LOCAL_LLM_CONTEXT_WINDOW` (single\n // value or per-model map), falling back to the conservative default. Uses\n // the same parser as `getContextWindow`'s `contextWindowEnvVar` path so\n // both surfaces agree on the number.\n const resolved = resolveContextWindow(process.env.LOCAL_LLM_CONTEXT_WINDOW, configured)\n return [{\n id: configured,\n name: configured,\n contextWindow: resolved ?? LOCAL_DEFAULT_CONTEXT_WINDOW,\n input: ['text'],\n }]\n },\n customFields: [\n {\n key: 'baseURL',\n label: 'Base URL',\n envVar: 'LOCAL_LLM_BASE_URL',\n placeholder: 'http://localhost:11434/v1',\n hint: 'Where your local LLM server is reachable. Examples: Ollama → http://localhost:11434/v1, vLLM → http://localhost:8000/v1, LM Studio → http://localhost:1234/v1.',\n required: true,\n },\n {\n key: 'apiKey',\n label: 'API key',\n envVar: 'LOCAL_LLM_API_KEY',\n placeholder: 'leave blank if your server is unauthenticated',\n },\n {\n key: 'model',\n label: 'Default model',\n envVar: 'LOCAL_LLM_DEFAULT_MODEL',\n placeholder: 'e.g. llama3.1:8b, qwen2.5-coder, mistral-small',\n hint: 'Model id your server exposes. Leave blank to pick later from the model picker.',\n },\n {\n key: 'contextWindow',\n label: 'Context window (tokens)',\n envVar: 'LOCAL_LLM_CONTEXT_WINDOW',\n placeholder: 'e.g. 32768 · or per-model: llama3.1:8b=32768, qwen2.5-coder=128k, 64k',\n hint: 'Context length(s) for your local model(s) — local runtimes don\\'t advertise this, so set it here to enable the context indicator and auto-compaction. Use a single value (32768, 128k) for all models, or a comma-separated map of `model=window` with an optional bare value as the fallback. Press',\n },\n ],\n contextWindowEnvVar: 'LOCAL_LLM_CONTEXT_WINDOW',\n}\n\n/**\n * Cursor models. pi-ai has no Cursor registry, so we ship a small curated\n * list (Cursor proxies these). Context windows are nominal — Cursor inference\n * isn't wired yet (the provider's `stream()` throws), so these only drive the\n * picker once the protobuf transport lands.\n */\nconst CURSOR_MODELS: readonly ModelInfo[] = [\n { id: 'claude-4.6-sonnet', name: 'Claude Sonnet 4.6', contextWindow: 200_000, reasoning: true, input: ['text', 'image'] },\n { id: 'claude-4.8-opus', name: 'Claude Opus 4.8', contextWindow: 200_000, reasoning: true, input: ['text', 'image'] },\n { id: 'gpt-5.5', name: 'GPT-5.5', contextWindow: 272_000, reasoning: true, input: ['text', 'image'] },\n { id: 'composer-2', name: 'Composer 2', contextWindow: 200_000, input: ['text'] },\n { id: 'composer-2.5', name: 'Composer 2.5', contextWindow: 200_000, input: ['text'] },\n]\n\nexport const cursorDescriptor: ProviderDescriptor = {\n key: 'cursor',\n label: 'Cursor',\n factory: cursor,\n defaultModel: 'claude-4.6-sonnet',\n // OAuth-only: no API-key env var.\n envKey: undefined,\n oauthProvider: cursorOAuthProvider,\n oauthHint: 'Cursor subscription',\n models: CURSOR_MODELS,\n}\n\n/**\n * Default provider registry. Passed verbatim when `runTui` is invoked without\n * an explicit `providers` option. Hosts that want to override per-provider\n * metadata can spread this and replace specific entries:\n *\n * ```ts\n * runTui({ providers: { ...BUILTIN_PROVIDERS, anthropic: myOwnAnthropicDescriptor } })\n * ```\n *\n * `cursor` is intentionally NOT registered here: OAuth works, but inference\n * isn't wired (`cursor.stream()` throws — Cursor speaks a protobuf/HTTP-2\n * agent protocol, not OpenAI-compat). Shipping it in the picker only lets users\n * select a model that errors every turn. The descriptor stays exported so a\n * host can opt in (`{ ...BUILTIN_PROVIDERS, cursor: cursorDescriptor }`) once\n * the transport lands — re-add it here at that point.\n */\nexport const BUILTIN_PROVIDERS: Readonly<Record<string, ProviderDescriptor>> = {\n anthropic: anthropicDescriptor,\n openai: openaiDescriptor,\n openrouter: openrouterDescriptor,\n cerebras: cerebrasDescriptor,\n xai: xaiDescriptor,\n arcee: arceeDescriptor,\n baseten: basetenDescriptor,\n zai: zaiDescriptor,\n mistral: mistralDescriptor,\n local: localDescriptor,\n}\n\n// ---------------------------------------------------------------------------\n// Model registry helper\n// ---------------------------------------------------------------------------\n\n/**\n * Resolve the model list for a given provider. Honors `descriptor.models`\n * when set; otherwise queries pi-ai via `descriptor.piProviderId`. Returns\n * `[]` for descriptors with no known mapping (custom providers without a\n * model list) — callers should hide the model picker in that case.\n */\nexport function modelsForDescriptor(descriptor: ProviderDescriptor): readonly ModelInfo[] {\n if (descriptor.models)\n return descriptor.models\n const piId = piIdOf(descriptor)\n const catalogKeys = catalogProvidersOf(descriptor)\n // A catalog whose `list` answers OWNS this provider's models — bundled\n // entries are not consulted, so it can hide as well as add. The picker\n // resolves metadata before any agent exists, so only the process-wide\n // catalog is visible here; `behavior.modelCatalog` governs the wire path.\n const owned = firstDefined(catalogKeys, key => listModels(key))\n let bundled: readonly ModelInfo[]\n if (owned) {\n // An id-less list entry is unusable downstream (the picker selects by id,\n // every lookup keys on it), so drop it with one warning rather than\n // rendering a blank selectable row. `ModelCatalogEntry.id` is optional for\n // the patch/table form, which is exactly how this mistake happens.\n const usable = owned.filter(entry => entry.id !== undefined && entry.id !== '')\n if (usable.length !== owned.length)\n warnOnce(`model catalog: dropped ${owned.length - usable.length} \"${piId}\" list entr${owned.length - usable.length === 1 ? 'y' : 'ies'} with no id`)\n bundled = usable.map(entry => toModelInfo(entry, piId))\n }\n else {\n try {\n bundled = getModels(piId as never) as readonly ModelInfo[]\n }\n catch {\n bundled = []\n }\n // Table-form entries OVERLAY: patch matching ids, append unknown ones,\n // leave everything they don't name intact.\n const overlay = dedupeById(catalogKeys.flatMap(key => listModelOverlay(key)))\n if (overlay.length > 0) {\n const known = new Set(bundled.map(m => m.id))\n bundled = [\n ...bundled.map((m) => {\n const entry = overlay.find(e => e.id === m.id)\n return entry ? mergeCatalogEntry(m, entry) : m\n }),\n ...overlay.filter(e => e.id !== undefined && !known.has(e.id)).map(entry => toModelInfo(entry, piId)),\n ]\n }\n }\n // `extraModels` is OUR stopgap for models pi-ai hasn't bundled yet — the\n // same role the catalog now fills properly. An owning catalog therefore\n // suppresses them too, otherwise a consumer couldn't hide a model we\n // hardcoded and \"owns the list\" would be a half-truth.\n if (!owned && descriptor.extraModels?.length) {\n // Merge extras ahead of the bundled list, de-duped by id (bundled wins, so a\n // model that lands in pi-ai later transparently supersedes the local entry).\n const bundledIds = new Set(bundled.map(m => m.id))\n const extras = descriptor.extraModels.filter(m => !bundledIds.has(m.id))\n bundled = [...extras, ...bundled]\n }\n return descriptor.optionsFor ? bundled.map(m => decorateOptions(m, descriptor)) : bundled\n}\n\n/** First non-`undefined` result of `resolve` across `keys`. */\nfunction firstDefined<T>(keys: readonly string[], resolve: (key: string) => T | undefined): T | undefined {\n for (const key of keys) {\n const value = resolve(key)\n if (value !== undefined)\n return value\n }\n return undefined\n}\n\n/** De-dupe catalog entries by id, first occurrence winning. */\nfunction dedupeById(entries: readonly ModelCatalogEntry[]): readonly ModelCatalogEntry[] {\n if (entries.length < 2)\n return entries\n const seen = new Set<string>()\n return entries.filter((entry) => {\n const id = entry.id ?? ''\n if (seen.has(id))\n return false\n seen.add(id)\n return true\n })\n}\n\n/**\n * Attach descriptor-resolved {@link ModelOption}s to a model that doesn't\n * already declare its own. Pure / non-mutating — returns the input untouched\n * when the model has options already or the descriptor resolves none.\n */\nfunction decorateOptions(model: ModelInfo, descriptor: ProviderDescriptor): ModelInfo {\n if (model.options?.length || !descriptor.optionsFor)\n return model\n const options = descriptor.optionsFor(model.id)\n return options?.length ? { ...model, options } : model\n}\n\n/**\n * Resolve a single model's metadata via the descriptor's model source.\n * Mirrors {@link modelsForDescriptor} routing: descriptor's own list wins,\n * pi-ai's registry is the fallback. Returns `null` when the model isn't\n * known.\n */\nexport function getModelInfo(descriptor: ProviderDescriptor, modelId: string): ModelInfo | null {\n const lookupId = descriptor.modelAliases?.[modelId] ?? modelId\n if (descriptor.models)\n return descriptor.models.find(m => m.id === lookupId) ?? null\n const piId = piIdOf(descriptor)\n const entry = firstDefined(catalogProvidersOf(descriptor), key => lookupModel(key, lookupId))\n try {\n const bundled = getModel(piId as never, lookupId as never) as ModelInfo | undefined\n if (bundled)\n return decorateOptions(mergeCatalogEntry(bundled, entry), descriptor)\n }\n catch {\n // fall through to the catalog entry, then extras\n }\n // Registry miss: a catalog entry stands on its own — that's the point, these\n // are models the bundled snapshot has never heard of.\n if (entry)\n return decorateOptions(toModelInfo({ ...entry, id: entry.id ?? lookupId }, piId), descriptor)\n const extra = descriptor.extraModels?.find(m => m.id === lookupId)\n return extra ? decorateOptions(extra, descriptor) : null\n}\n\n/**\n * De-duped warning sink. `modelsForDescriptor` runs on every picker render, so\n * a malformed catalog entry must not spam the terminal. Mirrors the\n * `[zidane:schema]` seen-set convention in `providers/schema-sanitize.ts`.\n */\nconst warnedCatalogLines = new Set<string>()\nfunction warnOnce(line: string): void {\n if (warnedCatalogLines.has(line))\n return\n warnedCatalogLines.add(line)\n console.warn(`[zidane:model-catalog] ${line}`)\n}\n\n/**\n * Catalog entry → `ModelInfo`. `contextWindow` is required on `ModelInfo` but\n * optional on an entry (most overrides only correct a ceiling or a rate), so a\n * missing one becomes `0`. That placeholder is NOT a window: `getContextWindow`\n * treats non-positive values as unknown and falls through to\n * `contextWindowEnvVar` / `null`, which is what keeps the context panel from\n * rendering percentages against a 1-token window.\n */\nfunction toModelInfo(entry: ModelCatalogEntry, provider: string): ModelInfo {\n return {\n id: entry.id ?? '',\n ...(entry.name !== undefined ? { name: entry.name } : {}),\n contextWindow: entry.contextWindow ?? 0,\n ...(entry.maxTokens !== undefined ? { maxTokens: entry.maxTokens } : {}),\n ...(entry.reasoning !== undefined ? { reasoning: entry.reasoning } : {}),\n ...(entry.input !== undefined ? { input: entry.input } : {}),\n ...(entry.cost !== undefined\n ? {\n cost: {\n input: entry.cost.input ?? 0,\n output: entry.cost.output ?? 0,\n ...(entry.cost.cacheRead !== undefined ? { cacheRead: entry.cost.cacheRead } : {}),\n ...(entry.cost.cacheWrite !== undefined ? { cacheWrite: entry.cost.cacheWrite } : {}),\n },\n }\n : {}),\n provider,\n }\n}\n\n/**\n * Parse a user-supplied context-window string into a token count.\n *\n * Accepts a plain integer (`\"32768\"`), or a number with a `k`/`m` suffix\n * (×1_000 / ×1_000_000, case-insensitive, optional space) — so `\"128k\"`\n * yields `128_000`, matching how the footer renders windows (`fmtTokens`).\n * Fractional values are floored (`\"1.5k\"` → `1500`). Returns `null` for\n * empty, malformed, zero, or negative input so callers fall through to\n * \"window unknown\" rather than a bogus ceiling.\n */\nexport function parseContextWindow(raw: string | undefined | null): number | null {\n if (raw == null)\n return null\n const trimmed = raw.trim()\n if (trimmed.length === 0)\n return null\n const match = /^(\\d+(?:\\.\\d+)?)\\s*([km])?$/i.exec(trimmed)\n if (!match)\n return null\n const suffix = match[2]?.toLowerCase()\n const mult = suffix === 'k' ? 1_000 : suffix === 'm' ? 1_000_000 : 1\n const value = Math.floor(Number.parseFloat(match[1]) * mult)\n return value >= 1 ? value : null\n}\n\n/**\n * Resolve a per-model context window from a user-supplied spec string.\n *\n * A spec is a comma-separated list of entries. An entry is either:\n * - `model=window` — a per-model override (split on the **first** `=`, so\n * model ids containing `:` or `/` survive), or\n * - a bare `window` — the fallback applied to any model not named above.\n *\n * Windows are parsed by {@link parseContextWindow} (plain int or k/m suffix).\n * Whitespace around entries and the `=` is ignored; malformed entries are\n * skipped rather than poisoning the whole spec; a later duplicate key wins.\n *\n * Lookup order for `modelId`: exact map entry → bare fallback → `null`. A\n * lone bare value (`\"32768\"`) therefore behaves as a single global window,\n * keeping the simple single-model config working.\n *\n * @example resolveContextWindow('llama3.1:8b=32768, qwen=128k, 64k', 'qwen') // 128000\n */\nexport function resolveContextWindow(spec: string | undefined | null, modelId: string): number | null {\n if (spec == null)\n return null\n const overrides = new Map<string, number>()\n let fallback: number | null = null\n for (const entry of spec.split(',')) {\n const eq = entry.indexOf('=')\n if (eq === -1) {\n const bare = parseContextWindow(entry)\n if (bare != null)\n fallback = bare\n continue\n }\n const key = entry.slice(0, eq).trim()\n const window = parseContextWindow(entry.slice(eq + 1))\n if (key.length > 0 && window != null)\n overrides.set(key, window)\n }\n return overrides.get(modelId) ?? fallback\n}\n\n/**\n * Look up the model's max context window via the descriptor's model source.\n * Falls back to {@link ProviderDescriptor.contextWindowEnvVar} (parsed via\n * {@link resolveContextWindow}, which supports a per-model map) when the\n * registry has nothing — this is how local LLMs, whose runtimes don't\n * advertise a window, get one. Returns `null` when neither source resolves a\n * value (custom slugs, providers without a registry or a configured window);\n * callers should hide the context indicator and skip auto-compaction then.\n */\nexport function getContextWindow(descriptor: ProviderDescriptor, modelId: string): number | null {\n // `> 0`, not just non-null: a model record can carry a placeholder `0` (a\n // catalog entry that only corrects a ceiling or a rate, a descriptor entry\n // with no advertised window). Returning it would make the caller treat 0 as\n // a REAL window — `effectiveContextWindow(0)` clamps to 1 token, and the\n // context panel then renders absurd percentages instead of hiding.\n const fromRegistry = getModelInfo(descriptor, modelId)?.contextWindow\n if (fromRegistry != null && fromRegistry > 0)\n return fromRegistry\n if (descriptor.contextWindowEnvVar)\n return resolveContextWindow(process.env[descriptor.contextWindowEnvVar], modelId)\n return null\n}\n\n// `OUTPUT_RESERVE_TOKENS` + `effectiveContextWindow` moved to the core\n// `src/compact/policy.ts` so the agent loop (which can't import `src/chat/`)\n// shares the same window math as the chat footer + auto-compact trigger.\n// Re-exported here so existing `chat/providers` importers keep working.\nexport { effectiveContextWindow, OUTPUT_RESERVE_TOKENS } from '../compact/policy'\n\n/**\n * Whether the given model exposes a reasoning / extended-thinking knob.\n * Drives the TUI's effort picker visibility — the `ctrl+n` shortcut and\n * the bottom-bar `effortName` segment only surface when this is `true`.\n * Returns `false` for unknown models (no registry entry → no advertised\n * capability).\n */\nexport function modelSupportsReasoning(descriptor: ProviderDescriptor, modelId: string): boolean {\n return getModelInfo(descriptor, modelId)?.reasoning === true\n}\n\n/**\n * Custom {@link ModelOption}s the given model supports (e.g. fast mode), or `[]`\n * when none. Drives the TUI/GUI options picker visibility — surfaces only when\n * non-empty.\n */\nexport function modelOptionsFor(descriptor: ProviderDescriptor, modelId: string): readonly ModelOption[] {\n return getModelInfo(descriptor, modelId)?.options ?? []\n}\n\n/**\n * Normalize a model-options blob to an enabled-only `{ id: true }` map.\n *\n * Single source of truth shared by every surface that reads or persists model\n * options — the TUI run path, the GUI engine reader, and the GUI IPC handlers.\n * Tolerant of arbitrary input (persisted JSON metadata may be anything): a\n * non-object → `{}`, and only entries strictly equal to `true` survive. This\n * keeps the persisted shape minimal (no `{ fast: false }` noise) and means\n * callers never forward a disabled option to `agent.run`.\n */\nexport function enabledModelOptions(raw: unknown): Record<string, boolean> {\n if (!raw || typeof raw !== 'object')\n return {}\n const out: Record<string, boolean> = {}\n for (const [id, on] of Object.entries(raw as Record<string, unknown>)) {\n if (on === true)\n out[id] = true\n }\n return out\n}\n\n/**\n * Resolve a model's remembered options for a (re)pick: keep only options the\n * model still declares AND that are enabled, dropping any that no longer apply\n * (e.g. switching away from a model that supported `fast`). Returns `undefined`\n * when nothing applies so callers can omit the field entirely.\n *\n * Shared by the TUI pick path and the launch-time resume path so \"which options\n * survive a model switch / relaunch\" is decided in exactly one place.\n */\nexport function restoreModelOptions(\n descriptor: ProviderDescriptor,\n modelId: string,\n remembered: Record<string, Record<string, boolean>> | undefined,\n): Record<string, boolean> | undefined {\n const validIds = new Set(modelOptionsFor(descriptor, modelId).map(o => o.id))\n if (validIds.size === 0)\n return undefined\n const enabled = enabledModelOptions(remembered?.[modelId])\n const filtered: Record<string, boolean> = {}\n for (const id of Object.keys(enabled)) {\n if (validIds.has(id))\n filtered[id] = true\n }\n return Object.keys(filtered).length > 0 ? filtered : undefined\n}\n","/**\n * Credential storage for the TUI.\n *\n * Lives at `<dataDir>/credentials.json` (default `~/.zidane/credentials.json`,\n * overridable via `ZIDANE_STORAGE_DIR`). Owner-only (0o600) so refresh tokens\n * and API keys don't leak on shared boxes.\n *\n * Two credential kinds:\n * - `apikey` — a plain API key entered by the user (e.g. via the setup wizard).\n * - `oauth` — tokens obtained via a provider's OAuth flow (refresh-aware).\n *\n * **Slots.** A provider may hold several credentials at once: at most one API\n * key and any number of OAuth accounts (optionally named via `label`). The\n * ACTIVE one — what every reader resolves — sits at the canonical file key;\n * the others sit at `#`-qualified siblings (`foo#alt` for the API key,\n * `foo#account:N` for OAuth accounts). See `./cred-keys` for the arithmetic.\n *\n * **Schema note.** File keys come from {@link ProviderDescriptor.credentialFileKey}\n * (falling back to `descriptor.key`). They must match what the harness providers\n * look up at runtime — for built-ins this is `anthropic`, `openai-codex`,\n * `openrouter`, `cerebras`. Hosts that ship custom providers control the file\n * key via the descriptor.\n *\n * The legacy `cwd/.credentials.json` (written by `bun run auth` in older\n * versions) is migrated on first read if the new file doesn't exist yet. The\n * legacy file is left in place — we don't delete user data behind their back.\n */\n\nimport type { ProviderDescriptor } from './providers'\nimport { existsSync, readFileSync, realpathSync } from 'node:fs'\nimport { homedir } from 'node:os'\nimport { dirname, resolve } from 'node:path'\nimport { writeFileAtomic } from '../atomic-write'\nimport { withCredentialsLock } from '../providers/oauth'\nimport {\n ACCOUNT_CRED_KEY_INFIX,\n ALTERNATE_CRED_KEY_SUFFIX,\n alternateCredKey,\n CRED_KEY_SLOT_SEPARATOR,\n isAccountCredKey,\n isReservedCredKey,\n nextAccountCredKey,\n} from './cred-keys'\nimport { credKeyOf } from './providers'\n\nexport {\n ACCOUNT_CRED_KEY_INFIX,\n accountCredKey,\n ALTERNATE_CRED_KEY_SUFFIX,\n alternateCredKey,\n CRED_KEY_SLOT_SEPARATOR,\n DEFAULT_OAUTH_ACCOUNT_LABEL,\n isAccountCredKey,\n isReservedCredKey,\n oauthAccountLabel,\n} from './cred-keys'\n\n/** POSIX mode for the credentials file. Ignored on Windows. */\nconst FILE_MODE = 0o600\n\nexport interface ApiKeyCredential {\n kind: 'apikey'\n /**\n * Bearer API key. May be empty when the descriptor has no `envKey` and\n * authenticates entirely via {@link customFields} (e.g. the `local`\n * provider, where the auth signal is the configured base URL).\n */\n value: string\n /**\n * Free-form fields collected by the wizard from the descriptor's\n * `customFields` definitions. Keys match the descriptor field's `key`.\n * Mirrored into `process.env[field.envVar]` by `applyApiKeyEnv` so the\n * provider's factory can read them via the existing env-var convention.\n */\n customFields?: Record<string, string>\n}\nexport interface OAuthCredential {\n kind: 'oauth'\n access: string\n refresh?: string\n expires?: number\n /**\n * User-given account name (\"work\", \"personal\"). Optional — absent reads as\n * `DEFAULT_OAUTH_ACCOUNT_LABEL` (see `oauthAccountLabel`). Survives token\n * refresh because the refresh path spreads the stored entry into the rotated\n * one (see `resolveOAuthApiKey`).\n */\n label?: string\n /** Provider-specific extras (e.g. OpenAI Codex `accountId`). */\n [extra: string]: unknown\n}\nexport type ProviderCredential = ApiKeyCredential | OAuthCredential\n\n/** One stored credential of a provider, addressed by the file key it lives at. */\nexport interface StoredCredential {\n /** File key — the selection identity for promote / remove / relabel. */\n key: string\n credential: ProviderCredential\n /** True for the canonical slot, i.e. the credential readers resolve. */\n active: boolean\n}\n\n/** Top-level shape of `credentials.json` — keys are credential-file keys. */\nexport type CredentialsFile = Record<string, ProviderCredential>\n\n/**\n * Resolve the credentials file path given the resolved TUI data directory\n * (typically `~/.zidane`, i.e. `config.paths.dir`).\n *\n * Matches the convention used elsewhere in the TUI (sessions.db, state.json)\n * so a single `ZIDANE_STORAGE_DIR` override moves the entire data root.\n */\nexport function credentialsPath(dataDir: string): string {\n return resolve(dataDir, 'credentials.json')\n}\n\n/**\n * Read credentials from disk.\n *\n * Returns `{}` when the file is missing or corrupt (last-ditch tolerance —\n * a hand-edit gone wrong shouldn't lock the user out of re-authing). On first\n * call with no file present, attempts a migration from `cwd/.credentials.json`\n * (the legacy location used by `bun run auth`).\n */\nexport function readCredentials(dataDir: string): CredentialsFile {\n const path = credentialsPath(dataDir)\n\n if (!existsSync(path)) {\n const migrated = migrateLegacyFile(path)\n if (migrated)\n return migrated\n return {}\n }\n\n return readStoredCredentials(path)\n}\n\nfunction readStoredCredentials(path: string): CredentialsFile {\n try {\n const raw = readFileSync(path, 'utf-8')\n const parsed = JSON.parse(raw)\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed))\n return {}\n return normalizeCredentials(parsed as Record<string, unknown>)\n }\n catch {\n return {}\n }\n}\n\n/**\n * Re-tag legacy / refresh-stripped entries so the discriminated union holds.\n *\n * An untagged object with a string `access` is an OAuth credential — either the\n * pre-TUI `bun run auth` shape, or one whose `kind` was dropped when a token\n * refresh was persisted (a provider refresh returns no `kind`). Without\n * re-tagging, {@link detectAuth} and {@link applyApiKeyEnv} stop recognizing it\n * and force a spurious re-login. Mirrors the provider-side tolerance in\n * `readOAuthCredentials`. Entries with an explicit `kind` and anything we can't\n * classify pass through untouched.\n */\nfunction normalizeCredentials(parsed: Record<string, unknown>): CredentialsFile {\n const result: CredentialsFile = {}\n for (const [key, value] of Object.entries(parsed)) {\n // Only re-tag entries with no explicit `kind`; `isOAuthLegacy` (shared with\n // the legacy-file migration) is the \"object carrying an access token\" guard.\n const untagged = !!value && typeof value === 'object' && (value as Record<string, unknown>).kind === undefined\n if (untagged && isOAuthLegacy(value))\n result[key] = { ...value, kind: 'oauth' }\n else\n result[key] = value as ProviderCredential\n }\n return result\n}\n\n/**\n * Canonical file key for a descriptor, rejecting the reserved slot separator.\n *\n * Loud on a misconfigured provider instead of quietly mixing its credentials\n * into another provider's slot (extension registration refuses the separator\n * upfront; this covers descriptors passed in directly). Read paths stay\n * tolerant — only the functions that WRITE slots go through here.\n */\nfunction assertableCredKey(descriptor: ProviderDescriptor): string {\n const fileKey = credKeyOf(descriptor)\n if (isReservedCredKey(fileKey)) {\n throw new Error(\n `Provider \"${descriptor.key}\" uses credential key \"${fileKey}\", but `\n + `\"${CRED_KEY_SLOT_SEPARATOR}\" is reserved for a provider's extra credential slots `\n + `(\\`${ALTERNATE_CRED_KEY_SUFFIX}\\`, \\`${ACCOUNT_CRED_KEY_INFIX}N\\`).`,\n )\n }\n return fileKey\n}\n\n/** Read a single provider's credential (translating via the descriptor). */\nexport function readProviderCredential(dataDir: string, descriptor: ProviderDescriptor): ProviderCredential | undefined {\n return readCredentials(dataDir)[credKeyOf(descriptor)]\n}\n\n/**\n * Both stored credentials for a provider: `active` is the one every reader\n * resolves, `alternate` is the stored-but-not-preferred one (if any).\n *\n * Predates multi-account and only sees the `#alt` slot; use\n * {@link readProviderCredentials} to list every stored account.\n */\nexport function readProviderCredentialPair(\n dataDir: string,\n descriptor: ProviderDescriptor,\n): { active?: ProviderCredential, alternate?: ProviderCredential } {\n const all = readCredentials(dataDir)\n const fileKey = credKeyOf(descriptor)\n return { active: all[fileKey], alternate: all[alternateCredKey(fileKey)] }\n}\n\n/**\n * Every file key holding one of `fileKey`'s credentials, in display order:\n * canonical, `#alt`, then `#account:N` ascending. Only keys present in `all`.\n */\nfunction slotKeysOf(all: Record<string, unknown>, fileKey: string): string[] {\n const altKey = alternateCredKey(fileKey)\n const accounts = Object.keys(all)\n .filter(key => isAccountCredKey(fileKey, key))\n .sort((a, b) => Number(a.slice(a.lastIndexOf(':') + 1)) - Number(b.slice(b.lastIndexOf(':') + 1)))\n return [\n ...(fileKey in all ? [fileKey] : []),\n ...(altKey in all ? [altKey] : []),\n ...accounts,\n ]\n}\n\nfunction isSlotOf(fileKey: string, key: string): boolean {\n return key === fileKey || key === alternateCredKey(fileKey) || isAccountCredKey(fileKey, key)\n}\n\n/**\n * Every stored credential of a provider, active first.\n *\n * Tolerant of any kind in any slot: `#alt` is meant for the API key and\n * `#account:N` for OAuth accounts, but a file written by an older build (which\n * parked the OAuth alternate in `#alt`) must still list everything it holds.\n */\nexport function readProviderCredentials(dataDir: string, descriptor: ProviderDescriptor): StoredCredential[] {\n return storedCredentialsOf(readCredentials(dataDir), credKeyOf(descriptor))\n}\n\n/** Pure form of {@link readProviderCredentials} over an already-read file. */\nexport function storedCredentialsOf(all: CredentialsFile, fileKey: string): StoredCredential[] {\n return slotKeysOf(all, fileKey).map(key => ({ key, credential: all[key], active: key === fileKey }))\n}\n\n/**\n * Write credentials atomically (write-then-rename) with mode 0o600.\n *\n * Atomic on the same filesystem — readers either see the previous file or the\n * new one, never a half-written intermediate. Creates the parent dir if needed\n * (first launch on a fresh machine: `~/.zidane/` may not exist yet).\n */\nexport function writeCredentials(dataDir: string, creds: CredentialsFile): void {\n withCredentialsLock(credentialsPath(dataDir), () => writeCredentialsUnlocked(dataDir, creds))\n}\n\nfunction writeCredentialsUnlocked(dataDir: string, creds: CredentialsFile): void {\n writeFileAtomic(\n credentialsPath(dataDir),\n `${JSON.stringify(creds, null, 2)}\\n`,\n { ensureDir: true, mode: FILE_MODE },\n )\n}\n\n/**\n * Run `fn` against the on-disk file under the mutation lock and persist what it\n * returns. Migrates the legacy file first (migration takes the same lock).\n */\nfunction mutateCredentials<T>(\n dataDir: string,\n fn: (all: CredentialsFile) => { result: T, write: boolean },\n): T {\n readCredentials(dataDir)\n return withCredentialsLock(credentialsPath(dataDir), () => {\n const all = readStoredCredentials(credentialsPath(dataDir))\n const { result, write } = fn(all)\n if (write)\n writeCredentialsUnlocked(dataDir, all)\n return result\n })\n}\n\n/**\n * Move an OAuth grant out of `#alt` into an account slot.\n *\n * Before multi-account, `#alt` held whichever kind was not active — so a file\n * from that era can have an OAuth grant there. `#alt` is now the API key's\n * slot, and every writer below relies on that; relocating first means the\n * grant is preserved instead of overwritten when an API key gets demoted.\n */\nfunction relocateLegacyOAuthAlternate(all: CredentialsFile, fileKey: string): void {\n const altKey = alternateCredKey(fileKey)\n const alt = all[altKey]\n if (alt?.kind !== 'oauth')\n return\n delete all[altKey]\n all[nextAccountCredKey(fileKey, Object.keys(all))] = alt\n}\n\n/**\n * Park a credential that just lost the canonical slot.\n *\n * API keys go to `#alt` (there is at most one, so the slot is free once\n * `relocateLegacyOAuthAlternate` has run). OAuth grants reuse the slot the\n * promoted credential vacated when that was an account slot — a swap, no new\n * numbering — and otherwise take the next free `#account:N`.\n */\nfunction parkDemoted(all: CredentialsFile, fileKey: string, cred: ProviderCredential, vacated?: string): void {\n if (cred.kind === 'apikey') {\n all[alternateCredKey(fileKey)] = cred\n return\n }\n if (vacated && isAccountCredKey(fileKey, vacated)) {\n all[vacated] = cred\n return\n }\n all[nextAccountCredKey(fileKey, Object.keys(all))] = cred\n}\n\nexport interface SetProviderCredentialOptions {\n /**\n * File key of the stored credential this one REPLACES (a re-auth of a known\n * account). The slot keeps its position — replacing the active one keeps it\n * active. When the key is no longer stored (removed from another window\n * mid-login), the credential is added as a new active account instead.\n */\n replaceKey?: string\n}\n\n/**\n * Store a credential as the provider's preferred one.\n *\n * The previous active credential is demoted, not destroyed: authenticating\n * with OAuth used to silently delete a working API key (and vice versa), and a\n * second OAuth login used to overwrite the first account. An API key moves to\n * {@link alternateCredKey}; an OAuth grant moves to the next free account slot.\n * Replacing an API key with an API key is a plain overwrite — there is only\n * ever one — and so is an OAuth login that names its predecessor via\n * `replaceKey`.\n */\nexport function setProviderCredential(\n dataDir: string,\n descriptor: ProviderDescriptor,\n cred: ProviderCredential,\n opts: SetProviderCredentialOptions = {},\n): void {\n mutateCredentials(dataDir, (all) => {\n const fileKey = assertableCredKey(descriptor)\n relocateLegacyOAuthAlternate(all, fileKey)\n\n if (opts.replaceKey !== undefined) {\n if (!isSlotOf(fileKey, opts.replaceKey))\n throw new Error(`\"${opts.replaceKey}\" is not a credential slot of provider \"${descriptor.key}\"`)\n if (opts.replaceKey in all) {\n all[opts.replaceKey] = cred\n return { result: undefined, write: true }\n }\n }\n\n const previous = all[fileKey]\n if (previous && !(previous.kind === 'apikey' && cred.kind === 'apikey'))\n parkDemoted(all, fileKey, previous)\n // A stale second API key in `#alt` (older builds could leave one) would\n // surface as a duplicate row that \"switches\" to itself.\n else if (cred.kind === 'apikey')\n delete all[alternateCredKey(fileKey)]\n all[fileKey] = cred\n return { result: undefined, write: true }\n })\n}\n\n/**\n * Outcome of {@link setPreferredCredential}.\n *\n * Three states rather than a boolean because the two non-promoting cases need\n * opposite handling: `already-active` is a harmless idempotent call (the UI\n * repaints and moves on), while `not-stored` means the caller asked for a\n * credential that does not exist and should say so.\n */\nexport type PreferCredentialResult = 'promoted' | 'already-active' | 'not-stored'\n\n/**\n * What {@link setPreferredCredential} promotes: a stored credential by file\n * key (from {@link readProviderCredentials} / `AuthMethod.key`), or the first\n * stored credential of a kind — the pre-multi-account form, still right when\n * the caller only knows \"switch to the API key\".\n */\nexport type PreferredCredentialTarget = ProviderCredential['kind'] | { key: string }\n\n/**\n * Make one of the provider's stored credentials the preferred one.\n *\n * Swaps the target into the canonical slot and parks the previous active one\n * (see {@link parkDemoted}). Returns which of the three outcomes happened so a\n * caller cannot report a swap that provably did not occur — the write is\n * silent either way, and a success-shaped `void` was a trap for any caller that\n * trusted it instead of refetching.\n */\nexport function setPreferredCredential(\n dataDir: string,\n descriptor: ProviderDescriptor,\n target: PreferredCredentialTarget,\n): PreferCredentialResult {\n return mutateCredentials(dataDir, (all) => {\n const fileKey = assertableCredKey(descriptor)\n relocateLegacyOAuthAlternate(all, fileKey)\n const active = all[fileKey]\n\n let key: string | undefined\n if (typeof target === 'string') {\n if (active?.kind === target)\n return { result: 'already-active', write: false }\n key = slotKeysOf(all, fileKey).find(k => k !== fileKey && all[k]?.kind === target)\n }\n else {\n if (target.key === fileKey && active)\n return { result: 'already-active', write: false }\n key = isSlotOf(fileKey, target.key) && target.key in all ? target.key : undefined\n }\n if (key === undefined)\n return { result: 'not-stored', write: false }\n\n const promoted = all[key]\n delete all[key]\n all[fileKey] = promoted\n if (active)\n parkDemoted(all, fileKey, active, key)\n return { result: 'promoted', write: true }\n })\n}\n\n/**\n * Drop ONE stored credential. Removing the active one promotes a survivor —\n * another credential of the same kind when there is one (a second OAuth\n * account), else whatever is left — so the provider does not go dark while it\n * still holds a working credential. Returns false when `key` was not stored.\n */\nexport function removeStoredCredential(dataDir: string, descriptor: ProviderDescriptor, key: string): boolean {\n return mutateCredentials(dataDir, (all) => {\n const fileKey = credKeyOf(descriptor)\n if (!isSlotOf(fileKey, key) || !(key in all))\n return { result: false, write: false }\n const removed = all[key]\n delete all[key]\n if (key === fileKey) {\n const rest = slotKeysOf(all, fileKey)\n const successor = rest.find(k => all[k]?.kind === removed.kind) ?? rest[0]\n if (successor !== undefined) {\n all[fileKey] = all[successor]\n delete all[successor]\n }\n }\n return { result: true, write: true }\n })\n}\n\n/**\n * Name a stored OAuth account. An empty label drops the field, which reads\n * back as {@link DEFAULT_OAUTH_ACCOUNT_LABEL}. Returns false when `key` holds\n * no OAuth credential.\n */\nexport function setCredentialLabel(dataDir: string, descriptor: ProviderDescriptor, key: string, label: string): boolean {\n return mutateCredentials(dataDir, (all) => {\n const fileKey = credKeyOf(descriptor)\n const entry = isSlotOf(fileKey, key) ? all[key] : undefined\n if (entry?.kind !== 'oauth')\n return { result: false, write: false }\n const trimmed = label.trim()\n if (trimmed)\n entry.label = trimmed\n else\n delete entry.label\n return { result: true, write: true }\n })\n}\n\n/**\n * Sign a provider out — drops EVERY stored credential.\n *\n * \"Signed out\" has to mean the provider resolves nothing afterwards, which is\n * what this has always guaranteed and what callers named it for; promoting a\n * survivor instead would silently leave the provider authenticated through\n * another source. Callers that want that behavior use\n * {@link removeStoredCredential} on the one slot they mean.\n */\nexport function removeProviderCredential(dataDir: string, descriptor: ProviderDescriptor): void {\n mutateCredentials(dataDir, (all) => {\n const keys = slotKeysOf(all, credKeyOf(descriptor))\n for (const key of keys)\n delete all[key]\n return { result: undefined, write: keys.length > 0 }\n })\n}\n\n/**\n * Reconcile `process.env` with the stored credentials so the harness providers\n * pick up the wizard's output via their existing env-var resolution. Called\n * once at TUI launch after the credentials file has been resolved.\n *\n * **Precedence: stored credential > ambient env.** When the user has run the\n * auth wizard, that's an explicit, recent action — it MUST win over whatever\n * ambient value is already in `process.env`. Otherwise an upgrade reliably\n * breaks for anyone whose cwd happens to contain a stale `.env`: Bun\n * auto-loads `.env` from cwd into `process.env` before our code runs, the\n * legacy `bun run auth` used to upsert tokens into `cwd/.env`, and even a\n * one-off `export ANTHROPIC_API_KEY=…` from months ago in the user's shell rc\n * can outlive its usefulness. None of those should override a key the user\n * just typed into the wizard.\n *\n * Three cases per registered provider with an `envKey`:\n *\n * 1. Stored `kind: 'apikey'` → overwrite env with the stored value. Wizard\n * wins. A debug log fires when this clobbers a different ambient value.\n * 2. Stored `kind: 'oauth'` → DELETE env, so {@link resolveOAuthApiKey}\n * falls through to the file-read + refresh path. A stale OAuth token\n * in env would otherwise short-circuit refresh and get rejected.\n * 3. No stored entry → leave env alone. Env-only setups (no wizard run yet,\n * hosts driving zidane purely from secrets management) keep working.\n *\n * To opt out and force ambient env to win again, the user signs the provider\n * out via the wizard (which deletes the stored entry → case 3 above).\n *\n * Descriptors without an `envKey` (OAuth-only providers, custom providers\n * that bypass env-var resolution) are skipped silently.\n */\nexport function applyApiKeyEnv(\n dataDir: string,\n registry: Readonly<Record<string, ProviderDescriptor>>,\n opts: { log?: (message: string) => void } = {},\n): void {\n // Diagnostics default to stderr, which is correct for the ONE launch-time\n // call this was written for — it runs before the renderer owns the terminal.\n // Callers that run while a full-screen TUI is up (a credential swap from the\n // settings modal) must pass a sink instead: a raw stderr write mid-render\n // paints straight over the frame.\n const log = opts.log ?? (message => void process.stderr.write(message))\n const creds = readCredentials(dataDir)\n for (const descriptor of Object.values(registry)) {\n const cred = creds[credKeyOf(descriptor)]\n\n // Mirror customFields → env regardless of whether the descriptor has an\n // envKey. Same \"stored beats ambient\" semantics as the API-key path.\n if (cred?.kind === 'apikey' && descriptor.customFields) {\n const stored = cred.customFields ?? {}\n for (const field of descriptor.customFields) {\n const value = stored[field.key]\n if (typeof value === 'string' && value.length > 0) {\n const ambient = process.env[field.envVar]\n if (ambient && ambient !== value && process.env.ZIDANE_DEBUG) {\n log(\n `[zidane/chat] applyApiKeyEnv: overriding ambient \\`${field.envVar}\\` `\n + `with stored value from credentials.json (provider=${descriptor.key}, field=${field.key}).\\n`,\n )\n }\n process.env[field.envVar] = value\n }\n }\n }\n\n if (!descriptor.envKey)\n continue\n const envKey = descriptor.envKey\n const ambient = process.env[envKey]\n\n if (cred?.kind === 'apikey' && cred.value) {\n if (ambient && ambient !== cred.value && process.env.ZIDANE_DEBUG) {\n log(\n `[zidane/chat] applyApiKeyEnv: overriding ambient \\`${envKey}\\` `\n + `with stored API key from credentials.json (provider=${descriptor.key}). `\n + `Sign out via the auth wizard if the ambient value was intended to win.\\n`,\n )\n }\n process.env[envKey] = cred.value\n continue\n }\n\n if (cred?.kind === 'oauth' && cred.access) {\n if (ambient !== undefined && process.env.ZIDANE_DEBUG) {\n log(\n `[zidane/chat] applyApiKeyEnv: clearing ambient \\`${envKey}\\` because `\n + `credentials.json has stored OAuth for ${descriptor.key} — refresh path needs the file.\\n`,\n )\n }\n delete process.env[envKey]\n continue\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Legacy migration\n// ---------------------------------------------------------------------------\n\n/**\n * `bun run auth` (pre-TUI) wrote `cwd/.credentials.json` with an entry per\n * provider mapping directly to an OAuthCredentials payload, e.g.:\n *\n * {\n * \"anthropic\": { \"access\": \"...\", \"refresh\": \"...\", \"expires\": 123 },\n * \"openai-codex\": { \"access\": \"...\", \"refresh\": \"...\", \"expires\": 123, \"accountId\": \"...\" }\n * }\n *\n * We don't delete the legacy file — it might still be used by a host that\n * imports the harness directly. We just copy its contents into the new\n * location under the kind-tagged shape so the TUI picks them up.\n *\n * Migration is provider-agnostic: any top-level entry with an `access` field\n * is preserved verbatim (extras included), under the same key. The TUI's\n * detection then looks them up via the matching descriptor's `credentialFileKey`.\n *\n * Returns the migrated credentials when the migration ran, or `null` when\n * there's no legacy file to migrate.\n */\nfunction migrateLegacyFile(targetPath: string): CredentialsFile | null {\n const legacyPath = resolve(process.cwd(), '.credentials.json')\n // Only trust a legacy file that sits directly in the user's home dir (where\n // `bun run auth` was typically run) or next to the migration target. A\n // cwd-relative read from an arbitrary checkout would let an untrusted repo\n // plant a `.credentials.json` that gets persisted as the USER-LEVEL\n // credential file. Compared via realpath so symlinked dirs (e.g. macOS\n // `/var` → `/private/var`) don't defeat the check.\n const real = (p: string): string => {\n try {\n return realpathSync(p)\n }\n catch {\n return p\n }\n }\n const legacyDir = real(dirname(legacyPath))\n if (legacyDir !== real(homedir()) && legacyDir !== real(dirname(resolve(targetPath))))\n return null\n if (!existsSync(legacyPath))\n return null\n\n let legacy: Record<string, unknown>\n try {\n legacy = JSON.parse(readFileSync(legacyPath, 'utf-8'))\n }\n catch {\n return null\n }\n if (!legacy || typeof legacy !== 'object' || Array.isArray(legacy))\n return null\n\n const migrated: CredentialsFile = {}\n for (const [fileKey, value] of Object.entries(legacy)) {\n if (!isOAuthLegacy(value))\n continue\n const { access, refresh, expires, ...extras } = value\n migrated[fileKey] = {\n kind: 'oauth',\n access,\n ...(typeof refresh === 'string' ? { refresh } : {}),\n ...(typeof expires === 'number' ? { expires } : {}),\n ...extras,\n }\n }\n\n if (Object.keys(migrated).length === 0)\n return null\n\n return withCredentialsLock(targetPath, () => {\n if (existsSync(targetPath))\n return readStoredCredentials(targetPath)\n writeFileAtomic(targetPath, `${JSON.stringify(migrated, null, 2)}\\n`, { ensureDir: true, mode: FILE_MODE })\n return migrated\n })\n}\n\nfunction isOAuthLegacy(value: unknown): value is { access: string, refresh?: string, expires?: number, [extra: string]: unknown } {\n return (\n typeof value === 'object'\n && value !== null\n && 'access' in value\n && typeof (value as { access: unknown }).access === 'string'\n )\n}\n","import type { ProviderCredential } from './credentials'\nimport type { ProviderDescriptor } from './providers'\nimport { oauthAccountLabel, readCredentials, storedCredentialsOf } from './credentials'\nimport { credKeyOf } from './providers'\n\n/**\n * Provider identifier as known to the TUI. Built-in keys are `anthropic`,\n * `openai`, `openrouter`, `cerebras`, but hosts can register additional\n * keys via {@link ProviderDescriptor} — so the type is open.\n */\nexport type ProviderKey = string\n\nexport interface AuthMethod {\n source: 'env' | 'oauth' | 'apikey'\n /** Human-readable detail (env var name, expiry timestamp, …). */\n detail: string\n /**\n * True for the credential every reader actually resolves.\n *\n * Exactly one STORED credential is active: the one in the canonical file key\n * (see `alternateCredKey`). A provider can hold an API key and any number of\n * OAuth accounts, so the list alone no longer says which one wins. `env` is\n * never marked active — it is an observable signal, not a stored credential\n * the user can promote.\n *\n * Optional so hosts that build `ProviderAuth` themselves (this type is public\n * via `zidane/chat`) keep compiling; absent reads as \"not active\".\n * {@link detectAuth} always sets it.\n */\n active?: boolean\n /**\n * Credentials-file key of a STORED credential — the identity to hand to\n * `setPreferredCredential({ key })` / `removeStoredCredential`. Several OAuth\n * accounts share `source: 'oauth'`, so `source` alone no longer selects one.\n * Absent for `env`.\n */\n key?: string\n /**\n * Account name — always set for stored OAuth credentials: the user-given one,\n * or `DEFAULT_OAUTH_ACCOUNT_LABEL` when the grant was never named.\n */\n label?: string\n}\n\nexport interface ProviderAuth {\n key: ProviderKey\n label: string\n /** True when at least one credential source is present. */\n available: boolean\n methods: AuthMethod[]\n /**\n * Env var this provider reads, when it declares one.\n *\n * Carried separately from {@link methods} because the settings list shows the\n * env slot even when it holds nothing. `methods` only ever lists sources that\n * are actually present, since `available` is derived from it.\n */\n envKey?: string\n}\n\n/**\n * Detect available auth for every registered provider.\n *\n * A method appears in `methods` for every layer that has a credential:\n *\n * - `kind: 'apikey'` from `credentials.json` (injected into env at TUI launch)\n * - explicit env var (descriptor's `envKey`)\n * - `kind: 'oauth'` from `credentials.json` (or legacy `cwd/.credentials.json`),\n * one per stored account\n *\n * The preferred credential lives in the canonical file key and is flagged\n * {@link AuthMethod.active}; the others sit in sibling slots and are reported\n * too, keyed by {@link AuthMethod.key}, so the UI can offer to promote or\n * remove them. Order within `methods` is presentational — active first — not a\n * resolution order, because the active flag answers that directly.\n *\n * Pure read — never refreshes or rewrites the credentials file.\n */\nexport function detectAuth(\n dataDir: string,\n registry: Readonly<Record<string, ProviderDescriptor>>,\n env: Record<string, string | undefined> = process.env,\n): ProviderAuth[] {\n const creds = readCredentials(dataDir)\n\n return Object.values(registry).map((descriptor) => {\n const methods: AuthMethod[] = []\n const stored = storedCredentialsOf(creds, credKeyOf(descriptor))\n const fileEntry = stored.find(s => s.active)?.credential\n\n // 1. Stored API key / OAuth accounts, active first. Every slot goes through\n // the same description so a parked credential renders identically to the\n // preferred one, differing only in `active`.\n const describeStored = (key: string, entry: ProviderCredential, active: boolean): AuthMethod | undefined => {\n if (entry.kind === 'apikey' && entry.value)\n return { source: 'apikey', detail: 'credentials.json', active, key }\n if (entry.kind === 'oauth' && entry.access) {\n const label = oauthAccountLabel(entry)\n const expiry = typeof entry.expires === 'number'\n ? `expires ${new Date(entry.expires).toLocaleString()}`\n : 'credentials.json'\n return { source: 'oauth', detail: `${label} · ${expiry}`, active, key, label }\n }\n return undefined\n }\n\n for (const slot of stored) {\n const method = describeStored(slot.key, slot.credential, slot.active)\n if (method)\n methods.push(method)\n }\n\n // 1b. customFields-only credential (descriptor has no `envKey` and is\n // authenticated entirely by configuration — e.g. the `local` provider,\n // where the auth signal is the configured base URL). The credential\n // is \"present\" when every required field has a stored value.\n if (\n fileEntry?.kind === 'apikey'\n && !fileEntry.value\n && descriptor.customFields\n && descriptor.customFields.length > 0\n ) {\n const stored = fileEntry.customFields ?? {}\n const required = descriptor.customFields.filter(f => f.required)\n const allRequiredPresent = required.every(\n f => typeof stored[f.key] === 'string' && (stored[f.key] as string).length > 0,\n )\n if (allRequiredPresent) {\n methods.push({ source: 'apikey', detail: 'credentials.json (configured)', active: true })\n }\n }\n\n // 1c. Env-only custom field configuration. This lets local/OpenAI-compatible\n // endpoints participate in auth/model detection when configured through\n // `LOCAL_LLM_BASE_URL` without first writing credentials.json.\n if (descriptor.customFields && descriptor.customFields.length > 0) {\n const required = descriptor.customFields.filter(f => f.required)\n const allRequiredPresent = required.length > 0 && required.every(\n f => typeof env[f.envVar] === 'string' && (env[f.envVar] as string).length > 0,\n )\n\n if (allRequiredPresent) {\n methods.push({ source: 'env', detail: required.map(f => f.envVar).join(', '), active: false })\n }\n }\n\n // 2. Env var (may be set by `applyApiKeyEnv` from a stored key, or by the\n // user's shell. Either way, treated as the same observable signal.)\n if (descriptor.envKey && env[descriptor.envKey])\n methods.push({ source: 'env', detail: descriptor.envKey, active: false })\n\n // Selection identity is the file key for stored credentials and `source`\n // for everything else. Two API-key rows cannot be a choice the user can\n // express (there is one API key per provider) — a customFields-configured\n // credential sitting beside a stored one, or a stale duplicate from an\n // older build, collapses to the FIRST, which the order above makes the\n // active one. OAuth accounts are distinct by key and all survive.\n const unique: AuthMethod[] = []\n for (const method of methods) {\n const duplicate = unique.some(seen =>\n method.source === 'oauth'\n ? seen.key !== undefined && seen.key === method.key\n : seen.source === method.source,\n )\n if (!duplicate)\n unique.push(method)\n }\n\n return {\n key: descriptor.key,\n label: descriptor.label,\n available: unique.length > 0,\n methods: unique,\n ...(descriptor.envKey ? { envKey: descriptor.envKey } : {}),\n }\n })\n}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"completion-core-BRo86R_L.js","names":[],"sources":["../src/chat/completion-core.ts"],"sourcesContent":["/**\n * Prompt autocompletion framework — pure core.\n *\n * Provider contract types + the renderer-agnostic span/reference helpers\n * (`findActiveTrigger`, `applyInsert`, `mergeReferences`, `collectReferences`).\n * No React, no node — so a browser-context renderer imports these via\n * `zidane/chat/pure`. The `useCompletion` React hook lives in `./completion`\n * (main barrel) and re-exports everything here for back-compat.\n *\n * Providers plug in by registering a `trigger` character (e.g. `/` for skills,\n * `@` for files) and exposing two operations:\n *\n * 1. `suggest(query)` — return ranked items for the live query.\n * 2. `parseReferences(text)` — find all references to the provider's\n * items in arbitrary text. Used to highlight in-prompt mentions and\n * drive submit-time side effects (activate the skill, attach the\n * file, …).\n */\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/**\n * A discoverable, selectable thing — one row in the autocomplete popover.\n * `TItem` is provider-specific; consumers can inspect `data` when they\n * need the originating payload (e.g. the full `SkillConfig` for tooltips).\n */\nexport interface CompletionItem<TItem = unknown> {\n /** Stable identifier within the provider. Used as React key + selection equality. */\n id: string\n /** User-visible primary string (\"research\"). */\n label: string\n /** Optional one-line secondary string (\"In-depth research with citations\"). */\n description?: string\n /**\n * Text that replaces the active span (trigger + query) on commit. Usually\n * `${trigger}${label}` with a trailing space so the cursor lands ready\n * for the next token.\n */\n insertText: string\n /**\n * Optional section header for popovers that host several providers on one\n * trigger (e.g. `/` → Commands + Skills). When set, the renderer groups\n * items under this label; when absent, items render flat. The engine\n * defaults it to the producing provider's `label` in {@link mergeSuggestions}.\n */\n section?: string\n /**\n * Id of the producing provider — stamped by {@link mergeSuggestions} so\n * renderers can apply provider-specific styling (e.g. chip-colored\n * titles) in a multi-provider popover. Items constructed outside the\n * merge path may leave it unset.\n */\n providerId?: string\n /** Original provider-specific payload. */\n data: TItem\n}\n\n/**\n * A reference to a provider item inside arbitrary prompt text. Producers:\n * `provider.parseReferences(text)`. Consumers: the TUI for highlighting,\n * the run flow for \"activate every referenced skill before agent.run()\".\n *\n * Spans are half-open `[start, end)` codepoint offsets into the source\n * string. Overlapping spans from the same or different providers are\n * caller-resolved — the helpers below ship a \"first wins\" merger.\n */\nexport interface CompletionReference<TItem = unknown> {\n providerId: string\n start: number\n end: number\n itemId: string\n data: TItem\n metadata?: Record<string, unknown>\n}\n\nexport interface CompletionSubmitContext<TItem = unknown> extends CompletionContext {\n references: readonly CompletionReference<TItem>[]\n}\n\n/**\n * Provider contract. Implementations decide their own ranking, fuzzy-match\n * rules, async loading behavior, and reference grammar.\n */\nexport interface CompletionProvider<TItem = unknown> {\n /** Stable id used for tagging references + React keys. */\n id: string\n /**\n * Single character that activates this provider. The engine considers a\n * trigger \"active\" when it appears at the start of the buffer or\n * immediately after whitespace, and is not closed by whitespace before\n * the cursor (so a trigger plus query is a contiguous token).\n */\n trigger: string\n /** Human-readable name. Reserved for future multi-provider popover headers. */\n label: string\n /**\n * Returns items for the active `query` (the text between the trigger and\n * the cursor, excluding the trigger itself). Synchronous return is the\n * common case; promises let providers paginate or hit a backend.\n *\n * `signal` is aborted when the query changes or the popover closes —\n * use it to cancel in-flight network calls.\n */\n suggest: (\n query: string,\n ctx: CompletionContext,\n signal: AbortSignal,\n ) => CompletionItem<TItem>[] | Promise<CompletionItem<TItem>[]>\n /**\n * Keep the trigger active across spaces (single line only — a newline\n * still closes it). Default false: a space ends the query token, which is\n * right for file/skill/chat mentions. The commands provider opts in so\n * `/deploy stag▌` can complete ARGUMENTS: its query becomes\n * `deploy stag` and it decides internally what to suggest. When the\n * query contains whitespace, only opted-in providers are consulted.\n */\n allowSpacesInQuery?: boolean\n /**\n * Find every reference to this provider's items in `text`. Pure: must not\n * mutate ctx. The TUI calls this on every keystroke for highlighting, so\n * keep it cheap (linear scan, no I/O).\n */\n parseReferences: (\n text: string,\n ctx: CompletionContext,\n ) => CompletionReference<TItem>[]\n /**\n * Optional submit-time resolver. Unlike `parseReferences`, this may do async\n * work and enrich references before the host turns them into attachments,\n * activated skills, or other side effects.\n *\n * Declared with method syntax (not a property arrow) on purpose: it makes the\n * `TItem`-typed params bivariant, so a heterogeneous `CompletionProvider`\n * collection (skills + files + extensions) stays assignable to\n * `CompletionProvider<unknown>[]`.\n */\n // eslint-disable-next-line ts/method-signature-style\n resolveReferences?(\n references: readonly CompletionReference<TItem>[],\n ctx: CompletionSubmitContext<TItem>,\n signal: AbortSignal,\n ): CompletionReference<TItem>[] | Promise<CompletionReference<TItem>[]>\n /** Optional submit-time side effect hook. Runs after references are resolved. */\n // eslint-disable-next-line ts/method-signature-style\n onSubmit?(\n submission: CompletionSubmitContext<TItem>,\n signal: AbortSignal,\n ): void | Promise<void>\n}\n\n/**\n * Read-only view of the active prompt buffer + cursor passed to provider\n * callbacks. Kept minimal so providers stay portable across renderers.\n */\nexport interface CompletionContext {\n /** Full prompt text. */\n text: string\n /** Codepoint offset of the cursor (0-based). */\n cursor: number\n}\n\n/**\n * Identified active trigger span. Returned by `findActiveTrigger` so\n * callers can show the popover, query the provider, and on commit replace\n * the span with the selected item's `insertText`.\n */\nexport interface ActiveTrigger<TItem = unknown> {\n provider: CompletionProvider<TItem>\n /** Substring after the trigger, up to the cursor. Empty if cursor sits right after the trigger. */\n query: string\n /** `[start, end)` — span covered by the trigger + query in the source. */\n span: { start: number, end: number }\n}\n\n// ---------------------------------------------------------------------------\n// Pure helpers — exported for unit tests + alternate renderers\n// ---------------------------------------------------------------------------\n\n/**\n * Resolve the provider trigger active at `cursor`, or `null` when none fits.\n *\n * Rules:\n * - The trigger character must sit at position 0 of the buffer OR be\n * preceded by whitespace. This prevents `http://` from triggering the\n * `/`-bound skills provider mid-URL.\n * - The cursor must be at or past the trigger position.\n * - Nothing between the trigger and the cursor may be whitespace (the\n * query is one contiguous token).\n * - The query length is bounded — `maxQueryLength` defaults to 64 — so\n * a runaway buffer scan can't pin the renderer.\n */\nexport function findActiveTrigger<TItem>(\n text: string,\n cursor: number,\n providers: readonly CompletionProvider<TItem>[],\n options: { maxQueryLength?: number } = {},\n): ActiveTrigger<TItem> | null {\n const multi = findActiveTriggers(text, cursor, providers, options)\n if (!multi)\n return null\n // Back-compat: the first matching provider is exactly what the old\n // `providers.find(...)` returned (filter preserves order).\n return { provider: multi.providers[0], query: multi.query, span: multi.span }\n}\n\n/** Like {@link ActiveTrigger} but carries EVERY provider bound to the active trigger char. */\nexport interface ActiveTriggers<TItem = unknown> {\n /** All providers whose `trigger` matches the active char, in registration order. */\n providers: readonly CompletionProvider<TItem>[]\n query: string\n span: { start: number, end: number }\n}\n\n/**\n * Multi-provider variant of {@link findActiveTrigger}: resolves ALL providers\n * bound to the trigger char active at `cursor` (e.g. `/` → a commands provider\n * AND the skills provider), sharing one query + span. The same trigger rules\n * apply (start-of-buffer or whitespace-preceded, contiguous query, bounded\n * length). Returns `null` when no trigger fits.\n */\nexport function findActiveTriggers<TItem>(\n text: string,\n cursor: number,\n providers: readonly CompletionProvider<TItem>[],\n options: { maxQueryLength?: number } = {},\n): ActiveTriggers<TItem> | null {\n if (providers.length === 0)\n return null\n const max = options.maxQueryLength ?? 64\n const safeCursor = Math.max(0, Math.min(cursor, text.length))\n // Whitespace test mirrors regex `\\s` so this surface agrees with the\n // built-in providers' `parseReferences` regex (which uses `\\s`). Without\n // this, pasting `text\\u00a0@README.md` would parse as a ref but the popover\n // wouldn't reopen on retype because NBSP wasn't recognized as a boundary.\n const isWhitespace = (ch: string | undefined) => ch === undefined ? false : /\\s/.test(ch)\n // Walk backward from the cursor to the nearest trigger char. Whitespace\n // normally closes the query token and ends the walk — unless a provider\n // opted into `allowSpacesInQuery` (argument completion), in which case\n // the walk continues across spaces (never newlines) and, once a trigger\n // is found, only space-tolerant providers are consulted for a spaced\n // query. Non-opted-in providers keep byte-identical behavior.\n const anyAllowsSpaces = providers.some(p => p.allowSpacesInQuery === true)\n let sawSpace = false\n for (let i = safeCursor - 1; i >= 0 && safeCursor - i <= max + 1; i--) {\n const ch = text[i]\n if (isWhitespace(ch)) {\n if (ch === '\\n' || ch === '\\r' || !anyAllowsSpaces)\n return null\n sawSpace = true\n continue\n }\n const matching = providers.filter(p => p.trigger === ch && (!sawSpace || p.allowSpacesInQuery === true))\n if (matching.length === 0)\n continue\n // Trigger must be at start of buffer or follow whitespace.\n const before = i > 0 ? text[i - 1] : ''\n if (before !== '' && !isWhitespace(before))\n continue\n const query = text.slice(i + 1, safeCursor)\n return {\n providers: matching,\n query,\n span: { start: i, end: safeCursor },\n }\n }\n return null\n}\n\n/**\n * Flatten several providers' suggestion lists into one, stamping\n * `providerId` on every item (for provider-aware row styling) and\n * defaulting `section` to the producing provider's `label` so the popover\n * can render group headers.\n *\n * `section` is stamped only when the TRIGGER hosts ≥2 providers (see\n * `groupCount`), not when ≥2 happen to return items — so a query that\n * filters a shared trigger (`/`) down to one group still shows that\n * group's header, while a genuinely single-provider trigger (`@` files)\n * renders flat (the header would just duplicate the popup's border\n * title). Items that already carry a value keep it.\n */\nexport function mergeSuggestions<TItem>(\n groups: readonly { provider: CompletionProvider<TItem>, items: readonly CompletionItem<TItem>[] }[],\n groupCount = groups.length,\n): CompletionItem<TItem>[] {\n const sectioned = groupCount > 1\n const out: CompletionItem<TItem>[] = []\n for (const { provider, items } of groups) {\n for (const item of items) {\n const section = sectioned ? (item.section ?? provider.label) : item.section\n const providerId = item.providerId ?? provider.id\n out.push(item.section === section && item.providerId === providerId ? item : { ...item, section, providerId })\n }\n }\n return out\n}\n\n/**\n * Replace `[span.start, span.end)` in `text` with `insertText`. Returns the\n * mutated text and the new cursor position (end of insertion).\n */\nexport function applyInsert(\n text: string,\n span: { start: number, end: number },\n insertText: string,\n): { text: string, cursor: number } {\n const next = text.slice(0, span.start) + insertText + text.slice(span.end)\n return { text: next, cursor: span.start + insertText.length }\n}\n\n/**\n * Merge reference lists from multiple providers into one ordered list with\n * earlier-start-wins disambiguation when spans overlap. Ties broken by\n * insertion order. Spans are sorted ascending so renderers can walk them\n * sequentially with a cursor through the source string.\n */\nexport function mergeReferences<TItem>(\n refs: readonly CompletionReference<TItem>[],\n): CompletionReference<TItem>[] {\n const sorted = [...refs].sort((a, b) => a.start - b.start)\n const merged: CompletionReference<TItem>[] = []\n let lastEnd = -1\n for (const ref of sorted) {\n if (ref.start < lastEnd)\n continue\n merged.push(ref)\n lastEnd = ref.end\n }\n return merged\n}\n\n/**\n * Collect every provider's references in one pass. Convenience wrapper —\n * the TUI textarea component calls this on every keystroke to highlight\n * in-prompt mentions.\n */\nexport function collectReferences<TItem>(\n text: string,\n providers: readonly CompletionProvider<TItem>[],\n cursor = text.length,\n): CompletionReference<TItem>[] {\n const ctx: CompletionContext = { text, cursor }\n const refs: CompletionReference<TItem>[] = []\n for (const p of providers) {\n for (const ref of p.parseReferences(text, ctx))\n refs.push(ref)\n }\n return mergeReferences(refs)\n}\n\nexport async function resolveCompletionSubmit<TItem>(\n text: string,\n providers: readonly CompletionProvider<TItem>[],\n options: { cursor?: number, signal?: AbortSignal } = {},\n): Promise<CompletionReference<TItem>[]> {\n const cursor = options.cursor ?? text.length\n const signal = options.signal ?? new AbortController().signal\n const byProvider = new Map<string, CompletionReference<TItem>[]>()\n for (const ref of collectReferences(text, providers, cursor)) {\n const bucket = byProvider.get(ref.providerId) ?? []\n bucket.push(ref)\n byProvider.set(ref.providerId, bucket)\n }\n const resolved: CompletionReference<TItem>[] = []\n for (const provider of providers) {\n const refs = byProvider.get(provider.id)\n if (!refs?.length)\n continue\n const ctx: CompletionSubmitContext<TItem> = { text, cursor, references: refs }\n const next = provider.resolveReferences\n ? await provider.resolveReferences(refs, ctx, signal)\n : refs\n const submitCtx: CompletionSubmitContext<TItem> = { text, cursor, references: next }\n await provider.onSubmit?.(submitCtx, signal)\n resolved.push(...next)\n }\n return mergeReferences(resolved)\n}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"daytona.js","names":[],"sources":["../../src/contexts/daytona.ts"],"sourcesContent":["/**\n * Daytona sandbox provider.\n *\n * Implements {@link SandboxProvider} on top of Daytona sandboxes. Pair it with\n * {@link createSandboxContext} to run an agent's shell + filesystem tools\n * inside a Daytona sandbox:\n *\n * ```ts\n * import { createSandboxContext } from 'zidane/contexts'\n * import { createDaytonaProvider } from 'zidane/contexts/daytona'\n *\n * const execution = createSandboxContext(createDaytonaProvider())\n * ```\n *\n * Requires `@daytona/sdk` as an optional peer dependency:\n * `bun add @daytona/sdk`.\n */\n\nimport type { Logger } from '../logger'\nimport type { SandboxProvider } from './sandbox'\nimport type { ExecResult, SpawnConfig, StreamHandle } from './types'\nimport { Buffer } from 'node:buffer'\nimport { errorMessage } from '../errors'\nimport { consoleSink, createLogger } from '../logger'\nimport { capBufferedExecOutput } from './output-cap'\nimport { StreamQueue } from './stream-queue'\n\n// ---------------------------------------------------------------------------\n// Minimal structural views of the `@daytona/sdk` surface we touch. Declared\n// locally so zidane typechecks without the optional peer dependency installed.\n// ---------------------------------------------------------------------------\n\ninterface DaytonaExecuteResponse {\n exitCode?: number | null\n result?: string | null\n output?: string | null\n stdout?: string | null\n stderr?: string | null\n artifacts?: { stdout?: string | null }\n}\n\ninterface DaytonaFileEntry {\n name: string\n /** Present in the Daytona SDK's FileInfo; used for the `/` dir suffix. */\n isDir?: boolean\n}\n\n/**\n * Structural subset of Daytona's `PtyHandle`.\n *\n * Daytona's process/session API has NO stdin channel at all — sessions are\n * fire-and-forget with log streaming — so a PTY is the only way to reach a\n * long-lived process interactively. See the `execStream` implementation for\n * what that costs.\n */\ninterface DaytonaPtyHandle {\n pid?: number\n waitForConnection: () => Promise<unknown>\n sendInput: (data: string | Uint8Array) => Promise<unknown>\n wait: () => Promise<{ exitCode?: number }>\n kill?: () => Promise<unknown>\n disconnect: () => Promise<unknown>\n}\n\ninterface DaytonaSandbox {\n id: string\n state?: string\n snapshot?: string\n process: {\n executeCommand: (command: string, cwd?: string, env?: Record<string, string>, timeout?: number) => Promise<DaytonaExecuteResponse>\n /** Present in recent SDKs; only `execStream` uses it. */\n createPty?: (options: {\n /**\n * REQUIRED by the API despite reading like a convenience handle: the\n * SDK forwards it as the session id and the control plane rejects the\n * create with 400 `session ID is required` when it is absent.\n */\n id: string\n cwd?: string\n envs?: Record<string, string>\n onData?: (data: Uint8Array) => void\n }) => Promise<DaytonaPtyHandle>\n }\n fs: {\n /**\n * A Node Buffer, not a bare Uint8Array — that is what the SDK declares and\n * what its download path actually materializes (`Buffer.concat` over the\n * response parts), so `readFile` decodes with `.toString('utf8')` instead\n * of guarding a union. Leaning on that is only safe because the\n * `@daytona/sdk` peer range pins `^0.187.0`: this interface is a\n * hand-rolled structural type over a lazily-imported SDK, so a wider\n * install returning a plain Uint8Array would decode to \"104,101,…\"\n * at runtime with nothing failing at compile time.\n */\n downloadFile: (path: string) => Promise<Buffer>\n uploadFile: (content: Uint8Array | Buffer, path: string) => Promise<unknown>\n listFiles: (path: string) => Promise<DaytonaFileEntry[]>\n }\n getWorkDir?: () => Promise<string>\n start?: (timeout?: number) => Promise<unknown>\n delete: (timeout?: number) => Promise<unknown>\n stop?: (timeout?: number) => Promise<unknown>\n archive?: () => Promise<unknown>\n}\n\ninterface DaytonaClientConfig {\n apiKey?: string\n apiUrl?: string\n target?: string\n}\n\ninterface DaytonaResources {\n cpu?: number\n memory?: number\n disk?: number\n gpu?: number\n [key: string]: unknown\n}\n\ninterface DaytonaCreateParams {\n language?: string\n snapshot?: string\n image?: unknown\n resources?: DaytonaResources\n envVars?: Record<string, string>\n [key: string]: unknown\n}\n\ninterface DaytonaClient {\n create: (params?: DaytonaCreateParams) => Promise<DaytonaSandbox>\n get: (sandboxIdOrName: string) => Promise<DaytonaSandbox>\n}\n\ntype DaytonaConstructor = new (config?: DaytonaClientConfig) => DaytonaClient\n\n// ---------------------------------------------------------------------------\n// Provider options\n// ---------------------------------------------------------------------------\n\nexport type DaytonaOnDestroy = 'delete' | 'kill' | 'stop' | 'pause' | 'archive' | 'leave'\n\nexport interface DaytonaProviderOptions {\n /** Daytona API key. Falls back to the `DAYTONA_API_KEY` env var when omitted. */\n apiKey?: string\n /** Daytona API URL. Falls back to `DAYTONA_API_URL`, then Daytona's hosted default. */\n apiUrl?: string\n /** Daytona target / region. Falls back to `DAYTONA_TARGET`, then the org default. */\n target?: string\n /**\n * Attach to a pre-existing sandbox by id or name instead of creating a fresh\n * one. By default the provider treats attached sandboxes as externally owned\n * and leaves them running on destroy/failure (set {@link ownAttached} to take\n * over the lifecycle).\n */\n sandboxId?: string\n /**\n * Take ownership of an attached ({@link sandboxId}) sandbox's lifecycle, so\n * `destroy()` applies {@link onDestroy} to it (stop / archive / delete)\n * instead of leaving it running.\n *\n * The intended caller is a durable host that *owns* the sandbox across\n * invocations: mint it once, journal its id, re-attach via `sandboxId` on\n * every replay/resume, and clean it up at the end of the logical run. Without\n * this an attached sandbox can never be stopped/deleted through the provider —\n * a leak for host-owned sandboxes. No effect when creating a fresh sandbox\n * (those are always owned). Spawn-time failures (readiness timeout, pregame\n * error) still never delete a pre-existing attached sandbox — only sandboxes\n * this provider *created* are auto-cleaned on spawn failure.\n *\n * Default: `false`.\n */\n ownAttached?: boolean\n /** Daytona snapshot used when creating a fresh sandbox. */\n snapshot?: string\n /** Runtime used for Daytona's code tooling. Shell execution works either way. */\n language?: string\n /** Daytona image/declarative builder input used when creating a fresh sandbox. */\n image?: unknown\n /** Daytona sandbox resources. */\n resources?: DaytonaResources\n /** Default working directory, applied when a spawn doesn't set `SpawnConfig.cwd`. */\n cwd?: string\n /** Environment variables baked into the sandbox and re-sent on every command. */\n env?: Record<string, string>\n /** Logger for provider-level lifecycle lines. */\n logger?: Logger\n /** Total deadline, in seconds, for the post-create readiness probe. Defaults to `60`. */\n readinessTimeoutSeconds?: number\n /** Setup script to upload and run once the sandbox is ready. */\n pregame?: { name: string, content: string }\n /**\n * What to do with an owned sandbox on destroy:\n *\n * - `'delete'` / `'kill'` (default) — call `sandbox.delete()`.\n * - `'stop'` / `'pause'` — call `sandbox.stop()` so Daytona preserves state\n * according to the runner's stop semantics.\n * - `'archive'` — stop, then archive for longer-term persistence.\n * - `'leave'` — make no API call.\n */\n onDestroy?: DaytonaOnDestroy\n}\n\n/** Per-probe timeout for the readiness `true` command. */\nconst READINESS_PROBE_TIMEOUT_SECONDS = 5\n/** Pause between readiness probes while the sandbox is still warming up. */\nconst READINESS_BACKOFF_MS = 250\n/** Setup scripts commonly install dependencies, so give them a generous cap. */\nconst PREGAME_TIMEOUT_SECONDS = 30 * 60\n\nexport interface DaytonaReadinessOptions {\n /** Total deadline in seconds. `<= 0` disables the probe entirely. */\n timeoutSeconds: number\n /** Optional cleanup for owned sandboxes that never become ready. */\n cleanupOnTimeout?: () => Promise<void>\n /** Clock source — injectable for deterministic tests. */\n now?: () => number\n /** Sleep between probes — injectable for deterministic tests. */\n sleep?: (ms: number) => Promise<void>\n}\n\nexport function resolveDaytonaEnv(\n optionEnv: Record<string, string> | undefined,\n perSpawnEnv: Record<string, string> | undefined,\n): Record<string, string> | undefined {\n const merged = { ...optionEnv, ...perSpawnEnv }\n return Object.keys(merged).length > 0 ? merged : undefined\n}\n\nexport function normalizeDaytonaExecResult(response: DaytonaExecuteResponse): ExecResult {\n return {\n stdout: response.stdout ?? response.artifacts?.stdout ?? response.result ?? response.output ?? '',\n stderr: response.stderr ?? '',\n exitCode: typeof response.exitCode === 'number' ? response.exitCode : 0,\n }\n}\n\nfunction capExecResult(result: ExecResult): ExecResult {\n return { ...capBufferedExecOutput(result.stdout, result.stderr), exitCode: result.exitCode }\n}\n\nfunction normalizeDaytonaExecError(error: unknown): ExecResult {\n const err = error as { exitCode?: unknown, stdout?: unknown, stderr?: unknown, result?: unknown, output?: unknown, message?: unknown }\n if (typeof err.exitCode === 'number') {\n return {\n stdout: typeof err.stdout === 'string'\n ? err.stdout\n : typeof err.result === 'string'\n ? err.result\n : typeof err.output === 'string'\n ? err.output\n : '',\n stderr: typeof err.stderr === 'string' ? err.stderr : typeof err.message === 'string' ? err.message : '',\n exitCode: err.exitCode,\n }\n }\n return { stdout: '', stderr: error instanceof Error ? error.message : String(error), exitCode: 124 }\n}\n\nfunction toClientConfig(apiKey?: string, apiUrl?: string, target?: string): DaytonaClientConfig | undefined {\n const config: DaytonaClientConfig = {}\n if (apiKey !== undefined)\n config.apiKey = apiKey\n if (apiUrl !== undefined)\n config.apiUrl = apiUrl\n if (target !== undefined)\n config.target = target\n return Object.keys(config).length > 0 ? config : undefined\n}\n\nfunction takeString(value: unknown): string | undefined {\n return typeof value === 'string' && value.length > 0 ? value : undefined\n}\n\nexport function resolveDaytonaCreateParams(\n sandboxConfig: Record<string, unknown>,\n options: DaytonaProviderOptions,\n envVars: Record<string, string> | undefined,\n): DaytonaCreateParams | undefined {\n const reserved = new Set(['provider', 'apiKey', 'apiUrl', 'target', 'sandboxId'])\n const params: DaytonaCreateParams = {}\n\n for (const [key, value] of Object.entries(sandboxConfig)) {\n if (!reserved.has(key) && value !== undefined)\n params[key] = value\n }\n\n const language = takeString(sandboxConfig.language) ?? options.language\n const snapshot = takeString(sandboxConfig.snapshot) ?? options.snapshot\n const image = sandboxConfig.image ?? options.image\n const resources = (sandboxConfig.resources as DaytonaResources | undefined) ?? options.resources\n const configEnvVars = sandboxConfig.envVars as Record<string, string> | undefined\n const mergedEnvVars = resolveDaytonaEnv(envVars, configEnvVars)\n\n if (language !== undefined)\n params.language = language\n if (snapshot !== undefined)\n params.snapshot = snapshot\n if (image !== undefined)\n params.image = image\n if (resources !== undefined)\n params.resources = resources\n if (mergedEnvVars !== undefined)\n params.envVars = mergedEnvVars\n\n return Object.keys(params).length > 0 ? params : undefined\n}\n\nexport async function waitForDaytonaReady(\n sandbox: DaytonaSandbox,\n logger: Logger,\n opts: DaytonaReadinessOptions,\n): Promise<void> {\n if (opts.timeoutSeconds <= 0)\n return\n\n const now = opts.now ?? Date.now\n const sleep = opts.sleep ?? (ms => new Promise<void>(resolve => setTimeout(resolve, ms)))\n const start = now()\n const deadline = start + opts.timeoutSeconds * 1000\n\n logger.info('waiting for Daytona sandbox to initialize', { sandboxId: sandbox.id })\n\n while (true) {\n try {\n await sandbox.process.executeCommand('true', undefined, undefined, READINESS_PROBE_TIMEOUT_SECONDS)\n logger.info('Daytona sandbox ready', { sandboxId: sandbox.id, elapsedMs: now() - start })\n return\n }\n catch {\n if (now() >= deadline) {\n await opts.cleanupOnTimeout?.().catch(() => {})\n throw new Error(`Daytona sandbox ${sandbox.id} did not become ready within ${opts.timeoutSeconds}s`)\n }\n await sleep(READINESS_BACKOFF_MS)\n }\n }\n}\n\nexport async function runDaytonaPregame(\n sandbox: DaytonaSandbox,\n logger: Logger,\n opts: { name: string, content: string, cwd?: string, envVars?: Record<string, string> },\n): Promise<void> {\n const path = `/tmp/${opts.name}`\n logger.info('running Daytona pregame script', { sandboxId: sandbox.id, path })\n await sandbox.fs.uploadFile(Buffer.from(opts.content), path)\n\n const quoted = JSON.stringify(path)\n let result: ExecResult\n try {\n result = normalizeDaytonaExecResult(\n await sandbox.process.executeCommand(`chmod +x ${quoted} && ${quoted}`, opts.cwd, opts.envVars, PREGAME_TIMEOUT_SECONDS),\n )\n }\n catch (err) {\n result = normalizeDaytonaExecError(err)\n }\n\n if (result.exitCode !== 0) {\n logger.error('Daytona pregame script failed', { sandboxId: sandbox.id, exitCode: result.exitCode, stdout: result.stdout, stderr: result.stderr })\n throw new Error(`Daytona pregame script ${opts.name} failed with exit code ${result.exitCode}`)\n }\n\n logger.info('Daytona pregame ready', { sandboxId: sandbox.id, stdout: result.stdout, stderr: result.stderr })\n}\n\n// ---------------------------------------------------------------------------\n// Provider\n// ---------------------------------------------------------------------------\n\nexport function createDaytonaProvider(options: DaytonaProviderOptions = {}): SandboxProvider {\n const live = new Map<string, { sandbox: DaytonaSandbox, env?: Record<string, string>, owned: boolean }>()\n const logger = options.logger ?? createLogger(consoleSink())\n const readinessTimeoutSeconds = options.readinessTimeoutSeconds ?? 60\n\n async function loadSdk(): Promise<DaytonaConstructor> {\n try {\n const mod = await import('@daytona/sdk') as { Daytona: DaytonaConstructor }\n return mod.Daytona\n }\n catch (err) {\n // Distinguish \"not installed\" from \"installed but failed to load\". This\n // used to be a bare `catch` that reported the install hint either way,\n // which sent people to `bun add` a package that was already present. The\n // realistic load failure is a `tslib` hoist: `@daytona/sdk` declares no\n // tslib dependency of its own and reaches AWS SDK v3 through\n // `@aws-sdk/client-s3`, so a stale `@aws-crypto` v3/v4 floor\n // (`tslib: ^1.11.1`) can win the hoist and the SDK's initializer helpers\n // resolve against tslib 1.x, which lacks them. Fix with a root\n // `overrides: { \"tslib\": \"^2.8.1\" }` — see `docs/RESTATE.md`.\n const message = errorMessage(err)\n const notInstalled = /cannot find (?:module|package)|module not found|failed to resolve/i.test(message)\n throw new Error(\n notInstalled\n ? '@daytona/sdk is required for the Daytona sandbox provider. Install it with: bun add @daytona/sdk'\n : `@daytona/sdk is installed but failed to load: ${message}. If this mentions tslib, pin it with a root override: \"overrides\": { \"tslib\": \"^2.8.1\" } — see docs/RESTATE.md.`,\n { cause: err },\n )\n }\n }\n\n function get(sandboxId: string): { sandbox: DaytonaSandbox, env?: Record<string, string>, owned: boolean } {\n const entry = live.get(sandboxId)\n if (!entry)\n throw new Error(`Daytona sandbox ${sandboxId} is not tracked by this provider`)\n return entry\n }\n\n async function deleteOwnedSandbox(sandbox: DaytonaSandbox): Promise<void> {\n try {\n await sandbox.delete()\n }\n catch {\n // Already gone — nothing to clean up.\n }\n }\n\n async function stopOwnedSandbox(sandbox: DaytonaSandbox): Promise<void> {\n if (!sandbox.stop) {\n logger.error('cannot stop Daytona sandbox: this @daytona/sdk install exposes no stop() method; leaving it running', { sandboxId: sandbox.id })\n return\n }\n try {\n await sandbox.stop()\n logger.info('stopped Daytona sandbox; reconnect later via the sandboxId option (CLI: --sandbox)', { sandboxId: sandbox.id })\n }\n catch (err) {\n logger.error('failed to stop Daytona sandbox; it will run until Daytona lifecycle policy stops it', { sandboxId: sandbox.id, error: err instanceof Error ? err.message : String(err) })\n }\n }\n\n async function archiveOwnedSandbox(sandbox: DaytonaSandbox): Promise<void> {\n if (!sandbox.stop || !sandbox.archive) {\n logger.error('cannot archive Daytona sandbox: this @daytona/sdk install exposes no stop() and archive() pair; leaving it running', { sandboxId: sandbox.id })\n return\n }\n try {\n await sandbox.stop()\n await sandbox.archive()\n logger.info('archived Daytona sandbox; reconnect later via the sandboxId option (CLI: --sandbox)', { sandboxId: sandbox.id })\n }\n catch (err) {\n logger.error('failed to archive Daytona sandbox; it will run until Daytona lifecycle policy stops it', { sandboxId: sandbox.id, error: err instanceof Error ? err.message : String(err) })\n }\n }\n\n async function startAttachedSandboxIfNeeded(sandbox: DaytonaSandbox): Promise<void> {\n if (sandbox.state !== 'stopped' && sandbox.state !== 'archived')\n return\n if (!sandbox.start) {\n logger.error('cannot start Daytona sandbox before attach readiness probe: this @daytona/sdk install exposes no start() method', { sandboxId: sandbox.id, state: sandbox.state })\n return\n }\n logger.info('starting Daytona sandbox before attach readiness probe', { sandboxId: sandbox.id, state: sandbox.state })\n await sandbox.start(readinessTimeoutSeconds > 0 ? readinessTimeoutSeconds : undefined)\n }\n\n return {\n name: 'daytona',\n\n async spawn(config: SpawnConfig = {}): Promise<{ id: string, cwd: string }> {\n const Daytona = await loadSdk()\n const sb: Record<string, unknown> = config.sandbox ?? {}\n\n const apiKey = takeString(sb.apiKey) ?? options.apiKey\n const apiUrl = takeString(sb.apiUrl) ?? options.apiUrl\n const target = takeString(sb.target) ?? options.target\n const connectId = takeString(sb.sandboxId) ?? options.sandboxId\n const snapshot = takeString(sb.snapshot) ?? options.snapshot\n const envVars = resolveDaytonaEnv(options.env, config.env)\n\n const daytona = new Daytona(toClientConfig(apiKey, apiUrl, target))\n let attached = connectId !== undefined\n let sandbox = connectId !== undefined\n ? await daytona.get(connectId)\n : await daytona.create(resolveDaytonaCreateParams(sb, options, envVars))\n\n // An attached sandbox built from a stale snapshot is recreated from\n // scratch: the caller's snapshot pin wins over the journaled sandbox.\n if (attached && snapshot !== undefined && sandbox.snapshot !== snapshot) {\n logger.info('recreating Daytona sandbox: snapshot changed', {\n sandboxId: sandbox.id,\n previousSnapshot: sandbox.snapshot,\n newSnapshot: snapshot,\n })\n await deleteOwnedSandbox(sandbox)\n attached = false\n sandbox = await daytona.create(resolveDaytonaCreateParams(sb, options, envVars))\n }\n\n // Lifecycle ownership drives `destroy()` (onDestroy). A created sandbox is\n // always owned; an attached one is owned only when the host opts in via\n // `ownAttached`. Spawn-FAILURE cleanup is separate (see below) and keyed\n // on `!attached` so a pre-existing sandbox is never auto-deleted.\n const owned = !attached || options.ownAttached === true\n\n if (attached)\n await startAttachedSandboxIfNeeded(sandbox)\n\n live.set(sandbox.id, { sandbox, env: envVars, owned })\n\n try {\n await waitForDaytonaReady(sandbox, logger, {\n timeoutSeconds: readinessTimeoutSeconds,\n // Only auto-delete a sandbox we CREATED on readiness failure — never a\n // pre-existing attached one, even when we own its lifecycle.\n ...(!attached ? { cleanupOnTimeout: () => deleteOwnedSandbox(sandbox) } : {}),\n })\n }\n catch (err) {\n live.delete(sandbox.id)\n throw err\n }\n\n const requestedCwd = config.cwd ?? options.cwd\n let cwd: string\n if (requestedCwd) {\n cwd = requestedCwd\n await sandbox.process.executeCommand(`mkdir -p ${JSON.stringify(cwd)}`).catch(() => {})\n }\n else if (sandbox.getWorkDir) {\n cwd = await sandbox.getWorkDir().catch(() => '')\n if (!cwd)\n cwd = await sandbox.process.executeCommand('pwd').then(r => normalizeDaytonaExecResult(r).stdout.trim()).catch(() => '') || '/home/daytona'\n }\n else {\n cwd = await sandbox.process.executeCommand('pwd').then(r => normalizeDaytonaExecResult(r).stdout.trim()).catch(() => '') || '/home/daytona'\n }\n\n if (options.pregame) {\n try {\n await runDaytonaPregame(sandbox, logger, { name: options.pregame.name, content: options.pregame.content, cwd, envVars })\n }\n catch (err) {\n // Spawn-failure cleanup: only delete a sandbox we created, never a\n // pre-existing attached one (the host owns its prior state).\n if (!attached)\n await deleteOwnedSandbox(sandbox)\n live.delete(sandbox.id)\n throw err\n }\n }\n\n return { id: sandbox.id, cwd }\n },\n\n async exec(sandboxId, command, opts): Promise<ExecResult> {\n const { sandbox, env } = get(sandboxId)\n const timeoutSeconds = opts?.timeout ?? 30\n try {\n // The SDK buffers the streams uncapped (see the KNOWN RESIDUAL note\n // on `ExecutionContext.fileSize`) — cap host-side before the strings\n // propagate into tool results and session history.\n return capExecResult(normalizeDaytonaExecResult(\n await sandbox.process.executeCommand(\n command,\n opts?.cwd,\n resolveDaytonaEnv(env, opts?.env),\n timeoutSeconds > 0 ? timeoutSeconds : undefined,\n ),\n ))\n }\n catch (err) {\n return capExecResult(normalizeDaytonaExecError(err))\n }\n },\n\n /**\n * Live bidirectional stream — see `ExecutionContext.execStream`.\n *\n * PTY-backed, and not by preference: Daytona's process + session API\n * exposes no stdin at all (sessions are fire-and-forget with log\n * streaming), so a pseudo-terminal is the only interactive channel the\n * platform offers. That forces three mitigations, all in the launch\n * wrapper below, because a PTY is a terminal and a protocol stream is\n * not:\n *\n * 1. `stty -echo` — a terminal echoes everything written to stdin back\n * onto stdout. Without this the caller reads its own requests back\n * as if they were responses.\n * 2. `stty raw` — canonical mode rewrites `\\n`→`\\r\\n` and caps a line\n * at ~4096 bytes. Protocol frames routinely exceed that and would\n * be silently truncated mid-message.\n * 3. `2><file>` — a PTY has ONE stream, so the child's stderr would\n * otherwise interleave into the protocol data and corrupt framing.\n * It is redirected to a file inside the sandbox and surfaced on the\n * returned handle by polling that file through the exec API.\n *\n * The mitigations must run before the child's first byte — but the\n * launch line ITSELF is typed into a canonical-mode terminal, so it is\n * echoed once (a short non-JSON line the transport's tolerant framing\n * discards) and subject to the ~4096-byte line cap. That is why the\n * real command lives in an uploaded script and the typed line stays\n * short and constant: a long command line would be truncated before\n * `stty` ever ran. The script also restores `sh` compound-command\n * semantics that a bare `exec <command>` would break.\n */\n async execStream(sandboxId, command, opts): Promise<StreamHandle> {\n const { sandbox, env } = get(sandboxId)\n\n if (!sandbox.process.createPty) {\n throw new Error(\n 'Daytona SDK is too old: `process.createPty` is required for execStream '\n + '(used to run stdio MCP servers inside the sandbox).',\n )\n }\n\n opts?.signal?.throwIfAborted()\n\n const token = Math.random().toString(36).slice(2, 10)\n const stderrPath = `/tmp/.zidane-stream-${token}.err`\n const scriptPath = `/tmp/.zidane-stream-${token}.sh`\n\n // `exec 2>` rebinds the shell's stderr before the command runs, so\n // the command text needs no rewriting and stays compound-safe.\n await sandbox.fs.uploadFile(\n Buffer.from(`exec 2>${stderrPath}\\n${command}\\n`),\n scriptPath,\n )\n\n // See the E2B counterpart: the PTY's `onData` is a sync `void`\n // callback, so a stalled consumer can only be reported, never slowed.\n const warnBacklog = (which: string) => (bytes: number) =>\n logger.warn('execStream consumer is not draining output', { sandboxId, stream: which, bufferedBytes: bytes })\n const stdout = new StreamQueue(warnBacklog('stdout'))\n const stderr = new StreamQueue(warnBacklog('stderr'))\n\n // Everything the shell emits before the child is actually running is\n // NOISE that must never reach the caller: the echo of our own launch\n // line (typed before `-echo` takes effect), the login shell's prompt,\n // and its line-editor redraw escapes. `createPty` is `lazyStart`, so\n // the shell frequently does not exist yet when we type — it boots\n // afterwards and REPLAYS the buffered input through its line editor.\n // Observed on a stock sandbox: the stream opened with the echoed\n // launch line, the echoed first request, `\\u001b[?2004h` bracketed\n // paste, and a `%` prompt. An MCP client parses that echoed request\n // as a message and the handshake collapses.\n //\n // So: gate the stream on a sentinel the CHILD side prints, and drop\n // every byte before it. The sentinel is assembled by `printf` at\n // runtime (`Z-%s-READY` + token) so the echoed *source* line, which\n // contains the format string rather than the result, cannot match it.\n const sentinel = `Z-${token}-READY`\n let gateOpen = false\n let gateBuffer = ''\n let openGate: () => void = () => {}\n const gateReady = new Promise<void>((r) => { openGate = r })\n\n const pty = await sandbox.process.createPty({\n id: `zidane-stream-${token}`,\n ...(opts?.cwd ? { cwd: opts.cwd } : {}),\n envs: resolveDaytonaEnv(env, opts?.env) ?? {},\n onData: (data: Uint8Array) => {\n if (gateOpen) {\n stdout.push(data)\n return\n }\n gateBuffer += Buffer.from(data).toString('utf8')\n const at = gateBuffer.indexOf(sentinel)\n if (at === -1) {\n // Bounded: never let pre-gate noise grow without limit, but keep\n // a tail longer than the sentinel so it can't be split away.\n if (gateBuffer.length > 64 * 1024)\n gateBuffer = gateBuffer.slice(-1024)\n return\n }\n gateOpen = true\n openGate()\n // Everything after the sentinel's trailing newline is real output.\n const rest = gateBuffer.slice(at + sentinel.length).replace(/^\\r?\\n/, '')\n gateBuffer = ''\n if (rest.length > 0)\n stdout.push(new TextEncoder().encode(rest))\n },\n })\n await pty.waitForConnection()\n\n // Short, constant, and under any canonical line cap. `exec` replaces\n // the login shell so the PTY's lifetime is the script's lifetime and\n // no shell prompt can reach stdout.\n await pty.sendInput(`stty raw -echo; printf 'Z-%s-READY\\\\n' ${token}; exec sh ${scriptPath}\\n`)\n\n // Block until the child is genuinely live. Without this the caller can\n // write its first frame into a shell that hasn't started, where it is\n // echoed back and misread as a response.\n let gateTimer: ReturnType<typeof setTimeout> | undefined\n await Promise.race([\n gateReady,\n new Promise<void>((r) => { gateTimer = setTimeout(r, 30_000) }),\n ])\n clearTimeout(gateTimer)\n if (!gateOpen) {\n // Full teardown, not just a disconnect: Daytona PTY sessions persist\n // server-side after the websocket drops, so a bare disconnect leaks\n // one login shell plus two /tmp files per attempt — a bootstrap\n // retry loop against a broken image would stack them until the\n // sandbox dies.\n await (pty.kill?.() ?? Promise.resolve()).catch(() => {})\n await pty.disconnect().catch(() => {})\n await sandbox.process.executeCommand(`rm -f ${scriptPath} ${stderrPath}`).catch(() => {})\n throw new Error(\n `Daytona execStream: PTY did not reach the ready sentinel within 30s `\n + `(sandbox ${sandboxId}). The login shell may not have started.`,\n )\n }\n\n // stderr file → handle.stderr. Poll rather than stream: the exec API\n // is the only other channel into the sandbox, and diagnostics don't\n // need latency. Byte offsets keep re-reads incremental.\n //\n // The offset comes from the FILE (`wc -c`), never from measuring the\n // returned content: an exec API that trims a trailing newline from\n // command output would make a content-derived offset under-advance by\n // one byte per poll, re-emitting the tail byte forever. The size rides\n // back on the same round trip as a `ZEOF-<token>:` suffix; content is\n // whatever precedes it, so content-side transforms can't drift the\n // cursor either.\n let stderrOffset = 0\n let torndown = false\n const eofMark = `ZEOF-${token}:`\n const drainStderr = async (): Promise<void> => {\n try {\n const res = normalizeDaytonaExecResult(await sandbox.process.executeCommand(\n `tail -c +${stderrOffset + 1} ${stderrPath} 2>/dev/null; printf '${eofMark}%s' \"$(wc -c < ${stderrPath} 2>/dev/null || echo 0)\"`,\n ))\n const at = res.stdout.lastIndexOf(eofMark)\n if (at === -1)\n return\n const content = res.stdout.slice(0, at)\n const size = Number.parseInt(res.stdout.slice(at + eofMark.length).trim(), 10)\n if (content.length > 0)\n stderr.push(new TextEncoder().encode(content))\n if (Number.isFinite(size) && size > stderrOffset)\n stderrOffset = size\n }\n catch {\n // Diagnostic channel — polling failures must never surface as\n // protocol errors.\n }\n }\n const stderrTimer = setInterval(() => { void drainStderr() }, 2000)\n // Guarded: Bun/Node timers support unref, test fakes may not.\n ;(stderrTimer as { unref?: () => void }).unref?.()\n\n // Shared teardown for both exit paths (natural exit and kill): one\n // final stderr drain so a crash message isn't lost, then remove the\n // script + stderr files that would otherwise accumulate in /tmp for\n // the sandbox's lifetime.\n const teardown = async (): Promise<void> => {\n if (torndown)\n return\n torndown = true\n clearInterval(stderrTimer)\n await drainStderr()\n await sandbox.process.executeCommand(`rm -f ${scriptPath} ${stderrPath}`).catch(() => {})\n stdout.close()\n stderr.close()\n }\n\n const exited = pty.wait()\n .then(result => result?.exitCode ?? 0)\n .catch(() => 137)\n .finally(teardown)\n\n const kill = async (): Promise<void> => {\n // A PTY login shell never exits on its own — killing it IS the\n // normal termination path here, not an error path.\n await (pty.kill?.() ?? Promise.resolve()).catch(() => {})\n await pty.disconnect().catch(() => {})\n await teardown()\n }\n\n if (opts?.signal) {\n if (opts.signal.aborted) {\n void kill()\n }\n else {\n // Detach on exit — the (typically run-long) signal otherwise\n // retains the `kill` closure and the PTY handle for the rest of\n // the run, one dead closure per exec.\n const onAbort = () => { void kill() }\n const detach = () => opts.signal!.removeEventListener('abort', onAbort)\n opts.signal.addEventListener('abort', onAbort, { once: true })\n exited.then(detach, detach)\n }\n }\n\n return {\n pid: pty.pid ?? -1,\n stdout,\n stderr,\n write: async (data: Uint8Array): Promise<void> => {\n await pty.sendInput(data)\n },\n kill,\n exited,\n }\n },\n\n async readFile(sandboxId, path): Promise<string> {\n const data = await get(sandboxId).sandbox.fs.downloadFile(path)\n return data.toString('utf8')\n },\n\n async writeFile(sandboxId, path, content, encoding: BufferEncoding = 'utf-8'): Promise<void> {\n const data = typeof content === 'string' ? Buffer.from(content, encoding) : content\n await get(sandboxId).sandbox.fs.uploadFile(data, path)\n },\n\n async listFiles(sandboxId, path): Promise<string[]> {\n const entries = await get(sandboxId).sandbox.fs.listFiles(path)\n // Trailing `/` on directories — parity with process/docker contexts.\n return entries.map(e => (e.isDir ? `${e.name}/` : e.name))\n },\n\n async destroy(sandboxId): Promise<void> {\n const entry = live.get(sandboxId)\n if (!entry)\n return\n if (entry.owned) {\n const action = options.onDestroy ?? 'delete'\n if (action === 'stop' || action === 'pause')\n await stopOwnedSandbox(entry.sandbox)\n else if (action === 'archive')\n await archiveOwnedSandbox(entry.sandbox)\n else if (action === 'delete' || action === 'kill')\n await deleteOwnedSandbox(entry.sandbox)\n }\n live.delete(sandboxId)\n },\n }\n}\n"],"mappings":"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"}