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,12 +1,130 @@
1
- import { $l as Theme, B as McpCredentialStore, Bv as SkillConfig, Dd as EditOutcome, Dl as SourcedScanPath, Fd as SessionMeta, Id as Settings, Ji as AgentHookMap, Jm as CacheBreakInfo, Ld as StreamEvent, Ms as ExtensionHostRuntimeBridge, Pd as Screen, Pm as ContextBreakdown, Qo as ExtensionRegistry, Rf as ProviderKey, U_ as SessionTurn, Vd as ProjectBrief, Wf as ProviderDescriptor, Wv as SkillsConfig, Wy as SessionArtifact, Xu as CompletionItem, Yi as AgentHooks, Z_ as ThinkingLevel, Zl as SyntaxStyles, Zu as CompletionProvider, Zv as SessionStore, a_ as AgentStats, bf as ModelChainEntry, dv as TurnUsage, eu as ThemeColors, gv as ToolDef, kd as EditPayload, km as ContextSegment, nu as ThemeSurfaces, o_ as AutoCompact, qi as Agent, qv as Session, sh as Provider, tu as ThemeSelect, uv as TurnFinishReason, v_ as JsonValue, vp as InteractionRequest, x_ as McpToolSchema, y_ as McpServerConfig, yo as ExtensionArtifactRow, yp as InteractionResponse, z as McpCredentialEntry } from "./index-BgN1T3lk.js";
2
- import { Bt as Hint, Tt as KeyBindings, Y as ResolvedConfig, _ as DiscoveredMcp, f as DiscoveredPlugin, v as DiscoveryError } from "./transcript-anchors-BeudMbXx.js";
3
- import { P as ProviderUsageSnapshot, Wt as FileEntry, _t as PreviewResult, a as ActiveTodosState, h as TodoTally, mt as HunkResolution, p as TodoItem } from "./turn-operations-C8bMBDl_.js";
4
- import { H as ApprovalRequest, V as ApprovalDecision, dt as PromptTurnRef } from "./tool-formatters-DoRLnfuD.js";
5
- import { Hookable } from "hookable";
1
+ import { B as McpCredentialStore, Bg as AgentHookMap, Bp as Settings, Cy as McpToolSchema, Dh as EnvironmentProvider, Du as StorageMode, Eg as ContextBreakdown, Fd as CompletionProvider, Fp as Picked, Gh as ProviderAuth, Gp as ProjectBrief, Gy as SessionTurn, Id as CompletionReference, Ip as Screen, Iu as Theme, Jb as SkillsConfig, Jg as CacheBreakInfo, Jx as SessionArtifact, Kh as ProviderKey, Lm as ModelChainEntry, Lp as SessionEnvironmentSummary, Lu as ThemeColors, Md as ActiveTrigger, Mh as EnvironmentVerifyResult, Oc as StoredExtensionConfigItem, Oh as EnvironmentRegistry, Pd as CompletionItem, Ph as SessionEnvironment, Pu as SyntaxStyles, Qb as SessionData, Qd as CommandOutputBlock, Qh as ProviderDescriptor, Rp as SessionHeader, Ru as ThemeSelect, V as McpOAuthProvider, Vg as AgentHooks, Vp as StreamEvent, Vy as SessionContentBlock, Wb as SkillConfig, Wd as Command, Xh as ModelInfo, Yi as Agent, Za as ExtensionMcpHost, Zb as Session, _f as InteractionResponse, _s as ExtensionRegistry, ab as ToolResultContent, ay as AgentStats, bb as ToolDef, by as JsonValue, dl as SubagentOverride, eb as ThinkingLevel, ec as ExtensionHostRuntimeBridge, eh as EnvironmentDescriptor, ex as SessionRun, fl as SubagentRegistry, gc as ExtensionConfigCategory, gf as InteractionRequest, gh as EnvironmentActionSummary, gp as AgentRegistry, hh as EnvironmentActionResult, hu as SourcedScanPath, jh as EnvironmentTeardownResult, jl as LoadedPlugin, jp as EditPayload, kh as EnvironmentRequest, ko as ExtensionArtifactRow, kp as EditOutcome, mb as TurnUsage, nS as McpConnection, nh as EnvironmentSecrets, oy as AutoCompact, pb as TurnFinishReason, s_ as Provider, t as Preset, tx as SessionStore, xg as ContextSegment, xy as McpServerConfig, yh as EnvironmentConfig, z as McpCredentialEntry, zp as SessionMeta, zu as ThemeSurfaces } from "./index-BGe1fQT7.js";
2
+ import { c as BackgroundTerminalOptions, g as ExecutionHandle, h as ExecutionContext, m as ExecResult, v as ListFilesEntry, x as StreamHandle, y as ListFilesOptions } from "./isolation-D9L650W5.js";
3
+ import { $n as BrowserOpInputs, An as MachineResult, Cn as MachineInfo, Dn as MachineParams, En as MachineMethodName, Ft as HostProbe, Gi as PromptTurnRef, Jt as TodoTally, Kt as TodoItem, Mt as HostEntry, Nn as MachineStatusChange, On as MachineRegisterParams, Rt as HostTestResult, Si as ApprovalRequest, Tn as MachineLinksStatus, Zn as BrowserOp, Zr as HunkResolution, _n as TerminalReadResult, ei as PreviewResult, fn as TerminalAttachResult, gt as McpAuthStateMap, hn as TerminalOpenInput, ht as McpAuthEvent, j as ProviderUsageSnapshot, kn as MachineRegisterResult, ma as FileEntry, mn as TerminalInfo, nr as BrowserResponse, ra as ParsedBinding, tr as BrowserRequest, vn as TerminalSessionState, xi as ApprovalDecision, zt as ActiveTodosState } from "./turn-operations-DQX5ZZni.js";
6
4
  import { spawn } from "node:child_process";
5
+ import { Hookable } from "hookable";
7
6
  import { Dispatch, ReactNode, SetStateAction } from "react";
8
7
  import { OAuthClientProvider } from "@modelcontextprotocol/sdk/client/auth.js";
9
8
  import { OAuthCredentials, OAuthDeviceCodeInfo, OAuthPrompt, OAuthPrompt as OAuthPrompt$1, OAuthSelectPrompt } from "@earendil-works/pi-ai/oauth";
9
+ import "@modelcontextprotocol/sdk/shared/auth.js";
10
+ //#region src/chat/completion.d.ts
11
+ /** State surface returned by `useCompletion()`. */
12
+ interface CompletionState<TItem = unknown> {
13
+ /** Active trigger, or null when the cursor doesn't sit in a completable span. */
14
+ active: ActiveTrigger<TItem> | null;
15
+ /** Items for the active query. Empty when no trigger is active. */
16
+ items: readonly CompletionItem<TItem>[];
17
+ /** True while the provider's `suggest` promise is pending. */
18
+ loading: boolean;
19
+ /** Index of the highlighted item inside `items`. */
20
+ selectedIndex: number;
21
+ /** Move the highlight. Wraps at the ends. No-op when `items` is empty. */
22
+ selectNext: () => void;
23
+ selectPrev: () => void;
24
+ /**
25
+ * Commit the highlighted item. Returns the new text + cursor pair, or
26
+ * `null` when there's nothing to commit (no active trigger, empty list).
27
+ */
28
+ commit: () => {
29
+ text: string;
30
+ cursor: number;
31
+ } | null;
32
+ /** Force-close the popover until the user types again. */
33
+ dismiss: () => void;
34
+ /** All references in the current buffer, regardless of provider. */
35
+ references: readonly CompletionReference<TItem>[];
36
+ }
37
+ /**
38
+ * Drive a prompt-completion popover from React state.
39
+ *
40
+ * Inputs are pull-style — the caller owns `text` + `cursor` (typically
41
+ * mirroring an `OpenTUI TextareaRenderable` or a `<textarea>` element) and
42
+ * passes them in each render. Providers are matched by their `trigger`
43
+ * character; the engine picks at most one active provider per cursor
44
+ * position.
45
+ *
46
+ * Asynchronous suggest calls are aborted when the query changes or the
47
+ * popover closes, so providers that hit a backend don't leak.
48
+ */
49
+ declare function useCompletion<TItem = unknown>(input: {
50
+ text: string;
51
+ cursor: number;
52
+ }, providers: readonly CompletionProvider<TItem>[], options?: {
53
+ maxQueryLength?: number;
54
+ }): CompletionState<TItem>;
55
+ //#endregion
56
+ //#region src/markdown-defs/types.d.ts
57
+ /** A non-fatal problem with one markdown definition file. The file may still load. */
58
+ interface MarkdownDefDiagnostic {
59
+ path: string;
60
+ kind: 'command' | 'agent';
61
+ message: string;
62
+ }
63
+ //#endregion
64
+ //#region src/markdown-defs/model-aliases.d.ts
65
+ interface ModelAliasContext {
66
+ /** Provider name of the parent session, e.g. `'anthropic'`. */
67
+ parentProvider?: string;
68
+ /** Model ids the spawn catalog lists, when one was built. */
69
+ catalogIds?: ReadonlySet<string>;
70
+ }
71
+ //#endregion
72
+ //#region src/markdown-defs/builtin-agents.d.ts
73
+ /**
74
+ * Claude-trained models emit `subagent_type: 'Explore'` / `'Plan'`. Shipping
75
+ * both gives those calls a target. No `model`: the `subagentModels` setting
76
+ * or the parent model applies. A file agent with the same name overrides.
77
+ */
78
+ declare const BUILTIN_SUBAGENTS: Readonly<SubagentRegistry>;
79
+ //#endregion
80
+ //#region src/markdown-defs/index.d.ts
81
+ interface LoadMarkdownOptions {
82
+ cwd?: string;
83
+ home?: string;
84
+ /** Storage prefix, default `.zidane`. */
85
+ prefix?: string;
86
+ /** `settings.importForeignConfigs`. */
87
+ includeForeign: boolean;
88
+ /**
89
+ * Default true. False drops every `source: 'project'` root (native and
90
+ * foreign). The seam for SDK hosts that open untrusted repos: a project file
91
+ * writes a prompt (command body, subagent system prompt) the user did not,
92
+ * so such hosts load user-level files only. Headless sets it false unless
93
+ * `--project-agents` is passed.
94
+ */
95
+ includeProject?: boolean;
96
+ }
97
+ interface LoadMarkdownCommandsOptions extends LoadMarkdownOptions {
98
+ /**
99
+ * True when an enabled skill answers `/<name>`. Skills win that name, as in
100
+ * Claude Code, so the file command is dropped with a diagnostic instead of
101
+ * silently hiding the skill.
102
+ */
103
+ isSkillName?: (name: string) => boolean;
104
+ }
105
+ /** Markdown slash commands, first-wins by name in root order. Never throws. */
106
+ declare function loadMarkdownCommands(opts: LoadMarkdownCommandsOptions): {
107
+ commands: Command[];
108
+ diagnostics: MarkdownDefDiagnostic[];
109
+ };
110
+ /**
111
+ * Builtin Explore/Plan plus markdown agents. Files override builtins;
112
+ * among files, first-wins by name in root order. Never throws.
113
+ */
114
+ declare function loadMarkdownAgents(opts: LoadMarkdownOptions & {
115
+ modelContext: ModelAliasContext;
116
+ }): {
117
+ subagents: SubagentRegistry;
118
+ diagnostics: MarkdownDefDiagnostic[];
119
+ };
120
+ /**
121
+ * Fold markdown/builtin agents into the preset's spawn tool, filling gaps
122
+ * only: an agent the profile or an extension already registered keeps its
123
+ * definition. Runs after `composeExtensionPreset`, so precedence is
124
+ * builtin < file < extension without threading files through composition.
125
+ */
126
+ declare function applyMarkdownSubagents(preset: Preset, subagents: SubagentRegistry): Preset;
127
+ //#endregion
10
128
  //#region src/tools/call-tool.d.ts
11
129
  declare const CALL_TOOL_NAME = "call_tool";
12
130
  /** The inner call a `call_tool` envelope carries. */
@@ -125,7 +243,7 @@ declare const DOING_TASKS_DOCTRINE = "# Doing tasks\n- Match the verb of the req
125
243
  * heavy lifting at the wire level; this prompt nudges the model away from
126
244
  * the access patterns that defeat them.
127
245
  */
128
- declare const TOKEN_DISCIPLINE_DOCTRINE = "# Working efficiently\n- Search first (`grep` / `glob`), then `read_file` the specific file. Don't open a 2000-line file for one function.\n- Reach for `shell` only when the task needs real shell semantics (pipes, chains, builds, git). For plain searches and reads the dedicated tools honor .gitignore, clip runaway lines, and have no quoting pitfalls.\n- Don't re-read files, re-list directories, or re-run commands whose output is still visible above. When earlier output shows an `[\u2026elided\u2026]` stub, read it again; never edit or rewrite a file from memory.\n- Prefer `edit` / `multi_edit` over `write_file` for changes to existing files.\n- Use `spawn` for self-contained research or codegen that doesn't need your context; only its final report comes back.\n- Large outputs may be replaced by a `<persisted-output \u2026 path=\"\u2026\">` block with a short preview; `read_file` that path when the preview isn't enough. Copy load-bearing data into your response text before it scrolls away.";
246
+ declare const TOKEN_DISCIPLINE_DOCTRINE = "# Working efficiently\n- Search first (`grep` / `glob`), then `read_file` the specific file. Don't open a 2000-line file for one function.\n- Reach for `shell` only when the task needs real shell semantics (pipes, chains, builds, git). For plain searches and reads the dedicated tools honor .gitignore, clip runaway lines, and have no quoting pitfalls.\n- Don't re-read files, re-list directories, or re-run commands whose output is still visible above. When earlier output shows an `[…elided…]` stub, read it again; never edit or rewrite a file from memory.\n- Prefer `edit` / `multi_edit` over `write_file` for changes to existing files.\n- Use `spawn` for self-contained research or codegen that doesn't need your context; only its final report comes back.\n- Large outputs may be replaced by a `<persisted-output … path=\"…\">` block with a short preview; `read_file` that path when the preview isn't enough. Copy load-bearing data into your response text before it scrolls away.";
129
247
  /**
130
248
  * User-facing communication style. Lifted from claude-code's
131
249
  * `# Communicating with the user` + `# Tone and style` sections (~404-442).
@@ -836,6 +954,87 @@ interface RunUpdateCommandResult {
836
954
  */
837
955
  declare function runUpdateCommand(options: RunUpdateCommandOptions): Promise<RunUpdateCommandResult>;
838
956
  //#endregion
957
+ //#region src/chat/hints.d.ts
958
+ /**
959
+ * Single hint entry — `<key> <label>`, optionally followed by an inline
960
+ * secondary chord (`<key><extra.key> <label> <extra.label>`) for compound
961
+ * shortcuts like `ctrl+m/n model effort`.
962
+ *
963
+ * Colour fields are advisory hex strings (resolved against the active
964
+ * theme by the renderer); leaving them undefined lets the renderer pick
965
+ * its conventional fallback (e.g. `warn` for keys, `dim` for labels,
966
+ * `mute` for secondary).
967
+ */
968
+ interface Hint {
969
+ key: string;
970
+ label: string;
971
+ /** Optional override for the key color. Renderer default: warn. */
972
+ keyColor?: string;
973
+ /** Optional override for the label color. Renderer default: dim. */
974
+ labelColor?: string;
975
+ /**
976
+ * Optional secondary chord + value rendered inline after the primary
977
+ * pair: `<key><extra.key> <label> <extra.label>`. Lets one hint advertise
978
+ * a compound shortcut (e.g. `ctrl+m/n model effort`) without forcing
979
+ * callers into a parallel struct or a custom renderer.
980
+ */
981
+ extra?: {
982
+ key: string;
983
+ label: string;
984
+ /**
985
+ * Dispatchable binding spec behind `extra.key` when the displayed
986
+ * chord is an abbreviation (`/l` for `ctrl+l` — see `shortChord`).
987
+ * The abbreviation itself doesn't parse as a keypress, so a renderer
988
+ * that makes the secondary chord independently clickable needs the
989
+ * full spec. Defaults to `extra.key` when omitted.
990
+ */
991
+ keySpec?: string;
992
+ /** Defaults to mute on the renderer side. */
993
+ keyColor?: string;
994
+ /** Defaults to dim on the renderer side. */
995
+ labelColor?: string;
996
+ };
997
+ }
998
+ /**
999
+ * Truncate `text` to at most `max` characters, replacing the trailing
1000
+ * overflow with `…`. Edge cases:
1001
+ * - `max <= 0` → empty string (no room to render at all).
1002
+ * - `max === 1` → just the ellipsis glyph.
1003
+ * - `text.length <= max` → unchanged.
1004
+ *
1005
+ * Trailing-style truncation matches the natural read order of titles:
1006
+ * the prefix carries enough signal to identify the surface.
1007
+ */
1008
+ declare function truncateTrailing(text: string, max: number): string;
1009
+ /**
1010
+ * Plain-text width estimate for a list of {@link Hint}s rendered as
1011
+ * `<key> <label> · <key> <label> · …`. Exported so prompt-box overlays
1012
+ * can run the same responsive math as the bottom-bar footer when
1013
+ * deciding whether trigger hints fit. Pure / total.
1014
+ */
1015
+ declare function hintsLength(hints: readonly Hint[]): number;
1016
+ /**
1017
+ * Stable empty list so callers can compare by reference. Exported so
1018
+ * any consumer that needs to feed "no primary hints" into the same
1019
+ * `clipHintsToWidth` / `renderHintSpans` pipeline (e.g. the TUI's
1020
+ * minimal UI mode, where the prompt overlay shows only `@` / `/`
1021
+ * triggers) reuses the same frozen array instead of allocating a new
1022
+ * one each render.
1023
+ */
1024
+ declare const EMPTY_HINTS: readonly Hint[];
1025
+ /**
1026
+ * Return the longest prefix of `hints` whose rendered width fits within
1027
+ * `budget`. Used to degrade hint rows gracefully at narrow terminal
1028
+ * widths instead of letting an absolutely-positioned hint row wrap
1029
+ * mid-segment (which paints the overflow over surrounding borders and
1030
+ * looks like garbled glyphs in a TUI).
1031
+ *
1032
+ * Prefix-only (no reordering, no last-hint priority) so the survivors
1033
+ * keep their authored order — the user's muscle memory for "leftmost
1034
+ * hint = primary action" stays intact as the terminal shrinks.
1035
+ */
1036
+ declare function clipHintsToWidth(hints: readonly Hint[], budget: number): readonly Hint[];
1037
+ //#endregion
839
1038
  //#region src/chat/auto-update-hook.d.ts
