zidane 9.1.1 → 9.1.3

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 (279) hide show
  1. package/dist/{acp-Di9WvMDF.js → acp-CeCavNlg.js} +7 -7
  2. package/dist/{acp-Di9WvMDF.js.map → acp-CeCavNlg.js.map} +1 -1
  3. package/dist/acp-cli.js +5 -5
  4. package/dist/acp.d.ts +2 -2
  5. package/dist/acp.js +1 -1
  6. package/dist/{agent-DJhpTS-B.js → agent-Dh_XZ7fj.js} +74 -19
  7. package/dist/agent-Dh_XZ7fj.js.map +1 -0
  8. package/dist/agent.d.ts +1 -1
  9. package/dist/agent.js +1 -1
  10. package/dist/{auth-store-port-DFgf22oo.js → auth-store-port-BKpjlJ8a.js} +2 -2
  11. package/dist/{auth-store-port-DFgf22oo.js.map → auth-store-port-BKpjlJ8a.js.map} +1 -1
  12. package/dist/{auto-update-restart-BFB7Ek87.js → auto-update-restart-CQJtndMG.js} +277 -41
  13. package/dist/auto-update-restart-CQJtndMG.js.map +1 -0
  14. package/dist/{browser-BcoCCB_n.js → browser-BkdeKnxO.js} +56 -2
  15. package/dist/browser-BkdeKnxO.js.map +1 -0
  16. package/dist/{browser-entry-ypi8CyE-.d.ts → browser-entry-BCwruqNa.d.ts} +10 -10
  17. package/dist/{browser-entry-ypi8CyE-.d.ts.map → browser-entry-BCwruqNa.d.ts.map} +1 -1
  18. package/dist/channels.d.ts +5 -5
  19. package/dist/chat/browser.d.ts +4 -4
  20. package/dist/chat/browser.js +6 -6
  21. package/dist/chat/pure.d.ts +5 -5
  22. package/dist/chat/pure.js +8 -8
  23. package/dist/chat.d.ts +5 -5
  24. package/dist/chat.js +55 -55
  25. package/dist/chat.js.map +1 -1
  26. package/dist/{cli-shared-CabruH8Z.js → cli-shared-DeRHgYvl.js} +4 -4
  27. package/dist/{cli-shared-CabruH8Z.js.map → cli-shared-DeRHgYvl.js.map} +1 -1
  28. package/dist/cli-style-DBEm6CyT.js +105 -0
  29. package/dist/cli-style-DBEm6CyT.js.map +1 -0
  30. package/dist/{client-core-CIixwznM.d.ts → client-core-C_4m_oL6.d.ts} +4 -4
  31. package/dist/{client-core-CIixwznM.d.ts.map → client-core-C_4m_oL6.d.ts.map} +1 -1
  32. package/dist/{client-CCaIXWue.d.ts → client-ygTASGis.d.ts} +2 -2
  33. package/dist/{client-CCaIXWue.d.ts.map → client-ygTASGis.d.ts.map} +1 -1
  34. package/dist/codemode-worker.js +8 -5
  35. package/dist/codemode-worker.js.map +1 -1
  36. package/dist/codemode.d.ts +16 -0
  37. package/dist/codemode.d.ts.map +1 -1
  38. package/dist/codemode.js +2 -2
  39. package/dist/{connection-loop-QmWL-fJf.d.ts → connection-loop-D_pRONCr.d.ts} +4 -4
  40. package/dist/{connection-loop-QmWL-fJf.d.ts.map → connection-loop-D_pRONCr.d.ts.map} +1 -1
  41. package/dist/contexts/daytona.d.ts +3 -3
  42. package/dist/contexts/docker.d.ts +1 -1
  43. package/dist/contexts/e2b.d.ts +2 -2
  44. package/dist/contexts/sandbox.d.ts +1 -1
  45. package/dist/{contexts-C72b0mMa.js → contexts-DLZTSRHU.js} +17 -106
  46. package/dist/contexts-DLZTSRHU.js.map +1 -0
  47. package/dist/contexts.d.ts +4 -4
  48. package/dist/contexts.js +2 -2
  49. package/dist/{credentials-wwETW8rI.js → credentials-Bqg-qNBM.js} +2 -2
  50. package/dist/{credentials-wwETW8rI.js.map → credentials-Bqg-qNBM.js.map} +1 -1
  51. package/dist/{cwd-picker-CcekODCt.js → cwd-picker-BsHA6HKv.js} +4 -4
  52. package/dist/{cwd-picker-CcekODCt.js.map → cwd-picker-BsHA6HKv.js.map} +1 -1
  53. package/dist/daemon-cli.js +4 -3
  54. package/dist/daemon-cli.js.map +1 -1
  55. package/dist/{decisions-CXmK0nz9.js → decisions-hsw5XS2g.js} +3 -1
  56. package/dist/decisions-hsw5XS2g.js.map +1 -0
  57. package/dist/{discovery-DycXFxdA.js → discovery-CX7nbMCn.js} +4 -4
  58. package/dist/{discovery-DycXFxdA.js.map → discovery-CX7nbMCn.js.map} +1 -1
  59. package/dist/durable/sqlite.d.ts +2 -2
  60. package/dist/durable.d.ts +1 -1
  61. package/dist/{engine-VQ86XGdG.js → engine-CxBGgU94.js} +6 -6
  62. package/dist/{engine-VQ86XGdG.js.map → engine-CxBGgU94.js.map} +1 -1
  63. package/dist/{eval-Bod0I1W_.js → eval-CfGUDAkN.js} +3 -3
  64. package/dist/{eval-Bod0I1W_.js.map → eval-CfGUDAkN.js.map} +1 -1
  65. package/dist/eval.d.ts +1 -1
  66. package/dist/eval.js +1 -1
  67. package/dist/{executor-C7RQrkEq.js → executor-j_LFRBwS.js} +4 -4
  68. package/dist/{executor-C7RQrkEq.js.map → executor-j_LFRBwS.js.map} +1 -1
  69. package/dist/{extension-mark-BqKdEZwF.js → extension-mark-_rhkDWL5.js} +9 -6
  70. package/dist/extension-mark-_rhkDWL5.js.map +1 -0
  71. package/dist/extensions/testing.d.ts +1 -1
  72. package/dist/extensions/testing.js +2 -2
  73. package/dist/{extensions-BCx4QuGB.js → extensions-ADlhBQ_1.js} +8 -8
  74. package/dist/{extensions-BCx4QuGB.js.map → extensions-ADlhBQ_1.js.map} +1 -1
  75. package/dist/{extensions-client-D8tEAcMw.d.ts → extensions-client-D4Q2XRKV.d.ts} +31 -5
  76. package/dist/extensions-client-D4Q2XRKV.d.ts.map +1 -0
  77. package/dist/extensions.d.ts +1 -1
  78. package/dist/extensions.js +6 -6
  79. package/dist/{fold-rules-DvLK8pbf.d.ts → fold-rules-1DpDwG9h.d.ts} +2 -2
  80. package/dist/{fold-rules-DvLK8pbf.d.ts.map → fold-rules-1DpDwG9h.d.ts.map} +1 -1
  81. package/dist/{google-Crva32sk.js → google-B-Dhmzzf.js} +2 -2
  82. package/dist/{google-Crva32sk.js.map → google-B-Dhmzzf.js.map} +1 -1
  83. package/dist/{headless-CCm-jLLT.js → headless-CQm19BL0.js} +4 -4
  84. package/dist/{headless-CCm-jLLT.js.map → headless-CQm19BL0.js.map} +1 -1
  85. package/dist/headless.d.ts +1 -1
  86. package/dist/headless.js +1 -1
  87. package/dist/history-worker.js +1 -1
  88. package/dist/{host-wO_6GW0y.js → host-C61ntFBC.js} +276 -103
  89. package/dist/host-C61ntFBC.js.map +1 -0
  90. package/dist/{host-BHMs9jsN.js → host-DEoEzQal.js} +13 -4
  91. package/dist/host-DEoEzQal.js.map +1 -0
  92. package/dist/{host-scoped-settings-CrbcqOkP.js → host-scoped-settings-WBYaCork.js} +2 -2
  93. package/dist/{host-scoped-settings-CrbcqOkP.js.map → host-scoped-settings-WBYaCork.js.map} +1 -1
  94. package/dist/{host-signals-Jsjd_-4g.js → host-signals-Cb2TlGtN.js} +5 -5
  95. package/dist/{host-signals-Jsjd_-4g.js.map → host-signals-Cb2TlGtN.js.map} +1 -1
  96. package/dist/{host-version-CNAr9pov.js → host-version-BIx4CuBM.js} +2 -2
  97. package/dist/{host-version-CNAr9pov.js.map → host-version-BIx4CuBM.js.map} +1 -1
  98. package/dist/{index-C6nnKu32.d.ts → index-B0PLo9jn.d.ts} +4 -4
  99. package/dist/{index-C6nnKu32.d.ts.map → index-B0PLo9jn.d.ts.map} +1 -1
  100. package/dist/{index-Djnc8Peh.d.ts → index-De4x-s76.d.ts} +14 -14
  101. package/dist/{index-Djnc8Peh.d.ts.map → index-De4x-s76.d.ts.map} +1 -1
  102. package/dist/{index-CGbOWa7y.d.ts → index-WlE93Q6d.d.ts} +411 -3
  103. package/dist/index-WlE93Q6d.d.ts.map +1 -0
  104. package/dist/{index-CCf_WeTQ.d.ts → index-f-XWIoYo.d.ts} +2 -2
  105. package/dist/{index-CCf_WeTQ.d.ts.map → index-f-XWIoYo.d.ts.map} +1 -1
  106. package/dist/index.d.ts +5 -5
  107. package/dist/index.js +17 -17
  108. package/dist/ipc/browser-types.d.ts +304 -7
  109. package/dist/ipc/browser-types.d.ts.map +1 -1
  110. package/dist/ipc/browser.d.ts +4 -4
  111. package/dist/ipc/browser.js +2 -2
  112. package/dist/ipc/remote.d.ts +6 -6
  113. package/dist/ipc/remote.js +3 -3
  114. package/dist/{ipc-BzWhIybr.js → ipc-DiH_P-wr.js} +4 -4
  115. package/dist/{ipc-BzWhIybr.js.map → ipc-DiH_P-wr.js.map} +1 -1
  116. package/dist/ipc.d.ts +10 -10
  117. package/dist/ipc.js +8 -8
  118. package/dist/{json-Dj-YATkZ.js → json-BjClr43H.js} +2 -20
  119. package/dist/json-BjClr43H.js.map +1 -0
  120. package/dist/{links-CJG_rFvf.js → links-BbC_lOnn.js} +9 -9
  121. package/dist/{links-CJG_rFvf.js.map → links-BbC_lOnn.js.map} +1 -1
  122. package/dist/{loaded-entry-DTURJo_R.js → loaded-entry-C0vkCVNQ.js} +2 -2
  123. package/dist/{loaded-entry-DTURJo_R.js.map → loaded-entry-C0vkCVNQ.js.map} +1 -1
  124. package/dist/mcp.d.ts +1 -1
  125. package/dist/{media-type-BtA0cbfI.d.ts → media-type-ZSmAGHvg.d.ts} +188 -7
  126. package/dist/media-type-ZSmAGHvg.d.ts.map +1 -0
  127. package/dist/{memory-DuVGrzbl.js → memory-C3w8nO8f.js} +132 -31
  128. package/dist/memory-C3w8nO8f.js.map +1 -0
  129. package/dist/model-test-CAtJ76kM.js.map +1 -1
  130. package/dist/{move-session-kylExJ5H.js → move-session-D7ILECs-.js} +4 -4
  131. package/dist/{move-session-kylExJ5H.js.map → move-session-D7ILECs-.js.map} +1 -1
  132. package/dist/{nested-call-record-DlzPeTB7.js → nested-call-record-OayszZBQ.js} +4 -3
  133. package/dist/nested-call-record-OayszZBQ.js.map +1 -0
  134. package/dist/output/stream-json.d.ts +1 -1
  135. package/dist/output/terminal.d.ts +1 -1
  136. package/dist/{path-display-COVV71Yu.js → path-display-DRAEpKOU.js} +3 -3
  137. package/dist/{path-display-COVV71Yu.js.map → path-display-DRAEpKOU.js.map} +1 -1
  138. package/dist/plugins.d.ts +1 -1
  139. package/dist/{prelude-CVKoUzV8.js → prelude-BwiSR1uA.js} +16 -5
  140. package/dist/prelude-BwiSR1uA.js.map +1 -0
  141. package/dist/{presets-CBt6rFfS.js → presets-CMY43nUP.js} +3 -3
  142. package/dist/{presets-CBt6rFfS.js.map → presets-CMY43nUP.js.map} +1 -1
  143. package/dist/presets.d.ts +1 -1
  144. package/dist/presets.js +1 -1
  145. package/dist/{protocol-CEVqBO-q.d.ts → protocol-C2lqKuMd.d.ts} +50 -7
  146. package/dist/protocol-C2lqKuMd.d.ts.map +1 -0
  147. package/dist/protocol-DOGyHgLQ.js.map +1 -1
  148. package/dist/protocol-NSllElLw.js.map +1 -1
  149. package/dist/providers/anthropic.d.ts +1 -1
  150. package/dist/providers/arcee.d.ts +1 -1
  151. package/dist/providers/baseten.d.ts +1 -1
  152. package/dist/providers/cerebras.d.ts +1 -1
  153. package/dist/providers/google.d.ts +1 -1
  154. package/dist/providers/google.js +1 -1
  155. package/dist/providers/local.d.ts +1 -1
  156. package/dist/providers/neuralwatt.d.ts +1 -1
  157. package/dist/providers/openai-compat.d.ts +1 -1
  158. package/dist/providers/openai.d.ts +1 -1
  159. package/dist/providers/openrouter.d.ts +1 -1
  160. package/dist/providers/xai.d.ts +1 -1
  161. package/dist/{providers-Z0TaBp1O.js → providers-CpXV_uLE.js} +5 -5
  162. package/dist/providers-CpXV_uLE.js.map +1 -0
  163. package/dist/providers.d.ts +1 -1
  164. package/dist/providers.js +1 -1
  165. package/dist/{readonly-db-D7Z-k0pQ.js → readonly-db-DrbT8tX6.js} +2 -2
  166. package/dist/{readonly-db-D7Z-k0pQ.js.map → readonly-db-DrbT8tX6.js.map} +1 -1
  167. package/dist/{registry-BKjeBnoy.js → registry-CJHvmvaP.js} +2 -2
  168. package/dist/{registry-BKjeBnoy.js.map → registry-CJHvmvaP.js.map} +1 -1
  169. package/dist/{registry-B6Ww76iW.js → registry-CN6JlZKj.js} +2 -2
  170. package/dist/{registry-B6Ww76iW.js.map → registry-CN6JlZKj.js.map} +1 -1
  171. package/dist/{registry-B9INJwJR.js → registry-CnnKaY9s.js} +324 -39
  172. package/dist/registry-CnnKaY9s.js.map +1 -0
  173. package/dist/{remote-hosts-BMWoHCWO.js → remote-hosts-DoO9CJUQ.js} +4 -4
  174. package/dist/{remote-hosts-BMWoHCWO.js.map → remote-hosts-DoO9CJUQ.js.map} +1 -1
  175. package/dist/{repo-url-D4Dr8NfZ.js → repo-url-DLjjuVk_.js} +169 -2
  176. package/dist/repo-url-DLjjuVk_.js.map +1 -0
  177. package/dist/restate.d.ts +2 -2
  178. package/dist/{sandbox-BwiPGKF7.d.ts → sandbox-B4OW6oNx.d.ts} +2 -2
  179. package/dist/{sandbox-BwiPGKF7.d.ts.map → sandbox-B4OW6oNx.d.ts.map} +1 -1
  180. package/dist/script-progress-CrZ507Qq.js +900 -0
  181. package/dist/script-progress-CrZ507Qq.js.map +1 -0
  182. package/dist/session/sqlite.d.ts +1 -1
  183. package/dist/session.d.ts +1 -1
  184. package/dist/{setting-BQBSyUq0.js → setting-D_FpKFMw.js} +6 -6
  185. package/dist/{setting-BQBSyUq0.js.map → setting-D_FpKFMw.js.map} +1 -1
  186. package/dist/{settings-catalog-4IU2i_jG.js → settings-catalog-DEZlXP51.js} +14 -2
  187. package/dist/settings-catalog-DEZlXP51.js.map +1 -0
  188. package/dist/{settings-keys-iI_Y-7MC.js → settings-keys-CW2UgHgb.js} +3 -3
  189. package/dist/{settings-keys-iI_Y-7MC.js.map → settings-keys-CW2UgHgb.js.map} +1 -1
  190. package/dist/{skills-CFXRItJ6.js → skills-CEbosUlM.js} +2 -2
  191. package/dist/{skills-CFXRItJ6.js.map → skills-CEbosUlM.js.map} +1 -1
  192. package/dist/skills.d.ts +1 -1
  193. package/dist/skills.js +1 -1
  194. package/dist/{source-Bl1Z501r.js → source-3ZnEPOSa.js} +3 -2
  195. package/dist/source-3ZnEPOSa.js.map +1 -0
  196. package/dist/{sqlite-runtime-BZ2L4RaG.d.ts → sqlite-runtime-CS_LBaAN.d.ts} +2 -2
  197. package/dist/sqlite-runtime-CS_LBaAN.d.ts.map +1 -0
  198. package/dist/{src-BY_ND2xn.js → src-BmTXDta8.js} +2 -2
  199. package/dist/{src-BY_ND2xn.js.map → src-BmTXDta8.js.map} +1 -1
  200. package/dist/{store-C6gI7FfZ.js → store-ByCDaKgJ.js} +14 -3
  201. package/dist/store-ByCDaKgJ.js.map +1 -0
  202. package/dist/{system-freeze-m5o1sGIM.js → system-freeze-XgDPIwQS.js} +3 -3
  203. package/dist/{system-freeze-m5o1sGIM.js.map → system-freeze-XgDPIwQS.js.map} +1 -1
  204. package/dist/{todo-edits-CbukbjyV.js → todo-edits-Dyj32ds6.js} +4 -3
  205. package/dist/todo-edits-Dyj32ds6.js.map +1 -0
  206. package/dist/tools/fetch-url.d.ts +1 -1
  207. package/dist/tools/web-search.d.ts +1 -1
  208. package/dist/{tools-CVTGhggj.js → tools-BsrGQ8K4.js} +2 -2
  209. package/dist/{tools-CVTGhggj.js.map → tools-BsrGQ8K4.js.map} +1 -1
  210. package/dist/tools.d.ts +1 -1
  211. package/dist/tools.js +3 -3
  212. package/dist/{transcript-anchors-CiSi_UBn.js → transcript-anchors-DMjvyUjk.js} +3 -3
  213. package/dist/{transcript-anchors-CiSi_UBn.js.map → transcript-anchors-DMjvyUjk.js.map} +1 -1
  214. package/dist/{transcript-reconcile-jbSBjLq2.js → transcript-reconcile-DpQO85wl.js} +3 -3
  215. package/dist/{transcript-reconcile-jbSBjLq2.js.map → transcript-reconcile-DpQO85wl.js.map} +1 -1
  216. package/dist/tui.d.ts +9 -9
  217. package/dist/tui.d.ts.map +1 -1
  218. package/dist/tui.js +289 -38
  219. package/dist/tui.js.map +1 -1
  220. package/dist/{types-bDQagoiL.d.ts → types-2HrgolQz.d.ts} +20 -8
  221. package/dist/types-2HrgolQz.d.ts.map +1 -0
  222. package/dist/types-4q1bFG53.js +122 -0
  223. package/dist/types-4q1bFG53.js.map +1 -0
  224. package/dist/{types-DnRd9_cA.d.ts → types-SXqk6hLD.d.ts} +3 -3
  225. package/dist/{types-DnRd9_cA.d.ts.map → types-SXqk6hLD.d.ts.map} +1 -1
  226. package/dist/types-myxheQkw.js.map +1 -1
  227. package/dist/types.d.ts +3 -3
  228. package/dist/{usage-summary-BfcXjiSM.js → usage-summary-BM7B3JdI.js} +2 -2
  229. package/dist/{usage-summary-BfcXjiSM.js.map → usage-summary-BM7B3JdI.js.map} +1 -1
  230. package/dist/{version-DqqqvHDP.js → version-szdW1Od9.js} +2 -2
  231. package/dist/{version-DqqqvHDP.js.map → version-szdW1Od9.js.map} +1 -1
  232. package/dist/{wait-task-BX4NcOOm.js → wait-task-BiI5UaZQ.js} +13 -3
  233. package/dist/wait-task-BiI5UaZQ.js.map +1 -0
  234. package/dist/{wire-CLm2rMK0.js → wire-C3l9jl9x.js} +2 -2
  235. package/dist/{wire-CLm2rMK0.js.map → wire-C3l9jl9x.js.map} +1 -1
  236. package/dist/{write-file-0E6TUJ9A.js → write-file-Dquu7MzB.js} +5 -3
  237. package/dist/write-file-Dquu7MzB.js.map +1 -0
  238. package/dist/{ws-DfGGYQ4W.js → ws-CEHi0CiB.js} +4 -4
  239. package/dist/{ws-DfGGYQ4W.js.map → ws-CEHi0CiB.js.map} +1 -1
  240. package/dist/{ws-client-CFMbhsNq.js → ws-client-t8XNWxcq.js} +2 -2
  241. package/dist/{ws-client-CFMbhsNq.js.map → ws-client-t8XNWxcq.js.map} +1 -1
  242. package/docs/CHAT.md +55 -0
  243. package/docs/CODEMODE.md +37 -0
  244. package/docs/EXTENSIONS.md +8 -6
  245. package/docs/RELEASE-NOTES-NEXT.md +48 -1
  246. package/docs/RELEASE.md +9 -1
  247. package/package.json +2 -3
  248. package/dist/agent-DJhpTS-B.js.map +0 -1
  249. package/dist/auto-update-restart-BFB7Ek87.js.map +0 -1
  250. package/dist/browser-BcoCCB_n.js.map +0 -1
  251. package/dist/contexts-C72b0mMa.js.map +0 -1
  252. package/dist/decisions-CXmK0nz9.js.map +0 -1
  253. package/dist/extension-mark-BqKdEZwF.js.map +0 -1
  254. package/dist/extensions-client-D8tEAcMw.d.ts.map +0 -1
  255. package/dist/host-BHMs9jsN.js.map +0 -1
  256. package/dist/host-wO_6GW0y.js.map +0 -1
  257. package/dist/index-CGbOWa7y.d.ts.map +0 -1
  258. package/dist/json-Dj-YATkZ.js.map +0 -1
  259. package/dist/media-type-BtA0cbfI.d.ts.map +0 -1
  260. package/dist/memory-DuVGrzbl.js.map +0 -1
  261. package/dist/nested-call-events-BYk8XvGX.js +0 -182
  262. package/dist/nested-call-events-BYk8XvGX.js.map +0 -1
  263. package/dist/nested-call-record-DlzPeTB7.js.map +0 -1
  264. package/dist/prelude-CVKoUzV8.js.map +0 -1
  265. package/dist/protocol-CEVqBO-q.d.ts.map +0 -1
  266. package/dist/providers-Z0TaBp1O.js.map +0 -1
  267. package/dist/registry-B9INJwJR.js.map +0 -1
  268. package/dist/repo-url-D4Dr8NfZ.js.map +0 -1
  269. package/dist/settings-catalog-4IU2i_jG.js.map +0 -1
  270. package/dist/source-Bl1Z501r.js.map +0 -1
  271. package/dist/sqlite-runtime-BZ2L4RaG.d.ts.map +0 -1
  272. package/dist/store-C6gI7FfZ.js.map +0 -1
  273. package/dist/todo-edits-CbukbjyV.js.map +0 -1
  274. package/dist/types-DTMEhKJO.js +0 -14
  275. package/dist/types-DTMEhKJO.js.map +0 -1
  276. package/dist/types-bDQagoiL.d.ts.map +0 -1
  277. package/dist/wait-task-BX4NcOOm.js.map +0 -1
  278. package/dist/write-file-0E6TUJ9A.js.map +0 -1
  279. package/scripts/eval.ts +0 -179
@@ -1 +1 @@
1
- {"version":3,"file":"model-test-CAtJ76kM.js","names":[],"sources":["../src/chat/host/edit-outcome-ledger.ts","../src/chat/run-end-notice.ts","../src/chat/auto-compact.ts","../src/chat/interactions-protocol.ts","../src/chat/host/signals.ts","../src/chat/host/launch-overrides.ts","../src/chat/auto-title.ts","../src/chat/execution-cache.ts","../src/chat/model-test.ts"],"sourcesContent":["/**\n * Per-callId stash for the per-edit approval pipeline. Written by\n * `tool:gate` on a `partial` decision and consumed by `tool:transform`\n * to append the canonical `<edit-outcomes>` annotation onto the tool's\n * natural output.\n *\n * Carries the original input + payload so `tool:before` can paint the\n * `tool` stream event with ALL hunks (including denied ones) badged\n * via outcomes — `ctx.input` has been reduced to the approved subset\n * by then, so `extractEditPayload` on the live ctx would miss the\n * denied hunks.\n *\n * Lifecycle: `tool:gate` writes; `tool:transform` reads + clears;\n * `tool:error` clears (no transform on the throw path); `agent:done`\n * clears the whole map so entries whose transform never fired\n * (validation reject / gate throw / retry loops) stay bounded by run\n * lifetime rather than session lifetime.\n */\nimport type { EditOutcome, EditPayload } from '../types'\n\n/** An annotation waiting to be appended to an edit tool's output. */\nexport interface PendingAnnotation {\n /** Per-hunk decisions from the approval. */\n outcomes: readonly EditOutcome[]\n /** Tool input before it was reduced to the approved hunks. */\n originalInput: Record<string, unknown>\n /** Edit payload before it was reduced to the approved hunks. */\n originalPayload: EditPayload\n}\n\n/** Holds the per-edit approval outcomes of in-flight tool calls, keyed by call id. */\nexport class EditOutcomeLedger {\n private _entries = new Map<string, PendingAnnotation>()\n\n /**\n * Stores the annotation for a tool call, replacing an earlier one.\n *\n * @param callId - Tool call id.\n * @param payload - Annotation to hold.\n */\n stash(callId: string, payload: PendingAnnotation): void {\n this._entries.set(callId, payload)\n }\n\n /**\n * Read + remove the entry for `callId`. `null` when no stash exists.\n *\n * @param callId - Tool call id.\n */\n drain(callId: string): PendingAnnotation | null {\n const entry = this._entries.get(callId)\n if (!entry)\n return null\n this._entries.delete(callId)\n return entry\n }\n\n /**\n * Non-removing peek — used by `tool:before` to paint events without consuming the stash.\n *\n * @param callId - Tool call id.\n */\n peek(callId: string): PendingAnnotation | null {\n return this._entries.get(callId) ?? null\n }\n\n /**\n * Drop entry without reading — used by `tool:error` (no transform on throw path).\n *\n * @param callId - Tool call id.\n */\n discard(callId: string): void {\n this._entries.delete(callId)\n }\n\n /** Clear every entry — called on `agent:done` and session disposal. */\n clear(): void {\n this._entries.clear()\n }\n}\n","import type { TurnFinishReason } from '../types'\n/**\n * The run-end \"why did this stop?\" notice, in one place.\n *\n * When a run ends because the model bumped a ceiling rather than finishing,\n * the transcript otherwise shows an ordinary — often empty — assistant turn.\n * That is the classic \"the session just stalled\" report: nothing errored, so\n * nothing was said.\n *\n * One implementation serves every surface: the shared chat host (which serves\n * the GUI and daemon-attached windows) calls it from its `agent:done` hook in\n * `host/notices`, so the same run explains itself with the same text\n * wherever you read it.\n *\n * Returns a STRUCTURED notice — `{ severity, label }` plus text — rather than\n * a bare string, because the transcript renders notices with a severity glyph\n * and a label chip (`StreamEvent.notice`). Returning text alone would force\n * each surface to invent the label, which is exactly the divergence this\n * module exists to prevent. The text therefore carries no leading glyph (the\n * renderer supplies it) and reads as a full sentence, since not every surface\n * draws the label chip.\n *\n * The `length` copy names the two settings that change the outcome. Both carry\n * these exact labels on every surface (`resumeOnMaxTokens` → \"Auto-resume on\n * output limit\", `maxOutputTokens` → \"Max output tokens\"), so the instruction\n * is followable wherever the notice appears; `test/run-end-notice.test.ts`\n * pins the labels so a rename cannot turn it into a lie.\n */\nimport type { StreamEvent } from './types'\n\nexport interface RunEndNotice {\n notice: NonNullable<StreamEvent['notice']>\n text: string\n}\n\n/**\n * Structured stop notice for an abnormal `finishReason`, or `undefined` for a\n * normal end.\n *\n * Normal completions (`stop` / `tool-calls`) and user aborts return\n * `undefined` — and so does a SUCCESSFUL auto-resume, because recovery ends\n * the run on `stop`. That is what keeps the notice quiet exactly when the\n * ceiling was handled rather than hit. `error` runs already surface their own\n * error event, so a second notice would double-report.\n *\n * @param reason - Finish reason of the run's last turn.\n */\nexport function runEndNotice(reason: TurnFinishReason | undefined): RunEndNotice | undefined {\n switch (reason) {\n case 'length':\n return {\n notice: { severity: 'warn', label: 'output limit' },\n text: 'Stopped at the model output limit (max_tokens). Turn on \"Auto-resume on output limit\" or raise \"Max output tokens\" in Settings to continue past it.',\n }\n case 'pause':\n return {\n notice: { severity: 'warn', label: 'pause cap' },\n text: 'Stopped after repeated empty pause turns from the provider (maxConsecutivePauseTurns cap is armed).',\n }\n case 'content-filter':\n /*\n * Refusal stop (e.g. Anthropic `stop_reason: refusal`, often after\n * PDF-heavy tool results). The loop already retried with synthetic\n * continues before giving up, so reaching here means the refusal\n * persisted — say so rather than letting the run end as a silent,\n * empty \"completion\".\n */\n return {\n notice: { severity: 'warn', label: 'refusal' },\n text: 'The provider refused to continue (safety filter / refusal stop) despite automatic retries. Rephrase the request, or start a fresh session if it keeps happening.',\n }\n default:\n return undefined\n }\n}\n","/**\n * Chat-layer auto-compaction helpers.\n *\n * The decision policy lives in `src/compact/policy.ts` (core, no chat\n * dependency) so the agent loop's loop-native auto-compaction shares the exact\n * same predicate as the chat hosts. This module re-exports it and adds the\n * host-side glue that maps chat settings + a model window into the SDK's\n * `behavior.autoCompact` value.\n */\n\nimport type { Provider } from '../providers'\nimport type { AutoCompact } from '../types'\n\nexport {\n AUTO_COMPACT_MIN_GROWTH_FRACTION,\n type AutoCompactDecision,\n type AutoCompactInput,\n shouldAutoCompact,\n} from '../compact/policy'\n\n/**\n * Build the per-run `behavior.autoCompact` a chat host passes to `agent.run`.\n *\n * Loop-native auto-compaction is the only mechanism; the TUI + GUI just\n * translate the user's enable/threshold settings plus the picked model's raw\n * context window (the SDK core has no model catalog) into the config the loop\n * consumes. Returns `false` when disabled. `rawContextWindow: null` (unknown\n * model) still enables it but leaves the trigger inert — the loop skips with\n * `unknown-window` rather than guessing.\n *\n * @param opts - Whether auto-compaction is on, the trigger threshold, the main model's raw context window and an optional summarizer override.\n */\nexport function chatAutoCompactBehavior(opts: {\n enabled: boolean\n threshold: number\n rawContextWindow: number | null\n /**\n * Summarizer override — run the summary call on a different\n * provider/model than the main one (the user's \"compaction model\"\n * setting). Omitted = summarize on the run's main provider/model.\n * `rawContextWindow` above stays the MAIN model's window: the trigger\n * measures the live conversation, not the summarizer.\n */\n summarizer?: { provider: Provider, model: string }\n}): AutoCompact {\n if (!opts.enabled)\n return false\n const contextWindow = typeof opts.rawContextWindow === 'number'\n && Number.isFinite(opts.rawContextWindow)\n && opts.rawContextWindow > 0\n ? opts.rawContextWindow\n : null\n return {\n threshold: opts.threshold,\n ...(contextWindow ? { contextWindow } : {}),\n ...(opts.summarizer ? { provider: opts.summarizer.provider, model: opts.summarizer.model } : {}),\n // Chat hosts are long-lived processes with a persist dir always\n // configured, so trade inline transcript fidelity of compacted-away\n // turns (recoverable via each stub's persisted path) for bounded\n // memory. Headless/SDK runs keep the lossless default.\n releaseReplacedTurns: true,\n }\n}\n\n/**\n * The per-run window knobs a chat host spreads into `agent.run`'s `behavior`:\n * the auto-compaction config from {@link chatAutoCompactBehavior} PLUS the\n * picked model's raw window as `behavior.contextWindow`, so window-relative\n * wire budgets (`compactThresholdWindowFraction`) see the window even when the\n * user has auto-compaction switched off. Both hosts go through this so they\n * cannot drift: the TUI and the GUI/daemon engine hand the loop the same\n * shape for the same settings.\n *\n * @param opts - Same options as {@link chatAutoCompactBehavior}.\n */\nexport function chatWindowBehavior(opts: Parameters<typeof chatAutoCompactBehavior>[0]): {\n autoCompact: AutoCompact\n contextWindow?: number\n} {\n const autoCompact = chatAutoCompactBehavior(opts)\n const contextWindow = typeof opts.rawContextWindow === 'number'\n && Number.isFinite(opts.rawContextWindow)\n && opts.rawContextWindow > 0\n ? opts.rawContextWindow\n : undefined\n return { autoCompact, ...(contextWindow !== undefined ? { contextWindow } : {}) }\n}\n","/**\n * Interactions — plan approvals and Q&A clarifications between the agent\n * and the user.\n *\n * Renderer-agnostic by design: this module owns the tool factories, the\n * pending-state derivation from `session.turns`, the response serializer,\n * and the queue types. The React queue context lives in `interactions.tsx`,\n * which re-exports all of this; hosts and daemons import this module directly.\n * The TUI / GUI consume the queue and provide the actual picker UI; the same\n * data plumbing works for both.\n *\n * Persistence model — the call IS the persisted state:\n *\n * - The agent emits `present_plan` / `ask_user` as a regular tool call.\n * The `tool_call` block lands in `session.turns` like any other tool.\n * When the user responds, the corresponding `tool_result` block follows\n * in the next user turn. Standard tool-call/tool-result contract — no\n * parallel store, no extra schema.\n *\n * - \"Pending\" = the trailing assistant turn has a `tool_call` to one of\n * the interaction tools with no matching `tool_result` in a later\n * user turn. Derived at load time via\n * {@link pendingInteractionsFromTurns}; no parallel bookkeeping.\n *\n * - Resumed sessions land in the same UI as live ones. The host only\n * swaps the resolver:\n * - **Live**: the tool's `execute()` is awaiting a Promise; the\n * resolver resolves that Promise. The agent loop persists the\n * tool_result naturally on its next iteration.\n * - **Resumed**: no Promise exists (the prior run was destroyed).\n * The resolver appends a synthetic user turn with the tool_result\n * block and triggers a follow-up `agent.run()` so the model\n * continues from where it left off.\n *\n * No OpenTUI imports — a GUI consumer can ship its own renderer and reuse\n * this module verbatim.\n */\nimport type { StepKey } from '../durable/keys'\nimport type { DurableAwakeable, DurableMode } from '../durable/types'\nimport type { DurableToolDef, ToolContext, ToolDef } from '../tools/types'\nimport type { SessionContentBlock, SessionTurn } from '../types'\nimport { stepKey } from '../durable/keys'\n\n// ---------------------------------------------------------------------------\n// Tool identities — canonical names persisted in `session.turns`. Stable\n// across the lifetime of the project; renaming would break resume.\n// ---------------------------------------------------------------------------\n\n/** Canonical name of the tool that presents a plan for approval. */\nexport const PRESENT_PLAN_TOOL = 'present_plan'\n/** Canonical name of the tool that asks the user one or more questions. */\nexport const ASK_USER_TOOL = 'ask_user'\n\n/**\n * True when `name` is one of the interaction tool canonical names.\n *\n * @param name - Tool name to test.\n */\nexport function isInteractionTool(name: string): boolean {\n return name === PRESENT_PLAN_TOOL || name === ASK_USER_TOOL\n}\n\n// ---------------------------------------------------------------------------\n// Payload shapes — parsed views of each tool's `input` blob.\n// ---------------------------------------------------------------------------\n\n/** One step of a {@link PlanPayload}. */\nexport interface PlanStep {\n /** Step id. */\n id: string\n /** Short step title. */\n title: string\n /** Longer step detail. */\n description?: string\n}\n\n/** Parsed input of a `present_plan` call. */\nexport interface PlanPayload {\n /** Short headline summary — used as the modal title. */\n title: string\n /** Full plan body. Markdown. */\n plan: string\n /** Optional structured step list — surfaced as a checklist preview. */\n steps?: readonly PlanStep[]\n}\n\n/** One option of a `select` question. */\nexport interface QuestionChoice {\n /** Choice id, returned as the answer when picked. */\n id: string\n /** Text shown for the choice. */\n label: string\n /** One-line detail shown under the label. */\n description?: string\n /**\n * Open a `multi` select with this choice already ticked. Ignored by\n * single selects (which position the cursor from the carried answer\n * instead) and overridden by a carried answer on re-entry, so resuming a\n * question keeps what the user had rather than snapping back to the\n * defaults.\n *\n * Exists because the alternative for an opt-out list is N keystrokes to\n * accept N items: a caller offering \"which of these 20 findings do you\n * want to send\" wants them pre-ticked and the user deselecting a few.\n */\n selected?: boolean\n}\n\n/**\n * Kind of an `ask_user` question, the discriminator of {@link Question}:\n *\n * - `text` — single-line free-text answer (rendered via `<input>`).\n * - `textarea` — multi-line free-text answer (rendered via `<textarea>`).\n * - `select` — single pick from a fixed set of `choices`.\n * - `confirm` — yes/no boolean (rendered as a 2-option select).\n *\n * The model batches related clarifications into one tool call instead of\n * pinging the user N times with N round-trips.\n */\nexport type QuestionType = 'text' | 'textarea' | 'select' | 'confirm'\n\ninterface BaseQuestion {\n /** Stable id — keys the response back to the model. */\n id: string\n /** Question text shown above the input (Markdown allowed). */\n prompt: string\n /** Optional helper text under the prompt. */\n description?: string\n /**\n * Whether the answer is required (non-empty). Default: `true` for\n * `select` / `confirm` (no sensible empty), `false` for free-text\n * (skipping = empty string). Free-text questions still accept an\n * answer; the flag only gates submission.\n */\n required?: boolean\n}\n\n/** A free-text question. */\nexport interface TextQuestion extends BaseQuestion {\n /** `text` for a single line, `textarea` for several. */\n type: 'text' | 'textarea'\n /** Placeholder shown when the input is empty. */\n placeholder?: string\n /** Pre-fill the input with this value (still editable). */\n default?: string\n}\n\n/** A question answered by picking from fixed choices. */\nexport interface SelectQuestion extends BaseQuestion {\n /** Question type discriminator. */\n type: 'select'\n /** Options to pick from. */\n choices: readonly QuestionChoice[]\n /**\n * Allow picking MORE than one choice. The answer becomes a\n * `readonly string[]` of choice ids (space toggles, ↵ commits in the\n * TUI). `required` (default `true`) demands at least one selection.\n */\n multi?: boolean\n}\n\n/** A yes/no question. */\nexport interface ConfirmQuestion extends BaseQuestion {\n /** Question type discriminator. */\n type: 'confirm'\n /** Label for the affirmative option. Default: `'yes'`. */\n affirmLabel?: string\n /** Label for the negative option. Default: `'no'`. */\n denyLabel?: string\n}\n\n/** One question in an `ask_user` form, discriminated by `type`. */\nexport type Question = TextQuestion | SelectQuestion | ConfirmQuestion\n\n/** Parsed input of an `ask_user` call. */\nexport interface QuestionPayload {\n /** Optional intro / context shown above every question (Markdown). */\n intro?: string\n /** One or more questions for the user to answer in one form. */\n questions: readonly Question[]\n}\n\n/** Per-question answer value — `string` for text/textarea/select, `boolean` for confirm, `string[]` for multi-select. */\nexport type AnswerValue = string | boolean | readonly string[]\n\n/**\n * Who raised a request. `model` is a tool call awaiting a `tool_result`;\n * `host` is a slash command or extension prompt whose consumer is local\n * code. Automatic mode may only answer on the model's behalf, so this is\n * carried as data rather than inferred from the id — a missing value is a\n * compile error at the construction site instead of a silent mis-answer.\n */\nexport type InteractionOrigin = 'model' | 'host'\n\ninterface BaseRequest {\n /** Stable id — the `tool_call.id` from the model. */\n id: string\n /** Whether the model or the host raised this request. */\n origin: InteractionOrigin\n /** Tool canonical name. */\n tool: string\n /** Run that emitted the call, when known. */\n runId?: string\n /** Turn id the call lives in. */\n turnId?: string\n /** Unix ms when the request was raised. */\n createdAt: number\n}\n\n/** A pending request to approve a plan. */\nexport interface PlanRequest extends BaseRequest {\n /** Request kind discriminator. */\n kind: 'plan'\n tool: typeof PRESENT_PLAN_TOOL\n /** The plan to approve. */\n payload: PlanPayload\n}\n\n/** A pending request to answer questions. */\nexport interface QuestionRequest extends BaseRequest {\n /** Request kind discriminator. */\n kind: 'question'\n tool: typeof ASK_USER_TOOL\n /** The questions to answer. */\n payload: QuestionPayload\n}\n\n/** A request for user input raised by the model or the host. */\nexport type InteractionRequest = PlanRequest | QuestionRequest\n\n// ---------------------------------------------------------------------------\n// Response shapes — what the user gives back. Serialized as JSON below so\n// the model parses a stable, explicit envelope.\n// ---------------------------------------------------------------------------\n\n/**\n * Who answered a prompt when it was not the person at a window: an\n * orchestrator session, an extension, or a connected client (MCP, a script).\n */\nexport interface InteractionAnswerer {\n /** What answered. */\n kind: 'session' | 'extension' | 'client'\n /** The session id, the extension name, or the client's token id. */\n id?: string\n /** How to name it to a reader: the session's title, `extension <name>`, the token's name. */\n label: string\n}\n\n/** How the user answered a plan: approve it, reject it, or ask for a revision. */\nexport type PlanDecision = 'approve' | 'reject' | 'revise'\n\n/** The user's answer to a {@link PlanRequest}. */\nexport interface PlanResponse {\n /** Response kind discriminator. */\n kind: 'plan'\n /** The user's decision. */\n decision: PlanDecision\n /** Optional free-text feedback — shown to the model verbatim. */\n comment?: string\n /**\n * Resolved by automatic mode rather than by a person. Serialized so the\n * model can tell a real decision from a default it is free to override.\n */\n automatic?: boolean\n /** Answered by an orchestrator, an extension or a client rather than at a window. */\n answeredBy?: InteractionAnswerer\n}\n\n/** The user's answers to a {@link QuestionRequest}. */\nexport interface QuestionResponse {\n /** Response kind discriminator. */\n kind: 'question'\n /**\n * Map of `Question.id → AnswerValue` for every question the user\n * answered. Missing keys = question skipped (only possible when\n * `required: false`).\n */\n answers: Readonly<Record<string, AnswerValue>>\n /**\n * Free text the user attached to the whole form — every ask_user surface\n * offers it unconditionally, so the user can always refine or override\n * the questions instead of being boxed into the model's choices. Parallel\n * to {@link PlanResponse.comment}; absent when left empty.\n */\n instructions?: string\n /** See {@link PlanResponse.automatic}. */\n automatic?: boolean\n /** See {@link PlanResponse.answeredBy}. */\n answeredBy?: InteractionAnswerer\n}\n\n/** The user's answer to an {@link InteractionRequest}. */\nexport type InteractionResponse = PlanResponse | QuestionResponse\n\n/**\n * Instruction sent to the model when automatic mode answers an interaction on the user's behalf.\n */\nexport const AUTOMATIC_INTERACTION_INSTRUCTION\n = 'Automatic mode is enabled. Make the best choice from the repository and current task, then continue without asking for this input again.'\n\n/**\n * Id prefix for interactions the HOST synthesizes — slash commands and\n * extension `ctx.interaction.*` share the prompt slot with the tools but\n * never pass through {@link createInteractionTools}. Model-originated\n * requests carry the provider's `tool_call.id` instead. Naming only:\n * origin policy reads {@link BaseRequest.origin}, never the id.\n */\nexport const HOST_INTERACTION_ID_PREFIX = 'command_'\n\n/**\n * Whether a queued request came from a model tool call. Automatic mode\n * answers on the model's behalf, so it must leave host-originated prompts\n * alone: their consumer is local code awaiting a real decision, with no\n * model on the other end to act on an instruction sentence.\n *\n * @param request - Queued request to classify.\n */\nexport function isModelInteraction(request: InteractionRequest): boolean {\n return request.origin === 'model'\n}\n\n/**\n * Stand-in for a free-text answer nobody typed. Deliberately NOT the\n * instruction sentence: {@link serializeInteractionResponse} already leads\n * the envelope with it once, so repeating it per answer only restates the\n * same disclosure — four times over on a three-question form.\n */\nconst AUTOMATIC_FREE_TEXT_ANSWER = '(no answer, automatic mode)'\n\n/**\n * Automatic answer for one question, in the shape its `type` promises:\n * `confirm` declares a boolean and `select` declares one of its own choice\n * ids, so a free-text placeholder would be out of contract there.\n *\n * These are defaults, not decisions — nobody chose them. The response\n * carries `automatic: true` so the model reads them as such and overrides\n * whatever doesn't fit; see {@link serializeInteractionResponse}.\n */\nfunction automaticAnswerFor(question: Question): AnswerValue {\n switch (question.type) {\n // Proceed, consistent with auto-approving plans.\n case 'confirm':\n return true\n // Nothing marks a choice as recommended, so the first one is an\n // arbitrary placeholder the model is expected to reconsider.\n case 'select': {\n const picked = question.choices[0]?.id ?? ''\n return question.multi ? (picked ? [picked] : []) : picked\n }\n default:\n return AUTOMATIC_FREE_TEXT_ANSWER\n }\n}\n\n/**\n * Build the deterministic response used when the host skips interaction UI.\n *\n * @param request - Request to answer. A plan is approved, a `confirm` question gets `true`, a `select` question gets its first choice, and free text gets a placeholder.\n * @returns A response marked `automatic: true`.\n */\nexport function createAutomaticInteractionResponse(request: InteractionRequest): InteractionResponse {\n if (request.kind === 'plan') {\n // No `comment`: that field is the user's own free-text feedback, shown to\n // the model verbatim. Harness text belongs in the envelope's `note`, which\n // serialization adds once for every automatic response.\n return {\n kind: 'plan',\n decision: 'approve',\n automatic: true,\n }\n }\n const answers: Record<string, AnswerValue> = {}\n for (const question of request.payload.questions)\n answers[question.id] = automaticAnswerFor(question)\n return { kind: 'question', answers, automatic: true }\n}\n\n// ---------------------------------------------------------------------------\n// Serialization — what the model sees as the tool_result `output`.\n// ---------------------------------------------------------------------------\n\n/**\n * Format an {@link InteractionResponse} as the tool_result content. JSON\n * keeps the envelope parseable and explicit. Empty fields are stripped so\n * the model doesn't waste tokens reading absent values.\n *\n * @param response - The user's or automatic mode's response.\n * @returns A JSON string for the `tool_result` content.\n */\nexport function serializeInteractionResponse(response: InteractionResponse): string {\n // Leads the envelope so the model reads \"nobody answered this\" before the\n // values themselves. Absent on user-submitted responses, which carry no\n // extra tokens.\n const automatic: Record<string, unknown> = {\n ...(response.automatic ? { automatic: true, note: AUTOMATIC_INTERACTION_INSTRUCTION } : {}),\n // Who decided, when it was not the person: the model weighs an\n // orchestrator's answer differently from its user's.\n ...(response.answeredBy ? { answeredBy: response.answeredBy.label } : {}),\n }\n if (response.kind === 'plan') {\n const body: Record<string, unknown> = { ...automatic, decision: response.decision }\n if (response.comment && response.comment.trim().length > 0)\n body.comment = response.comment.trim()\n return JSON.stringify(body)\n }\n // Normalize answer values: trim strings, drop empty / undefined entries\n // so the model sees a tight payload of only the meaningful answers.\n const answers: Record<string, AnswerValue> = {}\n for (const [id, value] of Object.entries(response.answers)) {\n if (typeof value === 'string') {\n const trimmed = value.trim()\n if (trimmed.length > 0)\n answers[id] = trimmed\n }\n else if (typeof value === 'boolean') {\n answers[id] = value\n }\n else if (Array.isArray(value)) {\n // Multi-select — keep non-empty trimmed ids, drop empty selections.\n const picked = value.map(v => String(v).trim()).filter(v => v.length > 0)\n if (picked.length > 0)\n answers[id] = picked\n }\n }\n const body: Record<string, unknown> = { ...automatic, answers }\n const instructions = response.instructions?.trim()\n if (instructions)\n body.instructions = instructions\n return JSON.stringify(body)\n}\n\n/**\n * Why a run stopped on a rejected plan: the reason its abort carries. A caller\n * the abort cuts short before it hands on the plan's result (a codemode script\n * awaiting `present_plan`) reports this instead, so the model still reads the\n * decision and the comment.\n *\n * @param response - The plan's `reject` response.\n * @returns The abort reason.\n */\nexport function planRejectionReason(response: InteractionResponse): string {\n return `The user rejected the plan and stopped the run: ${serializeInteractionResponse(response)}`\n}\n\n// ---------------------------------------------------------------------------\n// Resumed-flow turn builder — batches every pending interaction's response\n// into ONE matched user turn.\n//\n// Why a dedicated builder: the agent loop's history-validation contract\n// requires every `tool_use` block in an assistant turn to be followed by\n// EXACTLY ONE matched `tool_result` block in the very next user turn.\n// When a session was killed mid-batch — say the model emitted both\n// `present_plan` and `ask_user` in parallel and the process died before\n// either Promise resolved — naively writing one `tool_result` per UI\n// submission would persist a user turn missing the sibling's match, and\n// the next `agent.run()` would reject the history on the API side.\n//\n// The wizard UI accumulates answers across submissions; this helper\n// converts the accumulated map back into a protocol-valid user turn\n// containing one `tool_result` per pending request, in pending order.\n// ---------------------------------------------------------------------------\n\n/**\n * Build a protocol-valid user `SessionTurn` that closes a batch of\n * resumed pending interactions.\n *\n * - `requests` MUST be the same list returned by\n * {@link pendingInteractionsFromTurns} for the session, in order.\n * - `responses` MUST contain an entry for every request id; throws\n * otherwise (caller bug — flush before all answers collected).\n *\n * Pure: reads no store. Tested in isolation so the batching invariant doesn't\n * regress without the resumed-flow integration noticing.\n *\n * @param requests - Pending requests, in the order {@link pendingInteractionsFromTurns} returned them.\n * @param responses - Responses keyed by request id. Must cover every request.\n * @param options - Turn id for the new turn, plus an optional run id and creation time.\n * @returns A user turn with one `tool_result` per request.\n * @throws If `requests` is empty or a request has no response.\n */\nexport function buildResumedToolResultsTurn(\n requests: readonly InteractionRequest[],\n responses: ReadonlyMap<string, InteractionResponse>,\n options: {\n /** Turn id minted by the session store. */\n turnId: string\n /**\n * Run id that emitted the pending tool calls — when known, attaching\n * it ties the resumed turn back to the originating `SessionRun` row\n * so consumers grouping turns by run keep clean ancestry across the\n * close/reopen boundary.\n */\n runId?: string\n /** `createdAt` timestamp. Defaults to `Date.now()`. */\n createdAt?: number\n },\n): SessionTurn {\n if (requests.length === 0)\n throw new Error('buildResumedToolResultsTurn: at least one request is required')\n\n const content: SessionContentBlock[] = requests.map((req) => {\n const response = responses.get(req.id)\n if (!response) {\n throw new Error(\n `buildResumedToolResultsTurn: missing response for request \"${req.id}\". `\n + `All ${requests.length} pending interactions must be resolved before flushing.`,\n )\n }\n return {\n type: 'tool_result',\n callId: req.id,\n output: serializeInteractionResponse(response),\n }\n })\n\n return {\n id: options.turnId,\n role: 'user',\n content,\n createdAt: options.createdAt ?? Date.now(),\n ...(options.runId ? { runId: options.runId } : {}),\n }\n}\n\n// ---------------------------------------------------------------------------\n// Pending-state derivation — pure, store-agnostic.\n//\n// The single source of truth for \"what is the session waiting on?\". Used by\n// the chat layer on session activation to decide whether to render the\n// resumed-interaction UI instead of the prompt textarea.\n// ---------------------------------------------------------------------------\n\n/**\n * Scan `turns` and return every interaction tool call without a matching\n * `tool_result`, ordered by turn appearance. Works equally for live mid-run\n * inspection and reload-from-disk.\n *\n * Malformed payloads (input that doesn't match the tool's schema) are\n * skipped silently — the call still lives in the persisted history, but\n * the renderer wouldn't know how to display it. The next `agent.run()` on\n * the session will see the orphan and synthesize an error tool_result via\n * the loop's normal recovery path.\n *\n * @param turns - Session turns, oldest first.\n */\nexport function pendingInteractionsFromTurns(turns: readonly SessionTurn[]): InteractionRequest[] {\n interface CallInfo {\n callId: string\n tool: string\n input: Record<string, unknown>\n runId?: string\n turnId: string\n createdAt: number\n }\n const calls: CallInfo[] = []\n const resultIds = new Set<string>()\n\n for (const turn of turns) {\n for (const block of turn.content) {\n if (block.type === 'tool_call' && isInteractionTool(block.name)) {\n calls.push({\n callId: block.id,\n tool: block.name,\n input: block.input,\n ...(turn.runId ? { runId: turn.runId } : {}),\n turnId: turn.id,\n createdAt: turn.createdAt,\n })\n }\n else if (block.type === 'tool_result') {\n resultIds.add(block.callId)\n }\n }\n }\n\n const pending: InteractionRequest[] = []\n for (const call of calls) {\n if (resultIds.has(call.callId))\n continue\n if (call.tool === PRESENT_PLAN_TOOL) {\n const payload = parsePlanPayload(call.input)\n if (!payload)\n continue\n pending.push({\n id: call.callId,\n // Reconstructed from an unmatched `tool_use` block, so model by\n // construction — host prompts never reach the transcript.\n origin: 'model',\n kind: 'plan',\n tool: PRESENT_PLAN_TOOL,\n payload,\n ...(call.runId ? { runId: call.runId } : {}),\n turnId: call.turnId,\n createdAt: call.createdAt,\n })\n }\n else if (call.tool === ASK_USER_TOOL) {\n const payload = parseQuestionPayload(call.input)\n if (!payload)\n continue\n pending.push({\n id: call.callId,\n origin: 'model',\n kind: 'question',\n tool: ASK_USER_TOOL,\n payload,\n ...(call.runId ? { runId: call.runId } : {}),\n turnId: call.turnId,\n createdAt: call.createdAt,\n })\n }\n }\n return pending\n}\n\n/**\n * Defensive parsers — tools validate via JSON Schema before `execute`, but\n * persisted turns can carry malformed input (model regressions, schema\n * migrations). We accept what we recognize and drop what we don't.\n */\nfunction parsePlanPayload(input: Record<string, unknown>): PlanPayload | null {\n const title = typeof input.title === 'string' ? input.title.trim() : ''\n const plan = typeof input.plan === 'string' ? input.plan : ''\n if (!title || !plan)\n return null\n const stepsRaw = Array.isArray(input.steps) ? input.steps : undefined\n const steps: PlanStep[] = []\n if (stepsRaw) {\n for (const s of stepsRaw) {\n if (!s || typeof s !== 'object')\n continue\n const cast = s as { id?: unknown, title?: unknown, description?: unknown }\n if (typeof cast.id !== 'string' || typeof cast.title !== 'string')\n continue\n steps.push({\n id: cast.id,\n title: cast.title,\n ...(typeof cast.description === 'string' ? { description: cast.description } : {}),\n })\n }\n }\n return {\n title,\n plan,\n ...(steps.length > 0 ? { steps } : {}),\n }\n}\n\n/**\n * Defensive parser for an `ask_user` request. Accepts BOTH the\n * multi-question shape and the single-question shape (so sessions that\n * persisted it still surface correctly on resume).\n *\n * - Multi: `{ intro?, questions: [...] }`.\n * - Single: `{ question, choices?, allowComment? }` → wrapped into a\n * single `select` (if choices) or `textarea` (otherwise) question\n * keyed by the synthetic id `'answer'`.\n */\nfunction parseQuestionPayload(input: Record<string, unknown>): QuestionPayload | null {\n const intro = typeof input.intro === 'string' && input.intro.trim().length > 0\n ? input.intro\n : undefined\n\n // Multi-question shape: explicit `questions` array. Parse each entry; drop\n // malformed ones rather than the whole payload so a single bad\n // question doesn't make the entire batch invisible on reload.\n //\n // Dedup by id (first wins). React would warn on the duplicate `key`\n // anyway, but more importantly the response `answers` map is keyed\n // by id — duplicates would silently collapse to a single value the\n // model would receive for every question that shared the id. First-\n // occurrence-wins keeps the form predictable; the model's own\n // schema validation should have caught it earlier.\n if (Array.isArray(input.questions)) {\n const questions: Question[] = []\n const seen = new Set<string>()\n for (const raw of input.questions) {\n const q = parseQuestion(raw)\n if (!q)\n continue\n if (seen.has(q.id))\n continue\n seen.add(q.id)\n questions.push(q)\n }\n if (questions.length === 0)\n return null\n return { ...(intro ? { intro } : {}), questions }\n }\n\n // Single-question shape: flatten to a one-question payload so resumed\n // sessions that persisted it still render.\n const question = typeof input.question === 'string' ? input.question.trim() : ''\n if (!question)\n return null\n const choices = parseChoices(input.choices)\n const legacy: Question = choices.length > 0\n ? { id: 'answer', prompt: question, type: 'select', choices }\n : { id: 'answer', prompt: question, type: 'textarea' }\n return { ...(intro ? { intro } : {}), questions: [legacy] }\n}\n\nfunction parseQuestion(raw: unknown): Question | null {\n if (!raw || typeof raw !== 'object')\n return null\n const cast = raw as {\n id?: unknown\n prompt?: unknown\n type?: unknown\n description?: unknown\n required?: unknown\n placeholder?: unknown\n default?: unknown\n choices?: unknown\n multi?: unknown\n affirmLabel?: unknown\n denyLabel?: unknown\n }\n if (typeof cast.id !== 'string' || !cast.id)\n return null\n if (typeof cast.prompt !== 'string' || !cast.prompt.trim())\n return null\n const type = cast.type\n if (type !== 'text' && type !== 'textarea' && type !== 'select' && type !== 'confirm')\n return null\n\n const base = {\n id: cast.id,\n prompt: cast.prompt,\n ...(typeof cast.description === 'string' ? { description: cast.description } : {}),\n ...(typeof cast.required === 'boolean' ? { required: cast.required } : {}),\n }\n\n if (type === 'text' || type === 'textarea') {\n return {\n ...base,\n type,\n ...(typeof cast.placeholder === 'string' ? { placeholder: cast.placeholder } : {}),\n ...(typeof cast.default === 'string' ? { default: cast.default } : {}),\n }\n }\n\n if (type === 'select') {\n const choices = parseChoices(cast.choices)\n if (choices.length === 0)\n return null // a select with zero choices is unusable\n return { ...base, type, choices, ...(cast.multi === true ? { multi: true } : {}) }\n }\n\n // type === 'confirm'\n return {\n ...base,\n type,\n ...(typeof cast.affirmLabel === 'string' ? { affirmLabel: cast.affirmLabel } : {}),\n ...(typeof cast.denyLabel === 'string' ? { denyLabel: cast.denyLabel } : {}),\n }\n}\n\nfunction parseChoices(raw: unknown): QuestionChoice[] {\n if (!Array.isArray(raw))\n return []\n const choices: QuestionChoice[] = []\n for (const c of raw) {\n if (!c || typeof c !== 'object')\n continue\n const cast = c as { id?: unknown, label?: unknown, description?: unknown, selected?: unknown }\n if (typeof cast.id !== 'string' || typeof cast.label !== 'string')\n continue\n choices.push({\n id: cast.id,\n label: cast.label,\n ...(typeof cast.description === 'string' ? { description: cast.description } : {}),\n // Preserved through the sanitizer, not just accepted at the type\n // level: a question rehydrated from a persisted session would\n // otherwise reopen with every pre-tick silently dropped.\n ...(cast.selected === true ? { selected: true } : {}),\n })\n }\n return choices\n}\n\n// ---------------------------------------------------------------------------\n// Tool factory — builds `present_plan` and `ask_user` ToolDefs bound to a\n// host-provided async resolver. Used by the chat host's agent build\n// (`host/agent-build/tools.ts`); a GUI consumer wires its own React resolver\n// the same way.\n// ---------------------------------------------------------------------------\n\n/**\n * Key of the awakeable a checkpoint run parks a model interaction on\n * (`ask_user`, `present_plan`): its run and call id, which the session's\n * turns hold too, so a host that rehydrates the prompt after a restart finds\n * the awakeable (`DurableRuntime.findAwakeable`). Turn 0: awakeables are\n * kept until their run ends, never pruned by turn.\n * @param runId - The run that asked.\n * @param requestId - The request id: the tool call id.\n */\nexport function interactionAwakeableKey(runId: string, requestId: string): StepKey {\n return stepKey({ run: runId, turn: 0, call: requestId, attempt: 0 })\n}\n\n/**\n * What a prompt's awakeable holds when the prompt was refused or dismissed\n * (its resolver rejected) instead of answered: a run resumed past it gets\n * the same tool error again, and is never asked again.\n */\nexport interface RefusedInteraction {\n refused: string\n}\n\n/** Whether an awakeable's value is a refusal ({@link RefusedInteraction}), not an answer. */\nexport function isRefusedInteraction(value: unknown): value is RefusedInteraction {\n return typeof value === 'object' && value !== null && !Array.isArray(value) && typeof (value as { refused?: unknown }).refused === 'string'\n}\n\n/** What the interaction tools hand {@link CreateInteractionToolsOptions.requestInteraction} besides the request. */\nexport interface RequestInteractionOptions {\n /**\n * Set when the run keeps step checkpoints: the run parks on this awakeable,\n * and the response is the value it resolves with. A resolver answers with\n * `runtime.resolveAwakeable(awakeable.id, response)` so the answer survives\n * a process death, and settles the request when the awakeable is resolved\n * elsewhere (another process answered it). The tool settles on whichever\n * comes first.\n */\n awakeable?: DurableAwakeable<InteractionResponse>\n}\n\n/** Options for {@link createInteractionTools}. */\nexport interface CreateInteractionToolsOptions {\n /**\n * Called when the agent invokes one of the interaction tools. Resolves\n * with the user's response (forwarded to the model as the tool_result);\n * reject to signal denial / abort (the model receives\n * `Tool error: <message>` as the result).\n */\n requestInteraction: (request: InteractionRequest, options?: RequestInteractionOptions) => Promise<InteractionResponse>\n /** Evaluated at invocation time so hosts can switch modes without rebuilding the agent. */\n shouldSkipInteractions?: () => boolean\n}\n\n/**\n * Create the `present_plan` and `ask_user` tools as a record, ready to\n * spread into `createAgent({ tools })` or composed into a {@link Preset}.\n *\n * @param opts - Resolver for the user's response and an optional skip check.\n * @example\n * ```ts\n * import { createInteractionTools } from 'zidane/chat'\n *\n * const tools = createInteractionTools({\n * requestInteraction: async () => ({ kind: 'plan', decision: 'approve' }),\n * })\n * ```\n */\nexport function createInteractionTools(opts: CreateInteractionToolsOptions): Record<string, ToolDef> {\n return {\n [PRESENT_PLAN_TOOL]: createPlanTool(opts),\n [ASK_USER_TOOL]: createQuestionTool(opts),\n }\n}\n\n/**\n * A checkpoint run's interaction runs outside a step and parks on an\n * awakeable (see {@link awaitInteractionResponse}); a step around it would\n * report an open prompt interrupted after a process death. Replay mode keeps\n * the step.\n */\nconst interactionDurableBody = (_execution: unknown, mode: DurableMode): 'composite' | undefined => mode === 'checkpoint' ? 'composite' : undefined\n\n/**\n * The response to a model interaction. A run that keeps step checkpoints\n * parks on an awakeable keyed by its call ({@link interactionAwakeableKey}):\n * the answer is stored before the tool goes on, and a run resumed after a\n * process death gets it back, from an answer given before or during the\n * downtime, without asking again. A refusal settles it too\n * ({@link RefusedInteraction}): an awakeable left unresolved would park the\n * run for good. Other runs ask the resolver alone.\n */\nasync function awaitInteractionResponse(\n { requestInteraction, shouldSkipInteractions }: CreateInteractionToolsOptions,\n request: InteractionRequest,\n ctx: ToolContext,\n): Promise<InteractionResponse> {\n if (shouldSkipInteractions?.())\n return createAutomaticInteractionResponse(request)\n const durable = ctx.durable\n if (durable?.checkpoint !== true)\n return requestInteraction(request)\n const awakeable = await durable.awakeable<InteractionResponse | RefusedInteraction>({ key: interactionAwakeableKey(durable.runId, request.id) })\n if (awakeable.settled)\n return storedInteractionResponse(awakeable.settled.value)\n // No deadline: the awakeable settles only with a value.\n const answered = awakeable.promise.then(outcome => outcome.timedOut ? new Promise<never>(() => {}) : outcome.value)\n let response: InteractionResponse | RefusedInteraction\n try {\n response = await Promise.race([requestInteraction(request, { awakeable: awakeable as DurableAwakeable<InteractionResponse> }), answered])\n }\n catch (err) {\n // An aborted run (stopped, taken over, the host shutting down) leaves the awakeable alone: after a takeover it is the new owner's.\n if (!ctx.signal.aborted) {\n const refused: RefusedInteraction = { refused: err instanceof Error ? err.message : String(err) }\n await durable.runtime.resolveAwakeable(awakeable.id, { ...refused }).catch(() => false)\n }\n throw err\n }\n return storedInteractionResponse(response)\n}\n\n/** The response an awakeable holds; a refusal throws its error again. */\nfunction storedInteractionResponse(value: InteractionResponse | RefusedInteraction): InteractionResponse {\n if (isRefusedInteraction(value))\n throw new Error(value.refused)\n return value\n}\n\nconst PLAN_DESCRIPTION\n = 'Present a multi-step plan to the user for approval BEFORE executing it. '\n + 'Use this when you have a concrete proposal that materially changes the workspace '\n + 'and want explicit confirmation. The user can approve, reject, or request revisions; '\n + 'revisions come back as a comment alongside a `revise` decision.'\n\nconst QUESTION_DESCRIPTION\n = 'Ask the user one or more clarifying questions and pause until they answer. '\n + 'Batch related questions into a single call instead of asking one at a time — '\n + 'every call pauses the conversation. '\n + 'Each question carries a `type`: '\n + '`text` (single line), `textarea` (multi-line), `select` (one of `choices`), or '\n + '`confirm` (yes/no). Use sparingly: only when explicit user input materially '\n + 'changes the next steps. The response may carry `instructions`: free text the '\n + 'user added alongside their answers. It refines or overrides the questions and is '\n + 'authoritative.'\n\nfunction createPlanTool(opts: CreateInteractionToolsOptions): ToolDef {\n const tool: DurableToolDef = {\n durableBody: interactionDurableBody,\n spec: {\n name: PRESENT_PLAN_TOOL,\n description: PLAN_DESCRIPTION,\n inputSchema: {\n type: 'object',\n properties: {\n title: { type: 'string', description: 'Short headline summary of the plan.' },\n plan: { type: 'string', description: 'Full plan body, written as Markdown.' },\n steps: {\n type: 'array',\n description: 'Optional structured step list. Each step has an `id`, `title`, and optional `description`.',\n items: {\n type: 'object',\n properties: {\n id: { type: 'string' },\n title: { type: 'string' },\n description: { type: 'string' },\n },\n required: ['id', 'title'],\n },\n },\n },\n required: ['title', 'plan'],\n },\n },\n async execute(input, ctx) {\n const payload = parsePlanPayload(input)\n if (!payload)\n throw new Error('present_plan: invalid input, expected non-empty `title` and `plan` strings.')\n const request: PlanRequest = {\n id: ctx.callId,\n origin: 'model',\n kind: 'plan',\n tool: PRESENT_PLAN_TOOL,\n payload,\n ...(ctx.runId ? { runId: ctx.runId } : {}),\n turnId: ctx.turnId,\n createdAt: Date.now(),\n }\n const response = await awaitInteractionResponse(opts, request, ctx)\n if (response.kind !== 'plan')\n throw new Error(`present_plan: unexpected response kind \"${response.kind}\".`)\n return serializeInteractionResponse(response)\n },\n }\n return tool\n}\n\nfunction createQuestionTool(opts: CreateInteractionToolsOptions): ToolDef {\n const tool: DurableToolDef = {\n durableBody: interactionDurableBody,\n spec: {\n name: ASK_USER_TOOL,\n description: QUESTION_DESCRIPTION,\n inputSchema: {\n type: 'object',\n properties: {\n intro: {\n type: 'string',\n description: 'Optional context shown above the questions (Markdown).',\n },\n questions: {\n type: 'array',\n description: 'One or more questions for the user to answer in a single form.',\n minItems: 1,\n items: {\n type: 'object',\n properties: {\n id: {\n type: 'string',\n description: 'Stable identifier — keys the answer back to the model.',\n },\n prompt: {\n type: 'string',\n description: 'The question to display.',\n },\n type: {\n type: 'string',\n enum: ['text', 'textarea', 'select', 'confirm'],\n description: '`text` = single line, `textarea` = multi-line, `select` = pick from `choices`, `confirm` = yes/no.',\n },\n description: {\n type: 'string',\n description: 'Optional helper text shown under the prompt.',\n },\n required: {\n type: 'boolean',\n description: 'Whether a non-empty answer is required.',\n },\n placeholder: {\n type: 'string',\n description: 'For `text` / `textarea`: placeholder shown when the input is empty.',\n },\n default: {\n type: 'string',\n description: 'For `text` / `textarea`: pre-fill the input with this value.',\n },\n choices: {\n type: 'array',\n description: 'The available choices. Required when `type` is `select`; omit for other types.',\n items: {\n type: 'object',\n properties: {\n id: { type: 'string' },\n label: { type: 'string' },\n description: { type: 'string' },\n selected: {\n type: 'boolean',\n description: 'For a `multi` select: open with this choice already ticked (opt-out lists).',\n },\n },\n required: ['id', 'label'],\n },\n },\n affirmLabel: {\n type: 'string',\n description: 'For `confirm`: label for the affirmative option. Default: `\"yes\"`.',\n },\n denyLabel: {\n type: 'string',\n description: 'For `confirm`: label for the negative option. Default: `\"no\"`.',\n },\n },\n required: ['id', 'prompt', 'type'],\n },\n },\n },\n required: ['questions'],\n },\n },\n async execute(input, ctx) {\n const payload = parseQuestionPayload(input)\n if (!payload)\n throw new Error('ask_user: invalid input, expected a non-empty `questions` array.')\n const request: QuestionRequest = {\n id: ctx.callId,\n origin: 'model',\n kind: 'question',\n tool: ASK_USER_TOOL,\n payload,\n ...(ctx.runId ? { runId: ctx.runId } : {}),\n turnId: ctx.turnId,\n createdAt: Date.now(),\n }\n const response = await awaitInteractionResponse(opts, request, ctx)\n if (response.kind !== 'question')\n throw new Error(`ask_user: unexpected response kind \"${response.kind}\".`)\n return serializeInteractionResponse(response)\n },\n }\n return tool\n}\n\n// ---------------------------------------------------------------------------\n// Queue entries — what the React queue (`interactions.tsx`) holds.\n// ---------------------------------------------------------------------------\n\n/** A queued interaction request with the callbacks that settle it. */\nexport interface PendingInteractionEntry {\n /** The request waiting for the user. */\n request: InteractionRequest\n /** The user picked something — forward to the live tool Promise or the resumed tool_result writer. */\n resolve: (response: InteractionResponse) => void\n /**\n * The user / system cancelled — distinct from `resolve` so renderers can\n * differentiate \"user picked something\" from \"user gave up / aborted\".\n * Live entries reject the tool's Promise; resumed entries decide what\n * to persist as the cancelled tool_result.\n */\n cancel: (reason?: string) => void\n}\n\n/**\n * Stable queue actions exposed by {@link InteractionsProvider}. Read them with {@link useInteractionsActions}.\n */\nexport interface InteractionsActions {\n /** Add a request to the FIFO queue. */\n enqueue: (entry: PendingInteractionEntry) => void\n /** Resolve the head request with `response`. */\n resolveHead: (response: InteractionResponse) => void\n /**\n * Cancel the head request. Meant for per-call cancel\n * affordances (a \"skip this\" button, a GUI's close-modal-X) — the\n * built-in TUI currently cancels via the run-level `cancelAll` path\n * only. Kept on the public API so consumer renderers can wire it\n * without a follow-up surface change.\n */\n cancelHead: (reason?: string) => void\n /** Cancel every queued request. Used by abort / session teardown. */\n cancelAll: (reason?: string) => void\n /**\n * Remove the queued entry whose `request.id` matches, WITHOUT settling\n * it locally — used by the daemon runtime when ANOTHER window resolved\n * the same shared interaction. No-op when absent.\n */\n dropById: (requestId: string) => void\n}\n\n// ---------------------------------------------------------------------------\n// Live-tool binding helper — wraps the queue actions into a Promise-shaped\n// `requestInteraction` that {@link createInteractionTools} expects.\n// ---------------------------------------------------------------------------\n\n/**\n * Build the `requestInteraction` callback for `createInteractionTools` from\n * the React-side {@link InteractionsActions}. Each call adds a fresh entry\n * to the queue whose `resolve` / `cancel` route back to one Promise that\n * the tool's `execute` awaits.\n *\n * @param actions - Queue actions from {@link useInteractionsActions}.\n */\nexport function makeRequestInteraction(\n actions: InteractionsActions,\n): CreateInteractionToolsOptions['requestInteraction'] {\n return request => new Promise<InteractionResponse>((resolve, reject) => {\n actions.enqueue({\n request,\n resolve,\n cancel: reason => reject(new Error(reason ?? 'Interaction cancelled')),\n })\n })\n}\n","/**\n * Host-domain wire shapes — the JSON-serializable types the chat host\n * (`createChatHost`) emits and consumes, independent of any transport.\n *\n * They live here so the host (`src/chat/host/**`) has zero dependency on\n * `src/ipc/**`. The IPC layer re-exports them from `src/ipc/protocol.ts`\n * alongside its transport-only\n * contract (method map, handshake, protocol version), so `zidane/ipc`\n * consumers are unaffected.\n *\n * Everything in this file is JSON-serializable: it must survive Electron's\n * structured clone AND a JSON-RPC socket hop unchanged. Canonical agent\n * types (`StreamEvent`, `InteractionRequest`, …) are re-used where they are\n * already wire-safe; host-only shapes (live `resolve` callbacks, `Map`s)\n * never appear here.\n */\nimport type { CacheBreakInfo } from '../../cache-telemetry'\nimport type { RunErrorDetail } from '../../errors'\nimport type { LogRecord } from '../../logger'\nimport type { PerfSample } from '../../perf/types'\nimport type { SessionArtifact } from '../../session/artifacts'\nimport type { TextOrigin } from '../../text-origin'\nimport type { JsonValue, TurnUsage } from '../../types'\nimport type { BrowserRequest } from '../browser/protocol'\nimport type { HunkResolution } from '../edit-diff'\nimport type { IntegrationInfo } from '../integrations'\nimport type { InteractionRequest } from '../interactions'\nimport type { InteractionAnswerer } from '../interactions-protocol'\nimport type { MachineLinksStatus, MachineStatusChange } from '../machine-protocol'\nimport type { RunEstimateView } from '../run-estimate'\nimport type { ApprovalDecision, ApprovalRequest } from '../safe-mode-context'\nimport type { SystemMdFile } from '../system-md'\nimport type { TerminalInfo } from '../terminal/protocol'\nimport type { ActiveTodosState, TodoItem, TodoTally } from '../todos'\nimport type { PromptTurnRef } from '../turn-selection'\nimport type { EditPayload, Settings, StreamEvent } from '../types'\nimport type { ExtensionsSignal } from './extensions-wire'\nimport type { ProviderDiagnostic } from './provider-diagnostic'\n\n/** Caller attribution for approvals raised by subagents. */\nexport type ApprovalOriginator = { kind: 'parent' } | { kind: 'child', label: string }\n\n/**\n * Serializable mirror of canonical `ActiveTodosState` — plain arrays so the\n * payload survives structured clone / JSON without `readonly` friction.\n */\nexport interface TodosSnapshot {\n /** Id of the run the todos belong to, or `null` when none is active. */\n runId: string | null\n /** Current todo items. */\n todos: TodoItem[]\n /** Last non-empty list, kept so the UI can show it after the live list is cleared. */\n archive: TodoItem[]\n /** The item being worked on, or `null`. */\n inProgress: TodoItem | null\n /** Counts of items by state. */\n tally: TodoTally\n}\n\n/**\n * Project canonical `ActiveTodosState` onto its wire mirror.\n *\n * One implementation because the copy is load-bearing, not cosmetic: the arrays\n * must be detached from the live session's before they cross a process boundary\n * (or reach another window in-process), and a site that forgot to copy would\n * hand out a reference that mutates under its reader. The broadcast and RPC\n * sites all call it.\n *\n * @param state - Active todos state of a session.\n */\nexport function toTodosSnapshot(state: ActiveTodosState): TodosSnapshot {\n return {\n runId: state.runId,\n todos: [...state.todos],\n archive: [...state.archive],\n inProgress: state.inProgress,\n tally: state.tally,\n }\n}\n\n/** Parent-level cancellable tool call or background task. */\nexport interface RunningWorkItem {\n /** Whether the item is a tool call or a background task. */\n kind: 'tool' | 'task'\n /** Tool call id or task id. */\n id: string\n /** Short description shown to the user. */\n label: string\n /** Start time in epoch ms. */\n startedAt: number\n /** Id of the subagent that started the item, when it was not the parent. */\n childId?: string\n /**\n * A call of a batch a durable run resumed after a process death, that the\n * dead process did not finish (its `tool` event says `resuming`). A\n * window attaching mid-run reads it from here. Optional: older hosts never\n * send it.\n */\n resuming?: true\n}\n\n/**\n * Serializable mirror of `PreviewResult` — `readonly` arrays widened so the\n * payload round-trips without TypeScript friction on the client side.\n */\nexport interface SerializedPreview {\n /** Unified diff of the proposed edit. */\n diffText: string\n /** The diff split into one string per hunk. */\n perHunkDiff: readonly string[]\n /** How the edit tool would resolve each hunk: whether its `old_string` is found without ambiguity. */\n resolution: readonly HunkResolution[]\n /** Edit payload with the resolution applied. */\n resolvedPayload: EditPayload\n}\n\n/**\n * Wire-safe approval request. Canonical `ApprovalRequest` minus its live\n * `resolve` callback (kept host-side, keyed by `id`), plus the context a\n * remote approval surface needs: owning session, safelist cwd, precomputed\n * safelist suggestions, and the edit preview byproducts.\n */\nexport interface WireApprovalRequest extends Omit<ApprovalRequest, 'resolve'> {\n /** Session that raised the request. */\n sessionId: string\n /** Working directory that safelist entries are scoped to. */\n cwd: string\n /** Safelist entries the user can add when approving. */\n suggestedSafelistEntries: string[]\n /** The edit under approval, for edit tools. */\n edit?: EditPayload\n /** File content before the edit. `null` when the file did not exist. */\n priorContent?: string | null\n /** Diff preview of the edit. */\n preview?: SerializedPreview\n}\n\n/** Attachment carried alongside the prompt text (base64, no data-URL prefix). */\nexport interface PromptAttachment {\n /** MIME type of the attachment. */\n mediaType: string\n /** Base64 content. */\n data: string\n /** File name, when known. */\n name?: string\n}\n\n/** `chat.send` options. */\nexport interface SendOptions {\n /** Session to send the prompt to. */\n sessionId: string\n /** Prompt text. */\n prompt: string\n /** Skill names picked via `/` completion — activated before the run. */\n activatedSkills?: string[]\n /** Attachments sent with the prompt. */\n attachments?: PromptAttachment[]\n /**\n * Host-internal wake run: delivers pending background-task notifications\n * to the model with an empty prompt. Skips the `user-prompt` broadcast\n * (there is no user message) — mirrors the local TUI's wake semantics.\n */\n wake?: boolean\n /**\n * The client resolved this text as the `/` command of this name in ITS\n * registry. The host re-resolves against the SESSION's registry (its cwd,\n * its extensions) and refuses when that misses, instead of sending\n * `/fix comments` to the model as prose — the two registries can disagree\n * (extension installed after the runtime cache was built, different cwd),\n * and a silent prose send is indistinguishable from a broken command.\n */\n asCommand?: string\n /**\n * Sent from outside the composer (Continue, Retry, a palette prompt): the\n * shared draft was not consumed, so the daemon leaves it alone instead of\n * clearing it in every other window.\n */\n keepDraft?: boolean\n /**\n * Who or what the prompt comes from, when not the session's own user: the\n * prompt's text block in the persisted user turn carries it. The host never\n * rewrites the text because of it.\n */\n origin?: TextOrigin\n /** Never resolve the text as a `/` command: it reaches the model as sent. */\n literal?: boolean\n /**\n * The client's own id for this message: the host reports what becomes of it\n * (`delivery:changed`), and sets it as `origin.messageId` when the message\n * has an origin without one.\n */\n clientMessageId?: string\n /**\n * Extension data riding with the prompt (a `prompt` interceptor's\n * `metadata`): later interceptors and the run's `run` interceptors see it\n * (`AgentRunOptions.metadata`). Not saved with the user turn.\n */\n metadata?: Record<string, JsonValue>\n}\n\n/** The fields of a prompt that wait with it in the queue until it runs (`chat.pushQueued`, `queue.add`). */\nexport type QueuedPromptFields = Pick<SendOptions, 'origin' | 'literal' | 'clientMessageId' | 'metadata'>\n\n/**\n * What became of a message sent with a `clientMessageId`: parked in the host\n * queue, injected into the live run, the prompt of a run that started, the\n * end of the run that took it (`answered` / `failed` / `interrupted`), or\n * removed from the queue unsent (`dropped`).\n */\nexport type DeliveryState = 'queued' | 'injected' | 'started' | 'answered' | 'failed' | 'interrupted' | 'dropped'\n\n/**\n * Wire-safe summary of an extension-contributed `/` command — the\n * projection of canonical `Command` (src/chat/commands.ts) minus its\n * function-valued fields (steps, completeArgs).\n */\nexport interface CommandSummary {\n /** Command name, without the leading `/`. */\n name: string\n /** One-line description shown in completion lists. */\n description: string\n /** Alternative names that run the same command. */\n aliases?: string[]\n\n /** Grouping label — defaults to the contributing extension's name. */\n group?: string\n\n /**\n * `true` when the canonical command declares `completeArgs` — lets a\n * client wire an arg-completion shim only for commands that actually\n * complete, instead of firing blind RPCs on every `/name <prefix>`.\n */\n hasArgCompletion?: boolean\n}\n\n/** Wire-safe projection of canonical `CommandArgSuggestion`. */\nexport interface CommandArgSuggestionSummary {\n /** Text inserted after `/<name> ` on accept. */\n value: string\n\n /** Display label. Defaults to `value`. */\n label?: string\n /** Secondary text shown next to the label. */\n description?: string\n}\n\n/** Host settings — canonical `Settings` plus the extension allowlist. */\nexport type HostSettings = Settings & {\n enabledExtensions?: readonly string[]\n /** @deprecated Use `approvalMode` (`safeMode: false` is `approvalMode: 'off'`); read only when `approvalMode` is absent. */\n safeMode?: boolean\n}\n\n/** One entry in the shared type-ahead queue. */\nexport interface QueuedItem {\n /** Queue entry id. */\n id: string\n /** Queued prompt text. */\n text: string\n /** The sender's id for the message (`SendOptions.clientMessageId`), when it gave one. */\n clientMessageId?: string\n /**\n * What rides with the text, WITHOUT the bytes — enough for a queue row to\n * show \"+2 attachments\" chips the way the echoed prompt will. The payload\n * itself stays host-side until the entry is sent.\n */\n attachments?: QueuedAttachment[]\n}\n\n/** An attachment on a queued prompt, without its bytes. */\nexport interface QueuedAttachment {\n /** MIME type of the attachment. */\n mediaType: string\n /** File name, when known. */\n name?: string\n}\n\n/**\n * Live per-session presence — which sessions have attached windows and\n * what state they're in. Drives the session list's \"active\" scope and\n * per-row status icons. A session appears here when it has ≥1 attached\n * window, a run in flight, or pending HITL state.\n */\nexport interface SessionPresence {\n /** Session the presence is for. */\n sessionId: string\n /** Number of windows currently attached (watching) this session. */\n windows: number\n /** A run is executing right now. */\n running: boolean\n /** Pending approval or interaction — awaiting user input somewhere. */\n attention: boolean\n}\n\n/** Pending interaction row as returned by `interactions.list`. */\nexport interface PendingInteraction {\n /** Session that raised the interaction. */\n sessionId: string\n /** The interaction request awaiting an answer. */\n request: InteractionRequest\n /** `true` when a live agent loop is waiting on the answer. `false` for one restored after a restart. */\n live: boolean\n}\n\n/** Payload of the `stream:progress` signal — see the variant's doc. */\nexport interface StreamProgressInfo {\n /** Turn that is streaming. */\n turnId: string\n /** Output tokens so far. */\n outputTokens: number\n /** `true` when `outputTokens` is an estimate rather than provider-counted. */\n estimated: boolean\n /** Time since the turn started in ms. */\n elapsedMs: number\n /** Running cost estimate for this turn in USD. Absent for unpriced models. */\n estCostUsd?: number\n}\n\n/**\n * The single push channel. Chat stream signals mirror the agent loop hooks;\n * interaction/approval signals carry the HITL queues so every window renders\n * the same pending state; `sessions:changed` keeps session lists in sync\n * across windows.\n */\nexport type HostSignal\n /** `startedAt`: when the run began, epoch ms on the host's clock. Additive: absent from older hosts. */\n = | { kind: 'run:start', sessionId: string, runId: string, startedAt?: number }\n /**\n * `background: true` marks a session the user doesn't open themselves\n * (`isBackgroundSession`), so clients keep quiet about its run ending.\n * `detail` is what a failed run's one-line `error` leaves out.\n */\n /** `takenOver`: another process claimed the run, which goes on there (`error` says so). Optional: older clients show `error`. */\n | { kind: 'run:end', sessionId: string, runId: string, ok: boolean, error?: string, detail?: RunErrorDetail, background?: true, takenOver?: true }\n /**\n * A turn finished. `usage` carries the loop-reported token/cost figures\n * that drive footer gauges (context size = prompt-side tokens of the\n * LAST parent turn; cost accrues across parent + child turns). A parent\n * turn's `runOutputTokens` is its run's output so far, this turn\n * included: a run readout takes it as is instead of adding turns up, so a\n * window that read the count mid-run never counts a turn twice.\n */\n | { kind: 'turn:end', sessionId: string, turnId: string, childId?: string, depth?: number, usage?: TurnUsage, runOutputTokens?: number }\n /**\n * `echo: true` marks host-generated SEND/STEER echoes of the user's own\n * message. Clients that already render the prompt locally (the GUI's\n * enriched append) drop echo-tagged signals; command-module\n * user-prompts are never echoes and always render.\n */\n | { kind: 'event', sessionId: string, event: StreamEvent, echo?: boolean }\n | { kind: 'compacting:start', sessionId: string }\n /**\n * Live token estimate for an in-flight compaction summary. Throttled by the\n * loop, so this is a low-rate progress tick rather than a stream.\n *\n * Without it a daemon-mode window shows a static \"Compacting…\" with no\n * indication of progress — a compaction of a large transcript can run for\n * minutes, which is indistinguishable from a hang.\n */\n | { kind: 'compacting:progress', sessionId: string, estOutputTokens: number }\n /**\n * Live counters for the parent turn currently streaming — the loop's\n * `stream:progress` (throttled ~4/s, one final tick before `stream:end`).\n * `outputTokens` is provider-counted unless `estimated`; `estCostUsd` is\n * the running catalog-rate estimate for THIS turn (absent for unpriced\n * models and flat-rate subscription traffic). Clients add it to the settled\n * session figure while streaming and drop it when the matching `turn:end`\n * brings the real `usage.cost`.\n */\n | ({ kind: 'stream:progress', sessionId: string } & StreamProgressInfo)\n /**\n * Prompt turns that just persisted, reduced to the fields needed to bind a\n * live `user-prompt` event to its turn id (see {@link PromptTurnRef}).\n *\n * Deliberately NOT the full `SessionTurn`: those carry every content block,\n * including base64 image/document payloads, which would be megabytes per\n * prompt to ship for the sake of an id. Without this a prompt sent from a\n * daemon-attached window is unselectable — no hover, edit, or delete — until\n * the run-end transcript replace.\n */\n | { kind: 'prompt-turns', sessionId: string, refs: readonly PromptTurnRef[] }\n /**\n * `effectiveTokens` (success only) is the loop's post-compact context\n * estimate — windows drop their footer ctx gauge to it immediately\n * instead of waiting for the next turn's usage.\n */\n | { kind: 'compacting:end', sessionId: string, ok: boolean, effectiveTokens?: number }\n | {\n kind: 'model:fallback'\n sessionId: string\n from: { provider: string, model: string }\n to: { provider: string, model: string }\n /** The failed target had streamed reasoning for this turn: windows drop it (see `stream:retry`). */\n discardTurnId?: string\n }\n | { kind: 'work:snapshot', sessionId: string, items: RunningWorkItem[] }\n | { kind: 'todos:snapshot', sessionId: string, snapshot: TodosSnapshot }\n /**\n * The session recorded an artifact (PR, branch, commit, file, extension\n * kind — see `session/artifacts.ts`). Carries the stored row so a window\n * can badge / list without a round-trip; `sessions.artifacts` is the\n * authoritative query. Additive — older clients ignore unknown kinds.\n */\n | { kind: 'artifact:recorded', sessionId: string, artifact: SessionArtifact }\n /**\n * An artifact was removed or refreshed through the host (`artifacts.delete`\n * / `artifacts.upsert`) — windows re-query. Carries the row for `upsert`.\n */\n | { kind: 'artifact:changed', sessionId: string, op: 'delete' | 'upsert', artifactId: string, artifact?: SessionArtifact }\n | { kind: 'interaction:request', sessionId: string, request: InteractionRequest, live: boolean }\n | { kind: 'interaction:resolved', sessionId: string, requestId: string, answeredBy?: InteractionAnswerer }\n /**\n * Dev browser (`browser.ts` hub): a tool call for the page, addressed to\n * the registered browser host (`hostId`); other clients ignore it. The\n * host answers through `browser.respond`. `browser:cancel` withdraws it.\n */\n | { kind: 'browser:request', sessionId: string, hostId: string, request: BrowserRequest }\n | { kind: 'browser:cancel', sessionId: string, hostId: string, requestId: string }\n /**\n * Terminals (`terminal/` manager). `changed` carries the session's full\n * list; `output` is a batch starting at `offset` (clients drop what they\n * already have and `terminal.ack` what they wrote); `reset` replaces a\n * view's content with history after a client fell too far behind.\n */\n | { kind: 'terminal:changed', sessionId: string, terminals: TerminalInfo[] }\n | { kind: 'terminal:output', sessionId: string, terminalId: string, offset: number, data: string }\n | { kind: 'terminal:reset', sessionId: string, terminalId: string, offset: number, data: string }\n /**\n * The full list of a project's terminals (`draftId` absent) or of one\n * draft's. No `sessionId`: these belong to no session (session signals are\n * numbered and replayed per session). Output of these terminals still\n * arrives as `terminal:output` / `terminal:reset` with `sessionId: ''`,\n * sent only to the views attached to them.\n */\n | { kind: 'terminal:project-changed', root: string, draftId?: string, terminals: TerminalInfo[] }\n /**\n * A streamed workspace command (`sessions.workspaceExecStream`): its output\n * in arrival order, then exactly one `exit`. Sent to the connection that\n * started it only, never fanned out or replayed. `code` is `null` and\n * `signal` set when the host killed it (`cancelled`: the client asked;\n * `truncated`: the output cap was reached).\n */\n | { kind: 'workspace:exec:data', execId: string, stream: 'stdout' | 'stderr', chunk: string }\n | { kind: 'workspace:exec:exit', execId: string, code: number | null, signal: string | null, truncated?: true, cancelled?: true }\n /** Git's progress on the connection's `git.clone`, to that connection only. */\n | { kind: 'git:clone:progress', phase: string, percent: number }\n /** A checkout's refs moved (`git.watch`), to the connections watching `cwd` only. */\n | { kind: 'git:refs', cwd: string }\n /** An extension panel's content changed (`panels.watch`), to the connections watching it only. */\n | { kind: 'panel:changed', cwd: string, panelId: string }\n /** `cwd`'s extension palette items may have changed (`palette.watch`), to the connections watching only. */\n | { kind: 'palette:changed', cwd: string }\n /** A new record in a zidane-server's log (`logs.watch`), to the connections following it only. */\n | { kind: 'logs:record', record: LogRecord }\n /** The host's performance sample (`perf.watch`), to the connections watching it only. */\n | { kind: 'perf:sample', sample: PerfSample }\n /**\n * A client installed, updated or removed something on the host\n * (`catalog.install` / `remove` / `update`, `mcp.upsert` / `remove`): every\n * window re-reads that catalog. `models`: what `models.list` answers\n * changed (an extension's provider or account model list).\n */\n | { kind: 'catalog:changed', catalog: 'skills' | 'mcps' | 'extensions' | 'plugins' | 'models' }\n /** A server's settings-sync record changed (a claim, a push, an override, a reset): settings UIs re-read `sync.status`. */\n | { kind: 'sync:changed' }\n /** A zidane-server's tokens changed (one created, revoked, paired, or the token file edited): token lists re-read `tokens.list`. */\n | { kind: 'tokens:changed' }\n | { kind: 'approval:request', request: WireApprovalRequest }\n /**\n * An approval left the pending queue (answered or denied in bulk) — every\n * OTHER window clears its surface. The window that resolved it already\n * knows; the signal makes the queue converge everywhere else.\n */\n | { kind: 'approval:resolved', sessionId: string, requestId: string, decision: ApprovalDecision, answeredBy?: InteractionAnswerer }\n | { kind: 'sessions:changed', op: 'create' | 'update' | 'delete', sessionId: string }\n /**\n * A session's facet values changed (`ctx.sessions.facets`): every facet\n * it holds now, keyed `<extensionId>.<key>`, the same as `SessionRow.facets`.\n * Not a `sessions:changed`: the row's other fields stand.\n */\n | { kind: 'sessions:facets', sessionId: string, facets: Record<string, JsonValue> }\n /**\n * Settings were written through the host (`settings.set`) — every window\n * applies the new state so settings UIs converge without a restart.\n * Windows that persist settings themselves must treat this as\n * authoritative for the delivered snapshot.\n */\n | { kind: 'settings:changed', settings: HostSettings }\n /**\n * Another window edited the session's shared prompt draft. The daemon\n * EXCLUDES the originating peer from this fan-out, so applying it can\n * never fight the local cursor — clients apply unconditionally\n * (last-writer-wins whole-text replacement). Empty `text` clears the\n * draft (broadcast when any window submits the prompt).\n */\n | { kind: 'draft:changed', sessionId: string, text: string, shell?: boolean }\n /**\n * The shared draft's composing ATTACHMENTS changed (paste/drop/remove in\n * any window). Payloads ride along so a remote window can submit the\n * shared draft attachments-and-all. Same origin-exclusion as text.\n */\n | { kind: 'draft:attachments', sessionId: string, attachments: PromptAttachment[] }\n /**\n * The session's shared transcript-search state changed (mode entered /\n * exited, query edited) in another window. Same origin-exclusion and\n * last-writer-wins contract as the draft — every window on the session\n * shows the same filtered transcript.\n */\n | { kind: 'search:changed', sessionId: string, active: boolean, query: string }\n /**\n * The daemon resolved another window's inline-autocomplete query\n * (`suggest.resolve`). Excludes the requesting peer (it already has\n * the result from its RPC reply). Receivers mirror the ghost ONLY\n * when their live buffer still equals `basis` — the standard\n * staleness guard; otherwise discard.\n */\n | { kind: 'suggest:changed', sessionId: string, seq: number, basis: string, caret: number, suggestion: string | null, candidates?: string[] }\n /**\n * The text model's guess at the user's next prompt after run `runId`\n * (`Settings.promptSuggestions`). Windows offer it in the empty composer\n * and drop it on the next `run:start`, a send, an edit or a compaction.\n */\n | { kind: 'suggestion:ready', sessionId: string, runId: string, text: string }\n /**\n * A run of the session was estimated, or its wait on the user started or\n * ended (`Settings.runEstimates`): the session's own run (`depth` 0) or a\n * subagent's (`label`, `callId`). Saved on the run record too, so a window\n * that missed it reads it from the session row's `runEstimates`.\n */\n | { kind: 'run:estimate', sessionId: string, estimate: RunEstimateView }\n /**\n * The daemon-side agent's active-skill set changed (`skills_use` /\n * run-end deactivation). Windows mirror it into their skill chips.\n */\n | { kind: 'skills:snapshot', sessionId: string, active: string[] }\n /**\n * A daemon-side MCP server needs (re-)authentication. Windows surface\n * the badge; the interactive login flow itself runs in whichever window\n * the user triggers it from (tokens land in the shared credential\n * store; the daemon reconnects on the next agent rebuild).\n */\n | { kind: 'mcp:auth', sessionId: string, name: string, reason: string }\n /**\n * The session agent's MCP bootstrap finished: every server has its\n * outcome (`mcp:status` / `mcp:auth` came first). A window's deadline for\n * the one-shot auto-login it arms when the user adds a server.\n */\n | { kind: 'mcp:settled', sessionId: string }\n /**\n * A host-side MCP server connected, or failed to. Windows feed it to the\n * same status reducer the in-process path uses (badges: connected, signed\n * in, needs sign-in, key refused, couldn't connect). `error` is the\n * failure's message and `code` its HTTP-ish code when it had one, enough\n * for the shared classifier (`classifyMcpTest`) to read it the same way.\n */\n | { kind: 'mcp:status', sessionId: string, name: string, status: 'connected' }\n | { kind: 'mcp:status', sessionId: string, name: string, status: 'failed', transport: string, error: string, code?: number }\n /**\n * Provider retry in progress (rate-limit / overload / silent stall) —\n * drives the footer's \"retrying n/m\" indicator. `source` is `'parent'`\n * or the retrying child's id; the indicator clears when that source\n * streams again or the run ends. `discardTurnId`: the failed attempt had\n * streamed reasoning for that turn; windows drop it (`discardThinking`),\n * since the retry streams its own and the saved turn never holds it.\n */\n | { kind: 'stream:retry', sessionId: string, attempt: number, max: number, source: string, discardTurnId?: string }\n /**\n * A retried stream reached a terminal state for `source` (`'parent'` or a\n * child id) — clear that source's retry indicator.\n *\n * Distinct kind rather than a nullable `attempt` so the two states stay\n * unambiguous on the wire. Consumers also clear at run end as a backstop;\n * this exists for the mid-run case, where a subagent's stream fails while\n * the parent run continues and the indicator would otherwise stick.\n */\n | { kind: 'stream:retry:cleared', sessionId: string, source: string }\n /**\n * Which `SYSTEM.md` override (if any) the host folded into this session's\n * system prompt, emitted at agent-build time.\n *\n * The prompt is composed where the agent is built — in the daemon, not in\n * the window — so a window cannot derive this by probing its own disk: it\n * may have a different cwd, and `userInstructionsScope` is the daemon's.\n * Windows need it to tell \"no override on disk\" apart from \"override in\n * force\", which is the difference between doctrine the user wrote and\n * doctrine they think they replaced. `shadowed` carries precedence losers\n * so the Instructions surface can mark them inactive.\n */\n | { kind: 'system-md', sessionId: string, file: SystemMdFile | null, shadowed: readonly SystemMdFile[] }\n /**\n * A cache-prefix break was detected for this session's last request — the\n * footer chip names the culprit dimension and the re-billed prefix size.\n * Detection is per-agent closure state inside the loop, so in daemon mode\n * only the daemon can observe it. Cost pricing stays window-side (it needs\n * only the provider key and the token count).\n */\n | { kind: 'cache:break', sessionId: string, info: CacheBreakInfo }\n | { kind: 'provider:diagnostic', sessionId: string, diagnostic: ProviderDiagnostic }\n\n /**\n * Presence snapshot — recomputed and fanned out whenever a window\n * attaches/detaches or any tracked session's run/HITL state changes.\n * Full snapshot (the set is small: only live sessions appear).\n */\n | { kind: 'presence:changed', sessions: SessionPresence[] }\n /**\n * The host's integrations changed (`integrations.register` / `update`, or\n * the client that registered one disconnected). Full list, like presence.\n */\n | { kind: 'integrations:changed', integrations: IntegrationInfo[] }\n /** A machine of this host came online or went offline (`docs/MACHINES.md`). */\n | ({ kind: 'machine:status' } & MachineStatusChange)\n /** This local host's machine links changed (`machines.links.status`): shares of this computer, servers used as machines. */\n | { kind: 'machine:links', links: MachineLinksStatus }\n /**\n * A server lending itself as a machine to the local host on this\n * connection (`machine.attach`): one of its machine notifications\n * (`terminal.data`, `task.exit`, `exec.stream.*`). Sent to that\n * connection only, never broadcast.\n */\n | { kind: 'machine:notification', method: string, params: JsonValue }\n /**\n * The session's type-ahead queue changed (message queued while a run is\n * in flight, drained by the host on run end, pushed early, or dropped).\n * Broadcast to EVERY peer including the origin — the queue box is shared\n * state and all windows render the same list.\n */\n | { kind: 'queue:changed', sessionId: string, items: QueuedItem[] }\n /**\n * A message sent with a `clientMessageId` changed state (`DeliveryState`).\n * `runId`: the run that started with it, took it, or ended with it. For\n * integrations; windows ignore it.\n */\n | { kind: 'delivery:changed', sessionId: string, clientMessageId: string, state: DeliveryState, runId?: string }\n /** Extension API: RPC events, UI items, dialogs, client UI requests, storage, commands, integrations (`./extensions-wire`). */\n | ExtensionsSignal\n","/**\n * A window's overrides for a host-built session agent: the `zidane run`\n * launch flags (`--system / --mcp / --context …`) and a working directory\n * picked mid-session. Owned by the window (never persisted). Dependency-free\n * so the TUI, the daemon dispatcher, and the host share one rule without\n * importing the engine.\n */\nimport type { McpServerConfig } from '../../mcp'\nimport type { StartLocalOptions } from '../../start/shared-options'\n\n/** `zidane run --context …`: the execution this launch runs in, overriding the session's environment. */\nexport interface LaunchExecution {\n /** Execution context kind (`docker`, `e2b`, `daytona`). `process` is the default and not an override. */\n context: string\n image?: string\n env?: Record<string, string>\n pregame?: { name: string, content: string }\n sandboxId?: string\n sandboxOnDestroy?: 'kill' | 'pause' | 'leave'\n}\n\n/** `ChatHost.setLaunchOverrides`: what the window overrides on a session's agent. */\nexport interface SessionLaunchOverrides {\n /** Replaces the base system prompt (extension fragments still append). */\n system?: string\n /** Extra MCP servers, after the discovered and plugin ones (which win a name collision). */\n mcp?: readonly McpServerConfig[]\n /** Execution context to run the session in. */\n execution?: LaunchExecution\n /**\n * Working directory picked mid-session (the TUI's cwd picker), instead of\n * the session's folder: the live agent's tools move at once, its prompt on\n * the next turn, and later builds use it. Not persisted.\n */\n cwd?: string\n}\n\n/**\n * The overrides that shape an agent, or `null` when none do (no `system`,\n * no MCP server, no non-`process` context, no cwd). The one rule for \"are launch\n * overrides set\", shared by the host, the daemon dispatcher, and the TUI.\n *\n * @param overrides - Overrides to reduce. `null` and `undefined` mean none.\n */\nexport function normalizeLaunchOverrides(overrides: SessionLaunchOverrides | null | undefined): SessionLaunchOverrides | null {\n if (!overrides)\n return null\n const mcp = overrides.mcp && overrides.mcp.length > 0 ? overrides.mcp : undefined\n const execution = overrides.execution && overrides.execution.context !== 'process' ? overrides.execution : undefined\n const cwd = overrides.cwd || undefined\n if (overrides.system === undefined && !mcp && !execution && !cwd)\n return null\n return {\n ...(overrides.system !== undefined ? { system: overrides.system } : {}),\n ...(mcp ? { mcp } : {}),\n ...(execution ? { execution } : {}),\n ...(cwd ? { cwd } : {}),\n }\n}\n\n/**\n * A window's overrides for a session (see {@link normalizeLaunchOverrides}):\n * what its `zidane run` launch carries, plus its cwd when that is not the\n * session's own folder (picked mid-session).\n */\nexport function launchOverridesFrom(\n launch: Partial<StartLocalOptions> | null | undefined,\n location?: { cwd: string, sessionFolder: string },\n): SessionLaunchOverrides | null {\n return normalizeLaunchOverrides({\n ...(location && location.cwd !== location.sessionFolder ? { cwd: location.cwd } : {}),\n ...fromLaunch(launch),\n })\n}\n\nfunction fromLaunch(launch: Partial<StartLocalOptions> | null | undefined): SessionLaunchOverrides {\n if (!launch)\n return {}\n return {\n ...(launch.system !== undefined ? { system: launch.system } : {}),\n ...(launch.mcp ? { mcp: launch.mcp } : {}),\n ...(launch.context\n ? {\n execution: {\n context: launch.context,\n ...(launch.image !== undefined ? { image: launch.image } : {}),\n ...(launch.env !== undefined ? { env: launch.env } : {}),\n ...(launch.pregame !== undefined ? { pregame: launch.pregame } : {}),\n ...(launch.sandboxId !== undefined ? { sandboxId: launch.sandboxId } : {}),\n ...(launch.sandboxOnDestroy !== undefined ? { sandboxOnDestroy: launch.sandboxOnDestroy } : {}),\n },\n }\n : {}),\n }\n}\n","/**\n * Automatic session titling (`Settings.autoTitle`): after a run, decide\n * whether the session's title still describes it and regenerate it when not.\n *\n * Title provenance lives on `metadata.titleSource`:\n * - `'auto'`: written by the title generator (automatic, or the session\n * details' \"generate title\" action). Auto-titling may replace it.\n * - `'user'`: typed by the user (a rename). Never replaced automatically.\n * - absent with a title: predates provenance; treated as the user's.\n *\n * Staleness is a decision (`ctx.decide` predicate), checked at most once per\n * {@link MIN_NEW_PROMPTS} user prompts so short follow-ups never retitle.\n */\n\nimport type { AgentDecide } from '../decisions/types'\nimport type { SessionTurn } from '../types'\nimport { renderTurnsForPrompt } from './generate-title'\nimport { oneLineTitle, titleFromText } from './session-title-text'\n\n/** Metadata key that records whether the title was set by auto-titling or by the user. */\nexport const TITLE_SOURCE_KEY = 'titleSource'\n/** User-prompt count at the last staleness check (or generation). */\nexport const TITLE_CHECKED_KEY = 'titleCheckedPrompts'\n/** New user prompts needed before a title is re-checked. */\nexport const MIN_NEW_PROMPTS = 2\n/** Probability above which the title counts as stale. */\nexport const STALE_THRESHOLD = 0.7\n/** Word cap on titles while auto-titling is on (sidebar-sized). */\nexport const AUTO_TITLE_MAX_WORDS = 5\n/** The last auto-title attempt's outcome ({@link TitleCheckRecord}), shown in session details. */\nexport const TITLE_CHECK_KEY = 'titleCheck'\n\n/** Who set a session's title: auto-titling or the user. */\nexport type TitleSource = 'auto' | 'user'\n\n/**\n * What the last auto-title attempt did. Failures are recorded too: auto-\n * titling never surfaces an error mid-conversation, so this is where a\n * missing key or a timeout shows up.\n */\nexport interface TitleCheckRecord {\n /** When the attempt ran, in ms since epoch. */\n at: number\n /** What the attempt did or how it failed. */\n outcome: 'generated' | 'renamed' | 'kept' | 'decision-failed' | 'generation-failed'\n /** Decision failure reason (`DecisionFailureReason`), for `decision-failed`. */\n reason?: string\n /** Failure detail, when the attempt failed. */\n message?: string\n}\n\nfunction readTitleCheck(metadata: Readonly<Record<string, unknown>> | undefined): TitleCheckRecord | undefined {\n const raw = metadata?.[TITLE_CHECK_KEY]\n if (!raw || typeof raw !== 'object')\n return undefined\n const r = raw as Partial<TitleCheckRecord>\n return typeof r.at === 'number' && typeof r.outcome === 'string' ? (r as TitleCheckRecord) : undefined\n}\n\n/** How long a failed attempt holds off the next one: a missing key or a rejected model fails every run until fixed. */\nexport const TITLE_FAILURE_COOLDOWN_MS = 5 * 60_000\n\n/**\n * Whether the last attempt failed less than {@link TITLE_FAILURE_COOLDOWN_MS} ago.\n *\n * @param metadata - Session metadata holding the last check record.\n * @param now - Current time in ms since epoch.\n */\nexport function inTitleFailureCooldown(metadata: Readonly<Record<string, unknown>> | undefined, now: number = Date.now()): boolean {\n const check = readTitleCheck(metadata)\n return !!check && (check.outcome === 'decision-failed' || check.outcome === 'generation-failed')\n && now - check.at < TITLE_FAILURE_COOLDOWN_MS\n}\n\nfunction ago(ms: number): string {\n const s = Math.max(0, Math.round(ms / 1000))\n if (s < 60)\n return 'just now'\n const m = Math.round(s / 60)\n if (m < 60)\n return `${m}m ago`\n const h = Math.round(m / 60)\n return h < 48 ? `${h}h ago` : `${Math.round(h / 24)}d ago`\n}\n\n/**\n * Session details' title facts, one badge each: where the title came from and\n * what the last auto-title attempt did, e.g. `auto` `checked 5m ago, still\n * fits` or `auto` `check failed 2m ago (unavailable: needs a platform key)`.\n *\n * @param metadata - Session metadata that holds the title, its source and the last check record.\n * @param now - Current time in ms since epoch, used for relative times.\n */\nexport function describeTitleStatus(metadata: Readonly<Record<string, unknown>> | undefined, now: number = Date.now()): string[] {\n const source = metadata?.[TITLE_SOURCE_KEY]\n const hasTitle = currentTitle(metadata).length > 0\n const origin = !hasTitle ? 'untitled' : source === 'auto' ? 'auto' : source === 'user' ? 'typed by you' : 'set before auto-titling'\n const check = readTitleCheck(metadata)\n if (!check)\n return [origin]\n const when = ago(now - check.at)\n const detail = check.reason || check.message ? ` (${[check.reason, check.message].filter(Boolean).join(': ')})` : ''\n const what = {\n 'generated': `generated ${when}`,\n 'renamed': `renamed ${when}, topic changed`,\n 'kept': `checked ${when}, still fits`,\n 'decision-failed': `check failed ${when}${detail}`,\n 'generation-failed': `generation failed ${when}${detail}`,\n }[check.outcome] ?? check.outcome\n return [origin, what]\n}\n\nfunction currentTitle(metadata: Readonly<Record<string, unknown>> | undefined): string {\n return typeof metadata?.title === 'string' ? metadata.title.trim() : ''\n}\n\n/**\n * User turns carrying text — the prompts the user actually typed.\n *\n * @param turns - The session's turns.\n */\nexport function userPromptCount(turns: readonly SessionTurn[]): number {\n let n = 0\n for (const turn of turns) {\n if (turn.role === 'user' && !turn.injected && turn.content.some(b => b.type === 'text' && !b.systemNote && b.text.trim().length > 0))\n n++\n }\n return n\n}\n\n/** Text of the session's first user prompt. */\nfunction firstPromptText(turns: readonly SessionTurn[]): string {\n for (const turn of turns) {\n if (turn.role !== 'user')\n continue\n const text = turn.content.map(b => (b.type === 'text' && !b.systemNote ? b.text : '')).join(' ')\n if (text.trim())\n return text\n }\n return ''\n}\n\n/** Older desktop clients' clip: the first 40 characters, line breaks kept. */\nconst LEGACY_CLIP_MAX = 40\n\n/**\n * A title the client stamped at creation rather than one anyone chose: the\n * first prompt clipped exactly as the desktop app does (`titleFromText`, or\n * older clients' raw 40-character slice), or its \"New chat\" fallback. Such\n * titles carry no `titleSource`; auto-titling replaces them like an\n * untitled session. Only the exact clips count: a typed title that merely\n * starts the prompt (\"fix\", \"refactor auth\") is the user's.\n */\nexport function isPlaceholderTitle(title: string, turns: readonly SessionTurn[]): boolean {\n if (title === 'New chat')\n return true\n const prompt = firstPromptText(turns).trim()\n if (!prompt)\n return false\n // Older clients stored the clip with its line breaks: compare on one line.\n const line = oneLineTitle(title)\n const clip = titleFromText(prompt)\n return line === clip\n || (clip?.endsWith('…') === true && line === clip.slice(0, -1))\n || line === oneLineTitle(prompt.slice(0, LEGACY_CLIP_MAX))\n}\n\n/** What auto-titling should do next: leave the title, generate one, or check whether it is stale. */\nexport type AutoTitleAction = 'skip' | 'generate' | 'check'\n\n/**\n * What auto-titling should do for this session now:\n * - `generate`: no title yet and the first exchange happened.\n * - `check`: an auto title, with enough new prompts since the last check.\n * - `skip`: a user title, too few turns, or checked recently.\n *\n * @param turns - The session's turns.\n * @param metadata - Session metadata that holds the title, its source and the last check count.\n */\nexport function autoTitleAction(turns: readonly SessionTurn[], metadata: Readonly<Record<string, unknown>> | undefined): AutoTitleAction {\n const title = currentTitle(metadata)\n const source = metadata?.[TITLE_SOURCE_KEY]\n if (!title || (source === undefined && isPlaceholderTitle(title, turns)))\n return turns.length >= 2 ? 'generate' : 'skip'\n if (source !== 'auto')\n return 'skip'\n const checkedRaw = metadata?.[TITLE_CHECKED_KEY]\n const checked = typeof checkedRaw === 'number' ? checkedRaw : 0\n return userPromptCount(turns) - checked >= MIN_NEW_PROMPTS ? 'check' : 'skip'\n}\n\nconst STALE_INSTRUCTIONS = [\n 'The transcript is a coding session between a user and an agent; \"title\" is its current label.',\n 'Is the title no longer an accurate label for what the session as a whole is about?',\n 'Answer true only when the main topic has clearly moved on. A side question, a small follow-up, or extra detail on the same topic does not make the title stale.',\n].join(' ')\n\nexport type TitleStaleness = { stale: boolean } | { failure: { reason: string, message: string } }\n\n/**\n * Ask the decision model whether `title` is stale. A failed decision comes\n * back as `failure` (no answer is not a \"keep\": the caller retries later).\n *\n * @param decide - Decision function that asks the decision model.\n * @param title - The current title.\n * @param turns - The session's turns.\n */\nexport async function isTitleStale(decide: AgentDecide, title: string, turns: readonly SessionTurn[]): Promise<TitleStaleness> {\n const opening = turns.find(t => t.role === 'user' && renderTurnsForPrompt([t]).length > 0)\n const recent = renderTurnsForPrompt(turns.slice(-8))\n const r = await decide({\n input: {\n title,\n opening: opening ? renderTurnsForPrompt([opening]) : '',\n recent,\n },\n questions: { stale: { type: 'predicate', instructions: STALE_INSTRUCTIONS } },\n })\n if (!r.ok)\n return { failure: { reason: r.reason, message: r.message } }\n if (r.answers.stale.type !== 'predicate')\n return { failure: { reason: 'refusal', message: 'The decision model declined to answer' } }\n return { stale: r.answers.stale.probability > STALE_THRESHOLD }\n}\n","/**\n * Per-session execution cache — the session, not the agent, owns where its\n * tools run.\n *\n * Hosts rebuild a session's agent for many reasons that have nothing to do\n * with execution: a model or profile swap, a settings change, an MCP login,\n * an extension reload. An agent that spawned its own handle destroys it on\n * teardown, and for the process context that SIGTERMs every background task\n * the session started (`npm run dev` dies because the user switched models);\n * for a container or remote sandbox it would delete the machine. So hosts\n * build agents on a HOST-OWNED handle from this cache instead\n * (`AgentOptions.handle` — the agent never spawns or destroys it) and the\n * handle outlives every rebuild:\n *\n * 1. `acquire(sessionId, key, create)` → the session's context + a lazy\n * handle resolver. Reused while `key` (the context's configuration:\n * cwd, isolation policy, …) is unchanged; a changed key replaces the\n * context — the old one is destroyed, taking its tasks with it, because\n * they run under the old configuration (e.g. without a sandbox the user\n * just enabled).\n * 2. `adopt(sessionId, agent.hooks)` once the new agent's listeners are\n * wired: running tasks' exit/stall callbacks move to the new agent's\n * hook bus, and each is announced via `background:adopt` so the host's\n * running-work view re-registers it. Tasks that exited while no agent\n * owned them still reach the model through the agent's run-boundary\n * `listBackground` reconcile.\n * 3. `release*` when the SESSION is done with its execution: deleted,\n * fully disposed, idle-evicted without running tasks, host shutdown.\n *\n * Subagents are unaffected: the spawn tool hands children the real context,\n * so a non-shared child still spawns (and destroys) its own handle.\n */\nimport type { Agent } from '../agent'\nimport type { ExecutionContext, ExecutionHandle } from '../contexts/types'\nimport { createDebugLog } from '../debug-log'\n\nconst debugLog = createDebugLog('execution-cache')\n\n/**\n * A session's execution context and the lazy handle resolver bound to it, as returned by {@link SessionExecutionCache}.\n */\nexport interface SessionExecution {\n /** The session's execution context — pass as `AgentOptions.execution`. */\n context: ExecutionContext\n /** Host-owned handle, spawned on first use — pass as `AgentOptions.handle`. */\n handle: () => Promise<ExecutionHandle>\n}\n\n/**\n * Registry of host-owned execution contexts and handles, one per session. Created by {@link createSessionExecutionCache}.\n */\nexport interface SessionExecutionCache {\n /**\n * The session's execution, created with `create()` on first use and\n * reused while `key` matches. A different `key` replaces it (the previous\n * context is released in the background).\n */\n acquire: (sessionId: string, key: string, create: () => ExecutionContext) => SessionExecution\n /**\n * The session's current execution, whatever its key — for side work\n * (environment actions) that must run where the agent runs without\n * re-deriving the key (a mismatch would replace the live context).\n */\n peek: (sessionId: string) => SessionExecution | undefined\n /**\n * Hand the session's still-running background tasks to a newly built\n * agent: rebind their exit/stall callbacks to `hooks` and fire\n * `background:adopt` per task. Returns how many were adopted.\n */\n adopt: (sessionId: string, hooks: Agent['hooks']) => Promise<number>\n /** True while any background task on the session's handle is running. */\n hasRunningTasks: (sessionId: string) => Promise<boolean>\n /**\n * Destroy the session's handle (killing its tasks) and forget it. Resolves\n * once the destroy has finished — including one already started by an\n * earlier release or a key replacement — so a caller can rely on the\n * session's processes being gone (e.g. before removing its worktree).\n */\n release: (sessionId: string) => Promise<void>\n /**\n * {@link release} unless tasks are still running; resolves whether it\n * released. `stillIdle` is re-checked after the (async) task listing,\n * right before destroying — hosts pass \"no agent was rebuilt for this\n * session meanwhile\", so an eviction can't pull the handle out from\n * under a build that just acquired it.\n */\n releaseIfIdle: (sessionId: string, stillIdle?: () => boolean) => Promise<boolean>\n /** Release every session — host shutdown. */\n releaseAll: () => Promise<void>\n /** Sessions currently holding an execution. */\n sessions: () => string[]\n}\n\ninterface Entry {\n key: string\n context: ExecutionContext\n handle: ExecutionHandle | null\n spawning: Promise<ExecutionHandle> | null\n released: boolean\n}\n\n/**\n * Creates an empty {@link SessionExecutionCache}.\n * Handles are spawned lazily on first use and destroyed on release, key replacement, or host shutdown.\n *\n * @returns A cache keyed by session id.\n */\nexport function createSessionExecutionCache(): SessionExecutionCache {\n const entries = new Map<string, Entry>()\n /** Destroys in flight per session, so a later `release` can await them. */\n const destroying = new Map<string, Promise<void>>()\n\n /** Forget the session's entry and destroy it, tracking the in-flight destroy. */\n function detach(sessionId: string, entry: Entry): Promise<void> {\n if (entries.get(sessionId) === entry)\n entries.delete(sessionId)\n // Chain onto destroys already in flight (e.g. from a key replacement) so\n // awaiting this one also waits for every earlier destroy of the session.\n const pending = Promise.all([destroying.get(sessionId), destroyEntry(entry)]).then(() => {})\n destroying.set(sessionId, pending)\n void pending.finally(() => {\n if (destroying.get(sessionId) === pending)\n destroying.delete(sessionId)\n })\n return pending\n }\n\n function resolveHandle(entry: Entry): Promise<ExecutionHandle> {\n // A stale agent built on a replaced/released entry must not mint a\n // handle nobody will ever destroy.\n if (entry.released)\n return Promise.reject(new Error('session execution was released'))\n if (entry.handle)\n return Promise.resolve(entry.handle)\n // A release racing this spawn is handled by `destroyEntry`, which\n // awaits the in-flight spawn and destroys what it yields.\n entry.spawning ??= entry.context.spawn().then(\n (handle) => {\n entry.handle = handle\n entry.spawning = null\n return handle\n },\n (err: unknown) => {\n entry.spawning = null\n throw err\n },\n )\n return entry.spawning\n }\n\n async function destroyEntry(entry: Entry): Promise<void> {\n if (entry.released)\n return\n entry.released = true\n const handle = entry.handle ?? await entry.spawning?.catch(() => null) ?? null\n if (!handle)\n return\n entry.handle = null\n try {\n await entry.context.destroy(handle)\n }\n catch (err) {\n debugLog('destroy failed', err)\n }\n }\n\n async function runningTasks(entry: Entry | undefined) {\n if (!entry?.handle || !entry.context.listBackground)\n return []\n try {\n return (await entry.context.listBackground(entry.handle)).filter(task => task.status === 'running')\n }\n catch (err) {\n debugLog('listBackground failed', err)\n return []\n }\n }\n\n const cache: SessionExecutionCache = {\n acquire(sessionId, key, create) {\n let entry = entries.get(sessionId)\n if (entry && entry.key !== key) {\n void detach(sessionId, entry)\n entry = undefined\n }\n if (!entry) {\n entry = { key, context: create(), handle: null, spawning: null, released: false }\n entries.set(sessionId, entry)\n }\n const bound = entry\n return { context: bound.context, handle: () => resolveHandle(bound) }\n },\n\n peek(sessionId) {\n const entry = entries.get(sessionId)\n return entry ? { context: entry.context, handle: () => resolveHandle(entry) } : undefined\n },\n\n async adopt(sessionId, hooks) {\n const entry = entries.get(sessionId)\n const handle = entry?.handle\n if (!entry || !handle || !entry.context.reassignBackgroundTasks)\n return 0\n let adopted\n try {\n adopted = await entry.context.reassignBackgroundTasks(\n handle,\n handle,\n (info) => {\n Promise.resolve(hooks.callHook('background:exit', info)).catch(err => debugLog('background:exit hook rejected', err))\n },\n (info) => {\n Promise.resolve(hooks.callHook('background:stall', info)).catch(err => debugLog('background:stall hook rejected', err))\n },\n )\n }\n catch (err) {\n debugLog('adopt failed', err)\n return 0\n }\n for (const task of adopted) {\n await Promise.resolve(hooks.callHook('background:adopt', {\n taskId: task.taskId,\n pid: task.pid,\n command: task.command,\n cwd: task.cwd,\n outputPath: task.outputPath,\n startedAt: task.startedAt,\n })).catch(err => debugLog('background:adopt hook rejected', err))\n }\n return adopted.length\n },\n\n async hasRunningTasks(sessionId) {\n return (await runningTasks(entries.get(sessionId))).length > 0\n },\n\n async release(sessionId) {\n const entry = entries.get(sessionId)\n if (!entry)\n return destroying.get(sessionId)\n await detach(sessionId, entry)\n },\n\n async releaseIfIdle(sessionId, stillIdle) {\n const entry = entries.get(sessionId)\n if (!entry)\n return true\n if ((await runningTasks(entry)).length > 0)\n return false\n // Re-check after the await: the entry may have been replaced, or the\n // host may have started using it again.\n if (entries.get(sessionId) !== entry || (stillIdle && !stillIdle()))\n return false\n await cache.release(sessionId)\n return true\n },\n\n sessions: () => [...entries.keys()],\n\n async releaseAll() {\n const all = [...entries]\n await Promise.all([...all.map(([sessionId, entry]) => detach(sessionId, entry)), ...destroying.values()])\n },\n }\n return cache\n}\n","/**\n * A model's Test in the settings: one tiny prompt with this host's\n * credentials, to check the model answers. The chat models' counterpart of\n * `testDecisionModel`, with the same result shape.\n */\nimport type { Provider } from '../providers'\nimport type { ProviderDescriptor } from './providers'\nimport { parseModelSetting } from './model-setting'\n\n/** Outcome of {@link testModel}. */\nexport interface ModelTestResult {\n /** True when the model answered. */\n ok: boolean\n /** Provider the probe ran on. */\n provider?: string\n /** Model the probe used. */\n model?: string\n /** Time the probe took, in ms. */\n durationMs: number\n /** Why it failed: `unavailable` (no such provider, no credentials), `timeout` or `error`. */\n reason?: string\n /** Failure detail. */\n message?: string\n}\n\nconst PROBE = 'Reply with the single word OK.'\nconst DEFAULT_TIMEOUT_MS = 30_000\n\n/**\n * Send one tiny prompt to check a model answers with this host's\n * credentials. `model` (`\"providerKey[@account]/modelId\"`) is tested\n * exactly, on its account, with no fallback, so a broken pick is reported as\n * broken. Never throws.\n *\n * @param input - What to test, and with what.\n * @param input.providers - This host's provider descriptors.\n * @param input.buildProvider - Builds a provider on an account; throws without credentials.\n * @param input.model - The model to test, as `\"providerKey[@account]/modelId\"`.\n * @param input.timeoutMs - How long to wait for the answer. Default: 30 000.\n */\nexport async function testModel(input: {\n providers: Readonly<Record<string, ProviderDescriptor>>\n buildProvider: (providerKey: string, account?: string) => Provider\n model: string\n timeoutMs?: number\n}): Promise<ModelTestResult> {\n const started = Date.now()\n const pick = parseModelSetting(input.model)\n const fail = (reason: string, message: string): ModelTestResult => ({\n ok: false,\n ...(pick ? { provider: pick.providerKey, model: pick.modelId } : {}),\n durationMs: Date.now() - started,\n reason,\n message,\n })\n if (!pick)\n return fail('unavailable', `Not a model: \"${input.model}\"`)\n if (!input.providers[pick.providerKey])\n return fail('unavailable', `Provider \"${pick.providerKey}\" is not configured`)\n\n let provider: Provider\n try {\n provider = input.buildProvider(pick.providerKey, pick.account)\n }\n catch (err) {\n return fail('unavailable', err instanceof Error ? err.message : String(err))\n }\n\n const timeoutMs = input.timeoutMs ?? DEFAULT_TIMEOUT_MS\n const controller = new AbortController()\n const timer = setTimeout(() => controller.abort(), timeoutMs)\n try {\n await provider.stream(\n { model: pick.modelId, system: PROBE, tools: [], messages: [provider.userMessage(PROBE)], maxTokens: 256, signal: controller.signal },\n { onText: () => {} },\n )\n return { ok: true, provider: pick.providerKey, model: pick.modelId, durationMs: Date.now() - started }\n }\n catch (err) {\n return controller.signal.aborted\n ? fail('timeout', `No answer within ${Math.round(timeoutMs / 1000)}s`)\n : fail('error', err instanceof Error ? err.message : String(err))\n }\n finally {\n clearTimeout(timer)\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"model-test-CAtJ76kM.js","names":[],"sources":["../src/chat/host/edit-outcome-ledger.ts","../src/chat/run-end-notice.ts","../src/chat/auto-compact.ts","../src/chat/interactions-protocol.ts","../src/chat/host/signals.ts","../src/chat/host/launch-overrides.ts","../src/chat/auto-title.ts","../src/chat/execution-cache.ts","../src/chat/model-test.ts"],"sourcesContent":["/**\n * Per-callId stash for the per-edit approval pipeline. Written by\n * `tool:gate` on a `partial` decision and consumed by `tool:transform`\n * to append the canonical `<edit-outcomes>` annotation onto the tool's\n * natural output.\n *\n * Carries the original input + payload so `tool:before` can paint the\n * `tool` stream event with ALL hunks (including denied ones) badged\n * via outcomes — `ctx.input` has been reduced to the approved subset\n * by then, so `extractEditPayload` on the live ctx would miss the\n * denied hunks.\n *\n * Lifecycle: `tool:gate` writes; `tool:transform` reads + clears;\n * `tool:error` clears (no transform on the throw path); `agent:done`\n * clears the whole map so entries whose transform never fired\n * (validation reject / gate throw / retry loops) stay bounded by run\n * lifetime rather than session lifetime.\n */\nimport type { EditOutcome, EditPayload } from '../types'\n\n/** An annotation waiting to be appended to an edit tool's output. */\nexport interface PendingAnnotation {\n /** Per-hunk decisions from the approval. */\n outcomes: readonly EditOutcome[]\n /** Tool input before it was reduced to the approved hunks. */\n originalInput: Record<string, unknown>\n /** Edit payload before it was reduced to the approved hunks. */\n originalPayload: EditPayload\n}\n\n/** Holds the per-edit approval outcomes of in-flight tool calls, keyed by call id. */\nexport class EditOutcomeLedger {\n private _entries = new Map<string, PendingAnnotation>()\n\n /**\n * Stores the annotation for a tool call, replacing an earlier one.\n *\n * @param callId - Tool call id.\n * @param payload - Annotation to hold.\n */\n stash(callId: string, payload: PendingAnnotation): void {\n this._entries.set(callId, payload)\n }\n\n /**\n * Read + remove the entry for `callId`. `null` when no stash exists.\n *\n * @param callId - Tool call id.\n */\n drain(callId: string): PendingAnnotation | null {\n const entry = this._entries.get(callId)\n if (!entry)\n return null\n this._entries.delete(callId)\n return entry\n }\n\n /**\n * Non-removing peek — used by `tool:before` to paint events without consuming the stash.\n *\n * @param callId - Tool call id.\n */\n peek(callId: string): PendingAnnotation | null {\n return this._entries.get(callId) ?? null\n }\n\n /**\n * Drop entry without reading — used by `tool:error` (no transform on throw path).\n *\n * @param callId - Tool call id.\n */\n discard(callId: string): void {\n this._entries.delete(callId)\n }\n\n /** Clear every entry — called on `agent:done` and session disposal. */\n clear(): void {\n this._entries.clear()\n }\n}\n","import type { TurnFinishReason } from '../types'\n/**\n * The run-end \"why did this stop?\" notice, in one place.\n *\n * When a run ends because the model bumped a ceiling rather than finishing,\n * the transcript otherwise shows an ordinary — often empty — assistant turn.\n * That is the classic \"the session just stalled\" report: nothing errored, so\n * nothing was said.\n *\n * One implementation serves every surface: the shared chat host (which serves\n * the GUI and daemon-attached windows) calls it from its `agent:done` hook in\n * `host/notices`, so the same run explains itself with the same text\n * wherever you read it.\n *\n * Returns a STRUCTURED notice — `{ severity, label }` plus text — rather than\n * a bare string, because the transcript renders notices with a severity glyph\n * and a label chip (`StreamEvent.notice`). Returning text alone would force\n * each surface to invent the label, which is exactly the divergence this\n * module exists to prevent. The text therefore carries no leading glyph (the\n * renderer supplies it) and reads as a full sentence, since not every surface\n * draws the label chip.\n *\n * The `length` copy names the two settings that change the outcome. Both carry\n * these exact labels on every surface (`resumeOnMaxTokens` → \"Auto-resume on\n * output limit\", `maxOutputTokens` → \"Max output tokens\"), so the instruction\n * is followable wherever the notice appears; `test/run-end-notice.test.ts`\n * pins the labels so a rename cannot turn it into a lie.\n */\nimport type { StreamEvent } from './types'\n\nexport interface RunEndNotice {\n notice: NonNullable<StreamEvent['notice']>\n text: string\n}\n\n/**\n * Structured stop notice for an abnormal `finishReason`, or `undefined` for a\n * normal end.\n *\n * Normal completions (`stop` / `tool-calls`) and user aborts return\n * `undefined` — and so does a SUCCESSFUL auto-resume, because recovery ends\n * the run on `stop`. That is what keeps the notice quiet exactly when the\n * ceiling was handled rather than hit. `error` runs already surface their own\n * error event, so a second notice would double-report.\n *\n * @param reason - Finish reason of the run's last turn.\n */\nexport function runEndNotice(reason: TurnFinishReason | undefined): RunEndNotice | undefined {\n switch (reason) {\n case 'length':\n return {\n notice: { severity: 'warn', label: 'output limit' },\n text: 'Stopped at the model output limit (max_tokens). Turn on \"Auto-resume on output limit\" or raise \"Max output tokens\" in Settings to continue past it.',\n }\n case 'pause':\n return {\n notice: { severity: 'warn', label: 'pause cap' },\n text: 'Stopped after repeated empty pause turns from the provider (maxConsecutivePauseTurns cap is armed).',\n }\n case 'content-filter':\n /*\n * Refusal stop (e.g. Anthropic `stop_reason: refusal`, often after\n * PDF-heavy tool results). The loop already retried with synthetic\n * continues before giving up, so reaching here means the refusal\n * persisted — say so rather than letting the run end as a silent,\n * empty \"completion\".\n */\n return {\n notice: { severity: 'warn', label: 'refusal' },\n text: 'The provider refused to continue (safety filter / refusal stop) despite automatic retries. Rephrase the request, or start a fresh session if it keeps happening.',\n }\n default:\n return undefined\n }\n}\n","/**\n * Chat-layer auto-compaction helpers.\n *\n * The decision policy lives in `src/compact/policy.ts` (core, no chat\n * dependency) so the agent loop's loop-native auto-compaction shares the exact\n * same predicate as the chat hosts. This module re-exports it and adds the\n * host-side glue that maps chat settings + a model window into the SDK's\n * `behavior.autoCompact` value.\n */\n\nimport type { Provider } from '../providers'\nimport type { AutoCompact } from '../types'\n\nexport {\n AUTO_COMPACT_MIN_GROWTH_FRACTION,\n type AutoCompactDecision,\n type AutoCompactInput,\n shouldAutoCompact,\n} from '../compact/policy'\n\n/**\n * Build the per-run `behavior.autoCompact` a chat host passes to `agent.run`.\n *\n * Loop-native auto-compaction is the only mechanism; the TUI + GUI just\n * translate the user's enable/threshold settings plus the picked model's raw\n * context window (the SDK core has no model catalog) into the config the loop\n * consumes. Returns `false` when disabled. `rawContextWindow: null` (unknown\n * model) still enables it but leaves the trigger inert — the loop skips with\n * `unknown-window` rather than guessing.\n *\n * @param opts - Whether auto-compaction is on, the trigger threshold, the main model's raw context window and an optional summarizer override.\n */\nexport function chatAutoCompactBehavior(opts: {\n enabled: boolean\n threshold: number\n rawContextWindow: number | null\n /**\n * Summarizer override — run the summary call on a different\n * provider/model than the main one (the user's \"compaction model\"\n * setting). Omitted = summarize on the run's main provider/model.\n * `rawContextWindow` above stays the MAIN model's window: the trigger\n * measures the live conversation, not the summarizer.\n */\n summarizer?: { provider: Provider, model: string }\n}): AutoCompact {\n if (!opts.enabled)\n return false\n const contextWindow = typeof opts.rawContextWindow === 'number'\n && Number.isFinite(opts.rawContextWindow)\n && opts.rawContextWindow > 0\n ? opts.rawContextWindow\n : null\n return {\n threshold: opts.threshold,\n ...(contextWindow ? { contextWindow } : {}),\n ...(opts.summarizer ? { provider: opts.summarizer.provider, model: opts.summarizer.model } : {}),\n // Chat hosts are long-lived processes with a persist dir always\n // configured, so trade inline transcript fidelity of compacted-away\n // turns (recoverable via each stub's persisted path) for bounded\n // memory. Headless/SDK runs keep the lossless default.\n releaseReplacedTurns: true,\n }\n}\n\n/**\n * The per-run window knobs a chat host spreads into `agent.run`'s `behavior`:\n * the auto-compaction config from {@link chatAutoCompactBehavior} PLUS the\n * picked model's raw window as `behavior.contextWindow`, so window-relative\n * wire budgets (`compactThresholdWindowFraction`) see the window even when the\n * user has auto-compaction switched off. Both hosts go through this so they\n * cannot drift: the TUI and the GUI/daemon engine hand the loop the same\n * shape for the same settings.\n *\n * @param opts - Same options as {@link chatAutoCompactBehavior}.\n */\nexport function chatWindowBehavior(opts: Parameters<typeof chatAutoCompactBehavior>[0]): {\n autoCompact: AutoCompact\n contextWindow?: number\n} {\n const autoCompact = chatAutoCompactBehavior(opts)\n const contextWindow = typeof opts.rawContextWindow === 'number'\n && Number.isFinite(opts.rawContextWindow)\n && opts.rawContextWindow > 0\n ? opts.rawContextWindow\n : undefined\n return { autoCompact, ...(contextWindow !== undefined ? { contextWindow } : {}) }\n}\n","/**\n * Interactions — plan approvals and Q&A clarifications between the agent\n * and the user.\n *\n * Renderer-agnostic by design: this module owns the tool factories, the\n * pending-state derivation from `session.turns`, the response serializer,\n * and the queue types. The React queue context lives in `interactions.tsx`,\n * which re-exports all of this; hosts and daemons import this module directly.\n * The TUI / GUI consume the queue and provide the actual picker UI; the same\n * data plumbing works for both.\n *\n * Persistence model — the call IS the persisted state:\n *\n * - The agent emits `present_plan` / `ask_user` as a regular tool call.\n * The `tool_call` block lands in `session.turns` like any other tool.\n * When the user responds, the corresponding `tool_result` block follows\n * in the next user turn. Standard tool-call/tool-result contract — no\n * parallel store, no extra schema.\n *\n * - \"Pending\" = the trailing assistant turn has a `tool_call` to one of\n * the interaction tools with no matching `tool_result` in a later\n * user turn. Derived at load time via\n * {@link pendingInteractionsFromTurns}; no parallel bookkeeping.\n *\n * - Resumed sessions land in the same UI as live ones. The host only\n * swaps the resolver:\n * - **Live**: the tool's `execute()` is awaiting a Promise; the\n * resolver resolves that Promise. The agent loop persists the\n * tool_result naturally on its next iteration.\n * - **Resumed**: no Promise exists (the prior run was destroyed).\n * The resolver appends a synthetic user turn with the tool_result\n * block and triggers a follow-up `agent.run()` so the model\n * continues from where it left off.\n *\n * No OpenTUI imports — a GUI consumer can ship its own renderer and reuse\n * this module verbatim.\n */\nimport type { StepKey } from '../durable/keys'\nimport type { DurableAwakeable, DurableMode } from '../durable/types'\nimport type { DurableToolDef, ToolContext, ToolDef } from '../tools/types'\nimport type { SessionContentBlock, SessionTurn } from '../types'\nimport { stepKey } from '../durable/keys'\n\n// ---------------------------------------------------------------------------\n// Tool identities — canonical names persisted in `session.turns`. Stable\n// across the lifetime of the project; renaming would break resume.\n// ---------------------------------------------------------------------------\n\n/** Canonical name of the tool that presents a plan for approval. */\nexport const PRESENT_PLAN_TOOL = 'present_plan'\n/** Canonical name of the tool that asks the user one or more questions. */\nexport const ASK_USER_TOOL = 'ask_user'\n\n/**\n * True when `name` is one of the interaction tool canonical names.\n *\n * @param name - Tool name to test.\n */\nexport function isInteractionTool(name: string): boolean {\n return name === PRESENT_PLAN_TOOL || name === ASK_USER_TOOL\n}\n\n// ---------------------------------------------------------------------------\n// Payload shapes — parsed views of each tool's `input` blob.\n// ---------------------------------------------------------------------------\n\n/** One step of a {@link PlanPayload}. */\nexport interface PlanStep {\n /** Step id. */\n id: string\n /** Short step title. */\n title: string\n /** Longer step detail. */\n description?: string\n}\n\n/** Parsed input of a `present_plan` call. */\nexport interface PlanPayload {\n /** Short headline summary — used as the modal title. */\n title: string\n /** Full plan body. Markdown. */\n plan: string\n /** Optional structured step list — surfaced as a checklist preview. */\n steps?: readonly PlanStep[]\n}\n\n/** One option of a `select` question. */\nexport interface QuestionChoice {\n /** Choice id, returned as the answer when picked. */\n id: string\n /** Text shown for the choice. */\n label: string\n /** One-line detail shown under the label. */\n description?: string\n /**\n * Open a `multi` select with this choice already ticked. Ignored by\n * single selects (which position the cursor from the carried answer\n * instead) and overridden by a carried answer on re-entry, so resuming a\n * question keeps what the user had rather than snapping back to the\n * defaults.\n *\n * Exists because the alternative for an opt-out list is N keystrokes to\n * accept N items: a caller offering \"which of these 20 findings do you\n * want to send\" wants them pre-ticked and the user deselecting a few.\n */\n selected?: boolean\n}\n\n/**\n * Kind of an `ask_user` question, the discriminator of {@link Question}:\n *\n * - `text` — single-line free-text answer (rendered via `<input>`).\n * - `textarea` — multi-line free-text answer (rendered via `<textarea>`).\n * - `select` — single pick from a fixed set of `choices`.\n * - `confirm` — yes/no boolean (rendered as a 2-option select).\n *\n * The model batches related clarifications into one tool call instead of\n * pinging the user N times with N round-trips.\n */\nexport type QuestionType = 'text' | 'textarea' | 'select' | 'confirm'\n\ninterface BaseQuestion {\n /** Stable id — keys the response back to the model. */\n id: string\n /** Question text shown above the input (Markdown allowed). */\n prompt: string\n /** Optional helper text under the prompt. */\n description?: string\n /**\n * Whether the answer is required (non-empty). Default: `true` for\n * `select` / `confirm` (no sensible empty), `false` for free-text\n * (skipping = empty string). Free-text questions still accept an\n * answer; the flag only gates submission.\n */\n required?: boolean\n}\n\n/** A free-text question. */\nexport interface TextQuestion extends BaseQuestion {\n /** `text` for a single line, `textarea` for several. */\n type: 'text' | 'textarea'\n /** Placeholder shown when the input is empty. */\n placeholder?: string\n /** Pre-fill the input with this value (still editable). */\n default?: string\n}\n\n/** A question answered by picking from fixed choices. */\nexport interface SelectQuestion extends BaseQuestion {\n /** Question type discriminator. */\n type: 'select'\n /** Options to pick from. */\n choices: readonly QuestionChoice[]\n /**\n * Allow picking MORE than one choice. The answer becomes a\n * `readonly string[]` of choice ids (space toggles, ↵ commits in the\n * TUI). `required` (default `true`) demands at least one selection.\n */\n multi?: boolean\n}\n\n/** A yes/no question. */\nexport interface ConfirmQuestion extends BaseQuestion {\n /** Question type discriminator. */\n type: 'confirm'\n /** Label for the affirmative option. Default: `'yes'`. */\n affirmLabel?: string\n /** Label for the negative option. Default: `'no'`. */\n denyLabel?: string\n}\n\n/** One question in an `ask_user` form, discriminated by `type`. */\nexport type Question = TextQuestion | SelectQuestion | ConfirmQuestion\n\n/** Parsed input of an `ask_user` call. */\nexport interface QuestionPayload {\n /** Optional intro / context shown above every question (Markdown). */\n intro?: string\n /** One or more questions for the user to answer in one form. */\n questions: readonly Question[]\n}\n\n/** Per-question answer value — `string` for text/textarea/select, `boolean` for confirm, `string[]` for multi-select. */\nexport type AnswerValue = string | boolean | readonly string[]\n\n/**\n * Who raised a request. `model` is a tool call awaiting a `tool_result`;\n * `host` is a slash command or extension prompt whose consumer is local\n * code. Automatic mode may only answer on the model's behalf, so this is\n * carried as data rather than inferred from the id — a missing value is a\n * compile error at the construction site instead of a silent mis-answer.\n */\nexport type InteractionOrigin = 'model' | 'host'\n\ninterface BaseRequest {\n /** Stable id — the `tool_call.id` from the model. */\n id: string\n /** Whether the model or the host raised this request. */\n origin: InteractionOrigin\n /** Tool canonical name. */\n tool: string\n /** Run that emitted the call, when known. */\n runId?: string\n /** Turn id the call lives in. */\n turnId?: string\n /** Unix ms when the request was raised. */\n createdAt: number\n}\n\n/** A pending request to approve a plan. */\nexport interface PlanRequest extends BaseRequest {\n /** Request kind discriminator. */\n kind: 'plan'\n tool: typeof PRESENT_PLAN_TOOL\n /** The plan to approve. */\n payload: PlanPayload\n}\n\n/** A pending request to answer questions. */\nexport interface QuestionRequest extends BaseRequest {\n /** Request kind discriminator. */\n kind: 'question'\n tool: typeof ASK_USER_TOOL\n /** The questions to answer. */\n payload: QuestionPayload\n}\n\n/** A request for user input raised by the model or the host. */\nexport type InteractionRequest = PlanRequest | QuestionRequest\n\n// ---------------------------------------------------------------------------\n// Response shapes — what the user gives back. Serialized as JSON below so\n// the model parses a stable, explicit envelope.\n// ---------------------------------------------------------------------------\n\n/**\n * Who answered a prompt when it was not the person at a window: an\n * orchestrator session, an extension, or a connected client (MCP, a script).\n */\nexport interface InteractionAnswerer {\n /** What answered. */\n kind: 'session' | 'extension' | 'client'\n /** The session id, the extension name, or the client's token id. */\n id?: string\n /** How to name it to a reader: the session's title, `extension <name>`, the token's name. */\n label: string\n}\n\n/** How the user answered a plan: approve it, reject it, or ask for a revision. */\nexport type PlanDecision = 'approve' | 'reject' | 'revise'\n\n/** The user's answer to a {@link PlanRequest}. */\nexport interface PlanResponse {\n /** Response kind discriminator. */\n kind: 'plan'\n /** The user's decision. */\n decision: PlanDecision\n /** Optional free-text feedback — shown to the model verbatim. */\n comment?: string\n /**\n * Resolved by automatic mode rather than by a person. Serialized so the\n * model can tell a real decision from a default it is free to override.\n */\n automatic?: boolean\n /** Answered by an orchestrator, an extension or a client rather than at a window. */\n answeredBy?: InteractionAnswerer\n}\n\n/** The user's answers to a {@link QuestionRequest}. */\nexport interface QuestionResponse {\n /** Response kind discriminator. */\n kind: 'question'\n /**\n * Map of `Question.id → AnswerValue` for every question the user\n * answered. Missing keys = question skipped (only possible when\n * `required: false`).\n */\n answers: Readonly<Record<string, AnswerValue>>\n /**\n * Free text the user attached to the whole form — every ask_user surface\n * offers it unconditionally, so the user can always refine or override\n * the questions instead of being boxed into the model's choices. Parallel\n * to {@link PlanResponse.comment}; absent when left empty.\n */\n instructions?: string\n /** See {@link PlanResponse.automatic}. */\n automatic?: boolean\n /** See {@link PlanResponse.answeredBy}. */\n answeredBy?: InteractionAnswerer\n}\n\n/** The user's answer to an {@link InteractionRequest}. */\nexport type InteractionResponse = PlanResponse | QuestionResponse\n\n/**\n * Instruction sent to the model when automatic mode answers an interaction on the user's behalf.\n */\nexport const AUTOMATIC_INTERACTION_INSTRUCTION\n = 'Automatic mode is enabled. Make the best choice from the repository and current task, then continue without asking for this input again.'\n\n/**\n * Id prefix for interactions the HOST synthesizes — slash commands and\n * extension `ctx.interaction.*` share the prompt slot with the tools but\n * never pass through {@link createInteractionTools}. Model-originated\n * requests carry the provider's `tool_call.id` instead. Naming only:\n * origin policy reads {@link BaseRequest.origin}, never the id.\n */\nexport const HOST_INTERACTION_ID_PREFIX = 'command_'\n\n/**\n * Whether a queued request came from a model tool call. Automatic mode\n * answers on the model's behalf, so it must leave host-originated prompts\n * alone: their consumer is local code awaiting a real decision, with no\n * model on the other end to act on an instruction sentence.\n *\n * @param request - Queued request to classify.\n */\nexport function isModelInteraction(request: InteractionRequest): boolean {\n return request.origin === 'model'\n}\n\n/**\n * Stand-in for a free-text answer nobody typed. Deliberately NOT the\n * instruction sentence: {@link serializeInteractionResponse} already leads\n * the envelope with it once, so repeating it per answer only restates the\n * same disclosure — four times over on a three-question form.\n */\nconst AUTOMATIC_FREE_TEXT_ANSWER = '(no answer, automatic mode)'\n\n/**\n * Automatic answer for one question, in the shape its `type` promises:\n * `confirm` declares a boolean and `select` declares one of its own choice\n * ids, so a free-text placeholder would be out of contract there.\n *\n * These are defaults, not decisions — nobody chose them. The response\n * carries `automatic: true` so the model reads them as such and overrides\n * whatever doesn't fit; see {@link serializeInteractionResponse}.\n */\nfunction automaticAnswerFor(question: Question): AnswerValue {\n switch (question.type) {\n // Proceed, consistent with auto-approving plans.\n case 'confirm':\n return true\n // Nothing marks a choice as recommended, so the first one is an\n // arbitrary placeholder the model is expected to reconsider.\n case 'select': {\n const picked = question.choices[0]?.id ?? ''\n return question.multi ? (picked ? [picked] : []) : picked\n }\n default:\n return AUTOMATIC_FREE_TEXT_ANSWER\n }\n}\n\n/**\n * Build the deterministic response used when the host skips interaction UI.\n *\n * @param request - Request to answer. A plan is approved, a `confirm` question gets `true`, a `select` question gets its first choice, and free text gets a placeholder.\n * @returns A response marked `automatic: true`.\n */\nexport function createAutomaticInteractionResponse(request: InteractionRequest): InteractionResponse {\n if (request.kind === 'plan') {\n // No `comment`: that field is the user's own free-text feedback, shown to\n // the model verbatim. Harness text belongs in the envelope's `note`, which\n // serialization adds once for every automatic response.\n return {\n kind: 'plan',\n decision: 'approve',\n automatic: true,\n }\n }\n const answers: Record<string, AnswerValue> = {}\n for (const question of request.payload.questions)\n answers[question.id] = automaticAnswerFor(question)\n return { kind: 'question', answers, automatic: true }\n}\n\n// ---------------------------------------------------------------------------\n// Serialization — what the model sees as the tool_result `output`.\n// ---------------------------------------------------------------------------\n\n/**\n * Format an {@link InteractionResponse} as the tool_result content. JSON\n * keeps the envelope parseable and explicit. Empty fields are stripped so\n * the model doesn't waste tokens reading absent values.\n *\n * @param response - The user's or automatic mode's response.\n * @returns A JSON string for the `tool_result` content.\n */\nexport function serializeInteractionResponse(response: InteractionResponse): string {\n // Leads the envelope so the model reads \"nobody answered this\" before the\n // values themselves. Absent on user-submitted responses, which carry no\n // extra tokens.\n const automatic: Record<string, unknown> = {\n ...(response.automatic ? { automatic: true, note: AUTOMATIC_INTERACTION_INSTRUCTION } : {}),\n // Who decided, when it was not the person: the model weighs an\n // orchestrator's answer differently from its user's.\n ...(response.answeredBy ? { answeredBy: response.answeredBy.label } : {}),\n }\n if (response.kind === 'plan') {\n const body: Record<string, unknown> = { ...automatic, decision: response.decision }\n if (response.comment && response.comment.trim().length > 0)\n body.comment = response.comment.trim()\n return JSON.stringify(body)\n }\n // Normalize answer values: trim strings, drop empty / undefined entries\n // so the model sees a tight payload of only the meaningful answers.\n const answers: Record<string, AnswerValue> = {}\n for (const [id, value] of Object.entries(response.answers)) {\n if (typeof value === 'string') {\n const trimmed = value.trim()\n if (trimmed.length > 0)\n answers[id] = trimmed\n }\n else if (typeof value === 'boolean') {\n answers[id] = value\n }\n else if (Array.isArray(value)) {\n // Multi-select — keep non-empty trimmed ids, drop empty selections.\n const picked = value.map(v => String(v).trim()).filter(v => v.length > 0)\n if (picked.length > 0)\n answers[id] = picked\n }\n }\n const body: Record<string, unknown> = { ...automatic, answers }\n const instructions = response.instructions?.trim()\n if (instructions)\n body.instructions = instructions\n return JSON.stringify(body)\n}\n\n/**\n * Why a run stopped on a rejected plan: the reason its abort carries. A caller\n * the abort cuts short before it hands on the plan's result (a codemode script\n * awaiting `present_plan`) reports this instead, so the model still reads the\n * decision and the comment.\n *\n * @param response - The plan's `reject` response.\n * @returns The abort reason.\n */\nexport function planRejectionReason(response: InteractionResponse): string {\n return `The user rejected the plan and stopped the run: ${serializeInteractionResponse(response)}`\n}\n\n// ---------------------------------------------------------------------------\n// Resumed-flow turn builder — batches every pending interaction's response\n// into ONE matched user turn.\n//\n// Why a dedicated builder: the agent loop's history-validation contract\n// requires every `tool_use` block in an assistant turn to be followed by\n// EXACTLY ONE matched `tool_result` block in the very next user turn.\n// When a session was killed mid-batch — say the model emitted both\n// `present_plan` and `ask_user` in parallel and the process died before\n// either Promise resolved — naively writing one `tool_result` per UI\n// submission would persist a user turn missing the sibling's match, and\n// the next `agent.run()` would reject the history on the API side.\n//\n// The wizard UI accumulates answers across submissions; this helper\n// converts the accumulated map back into a protocol-valid user turn\n// containing one `tool_result` per pending request, in pending order.\n// ---------------------------------------------------------------------------\n\n/**\n * Build a protocol-valid user `SessionTurn` that closes a batch of\n * resumed pending interactions.\n *\n * - `requests` MUST be the same list returned by\n * {@link pendingInteractionsFromTurns} for the session, in order.\n * - `responses` MUST contain an entry for every request id; throws\n * otherwise (caller bug — flush before all answers collected).\n *\n * Pure: reads no store. Tested in isolation so the batching invariant doesn't\n * regress without the resumed-flow integration noticing.\n *\n * @param requests - Pending requests, in the order {@link pendingInteractionsFromTurns} returned them.\n * @param responses - Responses keyed by request id. Must cover every request.\n * @param options - Turn id for the new turn, plus an optional run id and creation time.\n * @returns A user turn with one `tool_result` per request.\n * @throws If `requests` is empty or a request has no response.\n */\nexport function buildResumedToolResultsTurn(\n requests: readonly InteractionRequest[],\n responses: ReadonlyMap<string, InteractionResponse>,\n options: {\n /** Turn id minted by the session store. */\n turnId: string\n /**\n * Run id that emitted the pending tool calls — when known, attaching\n * it ties the resumed turn back to the originating `SessionRun` row\n * so consumers grouping turns by run keep clean ancestry across the\n * close/reopen boundary.\n */\n runId?: string\n /** `createdAt` timestamp. Defaults to `Date.now()`. */\n createdAt?: number\n },\n): SessionTurn {\n if (requests.length === 0)\n throw new Error('buildResumedToolResultsTurn: at least one request is required')\n\n const content: SessionContentBlock[] = requests.map((req) => {\n const response = responses.get(req.id)\n if (!response) {\n throw new Error(\n `buildResumedToolResultsTurn: missing response for request \"${req.id}\". `\n + `All ${requests.length} pending interactions must be resolved before flushing.`,\n )\n }\n return {\n type: 'tool_result',\n callId: req.id,\n output: serializeInteractionResponse(response),\n }\n })\n\n return {\n id: options.turnId,\n role: 'user',\n content,\n createdAt: options.createdAt ?? Date.now(),\n ...(options.runId ? { runId: options.runId } : {}),\n }\n}\n\n// ---------------------------------------------------------------------------\n// Pending-state derivation — pure, store-agnostic.\n//\n// The single source of truth for \"what is the session waiting on?\". Used by\n// the chat layer on session activation to decide whether to render the\n// resumed-interaction UI instead of the prompt textarea.\n// ---------------------------------------------------------------------------\n\n/**\n * Scan `turns` and return every interaction tool call without a matching\n * `tool_result`, ordered by turn appearance. Works equally for live mid-run\n * inspection and reload-from-disk.\n *\n * Malformed payloads (input that doesn't match the tool's schema) are\n * skipped silently — the call still lives in the persisted history, but\n * the renderer wouldn't know how to display it. The next `agent.run()` on\n * the session will see the orphan and synthesize an error tool_result via\n * the loop's normal recovery path.\n *\n * @param turns - Session turns, oldest first.\n */\nexport function pendingInteractionsFromTurns(turns: readonly SessionTurn[]): InteractionRequest[] {\n interface CallInfo {\n callId: string\n tool: string\n input: Record<string, unknown>\n runId?: string\n turnId: string\n createdAt: number\n }\n const calls: CallInfo[] = []\n const resultIds = new Set<string>()\n\n for (const turn of turns) {\n for (const block of turn.content) {\n if (block.type === 'tool_call' && isInteractionTool(block.name)) {\n calls.push({\n callId: block.id,\n tool: block.name,\n input: block.input,\n ...(turn.runId ? { runId: turn.runId } : {}),\n turnId: turn.id,\n createdAt: turn.createdAt,\n })\n }\n else if (block.type === 'tool_result') {\n resultIds.add(block.callId)\n }\n }\n }\n\n const pending: InteractionRequest[] = []\n for (const call of calls) {\n if (resultIds.has(call.callId))\n continue\n if (call.tool === PRESENT_PLAN_TOOL) {\n const payload = parsePlanPayload(call.input)\n if (!payload)\n continue\n pending.push({\n id: call.callId,\n // Reconstructed from an unmatched `tool_use` block, so model by\n // construction — host prompts never reach the transcript.\n origin: 'model',\n kind: 'plan',\n tool: PRESENT_PLAN_TOOL,\n payload,\n ...(call.runId ? { runId: call.runId } : {}),\n turnId: call.turnId,\n createdAt: call.createdAt,\n })\n }\n else if (call.tool === ASK_USER_TOOL) {\n const payload = parseQuestionPayload(call.input)\n if (!payload)\n continue\n pending.push({\n id: call.callId,\n origin: 'model',\n kind: 'question',\n tool: ASK_USER_TOOL,\n payload,\n ...(call.runId ? { runId: call.runId } : {}),\n turnId: call.turnId,\n createdAt: call.createdAt,\n })\n }\n }\n return pending\n}\n\n/**\n * Defensive parsers — tools validate via JSON Schema before `execute`, but\n * persisted turns can carry malformed input (model regressions, schema\n * migrations). We accept what we recognize and drop what we don't.\n */\nfunction parsePlanPayload(input: Record<string, unknown>): PlanPayload | null {\n const title = typeof input.title === 'string' ? input.title.trim() : ''\n const plan = typeof input.plan === 'string' ? input.plan : ''\n if (!title || !plan)\n return null\n const stepsRaw = Array.isArray(input.steps) ? input.steps : undefined\n const steps: PlanStep[] = []\n if (stepsRaw) {\n for (const s of stepsRaw) {\n if (!s || typeof s !== 'object')\n continue\n const cast = s as { id?: unknown, title?: unknown, description?: unknown }\n if (typeof cast.id !== 'string' || typeof cast.title !== 'string')\n continue\n steps.push({\n id: cast.id,\n title: cast.title,\n ...(typeof cast.description === 'string' ? { description: cast.description } : {}),\n })\n }\n }\n return {\n title,\n plan,\n ...(steps.length > 0 ? { steps } : {}),\n }\n}\n\n/**\n * Defensive parser for an `ask_user` request. Accepts BOTH the\n * multi-question shape and the single-question shape (so sessions that\n * persisted it still surface correctly on resume).\n *\n * - Multi: `{ intro?, questions: [...] }`.\n * - Single: `{ question, choices?, allowComment? }` → wrapped into a\n * single `select` (if choices) or `textarea` (otherwise) question\n * keyed by the synthetic id `'answer'`.\n */\nfunction parseQuestionPayload(input: Record<string, unknown>): QuestionPayload | null {\n const intro = typeof input.intro === 'string' && input.intro.trim().length > 0\n ? input.intro\n : undefined\n\n // Multi-question shape: explicit `questions` array. Parse each entry; drop\n // malformed ones rather than the whole payload so a single bad\n // question doesn't make the entire batch invisible on reload.\n //\n // Dedup by id (first wins). React would warn on the duplicate `key`\n // anyway, but more importantly the response `answers` map is keyed\n // by id — duplicates would silently collapse to a single value the\n // model would receive for every question that shared the id. First-\n // occurrence-wins keeps the form predictable; the model's own\n // schema validation should have caught it earlier.\n if (Array.isArray(input.questions)) {\n const questions: Question[] = []\n const seen = new Set<string>()\n for (const raw of input.questions) {\n const q = parseQuestion(raw)\n if (!q)\n continue\n if (seen.has(q.id))\n continue\n seen.add(q.id)\n questions.push(q)\n }\n if (questions.length === 0)\n return null\n return { ...(intro ? { intro } : {}), questions }\n }\n\n // Single-question shape: flatten to a one-question payload so resumed\n // sessions that persisted it still render.\n const question = typeof input.question === 'string' ? input.question.trim() : ''\n if (!question)\n return null\n const choices = parseChoices(input.choices)\n const legacy: Question = choices.length > 0\n ? { id: 'answer', prompt: question, type: 'select', choices }\n : { id: 'answer', prompt: question, type: 'textarea' }\n return { ...(intro ? { intro } : {}), questions: [legacy] }\n}\n\nfunction parseQuestion(raw: unknown): Question | null {\n if (!raw || typeof raw !== 'object')\n return null\n const cast = raw as {\n id?: unknown\n prompt?: unknown\n type?: unknown\n description?: unknown\n required?: unknown\n placeholder?: unknown\n default?: unknown\n choices?: unknown\n multi?: unknown\n affirmLabel?: unknown\n denyLabel?: unknown\n }\n if (typeof cast.id !== 'string' || !cast.id)\n return null\n if (typeof cast.prompt !== 'string' || !cast.prompt.trim())\n return null\n const type = cast.type\n if (type !== 'text' && type !== 'textarea' && type !== 'select' && type !== 'confirm')\n return null\n\n const base = {\n id: cast.id,\n prompt: cast.prompt,\n ...(typeof cast.description === 'string' ? { description: cast.description } : {}),\n ...(typeof cast.required === 'boolean' ? { required: cast.required } : {}),\n }\n\n if (type === 'text' || type === 'textarea') {\n return {\n ...base,\n type,\n ...(typeof cast.placeholder === 'string' ? { placeholder: cast.placeholder } : {}),\n ...(typeof cast.default === 'string' ? { default: cast.default } : {}),\n }\n }\n\n if (type === 'select') {\n const choices = parseChoices(cast.choices)\n if (choices.length === 0)\n return null // a select with zero choices is unusable\n return { ...base, type, choices, ...(cast.multi === true ? { multi: true } : {}) }\n }\n\n // type === 'confirm'\n return {\n ...base,\n type,\n ...(typeof cast.affirmLabel === 'string' ? { affirmLabel: cast.affirmLabel } : {}),\n ...(typeof cast.denyLabel === 'string' ? { denyLabel: cast.denyLabel } : {}),\n }\n}\n\nfunction parseChoices(raw: unknown): QuestionChoice[] {\n if (!Array.isArray(raw))\n return []\n const choices: QuestionChoice[] = []\n for (const c of raw) {\n if (!c || typeof c !== 'object')\n continue\n const cast = c as { id?: unknown, label?: unknown, description?: unknown, selected?: unknown }\n if (typeof cast.id !== 'string' || typeof cast.label !== 'string')\n continue\n choices.push({\n id: cast.id,\n label: cast.label,\n ...(typeof cast.description === 'string' ? { description: cast.description } : {}),\n // Preserved through the sanitizer, not just accepted at the type\n // level: a question rehydrated from a persisted session would\n // otherwise reopen with every pre-tick silently dropped.\n ...(cast.selected === true ? { selected: true } : {}),\n })\n }\n return choices\n}\n\n// ---------------------------------------------------------------------------\n// Tool factory — builds `present_plan` and `ask_user` ToolDefs bound to a\n// host-provided async resolver. Used by the chat host's agent build\n// (`host/agent-build/tools.ts`); a GUI consumer wires its own React resolver\n// the same way.\n// ---------------------------------------------------------------------------\n\n/**\n * Key of the awakeable a checkpoint run parks a model interaction on\n * (`ask_user`, `present_plan`): its run and call id, which the session's\n * turns hold too, so a host that rehydrates the prompt after a restart finds\n * the awakeable (`DurableRuntime.findAwakeable`). Turn 0: awakeables are\n * kept until their run ends, never pruned by turn.\n * @param runId - The run that asked.\n * @param requestId - The request id: the tool call id.\n */\nexport function interactionAwakeableKey(runId: string, requestId: string): StepKey {\n return stepKey({ run: runId, turn: 0, call: requestId, attempt: 0 })\n}\n\n/**\n * What a prompt's awakeable holds when the prompt was refused or dismissed\n * (its resolver rejected) instead of answered: a run resumed past it gets\n * the same tool error again, and is never asked again.\n */\nexport interface RefusedInteraction {\n refused: string\n}\n\n/** Whether an awakeable's value is a refusal ({@link RefusedInteraction}), not an answer. */\nexport function isRefusedInteraction(value: unknown): value is RefusedInteraction {\n return typeof value === 'object' && value !== null && !Array.isArray(value) && typeof (value as { refused?: unknown }).refused === 'string'\n}\n\n/** What the interaction tools hand {@link CreateInteractionToolsOptions.requestInteraction} besides the request. */\nexport interface RequestInteractionOptions {\n /**\n * Set when the run keeps step checkpoints: the run parks on this awakeable,\n * and the response is the value it resolves with. A resolver answers with\n * `runtime.resolveAwakeable(awakeable.id, response)` so the answer survives\n * a process death, and settles the request when the awakeable is resolved\n * elsewhere (another process answered it). The tool settles on whichever\n * comes first.\n */\n awakeable?: DurableAwakeable<InteractionResponse>\n}\n\n/** Options for {@link createInteractionTools}. */\nexport interface CreateInteractionToolsOptions {\n /**\n * Called when the agent invokes one of the interaction tools. Resolves\n * with the user's response (forwarded to the model as the tool_result);\n * reject to signal denial / abort (the model receives\n * `Tool error: <message>` as the result).\n */\n requestInteraction: (request: InteractionRequest, options?: RequestInteractionOptions) => Promise<InteractionResponse>\n /** Evaluated at invocation time so hosts can switch modes without rebuilding the agent. */\n shouldSkipInteractions?: () => boolean\n}\n\n/**\n * Create the `present_plan` and `ask_user` tools as a record, ready to\n * spread into `createAgent({ tools })` or composed into a {@link Preset}.\n *\n * @param opts - Resolver for the user's response and an optional skip check.\n * @example\n * ```ts\n * import { createInteractionTools } from 'zidane/chat'\n *\n * const tools = createInteractionTools({\n * requestInteraction: async () => ({ kind: 'plan', decision: 'approve' }),\n * })\n * ```\n */\nexport function createInteractionTools(opts: CreateInteractionToolsOptions): Record<string, ToolDef> {\n return {\n [PRESENT_PLAN_TOOL]: createPlanTool(opts),\n [ASK_USER_TOOL]: createQuestionTool(opts),\n }\n}\n\n/**\n * A checkpoint run's interaction runs outside a step and parks on an\n * awakeable (see {@link awaitInteractionResponse}); a step around it would\n * report an open prompt interrupted after a process death. Replay mode keeps\n * the step.\n */\nconst interactionDurableBody = (_execution: unknown, mode: DurableMode): 'composite' | undefined => mode === 'checkpoint' ? 'composite' : undefined\n\n/**\n * The response to a model interaction. A run that keeps step checkpoints\n * parks on an awakeable keyed by its call ({@link interactionAwakeableKey}):\n * the answer is stored before the tool goes on, and a run resumed after a\n * process death gets it back, from an answer given before or during the\n * downtime, without asking again. A refusal settles it too\n * ({@link RefusedInteraction}): an awakeable left unresolved would park the\n * run for good. Other runs ask the resolver alone.\n */\nasync function awaitInteractionResponse(\n { requestInteraction, shouldSkipInteractions }: CreateInteractionToolsOptions,\n request: InteractionRequest,\n ctx: ToolContext,\n): Promise<InteractionResponse> {\n if (shouldSkipInteractions?.())\n return createAutomaticInteractionResponse(request)\n const durable = ctx.durable\n if (durable?.checkpoint !== true)\n return requestInteraction(request)\n const awakeable = await durable.awakeable<InteractionResponse | RefusedInteraction>({ key: interactionAwakeableKey(durable.runId, request.id) })\n if (awakeable.settled)\n return storedInteractionResponse(awakeable.settled.value)\n // No deadline: the awakeable settles only with a value.\n const answered = awakeable.promise.then(outcome => outcome.timedOut ? new Promise<never>(() => {}) : outcome.value)\n let response: InteractionResponse | RefusedInteraction\n try {\n response = await Promise.race([requestInteraction(request, { awakeable: awakeable as DurableAwakeable<InteractionResponse> }), answered])\n }\n catch (err) {\n // An aborted run (stopped, taken over, the host shutting down) leaves the awakeable alone: after a takeover it is the new owner's.\n if (!ctx.signal.aborted) {\n const refused: RefusedInteraction = { refused: err instanceof Error ? err.message : String(err) }\n await durable.runtime.resolveAwakeable(awakeable.id, { ...refused }).catch(() => false)\n }\n throw err\n }\n return storedInteractionResponse(response)\n}\n\n/** The response an awakeable holds; a refusal throws its error again. */\nfunction storedInteractionResponse(value: InteractionResponse | RefusedInteraction): InteractionResponse {\n if (isRefusedInteraction(value))\n throw new Error(value.refused)\n return value\n}\n\nconst PLAN_DESCRIPTION\n = 'Present a multi-step plan to the user for approval BEFORE executing it. '\n + 'Use this when you have a concrete proposal that materially changes the workspace '\n + 'and want explicit confirmation. The user can approve, reject, or request revisions; '\n + 'revisions come back as a comment alongside a `revise` decision.'\n\nconst QUESTION_DESCRIPTION\n = 'Ask the user one or more clarifying questions and pause until they answer. '\n + 'Batch related questions into a single call instead of asking one at a time — '\n + 'every call pauses the conversation. '\n + 'Each question carries a `type`: '\n + '`text` (single line), `textarea` (multi-line), `select` (one of `choices`), or '\n + '`confirm` (yes/no). Use sparingly: only when explicit user input materially '\n + 'changes the next steps. The response may carry `instructions`: free text the '\n + 'user added alongside their answers. It refines or overrides the questions and is '\n + 'authoritative.'\n\nfunction createPlanTool(opts: CreateInteractionToolsOptions): ToolDef {\n const tool: DurableToolDef = {\n durableBody: interactionDurableBody,\n spec: {\n name: PRESENT_PLAN_TOOL,\n description: PLAN_DESCRIPTION,\n inputSchema: {\n type: 'object',\n properties: {\n title: { type: 'string', description: 'Short headline summary of the plan.' },\n plan: { type: 'string', description: 'Full plan body, written as Markdown.' },\n steps: {\n type: 'array',\n description: 'Optional structured step list. Each step has an `id`, `title`, and optional `description`.',\n items: {\n type: 'object',\n properties: {\n id: { type: 'string' },\n title: { type: 'string' },\n description: { type: 'string' },\n },\n required: ['id', 'title'],\n },\n },\n },\n required: ['title', 'plan'],\n },\n },\n async execute(input, ctx) {\n const payload = parsePlanPayload(input)\n if (!payload)\n throw new Error('present_plan: invalid input, expected non-empty `title` and `plan` strings.')\n const request: PlanRequest = {\n id: ctx.callId,\n origin: 'model',\n kind: 'plan',\n tool: PRESENT_PLAN_TOOL,\n payload,\n ...(ctx.runId ? { runId: ctx.runId } : {}),\n turnId: ctx.turnId,\n createdAt: Date.now(),\n }\n const response = await awaitInteractionResponse(opts, request, ctx)\n if (response.kind !== 'plan')\n throw new Error(`present_plan: unexpected response kind \"${response.kind}\".`)\n return serializeInteractionResponse(response)\n },\n }\n return tool\n}\n\nfunction createQuestionTool(opts: CreateInteractionToolsOptions): ToolDef {\n const tool: DurableToolDef = {\n durableBody: interactionDurableBody,\n spec: {\n name: ASK_USER_TOOL,\n description: QUESTION_DESCRIPTION,\n inputSchema: {\n type: 'object',\n properties: {\n intro: {\n type: 'string',\n description: 'Optional context shown above the questions (Markdown).',\n },\n questions: {\n type: 'array',\n description: 'One or more questions for the user to answer in a single form.',\n minItems: 1,\n items: {\n type: 'object',\n properties: {\n id: {\n type: 'string',\n description: 'Stable identifier — keys the answer back to the model.',\n },\n prompt: {\n type: 'string',\n description: 'The question to display.',\n },\n type: {\n type: 'string',\n enum: ['text', 'textarea', 'select', 'confirm'],\n description: '`text` = single line, `textarea` = multi-line, `select` = pick from `choices`, `confirm` = yes/no.',\n },\n description: {\n type: 'string',\n description: 'Optional helper text shown under the prompt.',\n },\n required: {\n type: 'boolean',\n description: 'Whether a non-empty answer is required.',\n },\n placeholder: {\n type: 'string',\n description: 'For `text` / `textarea`: placeholder shown when the input is empty.',\n },\n default: {\n type: 'string',\n description: 'For `text` / `textarea`: pre-fill the input with this value.',\n },\n choices: {\n type: 'array',\n description: 'The available choices. Required when `type` is `select`; omit for other types.',\n items: {\n type: 'object',\n properties: {\n id: { type: 'string' },\n label: { type: 'string' },\n description: { type: 'string' },\n selected: {\n type: 'boolean',\n description: 'For a `multi` select: open with this choice already ticked (opt-out lists).',\n },\n },\n required: ['id', 'label'],\n },\n },\n affirmLabel: {\n type: 'string',\n description: 'For `confirm`: label for the affirmative option. Default: `\"yes\"`.',\n },\n denyLabel: {\n type: 'string',\n description: 'For `confirm`: label for the negative option. Default: `\"no\"`.',\n },\n },\n required: ['id', 'prompt', 'type'],\n },\n },\n },\n required: ['questions'],\n },\n },\n async execute(input, ctx) {\n const payload = parseQuestionPayload(input)\n if (!payload)\n throw new Error('ask_user: invalid input, expected a non-empty `questions` array.')\n const request: QuestionRequest = {\n id: ctx.callId,\n origin: 'model',\n kind: 'question',\n tool: ASK_USER_TOOL,\n payload,\n ...(ctx.runId ? { runId: ctx.runId } : {}),\n turnId: ctx.turnId,\n createdAt: Date.now(),\n }\n const response = await awaitInteractionResponse(opts, request, ctx)\n if (response.kind !== 'question')\n throw new Error(`ask_user: unexpected response kind \"${response.kind}\".`)\n return serializeInteractionResponse(response)\n },\n }\n return tool\n}\n\n// ---------------------------------------------------------------------------\n// Queue entries — what the React queue (`interactions.tsx`) holds.\n// ---------------------------------------------------------------------------\n\n/** A queued interaction request with the callbacks that settle it. */\nexport interface PendingInteractionEntry {\n /** The request waiting for the user. */\n request: InteractionRequest\n /** The user picked something — forward to the live tool Promise or the resumed tool_result writer. */\n resolve: (response: InteractionResponse) => void\n /**\n * The user / system cancelled — distinct from `resolve` so renderers can\n * differentiate \"user picked something\" from \"user gave up / aborted\".\n * Live entries reject the tool's Promise; resumed entries decide what\n * to persist as the cancelled tool_result.\n */\n cancel: (reason?: string) => void\n}\n\n/**\n * Stable queue actions exposed by {@link InteractionsProvider}. Read them with {@link useInteractionsActions}.\n */\nexport interface InteractionsActions {\n /** Add a request to the FIFO queue. */\n enqueue: (entry: PendingInteractionEntry) => void\n /** Resolve the head request with `response`. */\n resolveHead: (response: InteractionResponse) => void\n /**\n * Cancel the head request. Meant for per-call cancel\n * affordances (a \"skip this\" button, a GUI's close-modal-X) — the\n * built-in TUI currently cancels via the run-level `cancelAll` path\n * only. Kept on the public API so consumer renderers can wire it\n * without a follow-up surface change.\n */\n cancelHead: (reason?: string) => void\n /** Cancel every queued request. Used by abort / session teardown. */\n cancelAll: (reason?: string) => void\n /**\n * Remove the queued entry whose `request.id` matches, WITHOUT settling\n * it locally — used by the daemon runtime when ANOTHER window resolved\n * the same shared interaction. No-op when absent.\n */\n dropById: (requestId: string) => void\n}\n\n// ---------------------------------------------------------------------------\n// Live-tool binding helper — wraps the queue actions into a Promise-shaped\n// `requestInteraction` that {@link createInteractionTools} expects.\n// ---------------------------------------------------------------------------\n\n/**\n * Build the `requestInteraction` callback for `createInteractionTools` from\n * the React-side {@link InteractionsActions}. Each call adds a fresh entry\n * to the queue whose `resolve` / `cancel` route back to one Promise that\n * the tool's `execute` awaits.\n *\n * @param actions - Queue actions from {@link useInteractionsActions}.\n */\nexport function makeRequestInteraction(\n actions: InteractionsActions,\n): CreateInteractionToolsOptions['requestInteraction'] {\n return request => new Promise<InteractionResponse>((resolve, reject) => {\n actions.enqueue({\n request,\n resolve,\n cancel: reason => reject(new Error(reason ?? 'Interaction cancelled')),\n })\n })\n}\n","/**\n * Host-domain wire shapes — the JSON-serializable types the chat host\n * (`createChatHost`) emits and consumes, independent of any transport.\n *\n * They live here so the host (`src/chat/host/**`) has zero dependency on\n * `src/ipc/**`. The IPC layer re-exports them from `src/ipc/protocol.ts`\n * alongside its transport-only\n * contract (method map, handshake, protocol version), so `zidane/ipc`\n * consumers are unaffected.\n *\n * Everything in this file is JSON-serializable: it must survive Electron's\n * structured clone AND a JSON-RPC socket hop unchanged. Canonical agent\n * types (`StreamEvent`, `InteractionRequest`, …) are re-used where they are\n * already wire-safe; host-only shapes (live `resolve` callbacks, `Map`s)\n * never appear here.\n */\nimport type { CacheBreakInfo } from '../../cache-telemetry'\nimport type { RunErrorDetail } from '../../errors'\nimport type { LogRecord } from '../../logger'\nimport type { PerfSample } from '../../perf/types'\nimport type { SessionArtifact } from '../../session/artifacts'\nimport type { TextOrigin } from '../../text-origin'\nimport type { JsonValue, TurnUsage } from '../../types'\nimport type { BrowserRequest } from '../browser/protocol'\nimport type { HunkResolution } from '../edit-diff'\nimport type { IntegrationInfo } from '../integrations'\nimport type { InteractionRequest } from '../interactions'\nimport type { InteractionAnswerer } from '../interactions-protocol'\nimport type { MachineLinksStatus, MachineStatusChange } from '../machine-protocol'\nimport type { RunEstimateView } from '../run-estimate'\nimport type { ApprovalDecision, ApprovalRequest } from '../safe-mode-context'\nimport type { BlockRun } from '../shell-blocks'\nimport type { SystemMdFile } from '../system-md'\nimport type { TerminalInfo } from '../terminal/protocol'\nimport type { ActiveTodosState, TodoItem, TodoTally } from '../todos'\nimport type { PromptTurnRef } from '../turn-selection'\nimport type { EditPayload, Settings, StreamEvent } from '../types'\nimport type { ExtensionsSignal } from './extensions-wire'\nimport type { ProviderDiagnostic } from './provider-diagnostic'\n\n/** Caller attribution for approvals raised by subagents. */\nexport type ApprovalOriginator = { kind: 'parent' } | { kind: 'child', label: string }\n\n/**\n * Serializable mirror of canonical `ActiveTodosState` — plain arrays so the\n * payload survives structured clone / JSON without `readonly` friction.\n */\nexport interface TodosSnapshot {\n /** Id of the run the todos belong to, or `null` when none is active. */\n runId: string | null\n /** Current todo items. */\n todos: TodoItem[]\n /** Last non-empty list, kept so the UI can show it after the live list is cleared. */\n archive: TodoItem[]\n /** The item being worked on, or `null`. */\n inProgress: TodoItem | null\n /** Counts of items by state. */\n tally: TodoTally\n}\n\n/**\n * Project canonical `ActiveTodosState` onto its wire mirror.\n *\n * One implementation because the copy is load-bearing, not cosmetic: the arrays\n * must be detached from the live session's before they cross a process boundary\n * (or reach another window in-process), and a site that forgot to copy would\n * hand out a reference that mutates under its reader. The broadcast and RPC\n * sites all call it.\n *\n * @param state - Active todos state of a session.\n */\nexport function toTodosSnapshot(state: ActiveTodosState): TodosSnapshot {\n return {\n runId: state.runId,\n todos: [...state.todos],\n archive: [...state.archive],\n inProgress: state.inProgress,\n tally: state.tally,\n }\n}\n\n/** Parent-level cancellable tool call or background task. */\nexport interface RunningWorkItem {\n /** Whether the item is a tool call or a background task. */\n kind: 'tool' | 'task'\n /** Tool call id or task id. */\n id: string\n /** Short description shown to the user. */\n label: string\n /** Start time in epoch ms. */\n startedAt: number\n /** Id of the subagent that started the item, when it was not the parent. */\n childId?: string\n /**\n * A call of a batch a durable run resumed after a process death, that the\n * dead process did not finish (its `tool` event says `resuming`). A\n * window attaching mid-run reads it from here. Optional: older hosts never\n * send it.\n */\n resuming?: true\n}\n\n/**\n * Serializable mirror of `PreviewResult` — `readonly` arrays widened so the\n * payload round-trips without TypeScript friction on the client side.\n */\nexport interface SerializedPreview {\n /** Unified diff of the proposed edit. */\n diffText: string\n /** The diff split into one string per hunk. */\n perHunkDiff: readonly string[]\n /** How the edit tool would resolve each hunk: whether its `old_string` is found without ambiguity. */\n resolution: readonly HunkResolution[]\n /** Edit payload with the resolution applied. */\n resolvedPayload: EditPayload\n}\n\n/**\n * Wire-safe approval request. Canonical `ApprovalRequest` minus its live\n * `resolve` callback (kept host-side, keyed by `id`), plus the context a\n * remote approval surface needs: owning session, safelist cwd, precomputed\n * safelist suggestions, and the edit preview byproducts.\n */\nexport interface WireApprovalRequest extends Omit<ApprovalRequest, 'resolve'> {\n /** Session that raised the request. */\n sessionId: string\n /** Working directory that safelist entries are scoped to. */\n cwd: string\n /** Safelist entries the user can add when approving. */\n suggestedSafelistEntries: string[]\n /** The edit under approval, for edit tools. */\n edit?: EditPayload\n /** File content before the edit. `null` when the file did not exist. */\n priorContent?: string | null\n /** Diff preview of the edit. */\n preview?: SerializedPreview\n}\n\n/** Attachment carried alongside the prompt text (base64, no data-URL prefix). */\nexport interface PromptAttachment {\n /** MIME type of the attachment. */\n mediaType: string\n /** Base64 content. */\n data: string\n /** File name, when known. */\n name?: string\n}\n\n/** `chat.send` options. */\nexport interface SendOptions {\n /** Session to send the prompt to. */\n sessionId: string\n /** Prompt text. */\n prompt: string\n /** Skill names picked via `/` completion — activated before the run. */\n activatedSkills?: string[]\n /** Attachments sent with the prompt. */\n attachments?: PromptAttachment[]\n /**\n * Host-internal wake run: delivers pending background-task notifications\n * to the model with an empty prompt. Skips the `user-prompt` broadcast\n * (there is no user message) — mirrors the local TUI's wake semantics.\n */\n wake?: boolean\n /**\n * The client resolved this text as the `/` command of this name in ITS\n * registry. The host re-resolves against the SESSION's registry (its cwd,\n * its extensions) and refuses when that misses, instead of sending\n * `/fix comments` to the model as prose — the two registries can disagree\n * (extension installed after the runtime cache was built, different cwd),\n * and a silent prose send is indistinguishable from a broken command.\n */\n asCommand?: string\n /**\n * Sent from outside the composer (Continue, Retry, a palette prompt): the\n * shared draft was not consumed, so the daemon leaves it alone instead of\n * clearing it in every other window.\n */\n keepDraft?: boolean\n /**\n * Who or what the prompt comes from, when not the session's own user: the\n * prompt's text block in the persisted user turn carries it. The host never\n * rewrites the text because of it.\n */\n origin?: TextOrigin\n /** Never resolve the text as a `/` command: it reaches the model as sent. */\n literal?: boolean\n /**\n * The client's own id for this message: the host reports what becomes of it\n * (`delivery:changed`), and sets it as `origin.messageId` when the message\n * has an origin without one.\n */\n clientMessageId?: string\n /**\n * Extension data riding with the prompt (a `prompt` interceptor's\n * `metadata`): later interceptors and the run's `run` interceptors see it\n * (`AgentRunOptions.metadata`). Not saved with the user turn.\n */\n metadata?: Record<string, JsonValue>\n}\n\n/** The fields of a prompt that wait with it in the queue until it runs (`chat.pushQueued`, `queue.add`). */\nexport type QueuedPromptFields = Pick<SendOptions, 'origin' | 'literal' | 'clientMessageId' | 'metadata'>\n\n/**\n * What became of a message sent with a `clientMessageId`: parked in the host\n * queue, injected into the live run, the prompt of a run that started, the\n * end of the run that took it (`answered` / `failed` / `interrupted`), or\n * removed from the queue unsent (`dropped`).\n */\nexport type DeliveryState = 'queued' | 'injected' | 'started' | 'answered' | 'failed' | 'interrupted' | 'dropped'\n\n/**\n * Wire-safe summary of an extension-contributed `/` command — the\n * projection of canonical `Command` (src/chat/commands.ts) minus its\n * function-valued fields (steps, completeArgs).\n */\nexport interface CommandSummary {\n /** Command name, without the leading `/`. */\n name: string\n /** One-line description shown in completion lists. */\n description: string\n /** Alternative names that run the same command. */\n aliases?: string[]\n\n /** Grouping label — defaults to the contributing extension's name. */\n group?: string\n\n /**\n * `true` when the canonical command declares `completeArgs` — lets a\n * client wire an arg-completion shim only for commands that actually\n * complete, instead of firing blind RPCs on every `/name <prefix>`.\n */\n hasArgCompletion?: boolean\n}\n\n/** Wire-safe projection of canonical `CommandArgSuggestion`. */\nexport interface CommandArgSuggestionSummary {\n /** Text inserted after `/<name> ` on accept. */\n value: string\n\n /** Display label. Defaults to `value`. */\n label?: string\n /** Secondary text shown next to the label. */\n description?: string\n}\n\n/** Host settings — canonical `Settings` plus the extension allowlist. */\nexport type HostSettings = Settings & {\n enabledExtensions?: readonly string[]\n /** @deprecated Use `approvalMode` (`safeMode: false` is `approvalMode: 'off'`); read only when `approvalMode` is absent. */\n safeMode?: boolean\n}\n\n/** One entry in the shared type-ahead queue. */\nexport interface QueuedItem {\n /** Queue entry id. */\n id: string\n /** Queued prompt text. */\n text: string\n /** The sender's id for the message (`SendOptions.clientMessageId`), when it gave one. */\n clientMessageId?: string\n /**\n * What rides with the text, WITHOUT the bytes — enough for a queue row to\n * show \"+2 attachments\" chips the way the echoed prompt will. The payload\n * itself stays host-side until the entry is sent.\n */\n attachments?: QueuedAttachment[]\n}\n\n/** An attachment on a queued prompt, without its bytes. */\nexport interface QueuedAttachment {\n /** MIME type of the attachment. */\n mediaType: string\n /** File name, when known. */\n name?: string\n}\n\n/**\n * Live per-session presence — which sessions have attached windows and\n * what state they're in. Drives the session list's \"active\" scope and\n * per-row status icons. A session appears here when it has ≥1 attached\n * window, a run in flight, or pending HITL state.\n */\nexport interface SessionPresence {\n /** Session the presence is for. */\n sessionId: string\n /** Number of windows currently attached (watching) this session. */\n windows: number\n /** A run is executing right now. */\n running: boolean\n /** Pending approval or interaction — awaiting user input somewhere. */\n attention: boolean\n}\n\n/** Pending interaction row as returned by `interactions.list`. */\nexport interface PendingInteraction {\n /** Session that raised the interaction. */\n sessionId: string\n /** The interaction request awaiting an answer. */\n request: InteractionRequest\n /** `true` when a live agent loop is waiting on the answer. `false` for one restored after a restart. */\n live: boolean\n}\n\n/** Payload of the `stream:progress` signal — see the variant's doc. */\nexport interface StreamProgressInfo {\n /** Turn that is streaming. */\n turnId: string\n /** Output tokens so far. */\n outputTokens: number\n /** `true` when `outputTokens` is an estimate rather than provider-counted. */\n estimated: boolean\n /** Time since the turn started in ms. */\n elapsedMs: number\n /** Running cost estimate for this turn in USD. Absent for unpriced models. */\n estCostUsd?: number\n}\n\n/**\n * The single push channel. Chat stream signals mirror the agent loop hooks;\n * interaction/approval signals carry the HITL queues so every window renders\n * the same pending state; `sessions:changed` keeps session lists in sync\n * across windows.\n */\nexport type HostSignal\n /** `startedAt`: when the run began, epoch ms on the host's clock. Additive: absent from older hosts. */\n = | { kind: 'run:start', sessionId: string, runId: string, startedAt?: number }\n /**\n * `background: true` marks a session the user doesn't open themselves\n * (`isBackgroundSession`), so clients keep quiet about its run ending.\n * `detail` is what a failed run's one-line `error` leaves out.\n */\n /** `takenOver`: another process claimed the run, which goes on there (`error` says so). Optional: older clients show `error`. */\n | { kind: 'run:end', sessionId: string, runId: string, ok: boolean, error?: string, detail?: RunErrorDetail, background?: true, takenOver?: true }\n /**\n * A turn finished. `usage` carries the loop-reported token/cost figures\n * that drive footer gauges (context size = prompt-side tokens of the\n * LAST parent turn; cost accrues across parent + child turns). A parent\n * turn's `runOutputTokens` is its run's output so far, this turn\n * included: a run readout takes it as is instead of adding turns up, so a\n * window that read the count mid-run never counts a turn twice.\n */\n | { kind: 'turn:end', sessionId: string, turnId: string, childId?: string, depth?: number, usage?: TurnUsage, runOutputTokens?: number }\n /**\n * `echo: true` marks host-generated SEND/STEER echoes of the user's own\n * message. Clients that already render the prompt locally (the GUI's\n * enriched append) drop echo-tagged signals; command-module\n * user-prompts are never echoes and always render.\n */\n | { kind: 'event', sessionId: string, event: StreamEvent, echo?: boolean }\n | { kind: 'compacting:start', sessionId: string }\n /**\n * Live token estimate for an in-flight compaction summary. Throttled by the\n * loop, so this is a low-rate progress tick rather than a stream.\n *\n * Without it a daemon-mode window shows a static \"Compacting…\" with no\n * indication of progress — a compaction of a large transcript can run for\n * minutes, which is indistinguishable from a hang.\n */\n | { kind: 'compacting:progress', sessionId: string, estOutputTokens: number }\n /**\n * Live counters for the parent turn currently streaming — the loop's\n * `stream:progress` (throttled ~4/s, one final tick before `stream:end`).\n * `outputTokens` is provider-counted unless `estimated`; `estCostUsd` is\n * the running catalog-rate estimate for THIS turn (absent for unpriced\n * models and flat-rate subscription traffic). Clients add it to the settled\n * session figure while streaming and drop it when the matching `turn:end`\n * brings the real `usage.cost`.\n */\n | ({ kind: 'stream:progress', sessionId: string } & StreamProgressInfo)\n /**\n * Prompt turns that just persisted, reduced to the fields needed to bind a\n * live `user-prompt` event to its turn id (see {@link PromptTurnRef}).\n *\n * Deliberately NOT the full `SessionTurn`: those carry every content block,\n * including base64 image/document payloads, which would be megabytes per\n * prompt to ship for the sake of an id. Without this a prompt sent from a\n * daemon-attached window is unselectable — no hover, edit, or delete — until\n * the run-end transcript replace.\n */\n | { kind: 'prompt-turns', sessionId: string, refs: readonly PromptTurnRef[] }\n /**\n * `effectiveTokens` (success only) is the loop's post-compact context\n * estimate — windows drop their footer ctx gauge to it immediately\n * instead of waiting for the next turn's usage.\n */\n | { kind: 'compacting:end', sessionId: string, ok: boolean, effectiveTokens?: number }\n | {\n kind: 'model:fallback'\n sessionId: string\n from: { provider: string, model: string }\n to: { provider: string, model: string }\n /** The failed target had streamed reasoning for this turn: windows drop it (see `stream:retry`). */\n discardTurnId?: string\n }\n | { kind: 'work:snapshot', sessionId: string, items: RunningWorkItem[] }\n | { kind: 'todos:snapshot', sessionId: string, snapshot: TodosSnapshot }\n /**\n * The session recorded an artifact (PR, branch, commit, file, extension\n * kind — see `session/artifacts.ts`). Carries the stored row so a window\n * can badge / list without a round-trip; `sessions.artifacts` is the\n * authoritative query. Additive — older clients ignore unknown kinds.\n */\n | { kind: 'artifact:recorded', sessionId: string, artifact: SessionArtifact }\n /**\n * An artifact was removed or refreshed through the host (`artifacts.delete`\n * / `artifacts.upsert`) — windows re-query. Carries the row for `upsert`.\n */\n | { kind: 'artifact:changed', sessionId: string, op: 'delete' | 'upsert', artifactId: string, artifact?: SessionArtifact }\n | { kind: 'interaction:request', sessionId: string, request: InteractionRequest, live: boolean }\n | { kind: 'interaction:resolved', sessionId: string, requestId: string, answeredBy?: InteractionAnswerer }\n /**\n * Dev browser (`browser.ts` hub): a tool call for the page, addressed to\n * the registered browser host (`hostId`); other clients ignore it. The\n * host answers through `browser.respond`. `browser:cancel` withdraws it.\n */\n | { kind: 'browser:request', sessionId: string, hostId: string, request: BrowserRequest }\n | { kind: 'browser:cancel', sessionId: string, hostId: string, requestId: string }\n /**\n * Terminals (`terminal/` manager). `changed` carries the session's full\n * list; `output` is a batch starting at `offset` (clients drop what they\n * already have and `terminal.ack` what they wrote); `reset` replaces a\n * view's content with history after a client fell too far behind.\n */\n | { kind: 'terminal:changed', sessionId: string, terminals: TerminalInfo[] }\n | { kind: 'terminal:output', sessionId: string, terminalId: string, offset: number, data: string }\n | { kind: 'terminal:reset', sessionId: string, terminalId: string, offset: number, data: string }\n /**\n * The full list of a project's terminals (`draftId` absent) or of one\n * draft's. No `sessionId`: these belong to no session (session signals are\n * numbered and replayed per session). Output of these terminals still\n * arrives as `terminal:output` / `terminal:reset` with `sessionId: ''`,\n * sent only to the views attached to them.\n */\n | { kind: 'terminal:project-changed', root: string, draftId?: string, terminals: TerminalInfo[] }\n /**\n * A streamed workspace command (`sessions.workspaceExecStream`): its output\n * in arrival order, then exactly one `exit`. Sent to the connection that\n * started it only, never fanned out or replayed. `code` is `null` and\n * `signal` set when the host killed it (`cancelled`: the client asked;\n * `truncated`: the output cap was reached).\n */\n | { kind: 'workspace:exec:data', execId: string, stream: 'stdout' | 'stderr', chunk: string }\n | { kind: 'workspace:exec:exit', execId: string, code: number | null, signal: string | null, truncated?: true, cancelled?: true }\n /** A chunk of a Run here's output (`sessions.runBlock`), to the connection that started it only. */\n | { kind: 'block-run:data', runId: string, stream: 'stdout' | 'stderr', chunk: string }\n /** A Run here ended: the saved run, or why it could not run. To the connection that started it only. */\n | { kind: 'block-run:end', runId: string, run?: BlockRun, error?: string }\n /** Git's progress on the connection's `git.clone`, to that connection only. */\n | { kind: 'git:clone:progress', phase: string, percent: number }\n /** A checkout's refs moved (`git.watch`), to the connections watching `cwd` only. */\n | { kind: 'git:refs', cwd: string }\n /** An extension panel's content changed (`panels.watch`), to the connections watching it only. */\n | { kind: 'panel:changed', cwd: string, panelId: string }\n /** `cwd`'s extension palette items may have changed (`palette.watch`), to the connections watching only. */\n | { kind: 'palette:changed', cwd: string }\n /** A new record in a zidane-server's log (`logs.watch`), to the connections following it only. */\n | { kind: 'logs:record', record: LogRecord }\n /** The host's performance sample (`perf.watch`), to the connections watching it only. */\n | { kind: 'perf:sample', sample: PerfSample }\n /**\n * A client installed, updated or removed something on the host\n * (`catalog.install` / `remove` / `update`, `mcp.upsert` / `remove`): every\n * window re-reads that catalog. `models`: what `models.list` answers\n * changed (an extension's provider or account model list).\n */\n | { kind: 'catalog:changed', catalog: 'skills' | 'mcps' | 'extensions' | 'plugins' | 'models' }\n /** A server's settings-sync record changed (a claim, a push, an override, a reset): settings UIs re-read `sync.status`. */\n | { kind: 'sync:changed' }\n /** A zidane-server's tokens changed (one created, revoked, paired, or the token file edited): token lists re-read `tokens.list`. */\n | { kind: 'tokens:changed' }\n | { kind: 'approval:request', request: WireApprovalRequest }\n /**\n * An approval left the pending queue (answered or denied in bulk) — every\n * OTHER window clears its surface. The window that resolved it already\n * knows; the signal makes the queue converge everywhere else.\n */\n | { kind: 'approval:resolved', sessionId: string, requestId: string, decision: ApprovalDecision, answeredBy?: InteractionAnswerer }\n | { kind: 'sessions:changed', op: 'create' | 'update' | 'delete', sessionId: string }\n /** A session's saved block runs changed (a Run here ended, a run was removed): windows read them again. */\n | { kind: 'block-runs:changed', sessionId: string }\n /**\n * A session's facet values changed (`ctx.sessions.facets`): every facet\n * it holds now, keyed `<extensionId>.<key>`, the same as `SessionRow.facets`.\n * Not a `sessions:changed`: the row's other fields stand.\n */\n | { kind: 'sessions:facets', sessionId: string, facets: Record<string, JsonValue> }\n /**\n * Settings were written through the host (`settings.set`) — every window\n * applies the new state so settings UIs converge without a restart.\n * Windows that persist settings themselves must treat this as\n * authoritative for the delivered snapshot.\n */\n | { kind: 'settings:changed', settings: HostSettings }\n /**\n * Another window edited the session's shared prompt draft. The daemon\n * EXCLUDES the originating peer from this fan-out, so applying it can\n * never fight the local cursor — clients apply unconditionally\n * (last-writer-wins whole-text replacement). Empty `text` clears the\n * draft (broadcast when any window submits the prompt).\n */\n | { kind: 'draft:changed', sessionId: string, text: string, shell?: boolean }\n /**\n * The shared draft's composing ATTACHMENTS changed (paste/drop/remove in\n * any window). Payloads ride along so a remote window can submit the\n * shared draft attachments-and-all. Same origin-exclusion as text.\n */\n | { kind: 'draft:attachments', sessionId: string, attachments: PromptAttachment[] }\n /**\n * The session's shared transcript-search state changed (mode entered /\n * exited, query edited) in another window. Same origin-exclusion and\n * last-writer-wins contract as the draft — every window on the session\n * shows the same filtered transcript.\n */\n | { kind: 'search:changed', sessionId: string, active: boolean, query: string }\n /**\n * The daemon resolved another window's inline-autocomplete query\n * (`suggest.resolve`). Excludes the requesting peer (it already has\n * the result from its RPC reply). Receivers mirror the ghost ONLY\n * when their live buffer still equals `basis` — the standard\n * staleness guard; otherwise discard.\n */\n | { kind: 'suggest:changed', sessionId: string, seq: number, basis: string, caret: number, suggestion: string | null, candidates?: string[] }\n /**\n * The text model's guess at the user's next prompt after run `runId`\n * (`Settings.promptSuggestions`). Windows offer it in the empty composer\n * and drop it on the next `run:start`, a send, an edit or a compaction.\n */\n | { kind: 'suggestion:ready', sessionId: string, runId: string, text: string }\n /**\n * A run of the session was estimated, or its wait on the user started or\n * ended (`Settings.runEstimates`): the session's own run (`depth` 0) or a\n * subagent's (`label`, `callId`). Saved on the run record too, so a window\n * that missed it reads it from the session row's `runEstimates`.\n */\n | { kind: 'run:estimate', sessionId: string, estimate: RunEstimateView }\n /**\n * The daemon-side agent's active-skill set changed (`skills_use` /\n * run-end deactivation). Windows mirror it into their skill chips.\n */\n | { kind: 'skills:snapshot', sessionId: string, active: string[] }\n /**\n * A daemon-side MCP server needs (re-)authentication. Windows surface\n * the badge; the interactive login flow itself runs in whichever window\n * the user triggers it from (tokens land in the shared credential\n * store; the daemon reconnects on the next agent rebuild).\n */\n | { kind: 'mcp:auth', sessionId: string, name: string, reason: string }\n /**\n * The session agent's MCP bootstrap finished: every server has its\n * outcome (`mcp:status` / `mcp:auth` came first). A window's deadline for\n * the one-shot auto-login it arms when the user adds a server.\n */\n | { kind: 'mcp:settled', sessionId: string }\n /**\n * A host-side MCP server connected, or failed to. Windows feed it to the\n * same status reducer the in-process path uses (badges: connected, signed\n * in, needs sign-in, key refused, couldn't connect). `error` is the\n * failure's message and `code` its HTTP-ish code when it had one, enough\n * for the shared classifier (`classifyMcpTest`) to read it the same way.\n */\n | { kind: 'mcp:status', sessionId: string, name: string, status: 'connected' }\n | { kind: 'mcp:status', sessionId: string, name: string, status: 'failed', transport: string, error: string, code?: number }\n /**\n * Provider retry in progress (rate-limit / overload / silent stall) —\n * drives the footer's \"retrying n/m\" indicator. `source` is `'parent'`\n * or the retrying child's id; the indicator clears when that source\n * streams again or the run ends. `discardTurnId`: the failed attempt had\n * streamed reasoning for that turn; windows drop it (`discardThinking`),\n * since the retry streams its own and the saved turn never holds it.\n */\n | { kind: 'stream:retry', sessionId: string, attempt: number, max: number, source: string, discardTurnId?: string }\n /**\n * A retried stream reached a terminal state for `source` (`'parent'` or a\n * child id) — clear that source's retry indicator.\n *\n * Distinct kind rather than a nullable `attempt` so the two states stay\n * unambiguous on the wire. Consumers also clear at run end as a backstop;\n * this exists for the mid-run case, where a subagent's stream fails while\n * the parent run continues and the indicator would otherwise stick.\n */\n | { kind: 'stream:retry:cleared', sessionId: string, source: string }\n /**\n * Which `SYSTEM.md` override (if any) the host folded into this session's\n * system prompt, emitted at agent-build time.\n *\n * The prompt is composed where the agent is built — in the daemon, not in\n * the window — so a window cannot derive this by probing its own disk: it\n * may have a different cwd, and `userInstructionsScope` is the daemon's.\n * Windows need it to tell \"no override on disk\" apart from \"override in\n * force\", which is the difference between doctrine the user wrote and\n * doctrine they think they replaced. `shadowed` carries precedence losers\n * so the Instructions surface can mark them inactive.\n */\n | { kind: 'system-md', sessionId: string, file: SystemMdFile | null, shadowed: readonly SystemMdFile[] }\n /**\n * A cache-prefix break was detected for this session's last request — the\n * footer chip names the culprit dimension and the re-billed prefix size.\n * Detection is per-agent closure state inside the loop, so in daemon mode\n * only the daemon can observe it. Cost pricing stays window-side (it needs\n * only the provider key and the token count).\n */\n | { kind: 'cache:break', sessionId: string, info: CacheBreakInfo }\n | { kind: 'provider:diagnostic', sessionId: string, diagnostic: ProviderDiagnostic }\n\n /**\n * Presence snapshot — recomputed and fanned out whenever a window\n * attaches/detaches or any tracked session's run/HITL state changes.\n * Full snapshot (the set is small: only live sessions appear).\n */\n | { kind: 'presence:changed', sessions: SessionPresence[] }\n /**\n * The host's integrations changed (`integrations.register` / `update`, or\n * the client that registered one disconnected). Full list, like presence.\n */\n | { kind: 'integrations:changed', integrations: IntegrationInfo[] }\n /** A machine of this host came online or went offline (`docs/MACHINES.md`). */\n | ({ kind: 'machine:status' } & MachineStatusChange)\n /** This local host's machine links changed (`machines.links.status`): shares of this computer, servers used as machines. */\n | { kind: 'machine:links', links: MachineLinksStatus }\n /**\n * A server lending itself as a machine to the local host on this\n * connection (`machine.attach`): one of its machine notifications\n * (`terminal.data`, `task.exit`, `exec.stream.*`). Sent to that\n * connection only, never broadcast.\n */\n | { kind: 'machine:notification', method: string, params: JsonValue }\n /**\n * The session's type-ahead queue changed (message queued while a run is\n * in flight, drained by the host on run end, pushed early, or dropped).\n * Broadcast to EVERY peer including the origin — the queue box is shared\n * state and all windows render the same list.\n */\n | { kind: 'queue:changed', sessionId: string, items: QueuedItem[] }\n /**\n * A message sent with a `clientMessageId` changed state (`DeliveryState`).\n * `runId`: the run that started with it, took it, or ended with it. For\n * integrations; windows ignore it.\n */\n | { kind: 'delivery:changed', sessionId: string, clientMessageId: string, state: DeliveryState, runId?: string }\n /** Extension API: RPC events, UI items, dialogs, client UI requests, storage, commands, integrations (`./extensions-wire`). */\n | ExtensionsSignal\n","/**\n * A window's overrides for a host-built session agent: the `zidane run`\n * launch flags (`--system / --mcp / --context …`) and a working directory\n * picked mid-session. Owned by the window (never persisted). Dependency-free\n * so the TUI, the daemon dispatcher, and the host share one rule without\n * importing the engine.\n */\nimport type { McpServerConfig } from '../../mcp'\nimport type { StartLocalOptions } from '../../start/shared-options'\n\n/** `zidane run --context …`: the execution this launch runs in, overriding the session's environment. */\nexport interface LaunchExecution {\n /** Execution context kind (`docker`, `e2b`, `daytona`). `process` is the default and not an override. */\n context: string\n image?: string\n env?: Record<string, string>\n pregame?: { name: string, content: string }\n sandboxId?: string\n sandboxOnDestroy?: 'kill' | 'pause' | 'leave'\n}\n\n/** `ChatHost.setLaunchOverrides`: what the window overrides on a session's agent. */\nexport interface SessionLaunchOverrides {\n /** Replaces the base system prompt (extension fragments still append). */\n system?: string\n /** Extra MCP servers, after the discovered and plugin ones (which win a name collision). */\n mcp?: readonly McpServerConfig[]\n /** Execution context to run the session in. */\n execution?: LaunchExecution\n /**\n * Working directory picked mid-session (the TUI's cwd picker), instead of\n * the session's folder: the live agent's tools move at once, its prompt on\n * the next turn, and later builds use it. Not persisted.\n */\n cwd?: string\n}\n\n/**\n * The overrides that shape an agent, or `null` when none do (no `system`,\n * no MCP server, no non-`process` context, no cwd). The one rule for \"are launch\n * overrides set\", shared by the host, the daemon dispatcher, and the TUI.\n *\n * @param overrides - Overrides to reduce. `null` and `undefined` mean none.\n */\nexport function normalizeLaunchOverrides(overrides: SessionLaunchOverrides | null | undefined): SessionLaunchOverrides | null {\n if (!overrides)\n return null\n const mcp = overrides.mcp && overrides.mcp.length > 0 ? overrides.mcp : undefined\n const execution = overrides.execution && overrides.execution.context !== 'process' ? overrides.execution : undefined\n const cwd = overrides.cwd || undefined\n if (overrides.system === undefined && !mcp && !execution && !cwd)\n return null\n return {\n ...(overrides.system !== undefined ? { system: overrides.system } : {}),\n ...(mcp ? { mcp } : {}),\n ...(execution ? { execution } : {}),\n ...(cwd ? { cwd } : {}),\n }\n}\n\n/**\n * A window's overrides for a session (see {@link normalizeLaunchOverrides}):\n * what its `zidane run` launch carries, plus its cwd when that is not the\n * session's own folder (picked mid-session).\n */\nexport function launchOverridesFrom(\n launch: Partial<StartLocalOptions> | null | undefined,\n location?: { cwd: string, sessionFolder: string },\n): SessionLaunchOverrides | null {\n return normalizeLaunchOverrides({\n ...(location && location.cwd !== location.sessionFolder ? { cwd: location.cwd } : {}),\n ...fromLaunch(launch),\n })\n}\n\nfunction fromLaunch(launch: Partial<StartLocalOptions> | null | undefined): SessionLaunchOverrides {\n if (!launch)\n return {}\n return {\n ...(launch.system !== undefined ? { system: launch.system } : {}),\n ...(launch.mcp ? { mcp: launch.mcp } : {}),\n ...(launch.context\n ? {\n execution: {\n context: launch.context,\n ...(launch.image !== undefined ? { image: launch.image } : {}),\n ...(launch.env !== undefined ? { env: launch.env } : {}),\n ...(launch.pregame !== undefined ? { pregame: launch.pregame } : {}),\n ...(launch.sandboxId !== undefined ? { sandboxId: launch.sandboxId } : {}),\n ...(launch.sandboxOnDestroy !== undefined ? { sandboxOnDestroy: launch.sandboxOnDestroy } : {}),\n },\n }\n : {}),\n }\n}\n","/**\n * Automatic session titling (`Settings.autoTitle`): after a run, decide\n * whether the session's title still describes it and regenerate it when not.\n *\n * Title provenance lives on `metadata.titleSource`:\n * - `'auto'`: written by the title generator (automatic, or the session\n * details' \"generate title\" action). Auto-titling may replace it.\n * - `'user'`: typed by the user (a rename). Never replaced automatically.\n * - absent with a title: predates provenance; treated as the user's.\n *\n * Staleness is a decision (`ctx.decide` predicate), checked at most once per\n * {@link MIN_NEW_PROMPTS} user prompts so short follow-ups never retitle.\n */\n\nimport type { AgentDecide } from '../decisions/types'\nimport type { SessionTurn } from '../types'\nimport { renderTurnsForPrompt } from './generate-title'\nimport { oneLineTitle, titleFromText } from './session-title-text'\n\n/** Metadata key that records whether the title was set by auto-titling or by the user. */\nexport const TITLE_SOURCE_KEY = 'titleSource'\n/** User-prompt count at the last staleness check (or generation). */\nexport const TITLE_CHECKED_KEY = 'titleCheckedPrompts'\n/** New user prompts needed before a title is re-checked. */\nexport const MIN_NEW_PROMPTS = 2\n/** Probability above which the title counts as stale. */\nexport const STALE_THRESHOLD = 0.7\n/** Word cap on titles while auto-titling is on (sidebar-sized). */\nexport const AUTO_TITLE_MAX_WORDS = 5\n/** The last auto-title attempt's outcome ({@link TitleCheckRecord}), shown in session details. */\nexport const TITLE_CHECK_KEY = 'titleCheck'\n\n/** Who set a session's title: auto-titling or the user. */\nexport type TitleSource = 'auto' | 'user'\n\n/**\n * What the last auto-title attempt did. Failures are recorded too: auto-\n * titling never surfaces an error mid-conversation, so this is where a\n * missing key or a timeout shows up.\n */\nexport interface TitleCheckRecord {\n /** When the attempt ran, in ms since epoch. */\n at: number\n /** What the attempt did or how it failed. */\n outcome: 'generated' | 'renamed' | 'kept' | 'decision-failed' | 'generation-failed'\n /** Decision failure reason (`DecisionFailureReason`), for `decision-failed`. */\n reason?: string\n /** Failure detail, when the attempt failed. */\n message?: string\n}\n\nfunction readTitleCheck(metadata: Readonly<Record<string, unknown>> | undefined): TitleCheckRecord | undefined {\n const raw = metadata?.[TITLE_CHECK_KEY]\n if (!raw || typeof raw !== 'object')\n return undefined\n const r = raw as Partial<TitleCheckRecord>\n return typeof r.at === 'number' && typeof r.outcome === 'string' ? (r as TitleCheckRecord) : undefined\n}\n\n/** How long a failed attempt holds off the next one: a missing key or a rejected model fails every run until fixed. */\nexport const TITLE_FAILURE_COOLDOWN_MS = 5 * 60_000\n\n/**\n * Whether the last attempt failed less than {@link TITLE_FAILURE_COOLDOWN_MS} ago.\n *\n * @param metadata - Session metadata holding the last check record.\n * @param now - Current time in ms since epoch.\n */\nexport function inTitleFailureCooldown(metadata: Readonly<Record<string, unknown>> | undefined, now: number = Date.now()): boolean {\n const check = readTitleCheck(metadata)\n return !!check && (check.outcome === 'decision-failed' || check.outcome === 'generation-failed')\n && now - check.at < TITLE_FAILURE_COOLDOWN_MS\n}\n\nfunction ago(ms: number): string {\n const s = Math.max(0, Math.round(ms / 1000))\n if (s < 60)\n return 'just now'\n const m = Math.round(s / 60)\n if (m < 60)\n return `${m}m ago`\n const h = Math.round(m / 60)\n return h < 48 ? `${h}h ago` : `${Math.round(h / 24)}d ago`\n}\n\n/**\n * Session details' title facts, one badge each: where the title came from and\n * what the last auto-title attempt did, e.g. `auto` `checked 5m ago, still\n * fits` or `auto` `check failed 2m ago (unavailable: needs a platform key)`.\n *\n * @param metadata - Session metadata that holds the title, its source and the last check record.\n * @param now - Current time in ms since epoch, used for relative times.\n */\nexport function describeTitleStatus(metadata: Readonly<Record<string, unknown>> | undefined, now: number = Date.now()): string[] {\n const source = metadata?.[TITLE_SOURCE_KEY]\n const hasTitle = currentTitle(metadata).length > 0\n const origin = !hasTitle ? 'untitled' : source === 'auto' ? 'auto' : source === 'user' ? 'typed by you' : 'set before auto-titling'\n const check = readTitleCheck(metadata)\n if (!check)\n return [origin]\n const when = ago(now - check.at)\n const detail = check.reason || check.message ? ` (${[check.reason, check.message].filter(Boolean).join(': ')})` : ''\n const what = {\n 'generated': `generated ${when}`,\n 'renamed': `renamed ${when}, topic changed`,\n 'kept': `checked ${when}, still fits`,\n 'decision-failed': `check failed ${when}${detail}`,\n 'generation-failed': `generation failed ${when}${detail}`,\n }[check.outcome] ?? check.outcome\n return [origin, what]\n}\n\nfunction currentTitle(metadata: Readonly<Record<string, unknown>> | undefined): string {\n return typeof metadata?.title === 'string' ? metadata.title.trim() : ''\n}\n\n/**\n * User turns carrying text — the prompts the user actually typed.\n *\n * @param turns - The session's turns.\n */\nexport function userPromptCount(turns: readonly SessionTurn[]): number {\n let n = 0\n for (const turn of turns) {\n if (turn.role === 'user' && !turn.injected && turn.content.some(b => b.type === 'text' && !b.systemNote && b.text.trim().length > 0))\n n++\n }\n return n\n}\n\n/** Text of the session's first user prompt. */\nfunction firstPromptText(turns: readonly SessionTurn[]): string {\n for (const turn of turns) {\n if (turn.role !== 'user')\n continue\n const text = turn.content.map(b => (b.type === 'text' && !b.systemNote ? b.text : '')).join(' ')\n if (text.trim())\n return text\n }\n return ''\n}\n\n/** Older desktop clients' clip: the first 40 characters, line breaks kept. */\nconst LEGACY_CLIP_MAX = 40\n\n/**\n * A title the client stamped at creation rather than one anyone chose: the\n * first prompt clipped exactly as the desktop app does (`titleFromText`, or\n * older clients' raw 40-character slice), or its \"New chat\" fallback. Such\n * titles carry no `titleSource`; auto-titling replaces them like an\n * untitled session. Only the exact clips count: a typed title that merely\n * starts the prompt (\"fix\", \"refactor auth\") is the user's.\n */\nexport function isPlaceholderTitle(title: string, turns: readonly SessionTurn[]): boolean {\n if (title === 'New chat')\n return true\n const prompt = firstPromptText(turns).trim()\n if (!prompt)\n return false\n // Older clients stored the clip with its line breaks: compare on one line.\n const line = oneLineTitle(title)\n const clip = titleFromText(prompt)\n return line === clip\n || (clip?.endsWith('…') === true && line === clip.slice(0, -1))\n || line === oneLineTitle(prompt.slice(0, LEGACY_CLIP_MAX))\n}\n\n/** What auto-titling should do next: leave the title, generate one, or check whether it is stale. */\nexport type AutoTitleAction = 'skip' | 'generate' | 'check'\n\n/**\n * What auto-titling should do for this session now:\n * - `generate`: no title yet and the first exchange happened.\n * - `check`: an auto title, with enough new prompts since the last check.\n * - `skip`: a user title, too few turns, or checked recently.\n *\n * @param turns - The session's turns.\n * @param metadata - Session metadata that holds the title, its source and the last check count.\n */\nexport function autoTitleAction(turns: readonly SessionTurn[], metadata: Readonly<Record<string, unknown>> | undefined): AutoTitleAction {\n const title = currentTitle(metadata)\n const source = metadata?.[TITLE_SOURCE_KEY]\n if (!title || (source === undefined && isPlaceholderTitle(title, turns)))\n return turns.length >= 2 ? 'generate' : 'skip'\n if (source !== 'auto')\n return 'skip'\n const checkedRaw = metadata?.[TITLE_CHECKED_KEY]\n const checked = typeof checkedRaw === 'number' ? checkedRaw : 0\n return userPromptCount(turns) - checked >= MIN_NEW_PROMPTS ? 'check' : 'skip'\n}\n\nconst STALE_INSTRUCTIONS = [\n 'The transcript is a coding session between a user and an agent; \"title\" is its current label.',\n 'Is the title no longer an accurate label for what the session as a whole is about?',\n 'Answer true only when the main topic has clearly moved on. A side question, a small follow-up, or extra detail on the same topic does not make the title stale.',\n].join(' ')\n\nexport type TitleStaleness = { stale: boolean } | { failure: { reason: string, message: string } }\n\n/**\n * Ask the decision model whether `title` is stale. A failed decision comes\n * back as `failure` (no answer is not a \"keep\": the caller retries later).\n *\n * @param decide - Decision function that asks the decision model.\n * @param title - The current title.\n * @param turns - The session's turns.\n */\nexport async function isTitleStale(decide: AgentDecide, title: string, turns: readonly SessionTurn[]): Promise<TitleStaleness> {\n const opening = turns.find(t => t.role === 'user' && renderTurnsForPrompt([t]).length > 0)\n const recent = renderTurnsForPrompt(turns.slice(-8))\n const r = await decide({\n input: {\n title,\n opening: opening ? renderTurnsForPrompt([opening]) : '',\n recent,\n },\n questions: { stale: { type: 'predicate', instructions: STALE_INSTRUCTIONS } },\n })\n if (!r.ok)\n return { failure: { reason: r.reason, message: r.message } }\n if (r.answers.stale.type !== 'predicate')\n return { failure: { reason: 'refusal', message: 'The decision model declined to answer' } }\n return { stale: r.answers.stale.probability > STALE_THRESHOLD }\n}\n","/**\n * Per-session execution cache — the session, not the agent, owns where its\n * tools run.\n *\n * Hosts rebuild a session's agent for many reasons that have nothing to do\n * with execution: a model or profile swap, a settings change, an MCP login,\n * an extension reload. An agent that spawned its own handle destroys it on\n * teardown, and for the process context that SIGTERMs every background task\n * the session started (`npm run dev` dies because the user switched models);\n * for a container or remote sandbox it would delete the machine. So hosts\n * build agents on a HOST-OWNED handle from this cache instead\n * (`AgentOptions.handle` — the agent never spawns or destroys it) and the\n * handle outlives every rebuild:\n *\n * 1. `acquire(sessionId, key, create)` → the session's context + a lazy\n * handle resolver. Reused while `key` (the context's configuration:\n * cwd, isolation policy, …) is unchanged; a changed key replaces the\n * context — the old one is destroyed, taking its tasks with it, because\n * they run under the old configuration (e.g. without a sandbox the user\n * just enabled).\n * 2. `adopt(sessionId, agent.hooks)` once the new agent's listeners are\n * wired: running tasks' exit/stall callbacks move to the new agent's\n * hook bus, and each is announced via `background:adopt` so the host's\n * running-work view re-registers it. Tasks that exited while no agent\n * owned them still reach the model through the agent's run-boundary\n * `listBackground` reconcile.\n * 3. `release*` when the SESSION is done with its execution: deleted,\n * fully disposed, idle-evicted without running tasks, host shutdown.\n *\n * Subagents are unaffected: the spawn tool hands children the real context,\n * so a non-shared child still spawns (and destroys) its own handle.\n */\nimport type { Agent } from '../agent'\nimport type { ExecutionContext, ExecutionHandle } from '../contexts/types'\nimport { createDebugLog } from '../debug-log'\n\nconst debugLog = createDebugLog('execution-cache')\n\n/**\n * A session's execution context and the lazy handle resolver bound to it, as returned by {@link SessionExecutionCache}.\n */\nexport interface SessionExecution {\n /** The session's execution context — pass as `AgentOptions.execution`. */\n context: ExecutionContext\n /** Host-owned handle, spawned on first use — pass as `AgentOptions.handle`. */\n handle: () => Promise<ExecutionHandle>\n}\n\n/**\n * Registry of host-owned execution contexts and handles, one per session. Created by {@link createSessionExecutionCache}.\n */\nexport interface SessionExecutionCache {\n /**\n * The session's execution, created with `create()` on first use and\n * reused while `key` matches. A different `key` replaces it (the previous\n * context is released in the background).\n */\n acquire: (sessionId: string, key: string, create: () => ExecutionContext) => SessionExecution\n /**\n * The session's current execution, whatever its key — for side work\n * (environment actions) that must run where the agent runs without\n * re-deriving the key (a mismatch would replace the live context).\n */\n peek: (sessionId: string) => SessionExecution | undefined\n /**\n * Hand the session's still-running background tasks to a newly built\n * agent: rebind their exit/stall callbacks to `hooks` and fire\n * `background:adopt` per task. Returns how many were adopted.\n */\n adopt: (sessionId: string, hooks: Agent['hooks']) => Promise<number>\n /** True while any background task on the session's handle is running. */\n hasRunningTasks: (sessionId: string) => Promise<boolean>\n /**\n * Destroy the session's handle (killing its tasks) and forget it. Resolves\n * once the destroy has finished — including one already started by an\n * earlier release or a key replacement — so a caller can rely on the\n * session's processes being gone (e.g. before removing its worktree).\n */\n release: (sessionId: string) => Promise<void>\n /**\n * {@link release} unless tasks are still running; resolves whether it\n * released. `stillIdle` is re-checked after the (async) task listing,\n * right before destroying — hosts pass \"no agent was rebuilt for this\n * session meanwhile\", so an eviction can't pull the handle out from\n * under a build that just acquired it.\n */\n releaseIfIdle: (sessionId: string, stillIdle?: () => boolean) => Promise<boolean>\n /** Release every session — host shutdown. */\n releaseAll: () => Promise<void>\n /** Sessions currently holding an execution. */\n sessions: () => string[]\n}\n\ninterface Entry {\n key: string\n context: ExecutionContext\n handle: ExecutionHandle | null\n spawning: Promise<ExecutionHandle> | null\n released: boolean\n}\n\n/**\n * Creates an empty {@link SessionExecutionCache}.\n * Handles are spawned lazily on first use and destroyed on release, key replacement, or host shutdown.\n *\n * @returns A cache keyed by session id.\n */\nexport function createSessionExecutionCache(): SessionExecutionCache {\n const entries = new Map<string, Entry>()\n /** Destroys in flight per session, so a later `release` can await them. */\n const destroying = new Map<string, Promise<void>>()\n\n /** Forget the session's entry and destroy it, tracking the in-flight destroy. */\n function detach(sessionId: string, entry: Entry): Promise<void> {\n if (entries.get(sessionId) === entry)\n entries.delete(sessionId)\n // Chain onto destroys already in flight (e.g. from a key replacement) so\n // awaiting this one also waits for every earlier destroy of the session.\n const pending = Promise.all([destroying.get(sessionId), destroyEntry(entry)]).then(() => {})\n destroying.set(sessionId, pending)\n void pending.finally(() => {\n if (destroying.get(sessionId) === pending)\n destroying.delete(sessionId)\n })\n return pending\n }\n\n function resolveHandle(entry: Entry): Promise<ExecutionHandle> {\n // A stale agent built on a replaced/released entry must not mint a\n // handle nobody will ever destroy.\n if (entry.released)\n return Promise.reject(new Error('session execution was released'))\n if (entry.handle)\n return Promise.resolve(entry.handle)\n // A release racing this spawn is handled by `destroyEntry`, which\n // awaits the in-flight spawn and destroys what it yields.\n entry.spawning ??= entry.context.spawn().then(\n (handle) => {\n entry.handle = handle\n entry.spawning = null\n return handle\n },\n (err: unknown) => {\n entry.spawning = null\n throw err\n },\n )\n return entry.spawning\n }\n\n async function destroyEntry(entry: Entry): Promise<void> {\n if (entry.released)\n return\n entry.released = true\n const handle = entry.handle ?? await entry.spawning?.catch(() => null) ?? null\n if (!handle)\n return\n entry.handle = null\n try {\n await entry.context.destroy(handle)\n }\n catch (err) {\n debugLog('destroy failed', err)\n }\n }\n\n async function runningTasks(entry: Entry | undefined) {\n if (!entry?.handle || !entry.context.listBackground)\n return []\n try {\n return (await entry.context.listBackground(entry.handle)).filter(task => task.status === 'running')\n }\n catch (err) {\n debugLog('listBackground failed', err)\n return []\n }\n }\n\n const cache: SessionExecutionCache = {\n acquire(sessionId, key, create) {\n let entry = entries.get(sessionId)\n if (entry && entry.key !== key) {\n void detach(sessionId, entry)\n entry = undefined\n }\n if (!entry) {\n entry = { key, context: create(), handle: null, spawning: null, released: false }\n entries.set(sessionId, entry)\n }\n const bound = entry\n return { context: bound.context, handle: () => resolveHandle(bound) }\n },\n\n peek(sessionId) {\n const entry = entries.get(sessionId)\n return entry ? { context: entry.context, handle: () => resolveHandle(entry) } : undefined\n },\n\n async adopt(sessionId, hooks) {\n const entry = entries.get(sessionId)\n const handle = entry?.handle\n if (!entry || !handle || !entry.context.reassignBackgroundTasks)\n return 0\n let adopted\n try {\n adopted = await entry.context.reassignBackgroundTasks(\n handle,\n handle,\n (info) => {\n Promise.resolve(hooks.callHook('background:exit', info)).catch(err => debugLog('background:exit hook rejected', err))\n },\n (info) => {\n Promise.resolve(hooks.callHook('background:stall', info)).catch(err => debugLog('background:stall hook rejected', err))\n },\n )\n }\n catch (err) {\n debugLog('adopt failed', err)\n return 0\n }\n for (const task of adopted) {\n await Promise.resolve(hooks.callHook('background:adopt', {\n taskId: task.taskId,\n pid: task.pid,\n command: task.command,\n cwd: task.cwd,\n outputPath: task.outputPath,\n startedAt: task.startedAt,\n })).catch(err => debugLog('background:adopt hook rejected', err))\n }\n return adopted.length\n },\n\n async hasRunningTasks(sessionId) {\n return (await runningTasks(entries.get(sessionId))).length > 0\n },\n\n async release(sessionId) {\n const entry = entries.get(sessionId)\n if (!entry)\n return destroying.get(sessionId)\n await detach(sessionId, entry)\n },\n\n async releaseIfIdle(sessionId, stillIdle) {\n const entry = entries.get(sessionId)\n if (!entry)\n return true\n if ((await runningTasks(entry)).length > 0)\n return false\n // Re-check after the await: the entry may have been replaced, or the\n // host may have started using it again.\n if (entries.get(sessionId) !== entry || (stillIdle && !stillIdle()))\n return false\n await cache.release(sessionId)\n return true\n },\n\n sessions: () => [...entries.keys()],\n\n async releaseAll() {\n const all = [...entries]\n await Promise.all([...all.map(([sessionId, entry]) => detach(sessionId, entry)), ...destroying.values()])\n },\n }\n return cache\n}\n","/**\n * A model's Test in the settings: one tiny prompt with this host's\n * credentials, to check the model answers. The chat models' counterpart of\n * `testDecisionModel`, with the same result shape.\n */\nimport type { Provider } from '../providers'\nimport type { ProviderDescriptor } from './providers'\nimport { parseModelSetting } from './model-setting'\n\n/** Outcome of {@link testModel}. */\nexport interface ModelTestResult {\n /** True when the model answered. */\n ok: boolean\n /** Provider the probe ran on. */\n provider?: string\n /** Model the probe used. */\n model?: string\n /** Time the probe took, in ms. */\n durationMs: number\n /** Why it failed: `unavailable` (no such provider, no credentials), `timeout` or `error`. */\n reason?: string\n /** Failure detail. */\n message?: string\n}\n\nconst PROBE = 'Reply with the single word OK.'\nconst DEFAULT_TIMEOUT_MS = 30_000\n\n/**\n * Send one tiny prompt to check a model answers with this host's\n * credentials. `model` (`\"providerKey[@account]/modelId\"`) is tested\n * exactly, on its account, with no fallback, so a broken pick is reported as\n * broken. Never throws.\n *\n * @param input - What to test, and with what.\n * @param input.providers - This host's provider descriptors.\n * @param input.buildProvider - Builds a provider on an account; throws without credentials.\n * @param input.model - The model to test, as `\"providerKey[@account]/modelId\"`.\n * @param input.timeoutMs - How long to wait for the answer. Default: 30 000.\n */\nexport async function testModel(input: {\n providers: Readonly<Record<string, ProviderDescriptor>>\n buildProvider: (providerKey: string, account?: string) => Provider\n model: string\n timeoutMs?: number\n}): Promise<ModelTestResult> {\n const started = Date.now()\n const pick = parseModelSetting(input.model)\n const fail = (reason: string, message: string): ModelTestResult => ({\n ok: false,\n ...(pick ? { provider: pick.providerKey, model: pick.modelId } : {}),\n durationMs: Date.now() - started,\n reason,\n message,\n })\n if (!pick)\n return fail('unavailable', `Not a model: \"${input.model}\"`)\n if (!input.providers[pick.providerKey])\n return fail('unavailable', `Provider \"${pick.providerKey}\" is not configured`)\n\n let provider: Provider\n try {\n provider = input.buildProvider(pick.providerKey, pick.account)\n }\n catch (err) {\n return fail('unavailable', err instanceof Error ? err.message : String(err))\n }\n\n const timeoutMs = input.timeoutMs ?? DEFAULT_TIMEOUT_MS\n const controller = new AbortController()\n const timer = setTimeout(() => controller.abort(), timeoutMs)\n try {\n await provider.stream(\n { model: pick.modelId, system: PROBE, tools: [], messages: [provider.userMessage(PROBE)], maxTokens: 256, signal: controller.signal },\n { onText: () => {} },\n )\n return { ok: true, provider: pick.providerKey, model: pick.modelId, durationMs: Date.now() - started }\n }\n catch (err) {\n return controller.signal.aborted\n ? fail('timeout', `No answer within ${Math.round(timeoutMs / 1000)}s`)\n : fail('error', err instanceof Error ? err.message : String(err))\n }\n finally {\n clearTimeout(timer)\n }\n}\n"],"mappings":"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"}