840
1039
  interface UseUpdateCheckOptions {
841
1040
  /** npm package name. e.g. `'zidane-tui'`. */
@@ -1075,16 +1274,44 @@ interface ClaudeCopyResult {
1075
1274
  duplicates?: string[];
1076
1275
  }
1077
1276
  interface ClaudeImportSummary {
1277
+ /**
1278
+ * Where the import landed: always this computer (its session store and
1279
+ * its `.zidane` files). Claude Code's data is read from this machine, so
1280
+ * the import never targets a remote host.
1281
+ */
1282
+ target: 'this-computer';
1078
1283
  sessions?: ClaudeSessionImportResult;
1079
1284
  mcps?: ClaudeCopyResult;
1080
1285
  skills?: ClaudeCopyResult;
1286
+ commands?: ClaudeCopyResult;
1287
+ agents?: ClaudeCopyResult;
1081
1288
  /** What is deliberately NOT imported, spelled out for the UI. */
1082
1289
  notes: string[];
1083
1290
  }
1291
+ /** The host a window is using, as far as the import note cares. */
1292
+ interface ClaudeImportActiveHost {
1293
+ kind: 'in-process' | 'daemon' | 'server';
1294
+ name: string;
1295
+ }
1296
+ /**
1297
+ * What to tell the user when the window's sessions live on a remote host:
1298
+ * the import went to this computer, so the host's list won't show it.
1299
+ * `null` for a host on this machine (it shares the store).
1300
+ */
1301
+ declare function claudeImportHostNote(activeHost: ClaudeImportActiveHost | null | undefined): string | null;
1302
+ /**
1303
+ * Import Claude Code data from THIS machine into THIS machine: `store` must
1304
+ * be the local session store (never a remote host's), MCP servers, skills,
1305
+ * commands and subagents go to this machine's `.zidane` files. Pass `activeHost` (the
1306
+ * window's host) to get the remote-host note in `notes`.
1307
+ */
1084
1308
  declare function importFromClaudeCode(store: SessionStore, opts?: ClaudeImportOptions & {
1085
1309
  sessions?: boolean;
1086
1310
  mcps?: boolean;
1087
1311
  skills?: boolean;
1312
+ commands?: boolean;
1313
+ agents?: boolean;
1314
+ activeHost?: ClaudeImportActiveHost | null;
1088
1315
  }): Promise<ClaudeImportSummary>;
1089
1316
  //#endregion
1090
1317
  //#region src/chat/completion-artifacts.d.ts
@@ -1108,164 +1335,1644 @@ declare function createArtifactsCompletionProvider(opts: {
1108
1335
  limit?: number;
1109
1336
  }): CompletionProvider<ArtifactReferenceData>;
1110
1337
  //#endregion
1111
- //#region src/chat/config-context.d.ts
1112
- declare function ConfigProvider({
1113
- config,
1114
- children
1115
- }: {
1116
- config: ResolvedConfig;
1117
- children: ReactNode;
1118
- }): import("react").JSX.Element;
1119
- declare function useConfig(): ResolvedConfig;
1338
+ //#region src/start/shared-options.d.ts
1339
+ declare const startProviderNames: readonly ["anthropic", "openai", "openrouter", "cerebras", "xai", "arcee", "baseten", "local", "openai-compat"];
1340
+ type StartProviderName = typeof startProviderNames[number];
1341
+ declare const startPresetNames: readonly ["basic"];
1342
+ type StartPresetName = typeof startPresetNames[number];
1343
+ declare const startFormatNames: readonly ["zidane", "provider"];
1344
+ type StartFormatName = typeof startFormatNames[number];
1345
+ interface StartLocalOptions {
1346
+ system?: string;
1347
+ prompt: string;
1348
+ model?: string;
1349
+ baseURL?: string;
1350
+ apiKeyEnv?: string;
1351
+ headersEnv?: string;
1352
+ extraHeaders?: Record<string, string>;
1353
+ vision?: boolean;
1354
+ imageInToolResult?: boolean;
1355
+ temperature?: number;
1356
+ seed?: number;
1357
+ preset: StartPresetName;
1358
+ thinking: ThinkingLevel;
1359
+ provider: StartProviderName;
1360
+ context: string;
1361
+ image?: string;
1362
+ cwd?: string;
1363
+ env?: Record<string, string>;
1364
+ pregame?: {
1365
+ name: string;
1366
+ content: string;
1367
+ };
1368
+ sandboxId?: string;
1369
+ sandboxOnDestroy?: 'kill' | 'pause' | 'leave';
1370
+ mcp?: McpServerConfig[];
1371
+ sessionDb?: string;
1372
+ sessionId?: string;
1373
+ schema?: Record<string, unknown>;
1374
+ json: boolean;
1375
+ streamJson: boolean;
1376
+ format: StartFormatName;
1377
+ }
1120
1378
  //#endregion
1121
- //#region src/chat/discovery-context.d.ts
1122
- interface DiscoveryContextValue {
1123
- /** Parsed `SkillConfig[]` from project + user scan paths. */
1124
- skillsCatalog: readonly SkillConfig[];
1125
- /**
1126
- * Agent Plugins loaded from `.agents/plugins` / `.{prefix}/plugins`
1127
- * (project + user). Includes rejected plugins — they render as read-only
1128
- * diagnostics rather than vanishing.
1129
- */
1130
- pluginsCatalog: readonly DiscoveredPlugin[];
1131
- /** Parsed `mcps.json` entries. */
1132
- mcpsCatalog: readonly DiscoveredMcp[];
1133
- /** Non-fatal parse errors from `mcps.json` lookups. */
1134
- mcpsErrors: readonly DiscoveryError[];
1135
- /**
1136
- * Per-server full tool catalog from the on-disk cache (see
1137
- * `chat/mcp-tools-cache.ts`). Updated as MCP servers bootstrap and
1138
- * fire the `mcp:tools:list` hook. Missing key → no cache yet; the
1139
- * settings UI surfaces an empty-state row under the expanded server.
1140
- */
1141
- mcpToolsByServer: Record<string, readonly McpToolSchema[]>;
1142
- /** Current file walk results — empty until lazy-load lands. */
1143
- filesCatalog: readonly FileEntry[];
1144
- /** Force a fresh skills rescan. Resolves once state is committed. */
1145
- refreshSkills: () => Promise<void>;
1146
- /** Force a fresh plugin rescan. Resolves once state is committed. */
1147
- refreshPlugins: () => Promise<void>;
1379
+ //#region src/chat/keybindings.d.ts
1380
+ /**
1381
+ * Every action the keybindings system can drive. Adding a new action
1382
+ * here forces every consumer (TUI keyboard handler, hint rows, settings
1383
+ * UI) to handle it, and ships a default that the user can override.
1384
+ */
1385
+ type KeyAction = 'openSettings' | 'openSessionDetails' | 'newSession' | 'openModelPicker' | 'openEffortPicker' | 'openTodos' | 'openArtifacts' | 'panelNext' | 'panelPrev' | 'openContextPanel' | 'openMainSession' | 'openUsagePanel' | 'openKeybindings' | 'openPalette' | 'cycleAgent' | 'enterSelectTurnMode' | 'cancelToolCall' | 'abortRun' | 'changeCwd' | 'attachClipboardImage' | 'openExternalEditor' | 'enterQueueSelection' | 'pushQueuedMessage' | 'dropQueuedMessage' | 'turnFork' | 'turnDelete' | 'turnCopy' | 'turnEdit' | 'sessionDelete' | 'sessionCopyId' | 'sessionGenerateTitle' | 'sessionExportMarkdown' | 'sessionExportJson' | 'sessionCompact';
1386
+ /** Resolved keybindings — every action has a current spec string. */
1387
+ type KeyBindings = Record<KeyAction, string>;
1388
+ /**
1389
+ * Resolved spec string for one extension-contributed keybinding. Same
1390
+ * shape as a built-in {@link KeyBindings} entry but the `id` is
1391
+ * extension-owned (no `KeyAction` union member). The TUI dispatcher
1392
+ * iterates these alongside the built-ins; user overrides flow through
1393
+ * the same `keybindings.json` file by matching on `id`.
1394
+ */
1395
+ interface ResolvedExtensionKeybinding {
1396
+ /** Extension-owned id (matches `ExtensionKeybinding.id`). */
1397
+ id: string;
1398
+ /** Active spec string — user override when present, default otherwise. May be empty (= unbound). */
1399
+ spec: string;
1400
+ }
1401
+ /**
1402
+ * Logical grouping for one action — used by the keybindings panel to
1403
+ * render section headers. Stable strings (not an enum) so future
1404
+ * groups can be added without churning every existing entry.
1405
+ */
1406
+ type KeyBindingGroup = 'Global' | 'Message queue' | 'Turn details' | 'Session details';
1407
+ /** Static metadata for one action — used by the loader, hint rows, settings UI. */
1408
+ interface KeyBindingDef {
1409
+ /** Stable identifier — JSON key + TS union member. */
1410
+ action: KeyAction;
1411
+ /** Default trigger spec (e.g. `'ctrl+o'`). */
1412
+ default: string;
1413
+ /** Short label used in shortcut hint rows. */
1414
+ label: string;
1415
+ /** Longer description for the settings UI and the annotated JSON file. */
1416
+ description: string;
1148
1417
  /**
1149
- * Re-parse `mcps.json`. Sync-shaped under the hood, async for symmetry.
1150
- * Resolves `true` when the catalog actually changed (a publish that
1151
- * schedules downstream agent rebuilds), `false` on a fingerprint no-op
1152
- * or scan failure — callers that arm rebuild-coupled state (e.g. the
1153
- * add-MCP auto-login) disarm on `false`.
1418
+ * Group label rendered as a section header in the keybindings panel.
1419
+ * Items are listed in {@link KEYBINDING_DEFS} order; the panel just
1420
+ * inserts a header whenever this field changes between adjacent
1421
+ * entries — so groups stay contiguous and the order here is the
1422
+ * order on screen.
1154
1423
  */
1155
- refreshMcps: () => Promise<boolean>;
1156
- /** Force a fresh file walk. Wired into a future "refresh files" affordance. */
1157
- refreshFiles: () => Promise<void>;
1158
- /** Idempotent first-load trigger for skills (used by `/` popover + modal mount). */
1159
- ensureSkills: () => Promise<readonly SkillConfig[]>;
1160
- /** Idempotent first-load trigger for files (used by `@` popover). */
1161
- ensureFiles: () => Promise<readonly FileEntry[]>;
1424
+ group: KeyBindingGroup;
1162
1425
  }
1163
- declare function DiscoveryProvider({
1164
- value,
1165
- children
1166
- }: {
1167
- value: DiscoveryContextValue;
1168
- children: ReactNode;
1169
- }): import("react").JSX.Element;
1170
1426
  /**
1171
- * Read live discovery state + actions. Throws if used outside a
1172
- * `<DiscoveryProvider>` — discovery is a load-bearing dependency for
1173
- * settings + completion popovers; bailing loud here surfaces wiring
1174
- * mistakes at mount instead of producing empty catalogs at first
1175
- * keystroke.
1427
+ * The canonical action catalog. Order is the source of truth for the
1428
+ * generated keybindings file and the settings UI.
1176
1429
  */
1177
- declare function useDiscovery(): DiscoveryContextValue;
1430
+ declare const KEYBINDING_DEFS: readonly KeyBindingDef[];
1431
+ /** Index by action for O(1) lookup of label / description / default. */
1432
+ declare const KEYBINDING_DEF_BY_ACTION: Readonly<Record<KeyAction, KeyBindingDef>>;
1433
+ /** Defaults, derived from {@link KEYBINDING_DEFS} so the two never drift. */
1434
+ declare const DEFAULT_KEYBINDINGS: KeyBindings;
1178
1435
  /**
1179
- * Non-throwing variant — returns `null` when no provider is mounted.
1180
- * Used by composable modals (`<SettingsModal>`, …) that accept catalog
1181
- * props as a fallback for embedders who don't wire the full TUI
1182
- * shell. The in-app flow always mounts `<DiscoveryProvider>` so the
1183
- * modal sees live state; standalone embeds get the prop snapshot.
1436
+ * Test a KeyEvent against a parsed-or-string binding. Accepts either
1437
+ * form so callers can pre-parse (hot path) or pass the raw spec for
1438
+ * one-off checks.
1439
+ *
1440
+ * Modifier semantics: ALL declared modifiers must be pressed AND no
1441
+ * extra modifiers may be pressed. So `'o'` does NOT match `ctrl+o`,
1442
+ * and `'ctrl+o'` does NOT match `ctrl+shift+o`. This avoids surprises
1443
+ * where a binding that should be specific accidentally fires for a
1444
+ * superset of key combinations.
1184
1445
  */
1185
- declare function useDiscoveryOptional(): DiscoveryContextValue | null;
1186
- //#endregion
1187
- //#region src/chat/discovery-slot.d.ts
1446
+ declare function matchesBinding(event: {
1447
+ name?: string;
1448
+ ctrl?: boolean;
1449
+ shift?: boolean;
1450
+ meta?: boolean;
1451
+ option?: boolean;
1452
+ alt?: boolean;
1453
+ }, spec: string | ParsedBinding): boolean;
1454
+ /**
1455
+ * Render a binding spec (`"ctrl+return"`, `"backspace"`, `"delete"`) as a
1456
+ * compact display string the user recognizes at a glance. Substitutes
1457
+ * arrow / enter / backspace glyphs for their verbose names so the hint
1458
+ * row stays narrow; modifier names + plain keys pass through unchanged.
1459
+ *
1460
+ * Empty / missing specs return an empty string — callers decide whether
1461
+ * to render a placeholder (e.g. `'—'` for an unbound action in the
1462
+ * keybindings panel) or hide the surface entirely (e.g. hint rows that
1463
+ * fall back to a different chord when the binding is empty).
1464
+ */
1465
+ declare function formatBindingForDisplay(spec: string | null | undefined): string;
1466
+ /**
1467
+ * Merge a partial map of user overrides into the defaults. Unknown
1468
+ * action keys are dropped (a future TUI version may have retired the
1469
+ * action); invalid spec strings (parse fails) fall back to the default
1470
+ * for that action.
1471
+ */
1472
+ declare function mergeKeybindings(overrides: Readonly<Record<string, unknown>> | null | undefined): KeyBindings;
1473
+ /**
1474
+ * A `keybindings.json` value as one spec: a string, or an array of chords
1475
+ * (`["ctrl+w", "cmd+w"]`) joined as alternatives. Chords that don't parse
1476
+ * are dropped; `null` when none is left.
1477
+ */
1478
+ declare function normalizeBindingValue(raw: unknown): string | null;
1479
+ /** Path of the user-level keybindings file. */
1480
+ declare function keybindingsPath(userDir: string): string;
1481
+ /**
1482
+ * One contiguous section of the catalog — every action in a single
1483
+ * `group` rendered together with the section header inserted once.
1484
+ * Adjacent sections with the same group are NOT merged: catalog order
1485
+ * wins so the on-screen story matches the on-disk file.
1486
+ *
1487
+ * `rows` may carry built-in `KeyBindingDef`s (the common case) or
1488
+ * extension-owned ones (when an extension contributes a keybinding).
1489
+ * Both share label / description / spec — see {@link KeyBindingRow}.
1490
+ */
1491
+ interface KeyBindingSection {
1492
+ group: string;
1493
+ rows: ReadonlyArray<KeyBindingRow>;
1494
+ }
1188
1495
  /**
1189
- * Stale-while-revalidate catalog slot.
1496
+ * One row in the keybindings catalog. Either a built-in action (with
1497
+ * its `def`) or an extension-contributed one (with `id` instead). The
1498
+ * renderer reads `label` / `description` / `spec` uniformly and only
1499
+ * needs the `kind` discriminator when offering action-specific UI
1500
+ * (e.g. "edit in keybindings.json" by action name).
1501
+ */
1502
+ type KeyBindingRow = {
1503
+ kind: 'builtin';
1504
+ def: KeyBindingDef;
1505
+ spec: string;
1506
+ } | {
1507
+ kind: 'extension';
1508
+ id: string;
1509
+ label: string;
1510
+ description: string;
1511
+ spec: string;
1512
+ };
1513
+ /**
1514
+ * Walk {@link KEYBINDING_DEFS} in order and bucket rows into contiguous
1515
+ * sections by `group`. Shared between the standalone `KeybindingsModal`
1516
+ * and the `Keybindings` tab inside `SettingsModal` so both surfaces
1517
+ * render the same catalog with the same ordering rules.
1190
1518
  *
1191
- * One slot per (catalog kind, projectDir) pin. Holds the first-load
1192
- * promise + a `lastScannedAt` timestamp + an in-flight `refreshing`
1193
- * promise. Idempotent across concurrent callers, safe to rotate when
1194
- * the project changes (the slot's `abort` controller cancels in-flight
1195
- * walks and the `.then` guards drop stale results).
1519
+ * Pass `extensionRows` (resolved extension keybindings paired with
1520
+ * their definitions) to append an "Extensions" section at the end so
1521
+ * extension-contributed shortcuts are discoverable in the same UI.
1522
+ */
1523
+ declare function groupBindings(bindings: KeyBindings, extensionRows?: readonly {
1524
+ id: string;
1525
+ label: string;
1526
+ description: string;
1527
+ spec: string;
1528
+ }[]): readonly KeyBindingSection[];
1529
+ /**
1530
+ * Width (in cells) of the fixed key column used by every binding-row
1531
+ * renderer. Derived once from {@link KEYBINDING_DEFS} so a new action
1532
+ * with a wider default spec (`ctrl+shift+x`, …) automatically grows
1533
+ * the column instead of truncating the label.
1534
+ */
1535
+ declare const KEYBINDING_KEY_COL_WIDTH: number;
1536
+ /**
1537
+ * Load + merge the user's keybindings file (when present). Always
1538
+ * returns a fully-resolved {@link KeyBindings} — defaults fill in for
1539
+ * any missing / invalid entries so consumers can treat the result as
1540
+ * total.
1196
1541
  *
1197
- * Renderer-agnostic. AppShell wraps it in React refs so React closures
1198
- * stay stable; a non-TUI shell can use it directly against its own
1199
- * state propagation.
1542
+ * Failure modes (all tolerated, all fall back to defaults):
1543
+ * - File missing
1544
+ * - File not valid JSON / JSONC (after comment stripping)
1545
+ * - Top-level is not an object
1546
+ * - Individual entries have invalid spec strings
1547
+ */
1548
+ declare function readKeybindings(userDir: string): KeyBindings;
1549
+ /**
1550
+ * Create the user-level `keybindings.json` if it doesn't exist,
1551
+ * prefilled with every default. Returns the absolute file path either
1552
+ * way so the caller can open it in `$EDITOR`. Idempotent: an existing
1553
+ * user file is NEVER overwritten — the user's customizations are
1554
+ * sacred.
1555
+ */
1556
+ declare function ensureKeybindingsFile(userDir: string): string;
1557
+ /**
1558
+ * Read the raw on-disk JSON object from `keybindings.json` after the
1559
+ * JSONC stripper. Used by extension-keybinding resolution so the
1560
+ * user's overrides for extension-owned ids flow through the same file
1561
+ * the built-in actions use.
1200
1562
  *
1201
- * Contract:
1202
- * - `ensure()` — kick the first load if not started; return the
1203
- * cached promise. On subsequent calls, if `Date.now() -
1204
- * lastScannedAt >= throttleMs` and no refresh is in flight, kick
1205
- * a background revalidate that calls `onLoad(fresh)` when it
1206
- * resolves. The original first-load promise still resolves with
1207
- * the first walk's contents — consumers reading live state
1208
- * elsewhere (React state) see the refreshed catalog.
1209
- * - `refresh()` — force a rescan now, bypassing the throttle.
1210
- * De-duplicates: if a refresh is already in flight, returns the
1211
- * same promise.
1212
- * - `abort()` — cancel any in-flight walk and detach. After this
1213
- * the slot is dead; callers should rotate to a new slot.
1214
- * - `dispose()` — alias of `abort()`, named for symmetry with
1215
- * cleanup callbacks.
1563
+ * Returns an empty object on missing / malformed / non-object files
1564
+ * — same fault-tolerance contract as {@link readKeybindings}.
1565
+ */
1566
+ declare function readKeybindingsRaw(userDir: string): Readonly<Record<string, unknown>>;
1567
+ /**
1568
+ * Resolve the live spec string for each extension-contributed
1569
+ * keybinding. The user's `keybindings.json` is consulted for an
1570
+ * override on the binding's `id`; absent / invalid entries fall back
1571
+ * to the binding's `default`.
1216
1572
  *
1217
- * No state is exposed except through promises + the host's `onLoad`
1218
- * callback; the slot doesn't try to be a store, only a coordinator.
1573
+ * Pass the raw object (from {@link readKeybindingsRaw}) — passing
1574
+ * `KeyBindings` here wouldn't help because the typed map only carries
1575
+ * built-in `KeyAction` entries.
1576
+ *
1577
+ * Returns one entry per input binding, in source order. Empty `spec`
1578
+ * (binding declared `default: ''` and the user didn't bind it) is a
1579
+ * legitimate "unbound" state — the dispatcher never matches an empty
1580
+ * spec, so the action stays accessible via re-binding through
1581
+ * `keybindings.json`.
1219
1582
  */
1220
- /** Walk function — takes an abort signal, returns the discovered list. */
1221
- type Walk<T> = (signal: AbortSignal) => Promise<readonly T[]>;
1222
- /** Notification: a fresh walk has resolved. Host is expected to commit to its own state store. */
1223
- type Commit<T> = (items: readonly T[]) => void;
1224
- interface DiscoverySlotOptions<T> {
1225
- /** Discover function — runs the actual FS / network walk. */
1226
- walk: Walk<T>;
1227
- /** Commit hook — receives every successful walk's result (first + refreshes). */
1228
- onLoad: Commit<T>;
1583
+ declare function resolveExtensionKeybindings(bindings: readonly {
1584
+ id: string;
1585
+ default: string;
1586
+ }[], raw: Readonly<Record<string, unknown>>): ResolvedExtensionKeybinding[];
1587
+ /**
1588
+ * Strip `//` line comments and `/* … *\/` block comments from a JSONC
1589
+ * source string. Stays out of string literals so a `"foo // bar"` value
1590
+ * keeps its slashes. Exported for unit-tests; the only runtime caller
1591
+ * is {@link readKeybindings}.
1592
+ *
1593
+ * Trade-off: this is a tiny state-machine, not a full JSONC parser. It
1594
+ * doesn't understand `\\` outside strings (irrelevant for JSON) and
1595
+ * doesn't track line numbers in error messages. Good enough for a
1596
+ * config file — corruption in production gracefully falls back to the
1597
+ * defaults via the try/catch in `readKeybindings`.
1598
+ */
1599
+ declare function stripJsonComments(input: string): string;
1600
+ //#endregion
1601
+ //#region src/chat/store.d.ts
1602
+ interface TuiState {
1603
+ lastProvider?: ProviderKey;
1604
+ lastSessionId?: string;
1229
1605
  /**
1230
- * Min interval between background refreshes triggered by `ensure()`.
1231
- * Default: 3_000ms. `refresh()` ignores this — it's the user's
1232
- * explicit-intent path.
1606
+ * The host key of the saved server `lastSessionId` lives on (for a server,
1607
+ * its host id: `src/chat/hosts/session-key`), absent for a session on this
1608
+ * computer. Resume reopens it there, and only with remote hosts on. A
1609
+ * `--host` session writes neither field (see `lastSessionId`).
1610
+ * Read from the older `lastSessionHostId` too ({@link loadState}).
1233
1611
  */
1234
- throttleMs?: number;
1235
- /** Optional error sink. Defaults to no-op; useful for `debugLog`. */
1236
- onError?: (err: unknown, phase: 'first-load' | 'refresh') => void;
1612
+ lastSessionHostKey?: string;
1613
+ /** Per-provider last-used model id. */
1614
+ lastModelByProvider?: Partial<Record<ProviderKey, string>>;
1615
+ /**
1616
+ * Last-applied model fallback chain (entry 0 = primary, mirroring
1617
+ * `lastProvider` + `lastModelByProvider`; entries 1+ = fallbacks).
1618
+ * Additive — absent for single-model users and older state files.
1619
+ * Restored on launch only when entry 0 still matches the resolved
1620
+ * primary; entries are re-validated against the live registry.
1621
+ */
1622
+ lastModelChain?: ModelChainEntry[];
1623
+ /**
1624
+ * Per-model last-picked reasoning effort. Keyed by model id (not provider)
1625
+ * so switching the same model across providers preserves the user's
1626
+ * intent. Models without reasoning support are absent from the map.
1627
+ */
1628
+ lastEffortByModel?: Record<string, ThinkingLevel>;
1629
+ /**
1630
+ * Per-model last-picked custom options (e.g. `{ fast: true }`). Keyed by
1631
+ * model id like {@link lastEffortByModel}, so a toggle follows the model
1632
+ * across providers. Models with no options are absent from the map.
1633
+ */
1634
+ lastModelOptionsByModel?: Record<string, Record<string, boolean>>;
1635
+ /**
1636
+ * Last-active agent profile id (see {@link AgentRegistry}). Resumed on
1637
+ * launch so a returning user lands back in the same mode (Build / Plan /
1638
+ * a host profile). Falls back to the host's `defaultAgent` then the first
1639
+ * registry key if the id is no longer registered.
1640
+ */
1641
+ /**
1642
+ * @deprecated No longer read: the agent profile is per-session and fresh
1643
+ * sessions always start in the default. Kept so older state files parse.
1644
+ */
1645
+ lastAgent?: string;
1646
+ /** User-toggled transcript filters. Persisted so they outlive a launch. */
1647
+ settings?: Partial<Settings>;
1237
1648
  }
1238
- interface DiscoverySlot<T> {
1649
+ interface StateStoreApi {
1650
+ load: () => TuiState;
1651
+ save: (state: TuiState) => void;
1652
+ }
1653
+ declare function createStateStore(path: string): StateStoreApi;
1654
+ declare function loadState(path: string): TuiState;
1655
+ declare function saveState(path: string, state: TuiState): void;
1656
+ /**
1657
+ * Load every session and project it to the compact `SessionMeta` shape used by
1658
+ * the picker. Sorted by recency via the underlying store's `list()` contract
1659
+ * (sqlite store returns by `updated_at DESC`).
1660
+ *
1661
+ * Robust to per-row failures: `Promise.allSettled` so a single corrupt or
1662
+ * unreadable row (malformed JSON, partial write, transient I/O error)
1663
+ * doesn't take down the entire picker. Failed rows are silently skipped;
1664
+ * a stderr line is emitted under `ZIDANE_DEBUG` for diagnosis.
1665
+ */
1666
+ declare function listSessionMeta(store: SessionStore, filter?: {
1239
1667
  /**
1240
- * Kick the first load (idempotent). Returns the first-load promise
1241
- * once it exists. Schedules a background refresh if past throttle.
1668
+ * Restrict to sessions belonging to this project. Omit to ignore
1669
+ * the axis; pass `null` to ask for untagged (legacy) sessions
1670
+ * specifically. The TUI defaults to passing the current git root /
1671
+ * cwd here so each project sees only its own conversations.
1242
1672
  */
1243
- ensure: () => Promise<readonly T[]>;
1244
- /** Force-refresh now. De-duped across concurrent callers. */
1245
- refresh: () => Promise<void>;
1246
- /** True if a refresh is currently running. */
1247
- isRefreshing: () => boolean;
1248
- /** Cancel in-flight work + detach. Idempotent. */
1249
- abort: () => void;
1673
+ projectRoot?: string | null;
1674
+ }): Promise<SessionMeta[]>;
1675
+ /** The environment summary for list rows — omitted for local sessions (the common case). */
1676
+ declare function sessionEnvironmentSummary(metadata: Record<string, unknown> | undefined): SessionEnvironmentSummary | undefined;
1677
+ /** {@link SessionHeader} of a fully materialized session. */
1678
+ declare function headerFromData(data: SessionData): SessionHeader;
1679
+ /**
1680
+ * One session's header, without reading its turns when the store keeps a
1681
+ * header index (`getMeta`). A full load when it doesn't, or when the title
1682
+ * still has to be derived from the first message.
1683
+ */
1684
+ declare function getSessionHeader(store: SessionStore, id: string): Promise<SessionHeader | null>;
1685
+ /** Derive a short title from the first user message — returns null when empty. */
1686
+ declare function titleFromTurns(turns: SessionTurn[]): string | null;
1687
+ /**
1688
+ * Display title for a session — preferred sources, in order:
1689
+ *
1690
+ * 1. `metadata.title` if it's a non-empty string (typically set by
1691
+ * the session-details modal's "generate title" action).
1692
+ * 2. {@link titleFromTurns} — the first user message, one-line + clipped.
1693
+ * 3. {@link DEFAULT_SESSION_TITLE} (`'New session'`).
1694
+ *
1695
+ * Total / pure / defensive on non-string metadata values.
1696
+ */
1697
+ declare function deriveSessionTitle(turns: SessionTurn[], metadata?: Record<string, unknown>): string;
1698
+ /**
1699
+ * Replay persisted turns as a viewable transcript. Mirrors the event shape
1700
+ * produced live by the agent hooks so loaded and streaming history render
1701
+ * identically — including subagent ancestry when `runs` is supplied.
1702
+ *
1703
+ * Subagent reconstruction:
1704
+ * - Every turn carries a `runId`. We look that up in `runs` to get the
1705
+ * run's `depth` and tag the resulting events with `{ depth, childId }`
1706
+ * — the same shape the live `child:*` bubble hooks produce.
1707
+ * - We synthesize `spawn-start` / `spawn-end` markers at each child-run
1708
+ * boundary so the transcript reads the same as a live run did
1709
+ * (`🌱 [run-id] task` … child events … `🌳 [run-id] done · tokens`).
1710
+ * - For child runs (`depth > 0`), the user-role "task" text is suppressed
1711
+ * because `spawn-start` already shows it.
1712
+ *
1713
+ * Without `runs` (legacy callers / tests), the function falls back to the
1714
+ * old behavior: depth-0 events with no subagent grouping.
1715
+ */
1716
+ /**
1717
+ * Map a `compact-summary` content block to its `compact-summary` StreamEvent —
1718
+ * the single source of truth for the compaction boundary card. Used by
1719
+ * {@link eventsFromTurns} (reload / TUI live repaint) AND by chat hosts that
1720
+ * surface the boundary live without a full reload (the GUI broadcasts this
1721
+ * from the loop's `compact:end` hook), so the live card and the reloaded card
1722
+ * are byte-identical regardless of who created the marker (manual, the old
1723
+ * host trigger, or loop-native auto-compaction — all share this block shape).
1724
+ */
1725
+ declare function compactSummaryEvent(block: Extract<SessionContentBlock, {
1726
+ type: 'compact-summary';
1727
+ }>, turnId?: string): StreamEvent;
1728
+ declare function eventsFromTurns(turns: SessionTurn[], runs?: readonly SessionRun[]): StreamEvent[];
1729
+ /**
1730
+ * The whole replay, and where each turn's events start in it
1731
+ * (`turnStarts[turns.length]` = the end). A page of turns `[from, to)` is
1732
+ * `events.slice(turnStarts[from], turnStarts[to])`: consecutive pages
1733
+ * concatenate to exactly {@link eventsFromTurns} (exchange separators, run
1734
+ * receipts, subagent markers, tool names and command state as the whole
1735
+ * replay has them), because what a run or the turn before decides lands in
1736
+ * the page of the turn whose replay emitted it.
1737
+ */
1738
+ declare function replayTurns(turns: SessionTurn[], runs: readonly SessionRun[]): {
1739
+ events: StreamEvent[];
1740
+ turnStarts: number[];
1741
+ };
1742
+ declare function toolCallPreview(name: string, input: Record<string, unknown>): string;
1743
+ /**
1744
+ * Update the trailing `'tool'` event matching `callId` so its
1745
+ * `edit.outcomes` reflects the canonical merged outcomes the chat
1746
+ * layer's `tool:transform` hook just computed. Without this, the live
1747
+ * diff badges stay frozen at whatever the gate set (approval-side
1748
+ * outcomes) and never reflect body-side failures from `multi_edit`'s
1749
+ * best-effort run — only a session reload re-attaches the merged
1750
+ * outcomes via {@link eventsFromTurns}.
1751
+ *
1752
+ * Walks back-to-front because the matching event is overwhelmingly the
1753
+ * most recent (we're updating right after the body returned). Returns
1754
+ * the same reference when no match is found (or the event already
1755
+ * carries the target outcomes), so React skips the re-render.
1756
+ */
1757
+ declare function updateToolEventOutcomes(events: readonly StreamEvent[], callId: string, outcomes: readonly EditOutcome[]): StreamEvent[];
1758
+ /** Render tool output as plain text, whether it's a string or structured content. */
1759
+ declare function toolResultText(output: string | ToolResultContent[]): string;
1760
+ /**
1761
+ * Strip the `Tokens: …` line from a spawn tool-result. The spawn-end marker
1762
+ * displayed right above already shows the same stats; keeping the line in the
1763
+ * rendered tool-result body just produces a visible duplicate (and, on
1764
+ * reloaded pre-fix sessions, an *inconsistent* duplicate — the persisted line
1765
+ * uses the old `13 in / 4075 out` shape while the freshly synthesized
1766
+ * spawn-end uses the cache-aware `in 92615 (cache 92602) / 4075 out` shape).
1767
+ *
1768
+ * Display-only: the persisted tool_result content is untouched, so the LLM
1769
+ * still sees the full string in its context window. Anchored to start-of-line
1770
+ * and matches both `Tokens: 13 in / 4075 out` (legacy) and `Tokens: in 13 …`
1771
+ * (post-`formatTokenUsage`) shapes.
1772
+ */
1773
+ declare function stripSpawnTokensLine(text: string): string;
1774
+ /**
1775
+ * Resolve the top margin (in rows) for an event given the one rendered
1776
+ * just before it. Context-aware rules:
1777
+ *
1778
+ * - A `tool` / `tool-result` event right after another
1779
+ * `tool` / `tool-result` collapses to a tight list — call→result
1780
+ * pairs and back-to-back calls read as one logical block.
1781
+ * - Consecutive `task-notification` banners stack tight — a burst of
1782
+ * background-task completions reads as one log column rather than
1783
+ * scattered banners.
1784
+ *
1785
+ * NB: parent-level events (`depth === 0`) following a subagent block
1786
+ * (`depth > 0`) get their normal default margin. An earlier revision
1787
+ * collapsed this transition to 0 on the assumption that the subagent's
1788
+ * `🌳` end-marker provided enough visual separation — it didn't. A
1789
+ * 2-cell emoji on the same row as the close marker doesn't create a
1790
+ * vertical break, and in hide-subagent-output mode the SubagentBlock
1791
+ * box isn't rendered either, so the parent's follow-up markdown was
1792
+ * glued directly against the close line. Subagents should READ like
1793
+ * tool calls: open with breathing room, close tight inside, and open
1794
+ * a clean gap before the parent's next thought — that's the contract
1795
+ * the tool-call → markdown transition uses, mirrored here.
1796
+ *
1797
+ * Renderer-agnostic — TUI uses it as Yoga `marginTop`; a CSS host can
1798
+ * use the same number as the row's top margin in `em` / `rem`.
1799
+ */
1800
+ declare function marginTopFor(event: StreamEvent, previous: StreamEvent | undefined): number;
1801
+ /** Effective context size of the most recent assistant turn — drives the footer indicator. */
1802
+ /**
1803
+ * Walk from the end of `turns` and return the cache-aware input-token total
1804
+ * of the most recent **parent** (depth-0) assistant turn. Subagent turns
1805
+ * are skipped because their context window is the child's, not the parent's
1806
+ * — surfacing a subagent's `usage.input` as the footer's "context used"
1807
+ * after a session ends mid-spawn would be misleading. The next prompt
1808
+ * feeds the parent, so the parent's last view is what matters.
1809
+ *
1810
+ * Zero-context placeholder turns are also skipped. `executeTurn`
1811
+ * synthesizes an assistant turn with `usage: { input: 0, output: 0 }`
1812
+ * when the provider stream throws / aborts before any usage is reported
1813
+ * (see `src/loop.ts`'s catch path) — that turn is purely a shape
1814
+ * contract for resume, not a real context-window measurement. Without
1815
+ * the skip, an abort mid-stream shadows the prior real turn and the
1816
+ * footer reads `ctx 0` on the next session load even though earlier
1817
+ * turns accumulated meaningful context (and `run.cost`).
1818
+ *
1819
+ * `runs` is optional for backwards compatibility (older call sites and
1820
+ * tests that don't have ancestry info). Without it, this falls back to the
1821
+ * pre-fix behavior — depth is unknown so every assistant turn qualifies.
1822
+ */
1823
+ declare function lastContextSizeFromTurns(turns: SessionTurn[], runs?: readonly SessionRun[]): number;
1824
+ /**
1825
+ * Sum provider-reported USD cost across every run in a session. Used to
1826
+ * seed the live cost indicator on session activation. Returns 0 when no
1827
+ * run carries a `cost` (most providers — only OpenRouter reports today).
1828
+ */
1829
+ declare function sumRunCosts(runs: readonly SessionRun[]): number;
1830
+ /** Per-model token/spend totals across a session — see {@link usageByModel}. */
1831
+ interface ModelUsageSummary {
1832
+ /** Provider-echoed model id, or `"unknown"` for turns that never recorded one. */
1833
+ modelId: string;
1834
+ turns: number;
1835
+ /** Fresh (uncached) input tokens. */
1836
+ input: number;
1837
+ output: number;
1838
+ cacheRead: number;
1839
+ cacheCreation: number;
1840
+ /** Summed USD cost across turns that carried one (provider-reported or estimated). */
1841
+ cost: number;
1842
+ /**
1843
+ * `false` when at least one turn had no cost figure (no published rate
1844
+ * for the model) — `cost` is then a floor, not a total.
1845
+ */
1846
+ costComplete: boolean;
1250
1847
  }
1251
- declare function createDiscoverySlot<T>(options: DiscoverySlotOptions<T>): DiscoverySlot<T>;
1848
+ /**
1849
+ * Aggregate per-model token + spend totals across every run in a session.
1850
+ * Parent and subagent runs alike: each turn carries its own `modelId`, so
1851
+ * cost-tiered delegation (cheap spawn workers) shows up as separate rows
1852
+ * priced at their own rates. Sorted by cost (desc), then total tokens, so
1853
+ * the biggest spender reads first.
1854
+ */
1855
+ declare function usageByModel(runs: readonly SessionRun[]): ModelUsageSummary[];
1252
1856
  //#endregion
1253
- //#region src/chat/editor-command.d.ts
1857
+ //#region src/chat/config.d.ts
1254
1858
  /**
1255
- * `$EDITOR` / `$VISUAL` command parsing.
1256
- *
1257
- * These variables conventionally hold a command LINE, not a bare executable —
1258
- * `code --wait`, `cursor --wait`, `subl -w`, `emacsclient -nw` are all normal
1259
- * values. Spawning the whole string as argv[0] looks for a file literally named
1260
- * `cursor --wait` and fails with ENOENT, so the flags have to be split off.
1261
- *
1262
- * Kept separate from `app.tsx` (and free of `node:child_process`) so the
1263
- * tokenizer is unit-testable without pulling in the TUI.
1859
+ * Provider registry — a map keyed by `descriptor.key`. Passed verbatim to the
1860
+ * chat engine; there is no implicit merge with built-ins. Hosts that want the
1861
+ * four default providers spread {@link BUILTIN_PROVIDERS}; hosts that only
1862
+ * want their own pass exactly their own.
1264
1863
  */
1864
+ type ProviderRegistry = Readonly<Record<string, ProviderDescriptor>>;
1265
1865
  /**
1266
- * Split an editor command line into `[executable, ...args]`.
1267
- *
1268
- * Whitespace-separated, with single and double quotes honored so an editor
1866
+ * Options accepted by `resolveConfig()` (and, transitively, by `runTui()` and
1867
+ * any future GUI launcher). Every field is optional — defaults boot a working
1868
+ * chat against the built-in providers with sessions stored under `~/.zidane/`.
1869
+ */
1870
+ interface ChatOptions {
1871
+ /**
1872
+ * Directory name used under `storageDir` for sessions + state.
1873
+ * Default: `'.zidane'` → `~/.zidane/sessions.db` + `~/.zidane/state.json`.
1874
+ * Pass e.g. `'.myapp'` for `~/.myapp/...`, or `'myapp'` for a visible dir.
1875
+ */
1876
+ prefix?: string;
1877
+ /**
1878
+ * Storage root. Defaults to the user's home directory. Combined with
1879
+ * `prefix` to form the data directory.
1880
+ */
1881
+ storageDir?: string;
1882
+ /**
1883
+ * Provider registry. Each value is a {@link ProviderDescriptor} carrying
1884
+ * label + factory + credential metadata. **No automatic merge** — hosts
1885
+ * pass exactly what they want.
1886
+ *
1887
+ * - Omitted → {@link BUILTIN_PROVIDERS} (anthropic + openai + openrouter + cerebras).
1888
+ * - `providers: BUILTIN_PROVIDERS` → same as omitted, but explicit.
1889
+ * - `providers: { anthropic: anthropicDescriptor }` → only Anthropic.
1890
+ * - `providers: { ...BUILTIN_PROVIDERS, mine: customDescriptor }` → built-ins + custom.
1891
+ * - `providers: { mine: customDescriptor }` → custom-only, **no built-ins**.
1892
+ */
1893
+ providers?: ProviderRegistry;
1894
+ /**
1895
+ * Legacy single-agent preset (tools, system prompt, behavior). When set
1896
+ * **without** {@link ChatOptions.agents}, the resolver wraps this preset
1897
+ * into a one-entry {@link AgentRegistry} keyed `default` so existing
1898
+ * callers keep working and the picker is effectively hidden.
1899
+ *
1900
+ * Prefer `agents` for any multi-profile setup. Passing both throws.
1901
+ */
1902
+ preset?: Preset;
1903
+ /**
1904
+ * Agent profile registry — keyed by `profile.id`. Each profile bundles a
1905
+ * preset with display metadata (label, description, accent). The TUI
1906
+ * shows a picker (Ctrl+A) when more than one profile is registered.
1907
+ *
1908
+ * - Omitted (and `preset` omitted) → {@link BUILTIN_AGENTS} (Build + Plan).
1909
+ * - Omitted but `preset` provided → single-entry registry around the preset.
1910
+ * - Provided → host owns the registry; pass exactly what you want.
1911
+ *
1912
+ * Hosts mixing built-ins with their own typically spread:
1913
+ * `agents: { ...BUILTIN_AGENTS, debug: myDebugProfile }`.
1914
+ */
1915
+ agents?: AgentRegistry;
1916
+ /**
1917
+ * Id of the profile every fresh session (and the initial launch) starts
1918
+ * in. Resolved against `agents`; unknown ids fall through to
1919
+ * `DEFAULT_AGENT_ID`, then the registry's first key. Sessions remember
1920
+ * the profile they were created with; this is never overridden by a
1921
+ * previously picked profile.
1922
+ */
1923
+ defaultAgent?: string;
1924
+ /**
1925
+ * Session store — either an instance or a factory called with the
1926
+ * resolved storage paths. **Required** at the chat layer so this module
1927
+ * stays platform-agnostic (no `bun:sqlite` baked in). Terminal hosts use
1928
+ * `createTuiStore` from `zidane/session/sqlite`; a future GUI host can
1929
+ * supply a different adapter (IndexedDB, remote API, in-memory) without
1930
+ * touching this layer.
1931
+ *
1932
+ * ```ts
1933
+ * import { createTuiStore } from 'zidane/session/sqlite'
1934
+ *
1935
+ * // Either pass a factory…
1936
+ * resolveConfig({ store: paths => createTuiStore(paths.db) })
1937
+ *
1938
+ * // …or an instance built ahead of time.
1939
+ * const store = createTuiStore('/path/to/sessions.db')
1940
+ * resolveConfig({ store })
1941
+ * ```
1942
+ */
1943
+ store: SessionStore | ((paths: ResolvedPaths) => SessionStore);
1944
+ /**
1945
+ * Project-scoped session storage. Off by default; when it resolves
1946
+ * to `true` and the launch happens inside a git repo, zidane
1947
+ * auto-creates `<git-root>/.{prefix}/` and stores `sessions.db` +
1948
+ * `state.json` there — so sessions, mcps.json, and skills all live
1949
+ * alongside the project they belong to. Credentials and the
1950
+ * cross-project safelist stay at `~/.{prefix}/` regardless.
1951
+ *
1952
+ * Precedence (most → least specific): `options.projectDb` >
1953
+ * `ZIDANE_PROJECT_DB` env var (`0`/`false`/`off`/`no` → off,
1954
+ * `1`/`true`/`on`/`yes` → on) > `~/.{prefix}/config.json`
1955
+ * `projectDb` > `false`. Note the host option beats the env var —
1956
+ * setting `ZIDANE_PROJECT_DB` has no effect when the host passes an
1957
+ * explicit `projectDb`.
1958
+ *
1959
+ * When it resolves to `false`, OR when not inside a git repo,
1960
+ * everything lives at `~/.{prefix}/` — the classic single-store
1961
+ * layout.
1962
+ */
1963
+ projectDb?: boolean;
1964
+ /**
1965
+ * Override for the project root. Defaults to `process.cwd()`. Hosts
1966
+ * embedding the TUI from a non-CLI context (tests, daemons) can pin
1967
+ * the cwd here so the git-root walk produces a stable result. Has no
1968
+ * effect when `projectDb` resolves to `false`.
1969
+ */
1970
+ cwd?: string;
1971
+ /**
1972
+ * Optional one-shot launch request used by CLI shells such as `zidane run`.
1973
+ * The TUI opens/creates the requested session and submits the prompt after
1974
+ * the first provider/session activation, while keeping the normal renderer
1975
+ * and persistence behavior.
1976
+ */
1977
+ launch?: StartLocalOptions;
1978
+ /**
1979
+ * Auto-update wiring. When set, the App quietly checks the npm registry
1980
+ * for a newer release of `packageName` (matching the host's package),
1981
+ * caches the result for a day under `userDir/`, and surfaces a passive
1982
+ * `↑ vX.Y.Z` chip in the footer when one is available.
1983
+ *
1984
+ * Consumers wrapping `runTui` for their own CLI inline their own
1985
+ * `package.json` version at build time and pass it here. The bundled
1986
+ * `zidane-tui` binary wires this automatically in `tui/src/cli.ts`.
1987
+ *
1988
+ * Defaults to `null` — no auto-update plumbing, no network calls,
1989
+ * no chip.
1990
+ */
1991
+ autoUpdate?: AutoUpdateConfig | null;
1992
+ /**
1993
+ * Pre-built {@link ExtensionRegistry}. The `resolveConfig` surface is
1994
+ * synchronous; extension `setup()` can be async, so the host must
1995
+ * build the registry ahead of time and pass it here verbatim.
1996
+ *
1997
+ * Typical wiring (matches `runTui`):
1998
+ *
1999
+ * ```ts
2000
+ * import {
2001
+ * buildExtensionRegistry,
2002
+ * discoverExtensions,
2003
+ * inlineLoadedExtension,
2004
+ * resolveConfig,
2005
+ * } from 'zidane'
2006
+ *
2007
+ * const registry = await buildExtensionRegistry([
2008
+ * // Project-scoped extensions run arbitrary local code — they stay
2009
+ * // off unless the host establishes trust explicitly (or the user
2010
+ * // sets ZIDANE_TRUST_PROJECT_EXTENSIONS=1).
2011
+ * ...await discoverExtensions({ cwd, prefix, trustProjectExtensions: hostTrustsProject }),
2012
+ * ...inlineDefinitions.map(inlineLoadedExtension),
2013
+ * ])
2014
+ * const config = resolveConfig({ ...opts, extensionRegistry: registry })
2015
+ * ```
2016
+ *
2017
+ * Omitted → empty registry (no contributions).
2018
+ */
2019
+ extensionRegistry?: ExtensionRegistry;
2020
+ /**
2021
+ * Extra extension scan roots the host used (or wants runtime re-discovery
2022
+ * to use). Each path is a root containing extension dirs/packages:
2023
+ * `<root>/<name>/index.ts`. `runTui` wires this from
2024
+ * `ZIDANE_EXTENSION_DIRS` when omitted.
2025
+ */
2026
+ extensionScanPaths?: readonly string[];
2027
+ /**
2028
+ * Extra Agent Plugin scan roots. Each path is a root containing plugin
2029
+ * directories: `<root>/<name>/plugin.json`. `runTui` wires this from
2030
+ * `ZIDANE_PLUGIN_DIRS` when omitted.
2031
+ *
2032
+ * Appended after the four default roots, so a project or user plugin still
2033
+ * wins a name collision against a dev root.
2034
+ */
2035
+ pluginScanPaths?: readonly string[];
2036
+ /**
2037
+ * Whether project-scoped extensions are trusted to run their (arbitrary,
2038
+ * local) code. Off by default unless the embedding host has established
2039
+ * project trust; falls back to `ZIDANE_TRUST_PROJECT_EXTENSIONS=1`.
2040
+ *
2041
+ * Surfaced on {@link ResolvedConfig.trustProjectExtensions} so a host
2042
+ * that re-discovers extensions at runtime (e.g. the TUI on a working-
2043
+ * directory change) reuses the same trust decision as boot.
2044
+ */
2045
+ trustProjectExtensions?: boolean;
2046
+ }
2047
+ /**
2048
+ * Host-supplied auto-update settings. The check itself is gated on
2049
+ * `Settings.checkForUpdates`; passing a config wires the React plumbing.
2050
+ *
2051
+ * Hosts that want to publish their own consumer-facing package can pass
2052
+ * different values without forking anything — the registry / channel /
2053
+ * platform-package-prefix are all knobs.
2054
+ */
2055
+ interface AutoUpdateConfig {
2056
+ /** Top-level npm package the user installs (`zidane-tui`, `my-app`, …). */
2057
+ packageName: string;
2058
+ /** Inlined at build time — usually `pkg.version`. */
2059
+ currentVersion: string;
2060
+ /** dist-tag. Defaults to `'latest'`. */
2061
+ channel?: string;
2062
+ /** Registry base URL. Defaults to `'https://registry.npmjs.org'`. */
2063
+ registry?: string;
2064
+ /**
2065
+ * Override the read-through cache lifetime in ms for the in-TUI hook.
2066
+ * Defaults to `0` — every TUI launch fetches fresh, so the chip
2067
+ * never lies. Pass a positive value (e.g. `3_600_000` for 1 h) to
2068
+ * throttle the registry-poll rate at the cost of staleness.
2069
+ *
2070
+ * The on-disk cache is always WRITTEN, regardless of this value, so
2071
+ * `zidane upgrade` and other tooling read paths benefit immediately.
2072
+ */
2073
+ cacheTtlMs?: number;
2074
+ /**
2075
+ * Platform-package prefix used by the in-place self-update flow when
2076
+ * resolving the per-arch tarball name. Defaults to `'<packageName>-'`
2077
+ * (i.e. `zidane-tui-darwin-arm64` when `packageName === 'zidane-tui'`).
2078
+ * Override only when your platform packages don't follow the `<name>-<key>`
2079
+ * convention.
2080
+ */
2081
+ platformPackagePrefix?: string;
2082
+ /**
2083
+ * Executable name on PATH for `packageName`, used to probe the version
2084
+ * installed on disk (the `↻ vX` "restart to update" chip). Defaults to
2085
+ * `packageName`; the TUI passes `'zidane'` for `zidane-tui`.
2086
+ */
2087
+ binName?: string;
2088
+ }
2089
+ /** Resolved on-disk layout produced by {@link resolveConfig}. */
2090
+ interface ResolvedPaths {
2091
+ /**
2092
+ * Effective data root — equals `userDir` (configDir) when project
2093
+ * mode is off, `projectDir` when it's on. Kept for callers that
2094
+ * wrote against the pre-project-db API; new code should reference
2095
+ * the explicit slots (`configDir` / `dataDir` / `cacheDir` /
2096
+ * `stateDir`) or `projectDir` / `db` / `state` instead.
2097
+ */
2098
+ dir: string;
2099
+ /**
2100
+ * User-scoped config root — credentials, mcp-credentials, keybindings,
2101
+ * `config.json`, `mcps.json`, skills. Legacy alias for `configDir`;
2102
+ * existing call sites still reference `userDir`. In XDG mode this
2103
+ * lands at `$XDG_CONFIG_HOME/zidane`; in legacy mode the four slots
2104
+ * (config / data / state / cache) collapse to a single
2105
+ * `<storageDir>/<prefix>` directory.
2106
+ */
2107
+ userDir: string;
2108
+ /**
2109
+ * Project-scoped data root. Set to `<git-root>/<prefix>` when project
2110
+ * mode resolved to ON and a git root was found. `null` otherwise.
2111
+ */
2112
+ projectDir: string | null;
2113
+ /** Resolved `sessions.db` path. Lives under `projectDir` when active, else `dataDir`. */
2114
+ db: string;
2115
+ /** Resolved `state.json` path. Lives under `projectDir` when active, else `stateDir`. */
2116
+ state: string;
2117
+ /**
2118
+ * XDG config slot — `$XDG_CONFIG_HOME/zidane` on a fresh Linux install,
2119
+ * `<storageDir>/<prefix>` (e.g. `~/.zidane/`) in legacy mode. Holds
2120
+ * user-editable surface (`credentials.json`, `mcps.json`, …).
2121
+ */
2122
+ configDir: string;
2123
+ /**
2124
+ * XDG data slot — `$XDG_DATA_HOME/zidane` on Linux, single-dir
2125
+ * elsewhere. Holds long-lived chat history (`sessions.db`).
2126
+ */
2127
+ dataDir: string;
2128
+ /**
2129
+ * XDG state slot — `$XDG_STATE_HOME/zidane` on Linux, single-dir
2130
+ * elsewhere. Holds per-machine UI bookkeeping (`state.json`).
2131
+ */
2132
+ stateDir: string;
2133
+ /**
2134
+ * XDG cache slot — `$XDG_CACHE_HOME/zidane` on Linux, single-dir
2135
+ * elsewhere. Holds regenerable data: persisted tool results,
2136
+ * background-task logs, auto-update registry cache.
2137
+ */
2138
+ cacheDir: string;
2139
+ /**
2140
+ * Which resolution branch produced the layout. `'xdg'` when split
2141
+ * across the four XDG dirs; one of `'legacy-explicit'`,
2142
+ * `'legacy-existing'`, or `'default'` when the slots collapse to a
2143
+ * single directory. Surfaced for diagnostics + tests.
2144
+ */
2145
+ storageMode: StorageMode;
2146
+ }
2147
+ interface ResolvedConfig {
2148
+ prefix: string;
2149
+ storageDir: string;
2150
+ paths: ResolvedPaths;
2151
+ providers: ProviderRegistry;
2152
+ /**
2153
+ * Resolved profile registry. Always non-empty; the resolver throws when
2154
+ * the host passes an empty `agents` map (same contract as `providers`).
2155
+ */
2156
+ agents: AgentRegistry;
2157
+ /** Id of the profile to activate on mount. Guaranteed to key into `agents`. */
2158
+ initialAgentId: string;
2159
+ store: SessionStore;
2160
+ stateStore: StateStoreApi;
2161
+ /** Lookup by `ProviderKey` returning the available models for the picker. */
2162
+ modelsFor: (key: ProviderKey) => readonly ModelInfo[];
2163
+ initialState: TuiState;
2164
+ initialSettings: Partial<Settings>;
2165
+ resumeProvider: ProviderAuth | null;
2166
+ initialPicked: Picked | null;
2167
+ /**
2168
+ * Effective keybindings — defaults merged with the user-level
2169
+ * `<userDir>/keybindings.json` (when present). Always fully-resolved
2170
+ * so consumers can treat the record as total. See `./keybindings.ts`
2171
+ * for the action catalog + spec format.
2172
+ */
2173
+ keybindings: KeyBindings;
2174
+ /**
2175
+ * Resolved extension-contributed keybindings. Each entry pairs the
2176
+ * extension's binding definition with the user's effective spec
2177
+ * (override from `keybindings.json` when present, default
2178
+ * otherwise). The TUI dispatcher iterates these alongside the
2179
+ * built-in action catalog; the GUI host can do the same.
2180
+ *
2181
+ * Empty when no extension contributed a keybinding.
2182
+ */
2183
+ extensionKeybindings: readonly ResolvedExtensionKeybinding[];
2184
+ /**
2185
+ * Resolved auto-update knobs (forwarded verbatim from `ChatOptions.autoUpdate`)
2186
+ * — `null` when the host didn't wire one. The TUI feeds this into
2187
+ * `useUpdateCheck` so the footer chip + the `zidane upgrade` subcommand
2188
+ * share a single source of truth.
2189
+ */
2190
+ autoUpdate: AutoUpdateConfig | null;
2191
+ /**
2192
+ * Merged extension contributions — the registry passed via
2193
+ * {@link ChatOptions.extensionRegistry}, returned verbatim, or
2194
+ * {@link EMPTY_EXTENSION_REGISTRY} when the host didn't pass one.
2195
+ * `resolveConfig` is synchronous, so it never runs `setup()` itself;
2196
+ * the host pre-builds the registry with `buildExtensionRegistry`.
2197
+ *
2198
+ * Renderer-side wiring reads `tuiContributions` / `guiContributions`
2199
+ * to mount keybindings / footer hints / modals / panels. The
2200
+ * `extensionPreset` field below carries the agent-side surface
2201
+ * (tools / hooks / mcpServers / skills) ready to spread.
2202
+ */
2203
+ extensions: ExtensionRegistry;
2204
+ /**
2205
+ * Extra extension scan roots used at boot. Runtime re-discovery (TUI cwd
2206
+ * changes, future hosts) feeds these back into the shared discovery helper so
2207
+ * local/dev package roots stay active after the initial launch.
2208
+ */
2209
+ extensionScanPaths: readonly string[];
2210
+ /**
2211
+ * Extra Agent Plugin scan roots used at boot. Threaded into the discovery
2212
+ * shell so a dev root stays active across project/cwd changes, same as
2213
+ * {@link ResolvedConfig.extensionScanPaths}.
2214
+ */
2215
+ pluginScanPaths: readonly string[];
2216
+ /**
2217
+ * Single {@link Preset} folded together from every extension's
2218
+ * agent-side contributions. Hosts spread it into `createAgent()`
2219
+ * alongside the active profile's preset:
2220
+ *
2221
+ * ```ts
2222
+ * const profile = composePresets(extensionPreset, agentProfile.preset)
2223
+ * const agent = createAgent({ ...profile, provider })
2224
+ * ```
2225
+ *
2226
+ * Extension hooks are bucketed per event so every handler fires;
2227
+ * tools and mcpServers compose with profile contributions via the
2228
+ * usual `composePresets` rules. System-prompt fragments land as a
2229
+ * trailing block on `preset.system` (host profiles that re-derive
2230
+ * `system` per session — Build / Plan — append the fragments via
2231
+ * the `system:transform` hook so the live system always carries
2232
+ * them).
2233
+ */
2234
+ extensionPreset: Preset;
2235
+ /**
2236
+ * System-prompt fragments contributed by extensions, in registration
2237
+ * order. Surfaced separately from {@link extensionPreset} so hosts
2238
+ * that re-derive `system` per session (Build / Plan in the TUI) can
2239
+ * append them on `system:transform` instead of trying to merge two
2240
+ * `preset.system` strings.
2241
+ */
2242
+ extensionSystemFragments: readonly string[];
2243
+ /**
2244
+ * Spawnable subagents (adds) contributed by extensions, keyed by name.
2245
+ * Surfaced separately from {@link extensionPreset} (like
2246
+ * {@link extensionSystemFragments}) because subagents don't ride the
2247
+ * preset path — the host merges them onto the spawn tool's base
2248
+ * registry via `spawn.withSubagents(...)` at agent build time.
2249
+ */
2250
+ extensionSubagents: SubagentRegistry;
2251
+ /**
2252
+ * Subagent edits contributed by extensions, keyed by target name and
2253
+ * composed in registration order. Applied (after the adds above) by
2254
+ * the host's `spawn.withSubagents(...)` call so extensions can edit or
2255
+ * delete host- / extension-provided subagents by name.
2256
+ */
2257
+ extensionSubagentOverrides: Record<string, SubagentOverride>;
2258
+ /**
2259
+ * Merged extension-contributed config categories, keyed by category id.
2260
+ * Surfaced (alongside {@link extensionConfigItems}) so the TUI can read
2261
+ * the schema in-process — same way it reads `extensions.themes` — and
2262
+ * the GUI's `extensions:configSchema` IPC can serialize it.
2263
+ */
2264
+ extensionConfigCategories: Readonly<Record<string, ExtensionConfigCategory>>;
2265
+ /**
2266
+ * Merged extension-contributed config items, namespaced
2267
+ * `extName → itemKey → item`. Each item carries its resolved
2268
+ * `category`; values are read default-first via the core resolver.
2269
+ */
2270
+ extensionConfigItems: Readonly<Record<string, Readonly<Record<string, StoredExtensionConfigItem>>>>;
2271
+ /**
2272
+ * Resolved project-extension trust decision (option + env fallback).
2273
+ * Hosts that re-discover extensions at runtime feed this back into
2274
+ * {@link discoverAndBuildExtensionRegistry} so the rebuild trusts the
2275
+ * same scopes boot did.
2276
+ */
2277
+ trustProjectExtensions: boolean;
2278
+ /**
2279
+ * Raw user `keybindings.json` object (post-JSONC-strip). Surfaced so a
2280
+ * host re-resolving {@link extensionKeybindings} at runtime (after an
2281
+ * extension registry rebuild) can reuse the same override source
2282
+ * without a second disk read.
2283
+ */
2284
+ rawKeybindings: Readonly<Record<string, unknown>>;
2285
+ /** One-shot launch request forwarded from {@link ChatOptions.launch}. */
2286
+ launch: StartLocalOptions | null;
2287
+ }
2288
+ /**
2289
+ * Resolve user options into a fully-bound runtime config.
2290
+ *
2291
+ * NOT pure: besides disk reads, this mutates `process.env` so the harness
2292
+ * providers pick up the chat engine's credential store — it sets
2293
+ * `ZIDANE_CREDENTIALS_PATH` and reconciles stored API keys / OAuth entries
2294
+ * into env via `applyApiKeyEnv` (which can overwrite or delete ambient keys
2295
+ * like `ANTHROPIC_API_KEY`; see its JSDoc for the precedence rationale).
2296
+ */
2297
+ declare function resolveConfig(options: ChatOptions): ResolvedConfig;
2298
+ /**
2299
+ * Compute the on-disk layout for a single TUI session. Public so hosts
2300
+ * (CLI tools, tests, scripted bootstraps) can ask "where will sessions
2301
+ * land?" without going through `resolveConfig`'s full machinery.
2302
+ *
2303
+ * Storage-slot resolution (config / data / state / cache) is delegated
2304
+ * to {@link resolveStorageDirs}. In legacy modes the four slots collapse
2305
+ * to a single `<storageDir>/<prefix>` directory; in XDG mode they fan
2306
+ * out into `$XDG_CONFIG_HOME/zidane`, `$XDG_DATA_HOME/zidane`,
2307
+ * `$XDG_STATE_HOME/zidane`, `$XDG_CACHE_HOME/zidane`. See the module
2308
+ * doc on `./xdg.ts` for the full precedence chain.
2309
+ *
2310
+ * Decision logic for project mode (least → most specific):
2311
+ *
2312
+ * 1. Default: OFF — sessions live in the user-scoped `dataDir`
2313
+ * and get scoped per-project via the `SessionData.projectRoot` tag.
2314
+ * 2. `<configDir>/config.json` `projectDb` flag (when present).
2315
+ * 3. `ZIDANE_PROJECT_DB` env var (`'0'` / `'false'` / `'off'` → off,
2316
+ * anything else true if set).
2317
+ * 4. `options.projectDb` (host call site).
2318
+ *
2319
+ * Project mode activates only when the resolution above lands on
2320
+ * `true` AND a git repo is found above `cwd`. Outside git or with the
2321
+ * flag off, sessions + state stay at the user-scoped slots.
2322
+ *
2323
+ * Auto-creates the project `.{prefix}/` directory when mode resolves
2324
+ * to ON (silent — mirrors `ensureStateDir`). User slots are created
2325
+ * lazily by the state-store + credentials writers; we don't pre-touch
2326
+ * them here so a read-only invocation stays read-only.
2327
+ */
2328
+ declare function resolveStoragePaths(opts: {
2329
+ prefix: string;
2330
+ /** Explicit storage root. When set, forces legacy-explicit mode. */
2331
+ storageDir?: string;
2332
+ cwd: string;
2333
+ /** Host override for project mode. Beats the user-config + env-var sources. */
2334
+ projectDb?: boolean;
2335
+ }): ResolvedPaths;
2336
+ //#endregion
2337
+ //#region src/chat/config-context.d.ts
2338
+ declare function ConfigProvider({ config, children }: {
2339
+ config: ResolvedConfig;
2340
+ children: ReactNode;
2341
+ }): import("react").JSX.Element;
2342
+ declare function useConfig(): ResolvedConfig;
2343
+ //#endregion
2344
+ //#region src/chat/credentials.d.ts
2345
+ interface ApiKeyCredential {
2346
+ kind: 'apikey';
2347
+ /**
2348
+ * Bearer API key. May be empty when the descriptor has no `envKey` and
2349
+ * authenticates entirely via {@link customFields} (e.g. the `local`
2350
+ * provider, where the auth signal is the configured base URL).
2351
+ */
2352
+ value: string;
2353
+ /**
2354
+ * Free-form fields collected by the wizard from the descriptor's
2355
+ * `customFields` definitions. Keys match the descriptor field's `key`.
2356
+ * Mirrored into `process.env[field.envVar]` by `applyApiKeyEnv` so the
2357
+ * provider's factory can read them via the existing env-var convention.
2358
+ */
2359
+ customFields?: Record<string, string>;
2360
+ }
2361
+ interface OAuthCredential {
2362
+ kind: 'oauth';
2363
+ access: string;
2364
+ refresh?: string;
2365
+ expires?: number;
2366
+ /**
2367
+ * User-given account name ("work", "personal"). Optional — absent reads as
2368
+ * `DEFAULT_OAUTH_ACCOUNT_LABEL` (see `oauthAccountLabel`). Survives token
2369
+ * refresh because the refresh path spreads the stored entry into the rotated
2370
+ * one (see `resolveOAuthApiKey`).
2371
+ */
2372
+ label?: string;
2373
+ /** Provider-specific extras (e.g. OpenAI Codex `accountId`). */
2374
+ [extra: string]: unknown;
2375
+ }
2376
+ type ProviderCredential = ApiKeyCredential | OAuthCredential;
2377
+ /** One stored credential of a provider, addressed by the file key it lives at. */
2378
+ interface StoredCredential {
2379
+ /** File key — the selection identity for promote / remove / relabel. */
2380
+ key: string;
2381
+ credential: ProviderCredential;
2382
+ /** True for the canonical slot, i.e. the credential readers resolve. */
2383
+ active: boolean;
2384
+ }
2385
+ /** Top-level shape of `credentials.json` — keys are credential-file keys. */
2386
+ type CredentialsFile = Record<string, ProviderCredential>;
2387
+ /**
2388
+ * Resolve the credentials file path given the resolved TUI data directory
2389
+ * (typically `~/.zidane`, i.e. `config.paths.dir`).
2390
+ *
2391
+ * Matches the convention used elsewhere in the TUI (sessions.db, state.json)
2392
+ * so a single `ZIDANE_STORAGE_DIR` override moves the entire data root.
2393
+ */
2394
+ declare function credentialsPath(dataDir: string): string;
2395
+ /**
2396
+ * Read credentials from disk.
2397
+ *
2398
+ * Returns `{}` when the file is missing or corrupt (last-ditch tolerance —
2399
+ * a hand-edit gone wrong shouldn't lock the user out of re-authing). On first
2400
+ * call with no file present, attempts a migration from `cwd/.credentials.json`
2401
+ * (the legacy location used by `bun run auth`).
2402
+ */
2403
+ declare function readCredentials(dataDir: string): CredentialsFile;
2404
+ /** Read a single provider's credential (translating via the descriptor). */
2405
+ declare function readProviderCredential(dataDir: string, descriptor: ProviderDescriptor): ProviderCredential | undefined;
2406
+ /**
2407
+ * Both stored credentials for a provider: `active` is the one every reader
2408
+ * resolves, `alternate` is the stored-but-not-preferred one (if any).
2409
+ *
2410
+ * Predates multi-account and only sees the `#alt` slot; use
2411
+ * {@link readProviderCredentials} to list every stored account.
2412
+ */
2413
+ declare function readProviderCredentialPair(dataDir: string, descriptor: ProviderDescriptor): {
2414
+ active?: ProviderCredential;
2415
+ alternate?: ProviderCredential;
2416
+ };
2417
+ /**
2418
+ * Every stored credential of a provider, active first.
2419
+ *
2420
+ * Tolerant of any kind in any slot: `#alt` is meant for the API key and
2421
+ * `#account:N` for OAuth accounts, but a file written by an older build (which
2422
+ * parked the OAuth alternate in `#alt`) must still list everything it holds.
2423
+ */
2424
+ declare function readProviderCredentials(dataDir: string, descriptor: ProviderDescriptor): StoredCredential[];
2425
+ /** Pure form of {@link readProviderCredentials} over an already-read file. */
2426
+ declare function storedCredentialsOf(all: CredentialsFile, fileKey: string): StoredCredential[];
2427
+ /**
2428
+ * Write credentials atomically (write-then-rename) with mode 0o600.
2429
+ *
2430
+ * Atomic on the same filesystem — readers either see the previous file or the
2431
+ * new one, never a half-written intermediate. Creates the parent dir if needed
2432
+ * (first launch on a fresh machine: `~/.zidane/` may not exist yet).
2433
+ */
2434
+ declare function writeCredentials(dataDir: string, creds: CredentialsFile): void;
2435
+ interface SetProviderCredentialOptions {
2436
+ /**
2437
+ * File key of the stored credential this one REPLACES (a re-auth of a known
2438
+ * account). The slot keeps its position — replacing the active one keeps it
2439
+ * active. When the key is no longer stored (removed from another window
2440
+ * mid-login), the credential is added as a new active account instead.
2441
+ */
2442
+ replaceKey?: string;
2443
+ }
2444
+ /**
2445
+ * Store a credential as the provider's preferred one.
2446
+ *
2447
+ * The previous active credential is demoted, not destroyed: authenticating
2448
+ * with OAuth used to silently delete a working API key (and vice versa), and a
2449
+ * second OAuth login used to overwrite the first account. An API key moves to
2450
+ * {@link alternateCredKey}; an OAuth grant moves to the next free account slot.
2451
+ * Replacing an API key with an API key is a plain overwrite — there is only
2452
+ * ever one — and so is an OAuth login that names its predecessor via
2453
+ * `replaceKey`.
2454
+ */
2455
+ declare function setProviderCredential(dataDir: string, descriptor: ProviderDescriptor, cred: ProviderCredential, opts?: SetProviderCredentialOptions): void;
2456
+ /**
2457
+ * Outcome of {@link setPreferredCredential}.
2458
+ *
2459
+ * Three states rather than a boolean because the two non-promoting cases need
2460
+ * opposite handling: `already-active` is a harmless idempotent call (the UI
2461
+ * repaints and moves on), while `not-stored` means the caller asked for a
2462
+ * credential that does not exist and should say so.
2463
+ */
2464
+ type PreferCredentialResult = 'promoted' | 'already-active' | 'not-stored';
2465
+ /**
2466
+ * What {@link setPreferredCredential} promotes: a stored credential by file
2467
+ * key (from {@link readProviderCredentials} / `AuthMethod.key`), or the first
2468
+ * stored credential of a kind — the pre-multi-account form, still right when
2469
+ * the caller only knows "switch to the API key".
2470
+ */
2471
+ type PreferredCredentialTarget = ProviderCredential['kind'] | {
2472
+ key: string;
2473
+ };
2474
+ /**
2475
+ * Make one of the provider's stored credentials the preferred one.
2476
+ *
2477
+ * Swaps the target into the canonical slot and parks the previous active one
2478
+ * (see {@link parkDemoted}). Returns which of the three outcomes happened so a
2479
+ * caller cannot report a swap that provably did not occur — the write is
2480
+ * silent either way, and a success-shaped `void` was a trap for any caller that
2481
+ * trusted it instead of refetching.
2482
+ */
2483
+ declare function setPreferredCredential(dataDir: string, descriptor: ProviderDescriptor, target: PreferredCredentialTarget): PreferCredentialResult;
2484
+ /**
2485
+ * Drop ONE stored credential. Removing the active one promotes a survivor —
2486
+ * another credential of the same kind when there is one (a second OAuth
2487
+ * account), else whatever is left — so the provider does not go dark while it
2488
+ * still holds a working credential. Returns false when `key` was not stored.
2489
+ */
2490
+ declare function removeStoredCredential(dataDir: string, descriptor: ProviderDescriptor, key: string): boolean;
2491
+ /**
2492
+ * Name a stored OAuth account. An empty label drops the field, which reads
2493
+ * back as {@link DEFAULT_OAUTH_ACCOUNT_LABEL}. Returns false when `key` holds
2494
+ * no OAuth credential.
2495
+ */
2496
+ declare function setCredentialLabel(dataDir: string, descriptor: ProviderDescriptor, key: string, label: string): boolean;
2497
+ /**
2498
+ * Sign a provider out — drops EVERY stored credential.
2499
+ *
2500
+ * "Signed out" has to mean the provider resolves nothing afterwards, which is
2501
+ * what this has always guaranteed and what callers named it for; promoting a
2502
+ * survivor instead would silently leave the provider authenticated through
2503
+ * another source. Callers that want that behavior use
2504
+ * {@link removeStoredCredential} on the one slot they mean.
2505
+ */
2506
+ declare function removeProviderCredential(dataDir: string, descriptor: ProviderDescriptor): void;
2507
+ /**
2508
+ * Reconcile `process.env` with the stored credentials so the harness providers
2509
+ * pick up the wizard's output via their existing env-var resolution. Called
2510
+ * once at TUI launch after the credentials file has been resolved.
2511
+ *
2512
+ * **Precedence: stored credential > ambient env.** When the user has run the
2513
+ * auth wizard, that's an explicit, recent action — it MUST win over whatever
2514
+ * ambient value is already in `process.env`. Otherwise an upgrade reliably
2515
+ * breaks for anyone whose cwd happens to contain a stale `.env`: Bun
2516
+ * auto-loads `.env` from cwd into `process.env` before our code runs, the
2517
+ * legacy `bun run auth` used to upsert tokens into `cwd/.env`, and even a
2518
+ * one-off `export ANTHROPIC_API_KEY=…` from months ago in the user's shell rc
2519
+ * can outlive its usefulness. None of those should override a key the user
2520
+ * just typed into the wizard.
2521
+ *
2522
+ * Three cases per registered provider with an `envKey`:
2523
+ *
2524
+ * 1. Stored `kind: 'apikey'` → overwrite env with the stored value. Wizard
2525
+ * wins. A debug log fires when this clobbers a different ambient value.
2526
+ * 2. Stored `kind: 'oauth'` → DELETE env, so {@link resolveOAuthApiKey}
2527
+ * falls through to the file-read + refresh path. A stale OAuth token
2528
+ * in env would otherwise short-circuit refresh and get rejected.
2529
+ * 3. No stored entry → leave env alone. Env-only setups (no wizard run yet,
2530
+ * hosts driving zidane purely from secrets management) keep working.
2531
+ *
2532
+ * To opt out and force ambient env to win again, the user signs the provider
2533
+ * out via the wizard (which deletes the stored entry → case 3 above).
2534
+ *
2535
+ * Descriptors without an `envKey` (OAuth-only providers, custom providers
2536
+ * that bypass env-var resolution) are skipped silently.
2537
+ */
2538
+ declare function applyApiKeyEnv(dataDir: string, registry: Readonly<Record<string, ProviderDescriptor>>, opts?: {
2539
+ log?: (message: string) => void;
2540
+ }): void;
2541
+ //#endregion
2542
+ //#region src/chat/mcps-discovery.d.ts
2543
+ /**
2544
+ * One entry in the discovered list. `path` is the source file (one of
2545
+ * the `mcp(s).json` variants under `.agents/` or `.{prefix}/`) so a
2546
+ * Settings picker can show "Defined in ~/.zidane/mcp.json" tooltips
2547
+ * and the user can tell which file declared what.
2548
+ */
2549
+ interface DiscoveredMcp {
2550
+ config: McpServerConfig;
2551
+ source: 'project' | 'user';
2552
+ path: string;
2553
+ }
2554
+ /**
2555
+ * Search order for MCP config files — see {@link projectUserPaths}.
2556
+ *
2557
+ * Both `mcps.json` (zidane's canonical name) and `mcp.json` (Cursor /
2558
+ * common ecosystem alias) are picked up from every search root. The
2559
+ * canonical name wins within the same directory; across files the
2560
+ * usual first-found-by-name dedup applies, so `mcp.json` effectively
2561
+ * merges its non-overlapping entries on top of `mcps.json`.
2562
+ *
2563
+ * Order per scope (project beats user):
2564
+ * 1. `{project}/.agents/mcps.json`
2565
+ * 2. `{project}/.agents/mcp.json`
2566
+ * 3. `{project}/.{prefix}/mcps.json`
2567
+ * 4. `{project}/.{prefix}/mcp.json`
2568
+ * 5. `~/.agents/mcps.json`
2569
+ * 6. `~/.agents/mcp.json`
2570
+ * 7. `~/.{prefix}/mcps.json`
2571
+ * 8. `~/.{prefix}/mcp.json`
2572
+ *
2573
+ * Non-existent files are skipped without error.
2574
+ */
2575
+ declare function defaultMcpsConfigPaths(opts?: {
2576
+ cwd?: string;
2577
+ home?: string;
2578
+ prefix?: string;
2579
+ }): {
2580
+ path: string;
2581
+ source: 'project' | 'user';
2582
+ }[];
2583
+ /**
2584
+ * Parse one `mcps.json` file. Canonical zidane shape is a flat array; the
2585
+ * other shapes exist for copy-paste from neighboring tools:
2586
+ *
2587
+ * - `McpServerConfig[]` — flat array (recommended).
2588
+ * - `{ name: {...} }` — name-keyed map.
2589
+ * - `{ "mcpServers": { name: {...} } }` — Claude Desktop / Claude Code /
2590
+ * Cursor wrapper. Unwrapped before normalization (otherwise
2591
+ * `normalizeMcpServers` would treat the whole wrapper as a single server).
2592
+ *
2593
+ * Per-entry `enabled: false` (Cursor runtime state) drops the entry — same
2594
+ * intent as the source tool. Use the TUI toggle list for "show but disabled".
2595
+ *
2596
+ * Tolerant to common copy-paste artifacts: when strict `JSON.parse` fails,
2597
+ * retries after normalizing invisible whitespace that's valid Unicode but
2598
+ * not valid JSON whitespace (BOM, NBSP, zero-width spaces, line/paragraph
2599
+ * separators) — only OUTSIDE string literals, so user data isn't mutated.
2600
+ * If the sanitized version still fails, throws the original error with a
2601
+ * targeted hint when NBSPs are present.
2602
+ *
2603
+ * Validation flows through `normalizeMcpServers` so the result matches
2604
+ * what `createAgent` accepts. Throws on truly malformed JSON; the caller
2605
+ * decides whether to surface or swallow.
2606
+ */
2607
+ declare function parseMcpsFile(text: string): McpServerConfig[];
2608
+ /**
2609
+ * Per-file failure surfaced from {@link discoverProjectMcps}. The TUI shows
2610
+ * these in the MCP settings empty / preamble state so a malformed
2611
+ * `mcps.json` doesn't look identical to a missing one.
2612
+ */
2613
+ interface DiscoveryError {
2614
+ path: string;
2615
+ source: 'project' | 'user';
2616
+ message: string;
2617
+ /**
2618
+ * A deliberate, documented behavior being reported (e.g. the ruler stdio
2619
+ * security carve-out) rather than a failure. Hosts should style these as
2620
+ * info, not error — crying wolf over a working default trains users to
2621
+ * ignore the channel that also carries real parse failures.
2622
+ */
2623
+ informational?: boolean;
2624
+ }
2625
+ /**
2626
+ * Result of {@link discoverProjectMcps}. `servers` is the per-source-priority
2627
+ * merged list (project beats user, first-found wins on name collision).
2628
+ * `errors` is one entry per file that existed but failed to parse, plus
2629
+ * informational entries for servers a security gate skipped (ruler stdio
2630
+ * without the foreign-agent opt-in) — empty on the happy path.
2631
+ */
2632
+ interface DiscoveryResult {
2633
+ servers: DiscoveredMcp[];
2634
+ errors: DiscoveryError[];
2635
+ }
2636
+ /**
2637
+ * Walk the default config paths, parse every file that exists, and return
2638
+ * `{ servers, errors }` in source-priority order. Project entries appear
2639
+ * before user entries; first occurrence of a `name` wins (later
2640
+ * duplicates dropped).
2641
+ *
2642
+ * Parse failures are captured into `errors` rather than thrown — the picker
2643
+ * surfaces them while still showing whatever DID parse. A `ZIDANE_DEBUG`
2644
+ * log line is also emitted for ops visibility.
2645
+ */
2646
+ declare function discoverProjectMcps(opts?: {
2647
+ cwd?: string;
2648
+ home?: string;
2649
+ prefix?: string;
2650
+ /**
2651
+ * Also read MCP configs other agents maintain (Claude Code, Codex,
2652
+ * Pi) — see `foreign-configs.ts`. Mirrors the `importForeignConfigs`
2653
+ * setting; native locations always win name collisions, and project
2654
+ * beats user within each group.
2655
+ */
2656
+ foreignAgents?: boolean;
2657
+ /**
2658
+ * Also read Ruler MCP configs (`.ruler/ruler.toml` + `mcp.json`,
2659
+ * project and XDG-global) — see `ruler-configs.ts`. Mirrors the
2660
+ * `importRulerConfigs` setting; same collision rules as above.
2661
+ */
2662
+ ruler?: boolean;
2663
+ }): DiscoveryResult;
2664
+ /**
2665
+ * Map a user-toggled enable list onto the `mcpServers` array the agent
2666
+ * receives. Conventions match `buildSkillsConfig`:
2667
+ *
2668
+ * - `enabled === undefined` → every discovered server enabled (default).
2669
+ * - `enabled === []` → no servers (the agent runs MCP-less).
2670
+ * - `enabled === [names]` → allowlist; servers not in the list are
2671
+ * dropped.
2672
+ *
2673
+ * `disabledTools` is the per-server tool deny-list from
2674
+ * `Settings.disabledMcpTools`. For each server that survives the
2675
+ * server-level enable filter, the per-tool names are merged into
2676
+ * `McpServerConfig.disabledTools` via **union** with anything the JSON
2677
+ * file already pinned — a tool listed in either source is dropped.
2678
+ * Union (not override) keeps JSON-side policy stable: a server file
2679
+ * that pins `disabledTools: ['dangerous_tool']` can't be re-enabled
2680
+ * via the UI by accident.
2681
+ *
2682
+ * `enabledTools` (JSON-side allowlist) takes precedence over the
2683
+ * UI-side deny-list when both are set on the same server — the
2684
+ * allowlist's "only these" semantics would be meaningless if a UI
2685
+ * deny-list could subtract from it again. We don't emit `disabledTools`
2686
+ * when `enabledTools` is present.
2687
+ *
2688
+ * Returns a fresh array — callers can mutate without affecting the
2689
+ * underlying discovery list. Per-server config objects are also fresh
2690
+ * copies (shallow), so the merged `disabledTools` doesn't leak back
2691
+ * onto the discovery catalog.
2692
+ */
2693
+ declare function buildMcpServers(opts: {
2694
+ discovered: readonly DiscoveredMcp[];
2695
+ enabled?: readonly string[];
2696
+ disabledTools?: Record<string, readonly string[]>;
2697
+ }): McpServerConfig[];
2698
+ /**
2699
+ * Dedup a merged MCP-server list by `name`. First entry wins.
2700
+ *
2701
+ * Applied at agent build across every source that can contribute a server —
2702
+ * `mcps.json`, Agent Plugins, launch options, extensions — so the earliest
2703
+ * source in that order takes precedence.
2704
+ *
2705
+ * Drops are REPORTED rather than silent, via `onDropped`. A plugin shipping a
2706
+ * `github` server loses to a `github` in `mcps.json`, and nothing downstream
2707
+ * says so: the plugin's settings row still counts the server it shipped,
2708
+ * because the row renders what the PACKAGE declares and only this merge knows
2709
+ * what survived. That mirrors how the skills side reports the same class of
2710
+ * event — `resolveSkills` emits `name-collision-shadowed` on the winner rather
2711
+ * than annotating the loser.
2712
+ *
2713
+ * The reporter is injected rather than calling a logger directly so this stays
2714
+ * a pure function of its input, testable without a logging shim. It lives here
2715
+ * beside {@link buildMcpServers} rather than in the TUI because merge
2716
+ * precedence is a config concern, not a rendering one.
2717
+ */
2718
+ declare function dedupMcpServersByName<T extends {
2719
+ name: string;
2720
+ }>(servers: readonly T[], onDropped?: (names: readonly string[]) => void): T[];
2721
+ //#endregion
2722
+ //#region src/chat/project-user-paths.d.ts
2723
+ /**
2724
+ * Shared search-path builder for project + user config discovery.
2725
+ *
2726
+ * Both Skills (`skills/<name>/SKILL.md`) and MCP servers (`mcps.json`)
2727
+ * follow the same first-found-wins order:
2728
+ *
2729
+ * 1. `{project-root}/.agents/<sub>` — project, agnostic convention
2730
+ * 2. `{project-root}/.{prefix}/<sub>` — project, zidane (`.zidane` default)
2731
+ * 3. `~/.agents/<sub>` — user, agnostic
2732
+ * 4. `~/.{prefix}/<sub>` — user, zidane
2733
+ *
2734
+ * `{project-root}` is the git root walked up from `cwd` when present,
2735
+ * else `cwd` itself. This pairs with the project-scoped session
2736
+ * storage (`resolveStoragePaths`) so all three project-aware surfaces
2737
+ * — sessions, mcps, skills — anchor at the same `.{prefix}/`.
2738
+ *
2739
+ * Centralizing the pattern keeps the four paths in one place — a third
2740
+ * discovery surface (hooks, rules, …) gets the same convention for free
2741
+ * and stays in lock-step on a prefix bump.
2742
+ */
2743
+ /** A discovered path tagged with whether it lives in the project or the user dir. */
2744
+ interface ProjectUserPath {
2745
+ path: string;
2746
+ source: 'project' | 'user';
2747
+ }
2748
+ /**
2749
+ * Resolve the four search paths for a given subdirectory (`skills`,
2750
+ * `mcps.json`, …). Returns absolute paths regardless of whether they
2751
+ * exist — the caller chooses to `existsSync`-filter or feed straight
2752
+ * into a tolerant discovery routine.
2753
+ */
2754
+ declare function projectUserPaths(opts: {
2755
+ /** Subdirectory or filename relative to each search root. E.g. `'skills'`, `'mcps.json'`. */
2756
+ subPath: string;
2757
+ /** Discovery cwd. Default: `process.cwd()`. */
2758
+ cwd?: string;
2759
+ /** User home directory. Default: `os.homedir()`. */
2760
+ home?: string;
2761
+ /** Storage prefix. Leading dot tolerated. Default: `'.zidane'`. */
2762
+ prefix?: string;
2763
+ }): ProjectUserPath[];
2764
+ //#endregion
2765
+ //#region src/chat/plugins-discovery.d.ts
2766
+ /** One plugin found on disk, loaded (successfully or not). */
2767
+ interface DiscoveredPlugin {
2768
+ /**
2769
+ * Identity for dedup + the enabled allowlist. The manifest's `name` when
2770
+ * the plugin loaded; the directory basename when it didn't, so a rejected
2771
+ * plugin still has something stable to key a row and a toggle on.
2772
+ */
2773
+ name: string;
2774
+ /** Load result. Check `ok` before trusting `manifest`. */
2775
+ plugin: LoadedPlugin;
2776
+ /** Which search root it came from. */
2777
+ source: 'project' | 'user';
2778
+ /** Plugin root directory, as discovered. */
2779
+ path: string;
2780
+ }
2781
+ /**
2782
+ * Load every plugin reachable from the default search roots.
2783
+ *
2784
+ * Each immediate child directory of a root is one plugin — the same
2785
+ * depth-1 rule the spec applies inside `skills/`. A child that isn't a
2786
+ * directory, or that holds no `plugin.json`, is silently skipped: dropping a
2787
+ * README next to your plugins is not an error.
2788
+ *
2789
+ * Dedup is by plugin NAME, not directory name, because the manifest owns
2790
+ * identity — two directories called `tools/` shipping different plugins are
2791
+ * two plugins, and one plugin vendored at two scopes is one plugin.
2792
+ */
2793
+ declare function discoverProjectPlugins(opts?: {
2794
+ cwd?: string;
2795
+ home?: string;
2796
+ prefix?: string;
2797
+ /**
2798
+ * Extra scan roots appended AFTER the four defaults — typically from
2799
+ * {@link parsePluginScanPathEnv}. Ordered last so a real project or user
2800
+ * plugin always wins a name collision against a dev/host-provided root.
2801
+ *
2802
+ * Scoped `'project'`: they're supplied by whoever launched the process, the
2803
+ * same trust level as a plugin sitting in the working tree.
2804
+ */
2805
+ extraRoots?: readonly string[];
2806
+ signal?: AbortSignal;
2807
+ }): Promise<{
2808
+ plugins: DiscoveredPlugin[];
2809
+ errors: DiscoveryError[];
2810
+ }>;
2811
+ /**
2812
+ * Map the user's enabled-plugin list onto the contributions a host merges
2813
+ * into the agent.
2814
+ *
2815
+ * Conventions match `enabledSkills` / `enabledMcps`:
2816
+ * - `undefined` → every discovered plugin is enabled (default)
2817
+ * - `[]` → all off
2818
+ * - `[names]` → allowlist
2819
+ *
2820
+ * A plugin that failed to load contributes nothing regardless of the
2821
+ * allowlist — `ok: false` means components were never discovered.
2822
+ */
2823
+ declare function buildPluginContributions(opts: {
2824
+ discovered: readonly DiscoveredPlugin[];
2825
+ enabled?: readonly string[];
2826
+ }): {
2827
+ skills: SkillConfig[];
2828
+ mcpServers: McpServerConfig[];
2829
+ };
2830
+ //#endregion
2831
+ //#region src/chat/discovery-context.d.ts
2832
+ interface DiscoveryContextValue {
2833
+ /** Parsed `SkillConfig[]` from project + user scan paths. */
2834
+ skillsCatalog: readonly SkillConfig[];
2835
+ /**
2836
+ * Agent Plugins loaded from `.agents/plugins` / `.{prefix}/plugins`
2837
+ * (project + user). Includes rejected plugins — they render as read-only
2838
+ * diagnostics rather than vanishing.
2839
+ */
2840
+ pluginsCatalog: readonly DiscoveredPlugin[];
2841
+ /** Parsed `mcps.json` entries. */
2842
+ mcpsCatalog: readonly DiscoveredMcp[];
2843
+ /** Non-fatal parse errors from `mcps.json` lookups. */
2844
+ mcpsErrors: readonly DiscoveryError[];
2845
+ /**
2846
+ * Per-server full tool catalog from the on-disk cache (see
2847
+ * `chat/mcp-tools-cache.ts`). Updated as MCP servers bootstrap and
2848
+ * fire the `mcp:tools:list` hook. Missing key → no cache yet; the
2849
+ * settings UI surfaces an empty-state row under the expanded server.
2850
+ */
2851
+ mcpToolsByServer: Record<string, readonly McpToolSchema[]>;
2852
+ /** Current file walk results — empty until lazy-load lands. */
2853
+ filesCatalog: readonly FileEntry[];
2854
+ /** Force a fresh skills rescan. Resolves once state is committed. */
2855
+ refreshSkills: () => Promise<void>;
2856
+ /** Force a fresh plugin rescan. Resolves once state is committed. */
2857
+ refreshPlugins: () => Promise<void>;
2858
+ /**
2859
+ * Re-parse `mcps.json`. Sync-shaped under the hood, async for symmetry.
2860
+ * Resolves `true` when the catalog actually changed (a publish that
2861
+ * schedules downstream agent rebuilds), `false` on a fingerprint no-op
2862
+ * or scan failure — callers that arm rebuild-coupled state (e.g. the
2863
+ * add-MCP auto-login) disarm on `false`.
2864
+ */
2865
+ refreshMcps: () => Promise<boolean>;
2866
+ /** Force a fresh file walk. Wired into a future "refresh files" affordance. */
2867
+ refreshFiles: () => Promise<void>;
2868
+ /** Idempotent first-load trigger for skills (used by `/` popover + modal mount). */
2869
+ ensureSkills: () => Promise<readonly SkillConfig[]>;
2870
+ /** Idempotent first-load trigger for files (used by `@` popover). */
2871
+ ensureFiles: () => Promise<readonly FileEntry[]>;
2872
+ }
2873
+ declare function DiscoveryProvider({ value, children }: {
2874
+ value: DiscoveryContextValue;
2875
+ children: ReactNode;
2876
+ }): import("react").JSX.Element;
2877
+ /**
2878
+ * Read live discovery state + actions. Throws if used outside a
2879
+ * `<DiscoveryProvider>` — discovery is a load-bearing dependency for
2880
+ * settings + completion popovers; bailing loud here surfaces wiring
2881
+ * mistakes at mount instead of producing empty catalogs at first
2882
+ * keystroke.
2883
+ */
2884
+ declare function useDiscovery(): DiscoveryContextValue;
2885
+ /**
2886
+ * Non-throwing variant — returns `null` when no provider is mounted.
2887
+ * Used by composable modals (`<SettingsModal>`, …) that accept catalog
2888
+ * props as a fallback for embedders who don't wire the full TUI
2889
+ * shell. The in-app flow always mounts `<DiscoveryProvider>` so the
2890
+ * modal sees live state; standalone embeds get the prop snapshot.
2891
+ */
2892
+ declare function useDiscoveryOptional(): DiscoveryContextValue | null;
2893
+ //#endregion
2894
+ //#region src/chat/discovery-slot.d.ts
2895
+ /**
2896
+ * Stale-while-revalidate catalog slot.
2897
+ *
2898
+ * One slot per (catalog kind, projectDir) pin. Holds the first-load
2899
+ * promise + a `lastScannedAt` timestamp + an in-flight `refreshing`
2900
+ * promise. Idempotent across concurrent callers, safe to rotate when
2901
+ * the project changes (the slot's `abort` controller cancels in-flight
2902
+ * walks and the `.then` guards drop stale results).
2903
+ *
2904
+ * Renderer-agnostic. AppShell wraps it in React refs so React closures
2905
+ * stay stable; a non-TUI shell can use it directly against its own
2906
+ * state propagation.
2907
+ *
2908
+ * Contract:
2909
+ * - `ensure()` — kick the first load if not started; return the
2910
+ * cached promise. On subsequent calls, if `Date.now() -
2911
+ * lastScannedAt >= throttleMs` and no refresh is in flight, kick
2912
+ * a background revalidate that calls `onLoad(fresh)` when it
2913
+ * resolves. The original first-load promise still resolves with
2914
+ * the first walk's contents — consumers reading live state
2915
+ * elsewhere (React state) see the refreshed catalog.
2916
+ * - `refresh()` — force a rescan now, bypassing the throttle.
2917
+ * De-duplicates: if a refresh is already in flight, returns the
2918
+ * same promise.
2919
+ * - `abort()` — cancel any in-flight walk and detach. After this
2920
+ * the slot is dead; callers should rotate to a new slot.
2921
+ * - `dispose()` — alias of `abort()`, named for symmetry with
2922
+ * cleanup callbacks.
2923
+ *
2924
+ * No state is exposed except through promises + the host's `onLoad`
2925
+ * callback; the slot doesn't try to be a store, only a coordinator.
2926
+ */
2927
+ /** Walk function — takes an abort signal, returns the discovered list. */
2928
+ type Walk<T> = (signal: AbortSignal) => Promise<readonly T[]>;
2929
+ /** Notification: a fresh walk has resolved. Host is expected to commit to its own state store. */
2930
+ type Commit<T> = (items: readonly T[]) => void;
2931
+ interface DiscoverySlotOptions<T> {
2932
+ /** Discover function — runs the actual FS / network walk. */
2933
+ walk: Walk<T>;
2934
+ /** Commit hook — receives every successful walk's result (first + refreshes). */
2935
+ onLoad: Commit<T>;
2936
+ /**
2937
+ * Min interval between background refreshes triggered by `ensure()`.
2938
+ * Default: 3_000ms. `refresh()` ignores this — it's the user's
2939
+ * explicit-intent path.
2940
+ */
2941
+ throttleMs?: number;
2942
+ /** Optional error sink. Defaults to no-op; useful for `debugLog`. */
2943
+ onError?: (err: unknown, phase: 'first-load' | 'refresh') => void;
2944
+ }
2945
+ interface DiscoverySlot<T> {
2946
+ /**
2947
+ * Kick the first load (idempotent). Returns the first-load promise
2948
+ * once it exists. Schedules a background refresh if past throttle.
2949
+ */
2950
+ ensure: () => Promise<readonly T[]>;
2951
+ /** Force-refresh now. De-duped across concurrent callers. */
2952
+ refresh: () => Promise<void>;
2953
+ /** True if a refresh is currently running. */
2954
+ isRefreshing: () => boolean;
2955
+ /** Cancel in-flight work + detach. Idempotent. */
2956
+ abort: () => void;
2957
+ }
2958
+ declare function createDiscoverySlot<T>(options: DiscoverySlotOptions<T>): DiscoverySlot<T>;
2959
+ //#endregion
2960
+ //#region src/chat/editor-command.d.ts
2961
+ /**
2962
+ * `$EDITOR` / `$VISUAL` command parsing.
2963
+ *
2964
+ * These variables conventionally hold a command LINE, not a bare executable —
2965
+ * `code --wait`, `cursor --wait`, `subl -w`, `emacsclient -nw` are all normal
2966
+ * values. Spawning the whole string as argv[0] looks for a file literally named
2967
+ * `cursor --wait` and fails with ENOENT, so the flags have to be split off.
2968
+ *
2969
+ * Kept out of the TUI (and free of `node:child_process`) so the
2970
+ * tokenizer is unit-testable without pulling in the TUI.
2971
+ */
2972
+ /**
2973
+ * Split an editor command line into `[executable, ...args]`.
2974
+ *
2975
+ * Whitespace-separated, with single and double quotes honored so an editor
1269
2976
  * living under a path with spaces still works
1270
2977
  * (`"/Applications/My App/bin/ed" -w`). Quotes group and are stripped;
1271
2978
  * backslash escaping is deliberately NOT implemented, because on Windows
@@ -1306,6 +3013,174 @@ declare function parseEditorCommand(spec: string): string[];
1306
3013
  */
1307
3014
  declare function quoteForShell(token: string): string;
1308
3015
  //#endregion
3016
+ //#region src/chat/enabled-toggle-set.d.ts
3017
+ /**
3018
+ * Renderer-agnostic state machine for an "enabled allowlist" — the shape
3019
+ * used by `settings.enabledSkills` / `settings.enabledMcps` (and any future
3020
+ * `enabledX` toggle backed by a `Settings` field).
3021
+ *
3022
+ * Semantics, kept identical across all three call sites:
3023
+ * - `undefined` → every catalog entry is enabled (default — user has
3024
+ * never opened the picker; new entries flow in).
3025
+ * - `[]` → the whole subsystem is off.
3026
+ * - `[names]` → explicit allowlist; the persisted shape stays a
3027
+ * concrete array once the user has toggled anything.
3028
+ *
3029
+ * The hook exposes a live `enabledSet` (Set<string>) + a `toggle(name)`
3030
+ * callback that persists the result through `useSettings().setSetting`.
3031
+ * The first toggle seeds the persisted allowlist from the current catalog
3032
+ * so newly-added entries don't silently drop on the next launch.
3033
+ *
3034
+ * Renderer-neutral: works for the TUI's `<ToggleListModal>`, a GUI
3035
+ * checkbox list, a CLI flag tool. Hook tests live alongside the chat
3036
+ * suite; the rendering layer is tested where it lives.
3037
+ */
3038
+ /** Settings keys whose value type is `readonly string[] | undefined`. */
3039
+ type EnabledAllowlistKey = 'enabledSkills' | 'enabledMcps' | 'enabledExtensions' | 'enabledPlugins';
3040
+ interface EnabledToggleSet {
3041
+ /** Live set of enabled names — `Set` for O(1) `has`. */
3042
+ enabledSet: ReadonlySet<string>;
3043
+ /** Toggle one name on/off; persists immediately via `setSetting`. */
3044
+ toggle: (name: string) => void;
3045
+ }
3046
+ /**
3047
+ * Bind an "enabled allowlist" setting to a discovered catalog.
3048
+ *
3049
+ * `keyOf` extracts the persisted identity from a catalog entry (skill name,
3050
+ * MCP server name, …). Pass a stable, collision-free key — the persisted
3051
+ * allowlist is keyed against it forever.
3052
+ */
3053
+ declare function useEnabledToggleSet<T>(opts: {
3054
+ catalog: readonly T[];
3055
+ keyOf: (entry: T) => string;
3056
+ settingKey: EnabledAllowlistKey;
3057
+ /**
3058
+ * Names that survive the persisted-allowlist GC even when absent from
3059
+ * `catalog`. Use when the catalog deliberately hides entries that are
3060
+ * still known to exist — e.g. errored / untrusted extensions are kept
3061
+ * out of the toggle list but must not be dropped from the allowlist
3062
+ * by an unrelated toggle. Defaults to the catalog keys.
3063
+ */
3064
+ gcKeys?: readonly string[];
3065
+ }): EnabledToggleSet;
3066
+ //#endregion
3067
+ //#region src/chat/environment-credentials.d.ts
3068
+ declare function environmentCredentialsPath(dataDir: string): string;
3069
+ declare function createFileEnvironmentSecrets(dataDir: string): EnvironmentSecrets;
3070
+ /** In-memory secrets — tests and hosts without a data dir. */
3071
+ declare function createMemoryEnvironmentSecrets(initial?: Record<string, Record<string, string>>): EnvironmentSecrets;
3072
+ //#endregion
3073
+ //#region src/chat/environment-provisioning.d.ts
3074
+ interface ProvisionSessionEnvironmentInput {
3075
+ sessionId: string;
3076
+ /** The session as persisted now. */
3077
+ load: () => Promise<SessionData | null>;
3078
+ registry: EnvironmentRegistry;
3079
+ config: (kind: string) => EnvironmentConfig;
3080
+ /** Persist a metadata patch and announce it; `false` when the session no longer exists, throws on other write errors. */
3081
+ persist: (patch: Record<string, unknown>) => Promise<boolean>;
3082
+ signal?: AbortSignal;
3083
+ }
3084
+ interface ProvisionSessionEnvironmentResult {
3085
+ /** The attempt tore down what it prepared (aborted, or the session vanished). */
3086
+ tornDown: boolean;
3087
+ /** The environment as it ended — absent when there was nothing to provision. */
3088
+ environment?: SessionEnvironment;
3089
+ }
3090
+ declare function provisionSessionEnvironment(input: ProvisionSessionEnvironmentInput): Promise<ProvisionSessionEnvironmentResult>;
3091
+ //#endregion
3092
+ //#region src/chat/environments/local.d.ts
3093
+ declare const localEnvironmentProvider: EnvironmentProvider;
3094
+ //#endregion
3095
+ //#region src/chat/environments/worktree.d.ts
3096
+ declare const WORKTREE_ENVIRONMENT_KIND = "worktree";
3097
+ /** Provider-owned state on `environment.ref`. */
3098
+ interface WorktreeRef {
3099
+ /** The worktree's directory. */
3100
+ path: string;
3101
+ /** The main checkout it belongs to. */
3102
+ repoRoot: string;
3103
+ }
3104
+ declare function createWorktreeEnvironmentProvider(options: {
3105
+ dataDir: string;
3106
+ }): EnvironmentProvider;
3107
+ //#endregion
3108
+ //#region src/chat/environments/index.d.ts
3109
+ interface BuiltinEnvironmentOptions {
3110
+ /** Host data dir (`~/.zidane`) — default home of worktrees. */
3111
+ dataDir: string;
3112
+ }
3113
+ declare function builtinEnvironmentProviders(options: BuiltinEnvironmentOptions): EnvironmentProvider[];
3114
+ //#endregion
3115
+ //#region src/chat/execution-cache.d.ts
3116
+ interface SessionExecution {
3117
+ /** The session's execution context — pass as `AgentOptions.execution`. */
3118
+ context: ExecutionContext;
3119
+ /** Host-owned handle, spawned on first use — pass as `AgentOptions.handle`. */
3120
+ handle: () => Promise<ExecutionHandle>;
3121
+ }
3122
+ interface SessionExecutionCache {
3123
+ /**
3124
+ * The session's execution, created with `create()` on first use and
3125
+ * reused while `key` matches. A different `key` replaces it (the previous
3126
+ * context is released in the background).
3127
+ */
3128
+ acquire: (sessionId: string, key: string, create: () => ExecutionContext) => SessionExecution;
3129
+ /**
3130
+ * The session's current execution, whatever its key — for side work
3131
+ * (environment actions) that must run where the agent runs without
3132
+ * re-deriving the key (a mismatch would replace the live context).
3133
+ */
3134
+ peek: (sessionId: string) => SessionExecution | undefined;
3135
+ /**
3136
+ * Hand the session's still-running background tasks to a newly built
3137
+ * agent: rebind their exit/stall callbacks to `hooks` and fire
3138
+ * `background:adopt` per task. Returns how many were adopted.
3139
+ */
3140
+ adopt: (sessionId: string, hooks: Agent['hooks']) => Promise<number>;
3141
+ /** True while any background task on the session's handle is running. */
3142
+ hasRunningTasks: (sessionId: string) => Promise<boolean>;
3143
+ /**
3144
+ * Destroy the session's handle (killing its tasks) and forget it. Resolves
3145
+ * once the destroy has finished — including one already started by an
3146
+ * earlier release or a key replacement — so a caller can rely on the
3147
+ * session's processes being gone (e.g. before removing its worktree).
3148
+ */
3149
+ release: (sessionId: string) => Promise<void>;
3150
+ /**
3151
+ * {@link release} unless tasks are still running; resolves whether it
3152
+ * released. `stillIdle` is re-checked after the (async) task listing,
3153
+ * right before destroying — hosts pass "no agent was rebuilt for this
3154
+ * session meanwhile", so an eviction can't pull the handle out from
3155
+ * under a build that just acquired it.
3156
+ */
3157
+ releaseIfIdle: (sessionId: string, stillIdle?: () => boolean) => Promise<boolean>;
3158
+ /** Release every session — host shutdown. */
3159
+ releaseAll: () => Promise<void>;
3160
+ /** Sessions currently holding an execution. */
3161
+ sessions: () => string[];
3162
+ }
3163
+ declare function createSessionExecutionCache(): SessionExecutionCache;
3164
+ //#endregion
3165
+ //#region src/chat/extension-mcp-host.d.ts
3166
+ interface CreateExtensionMcpHostOptions {
3167
+ /** Storage prefix (`.zidane`) the discovery reads `mcps.json` under. */
3168
+ prefix: string;
3169
+ /** Live settings: the allowlist (`enabledMcps`) and import toggles. */
3170
+ readSettings: () => Partial<Settings> | undefined;
3171
+ /** Stored MCP OAuth credentials; omitted → OAuth servers can't connect. */
3172
+ credentialStore?: () => McpCredentialStore;
3173
+ /** Close a connection after this long without a call. Default 60 s. */
3174
+ idleMs?: number;
3175
+ /** How long a cwd's server list is reused. Default 10 s. */
3176
+ serversTtlMs?: number;
3177
+ /** Test seam: replaces `connectMcpServers`. */
3178
+ connect?: (configs: McpServerConfig[], opts: {
3179
+ buildAuthProvider?: (config: McpServerConfig) => McpOAuthProvider | undefined;
3180
+ }) => Promise<McpConnection>;
3181
+ }
3182
+ declare function createExtensionMcpHost(options: CreateExtensionMcpHostOptions): ExtensionMcpHost;
3183
+ //#endregion
1309
3184
  //#region src/chat/footer-hints.d.ts
1310
3185
  /**
1311
3186
  * Options bag for {@link buildHints}. Flat shape so call sites read as a
@@ -1458,13 +3333,7 @@ interface GenerateSessionTitleOptions {
1458
3333
  * Output is trimmed, single-line, with surrounding quotes / trailing
1459
3334
  * punctuation stripped, and truncated to `TITLE_MAX_CHARS`.
1460
3335
  */
1461
- declare function generateSessionTitle({
1462
- provider,
1463
- model,
1464
- turns,
1465
- maxTurns,
1466
- signal
1467
- }: GenerateSessionTitleOptions): Promise<string>;
3336
+ declare function generateSessionTitle({ provider, model, turns, maxTurns, signal }: GenerateSessionTitleOptions): Promise<string>;
1468
3337
  /**
1469
3338
  * Decide whether a session is ready for one-shot automatic titling.
1470
3339
  *
@@ -1495,10 +3364,129 @@ declare function shouldAutoGenerateTitle(turns: readonly SessionTurn[], metadata
1495
3364
  * without it.
1496
3365
  * - Truncate to `TITLE_MAX_CHARS` with a trailing `…` when over.
1497
3366
  *
1498
- * Exported as `cleanTitle` so tests can pin the normalization rules
1499
- * without going through a mock provider.
3367
+ * Exported as `cleanTitle` so tests can pin the normalization rules
3368
+ * without going through a mock provider.
3369
+ */
3370
+ declare function cleanTitle(raw: string): string;
3371
+ //#endregion
3372
+ //#region src/chat/host-scoped-settings.d.ts
3373
+ /**
3374
+ * Settings that belong to the window even while a host owns the settings
3375
+ * file (daemon or remote mode): which host the next launch uses.
3376
+ */
3377
+ declare const WINDOW_LOCAL_SETTINGS: readonly ["useDaemon", "remoteHosts"];
3378
+ declare function isWindowOnlySetting(key: keyof Settings | string): boolean;
3379
+ /** The allowlists a host leaving them unset means "everything enabled" for. */
3380
+ declare const HOST_ENABLED_LISTS: readonly ["enabledSkills", "enabledMcps", "enabledExtensions", "enabledPlugins", "disabledMcpTools"];
3381
+ /** `local` with the host's values for every key the host owns. */
3382
+ declare function overlayHostSettings<S extends Settings>(local: S, host: Partial<Settings>): S;
3383
+ /** Split a settings patch into the window's part and the host's part. */
3384
+ declare function splitHostSettingsPatch<S extends Partial<Settings>>(patch: S): {
3385
+ window: Partial<S>;
3386
+ host: Partial<S>;
3387
+ };
3388
+ //#endregion
3389
+ //#region src/chat/system-md.d.ts
3390
+ /** A discovered `SYSTEM.md`. */
3391
+ interface SystemMdFile {
3392
+ path: string;
3393
+ source: 'project' | 'user';
3394
+ contents: string;
3395
+ }
3396
+ /** Result of {@link discoverSystemMd}. */
3397
+ interface SystemMdResult {
3398
+ /**
3399
+ * The winning override, or `null` when no `SYSTEM.md` exists (the built-in
3400
+ * doctrine then applies unchanged).
3401
+ */
3402
+ file: SystemMdFile | null;
3403
+ /**
3404
+ * Files that exist but lost to `file` on precedence. Non-empty only when the
3405
+ * user has both a project and a user `SYSTEM.md`. Surfaced so the UI can mark
3406
+ * them as inactive instead of leaving the user wondering why edits to one of
3407
+ * them changed nothing.
3408
+ */
3409
+ shadowed: readonly SystemMdFile[];
3410
+ }
3411
+ interface DiscoverSystemMdOptions {
3412
+ /** Discovery cwd. Default: `process.cwd()`. */
3413
+ cwd?: string;
3414
+ /** User home directory. Default: `os.homedir()`. */
3415
+ home?: string;
3416
+ /** Storage prefix. Leading dot tolerated. Default: `'.zidane'`. */
3417
+ prefix?: string;
3418
+ /**
3419
+ * Which scopes may contribute an override — the same
3420
+ * {@link AgentsMdScope} the host applies to `AGENTS.md`, so one setting
3421
+ * governs both surfaces:
3422
+ * - `'none'` — no override, ever. Short-circuits before any FS probe.
3423
+ * - `'user'` — only `~/.{prefix}/SYSTEM.md`. The repo's file is IGNORED,
3424
+ * which is the whole point of the setting: it is how a user
3425
+ * says "do not trust this checkout".
3426
+ * - `'project'` — only `<root>/SYSTEM.md`.
3427
+ * - `'both'` — union, project winning (default).
3428
+ *
3429
+ * Previously a boolean that only implemented `'none'`, so `'user'` still let a
3430
+ * repo-root `SYSTEM.md` replace the entire doctrine — exactly the case the
3431
+ * setting exists to prevent.
3432
+ */
3433
+ scope?: AgentsMdScope;
3434
+ }
3435
+ /**
3436
+ * Absolute path of the USER-scope `SYSTEM.md` — the file the TUI creates when
3437
+ * the user asks for an override and none exists yet.
3438
+ *
3439
+ * Exported so the host doesn't hardcode the layout and can't drift from what
3440
+ * discovery actually reads.
3441
+ */
3442
+ declare function userSystemMdPath(opts?: {
3443
+ home?: string;
3444
+ prefix?: string;
3445
+ }): string;
3446
+ /**
3447
+ * Whether a USER-scope `SYSTEM.md` would be loaded under `scope`.
3448
+ *
3449
+ * Mirrors the candidate filter in {@link discoverSystemMd} so a host can ask
3450
+ * "would writing `~/.{prefix}/SYSTEM.md` accomplish anything?" without
3451
+ * re-deriving the rule and drifting from it. `'project'` excludes the user
3452
+ * candidate and `'none'` skips discovery altogether, so under either setting
3453
+ * creating that file is a silent no-op.
3454
+ */
3455
+ declare function userScopeOverridesLoad(scope: AgentsMdScope | undefined): boolean;
3456
+ /**
3457
+ * Find the active `SYSTEM.md` override, if any.
3458
+ *
3459
+ * Project paths anchor at the git root when one exists above `cwd`, else at
3460
+ * `cwd` — the same anchor as skills / mcps / AGENTS.md discovery, so all four
3461
+ * surfaces agree on what "this project" means.
3462
+ *
3463
+ * Tolerant: an unreadable or empty file is treated as absent (logged under
3464
+ * `ZIDANE_DEBUG`) and the next candidate gets its turn. Never throws — this
3465
+ * runs on the session-activation path.
3466
+ *
3467
+ * Candidates are filtered by {@link DiscoverSystemMdOptions.scope}, mirroring
3468
+ * how `discoverAgentsMd` builds its own list.
3469
+ */
3470
+ declare function discoverSystemMd(opts?: DiscoverSystemMdOptions): SystemMdResult;
3471
+ /**
3472
+ * Seed contents for a freshly created `SYSTEM.md` override, built from the
3473
+ * built-in doctrine.
3474
+ *
3475
+ * Seeding with the real doctrine (rather than an empty file) means creating the
3476
+ * override is a behavioral no-op until the user edits it. An empty seed would
3477
+ * silently delete every line of doctrine the moment the file appeared, making
3478
+ * the agent measurably worse with no visible cause.
3479
+ *
3480
+ * `currentSystem` should be DOCTRINE ONLY — callers pass
3481
+ * `splitSystemPrompt(doctrineOnlySystem()).static`, which excludes both the
3482
+ * generated `<env>` block (rebuilt per turn) and the `AGENTS.md` block. Baking
3483
+ * the latter in would duplicate it, since it is re-appended below the override
3484
+ * on every compose.
3485
+ *
3486
+ * `scope` is the active `userInstructionsScope`; it decides whether the seed's
3487
+ * precedence note is true — see {@link projectScopeOverridesLoad}.
1500
3488
  */
1501
- declare function cleanTitle(raw: string): string;
3489
+ declare function renderSystemMdSeed(currentSystem: string, scope?: AgentsMdScope): string;
1502
3490
  //#endregion
1503
3491
  //#region src/chat/host/provider-diagnostic.d.ts
1504
3492
  type ProviderDiagnostic = Pick<Parameters<AgentHooks['provider:diagnostic']>[0], 'requestId' | 'stage' | 'event' | 'elapsedMs' | 'stageElapsedMs' | 'attempt' | 'runId' | 'turnId' | 'httpStatus' | 'upstreamRequestId' | 'transportAttempt' | 'networkCode' | 'retryAfter'>;
@@ -1592,6 +3580,12 @@ interface SendOptions {
1592
3580
  * and a silent prose send is indistinguishable from a broken command.
1593
3581
  */
1594
3582
  asCommand?: string;
3583
+ /**
3584
+ * Sent from outside the composer (Continue, Retry, a palette prompt): the
3585
+ * shared draft was not consumed, so the daemon leaves it alone instead of
3586
+ * clearing it in every other window.
3587
+ */
3588
+ keepDraft?: boolean;
1595
3589
  }
1596
3590
  /**
1597
3591
  * Wire-safe summary of an extension-contributed `/` command — the
@@ -1683,27 +3677,27 @@ type HostSignal = {
1683
3677
  runId: string;
1684
3678
  ok: boolean;
1685
3679
  error?: string;
1686
- }
3680
+ } |
1687
3681
  /**
1688
3682
  * A turn finished. `usage` carries the loop-reported token/cost figures
1689
3683
  * that drive footer gauges (context size = prompt-side tokens of the
1690
3684
  * LAST parent turn; cost accrues across parent + child turns).
1691
3685
  */
1692
- | {
3686
+ {
1693
3687
  kind: 'turn:end';
1694
3688
  sessionId: string;
1695
3689
  turnId: string;
1696
3690
  childId?: string;
1697
3691
  depth?: number;
1698
3692
  usage?: TurnUsage;
1699
- }
3693
+ } |
1700
3694
  /**
1701
3695
  * `echo: true` marks host-generated SEND/STEER echoes of the user's own
1702
3696
  * message. Clients that already render the prompt locally (the GUI's
1703
3697
  * enriched append) drop echo-tagged signals; command-module
1704
3698
  * user-prompts are never echoes and always render.
1705
3699
  */
1706
- | {
3700
+ {
1707
3701
  kind: 'event';
1708
3702
  sessionId: string;
1709
3703
  event: StreamEvent;
@@ -1711,7 +3705,7 @@ type HostSignal = {
1711
3705
  } | {
1712
3706
  kind: 'compacting:start';
1713
3707
  sessionId: string;
1714
- }
3708
+ } |
1715
3709
  /**
1716
3710
  * Live token estimate for an in-flight compaction summary. Throttled by the
1717
3711
  * loop, so this is a low-rate progress tick rather than a stream.
@@ -1720,11 +3714,11 @@ type HostSignal = {
1720
3714
  * indication of progress — a compaction of a large transcript can run for
1721
3715
  * minutes, which is indistinguishable from a hang.
1722
3716
  */
1723
- | {
3717
+ {
1724
3718
  kind: 'compacting:progress';
1725
3719
  sessionId: string;
1726
3720
  estOutputTokens: number;
1727
- }
3721
+ } |
1728
3722
  /**
1729
3723
  * Live counters for the parent turn currently streaming — the loop's
1730
3724
  * `stream:progress` (throttled ~4/s, one final tick before `stream:end`).
@@ -1734,10 +3728,10 @@ type HostSignal = {
1734
3728
  * session figure while streaming and drop it when the matching `turn:end`
1735
3729
  * brings the real `usage.cost`.
1736
3730
  */
1737
- | ({
3731
+ ({
1738
3732
  kind: 'stream:progress';
1739
3733
  sessionId: string;
1740
- } & StreamProgressInfo)
3734
+ } & StreamProgressInfo) |
1741
3735
  /**
1742
3736
  * Prompt turns that just persisted, reduced to the fields needed to bind a
1743
3737
  * live `user-prompt` event to its turn id (see {@link PromptTurnRef}).
@@ -1748,17 +3742,17 @@ type HostSignal = {
1748
3742
  * daemon-attached window is unselectable — no hover, edit, or delete — until
1749
3743
  * the run-end transcript replace.
1750
3744
  */
1751
- | {
3745
+ {
1752
3746
  kind: 'prompt-turns';
1753
3747
  sessionId: string;
1754
3748
  refs: readonly PromptTurnRef[];
1755
- }
3749
+ } |
1756
3750
  /**
1757
3751
  * `effectiveTokens` (success only) is the loop's post-compact context
1758
3752
  * estimate — windows drop their footer ctx gauge to it immediately
1759
3753
  * instead of waiting for the next turn's usage.
1760
3754
  */
1761
- | {
3755
+ {
1762
3756
  kind: 'compacting:end';
1763
3757
  sessionId: string;
1764
3758
  ok: boolean;
@@ -1782,23 +3776,23 @@ type HostSignal = {
1782
3776
  kind: 'todos:snapshot';
1783
3777
  sessionId: string;
1784
3778
  snapshot: TodosSnapshot;
1785
- }
3779
+ } |
1786
3780
  /**
1787
3781
  * The session recorded an artifact (PR, branch, commit, file, extension
1788
3782
  * kind — see `session/artifacts.ts`). Carries the stored row so a window
1789
3783
  * can badge / list without a round-trip; `sessions.artifacts` is the
1790
3784
  * authoritative query. Additive — older clients ignore unknown kinds.
1791
3785
  */
1792
- | {
3786
+ {
1793
3787
  kind: 'artifact:recorded';
1794
3788
  sessionId: string;
1795
3789
  artifact: SessionArtifact;
1796
- }
3790
+ } |
1797
3791
  /**
1798
3792
  * An artifact was removed or refreshed through the host (`artifacts.delete`
1799
3793
  * / `artifacts.upsert`) — windows re-query. Carries the row for `upsert`.
1800
3794
  */
1801
- | {
3795
+ {
1802
3796
  kind: 'artifact:changed';
1803
3797
  sessionId: string;
1804
3798
  op: 'delete' | 'upsert';
@@ -1813,16 +3807,88 @@ type HostSignal = {
1813
3807
  kind: 'interaction:resolved';
1814
3808
  sessionId: string;
1815
3809
  requestId: string;
3810
+ } |
3811
+ /**
3812
+ * Dev browser (`browser.ts` hub): a tool call for the page, addressed to
3813
+ * the registered browser host (`hostId`); other clients ignore it. The
3814
+ * host answers through `browser.respond`. `browser:cancel` withdraws it.
3815
+ */
3816
+ {
3817
+ kind: 'browser:request';
3818
+ sessionId: string;
3819
+ hostId: string;
3820
+ request: BrowserRequest;
3821
+ } | {
3822
+ kind: 'browser:cancel';
3823
+ sessionId: string;
3824
+ hostId: string;
3825
+ requestId: string;
3826
+ } |
3827
+ /**
3828
+ * Terminals (`terminal.ts` manager). `changed` carries the session's full
3829
+ * list; `output` is a batch starting at `offset` (clients drop what they
3830
+ * already have and `terminal.ack` what they wrote); `reset` replaces a
3831
+ * view's content with history after a client fell too far behind.
3832
+ */
3833
+ {
3834
+ kind: 'terminal:changed';
3835
+ sessionId: string;
3836
+ terminals: TerminalInfo[];
3837
+ } | {
3838
+ kind: 'terminal:output';
3839
+ sessionId: string;
3840
+ terminalId: string;
3841
+ offset: number;
3842
+ data: string;
3843
+ } | {
3844
+ kind: 'terminal:reset';
3845
+ sessionId: string;
3846
+ terminalId: string;
3847
+ offset: number;
3848
+ data: string;
3849
+ } |
3850
+ /**
3851
+ * A streamed workspace command (`sessions.workspaceExecStream`): its output
3852
+ * in arrival order, then exactly one `exit`. Sent to the connection that
3853
+ * started it only, never fanned out or replayed. `code` is `null` and
3854
+ * `signal` set when the host killed it (`cancelled`: the client asked;
3855
+ * `truncated`: the output cap was reached).
3856
+ */
3857
+ {
3858
+ kind: 'workspace:exec:data';
3859
+ execId: string;
3860
+ stream: 'stdout' | 'stderr';
3861
+ chunk: string;
3862
+ } | {
3863
+ kind: 'workspace:exec:exit';
3864
+ execId: string;
3865
+ code: number | null;
3866
+ signal: string | null;
3867
+ truncated?: true;
3868
+ cancelled?: true;
3869
+ } |
3870
+ /**
3871
+ * A client installed, updated or removed something on the host
3872
+ * (`catalog.install` / `remove` / `update`, `mcp.upsert` / `remove`): every
3873
+ * window re-reads that catalog.
3874
+ */
3875
+ {
3876
+ kind: 'catalog:changed';
3877
+ catalog: 'skills' | 'mcps' | 'extensions' | 'plugins';
3878
+ } |
3879
+ /** A server's settings-sync record changed (a claim, a push, an override, a reset): settings UIs re-read `sync.status`. */
3880
+ {
3881
+ kind: 'sync:changed';
1816
3882
  } | {
1817
3883
  kind: 'approval:request';
1818
3884
  request: WireApprovalRequest;
1819
- }
3885
+ } |
1820
3886
  /**
1821
3887
  * An approval left the pending queue (answered or denied in bulk) — every
1822
3888
  * OTHER window clears its surface. The window that resolved it already
1823
3889
  * knows; the signal makes the queue converge everywhere else.
1824
3890
  */
1825
- | {
3891
+ {
1826
3892
  kind: 'approval:resolved';
1827
3893
  sessionId: string;
1828
3894
  requestId: string;
@@ -1831,17 +3897,17 @@ type HostSignal = {
1831
3897
  kind: 'sessions:changed';
1832
3898
  op: 'create' | 'update' | 'delete';
1833
3899
  sessionId: string;
1834
- }
3900
+ } |
1835
3901
  /**
1836
3902
  * Settings were written through the host (`settings.set`) — every window
1837
3903
  * applies the new state so settings UIs converge without a restart.
1838
3904
  * Windows that persist settings themselves must treat this as
1839
3905
  * authoritative for the delivered snapshot.
1840
3906
  */
1841
- | {
3907
+ {
1842
3908
  kind: 'settings:changed';
1843
3909
  settings: HostSettings;
1844
- }
3910
+ } |
1845
3911
  /**
1846
3912
  * Another window edited the session's shared prompt draft. The daemon
1847
3913
  * EXCLUDES the originating peer from this fan-out, so applying it can
@@ -1849,34 +3915,34 @@ type HostSignal = {
1849
3915
  * (last-writer-wins whole-text replacement). Empty `text` clears the
1850
3916
  * draft (broadcast when any window submits the prompt).
1851
3917
  */
1852
- | {
3918
+ {
1853
3919
  kind: 'draft:changed';
1854
3920
  sessionId: string;
1855
3921
  text: string;
1856
3922
  shell?: boolean;
1857
- }
3923
+ } |
1858
3924
  /**
1859
3925
  * The shared draft's composing ATTACHMENTS changed (paste/drop/remove in
1860
3926
  * any window). Payloads ride along so a remote window can submit the
1861
3927
  * shared draft attachments-and-all. Same origin-exclusion as text.
1862
3928
  */
1863
- | {
3929
+ {
1864
3930
  kind: 'draft:attachments';
1865
3931
  sessionId: string;
1866
3932
  attachments: PromptAttachment[];
1867
- }
3933
+ } |
1868
3934
  /**
1869
3935
  * The session's shared transcript-search state changed (mode entered /
1870
3936
  * exited, query edited) in another window. Same origin-exclusion and
1871
3937
  * last-writer-wins contract as the draft — every window on the session
1872
3938
  * shows the same filtered transcript.
1873
3939
  */
1874
- | {
3940
+ {
1875
3941
  kind: 'search:changed';
1876
3942
  sessionId: string;
1877
3943
  active: boolean;
1878
3944
  query: string;
1879
- }
3945
+ } |
1880
3946
  /**
1881
3947
  * The daemon resolved another window's inline-autocomplete query
1882
3948
  * (`suggest.resolve`). Excludes the requesting peer (it already has
@@ -1884,7 +3950,7 @@ type HostSignal = {
1884
3950
  * when their live buffer still equals `basis` — the standard
1885
3951
  * staleness guard; otherwise discard.
1886
3952
  */
1887
- | {
3953
+ {
1888
3954
  kind: 'suggest:changed';
1889
3955
  sessionId: string;
1890
3956
  seq: number;
@@ -1892,41 +3958,71 @@ type HostSignal = {
1892
3958
  caret: number;
1893
3959
  suggestion: string | null;
1894
3960
  candidates?: string[];
1895
- }
3961
+ } |
1896
3962
  /**
1897
3963
  * The daemon-side agent's active-skill set changed (`skills_use` /
1898
3964
  * run-end deactivation). Windows mirror it into their skill chips.
1899
3965
  */
1900
- | {
3966
+ {
1901
3967
  kind: 'skills:snapshot';
1902
3968
  sessionId: string;
1903
3969
  active: string[];
1904
- }
3970
+ } |
1905
3971
  /**
1906
3972
  * A daemon-side MCP server needs (re-)authentication. Windows surface
1907
3973
  * the badge; the interactive login flow itself runs in whichever window
1908
3974
  * the user triggers it from (tokens land in the shared credential
1909
3975
  * store; the daemon reconnects on the next agent rebuild).
1910
3976
  */
1911
- | {
3977
+ {
1912
3978
  kind: 'mcp:auth';
1913
3979
  sessionId: string;
1914
3980
  name: string;
1915
3981
  reason: string;
1916
- }
3982
+ } |
3983
+ /**
3984
+ * The session agent's MCP bootstrap finished: every server has its
3985
+ * outcome (`mcp:status` / `mcp:auth` came first). A window's deadline for
3986
+ * the one-shot auto-login it arms when the user adds a server.
3987
+ */
3988
+ {
3989
+ kind: 'mcp:settled';
3990
+ sessionId: string;
3991
+ } |
3992
+ /**
3993
+ * A host-side MCP server connected, or failed to. Windows feed it to the
3994
+ * same status reducer the in-process path uses (badges: connected, signed
3995
+ * in, needs sign-in, key refused, couldn't connect). `error` is the
3996
+ * failure's message and `code` its HTTP-ish code when it had one, enough
3997
+ * for the shared classifier (`classifyMcpTest`) to read it the same way.
3998
+ */
3999
+ {
4000
+ kind: 'mcp:status';
4001
+ sessionId: string;
4002
+ name: string;
4003
+ status: 'connected';
4004
+ } | {
4005
+ kind: 'mcp:status';
4006
+ sessionId: string;
4007
+ name: string;
4008
+ status: 'failed';
4009
+ transport: string;
4010
+ error: string;
4011
+ code?: number;
4012
+ } |
1917
4013
  /**
1918
4014
  * Provider retry in progress (rate-limit / overload / silent stall) —
1919
4015
  * drives the footer's "retrying n/m" indicator. `source` is `'parent'`
1920
4016
  * or the retrying child's id; the indicator clears when that source
1921
4017
  * streams again or the run ends.
1922
4018
  */
1923
- | {
4019
+ {
1924
4020
  kind: 'stream:retry';
1925
4021
  sessionId: string;
1926
4022
  attempt: number;
1927
4023
  max: number;
1928
4024
  source: string;
1929
- }
4025
+ } |
1930
4026
  /**
1931
4027
  * A retried stream reached a terminal state for `source` (`'parent'` or a
1932
4028
  * child id) — clear that source's retry indicator.
@@ -1936,11 +4032,11 @@ type HostSignal = {
1936
4032
  * this exists for the mid-run case, where a subagent's stream fails while
1937
4033
  * the parent run continues and the indicator would otherwise stick.
1938
4034
  */
1939
- | {
4035
+ {
1940
4036
  kind: 'stream:retry:cleared';
1941
4037
  sessionId: string;
1942
4038
  source: string;
1943
- }
4039
+ } |
1944
4040
  /**
1945
4041
  * Which `SYSTEM.md` override (if any) the host folded into this session's
1946
4042
  * system prompt, emitted at agent-build time.
@@ -1953,12 +4049,12 @@ type HostSignal = {
1953
4049
  * doctrine they think they replaced. `shadowed` carries precedence losers
1954
4050
  * so the Instructions surface can mark them inactive.
1955
4051
  */
1956
- | {
4052
+ {
1957
4053
  kind: 'system-md';
1958
4054
  sessionId: string;
1959
4055
  file: SystemMdFile | null;
1960
4056
  shadowed: readonly SystemMdFile[];
1961
- }
4057
+ } |
1962
4058
  /**
1963
4059
  * A cache-prefix break was detected for this session's last request — the
1964
4060
  * footer chip names the culprit dimension and the re-billed prefix size.
@@ -1966,7 +4062,7 @@ type HostSignal = {
1966
4062
  * only the daemon can observe it. Cost pricing stays window-side (it needs
1967
4063
  * only the provider key and the token count).
1968
4064
  */
1969
- | {
4065
+ {
1970
4066
  kind: 'cache:break';
1971
4067
  sessionId: string;
1972
4068
  info: CacheBreakInfo;
@@ -1974,46 +4070,68 @@ type HostSignal = {
1974
4070
  kind: 'provider:diagnostic';
1975
4071
  sessionId: string;
1976
4072
  diagnostic: ProviderDiagnostic;
1977
- }
4073
+ } |
1978
4074
  /**
1979
4075
  * Presence snapshot — recomputed and fanned out whenever a window
1980
4076
  * attaches/detaches or any tracked session's run/HITL state changes.
1981
4077
  * Full snapshot (the set is small: only live sessions appear).
1982
4078
  */
1983
- | {
4079
+ {
1984
4080
  kind: 'presence:changed';
1985
4081
  sessions: SessionPresence[];
1986
- }
4082
+ } |
4083
+ /** A machine of this host came online or went offline (`docs/MACHINES.md`). */
4084
+ ({
4085
+ kind: 'machine:status';
4086
+ } & MachineStatusChange) |
4087
+ /** This local host's machine links changed (`machines.links.status`): shares of this computer, servers used as machines. */
4088
+ {
4089
+ kind: 'machine:links';
4090
+ links: MachineLinksStatus;
4091
+ } |
4092
+ /**
4093
+ * A server lending itself as a machine to the local host on this
4094
+ * connection (`machine.attach`): one of its machine notifications
4095
+ * (`terminal.data`, `task.exit`, `exec.stream.*`). Sent to that
4096
+ * connection only, never broadcast.
4097
+ */
4098
+ {
4099
+ kind: 'machine:notification';
4100
+ method: string;
4101
+ params: JsonValue;
4102
+ } |
1987
4103
  /**
1988
4104
  * Daemon-side extension HUD snapshot (status lines + banners). Extension
1989
4105
  * hooks run in the daemon in daemon mode, so their `ctx.tui.setStatus` /
1990
4106
  * `setBanner` writes happen there — windows overlay this state under
1991
4107
  * their local HUD (local entries win per extension name).
1992
4108
  */
1993
- | {
4109
+ {
1994
4110
  kind: 'hud:state';
1995
4111
  statuses: Record<string, {
1996
4112
  text: string;
1997
4113
  loader: boolean;
1998
4114
  }>;
1999
4115
  banners: Record<string, string[]>;
2000
- } /** Daemon-side extension `ctx.tui.notify` — a one-shot transcript line. */ | {
4116
+ } |
4117
+ /** Daemon-side extension `ctx.tui.notify` — a one-shot transcript line. */
4118
+ {
2001
4119
  kind: 'hud:notify';
2002
4120
  extension: string;
2003
4121
  text: string;
2004
4122
  tone: 'info' | 'warn' | 'error';
2005
- }
4123
+ } |
2006
4124
  /**
2007
4125
  * The session's type-ahead queue changed (message queued while a run is
2008
4126
  * in flight, drained by the host on run end, pushed early, or dropped).
2009
4127
  * Broadcast to EVERY peer including the origin — the queue box is shared
2010
4128
  * state and all windows render the same list.
2011
4129
  */
2012
- | {
4130
+ {
2013
4131
  kind: 'queue:changed';
2014
4132
  sessionId: string;
2015
4133
  items: QueuedItem[];
2016
- }
4134
+ } |
2017
4135
  /**
2018
4136
  * Extension bus message (`ctx.bus.emit`, `'ipc'` permission). PRIVATE
2019
4137
  * per `extensionId`: receivers re-inject into the SAME extension's local
@@ -2026,7 +4144,7 @@ type HostSignal = {
2026
4144
  * is the emitter's per-process random id (additive — absent from older
2027
4145
  * peers, which dedupe on seq alone).
2028
4146
  */
2029
- | {
4147
+ {
2030
4148
  kind: 'extension:signal';
2031
4149
  extensionId: string;
2032
4150
  sessionId?: string;
@@ -2079,14 +4197,66 @@ interface ChatHostEnvironment {
2079
4197
  hostRuntime?: ExtensionHostRuntimeBridge;
2080
4198
  /** Base user-data directory (`~/.zidane`) — safelist, tool-result blobs. */
2081
4199
  userDataDir: () => string;
4200
+ /**
4201
+ * Root of the per-session artifact tree (`<dir>/sessions/<id>/`): persisted
4202
+ * tool outputs, background-task logs, MCP warning logs. Forks re-home and
4203
+ * deletes clean there. Defaults to {@link userDataDir}.
4204
+ */
4205
+ cacheDir?: () => string;
2082
4206
  /** Re-apply provider auth env right before building a provider. */
2083
4207
  applyAuthEnv?: () => void;
2084
4208
  /** OAuth credential store for MCP servers, when the host supports MCP auth. */
2085
4209
  mcpCredentialStore?: () => McpCredentialStore;
2086
4210
  /** Durable pending-approval persistence. Defaults to in-memory only. */
2087
4211
  approvalsPersistence?: PendingApprovalsStore;
4212
+ /**
4213
+ * Session environment providers (`chat/environment.ts`). Defaults to the
4214
+ * built-ins (`local`). Hosts add kinds here (worktree, docker, sandboxes).
4215
+ */
4216
+ environments?: EnvironmentRegistry;
4217
+ /** Environment secret store. Defaults to `<userDataDir>/environment-credentials.json`. */
4218
+ environmentSecrets?: () => EnvironmentSecrets;
4219
+ /** How often idle environments are checked against their kind's `idleSuspendMinutes`. Default 60 s (tests shorten it). */
4220
+ environmentIdleSweepMs?: number;
2088
4221
  /** Opt-in verbose stream logging to the host console. */
2089
4222
  debugStream?: boolean;
4223
+ /**
4224
+ * Confine the host to these folders (a zidane-server's `workspaceRoots`;
4225
+ * they must exist). The host checks them at every entry point that takes
4226
+ * a folder or a path (`ChatHost.workspace`, `../workspace-confinement`):
4227
+ * session creation (the wire, orchestration workers), folder changes,
4228
+ * launch-override `cwd`s (dropped), command / environment scans, and the
4229
+ * workspace file and exec methods, which also never reach the data dir
4230
+ * (`userDataDir`). A session without a folder works in the first root.
4231
+ * Unset (local hosts): unconfined.
4232
+ */
4233
+ workspaceRoots?: readonly string[];
4234
+ /**
4235
+ * Set by a zidane-server: it takes machines whatever its `remoteHosts`
4236
+ * setting (a server is remote by nature). Unset (local hosts): machines
4237
+ * exist only while remote hosts are on; off, `machines.json` is never read
4238
+ * and no machine kind resolves. `principalLive` answers whether a machine
4239
+ * token is still valid: a machine id is bound to the token that first
4240
+ * registered it until that token is revoked (`docs/MACHINES.md` § Security model).
4241
+ */
4242
+ machineHost?: {
4243
+ principalLive?: (principal: string) => boolean;
4244
+ };
4245
+ /**
4246
+ * Whether this host has a surface that can show terminals (the GUI).
4247
+ * `false` (a host in the TUI's own process) turns the terminal feature off
4248
+ * for its agents: no terminal tools, background tasks on plain pipes.
4249
+ * Default `true`; the `terminal*` settings still apply on top.
4250
+ */
4251
+ terminals?: boolean;
4252
+ /**
4253
+ * Called with every agent the host builds (rebuilds included), before its
4254
+ * first run. The embedder's own instrumentation, such as lifecycle logging
4255
+ * into the TUI's Logs tab for a host in the TUI's process.
4256
+ */
4257
+ instrumentAgent?: (agent: Agent, context: {
4258
+ sessionId: string;
4259
+ }) => void;
2090
4260
  }
2091
4261
  //#endregion
2092
4262
  //#region src/chat/host/approvals.d.ts
@@ -2099,6 +4269,8 @@ interface ApprovalRequestInput {
2099
4269
  priorContent?: string | null;
2100
4270
  preview?: SerializedPreview;
2101
4271
  originator?: ApprovalOriginator;
4272
+ /** Skip the persisted safelist and offer nothing to remember: the prompt must show every time. */
4273
+ alwaysAsk?: boolean;
2102
4274
  }
2103
4275
  interface ApprovalHub {
2104
4276
  request: (input: ApprovalRequestInput) => Promise<ApprovalDecision>;
@@ -2111,29 +4283,12 @@ interface ApprovalHub {
2111
4283
  /** In-memory fallback when the host doesn't provide durable persistence. */
2112
4284
  declare function createMemoryApprovalsStore(): PendingApprovalsStore;
2113
4285
  declare function createApprovalHub(options: {
2114
- broadcast: (signal: HostSignal) => void; /** Base dir for the shared `projects.json` safelist (`~/.zidane`). */
4286
+ broadcast: (signal: HostSignal) => void;
4287
+ /** Base dir for the shared `projects.json` safelist (`~/.zidane`). */
2115
4288
  userDataDir: () => string;
2116
4289
  persistence?: PendingApprovalsStore;
2117
4290
  }): ApprovalHub;
2118
4291
  //#endregion
2119
- //#region src/chat/host/compaction.d.ts
2120
- interface CompactionEntry {
2121
- sessionId: string;
2122
- cwd: string;
2123
- agent: Agent;
2124
- providerKey: ProviderKey;
2125
- providerDescriptor?: ProviderDescriptor;
2126
- model: string;
2127
- compactionController?: AbortController;
2128
- }
2129
- /**
2130
- * Run `compactConversation` against the session's current turns, persist
2131
- * the resulting summary as a new turn, and emit a compact-summary stream
2132
- * event. Brackets the in-flight window with `compacting:start` /
2133
- * `compacting:end` signals.
2134
- */
2135
- declare function runCompaction(entry: CompactionEntry, broadcast: (signal: HostSignal) => void, store: SessionStore): Promise<void>;
2136
- //#endregion
2137
4292
  //#region src/chat/host/edit-outcome-ledger.d.ts
2138
4293
  interface PendingAnnotation {
2139
4294
  outcomes: readonly EditOutcome[];
@@ -2153,6 +4308,184 @@ declare class EditOutcomeLedger {
2153
4308
  clear(): void;
2154
4309
  }
2155
4310
  //#endregion
4311
+ //#region src/chat/model-chain-metadata.d.ts
4312
+ interface ModelFallbackTarget {
4313
+ providerKey: ProviderKey;
4314
+ modelId: string;
4315
+ }
4316
+ /**
4317
+ * Assemble the `metadata.modelChain` value for a model pick — the full
4318
+ * canonical chain when at least one valid fallback survives filtering,
4319
+ * `undefined` for a single-model pick (metadata merges JSON-serialize,
4320
+ * so `undefined` REMOVES a previously persisted chain).
4321
+ */
4322
+ declare function modelChainMetadata(providerKey: ProviderKey, modelId: string, fallbacks: readonly ModelFallbackTarget[] | undefined): ModelChainEntry[] | undefined;
4323
+ /**
4324
+ * Validated fallback tail for list rows/pickers — head-matched against
4325
+ * the session's persisted primary, unregistered providers kept (the
4326
+ * caller filters against its own registry when it has one).
4327
+ */
4328
+ declare function sessionModelFallbacks(metadata: Record<string, unknown> | undefined): ModelFallbackTarget[] | undefined;
4329
+ //#endregion
4330
+ //#region src/chat/session-factory.d.ts
4331
+ interface CreateSessionInStoreParams {
4332
+ /**
4333
+ * Tool-execution directory; `projectRoot` is derived from it. `''` creates a
4334
+ * DIRECTORY-LESS session (the GUI's "Chats" bucket): no project, and
4335
+ * `resolveSessionCwd` anchors tool execution at the user's HOME at run time.
4336
+ */
4337
+ cwd: string;
4338
+ /** Explicit id. Omitted → the store's generator, else a random UUID. */
4339
+ id?: string;
4340
+ title?: string;
4341
+ /** Profile the session starts in. Defaults to `DEFAULT_AGENT_ID` (`build`). */
4342
+ agentId?: string;
4343
+ providerKey?: string;
4344
+ modelId?: string;
4345
+ fallbacks?: readonly ModelFallbackTarget[];
4346
+ /** Mark as an extension-authoring session (auto-activates the docs skill). */
4347
+ extensionEdit?: boolean;
4348
+ /** Mark as a skill-authoring session for the named skill (dir = `cwd`). */
4349
+ skillEdit?: {
4350
+ name: string;
4351
+ };
4352
+ /**
4353
+ * Where the session's work happens (see `environment.ts`) — resolved by the
4354
+ * host (`initialEnvironment`), which also provisions it. Omitted or local +
4355
+ * ready → nothing is stamped: an absent environment IS the local one.
4356
+ */
4357
+ environment?: SessionEnvironment;
4358
+ /**
4359
+ * Extra host-specific metadata (e.g. a pinned-skills seed). Applied FIRST,
4360
+ * so every canonical key stamped here wins on collision.
4361
+ */
4362
+ metadata?: Record<string, unknown>;
4363
+ }
4364
+ /**
4365
+ * Build a new, UNSAVED session with every creation-time field stamped:
4366
+ * `projectRoot` (git root of `cwd`), `cwd`, `agentId`, `title`, the model and
4367
+ * its fallback chain, the authoring markers, and the environment.
4368
+ *
4369
+ * Unsaved on purpose: the TUI's local runtime creates the session when a chat
4370
+ * opens but only persists it on the first run, so opening and abandoning
4371
+ * `/new` never leaves an empty row behind. Everyone else wants
4372
+ * {@link createSessionInStore}.
4373
+ *
4374
+ * `store` may be `undefined` for an in-memory-only session (ACP without a
4375
+ * store): same stamping, no persistence.
4376
+ */
4377
+ declare function newSession(store: SessionStore | undefined, params: CreateSessionInStoreParams): Promise<Session>;
4378
+ /** Create and persist a session; returns its id. */
4379
+ declare function createSessionInStore(store: SessionStore, params: CreateSessionInStoreParams): Promise<string>;
4380
+ /**
4381
+ * Create the orchestration main session; returns its id.
4382
+ *
4383
+ * Directory-less on purpose. The main is GLOBAL — one per store, orchestrating
4384
+ * sessions in many directories — so binding it to whichever cwd happened to be
4385
+ * active when the toggle flipped put it in that project's group and hid it
4386
+ * from every other project's list. With no cwd it sits in the "Chats" bucket
4387
+ * and its own tools run from HOME, which is the only directory that is always
4388
+ * the user's and never a project's.
4389
+ */
4390
+ declare function createMainSession(store: SessionStore): Promise<string>;
4391
+ /** What a fork reads from its parent — satisfied by `SessionData` and a live `Session`. */
4392
+ type ForkSource = Pick<SessionData, 'id' | 'turns' | 'runs' | 'metadata' | 'agentId' | 'projectRoot'>;
4393
+ interface ForkSessionParams {
4394
+ /**
4395
+ * Parent session — an id to load from `store`, or the parent itself (a
4396
+ * host holding the live `Session` passes it so unsaved turns fork too).
4397
+ */
4398
+ source: ForkSource | string;
4399
+ /** Last turn the fork keeps (inclusive); the prefix is made protocol-clean. */
4400
+ turnId: string;
4401
+ /**
4402
+ * Root of the per-session artifact tree (`<userDir>/sessions/<id>/`) that
4403
+ * the parent's persisted-output stubs point into. The fork gets its own
4404
+ * copies so deleting the parent can't strand the fork's content.
4405
+ */
4406
+ userDir: string;
4407
+ /** Title override. Default: the parent's explicit title, if any. */
4408
+ title?: string;
4409
+ }
4410
+ type ForkSessionResult = {
4411
+ ok: true;
4412
+ id: string;
4413
+ } | {
4414
+ ok: false;
4415
+ reason: 'source-not-found' | 'turn-not-found' | 'environment-not-ready';
4416
+ };
4417
+ /** The error a user sees when a fork is refused, the same on every surface. */
4418
+ declare function describeForkFailure(reason: Extract<ForkSessionResult, {
4419
+ ok: false;
4420
+ }>['reason'], sessionId: string, turnId: string): string;
4421
+ /**
4422
+ * Create and persist a new session whose history is the parent's turns up to
4423
+ * and including `turnId`. The parent is untouched.
4424
+ *
4425
+ * Carries: the protocol-clean turn prefix, the run records those turns
4426
+ * reference (so run stats survive and the next run can't re-mint `run_1`
4427
+ * over an existing reference), persisted-output blobs (copied and re-pointed),
4428
+ * `projectRoot` (a fork belongs to its parent's project, wherever it was
4429
+ * forked from), `agentId`, {@link FORK_INHERITED_METADATA_KEYS}, and the
4430
+ * parent's environment — marked not-owned: the fork runs in the same
4431
+ * worktree / sandbox but deleting it never tears that down. Refused while
4432
+ * the parent's environment isn't `ready`: nothing would provision the fork's
4433
+ * copy, and provisioning it would mint an environment nobody owns.
4434
+ */
4435
+ declare function forkSession(store: SessionStore, params: ForkSessionParams): Promise<ForkSessionResult>;
4436
+ /**
4437
+ * Delete a session's persisted data: the store row, then every on-disk
4438
+ * artifact it owns — the session-first tree (`<userDir>/sessions/<id>/`:
4439
+ * blobs, task logs, MCP warnings) plus the two pre-session-first locations
4440
+ * migration leaves behind. Blob cleanup is best-effort and never throws; it
4441
+ * runs AFTER the row delete so a cleanup failure can't leave a live row
4442
+ * pointing at removed blobs.
4443
+ *
4444
+ * Does NOT stop a live agent — hosts must retire it first (see
4445
+ * `ChatHost.deleteSession`), or its next save resurrects the row.
4446
+ */
4447
+ declare function deleteSessionData(store: SessionStore, sessionId: string, opts: {
4448
+ userDir: string;
4449
+ }): Promise<void>;
4450
+ //#endregion
4451
+ //#region src/chat/workspace-confinement.d.ts
4452
+ interface WorkspaceConfinement {
4453
+ readonly confined: boolean;
4454
+ /** Real paths: the workspace roots, or the user's home when unconfined. */
4455
+ readonly roots: readonly string[];
4456
+ /** The real path of `path` when it is a folder inside the roots; throws otherwise. */
4457
+ resolveFolder: (path: string) => string;
4458
+ /** A folder a session runs in or a scan reads: checked when confined (real path returned); `''` (no folder) passes. */
4459
+ checkFolder: (cwd: string) => string;
4460
+ /** A new session's folder from a client: `cwd` checked when confined, the first root when confined and empty. */
4461
+ sessionFolder: (cwd: string) => string;
4462
+ /** Where a session without a folder works on a confined host (the first root); `undefined` when unconfined. */
4463
+ folderlessCwd: () => string | undefined;
4464
+ /**
4465
+ * A file or folder a workspace entry point reads or writes, on this
4466
+ * machine's filesystem: when confined, its real path (the nearest existing
4467
+ * ancestor's, for a file not written yet) must be inside the roots and not
4468
+ * in the data dir, or inside `environmentRoot` (a folder the host made for
4469
+ * the session, such as a worktree under the data dir). Throws otherwise;
4470
+ * a no-op when unconfined.
4471
+ */
4472
+ checkPath: (path: string, environmentRoot?: string) => void;
4473
+ }
4474
+ //#endregion
4475
+ //#region src/chat/host/browser.d.ts
4476
+ interface BrowserHub {
4477
+ registerHost: (hostId: string) => void;
4478
+ unregisterHost: (hostId: string) => void;
4479
+ hasHost: () => boolean;
4480
+ /** Fires when the first host arrives or the last one leaves. */
4481
+ onAvailabilityChange: (listener: (available: boolean) => void) => () => void;
4482
+ request: <O extends BrowserOp>(sessionId: string, op: O, input: BrowserOpInputs[O], signal: AbortSignal) => Promise<BrowserResponse<O>>;
4483
+ /** `fromHosts`: when given, only a request addressed to one of these hosts is answered. */
4484
+ respond: (requestId: string, response: BrowserResponse, fromHosts?: ReadonlySet<string>) => {
4485
+ ok: boolean;
4486
+ };
4487
+ }
4488
+ //#endregion
2156
4489
  //#region src/chat/host/interactions.d.ts
2157
4490
  interface InteractionHub {
2158
4491
  /** Build the `requestInteraction` callback bound to one session. */
@@ -2198,7 +4531,144 @@ declare function createInteractionHub(options: {
2198
4531
  onPlanRejected?: (sessionId: string) => void;
2199
4532
  }): InteractionHub;
2200
4533
  //#endregion
2201
- //#region src/chat/host/orchestration.d.ts
4534
+ //#region src/chat/host/launch-overrides.d.ts
4535
+ /** `zidane run --context …`: the execution this launch runs in, overriding the session's environment. */
4536
+ interface LaunchExecution {
4537
+ /** Execution context kind (`docker`, `e2b`, `daytona`). `process` is the default and not an override. */
4538
+ context: string;
4539
+ image?: string;
4540
+ env?: Record<string, string>;
4541
+ pregame?: {
4542
+ name: string;
4543
+ content: string;
4544
+ };
4545
+ sandboxId?: string;
4546
+ sandboxOnDestroy?: 'kill' | 'pause' | 'leave';
4547
+ }
4548
+ /** `ChatHost.setLaunchOverrides`: what the window overrides on a session's agent. */
4549
+ interface SessionLaunchOverrides {
4550
+ /** Replaces the base system prompt (extension fragments still append). */
4551
+ system?: string;
4552
+ /** Extra MCP servers, after the discovered and plugin ones (which win a name collision). */
4553
+ mcp?: readonly McpServerConfig[];
4554
+ execution?: LaunchExecution;
4555
+ /**
4556
+ * Working directory picked mid-session (the TUI's cwd picker), instead of
4557
+ * the session's folder: the live agent's tools move at once, its prompt on
4558
+ * the next turn, and later builds use it. Not persisted.
4559
+ */
4560
+ cwd?: string;
4561
+ }
4562
+ /**
4563
+ * The overrides that shape an agent, or `null` when none do (no `system`,
4564
+ * no MCP server, no non-`process` context, no cwd). The one rule for "are launch
4565
+ * overrides set", shared by the host, the daemon dispatcher, and the TUI.
4566
+ */
4567
+ declare function normalizeLaunchOverrides(overrides: SessionLaunchOverrides | null | undefined): SessionLaunchOverrides | null;
4568
+ //#endregion
4569
+ //#region src/chat/host/machines/folders.d.ts
4570
+ interface ResolvedFolder {
4571
+ cwd: string;
4572
+ /** Set when the folder is a machine's: its name and its `local@` kind. */
4573
+ machine?: {
4574
+ name: string;
4575
+ kind: string;
4576
+ };
4577
+ }
4578
+ type FolderResolver = (cwd: string, kind?: string) => ResolvedFolder;
4579
+ //#endregion
4580
+ //#region src/chat/host/machines/project-files.d.ts
4581
+ interface MachineProject {
4582
+ /** The project root on the machine: its git root when inside the roots, else the session folder. */
4583
+ root: string;
4584
+ /** Project-scope `AGENTS.md` / `CLAUDE.md`, in the order the local discovery loads them. */
4585
+ agentsMd: AgentsMdFile[];
4586
+ /** `<root>/SYSTEM.md`, when present. */
4587
+ systemMd: SystemMdFile | null;
4588
+ skills: SkillConfig[];
4589
+ /** Stdio servers, context-scoped, placeholders resolved. */
4590
+ mcps: DiscoveredMcp[];
4591
+ /** What was left out, for the session's notices. */
4592
+ notices: string[];
4593
+ }
4594
+ //#endregion
4595
+ //#region src/chat/host/machines/link.d.ts
4596
+ /** The transport's side of a live machine connection (`src/ipc/daemon/machine-gateway.ts`). */
4597
+ interface MachineConnection {
4598
+ /** Rejects with {@link MachineLinkLostError} when the connection drops before the reply. */
4599
+ request: (method: string, params: unknown) => Promise<unknown>;
4600
+ notify: (method: string, params: unknown) => void;
4601
+ close: (reason: string) => void;
4602
+ }
4603
+ interface MachineCallOptions {
4604
+ /** The session the call is for: its notices, its give-up. */
4605
+ sessionId?: string;
4606
+ signal?: AbortSignal;
4607
+ /** Called once when `signal` aborts a call already sent (cancel it machine-side). */
4608
+ onAbort?: () => void;
4609
+ /** `false`: fail at once when offline instead of waiting (teardown, terminal keystrokes). */
4610
+ wait?: boolean;
4611
+ }
4612
+ interface TerminalListener {
4613
+ onData: (data: Uint8Array) => void;
4614
+ exit: (code: number) => void;
4615
+ /** Bytes received so far: a reattach replays only what came after. */
4616
+ seen: number;
4617
+ }
4618
+ /** A live `exec.stream` process, as the host side hears it. */
4619
+ interface StreamListener {
4620
+ data: (stream: 'stdout' | 'stderr', bytes: Uint8Array) => void;
4621
+ exit: (code: number) => void;
4622
+ }
4623
+ interface MachineLink {
4624
+ readonly machineId: string;
4625
+ name: () => string;
4626
+ online: () => boolean;
4627
+ call: <M extends MachineMethodName>(method: M, params: MachineParams<M>, options?: MachineCallOptions) => Promise<MachineResult<M>>;
4628
+ /** Fire-and-forget; dropped while offline. */
4629
+ notify: (method: string, params: unknown) => void;
4630
+ /** Background task exits by task id (the machine sends `task.exit`). */
4631
+ tasks: Map<string, (exit: unknown) => void>;
4632
+ /** Live terminals by id (the machine sends `terminal.data` / `terminal.exit`). */
4633
+ terminals: Map<string, TerminalListener>;
4634
+ /** Live streams by call id (`exec.stream.data` / `exec.stream.exit`); a dropped link ends them (exit -1). */
4635
+ streams: Map<string, StreamListener>;
4636
+ /** A notification from the machine. */
4637
+ deliver: (method: string, params: unknown) => void;
4638
+ /** The registry swaps the connection (`null`: offline). */
4639
+ setConnection: (connection: MachineConnection | null) => void;
4640
+ connection: () => MachineConnection | null;
4641
+ /** The host is closing: fail every waiting call, and wait no more. */
4642
+ shutdown: () => void;
4643
+ }
4644
+ //#endregion
4645
+ //#region src/chat/host/machines/registry.d.ts
4646
+ /** One registered connection, as the transport drives it. */
4647
+ interface AttachedMachine {
4648
+ result: MachineRegisterResult;
4649
+ /** A notification the machine sent. */
4650
+ notification: (method: string, params: unknown) => void;
4651
+ /** Its connection closed. */
4652
+ closed: () => void;
4653
+ }
4654
+ interface MachineRegistry {
4655
+ list: () => MachineInfo[];
4656
+ get: (machineId: string) => MachineInfo | undefined;
4657
+ link: (machineId: string) => MachineLink | undefined;
4658
+ attach: (params: MachineRegisterParams, connection: MachineConnection, meta?: {
4659
+ principal?: string;
4660
+ }) => AttachedMachine;
4661
+ /** Forget a machine (it disconnects; its environments stop resolving). */
4662
+ forget: (machineId: string) => boolean;
4663
+ /** A notification for `machineId`'s link (tests drive it directly). */
4664
+ deliver: (machineId: string, method: string, params: unknown) => void;
4665
+ /** The host is draining: calls waiting for an offline machine fail now instead of holding the drain. */
4666
+ close: () => void;
4667
+ /** Run `fn` when the host drains (what keeps links to machines open stops with it). */
4668
+ onClose: (fn: () => void) => void;
4669
+ }
4670
+ //#endregion
4671
+ //#region src/chat/host/orchestration/types.d.ts
2202
4672
  interface CreateWorkerParams {
2203
4673
  /** Directory the worker runs in — the headline use case. */
2204
4674
  cwd: string;
@@ -2272,7 +4742,98 @@ interface Orchestration {
2272
4742
  transcript: (mainId: string, sessionId: string, limit?: number) => Promise<string>;
2273
4743
  }
2274
4744
  //#endregion
2275
- //#region src/chat/host/engine.d.ts
4745
+ //#region src/chat/host/session-title.d.ts
4746
+ /** The window's pick, honored when this host has that provider. */
4747
+ interface SessionTitleHint {
4748
+ providerKey?: string;
4749
+ model?: string;
4750
+ }
4751
+ //#endregion
4752
+ //#region src/chat/host/terminal/types.d.ts
4753
+ interface AgentTaskTerminal {
4754
+ /** Pass as `execBackground`'s `terminal` option. */
4755
+ options: BackgroundTerminalOptions;
4756
+ /** The task started; `kill` ends it (closing the tab), `terminal` says whether it got a PTY. */
4757
+ started: (task: {
4758
+ taskId: string;
4759
+ terminal: boolean;
4760
+ kill: () => Promise<void>;
4761
+ }) => void;
4762
+ /** The task ended (or failed to start). */
4763
+ exited: (exitCode: number | null) => void;
4764
+ }
4765
+ interface TerminalManager {
4766
+ state: (sessionId: string) => Promise<TerminalSessionState>;
4767
+ open: (input: TerminalOpenInput) => Promise<TerminalInfo>;
4768
+ attach: (terminalId: string, clientId: string) => Promise<TerminalAttachResult>;
4769
+ detach: (terminalId: string, clientId: string) => void;
4770
+ /** Forget a client everywhere (its connection dropped). */
4771
+ detachClient: (clientId: string) => void;
4772
+ /** `from.response`: a reply the view's emulator generated, not typing (see the implementation). */
4773
+ write: (terminalId: string, data: string, from?: {
4774
+ clientId: string;
4775
+ response?: boolean;
4776
+ }) => Promise<void>;
4777
+ resize: (terminalId: string, clientId: string, cols: number, rows: number, claim?: boolean) => void;
4778
+ ack: (terminalId: string, clientId: string, offset: number) => void;
4779
+ restart: (terminalId: string) => Promise<TerminalInfo>;
4780
+ close: (terminalId: string) => Promise<void>;
4781
+ setHidden: (terminalId: string, hidden: boolean) => void;
4782
+ /** Who opened a terminal (`undefined` when unknown): approvals always ask before typing into a user's. */
4783
+ ownerOf: (terminalId: string) => TerminalInfo['owner'] | undefined;
4784
+ /** The agent's list of the session's terminals (skips hidden ones). */
4785
+ list: (sessionId: string, opts?: {
4786
+ includeHidden?: boolean;
4787
+ }) => Promise<TerminalInfo[]>;
4788
+ read: (sessionId: string, terminalId: string, opts?: {
4789
+ since?: number;
4790
+ lines?: number;
4791
+ }) => Promise<TerminalReadResult>;
4792
+ /** Resolves once output has been quiet for `quietMs` (or `maxMs` passed). */
4793
+ settle: (terminalId: string, opts?: {
4794
+ quietMs?: number;
4795
+ maxMs?: number;
4796
+ signal?: AbortSignal;
4797
+ }) => Promise<void>;
4798
+ /** A terminal for an agent background task, shown as an agent tab. */
4799
+ agentTask: (sessionId: string, input: {
4800
+ command: string;
4801
+ cwd: string;
4802
+ environment: string;
4803
+ }) => AgentTaskTerminal;
4804
+ /** Used recently (input or output), or running an agent task: keep the environment awake. */
4805
+ busy: (sessionId: string, withinMs?: number) => boolean;
4806
+ /** A user terminal runs in the session's execution and can't reconnect: releasing the execution would kill it. */
4807
+ holdsExecution: (sessionId: string) => boolean;
4808
+ /** Any user terminal that ends with this host (local PTYs) is running. */
4809
+ hasRunningLocal: () => boolean;
4810
+ /** The environment is pausing: let go of remote terminals (they reconnect later). */
4811
+ suspendSession: (sessionId: string) => Promise<void>;
4812
+ /** The session is gone: end its terminals and forget them. */
4813
+ closeSession: (sessionId: string) => Promise<void>;
4814
+ /**
4815
+ * Terminals were turned off: end every terminal a user opened. Agent task
4816
+ * terminals are left to their tasks (killing a running dev server over a
4817
+ * settings switch would be worse than letting it finish).
4818
+ */
4819
+ closeUserTerminals: () => Promise<void>;
4820
+ /** Host exit: end local terminals, let go of remote ones. */
4821
+ shutdown: () => Promise<void>;
4822
+ /**
4823
+ * Clients that can SHOW terminals (the desktop app on the daemon). Only
4824
+ * then does the agent get terminal tools: a TUI window, `zidane attach`, or
4825
+ * the desktop app's in-process host has no terminal panel, so tools there
4826
+ * would point the model at terminals nobody can see. Same contract as the
4827
+ * browser hub's hosts.
4828
+ */
4829
+ registerViewer: (viewerId: string) => void;
4830
+ unregisterViewer: (viewerId: string) => void;
4831
+ hasViewer: () => boolean;
4832
+ /** Fires when the first viewer arrives or the last one leaves. */
4833
+ onViewerChange: (listener: (available: boolean) => void) => () => void;
4834
+ }
4835
+ //#endregion
4836
+ //#region src/chat/host/host-api.d.ts
2276
4837
  /**
2277
4838
  * Instructions-tab payload. Mirrors the TUI's `getContextSegments` contract so
2278
4839
  * both settings surfaces render identical rows and identical explanations for
@@ -2289,6 +4850,15 @@ interface ContextSegmentsReport {
2289
4850
  /** A `SYSTEM.md` on disk that lost precedence — surfaced so edits to it don't look ignored for no reason. */
2290
4851
  shadowedSystemPath?: string;
2291
4852
  }
4853
+ /** `ChatHost.workspaceExec` options: the execution contexts' own, minus the (process-local) abort signal. */
4854
+ interface WorkspaceExecOptions {
4855
+ /** Working directory, resolved against the workspace root like any path. */
4856
+ cwd?: string;
4857
+ env?: Record<string, string>;
4858
+ timeoutSeconds?: number;
4859
+ }
4860
+ /** `ChatHost.workspaceListFiles` options: `ListFilesOptions` minus the abort signal. */
4861
+ type WorkspaceListFilesOptions = Omit<ListFilesOptions, 'signal'>;
2292
4862
  /** Everything a model pick carries — see `ChatHost.setSessionModel`. */
2293
4863
  interface SessionModelPick {
2294
4864
  providerKey: ProviderKey;
@@ -2300,39 +4870,87 @@ interface SessionModelPick {
2300
4870
  /** Enabled model options for the new model; omitted clears them. */
2301
4871
  modelOptions?: Record<string, boolean>;
2302
4872
  }
4873
+ /** `ChatHost.createSession` input: the factory params, with the environment as a request the host resolves. */
4874
+ type CreateSessionRequest = Omit<CreateSessionInStoreParams, 'environment'> & {
4875
+ /** Where the session's work happens. Omitted → local. */
4876
+ environment?: EnvironmentRequest;
4877
+ };
2303
4878
  interface ChatHost {
2304
4879
  /** HITL hubs — exposed so transports can route respond/list/rehydrate calls. */
2305
4880
  interactions: InteractionHub;
2306
4881
  approvals: ApprovalHub;
4882
+ /** Dev browser hub: the GUI registers as the host of the pages the `browser_*` tools drive. */
4883
+ browser: BrowserHub;
4884
+ /** The sessions' terminals (run in their environments; shown by the GUI, read by the agent). */
4885
+ terminals: TerminalManager;
2307
4886
  sendPrompt: (opts: SendOptions) => Promise<{
2308
4887
  runId: string;
2309
4888
  }>;
2310
4889
  interrupt: (sessionId: string) => void;
2311
- steer: (sessionId: string, message: string) => void;
2312
- followUp: (sessionId: string, message: string) => void;
4890
+ /** `false` when no run was live to take the message (it was dropped, not queued). */
4891
+ steer: (sessionId: string, message: string) => boolean;
4892
+ /** `false` when no run was live to take the message (it was dropped, not queued). */
4893
+ followUp: (sessionId: string, message: string) => boolean;
2313
4894
  /** Steer when a run is active, start a fresh run otherwise. */
2314
4895
  pushQueued: (sessionId: string, message: string, activatedSkills?: string[]) => Promise<{
2315
4896
  delivered: 'steer' | 'send' | 'command' | 'queued';
2316
4897
  }>;
2317
- /** Wire-safe `/` command catalog for a client's completion popover. */
2318
- listCommands: (cwd: string) => Promise<CommandSummary[]>;
4898
+ /**
4899
+ * Where sessions may work: a server's workspace roots (`confined`), or
4900
+ * unconfined on a local host. The host checks it itself at every entry
4901
+ * point taking a folder or a path; embedders read it for listings.
4902
+ */
4903
+ workspace: WorkspaceConfinement;
4904
+ /** Machines lending this host their execution (`docs/MACHINES.md`). */
4905
+ machines: MachineRegistry;
4906
+ /**
4907
+ * Whose folder `cwd` is (`docs/MACHINES.md` § Folders): a machine kind's
4908
+ * (`kind`, or the machine whose roots hold it when this host refuses it),
4909
+ * checked against that machine's roots; else this host's, checked by its
4910
+ * confinement. Throws when neither takes it.
4911
+ */
4912
+ resolveFolder: FolderResolver;
4913
+ /** {@link resolveFolder}, then a machine folder checked on the machine (real path, exists). */
4914
+ verifyFolder: (cwd: string, kind?: string) => Promise<ResolvedFolder>;
4915
+ /**
4916
+ * What to scan for a folder's catalogs: this host's folder, or for a
4917
+ * machine's, a folder of this host's for the user scope plus the project
4918
+ * files read on the machine.
4919
+ */
4920
+ folderCatalog: (cwd: string, kind?: string, prefix?: string) => Promise<{
4921
+ discoveryCwd: string;
4922
+ project?: MachineProject;
4923
+ }>;
4924
+ /** Wire-safe `/` command catalog for a client's completion popover. `kind`: the session's environment (a machine's folder). */
4925
+ listCommands: (cwd: string, kind?: string) => Promise<CommandSummary[]>;
2319
4926
  /** Argument suggestions for `/name <prefix>` — best-effort, empty on failure. */
2320
- completeCommandArgs: (cwd: string, name: string, argsPrefix: string) => Promise<CommandArgSuggestionSummary[]>;
4927
+ completeCommandArgs: (cwd: string, name: string, argsPrefix: string, kind?: string) => Promise<CommandArgSuggestionSummary[]>;
2321
4928
  /**
2322
4929
  * Shared type-ahead queue — host-owned so every window renders and
2323
4930
  * manipulates the same list (`queue:changed` fan-out). `queueAdd`
2324
- * queues while a run is in flight and sends immediately otherwise;
2325
- * the host drains FIFO at run end.
4931
+ * queues while a run is in flight — or while this host is still
4932
+ * provisioning the session's environment — and sends immediately
4933
+ * otherwise; the host drains FIFO at run end and when provisioning settles.
2326
4934
  */
2327
- queueAdd: (sessionId: string, text: string, activatedSkills?: string[], attachments?: PromptAttachment[], /** See `SendOptions.asCommand`. */
2328
-
4935
+ queueAdd: (sessionId: string, text: string, activatedSkills?: string[], attachments?: PromptAttachment[],
4936
+ /** See `SendOptions.asCommand`. */
2329
4937
  asCommand?: string) => Promise<{
2330
- delivered: 'queued' | 'sent';
4938
+ delivered: 'queued' | 'sent' | 'steer';
2331
4939
  }>;
2332
4940
  queueRemove: (sessionId: string, id: string) => boolean;
2333
- /** Edit a queued entry's text in place (keeps its position). `false` when the id is gone. */
2334
- queueUpdate: (sessionId: string, id: string, text: string) => boolean;
4941
+ /**
4942
+ * Edit a queued entry in place (keeps its position). `attachments`, when
4943
+ * given, replaces the entry's list (`[]` removes them all). `false` when
4944
+ * the id is gone.
4945
+ */
4946
+ queueUpdate: (sessionId: string, id: string, text: string, attachments?: PromptAttachment[]) => boolean;
2335
4947
  queueList: (sessionId: string) => QueuedItem[];
4948
+ /**
4949
+ * A queued entry's attachments WITH their bytes, which `queueList` and
4950
+ * `queue:changed` leave out, for a window that previews or edits them.
4951
+ * `null` when the id is gone.
4952
+ */
4953
+ queueAttachments: (sessionId: string, id: string) => PromptAttachment[] | null;
2336
4954
  /**
2337
4955
  * The `SYSTEM.md` override composing this session's prompt, for windows
2338
4956
  * attaching to an ALREADY-WARM agent (the `system-md` signal only fires at
@@ -2344,7 +4962,7 @@ interface ChatHost {
2344
4962
  shadowed: readonly SystemMdFile[];
2345
4963
  } | null;
2346
4964
  queuePushNow: (sessionId: string, id: string) => Promise<{
2347
- delivered: 'steer' | 'send' | 'missing';
4965
+ delivered: 'steer' | 'send' | 'queued' | 'missing';
2348
4966
  }>;
2349
4967
  isSessionRunning: (sessionId: string) => boolean;
2350
4968
  /** Sessions with a live in-memory agent, with their run state (presence). */
@@ -2366,7 +4984,7 @@ interface ChatHost {
2366
4984
  * in-memory snapshot didn't see. Not wire-exposable.
2367
4985
  */
2368
4986
  getLiveSession: (sessionId: string) => Session | null;
2369
- /** True when ANY session has a live run, in-flight compaction, or tracked work. */
4987
+ /** True when ANY session has a live run, in-flight compaction, tracked work, or a running local terminal. */
2370
4988
  hasActiveWork: () => boolean;
2371
4989
  listRunningWork: (sessionId: string) => RunningWorkItem[];
2372
4990
  cancelRunningWork: (sessionId: string, id: string | 'all', reason?: string) => Promise<boolean>;
@@ -2378,18 +4996,114 @@ interface ChatHost {
2378
4996
  */
2379
4997
  setTodos: (sessionId: string, items: readonly TodoItem[]) => Promise<ActiveTodosState | null>;
2380
4998
  /**
2381
- * Create a session in the shared store and return its id. The one factory
2382
- * shared by the daemon, the embedded GUI host, and orchestration.
4999
+ * Create a session in the shared store and return its id. The shared
5000
+ * lifecycle factory (`session-factory.ts`) bound to this host's store —
5001
+ * used by the daemon, the embedded GUI host, and orchestration.
5002
+ *
5003
+ * `environment` picks where the session's work happens (default: local).
5004
+ * A kind that needs provisioning returns immediately with the session in
5005
+ * `provisioning`; it becomes `ready` / `failed` in the background, each
5006
+ * transition announced as `sessions:changed`. Throws for an unknown or
5007
+ * unavailable kind.
2383
5008
  */
2384
- createSession: (params: {
2385
- cwd: string;
2386
- title?: string;
2387
- agentId?: string;
2388
- providerKey?: string;
2389
- modelId?: string;
2390
- fallbacks?: readonly ModelFallbackTarget[];
2391
- extensionEdit?: boolean;
2392
- }) => Promise<string>;
5009
+ createSession: (params: CreateSessionRequest) => Promise<string>;
5010
+ /**
5011
+ * Provision the session's environment again — after a failure, or when a
5012
+ * host restart left it `provisioning` with nothing running. No-op while
5013
+ * this host is already provisioning it or when it is ready. With
5014
+ * `background`, resolves once the row shows `provisioning` instead of when
5015
+ * the attempt ends — so a caller polling the row never reads the old failure.
5016
+ */
5017
+ retryEnvironment: (sessionId: string, opts?: {
5018
+ background?: boolean;
5019
+ }) => Promise<void>;
5020
+ /** Environment kinds this host can create and run. */
5021
+ environments: EnvironmentRegistry;
5022
+ /** Where this host keeps environment secrets (settings writes go through it). */
5023
+ environmentSecrets: EnvironmentSecrets;
5024
+ /**
5025
+ * Every registered kind described for a new session in `cwd` — enabled,
5026
+ * default, availability (with the reason), branch suggestion, options
5027
+ * (secrets only as "configured"). What pickers and settings render.
5028
+ */
5029
+ listEnvironments: (cwd: string, kind?: string) => Promise<EnvironmentDescriptor[]>;
5030
+ /** "Test connection" for `kind` with its current options. */
5031
+ verifyEnvironment: (kind: string) => Promise<EnvironmentVerifyResult>;
5032
+ /** The per-session commands the session's environment offers right now (push, open PR, …). */
5033
+ listEnvironmentActions: (sessionId: string) => Promise<EnvironmentActionSummary[]>;
5034
+ /**
5035
+ * Run one of {@link listEnvironmentActions} — in the session's environment,
5036
+ * resuming it when suspended. Throws with a user-facing message on failure.
5037
+ */
5038
+ runEnvironmentAction: (sessionId: string, actionId: string) => Promise<EnvironmentActionResult>;
5039
+ /**
5040
+ * The session's workspace: shell + file operations in the session's own
5041
+ * execution context (the one its agent's tools use), for any session.
5042
+ * The root is `environment.execCwd` when the files live inside the
5043
+ * environment (a sandbox's clone), else the session's cwd on this machine.
5044
+ * What the GUI's Files panel, mentions, `!` and diffs use for sandboxed
5045
+ * sessions, and what window-side extension code reaches as
5046
+ * `ctx.workspace` in the TUI's daemon mode.
5047
+ *
5048
+ * Paths (and exec `cwd`) resolve against the root exactly as the execution
5049
+ * context resolves them ({@link workspacePath}); containment is the
5050
+ * context's own (sandboxes confine, the local process context doesn't).
5051
+ * Exec `timeoutSeconds` defaults to 30, like the execution contexts.
5052
+ */
5053
+ workspaceExec: (sessionId: string, command: string, options?: WorkspaceExecOptions) => Promise<ExecResult>;
5054
+ /**
5055
+ * The same command as a live process (`ExecutionContext.execStream`, a
5056
+ * machine's included): its output as it comes, `kill()` ends its process
5057
+ * group. `null` when the session's context can't stream (callers fall back
5058
+ * to {@link workspaceExec}). Same `cwd` rules; no timeout: the caller owns it.
5059
+ */
5060
+ workspaceExecStream: (sessionId: string, command: string, options?: Omit<WorkspaceExecOptions, 'timeoutSeconds'>) => Promise<StreamHandle | null>;
5061
+ workspaceReadFile: (sessionId: string, path: string) => Promise<string>;
5062
+ workspaceWriteFile: (sessionId: string, path: string, content: string) => Promise<void>;
5063
+ /** `ListFilesOptions` semantics (`glob: true` treats `path` as a pattern under the root). */
5064
+ workspaceListFiles: (sessionId: string, path: string, options?: WorkspaceListFilesOptions) => Promise<ListFilesEntry[]>;
5065
+ /** Byte size of a file, or `null` when missing or unknowable. */
5066
+ workspaceFileSize: (sessionId: string, path: string) => Promise<number | null>;
5067
+ /**
5068
+ * Fork a session at a turn into a new persisted session (see
5069
+ * `forkSession` in `session-factory.ts`); blobs are rehomed under this
5070
+ * host's user dir.
5071
+ */
5072
+ forkSession: (params: Omit<ForkSessionParams, 'userDir'>) => Promise<ForkSessionResult>;
5073
+ /**
5074
+ * History edits. Both refuse while the session runs (in this host, or by
5075
+ * its fresh run marker in another process): the run's saves would erase
5076
+ * the edit, and its context would keep the old turn anyway, so the user
5077
+ * stops the run first. The write is atomic when the store offers
5078
+ * `mutateTurns`; the cached agent is retired so the next run reads the
5079
+ * edited history, and `sessions:changed` tells every window.
5080
+ */
5081
+ deleteTurn: (sessionId: string, turnId: string) => Promise<void>;
5082
+ /** Replace a turn's text, keeping its non-text blocks (see `replaceTurnTextBlocks`). */
5083
+ editTurnText: (sessionId: string, turnId: string, text: string) => Promise<void>;
5084
+ /**
5085
+ * Bind a session to a folder (`cwd`), or back to a plain chat (`''`):
5086
+ * `metadata.cwd` plus the derived project root. Refused while running,
5087
+ * like history edits; the Main session can't be bound.
5088
+ */
5089
+ setSessionFolder: (sessionId: string, cwd: string) => Promise<void>;
5090
+ /**
5091
+ * Append turns written outside a run (the `!` shell's output). Through the
5092
+ * live session when one exists, so its next save keeps them; to the store
5093
+ * otherwise. Announced with `sessions:changed`.
5094
+ */
5095
+ appendTurns: (sessionId: string, turns: SessionTurn[]) => Promise<void>;
5096
+ /**
5097
+ * THE session delete: retire the live agent + pending HITL state, end its
5098
+ * execution (background tasks included), tear down the environment it
5099
+ * owns, then remove the row and every on-disk artifact. Every surface's
5100
+ * delete must end here (the daemon RPC, the embedded GUI host). Resolves
5101
+ * with what happened to the environment (a provider may keep it, e.g. a
5102
+ * dirty worktree).
5103
+ */
5104
+ deleteSession: (sessionId: string) => Promise<{
5105
+ environment: EnvironmentTeardownResult;
5106
+ }>;
2393
5107
  /** Main-session orchestration — see `./orchestration.ts`. */
2394
5108
  orchestration: Orchestration;
2395
5109
  /**
@@ -2410,6 +5124,14 @@ interface ChatHost {
2410
5124
  patchSessionMeta: (sessionId: string, patch: Record<string, unknown>, opts?: {
2411
5125
  bumpUpdatedAt?: boolean;
2412
5126
  }) => Promise<void>;
5127
+ /**
5128
+ * Title the session with THIS host's providers and credentials (never the
5129
+ * window's: a server session's transcript stays with the server's
5130
+ * provider), write it as a rename (no recency bump) and return it. `hint`:
5131
+ * the window's provider/model, used when this host has that provider;
5132
+ * otherwise the session's own. Refuses the Main session.
5133
+ */
5134
+ generateSessionTitle: (sessionId: string, hint?: SessionTitleHint) => Promise<string>;
2413
5135
  /**
2414
5136
  * THE model-swap contract, shared by every surface: persists
2415
5137
  * `providerKey` / `modelId`, replaces `modelChain` (cleared when
@@ -2429,13 +5151,47 @@ interface ChatHost {
2429
5151
  */
2430
5152
  setLiveSessionAgentId: (sessionId: string, agentId: string) => Promise<boolean>;
2431
5153
  /**
2432
- * Retire the live agent so the next run rebuilds on freshly-patched
2433
- * metadata (model / agent swap). Deferred to run end while a run is in
2434
- * flight, so a swap never aborts a run mid-stream for other windows.
5154
+ * Retire the live agent so the next run rebuilds on freshly-patched
5155
+ * metadata (model / agent swap). Deferred to run end while a run is in
5156
+ * flight, so a swap never aborts a run mid-stream for other windows.
5157
+ */
5158
+ disposeAgentForRebuild: (sessionId: string) => void;
5159
+ /**
5160
+ * Launch-time overrides for one session's agent (`zidane run --system /
5161
+ * --mcp` on a host-backed window): what the local TUI applies to every
5162
+ * agent it builds, applied here to this session's. In memory only, never
5163
+ * persisted; they survive agent rebuilds and are dropped on `null`, on
5164
+ * session delete, or (daemon) when the client that set them disconnects.
5165
+ * A change retires the live agent like a model swap (deferred to run end),
5166
+ * except a cwd-only change, which moves the live agent in place.
5167
+ */
5168
+ setLaunchOverrides: (sessionId: string, overrides: SessionLaunchOverrides | null) => void;
5169
+ /**
5170
+ * `ctx.appendNote` outside a run (an extension's timer, a HUD action): a
5171
+ * display-only `command` turn, broadcast live and persisted under the same
5172
+ * id. A built agent's session takes it (so its later saves keep it);
5173
+ * otherwise it is appended in the store. False for an unknown session or
5174
+ * an empty note.
5175
+ */
5176
+ appendNote: (sessionId: string, blocks: readonly CommandOutputBlock[]) => Promise<boolean>;
5177
+ /**
5178
+ * `disposeAgentForRebuild` for every session with a live agent (extension
5179
+ * reload, agent-shaping settings). Resolves once every one of them has
5180
+ * actually been retired: immediately for idle sessions, at run end for
5181
+ * busy ones, so no run is aborted. `rediscover` also drops the discovery
5182
+ * caches (plugin catalogs), so the rebuilt agents re-walk the disk.
5183
+ */
5184
+ rebuildAllAgents: (options?: {
5185
+ rediscover?: boolean;
5186
+ }) => Promise<void>;
5187
+ /**
5188
+ * A settings write changed a key that shapes what agents are BUILT with
5189
+ * (terminal switches, `subagentModels`, `interactionMode` into or out of
5190
+ * `disabled`): agents are rebuilt (at run end when busy, no run aborted) so
5191
+ * their tools match, and turning terminals off closes the user's. Every
5192
+ * settings write path calls it with the before/after pair.
2435
5193
  */
2436
- disposeAgentForRebuild: (sessionId: string) => void;
2437
- /** `disposeAgentForRebuild` for every session with a live agent (extension reload). */
2438
- rebuildAllAgents: () => void;
5194
+ applyAgentSettings: (before: HostSettings, next: HostSettings) => void;
2439
5195
  getContextBreakdown: (sessionId: string) => Promise<ContextBreakdown | null>;
2440
5196
  /**
2441
5197
  * Every chunk of text in the session's system prompt, with provenance — the
@@ -2456,7 +5212,12 @@ interface ChatHost {
2456
5212
  path: string;
2457
5213
  created: boolean;
2458
5214
  }>;
2459
- compactSessionNow: (sessionId: string) => Promise<void>;
5215
+ /**
5216
+ * Compact the session now (any session: its agent is built if none is
5217
+ * live). Resolves what was done, `null` when nothing was (cancelled or
5218
+ * failed, both also announced as transcript events).
5219
+ */
5220
+ compactSessionNow: (sessionId: string) => Promise<CompactionReport | null>;
2460
5221
  /** Tear down one session's live agent + pending HITL state. */
2461
5222
  disposeSession: (sessionId: string) => void;
2462
5223
  /** Drain every session — call before process exit or config invalidation. */
@@ -2464,6 +5225,49 @@ interface ChatHost {
2464
5225
  /** Permanently refuse dispatch, abort work, and await setup, runs, and settlement. */
2465
5226
  drain: () => Promise<void>;
2466
5227
  }
5228
+ //#endregion
5229
+ //#region src/chat/host/compaction/run.d.ts
5230
+ /** What a compaction did, for the caller's confirmation (the TUI's "compacted N turns" banner). */
5231
+ interface CompactionReport {
5232
+ /** Turns absorbed into the summary. */
5233
+ replacedCount: number;
5234
+ /** Turns the prompt-too-long retry had to drop without summarizing (context lost). */
5235
+ droppedCount: number;
5236
+ /** Recently-read files and active skills re-injected after the summary. */
5237
+ restoredFiles: number;
5238
+ restoredSkills: number;
5239
+ model: string;
5240
+ inputTokens: number;
5241
+ outputTokens: number;
5242
+ /** Estimated context size on the next turn: the summary plus the restored content. */
5243
+ effectiveTokens: number;
5244
+ }
5245
+ interface CompactionEntry {
5246
+ sessionId: string;
5247
+ cwd: string;
5248
+ agent: Agent;
5249
+ providerKey: ProviderKey;
5250
+ providerDescriptor?: ProviderDescriptor;
5251
+ model: string;
5252
+ compactionController?: AbortController;
5253
+ }
5254
+ /**
5255
+ * Run `compactConversation` against the session's current turns, persist
5256
+ * the resulting summary as a new turn, and emit a compact-summary stream
5257
+ * event. Brackets the in-flight window with `compacting:start` /
5258
+ * `compacting:end` signals. Resolves what it did, or `null` when nothing
5259
+ * was compacted (no session, cancelled, failed: reported as an event).
5260
+ */
5261
+ declare function runCompaction(entry: CompactionEntry, broadcast: (signal: HostSignal) => void, store: SessionStore): Promise<CompactionReport | null>;
5262
+ //#endregion
5263
+ //#region src/chat/host/engine.d.ts
5264
+ /**
5265
+ * The composition root: creates the shared host state (`context.ts`) and the
5266
+ * feature modules, wires them to each other, and assembles the `ChatHost`.
5267
+ * Features never import this module. Where a feature needs one built after
5268
+ * it, it gets one of the hoisted forwarders at the bottom of this function
5269
+ * (only ever called at runtime, after construction).
5270
+ */
2467
5271
  declare function createChatHost(env: ChatHostEnvironment): ChatHost;
2468
5272
  //#endregion
2469
5273
  //#region src/chat/host/helpers.d.ts
@@ -2485,118 +5289,44 @@ declare function extractEditPayloadForGate(name: string, input: Record<string, u
2485
5289
  */
2486
5290
  declare function serializePreview(preview: PreviewResult): SerializedPreview;
2487
5291
  //#endregion
2488
- //#region src/chat/mcp-auth-state.d.ts
2489
- /**
2490
- * Per-server MCP OAuth status tracking, decoupled from the renderer.
2491
- *
2492
- * The TUI keeps a `Record<serverName, McpAuthStatus>` so it can render a
2493
- * "needs-auth" / "authorizing" / "error" badge in the MCP picker. The
2494
- * status map is driven entirely by hooks fired from the agent's MCP
2495
- * bootstrap + the interactive `loginMcpServer` flow — the reducer below
2496
- * is pure, makes no FS or network calls, and is shared between the live
2497
- * TUI store and the test harness.
2498
- *
2499
- * Status lifecycle:
2500
- *
2501
- * idle ──mcp:connect──▶ authed
2502
- * (bootstrap succeeded with stored tokens)
2503
- *
2504
- * idle ──mcp:auth:required──▶ needs-auth
2505
- * (bootstrap saw no/invalid tokens)
2506
- *
2507
- * needs-auth ──login()──▶ authorizing ──mcp:auth:url──▶ authorizing(url)
2508
- * ──mcp:auth:success──▶ authed
2509
- * ──mcp:auth:error──▶ error
2510
- *
2511
- * any ──logout──▶ idle
2512
- *
2513
- * Multiple servers progress through the lifecycle independently; the map
2514
- * is keyed by `McpServerConfig.name`.
2515
- */
2516
- type McpAuthStatus = {
2517
- kind: 'idle';
2518
- } | {
2519
- kind: 'needs-auth';
2520
- reason: 'no-tokens' | 'auto-promoted';
2521
- } | {
2522
- kind: 'authorizing';
2523
- url?: string;
2524
- } | {
2525
- kind: 'authed';
2526
- } | {
2527
- kind: 'error';
2528
- error: string;
2529
- } | {
2530
- kind: 'bootstrap-error';
2531
- error: string;
2532
- };
2533
- /**
2534
- * Map of server name → status. `idle` is the implicit default for any
2535
- * server NOT present in the map, so the renderer treats `undefined` as
2536
- * `{ kind: 'idle' }` and the map can stay sparse.
2537
- */
2538
- type McpAuthStateMap = Readonly<Record<string, McpAuthStatus>>;
2539
- /**
2540
- * Events the reducer accepts. Names mirror the agent hooks 1:1 plus two
2541
- * UI-only verbs (`login:start` / `login:cancel`) so the renderer can
2542
- * optimistically flip a row to `authorizing` before the SDK has emitted
2543
- * `mcp:auth:url` — useful when the loopback callback is slow to bind.
2544
- */
2545
- type McpAuthEvent = {
2546
- type: 'auth-required';
2547
- name: string;
2548
- reason: 'no-tokens' | 'auto-promoted';
2549
- } | {
2550
- type: 'auth-url';
2551
- name: string;
2552
- url: string;
2553
- } | {
2554
- type: 'auth-success';
2555
- name: string;
2556
- } | {
2557
- type: 'auth-error';
2558
- name: string;
2559
- error: string;
2560
- } | {
2561
- type: 'bootstrap-error';
2562
- name: string;
2563
- error: string;
2564
- } | {
2565
- type: 'connected';
2566
- name: string;
2567
- } | {
2568
- type: 'login:start';
2569
- name: string;
2570
- } | {
2571
- type: 'login:cancel';
2572
- name: string;
2573
- } | {
2574
- type: 'logout';
2575
- name: string;
2576
- } | {
2577
- type: 'reset';
2578
- name: string;
2579
- };
2580
- /**
2581
- * Apply one event to the state map. Pure, immutable — returns a new map
2582
- * with only the affected key reassigned.
2583
- *
2584
- * State transitions are simple "last write wins"; we don't gate (e.g.) a
2585
- * `login:start` from overriding `authorizing` because the loop layer
2586
- * never doubles those up — interactive login is serialized in the TUI.
2587
- */
2588
- declare function reduceMcpAuth(state: McpAuthStateMap, event: McpAuthEvent): McpAuthStateMap;
2589
- /** Convenience getter — treats `undefined` as `idle`. */
2590
- declare function getMcpAuthStatus(state: McpAuthStateMap, name: string): McpAuthStatus;
5292
+ //#region src/chat/hosts/registry.d.ts
5293
+ declare function hostsFilePath(dataDir: string): string;
5294
+ /** Where a host's token lives in the credentials store. */
5295
+ declare function hostCredentialKey(id: string): string;
5296
+ interface HostRegistry {
5297
+ list: () => HostEntry[];
5298
+ get: (id: string) => HostEntry | undefined;
5299
+ /**
5300
+ * Verify the host (it must answer as a zidane-server with this token),
5301
+ * then save it and its token. Adding a host already saved (same server
5302
+ * id) updates its URL and token in place, keeping its name unless one is
5303
+ * given. Throws {@link HostRegistryError}.
5304
+ */
5305
+ add: (input: {
5306
+ url: string;
5307
+ token: string;
5308
+ name?: string;
5309
+ }) => Promise<HostEntry>;
5310
+ /** Forget the entry and its token; `false` when it wasn't saved. */
5311
+ remove: (id: string) => boolean;
5312
+ rename: (id: string, name: string) => HostEntry;
5313
+ /** Connect with the saved token and say who answered; refreshes `lastConnectedAt` on success. */
5314
+ test: (id: string) => Promise<HostTestResult>;
5315
+ /** The saved token, for transports. Never display it. */
5316
+ token: (id: string) => string | undefined;
5317
+ }
5318
+ declare function createHostRegistry(options: {
5319
+ dataDir: string;
5320
+ probe: HostProbe;
5321
+ now?: () => number;
5322
+ }): HostRegistry;
2591
5323
  //#endregion
2592
5324
  //#region src/chat/mcp-auth-context.d.ts
2593
5325
  /**
2594
5326
  * Mount once near the top of the React tree (above `ModalRoot`). Children
2595
5327
  * use `useMcpAuthState()` to read and `useMcpAuthDispatch()` to fire events.
2596
5328
  */
2597
- declare function McpAuthProvider({
2598
- children
2599
- }: {
5329
+ declare function McpAuthProvider({ children }: {
2600
5330
  children: ReactNode;
2601
5331
  }): import("react").JSX.Element;
2602
5332
  declare function useMcpAuthState(): McpAuthStateMap;
@@ -2911,25 +5641,6 @@ interface RemoveMcpResult {
2911
5641
  */
2912
5642
  declare function removeMcpServer(filePath: string, name: string): RemoveMcpResult;
2913
5643
  //#endregion
2914
- //#region src/chat/model-chain-metadata.d.ts
2915
- interface ModelFallbackTarget {
2916
- providerKey: ProviderKey;
2917
- modelId: string;
2918
- }
2919
- /**
2920
- * Assemble the `metadata.modelChain` value for a model pick — the full
2921
- * canonical chain when at least one valid fallback survives filtering,
2922
- * `undefined` for a single-model pick (metadata merges JSON-serialize,
2923
- * so `undefined` REMOVES a previously persisted chain).
2924
- */
2925
- declare function modelChainMetadata(providerKey: ProviderKey, modelId: string, fallbacks: readonly ModelFallbackTarget[] | undefined): ModelChainEntry[] | undefined;
2926
- /**
2927
- * Validated fallback tail for list rows/pickers — head-matched against
2928
- * the session's persisted primary, unregistered providers kept (the
2929
- * caller filters against its own registry when it has one).
2930
- */
2931
- declare function sessionModelFallbacks(metadata: Record<string, unknown> | undefined): ModelFallbackTarget[] | undefined;
2932
- //#endregion
2933
5644
  //#region src/chat/oauth.d.ts
2934
5645
  declare function supportsOAuth(descriptor: ProviderDescriptor): boolean;
2935
5646
  /** True when the provider's OAuth flow needs the user to paste a code back into the TUI (no loopback callback). */
@@ -2962,6 +5673,16 @@ interface OAuthFlowOptions {
2962
5673
  onProgress?: (message: string) => void;
2963
5674
  /** Abort the in-flight login (e.g. user pressed esc). */
2964
5675
  signal?: AbortSignal;
5676
+ /** The redirect URL (or a pasted code), racing the loopback listener (pi-ai's `onManualCodeInput`). */
5677
+ onManualCodeInput?: () => Promise<string>;
5678
+ /**
5679
+ * `false`: the browser is on another machine (a client of a remote host).
5680
+ * zidane's providers bind no loopback listener here and read the redirect
5681
+ * from {@link onManualCodeInput}.
5682
+ */
5683
+ callbackServer?: false;
5684
+ /** `false`: never launch a browser on this machine. Default true. */
5685
+ openBrowser?: boolean;
2965
5686
  }
2966
5687
  /**
2967
5688
  * Run the OAuth login flow for a provider.
@@ -3112,11 +5833,22 @@ declare function formatPathForCwd(projectRelativePath: string, projectRoot: stri
3112
5833
  //#endregion
3113
5834
  //#region src/chat/project-root.d.ts
3114
5835
  /**
3115
- * Git root detection — walks parents from `cwd` looking for a `.git`
3116
- * entry, returning the absolute path of the repo root or `null` when
3117
- * the search reaches the filesystem root.
5836
+ * Git location — two different questions with two different answers once
5837
+ * linked worktrees exist:
5838
+ *
5839
+ * - **Checkout root** ({@link findGitRoot}): the working tree `cwd` sits
5840
+ * in — where its files are. Discovery reads here (`AGENTS.md`, skills,
5841
+ * MCP configs, file completion): a worktree on another branch has its
5842
+ * own copies, and those are the ones the agent works against.
5843
+ * - **Project root** ({@link findProjectRoot} / {@link projectRootOf}):
5844
+ * which project a session BELONGS to — the main checkout, for a linked
5845
+ * worktree. Identity keys here: `SessionData.projectRoot`, session
5846
+ * lists and grouping, the safelist, artifact scope, the project DB.
5847
+ * Every worktree of a repo is one project.
3118
5848
  *
3119
- * Why parent-walk over `git rev-parse --show-toplevel`:
5849
+ * For a plain checkout the two are the same directory.
5850
+ *
5851
+ * Why parent-walk + file reads over `git rev-parse`:
3120
5852
  *
3121
5853
  * - Zero dependencies (no shell-out, no git binary requirement,
3122
5854
  * works even when `git` is missing from `PATH`).
@@ -3134,13 +5866,58 @@ declare function formatPathForCwd(projectRelativePath: string, projectRoot: stri
3134
5866
  */
3135
5867
  /**
3136
5868
  * Walk parents of `cwd` looking for a `.git` entry. Returns the
3137
- * absolute path of the directory containing `.git`, or `null` when no
3138
- * git repo is found above `cwd`.
5869
+ * absolute path of the directory containing `.git` — the CHECKOUT root
5870
+ * (a linked worktree's own dir, not its main checkout) — or `null` when
5871
+ * no git repo is found above `cwd`.
3139
5872
  *
3140
5873
  * Stops at the filesystem root (`/`) — no infinite loop on `dirname`'s
3141
5874
  * idempotent root case (`dirname('/')` returns `/`).
3142
5875
  */
3143
5876
  declare function findGitRoot(cwd?: string): string | null;
5877
+ /**
5878
+ * The main checkout a LINKED WORKTREE belongs to, or `null` when
5879
+ * `checkoutRoot` is not one. Does not walk: `checkoutRoot` must be a
5880
+ * checkout root (what {@link findGitRoot} returns).
5881
+ *
5882
+ * A linked worktree's `.git` is a file (`gitdir: <main>/.git/worktrees/<name>`)
5883
+ * and that gitdir holds a `commondir` file pointing back at the shared
5884
+ * `<main>/.git`. Everything else resolves to `null` and stays its own
5885
+ * project:
5886
+ *
5887
+ * - a regular checkout (`.git` is a directory);
5888
+ * - a submodule (its gitdir has no `commondir` — a submodule IS a
5889
+ * separate repo);
5890
+ * - a worktree of a bare repo or of a `--separate-git-dir` repo (the
5891
+ * common dir isn't a `.git` inside a working tree, so there is no main
5892
+ * checkout to belong to);
5893
+ * - anything unreadable or malformed.
5894
+ */
5895
+ declare function mainCheckoutRoot(checkoutRoot: string): string | null;
5896
+ /**
5897
+ * The project `cwd` belongs to: the main checkout for a linked worktree,
5898
+ * the checkout root otherwise, `null` outside git. See the module header
5899
+ * for when to use this vs {@link findGitRoot}.
5900
+ */
5901
+ declare function findProjectRoot(cwd?: string): string | null;
5902
+ /**
5903
+ * {@link findProjectRoot} with the historical fallback: outside git, a
5904
+ * directory is its own project. The value to stamp as
5905
+ * `SessionData.projectRoot` and to key per-project state on.
5906
+ */
5907
+ declare function projectRootOf(cwd: string): string;
5908
+ /**
5909
+ * For persisted project roots that predate worktree-aware identity: the
5910
+ * `{ from, to }` pairs where a stored root is a linked worktree's checkout
5911
+ * root and should be its main checkout's. Only exact linked-worktree roots
5912
+ * remap — a plain directory that later ended up inside some repo keeps its
5913
+ * own identity (no parent walk here). Stores apply the pairs as a bulk
5914
+ * column update at open; idempotent, so a row reverted by a stale
5915
+ * full-document save converges on the next open.
5916
+ */
5917
+ declare function planWorktreeProjectRootRemaps(roots: Iterable<string>): Array<{
5918
+ from: string;
5919
+ to: string;
5920
+ }>;
3144
5921
  //#endregion
3145
5922
  //#region src/chat/provider-usage.d.ts
3146
5923
  /** How long a snapshot stays fresh. Usage windows move in minutes, not seconds. */
@@ -3229,6 +6006,13 @@ declare function runEndNotice(reason: TurnFinishReason | undefined): RunEndNotic
3229
6006
  * A short list of read-only tools is **implicitly safe** without being
3230
6007
  * persisted — see {@link IMPLICITLY_SAFE_TOOLS}.
3231
6008
  */
6009
+ /**
6010
+ * The `projects.json` key for a working directory: its project root (the
6011
+ * main checkout for a linked worktree; the dir itself outside git) — the
6012
+ * same "project" the TUI scopes its safelist to, so a subdirectory session,
6013
+ * every worktree of the repo, and every host share one safelist.
6014
+ */
6015
+ declare function safelistProjectKey(cwd: string): string;
3232
6016
  interface ProjectEntry {
3233
6017
  safelist?: string[];
3234
6018
  }
@@ -3247,6 +6031,20 @@ declare function writeProjects(dataDir: string, file: ProjectsFile): void;
3247
6031
  declare function addToSafelist(dataDir: string, projectDir: string, entries: string | readonly string[]): readonly string[];
3248
6032
  /** Read the safelist for one project. Returns `[]` for unknown projects. */
3249
6033
  declare function getSafelist(dataDir: string, projectDir: string): readonly string[];
6034
+ /**
6035
+ * The safelist in effect for a working directory — what every host's gate
6036
+ * must read. The project's entries ({@link safelistProjectKey}), plus
6037
+ * entries saved under older keys for the same place, still honored but never
6038
+ * written again:
6039
+ *
6040
+ * - the CHECKOUT root — the TUI keyed a linked worktree by its own dir
6041
+ * before worktrees resolved to their main checkout;
6042
+ * - the exact `cwd` — the daemon/GUI keyed by raw cwd before keys were
6043
+ * normalized to the project.
6044
+ */
6045
+ declare function getSafelistForCwd(dataDir: string, cwd: string): readonly string[];
6046
+ /** {@link addToSafelist} under the project key for `cwd` — the write every host's gate uses. */
6047
+ declare function addToSafelistForCwd(dataDir: string, cwd: string, entries: string | readonly string[]): readonly string[];
3250
6048
  /**
3251
6049
  * Tools that always pass without prompting. Four categories:
3252
6050
  *
@@ -3286,6 +6084,8 @@ declare const IMPLICITLY_SAFE_TOOLS: readonly string[];
3286
6084
  * - `"<tool>"` — broad match on tool name.
3287
6085
  * - `"<tool>:<token>:*"` — match when the primary arg's first token
3288
6086
  * equals `<token>`.
6087
+ * - `"browser_open:<host>"` / `"browser_navigate:<host>"` — match a
6088
+ * load of that host (port included).
3289
6089
  *
3290
6090
  * This function matches a **single command** against a **single entry**.
3291
6091
  * For shell commands that chain multiple programs (`&&`, `||`, `;`,
@@ -3320,6 +6120,8 @@ declare function isOnSafelist(entries: readonly string[], tool: string, input: R
3320
6120
  * would fail on `cat` and re-prompt forever.
3321
6121
  * - `shell` with an unparseable / empty command → first-token heuristic,
3322
6122
  * else the bare tool name.
6123
+ * - `browser_open` / `browser_navigate` to a non-loopback address →
6124
+ * `<tool>:<host>`: approving one site does not approve the web.
3323
6125
  * - anything else → bare tool name (broad).
3324
6126
  *
3325
6127
  * Always returns a non-empty list so the UI has a concrete set to render
@@ -3350,20 +6152,115 @@ declare function resolveSessionCwd(row: {
3350
6152
  */
3351
6153
  declare function scopeCwd(cwd: string): string;
3352
6154
  //#endregion
6155
+ //#region src/chat/session-export.d.ts
6156
+ /** File format supported by the exporter. */
6157
+ type SessionExportFormat = 'markdown' | 'json';
6158
+ /**
6159
+ * Anchor for the resolved destination directory:
6160
+ * - `project` — the nearest enclosing git repository (`<repoRoot>/.{prefix}/sessions`)
6161
+ * - `home` — fallback when no repo was found (`<dataDir>/sessions`; `~/.{prefix}/sessions`
6162
+ * outside an XDG layout)
6163
+ */
6164
+ type SessionExportAnchor = 'project' | 'home';
6165
+ /** Resolved file destination for a session export. */
6166
+ interface SessionExportTarget {
6167
+ /** Absolute directory holding the file. May not exist yet — `writeSessionExport` creates it on demand. */
6168
+ dir: string;
6169
+ /** Absolute path of the file (`dir/{id}.{ext}`). */
6170
+ filepath: string;
6171
+ /** Where the destination was anchored. */
6172
+ anchor: SessionExportAnchor;
6173
+ }
6174
+ /** Options shared by every resolve / write entry-point. */
6175
+ interface ResolveOptions {
6176
+ /** Discovery cwd. Defaults to `process.cwd()`. */
6177
+ cwd?: string;
6178
+ /** User home directory. Defaults to `os.homedir()`. */
6179
+ home?: string;
6180
+ /**
6181
+ * Storage prefix. Leading dot tolerated (`zidane` and `.zidane` are
6182
+ * equivalent). Defaults to `'.zidane'`.
6183
+ */
6184
+ prefix?: string;
6185
+ /**
6186
+ * User storage data dir; home-anchored exports land in `<dataDir>/sessions`.
6187
+ * Defaults to `resolveStorageDirs({ prefix, home }).dataDir`. Never a literal
6188
+ * `~/.{prefix}`: writing there would flip an XDG install to legacy storage.
6189
+ */
6190
+ dataDir?: string;
6191
+ }
6192
+ /**
6193
+ * Resolve the export target for a session id + format. Pure: no fs
6194
+ * write happens here, the caller decides whether to `existsSync` the
6195
+ * directory or hand the path to `writeSessionExport`.
6196
+ *
6197
+ * Anchor selection walks upward from `cwd` looking for a `.git` entry;
6198
+ * the first hit anchors to that repo's root (`project`). When the walk
6199
+ * exits without finding `.git`, the destination anchors to `home`: the
6200
+ * user's storage data dir.
6201
+ *
6202
+ * @throws RangeError when `sessionId` would resolve to an empty / `..`
6203
+ * filename — defensive guard against malicious or buggy callers
6204
+ * crossing into a sibling directory via the id.
6205
+ */
6206
+ declare function resolveSessionExportTarget(opts: {
6207
+ sessionId: string;
6208
+ format: SessionExportFormat;
6209
+ } & ResolveOptions): SessionExportTarget;
6210
+ /**
6211
+ * Render a session into a string in the requested format.
6212
+ *
6213
+ * `markdown` produces a human-readable transcript: a YAML-free header
6214
+ * block with the title and a one-line `id · created · turns` summary,
6215
+ * a stats section (turns / runs / tokens / cost / status), then a
6216
+ * `## Conversation` block where each turn renders as `### N. role ·
6217
+ * run_X · ISO-date` with text, thinking, tool calls, and tool results
6218
+ * formatted as appropriate fenced blocks. Useful for sharing a session
6219
+ * with a teammate or pasting into an issue tracker.
6220
+ *
6221
+ * `json` returns a 2-space-indented, deterministic dump of the full
6222
+ * `SessionData` blob. Useful for re-importing into the store or for
6223
+ * post-hoc analysis tooling.
6224
+ */
6225
+ declare function renderSession(session: SessionData, format: SessionExportFormat): string;
6226
+ /**
6227
+ * Render `session` and write the resulting bytes to disk. Returns the
6228
+ * resolved target so the caller can show the user where the file
6229
+ * landed. The parent directory is created on demand (`recursive: true`)
6230
+ * — first-time exports don't need any pre-flight setup.
6231
+ */
6232
+ declare function writeSessionExport(opts: {
6233
+ session: SessionData;
6234
+ format: SessionExportFormat;
6235
+ } & ResolveOptions): Promise<SessionExportTarget>;
6236
+ //#endregion
3353
6237
  //#region src/chat/settings-context.d.ts
3354
- declare const DEFAULT_SETTINGS: Settings;
3355
6238
  interface SettingsContextValue {
3356
6239
  settings: Settings;
3357
6240
  /** Flip a boolean setting in place. Restricted at the type level to boolean-valued keys. */
3358
6241
  toggle: (key: BooleanSettingKey) => void;
3359
6242
  /** Write any setting key to a new value. Used by string-valued choice rows. */
3360
6243
  setSetting: <K extends keyof Settings>(key: K, value: Settings[K]) => void;
6244
+ /** One line the Settings modal shows (a server refused the last write); cleared by the next change. */
6245
+ status?: string | null;
6246
+ /**
6247
+ * Settings sync markers, while the modal edits a server another computer
6248
+ * syncs to (`docs/SERVER.md` § Settings sync): which setting came from
6249
+ * which device, which was edited on the server since (an override), and
6250
+ * dropping an override. Absent elsewhere.
6251
+ */
6252
+ sync?: SettingsSyncMarks;
6253
+ }
6254
+ /** A setting's sync state on the server the modal edits. */
6255
+ interface SettingsSyncMarks {
6256
+ marker: (key: string) => {
6257
+ kind: 'synced' | 'override';
6258
+ device: string;
6259
+ } | null;
6260
+ /** Drop the override on `key`; false when it has none. */
6261
+ reset: (key: string) => boolean;
3361
6262
  }
3362
- declare function SettingsProvider({
3363
- initial,
3364
- onChange,
3365
- children
3366
- }: {
6263
+ declare function SettingsProvider({ initial, onChange, children }: {
3367
6264
  initial: Settings;
3368
6265
  onChange?: (settings: Settings) => void;
3369
6266
  children: ReactNode;
@@ -3379,7 +6276,7 @@ declare function useSettings(): SettingsContextValue;
3379
6276
  * silently broaden into `T | undefined` and short-circuit the `extends
3380
6277
  * boolean` check via `undefined`.
3381
6278
  */
3382
- type BooleanSettingKey = { [K in keyof Settings]-?: Settings[K] extends boolean ? K : never }[keyof Settings];
6279
+ type BooleanSettingKey = { [K in keyof Settings]-?: Settings[K] extends boolean ? K : never; }[keyof Settings];
3383
6280
  /**
3384
6281
  * Setting categories — drives the tab grouping in the TUI's
3385
6282
  * `SettingsModal` and, more importantly, gives any embedder (GUI,
@@ -3426,6 +6323,17 @@ interface SettingsToggle {
3426
6323
  * while the requirement is `false`, whichever client or file wrote it.
3427
6324
  */
3428
6325
  requires?: BooleanSettingKey;
6326
+ /**
6327
+ * The setting drives something only the desktop app shows (its browser,
6328
+ * its terminals). The TUI still lets you change it, and says so.
6329
+ */
6330
+ desktopOnly?: true;
6331
+ /**
6332
+ * Env var of the feature gate's kill switch (`FEATURE_GATES`). While it is
6333
+ * `'1'` the feature is off whatever the setting says, so renderers grey the
6334
+ * row out and name the variable.
6335
+ */
6336
+ killSwitchEnv?: string;
3429
6337
  }
3430
6338
  declare const SETTINGS_TOGGLES: readonly SettingsToggle[];
3431
6339
  /**
@@ -3448,7 +6356,7 @@ declare const SETTINGS_TOGGLES: readonly SettingsToggle[];
3448
6356
  * the result type — it would let `expect(next.x).toBe(false)` fail to compile
3449
6357
  * against a value that is, in fact, `false`.
3450
6358
  */
3451
- type WithInvariants<T> = { [K in keyof T]: K extends BooleanSettingKey ? boolean : T[K] };
6359
+ type WithInvariants<T> = { [K in keyof T]: K extends BooleanSettingKey ? boolean : T[K]; };
3452
6360
  declare function enforceSettingsInvariants<T extends Partial<Settings>>(settings: T): WithInvariants<T>;
3453
6361
  /**
3454
6362
  * String-valued settings that pick between a known set of options. The
@@ -3476,7 +6384,7 @@ declare const SETTINGS_CHOICES: readonly SettingsChoice[];
3476
6384
  * toggle table — a free-text row can only point at a string-valued
3477
6385
  * setting.
3478
6386
  */
3479
- type StringSettingKey = { [K in keyof Settings]-?: Settings[K] extends string ? K : never }[keyof Settings];
6387
+ type StringSettingKey = { [K in keyof Settings]-?: Settings[K] extends string ? K : never; }[keyof Settings];
3480
6388
  /**
3481
6389
  * Free-text settings edited via a single-line input. The `SettingsModal`
3482
6390
  * renders these as a row showing the current value (or `placeholder` when
@@ -3537,8 +6445,10 @@ declare function defaultSkillScanPaths(opts?: {
3537
6445
  declare function discoverProjectSkills(opts?: {
3538
6446
  cwd?: string;
3539
6447
  home?: string;
3540
- prefix?: string; /** See {@link defaultSkillScanPaths}. */
3541
- foreignAgents?: boolean; /** See {@link defaultSkillScanPaths}. */
6448
+ prefix?: string;
6449
+ /** See {@link defaultSkillScanPaths}. */
6450
+ foreignAgents?: boolean;
6451
+ /** See {@link defaultSkillScanPaths}. */
3542
6452
  ruler?: boolean;
3543
6453
  signal?: AbortSignal;
3544
6454
  }): Promise<SkillConfig[]>;
@@ -3583,9 +6493,11 @@ interface SpawnStartRecord {
3583
6493
  declare function buildSpawnStartEvent(ctx: {
3584
6494
  id: string;
3585
6495
  task: string;
6496
+ callId?: string;
3586
6497
  depth?: number;
3587
6498
  model?: string;
3588
6499
  resumesChildId?: string;
6500
+ parentChildId?: string;
3589
6501
  }, startedAt: number): StreamEvent;
3590
6502
  /**
3591
6503
  * `spawn-end` marker: ` · `-joined optional segments — elapsed wall-clock
@@ -3599,6 +6511,7 @@ declare function buildSpawnEndEvent(ctx: {
3599
6511
  depth?: number;
3600
6512
  status?: 'completed' | 'aborted' | 'timeout' | 'error';
3601
6513
  stats: AgentStats;
6514
+ parentChildId?: string;
3602
6515
  }, started: SpawnStartRecord | undefined, now?: number): StreamEvent;
3603
6516
  //#endregion
3604
6517
  //#region src/chat/streaming.d.ts
@@ -3661,123 +6574,26 @@ interface StreamBufferOptions {
3661
6574
  * a host that renders a virtualized list or exports events verbatim.
3662
6575
  */
3663
6576
  maxTranscriptEvents?: number | null;
3664
- }
3665
- declare function useStreamBuffer(setEvents: Dispatch<SetStateAction<StreamEvent[]>>, options?: StreamBufferOptions): StreamBuffer;
3666
- //#endregion
3667
- //#region src/chat/system-md.d.ts
3668
- /** A discovered `SYSTEM.md`. */
3669
- interface SystemMdFile {
3670
- path: string;
3671
- source: 'project' | 'user';
3672
- contents: string;
3673
- }
3674
- /** Result of {@link discoverSystemMd}. */
3675
- interface SystemMdResult {
3676
- /**
3677
- * The winning override, or `null` when no `SYSTEM.md` exists (the built-in
3678
- * doctrine then applies unchanged).
3679
- */
3680
- file: SystemMdFile | null;
3681
- /**
3682
- * Files that exist but lost to `file` on precedence. Non-empty only when the
3683
- * user has both a project and a user `SYSTEM.md`. Surfaced so the UI can mark
3684
- * them as inactive instead of leaving the user wondering why edits to one of
3685
- * them changed nothing.
3686
- */
3687
- shadowed: readonly SystemMdFile[];
3688
- }
3689
- interface DiscoverSystemMdOptions {
3690
- /** Discovery cwd. Default: `process.cwd()`. */
3691
- cwd?: string;
3692
- /** User home directory. Default: `os.homedir()`. */
3693
- home?: string;
3694
- /** Storage prefix. Leading dot tolerated. Default: `'.zidane'`. */
3695
- prefix?: string;
3696
6577
  /**
3697
- * Which scopes may contribute an override — the same
3698
- * {@link AgentsMdScope} the host applies to `AGENTS.md`, so one setting
3699
- * governs both surfaces:
3700
- * - `'none'` — no override, ever. Short-circuits before any FS probe.
3701
- * - `'user'` — only `~/.{prefix}/SYSTEM.md`. The repo's file is IGNORED,
3702
- * which is the whole point of the setting: it is how a user
3703
- * says "do not trust this checkout".
3704
- * - `'project'` — only `<root>/SYSTEM.md`.
3705
- * - `'both'` — union, project winning (default).
3706
- *
3707
- * Previously a boolean that only implemented `'none'`, so `'user'` still let a
3708
- * repo-root `SYSTEM.md` replace the entire doctrine — exactly the case the
3709
- * setting exists to prevent.
6578
+ * Live override of {@link maxTranscriptEvents}, read inside each drain
6579
+ * commit. Use it when the cap changes between renders: a pending drain
6580
+ * updater runs during the host's next `useState` evaluation, BEFORE this
6581
+ * hook re-mirrors the option, so a render-mirrored value is one commit
6582
+ * stale (a history reveal would be re-trimmed by that commit).
3710
6583
  */
3711
- scope?: AgentsMdScope;
6584
+ getMaxTranscriptEvents?: () => number | null | undefined;
3712
6585
  }
3713
- /**
3714
- * Absolute path of the USER-scope `SYSTEM.md` — the file the TUI creates when
3715
- * the user asks for an override and none exists yet.
3716
- *
3717
- * Exported so the host doesn't hardcode the layout and can't drift from what
3718
- * discovery actually reads.
3719
- */
3720
- declare function userSystemMdPath(opts?: {
3721
- home?: string;
3722
- prefix?: string;
3723
- }): string;
3724
- /**
3725
- * Whether a USER-scope `SYSTEM.md` would be loaded under `scope`.
3726
- *
3727
- * Mirrors the candidate filter in {@link discoverSystemMd} so a host can ask
3728
- * "would writing `~/.{prefix}/SYSTEM.md` accomplish anything?" without
3729
- * re-deriving the rule and drifting from it. `'project'` excludes the user
3730
- * candidate and `'none'` skips discovery altogether, so under either setting
3731
- * creating that file is a silent no-op.
3732
- */
3733
- declare function userScopeOverridesLoad(scope: AgentsMdScope | undefined): boolean;
3734
- /**
3735
- * Find the active `SYSTEM.md` override, if any.
3736
- *
3737
- * Project paths anchor at the git root when one exists above `cwd`, else at
3738
- * `cwd` — the same anchor as skills / mcps / AGENTS.md discovery, so all four
3739
- * surfaces agree on what "this project" means.
3740
- *
3741
- * Tolerant: an unreadable or empty file is treated as absent (logged under
3742
- * `ZIDANE_DEBUG`) and the next candidate gets its turn. Never throws — this
3743
- * runs on the session-activation path.
3744
- *
3745
- * Candidates are filtered by {@link DiscoverSystemMdOptions.scope}, mirroring
3746
- * how `discoverAgentsMd` builds its own list.
3747
- */
3748
- declare function discoverSystemMd(opts?: DiscoverSystemMdOptions): SystemMdResult;
3749
- /**
3750
- * Seed contents for a freshly created `SYSTEM.md` override, built from the
3751
- * built-in doctrine.
3752
- *
3753
- * Seeding with the real doctrine (rather than an empty file) means creating the
3754
- * override is a behavioral no-op until the user edits it. An empty seed would
3755
- * silently delete every line of doctrine the moment the file appeared, making
3756
- * the agent measurably worse with no visible cause.
3757
- *
3758
- * `currentSystem` should be DOCTRINE ONLY — callers pass
3759
- * `splitSystemPrompt(doctrineOnlySystem()).static`, which excludes both the
3760
- * generated `<env>` block (rebuilt per turn) and the `AGENTS.md` block. Baking
3761
- * the latter in would duplicate it, since it is re-appended below the override
3762
- * on every compose.
3763
- *
3764
- * `scope` is the active `userInstructionsScope`; it decides whether the seed's
3765
- * precedence note is true — see {@link projectScopeOverridesLoad}.
3766
- */
3767
- declare function renderSystemMdSeed(currentSystem: string, scope?: AgentsMdScope): string;
6586
+ declare function useStreamBuffer(setEvents: Dispatch<SetStateAction<StreamEvent[]>>, options?: StreamBufferOptions): StreamBuffer;
3768
6587
  //#endregion
3769
6588
  //#region src/chat/theme-context.d.ts
3770
- declare function ThemeProvider({
3771
- theme,
3772
- children
3773
- }: {
6589
+ declare function ThemeProvider({ theme, children }: {
3774
6590
  theme: Theme;
3775
6591
  children: ReactNode;
3776
6592
  }): import("react").JSX.Element;
3777
6593
  declare function useTheme(): Theme;
3778
6594
  /** Color palette only — equivalent to `useTheme().colors`. */
3779
6595
  declare function useColors(): ThemeColors;
3780
- /** Select-row styling — equivalent to `useTheme().select`. */
6596
+ /** Select-row styling — equivalent to `useTheme().select`: the `▶` marker and accent text carry the cursor. */
3781
6597
  declare function useSelectStyle(): ThemeSelect;
3782
6598
  /** Panel / surface backgrounds — equivalent to `useTheme().surfaces`. */
3783
6599
  declare function useSurfaces(): ThemeSurfaces;
@@ -3839,5 +6655,73 @@ declare function renderActiveTodoAnchor(todos: readonly TodoItem[], options?: Ac
3839
6655
  */
3840
6656
  declare function createActiveTodoAnchorHooks(options?: ActiveTodoAnchorOptions): AgentHookMap;
3841
6657
  //#endregion
3842
- export { ProjectsFile as $, ChatTurnsReadToolOptions as $i, shouldAutoGenerateTitle as $n, updateAppliedMarkerPath as $r, parentServerName as $t, buildSkillsConfig as A, ArtifactViewRow as Ai, createMemoryApprovalsStore as An, bootTick as Ar, modelChainMetadata as At, SettingsCategoryDescriptor as B, COMMUNICATION_DOCTRINE as Bi, QueuedAttachment as Bn, useUpdateCheck as Br, clearMcpToolsCache as Bt, userSystemMdPath as C, performInPlaceSelfUpdate as Ci, EditOutcomeLedger as Cn, ClaudeImportSummary as Cr, redirectUriFromAuthUrl as Ct, SpawnStartRecord as D, chatWindowBehavior as Di, ApprovalHub as Dn, importFromClaudeCode as Dr, runOAuthLogin as Dt, useStreamBuffer as E, chatAutoCompactBehavior as Ei, runCompaction as En, hasClaudeCodeSetup as Er, oauthUsesManualCodePaste as Et, SETTINGS_CATEGORIES as F, DiscoverAgentsMdOptions as Fi, CommandSummary as Fn, upgradeInstalledPackages as Fr, mcpsWritePath as Ft, StringSettingKey as G, INTERACTION_GUIDANCE_NO_PROMPTS as Gi, SessionPresence as Gn, InstalledVersionWatcher as Gr, subscribeMcpToolsCache as Gt, SettingsInput as H, EnvSectionOptions as Hi, RunningWorkItem as Hn, RunUpdateCommandResult as Hr, mcpToolsCachePath as Ht, SETTINGS_CHOICES as I, discoverAgentsMd as Ii, HostSettings as In, BuildUpdateHintOptions as Ir, removeMcpServer as It, folderlessCwd as J, SUBAGENT_GUIDANCE as Ji, WireApprovalRequest as Jn, UseRestartRequiredOptions as Jr, useMcpToolToggleMap as Jt, enforceSettingsInvariants as K, PLAN_MODE_DOCTRINE as Ki, StreamProgressInfo as Kn, UPDATE_APPLIED_MARKER as Kr, McpToolToggleSet as Kt, SETTINGS_INPUTS as L, renderAgentsMdBlock as Li, HostSignal as Ln, UseUpdateCheckOptions as Lr, upsertMcpServer as Lt, discoverProjectSkills as M, summarizeArtifactCounts as Mi, PendingApprovalsStore as Mn, UpgradePackageResult as Mr, McpConfigScope as Mt, BooleanSettingKey as N, AgentsMdFile as Ni, ApprovalOriginator as Nn, UpgradeProgressEvent as Nr, RemoveMcpResult as Nt, buildSpawnEndEvent as O, ArtifactScope as Oi, ApprovalRequestInput as On, tryOpenBrowser as Or, supportsOAuth as Ot, DEFAULT_SETTINGS as P, AgentsMdResult as Pi, CommandArgSuggestionSummary as Pn, UpgradeReport as Pr, UpsertMcpResult as Pt, ProjectEntry as Q, envSection as Qi, generateSessionTitle as Qn, readUpdateAppliedMarker as Qr, indexOfServerRow as Qt, SETTINGS_TOGGLES as R, ACTIONS_WITH_CARE_DOCTRINE as Ri, PendingInteraction as Rn, buildRestartHint as Rr, CachedMcpToolList as Rt, userScopeOverridesLoad as S, parseSemver as Si, createInteractionHub as Sn, ClaudeImportOptions as Sr, normalizeRedirectPaste as St, StreamSource as T, resolvePlatformPackage as Ti, CompactionEntry as Tn, countClaudeSessionFiles as Tr, OAuthPrompt$1 as Tt, SettingsProvider as U, IDENTITY_PREFIX as Ui, SendOptions as Un, runUpdateCommand as Ur, refreshMcpToolsCatalog as Ut, SettingsChoice as V, DOING_TASKS_DOCTRINE as Vi, QueuedItem as Vn, RunUpdateCommandOptions as Vr, loadMcpToolsCache as Vt, SettingsToggle as W, INTERACTION_GUIDANCE as Wi, SerializedPreview as Wn, InstalledVersionState as Wr, saveMcpToolsCache as Wt, scopeCwd as X, buildBuildSystem as Xi, GenerateSessionTitleOptions as Xn, hasUpdateAppliedMarker as Xr, VisibleMcpRow as Xt, resolveSessionCwd as Y, TOKEN_DISCIPLINE_DOCTRINE as Yi, toTodosSnapshot as Yn, WatchInstalledVersionOptions as Yr, useMcpToolToggleSet as Yt, IMPLICITLY_SAFE_TOOLS as Z, buildPlanSystem as Zi, cleanTitle as Zn, probeInstalledVersion as Zr, buildVisibleMcpRows as Zt, DiscoverSystemMdOptions as _, checkForUpdate as _i, createChatHost as _n, ArtifactReferenceData as _r, usageSourceFor as _t, GRUVBOX_DARK as a, findInstalledCompanion as ai, useMcpAuthState as an, Commit as ar, readProjects as at, discoverSystemMd as b, detectLibc as bi, WorkerSummary as bn, scoreArtifacts as br, FetchOAuthRedirectResult as bt, CATPPUCCIN_LATTE as c, InPlaceUpdateResult as ci, McpAuthStatus as cn, Walk as cr, runEndNotice as ct, ThemeProvider as d, PerformInPlaceUpdateOptions as di, extractEditPayloadForGate as dn, DiscoveryProvider as dr, clearProviderUsageCache as dt, chatReferenceTokensForPrompt as ea, useRestartRequired as ei, createFileMcpCredentialStore as en, BuildHintsOptions as er, addToSafelist as et, useColors as f, PerformSelfUpdateOptions as fi, readFileSafe as fn, useDiscovery as fr, fetchAllProviderUsage as ft, useTheme as g, UpdateStatus as gi, ContextSegmentsReport as gn, ARTIFACTS_COMPLETION_PROVIDER_ID as gr, usageChecksOptedOut as gt, useSyntaxStyles as h, TarballAvailabilityOptions as hi, ChatHost as hn, useConfig as hr, providerUsageSupported as ht, VAPORWAVE_THEME as i, unwrapCallTool as ia, InstalledCompanion as ii, useMcpAuthDispatch as in, quoteForShell as ir, projectsFilePath as it, defaultSkillScanPaths as j, groupArtifacts as ji, ChatHostEnvironment as jn, UpgradeInstalledPackagesOptions as jr, sessionModelFallbacks as jt, buildSpawnStartEvent as k, ArtifactViewGroup as ki, createApprovalHub as kn, bootProfileEnabled as kr, ModelFallbackTarget as kt, CATPPUCCIN_MACCHIATO as l, PackageManagerCommand as li, getMcpAuthStatus as ln, createDiscoverySlot as lr, FetchProviderUsageOptions as lt, useSurfaces as m, SelfUpdateResult as mi, serializePreview as mn, ConfigProvider as mr, fetchProviderUsageAccounts as mt, createActiveTodoAnchorHooks as n, CALL_TOOL_NAME as na, writeUpdateAppliedMarker as ni, patchMcpCredential as nn, shortChord as nr, isOnSafelist as nt, GRUVBOX_LIGHT as o, CheckForUpdateOptions as oi, McpAuthEvent as on, DiscoverySlot as or, suggestSafelistEntries as ot, useSelectStyle as p, PlatformTarballOptions as pi, resolveCwdPath as pn, useDiscoveryOptional as pr, fetchProviderUsage as pt, useSettings as q, PLAN_MODE_DOCTRINE_NO_PROMPTS as qi, TodosSnapshot as qn, UpdateAppliedMarker as qr, buildToolToggle as qt, renderActiveTodoAnchor as r, UnwrappedToolCall as ra, CompanionSpec as ri, McpAuthProvider as rn, parseEditorCommand as rr, matchesSafelistEntry as rt, CATPPUCCIN_FRAPPE as s, DetectPackageManagerOptions as si, McpAuthStateMap as sn, DiscoverySlotOptions as sr, writeProjects as st, ActiveTodoAnchorOptions as t, createChatTurnsReadTool as ta, watchInstalledVersion as ti, mcpCredentialsPath as tn, buildHints as tr, getSafelist as tt, CATPPUCCIN_MOCHA as u, PackageManagerId as ui, reduceMcpAuth as un, DiscoveryContextValue as ur, USAGE_CACHE_TTL_MS as ut, SystemMdFile as v, checkTarballAvailable as vi, CreateWorkerParams as vn, ArtifactReferenceEntry as vr, findGitRoot as vt, StreamBuffer as w, performSelfUpdate as wi, PendingAnnotation as wn, ClaudeSessionImportResult as wr, OAuthFlowOptions as wt, renderSystemMdSeed as x, detectPackageManager as xi, InteractionHub as xn, ClaudeCopyResult as xr, fetchOAuthRedirect as xt, SystemMdResult as y, compareSemver as yi, WorkerRow as yn, createArtifactsCompletionProvider as yr, formatPathForCwd as yt, SettingsCategory as z, BuildSystemOptions as zi, PromptAttachment as zn, buildUpdateHint as zr, McpToolsCache as zt };
3843
- //# sourceMappingURL=index-C6NEd5y_.d.ts.map
6658
+ //#region src/chat/transcript-anchors.d.ts
6659
+ /**
6660
+ * One entry in the partitioned transcript-row list. Single events
6661
+ * render as a normal row; a `child-run` groups ONE subagent's events
6662
+ * from a consecutive run of subagent output into one bordered box
6663
+ * (concurrent siblings get a `child-run` each, so their rows never
6664
+ * braid together inside a shared frame); `tool-group`s
6665
+ * collect ALL parent-owned tool/tool-result events of one exchange
6666
+ * into a single collapsible summary row (`Settings.groupToolCalls`);
6667
+ * an `exchange-fold` goes further (`Settings.transcriptDensity:
6668
+ * 'compact'`) and takes every thinking, tool and worker event of the
6669
+ * exchange, leaving only the prompt and the assistant's prose in place.
6670
+ * `key` is stable across re-partitions of the same stream (derived
6671
+ * from the first grouped event) so expand state survives appends.
6672
+ *
6673
+ * Currently produced by the TUI's `partitionTranscript`; a GUI shell
6674
+ * grouping subagent runs the same way can produce the same shape.
6675
+ */
6676
+ type TranscriptItem = {
6677
+ kind: 'event';
6678
+ event: StreamEvent;
6679
+ previous?: StreamEvent;
6680
+ } | {
6681
+ kind: 'child-run';
6682
+ events: StreamEvent[];
6683
+ previous?: StreamEvent;
6684
+ } | {
6685
+ kind: 'tool-group';
6686
+ key: string;
6687
+ events: StreamEvent[];
6688
+ previous?: StreamEvent;
6689
+ } | {
6690
+ kind: 'exchange-fold';
6691
+ key: string;
6692
+ events: StreamEvent[];
6693
+ previous?: StreamEvent;
6694
+ } |
6695
+ /** Compact density: an exchange's earlier messages, ahead of its last one. */
6696
+ {
6697
+ kind: 'message-group';
6698
+ key: string;
6699
+ events: StreamEvent[];
6700
+ previous?: StreamEvent;
6701
+ };
6702
+ /**
6703
+ * Per-item anchor ids for auto-scroll. Walks `items` in render order
6704
+ * and, for each event, returns either:
6705
+ * - `'turn-anchor-<turnId>'` — the first event of this turn (the
6706
+ * scroll target).
6707
+ * - `undefined` — later event of an already-tagged turn (or a
6708
+ * synthetic event with no `turnId`).
6709
+ *
6710
+ * `ids[i]` is a tuple per item: length 1 for plain events, length N
6711
+ * for subagent runs (one entry per inner event). `idByTurn` is the
6712
+ * inverse lookup used by the scroll effect. `lastTurnId` is the
6713
+ * most-recently-rendered turn — the scroll effect special-cases it to
6714
+ * snap to bottom rather than scroll the anchor into view (which would
6715
+ * stop short of the actual tail).
6716
+ *
6717
+ * Exported so the anchor-tagging matrix can be unit-tested without
6718
+ * rendering anything.
6719
+ */
6720
+ declare function computeTurnAnchors(items: readonly TranscriptItem[]): {
6721
+ idByTurn: ReadonlyMap<string, string>;
6722
+ ids: readonly (readonly (string | undefined)[])[];
6723
+ lastTurnId: string | undefined;
6724
+ };
6725
+ //#endregion
6726
+ export { ProjectsFile as $, ClaudeImportSummary as $a, ResolvedPaths as $i, PendingApprovalsStore as $n, checkForUpdate as $o, EnabledAllowlistKey as $r, useMcpToolToggleSet as $t, SETTINGS_CHOICES as A, KeyBindingDef as Aa, PreferredCredentialTarget as Ai, normalizeLaunchOverrides as An, UseRestartRequiredOptions as Ao, cleanTitle as Ar, SUBAGENT_GUIDANCE as As, redirectUriFromAuthUrl as At, enforceSettingsInvariants as B, normalizeBindingValue as Ba, removeProviderCredential as Bi, describeForkFailure as Bn, InstalledCompanion as Bo, createSessionExecutionCache as Br, unwrapCallTool as Bs, removeMcpServer as Bt, buildSpawnEndEvent as C, updateToolEventOutcomes as Ca, defaultMcpsConfigPaths as Ci, ContextSegmentsReport as Cn, RunUpdateCommandOptions as Co, userSystemMdPath as Cr, DOING_TASKS_DOCTRINE as Cs, mainCheckoutRoot as Ct, discoverProjectSkills as D, KEYBINDING_DEF_BY_ACTION as Da, CredentialsFile as Di, WorkerRow as Dn, InstalledVersionWatcher as Do, overlayHostSettings as Dr, INTERACTION_GUIDANCE_NO_PROMPTS as Ds, FetchOAuthRedirectResult as Dt, defaultSkillScanPaths as E, KEYBINDING_DEFS as Ea, ApiKeyCredential as Ei, CreateWorkerParams as En, InstalledVersionState as Eo, isWindowOnlySetting as Er, INTERACTION_GUIDANCE as Es, formatPathForCwd as Et, SettingsChoice as F, formatBindingForDisplay as Fa, credentialsPath as Fi, ForkSessionResult as Fn, updateAppliedMarkerPath as Fo, shortChord as Fr, ChatTurnsReadToolOptions as Fs, supportsOAuth as Ft, renderSession as G, ARTIFACTS_COMPLETION_PROVIDER_ID as Ga, storedCredentialsOf as Gi, sessionModelFallbacks as Gn, PackageManagerCommand as Go, createWorktreeEnvironmentProvider as Gr, ModelAliasContext as Gs, loadMcpToolsCache as Gt, SessionExportAnchor as H, readKeybindingsRaw as Ha, setCredentialLabel as Hi, newSession as Hn, CheckForUpdateOptions as Ho, builtinEnvironmentProviders as Hr, loadMarkdownAgents as Hs, CachedMcpToolList as Ht, SettingsInput as I, groupBindings as Ia, readCredentials as Ii, ForkSource as In, useRestartRequired as Io, CreateExtensionMcpHostOptions as Ir, chatReferenceTokensForPrompt as Is, McpConfigScope as It, folderlessCwd as J, createArtifactsCompletionProvider as Ja, useConfig as Ji, ApprovalHub as Jn, PerformSelfUpdateOptions as Jo, ProvisionSessionEnvironmentResult as Jr, useCompletion as Js, saveMcpToolsCache as Jt, resolveSessionExportTarget as K, ArtifactReferenceData as Ka, writeCredentials as Ki, EditOutcomeLedger as Kn, PackageManagerId as Ko, localEnvironmentProvider as Kr, MarkdownDefDiagnostic as Ks, mcpToolsCachePath as Kt, SettingsProvider as L, keybindingsPath as La, readProviderCredential as Li, createMainSession as Ln, watchInstalledVersion as Lo, createExtensionMcpHost as Lr, createChatTurnsReadTool as Ls, RemoveMcpResult as Lt, SETTINGS_TOGGLES as M, KeyBindings as Ma, SetProviderCredentialOptions as Mi, createInteractionHub as Mn, hasUpdateAppliedMarker as Mo, shouldAutoGenerateTitle as Mr, buildBuildSystem as Ms, OAuthPrompt$1 as Mt, SettingsCategory as N, ResolvedExtensionKeybinding as Na, StoredCredential as Ni, CreateSessionInStoreParams as Nn, probeInstalledVersion as No, BuildHintsOptions as Nr, buildPlanSystem as Ns, oauthUsesManualCodePaste as Nt, BooleanSettingKey as O, KEYBINDING_KEY_COL_WIDTH as Oa, OAuthCredential as Oi, WorkerSummary as On, UPDATE_APPLIED_MARKER as Oo, splitHostSettingsPatch as Or, PLAN_MODE_DOCTRINE as Os, fetchOAuthRedirect as Ot, SettingsCategoryDescriptor as P, ensureKeybindingsFile as Pa, applyApiKeyEnv as Pi, ForkSessionParams as Pn, readUpdateAppliedMarker as Po, buildHints as Pr, envSection as Ps, runOAuthLogin as Pt, ProjectEntry as Q, ClaudeImportOptions as Qa, ResolvedConfig as Qi, ChatHostEnvironment as Qn, UpdateStatus as Qo, environmentCredentialsPath as Qr, useMcpToolToggleMap as Qt, SettingsToggle as R, matchesBinding as Ra, readProviderCredentialPair as Ri, createSessionInStore as Rn, writeUpdateAppliedMarker as Ro, SessionExecution as Rr, CALL_TOOL_NAME as Rs, UpsertMcpResult as Rt, SpawnStartRecord as S, toolResultText as Sa, dedupMcpServersByName as Si, ChatHost as Sn, truncateTrailing as So, userScopeOverridesLoad as Sr, COMMUNICATION_DOCTRINE as Ss, findProjectRoot as St, buildSkillsConfig as T, DEFAULT_KEYBINDINGS as Ta, parseMcpsFile as Ti, WorkspaceListFilesOptions as Tn, runUpdateCommand as To, WINDOW_LOCAL_SETTINGS as Tr, IDENTITY_PREFIX as Ts, projectRootOf as Tt, SessionExportFormat as U, resolveExtensionKeybindings as Ua, setPreferredCredential as Ui, ModelFallbackTarget as Un, DetectPackageManagerOptions as Uo, WORKTREE_ENVIRONMENT_KIND as Ur, loadMarkdownCommands as Us, McpToolsCache as Ut, useSettings as V, readKeybindings as Va, removeStoredCredential as Vi, forkSession as Vn, findInstalledCompanion as Vo, BuiltinEnvironmentOptions as Vr, applyMarkdownSubagents as Vs, upsertMcpServer as Vt, SessionExportTarget as W, stripJsonComments as Wa, setProviderCredential as Wi, modelChainMetadata as Wn, InPlaceUpdateResult as Wo, WorktreeRef as Wr, BUILTIN_SUBAGENTS as Ws, clearMcpToolsCache as Wt, scopeCwd as X, ClaudeCopyResult as Xa, ChatOptions as Xi, createApprovalHub as Xn, SelfUpdateResult as Xo, createFileEnvironmentSecrets as Xr, McpToolToggleSet as Xt, resolveSessionCwd as Y, scoreArtifacts as Ya, AutoUpdateConfig as Yi, ApprovalRequestInput as Yn, PlatformTarballOptions as Yo, provisionSessionEnvironment as Yr, subscribeMcpToolsCache as Yt, IMPLICITLY_SAFE_TOOLS as Z, ClaudeImportActiveHost as Za, ProviderRegistry as Zi, createMemoryApprovalsStore as Zn, TarballAvailabilityOptions as Zo, createMemoryEnvironmentSecrets as Zr, buildToolToggle as Zt, useSyntaxStyles as _, sessionEnvironmentSummary as _a, projectUserPaths as _i, serializePreview as _n, useUpdateCheck as _o, DiscoverSystemMdOptions as _r, DiscoverAgentsMdOptions as _s, fetchProviderUsageAccounts as _t, renderActiveTodoAnchor as a, compactSummaryEvent as aa, DiscoverySlot as ai, mcpCredentialsPath as an, tryOpenBrowser as ao, PendingInteraction as ar, performInPlaceSelfUpdate as as, matchesSafelistEntry as at, StreamSource as b, titleFromTurns as ba, DiscoveryResult as bi, CompactionReport as bn, clipHintsToWidth as bo, discoverSystemMd as br, ACTIONS_WITH_CARE_DOCTRINE as bs, usageSourceFor as bt, GRUVBOX_LIGHT as c, eventsFromTurns as ca, createDiscoverySlot as ci, useMcpAuthDispatch as cn, UpgradeInstalledPackagesOptions as co, QueuedItem as cr, chatAutoCompactBehavior as cs, safelistProjectKey as ct, CATPPUCCIN_MACCHIATO as d, lastContextSizeFromTurns as da, useDiscovery as di, createHostRegistry as dn, UpgradeReport as do, SerializedPreview as dr, ArtifactViewGroup as ds, runEndNotice as dt, resolveConfig as ea, EnabledToggleSet as ei, VisibleMcpRow as en, ClaudeSessionImportResult as eo, ApprovalOriginator as er, checkTarballAvailable as es, addToSafelist as et, CATPPUCCIN_MOCHA as f, listSessionMeta as fa, useDiscoveryOptional as fi, hostCredentialKey as fn, upgradeInstalledPackages as fo, SessionPresence as fr, ArtifactViewRow as fs, FetchProviderUsageOptions as ft, useSurfaces as g, saveState as ga, ProjectUserPath as gi, resolveCwdPath as gn, buildUpdateHint as go, toTodosSnapshot as gr, AgentsMdResult as gs, fetchProviderUsage as gt, useSelectStyle as h, replayTurns as ha, discoverProjectPlugins as hi, readFileSafe as hn, buildRestartHint as ho, WireApprovalRequest as hr, AgentsMdFile as hs, fetchAllProviderUsage as ht, createActiveTodoAnchorHooks as i, TuiState as ia, Commit as ii, createFileMcpCredentialStore as in, importFromClaudeCode as io, HostSignal as ir, parseSemver as is, isOnSafelist as it, SETTINGS_INPUTS as j, KeyBindingSection as ja, ProviderCredential as ji, InteractionHub as jn, WatchInstalledVersionOptions as jo, generateSessionTitle as jr, TOKEN_DISCIPLINE_DOCTRINE as js, OAuthFlowOptions as jt, SETTINGS_CATEGORIES as k, KeyAction as ka, PreferCredentialResult as ki, SessionLaunchOverrides as kn, UpdateAppliedMarker as ko, GenerateSessionTitleOptions as kr, PLAN_MODE_DOCTRINE_NO_PROMPTS as ks, normalizeRedirectPaste as kt, CATPPUCCIN_FRAPPE as l, getSessionHeader as la, DiscoveryContextValue as li, useMcpAuthState as ln, UpgradePackageResult as lo, RunningWorkItem as lr, chatWindowBehavior as ls, suggestSafelistEntries as lt, useColors as m, marginTopFor as ma, buildPluginContributions as mi, extractEditPayloadForGate as mn, UseUpdateCheckOptions as mo, TodosSnapshot as mr, summarizeArtifactCounts as ms, clearProviderUsageCache as mt, computeTurnAnchors as n, ModelUsageSummary as na, parseEditorCommand as ni, indexOfServerRow as nn, countClaudeSessionFiles as no, CommandSummary as nr, detectLibc as ns, getSafelist as nt, VAPORWAVE_THEME as o, createStateStore as oa, DiscoverySlotOptions as oi, patchMcpCredential as on, bootProfileEnabled as oo, PromptAttachment as or, performSelfUpdate as os, projectsFilePath as ot, ThemeProvider as p, loadState as pa, DiscoveredPlugin as pi, hostsFilePath as pn, BuildUpdateHintOptions as po, StreamProgressInfo as pr, groupArtifacts as ps, USAGE_CACHE_TTL_MS as pt, writeSessionExport as q, ArtifactReferenceEntry as qa, ConfigProvider as qi, PendingAnnotation as qn, PerformInPlaceUpdateOptions as qo, ProvisionSessionEnvironmentInput as qr, CompletionState as qs, refreshMcpToolsCatalog as qt, ActiveTodoAnchorOptions as r, StateStoreApi as ra, quoteForShell as ri, parentServerName as rn, hasClaudeCodeSetup as ro, HostSettings as rr, detectPackageManager as rs, getSafelistForCwd as rt, GRUVBOX_DARK as s, deriveSessionTitle as sa, Walk as si, McpAuthProvider as sn, bootTick as so, QueuedAttachment as sr, resolvePlatformPackage as ss, readProjects as st, TranscriptItem as t, resolveStoragePaths as ta, useEnabledToggleSet as ti, buildVisibleMcpRows as tn, claudeImportHostNote as to, CommandArgSuggestionSummary as tr, compareSemver as ts, addToSafelistForCwd as tt, CATPPUCCIN_LATTE as u, headerFromData as ua, DiscoveryProvider as ui, HostRegistry as un, UpgradeProgressEvent as uo, SendOptions as ur, ArtifactScope as us, writeProjects as ut, useTheme as v, stripSpawnTokensLine as va, DiscoveredMcp as vi, createChatHost as vn, EMPTY_HINTS as vo, SystemMdFile as vr, discoverAgentsMd as vs, providerUsageSupported as vt, buildSpawnStartEvent as w, usageByModel as wa, discoverProjectMcps as wi, CreateSessionRequest as wn, RunUpdateCommandResult as wo, HOST_ENABLED_LISTS as wr, EnvSectionOptions as ws, planWorktreeProjectRootRemaps as wt, useStreamBuffer as x, toolCallPreview as xa, buildMcpServers as xi, runCompaction as xn, hintsLength as xo, renderSystemMdSeed as xr, BuildSystemOptions as xs, findGitRoot as xt, StreamBuffer as y, sumRunCosts as ya, DiscoveryError as yi, CompactionEntry as yn, Hint as yo, SystemMdResult as yr, renderAgentsMdBlock as ys, usageChecksOptedOut as yt, StringSettingKey as z, mergeKeybindings as za, readProviderCredentials as zi, deleteSessionData as zn, CompanionSpec as zo, SessionExecutionCache as zr, UnwrappedToolCall as zs, mcpsWritePath as zt };
6727
+ //# sourceMappingURL=index-CHABJd-L.d.ts.map