zidane 9.1.1 → 9.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{acp-Di9WvMDF.js → acp-Cl4gCZN6.js} +7 -7
- package/dist/{acp-Di9WvMDF.js.map → acp-Cl4gCZN6.js.map} +1 -1
- package/dist/acp-cli.js +5 -5
- package/dist/acp.d.ts +2 -2
- package/dist/acp.js +1 -1
- package/dist/{agent-DJhpTS-B.js → agent-Dh_XZ7fj.js} +74 -19
- package/dist/agent-Dh_XZ7fj.js.map +1 -0
- package/dist/agent.d.ts +1 -1
- package/dist/agent.js +1 -1
- package/dist/{auth-store-port-DFgf22oo.js → auth-store-port-DBl2JUC6.js} +2 -2
- package/dist/{auth-store-port-DFgf22oo.js.map → auth-store-port-DBl2JUC6.js.map} +1 -1
- package/dist/{auto-update-restart-BFB7Ek87.js → auto-update-restart-CF_kjcJd.js} +277 -41
- package/dist/auto-update-restart-CF_kjcJd.js.map +1 -0
- package/dist/{browser-BcoCCB_n.js → browser-BkdeKnxO.js} +56 -2
- package/dist/browser-BkdeKnxO.js.map +1 -0
- package/dist/{browser-entry-ypi8CyE-.d.ts → browser-entry-BCwruqNa.d.ts} +10 -10
- package/dist/{browser-entry-ypi8CyE-.d.ts.map → browser-entry-BCwruqNa.d.ts.map} +1 -1
- package/dist/channels.d.ts +5 -5
- package/dist/chat/browser.d.ts +4 -4
- package/dist/chat/browser.js +6 -6
- package/dist/chat/pure.d.ts +5 -5
- package/dist/chat/pure.js +8 -8
- package/dist/chat.d.ts +5 -5
- package/dist/chat.js +55 -55
- package/dist/chat.js.map +1 -1
- package/dist/{cli-shared-CabruH8Z.js → cli-shared-DIV9c5Nr.js} +4 -4
- package/dist/{cli-shared-CabruH8Z.js.map → cli-shared-DIV9c5Nr.js.map} +1 -1
- package/dist/cli-style-DBEm6CyT.js +105 -0
- package/dist/cli-style-DBEm6CyT.js.map +1 -0
- package/dist/{client-core-CIixwznM.d.ts → client-core-C_4m_oL6.d.ts} +4 -4
- package/dist/{client-core-CIixwznM.d.ts.map → client-core-C_4m_oL6.d.ts.map} +1 -1
- package/dist/{client-CCaIXWue.d.ts → client-ygTASGis.d.ts} +2 -2
- package/dist/{client-CCaIXWue.d.ts.map → client-ygTASGis.d.ts.map} +1 -1
- package/dist/codemode-worker.js +8 -5
- package/dist/codemode-worker.js.map +1 -1
- package/dist/codemode.d.ts +16 -0
- package/dist/codemode.d.ts.map +1 -1
- package/dist/codemode.js +2 -2
- package/dist/{connection-loop-QmWL-fJf.d.ts → connection-loop-D_pRONCr.d.ts} +4 -4
- package/dist/{connection-loop-QmWL-fJf.d.ts.map → connection-loop-D_pRONCr.d.ts.map} +1 -1
- package/dist/contexts/daytona.d.ts +3 -3
- package/dist/contexts/docker.d.ts +1 -1
- package/dist/contexts/e2b.d.ts +2 -2
- package/dist/contexts/sandbox.d.ts +1 -1
- package/dist/{contexts-C72b0mMa.js → contexts-DLZTSRHU.js} +17 -106
- package/dist/contexts-DLZTSRHU.js.map +1 -0
- package/dist/contexts.d.ts +4 -4
- package/dist/contexts.js +2 -2
- package/dist/{credentials-wwETW8rI.js → credentials-D71duF64.js} +2 -2
- package/dist/{credentials-wwETW8rI.js.map → credentials-D71duF64.js.map} +1 -1
- package/dist/{cwd-picker-CcekODCt.js → cwd-picker-BsHA6HKv.js} +4 -4
- package/dist/{cwd-picker-CcekODCt.js.map → cwd-picker-BsHA6HKv.js.map} +1 -1
- package/dist/daemon-cli.js +4 -3
- package/dist/daemon-cli.js.map +1 -1
- package/dist/{decisions-CXmK0nz9.js → decisions-hsw5XS2g.js} +3 -1
- package/dist/decisions-hsw5XS2g.js.map +1 -0
- package/dist/{discovery-DycXFxdA.js → discovery-DD4wPWKS.js} +4 -4
- package/dist/{discovery-DycXFxdA.js.map → discovery-DD4wPWKS.js.map} +1 -1
- package/dist/durable/sqlite.d.ts +2 -2
- package/dist/durable.d.ts +1 -1
- package/dist/{engine-VQ86XGdG.js → engine-BDmtgr2O.js} +6 -6
- package/dist/{engine-VQ86XGdG.js.map → engine-BDmtgr2O.js.map} +1 -1
- package/dist/{eval-Bod0I1W_.js → eval-CfGUDAkN.js} +3 -3
- package/dist/{eval-Bod0I1W_.js.map → eval-CfGUDAkN.js.map} +1 -1
- package/dist/eval.d.ts +1 -1
- package/dist/eval.js +1 -1
- package/dist/{executor-C7RQrkEq.js → executor-EWOhLyd9.js} +4 -4
- package/dist/{executor-C7RQrkEq.js.map → executor-EWOhLyd9.js.map} +1 -1
- package/dist/{extension-mark-BqKdEZwF.js → extension-mark-_rhkDWL5.js} +9 -6
- package/dist/extension-mark-_rhkDWL5.js.map +1 -0
- package/dist/extensions/testing.d.ts +1 -1
- package/dist/extensions/testing.js +2 -2
- package/dist/{extensions-BCx4QuGB.js → extensions-DklXPjiq.js} +8 -8
- package/dist/{extensions-BCx4QuGB.js.map → extensions-DklXPjiq.js.map} +1 -1
- package/dist/{extensions-client-D8tEAcMw.d.ts → extensions-client-D4Q2XRKV.d.ts} +31 -5
- package/dist/extensions-client-D4Q2XRKV.d.ts.map +1 -0
- package/dist/extensions.d.ts +1 -1
- package/dist/extensions.js +6 -6
- package/dist/{fold-rules-DvLK8pbf.d.ts → fold-rules-1DpDwG9h.d.ts} +2 -2
- package/dist/{fold-rules-DvLK8pbf.d.ts.map → fold-rules-1DpDwG9h.d.ts.map} +1 -1
- package/dist/{google-Crva32sk.js → google-BtMW9KzL.js} +2 -2
- package/dist/{google-Crva32sk.js.map → google-BtMW9KzL.js.map} +1 -1
- package/dist/{headless-CCm-jLLT.js → headless-CQm19BL0.js} +4 -4
- package/dist/{headless-CCm-jLLT.js.map → headless-CQm19BL0.js.map} +1 -1
- package/dist/headless.d.ts +1 -1
- package/dist/headless.js +1 -1
- package/dist/history-worker.js +1 -1
- package/dist/{host-BHMs9jsN.js → host-DEoEzQal.js} +13 -4
- package/dist/host-DEoEzQal.js.map +1 -0
- package/dist/{host-scoped-settings-CrbcqOkP.js → host-scoped-settings-WBYaCork.js} +2 -2
- package/dist/{host-scoped-settings-CrbcqOkP.js.map → host-scoped-settings-WBYaCork.js.map} +1 -1
- package/dist/{host-signals-Jsjd_-4g.js → host-signals-91zyko_i.js} +5 -5
- package/dist/{host-signals-Jsjd_-4g.js.map → host-signals-91zyko_i.js.map} +1 -1
- package/dist/{host-wO_6GW0y.js → host-uP3t08w8.js} +276 -103
- package/dist/host-uP3t08w8.js.map +1 -0
- package/dist/{host-version-CNAr9pov.js → host-version-_RHlAB29.js} +2 -2
- package/dist/{host-version-CNAr9pov.js.map → host-version-_RHlAB29.js.map} +1 -1
- package/dist/{index-C6nnKu32.d.ts → index-B0PLo9jn.d.ts} +4 -4
- package/dist/{index-C6nnKu32.d.ts.map → index-B0PLo9jn.d.ts.map} +1 -1
- package/dist/{index-Djnc8Peh.d.ts → index-De4x-s76.d.ts} +14 -14
- package/dist/{index-Djnc8Peh.d.ts.map → index-De4x-s76.d.ts.map} +1 -1
- package/dist/{index-CGbOWa7y.d.ts → index-WlE93Q6d.d.ts} +411 -3
- package/dist/index-WlE93Q6d.d.ts.map +1 -0
- package/dist/{index-CCf_WeTQ.d.ts → index-f-XWIoYo.d.ts} +2 -2
- package/dist/{index-CCf_WeTQ.d.ts.map → index-f-XWIoYo.d.ts.map} +1 -1
- package/dist/index.d.ts +5 -5
- package/dist/index.js +17 -17
- package/dist/ipc/browser-types.d.ts +304 -7
- package/dist/ipc/browser-types.d.ts.map +1 -1
- package/dist/ipc/browser.d.ts +4 -4
- package/dist/ipc/browser.js +2 -2
- package/dist/ipc/remote.d.ts +6 -6
- package/dist/ipc/remote.js +3 -3
- package/dist/{ipc-BzWhIybr.js → ipc-CatnsVEd.js} +4 -4
- package/dist/{ipc-BzWhIybr.js.map → ipc-CatnsVEd.js.map} +1 -1
- package/dist/ipc.d.ts +10 -10
- package/dist/ipc.js +8 -8
- package/dist/{json-Dj-YATkZ.js → json-BjClr43H.js} +2 -20
- package/dist/json-BjClr43H.js.map +1 -0
- package/dist/{links-CJG_rFvf.js → links-a8C9eby4.js} +9 -9
- package/dist/{links-CJG_rFvf.js.map → links-a8C9eby4.js.map} +1 -1
- package/dist/{loaded-entry-DTURJo_R.js → loaded-entry-D6jzt5HQ.js} +2 -2
- package/dist/{loaded-entry-DTURJo_R.js.map → loaded-entry-D6jzt5HQ.js.map} +1 -1
- package/dist/mcp.d.ts +1 -1
- package/dist/{media-type-BtA0cbfI.d.ts → media-type-ZSmAGHvg.d.ts} +188 -7
- package/dist/media-type-ZSmAGHvg.d.ts.map +1 -0
- package/dist/{memory-DuVGrzbl.js → memory-CJy52DNi.js} +132 -31
- package/dist/memory-CJy52DNi.js.map +1 -0
- package/dist/model-test-CAtJ76kM.js.map +1 -1
- package/dist/{move-session-kylExJ5H.js → move-session-D7ILECs-.js} +4 -4
- package/dist/{move-session-kylExJ5H.js.map → move-session-D7ILECs-.js.map} +1 -1
- package/dist/{nested-call-record-DlzPeTB7.js → nested-call-record-OayszZBQ.js} +4 -3
- package/dist/nested-call-record-OayszZBQ.js.map +1 -0
- package/dist/output/stream-json.d.ts +1 -1
- package/dist/output/terminal.d.ts +1 -1
- package/dist/{path-display-COVV71Yu.js → path-display-CAWz-sMq.js} +3 -3
- package/dist/{path-display-COVV71Yu.js.map → path-display-CAWz-sMq.js.map} +1 -1
- package/dist/plugins.d.ts +1 -1
- package/dist/{prelude-CVKoUzV8.js → prelude-BwiSR1uA.js} +16 -5
- package/dist/prelude-BwiSR1uA.js.map +1 -0
- package/dist/{presets-CBt6rFfS.js → presets-CMY43nUP.js} +3 -3
- package/dist/{presets-CBt6rFfS.js.map → presets-CMY43nUP.js.map} +1 -1
- package/dist/presets.d.ts +1 -1
- package/dist/presets.js +1 -1
- package/dist/{protocol-CEVqBO-q.d.ts → protocol-C2lqKuMd.d.ts} +50 -7
- package/dist/protocol-C2lqKuMd.d.ts.map +1 -0
- package/dist/protocol-DOGyHgLQ.js.map +1 -1
- package/dist/protocol-NSllElLw.js.map +1 -1
- package/dist/providers/anthropic.d.ts +1 -1
- package/dist/providers/arcee.d.ts +1 -1
- package/dist/providers/baseten.d.ts +1 -1
- package/dist/providers/cerebras.d.ts +1 -1
- package/dist/providers/google.d.ts +1 -1
- package/dist/providers/google.js +1 -1
- package/dist/providers/local.d.ts +1 -1
- package/dist/providers/neuralwatt.d.ts +1 -1
- package/dist/providers/openai-compat.d.ts +1 -1
- package/dist/providers/openai.d.ts +1 -1
- package/dist/providers/openrouter.d.ts +1 -1
- package/dist/providers/xai.d.ts +1 -1
- package/dist/{providers-Z0TaBp1O.js → providers-zVRxiJDv.js} +5 -5
- package/dist/providers-zVRxiJDv.js.map +1 -0
- package/dist/providers.d.ts +1 -1
- package/dist/providers.js +1 -1
- package/dist/{readonly-db-D7Z-k0pQ.js → readonly-db-DrbT8tX6.js} +2 -2
- package/dist/{readonly-db-D7Z-k0pQ.js.map → readonly-db-DrbT8tX6.js.map} +1 -1
- package/dist/{registry-BKjeBnoy.js → registry-CBoYDIND.js} +2 -2
- package/dist/{registry-BKjeBnoy.js.map → registry-CBoYDIND.js.map} +1 -1
- package/dist/{registry-B6Ww76iW.js → registry-CQOU5jvF.js} +2 -2
- package/dist/{registry-B6Ww76iW.js.map → registry-CQOU5jvF.js.map} +1 -1
- package/dist/{registry-B9INJwJR.js → registry-YUUqbYGr.js} +324 -39
- package/dist/registry-YUUqbYGr.js.map +1 -0
- package/dist/{remote-hosts-BMWoHCWO.js → remote-hosts-CT8Q95dR.js} +4 -4
- package/dist/{remote-hosts-BMWoHCWO.js.map → remote-hosts-CT8Q95dR.js.map} +1 -1
- package/dist/{repo-url-D4Dr8NfZ.js → repo-url-DLjjuVk_.js} +169 -2
- package/dist/repo-url-DLjjuVk_.js.map +1 -0
- package/dist/restate.d.ts +2 -2
- package/dist/{sandbox-BwiPGKF7.d.ts → sandbox-B4OW6oNx.d.ts} +2 -2
- package/dist/{sandbox-BwiPGKF7.d.ts.map → sandbox-B4OW6oNx.d.ts.map} +1 -1
- package/dist/script-progress-CrZ507Qq.js +900 -0
- package/dist/script-progress-CrZ507Qq.js.map +1 -0
- package/dist/session/sqlite.d.ts +1 -1
- package/dist/session.d.ts +1 -1
- package/dist/{setting-BQBSyUq0.js → setting-_jh2OtiL.js} +6 -6
- package/dist/{setting-BQBSyUq0.js.map → setting-_jh2OtiL.js.map} +1 -1
- package/dist/{settings-catalog-4IU2i_jG.js → settings-catalog-DEZlXP51.js} +14 -2
- package/dist/settings-catalog-DEZlXP51.js.map +1 -0
- package/dist/{settings-keys-iI_Y-7MC.js → settings-keys-BT0_oKjO.js} +3 -3
- package/dist/{settings-keys-iI_Y-7MC.js.map → settings-keys-BT0_oKjO.js.map} +1 -1
- package/dist/{skills-CFXRItJ6.js → skills-CEbosUlM.js} +2 -2
- package/dist/{skills-CFXRItJ6.js.map → skills-CEbosUlM.js.map} +1 -1
- package/dist/skills.d.ts +1 -1
- package/dist/skills.js +1 -1
- package/dist/{source-Bl1Z501r.js → source-3ZnEPOSa.js} +3 -2
- package/dist/source-3ZnEPOSa.js.map +1 -0
- package/dist/{sqlite-runtime-BZ2L4RaG.d.ts → sqlite-runtime-CS_LBaAN.d.ts} +2 -2
- package/dist/sqlite-runtime-CS_LBaAN.d.ts.map +1 -0
- package/dist/{src-BY_ND2xn.js → src-BmTXDta8.js} +2 -2
- package/dist/{src-BY_ND2xn.js.map → src-BmTXDta8.js.map} +1 -1
- package/dist/{store-C6gI7FfZ.js → store-ByCDaKgJ.js} +14 -3
- package/dist/store-ByCDaKgJ.js.map +1 -0
- package/dist/{system-freeze-m5o1sGIM.js → system-freeze-DxJ-lRhu.js} +3 -3
- package/dist/{system-freeze-m5o1sGIM.js.map → system-freeze-DxJ-lRhu.js.map} +1 -1
- package/dist/{todo-edits-CbukbjyV.js → todo-edits-Dyj32ds6.js} +4 -3
- package/dist/todo-edits-Dyj32ds6.js.map +1 -0
- package/dist/tools/fetch-url.d.ts +1 -1
- package/dist/tools/web-search.d.ts +1 -1
- package/dist/{tools-CVTGhggj.js → tools-BsrGQ8K4.js} +2 -2
- package/dist/{tools-CVTGhggj.js.map → tools-BsrGQ8K4.js.map} +1 -1
- package/dist/tools.d.ts +1 -1
- package/dist/tools.js +3 -3
- package/dist/{transcript-anchors-CiSi_UBn.js → transcript-anchors-DMjvyUjk.js} +3 -3
- package/dist/{transcript-anchors-CiSi_UBn.js.map → transcript-anchors-DMjvyUjk.js.map} +1 -1
- package/dist/{transcript-reconcile-jbSBjLq2.js → transcript-reconcile-DpQO85wl.js} +3 -3
- package/dist/{transcript-reconcile-jbSBjLq2.js.map → transcript-reconcile-DpQO85wl.js.map} +1 -1
- package/dist/tui.d.ts +9 -9
- package/dist/tui.d.ts.map +1 -1
- package/dist/tui.js +289 -38
- package/dist/tui.js.map +1 -1
- package/dist/{types-bDQagoiL.d.ts → types-2HrgolQz.d.ts} +20 -8
- package/dist/types-2HrgolQz.d.ts.map +1 -0
- package/dist/types-4q1bFG53.js +122 -0
- package/dist/types-4q1bFG53.js.map +1 -0
- package/dist/{types-DnRd9_cA.d.ts → types-SXqk6hLD.d.ts} +3 -3
- package/dist/{types-DnRd9_cA.d.ts.map → types-SXqk6hLD.d.ts.map} +1 -1
- package/dist/types-myxheQkw.js.map +1 -1
- package/dist/types.d.ts +3 -3
- package/dist/{usage-summary-BfcXjiSM.js → usage-summary-BM7B3JdI.js} +2 -2
- package/dist/{usage-summary-BfcXjiSM.js.map → usage-summary-BM7B3JdI.js.map} +1 -1
- package/dist/{version-DqqqvHDP.js → version-DwQI12WA.js} +2 -2
- package/dist/{version-DqqqvHDP.js.map → version-DwQI12WA.js.map} +1 -1
- package/dist/{wait-task-BX4NcOOm.js → wait-task-BiI5UaZQ.js} +13 -3
- package/dist/wait-task-BiI5UaZQ.js.map +1 -0
- package/dist/{wire-CLm2rMK0.js → wire-Dxwxb7QQ.js} +2 -2
- package/dist/{wire-CLm2rMK0.js.map → wire-Dxwxb7QQ.js.map} +1 -1
- package/dist/{write-file-0E6TUJ9A.js → write-file-Dquu7MzB.js} +5 -3
- package/dist/write-file-Dquu7MzB.js.map +1 -0
- package/dist/{ws-DfGGYQ4W.js → ws-CA3o2nie.js} +4 -4
- package/dist/{ws-DfGGYQ4W.js.map → ws-CA3o2nie.js.map} +1 -1
- package/dist/{ws-client-CFMbhsNq.js → ws-client-C0iPAAzc.js} +2 -2
- package/dist/{ws-client-CFMbhsNq.js.map → ws-client-C0iPAAzc.js.map} +1 -1
- package/docs/CHAT.md +55 -0
- package/docs/CODEMODE.md +37 -0
- package/docs/EXTENSIONS.md +8 -6
- package/docs/RELEASE-NOTES-NEXT.md +48 -1
- package/docs/RELEASE.md +9 -1
- package/package.json +2 -3
- package/dist/agent-DJhpTS-B.js.map +0 -1
- package/dist/auto-update-restart-BFB7Ek87.js.map +0 -1
- package/dist/browser-BcoCCB_n.js.map +0 -1
- package/dist/contexts-C72b0mMa.js.map +0 -1
- package/dist/decisions-CXmK0nz9.js.map +0 -1
- package/dist/extension-mark-BqKdEZwF.js.map +0 -1
- package/dist/extensions-client-D8tEAcMw.d.ts.map +0 -1
- package/dist/host-BHMs9jsN.js.map +0 -1
- package/dist/host-wO_6GW0y.js.map +0 -1
- package/dist/index-CGbOWa7y.d.ts.map +0 -1
- package/dist/json-Dj-YATkZ.js.map +0 -1
- package/dist/media-type-BtA0cbfI.d.ts.map +0 -1
- package/dist/memory-DuVGrzbl.js.map +0 -1
- package/dist/nested-call-events-BYk8XvGX.js +0 -182
- package/dist/nested-call-events-BYk8XvGX.js.map +0 -1
- package/dist/nested-call-record-DlzPeTB7.js.map +0 -1
- package/dist/prelude-CVKoUzV8.js.map +0 -1
- package/dist/protocol-CEVqBO-q.d.ts.map +0 -1
- package/dist/providers-Z0TaBp1O.js.map +0 -1
- package/dist/registry-B9INJwJR.js.map +0 -1
- package/dist/repo-url-D4Dr8NfZ.js.map +0 -1
- package/dist/settings-catalog-4IU2i_jG.js.map +0 -1
- package/dist/source-Bl1Z501r.js.map +0 -1
- package/dist/sqlite-runtime-BZ2L4RaG.d.ts.map +0 -1
- package/dist/store-C6gI7FfZ.js.map +0 -1
- package/dist/todo-edits-CbukbjyV.js.map +0 -1
- package/dist/types-DTMEhKJO.js +0 -14
- package/dist/types-DTMEhKJO.js.map +0 -1
- package/dist/types-bDQagoiL.d.ts.map +0 -1
- package/dist/wait-task-BX4NcOOm.js.map +0 -1
- package/dist/write-file-0E6TUJ9A.js.map +0 -1
- package/scripts/eval.ts +0 -179
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"usage-summary-BfcXjiSM.js","names":[],"sources":["../src/chat/feature-gate.ts","../src/chat/footer-chips.ts","../src/chat/completion-chats.ts","../src/chat/model-lineup.ts","../src/chat/text-model.ts","../src/chat/run-estimate.ts","../src/chat/mcp-test-outcome.ts","../src/chat/mcp-allowlist.ts","../src/chat/onboarding/native-extensions.ts","../src/chat/extension-sources.ts","../src/chat/onboarding/brands.ts","../src/chat/onboarding/features.ts","../src/chat/onboarding/presets.ts","../src/chat/onboarding/steps.ts","../src/chat/onboarding/flow.ts","../src/chat/onboarding/result.ts","../src/chat/usage-summary.ts"],"sourcesContent":["/**\n * Opt-in feature gates: an experimental feature is one boolean setting\n * (default off) plus a kill-switch env var that wins over it.\n *\n * The kill switch exists so a bug in the gated path can never lock the user\n * out: the ungated path is always one flag away, even when the settings UI\n * itself is unreachable. Each gate names its flag (`--no-daemon`,\n * `--no-remote`); the CLI's {@link applyKillSwitchFlags} exports the env var\n * for the process, so child processes inherit the switch too.\n *\n * Node-free on purpose (re-exported from `zidane/chat/pure`): the TUI and the\n * GUI main process share one definition per gate, so both read the same\n * setting and honor the same kill switch.\n */\n\ntype Env = Record<string, string | undefined>\n\n/** Options for {@link defineFeatureGate}. */\nexport interface FeatureGateOptions<K extends string> {\n /** Settings key holding the opt-in. Only a literal `true` enables. */\n setting: K\n /** Env var that disables the feature when set to `'1'`, whatever the setting says. */\n killSwitchEnv: string\n /** The `zidane` flag that sets {@link killSwitchEnv} for the launch (`--no-…`). */\n flag: string\n}\n\n/** A feature gate created by {@link defineFeatureGate}. */\nexport interface FeatureGate<K extends string> {\n /** Settings key holding the opt-in. */\n readonly setting: K\n /** Env var that disables the feature when set to `'1'`. */\n readonly killSwitchEnv: string\n /** CLI flag that sets the kill-switch env var for the launch. */\n readonly flag: string\n /** The kill-switch env var is set to `'1'`. */\n isKilled: (env?: Env) => boolean\n /** Setting is `true` and the kill switch is not engaged. */\n isEnabled: (settings: Partial<Record<K, unknown>>, env?: Env) => boolean\n}\n\n/** `process.env` when there is one; a renderer has no `process`. */\nfunction ambientEnv(): Env {\n return (globalThis as { process?: { env?: Env } }).process?.env ?? {}\n}\n\n/**\n * Creates a {@link FeatureGate} from its setting key, kill-switch env var, and CLI flag.\n *\n * @param options - Setting key, kill-switch env var, and flag name.\n * @example\n * ```ts\n * import { defineFeatureGate } from 'zidane/chat'\n *\n * const gate = defineFeatureGate({ setting: 'daemon', killSwitchEnv: 'ZIDANE_NO_DAEMON', flag: '--no-daemon' })\n * gate.isEnabled({ daemon: true }, {}) // true\n * gate.isEnabled({ daemon: true }, { ZIDANE_NO_DAEMON: '1' }) // false\n * ```\n */\nexport function defineFeatureGate<K extends string>({ setting, killSwitchEnv, flag }: FeatureGateOptions<K>): FeatureGate<K> {\n const isKilled = (env: Env = ambientEnv()): boolean => env[killSwitchEnv] === '1'\n return {\n setting,\n killSwitchEnv,\n flag,\n isKilled,\n isEnabled: (settings, env) => settings[setting] === true && !isKilled(env),\n }\n}\n\n/**\n * The shared daemon runtime. `ZIDANE_NO_DAEMON=1` is set by\n * `zidane --no-daemon`.\n */\nexport const daemonGate = defineFeatureGate({ setting: 'useDaemon', killSwitchEnv: 'ZIDANE_NO_DAEMON', flag: '--no-daemon' })\n\n/**\n * Remote hosts (zidane-server). The setting (\"Remote hosts\")\n * gates the saved servers: the Hosts settings, the per-host session groups\n * and picking a host for a new session. Connecting through an explicit `--host` /\n * `ZIDANE_HOST_URL` is its own opt-in, but `ZIDANE_NO_REMOTE=1` still wins\n * over it (`src/tui/host-target.ts`). `ZIDANE_NO_REMOTE=1` is set by\n * `zidane --no-remote`.\n */\nexport const remoteHostsGate = defineFeatureGate({ setting: 'remoteHosts', killSwitchEnv: 'ZIDANE_NO_REMOTE', flag: '--no-remote' })\n\n/** Every gate, so a client can report which kill switches are engaged. */\nexport const FEATURE_GATES: readonly FeatureGate<string>[] = [daemonGate, remoteHostsGate]\n\n/**\n * The kill-switch env vars engaged in `env`, sorted. The GUI main process\n * hands this to its renderer (which has no `process.env`) so settings rows\n * can say why they are inert.\n *\n * @param env - Environment to read. Defaults to `process.env`, or an empty object when there is none.\n */\nexport function engagedKillSwitches(env: Env = ambientEnv()): string[] {\n return FEATURE_GATES.filter(g => g.isKilled(env)).map(g => g.killSwitchEnv).sort()\n}\n\n/**\n * Engages the kill switch of every gate whose flag is in `args` (sets its env\n * var to `'1'` in `env`, so child processes inherit it) and returns `args`\n * without those flags.\n */\nexport function applyKillSwitchFlags(args: readonly string[], env: Env): string[] {\n const rest: string[] = []\n for (const arg of args) {\n const gate = FEATURE_GATES.find(g => g.flag === arg)\n if (gate)\n env[gate.killSwitchEnv] = '1'\n else\n rest.push(arg)\n }\n return rest\n}\n\n/**\n * `name` is set to `'1'` in `env`: the kill-switch test, for a row that only knows the variable.\n *\n * @param name - Kill-switch env var name.\n * @param env - Environment to read. Defaults to `process.env`, or an empty object when there is none.\n */\nexport function isKillSwitchEngaged(name: string, env: Env = ambientEnv()): boolean {\n return env[name] === '1'\n}\n","/**\n * Presentation logic for the transient footer status chips, shared by every\n * surface: the prompt-cache chip, the provider-retry indicator, the live run\n * readout and the compaction readout.\n *\n * The cache and retry chips both report an invisible condition with no error attached — a turn re-billing\n * a prefix the provider should have served from cache, or a stalled stream being\n * retried — and both carry real judgment in their wording: which dimensions\n * changed, when a figure is an estimate, when there is no figure to show at\n * all, whose stream is stuck. That is why they live here instead of being\n * re-derived per surface. A GUI chip that rounded differently, or printed\n * `$0.00` where the TUI printed nothing, would be reporting different facts\n * about the same event.\n *\n * Node-free, so it is re-exported from `zidane/chat/pure` for the Electron\n * renderer.\n */\nimport type { CacheBreakInfo } from '../cache-telemetry'\nimport { fmtTokens, formatDuration, formatElapsed } from './format'\n\n/**\n * `warn` = a real cache break (a named dimension changed, so it was\n * avoidable); `dim` = ordinary TTL expiry, which is not anyone's fault and\n * should not shout.\n */\nexport type CacheChipTone = 'warn' | 'dim'\n\n/** Content of the prompt-cache chip. Built by {@link cacheBreakChip}. */\nexport interface CacheChipContent {\n /** Severity: `warn` for an avoidable break, `dim` for an ordinary expiry. */\n tone: CacheChipTone\n /** Leading glyph. Kept out of `label` so a surface can style it separately. */\n glyph: string\n /** Chip text after the glyph: `cache break in system: +41.6k tok, ~$0.48`. Plain, because the TUI shows it in a hint slot. */\n label: string\n}\n\n/**\n * Abbreviate a re-billed token count: `1200` → `1.2k`, `800` → `800`.\n * Thousands only: `1500000` reads as `1500.0k`.\n */\nfunction formatTokens(tokens: number): string {\n return tokens >= 1000 ? `${(tokens / 1000).toFixed(1)}k` : String(tokens)\n}\n\n/**\n * Build the chip for a cache break.\n *\n * `costUsd` is OPTIONAL and omitted entirely on a rate miss — never rendered\n * as `$0.00`. A fabricated zero would read as \"this was free\", which is the\n * opposite of what the chip exists to say. When present it is prefixed `~`\n * because it is an estimate (registry rates × re-billed prefix), and clamped\n * to `<0.01` rather than rounding a real charge down to nothing.\n *\n * @param info - Cache break report, with an optional estimated cost in USD.\n */\nexport function cacheBreakChip(info: CacheBreakInfo & { costUsd?: number }): CacheChipContent {\n const rebilled = formatTokens(info.rebilledTokens)\n const cost = info.costUsd !== undefined\n ? `, ~$${info.costUsd < 0.01 ? '<0.01' : info.costUsd.toFixed(2)}`\n : ''\n\n // No named dimension changed → the prefix simply aged out of the provider's\n // cache window. Reported quietly: nothing was done wrong.\n if (info.changed.length === 0) {\n return { tone: 'dim', glyph: '⚡', label: `cache expired: +${rebilled} tok${cost}` }\n }\n\n return {\n tone: 'warn',\n glyph: '⚡',\n label: `cache break in ${info.changed.join('/')}: +${rebilled} tok${cost}`,\n }\n}\n\n/**\n * Transcript-row text for a persisted cache-break notice. Multi-line, one\n * fact per line, so both hosts can show a short headline and fold or list\n * the rest:\n *\n * cache break ↑116.9k tokens ~$0.48\n * messagePrefix: message 16 of 41→43 rewritten\n * likely compaction or history trimming\n *\n * The footer chip stays terse; the row carries the culprit so a break is\n * diagnosable from the transcript. Only the headline on a TTL / eviction\n * break (nothing was done wrong).\n *\n * @param info - Cache break report, with an optional estimated cost in USD.\n */\nexport function cacheBreakNoticeText(info: CacheBreakInfo & { costUsd?: number }): string {\n const rebilled = formatTokens(info.rebilledTokens)\n const cost = info.costUsd !== undefined\n ? ` ~$${info.costUsd < 0.01 ? '<0.01' : info.costUsd.toFixed(2)}`\n : ''\n const headline = `${info.changed.length === 0 ? 'cache expired' : 'cache break'} ↑${rebilled} tokens${cost}`\n // One line per changed dimension, `name: detail`; a detail may itself span\n // lines (fact, then likely cause) and only its first line takes the prefix.\n const culprits = info.changed.flatMap((dim) => {\n const detail = info.details[dim]\n if (typeof detail !== 'string' || detail.length === 0)\n return [`${dim} changed`]\n const [first, ...rest] = detail.split('\\n')\n return [`${dim}: ${first}`, ...rest]\n })\n return [headline, ...culprits].join('\\n')\n}\n\n/**\n * Provider-retry label — `retrying 2/5`, or `retrying 2/5 (child-3)` when the\n * stalled stream belongs to a subagent rather than the parent.\n *\n * Naming the source matters: with subagents in flight, an unattributed\n * \"retrying\" leaves the user unable to tell whether their own turn is stuck or\n * a delegated worker is.\n *\n * @param info - Retry attempt, attempt limit, and the source of the stalled stream (`'parent'` or a subagent id).\n */\nexport function retryLabel(info: { attempt: number, max: number, source: string }): string {\n const suffix = info.source === 'parent' ? '' : ` (${info.source})`\n return `retrying ${info.attempt}/${info.max}${suffix}`\n}\n\n/** One value of the live run readout, so a renderer can tint and badge it by meaning. */\nexport interface RunStatusCell {\n /** What the cell shows: the run's elapsed time, its output tokens, or the stream rate. */\n kind: 'elapsed' | 'tokens' | 'rate'\n /** Display text: `1m 15s`, `↓1.2k` (`↓~1.2k` while estimated), `41 tok/s`. */\n text: string\n}\n\n/**\n * The live run readout as cells, one badge each: `1m 15s` `↓1.2k` `41 tok/s`.\n * Time runs from the run's start, the count covers the whole run (settled\n * turns plus the one streaming), and the rate is the honest windowed figure\n * from `stepStreamRate` (`null` until it has one). `↓~` marks a count that is\n * still the character heuristic. A tool-only stretch shows just the time.\n * Same model as `formatRunReceiptCells`, the receipt the run ends with.\n *\n * `detail` drops the least useful cell first when space runs out:\n * `'full'` → `'count'` (no rate) → `'time'`.\n *\n * @param run - Elapsed seconds, output token count, whether the count is estimated, and the stream rate in tokens per second (`null` when unknown).\n * @param detail - How much of the readout to keep. Defaults to `'full'`.\n */\nexport function runStatusCells(\n run: { elapsedSec: number, outputTokens: number, estimated: boolean, rate: number | null },\n detail: 'full' | 'count' | 'time' = 'full',\n): RunStatusCell[] {\n const cells: RunStatusCell[] = [{ kind: 'elapsed', text: formatElapsed(Math.max(0, Math.floor(run.elapsedSec))) }]\n if (detail !== 'time' && run.outputTokens > 0)\n cells.push({ kind: 'tokens', text: `↓${run.estimated ? '~' : ''}${fmtTokens(run.outputTokens)}` })\n if (detail === 'full' && run.outputTokens > 0 && run.rate !== null && run.rate > 0)\n cells.push({ kind: 'rate', text: `${run.rate} tok/s` })\n return cells\n}\n\n/**\n * Session cost while a turn streams: settled figure plus the running estimate\n * for the in-flight turn. `estimated` tells the surface to render it dimmed\n * (the number will move, then snap to the settled value at turn end). `null`\n * when there is nothing to show — never `$0.00`.\n *\n * @param settledUsd - Cost of settled turns in USD.\n * @param progress - Streaming progress carrying the running cost estimate for the in-flight turn, if any.\n */\nexport function liveSessionCost(\n settledUsd: number,\n progress: { estCostUsd?: number } | null | undefined,\n): { usd: number, estimated: boolean } | null {\n const live = progress?.estCostUsd ?? 0\n const total = settledUsd + live\n if (total <= 0)\n return null\n return { usd: total, estimated: live > 0 }\n}\n\n/**\n * Footer readout while a compaction summary is being generated: the label,\n * then `~12k` once the loop reports a token estimate, then `8s` once a second\n * has passed, one badge each. Same rule on every surface so a minutes-long\n * compaction of a large transcript reads as progress, not a hang, in the TUI\n * and the GUI alike.\n *\n * `now` is injectable so callers driving a 1 Hz tick don't have to re-read\n * the clock, and so tests are deterministic.\n *\n * @param estTokens - Token estimate reported by the loop, if any.\n * @param startedAt - Compaction start time in epoch milliseconds, if known.\n * @param now - Current time in epoch milliseconds. Defaults to `Date.now()`.\n */\nexport function compactingParts(\n estTokens: number | null | undefined,\n startedAt: number | null | undefined,\n now: number = Date.now(),\n): { label: string, meta: string[] } {\n const elapsedMs = typeof startedAt === 'number' ? now - startedAt : 0\n const meta = [\n typeof estTokens === 'number' && estTokens > 0 ? `~${fmtTokens(estTokens)}` : '',\n elapsedMs >= 1000 ? formatDuration(elapsedMs) : '',\n ].filter(Boolean)\n return { label: 'Compacting...', meta }\n}\n\n/**\n * {@link compactingParts} as one plain string, for a slot that cannot hold\n * badges: `Compacting... ~12k, 8s`.\n *\n * @param estTokens - Token estimate reported by the loop, if any.\n * @param startedAt - Compaction start time in epoch milliseconds, if known.\n * @param now - Current time in epoch milliseconds. Defaults to `Date.now()`.\n */\nexport function compactingLabel(\n estTokens: number | null | undefined,\n startedAt: number | null | undefined,\n now: number = Date.now(),\n): string {\n const { label, meta } = compactingParts(estTokens, startedAt, now)\n return meta.length > 0 ? `${label} ${meta.join(', ')}` : label\n}\n","/**\n * Completion provider for cross-chat references. Trigger: `#`.\n *\n * The visible row is human-friendly (session title, age, project), but the\n * inserted token is id-based (`#abc123`) so titles with spaces never enter the\n * prompt grammar. The token is the shortest normalized id prefix that is unique\n * within the reference catalog, with a six-character floor for readability.\n */\n\nimport type {\n CompletionContext,\n CompletionItem,\n CompletionProvider,\n CompletionReference,\n} from './completion-core'\nimport { Fzf } from 'fzf'\nimport { ageString, shortId } from './format'\n\n/** Trigger character that starts a chat reference. */\nexport const CHATS_TRIGGER = '#'\n\nconst DEFAULT_RESULT_LIMIT = 50\nconst MIN_TOKEN_LENGTH = 6\n\n/** Whether chat suggestions cover the current project or every project. */\nexport type ChatReferenceScope = 'project' | 'all'\n\n/** A chat that can be referenced from a prompt. */\nexport interface ChatReferenceEntry {\n /** Chat id. */\n id: string\n /** Chat title. */\n title: string\n /** Number of turns in the chat. */\n turnCount: number\n /** Number of user messages in the chat. */\n userMessageCount: number\n /** Number of agent runs in the chat. */\n runCount: number\n /** Project directory the chat belongs to. */\n projectRoot?: string\n /** Last update time in ms since epoch. */\n updatedAt: number\n /** Optional (store-dependent); shown as a hint when > 0. */\n artifactCount?: number\n}\n\n/** Provider payload for a chat reference. */\nexport interface ChatReferenceData {\n /** The referenced chat. */\n entry: ChatReferenceEntry\n /** The `#token` text that identifies the chat, without the `#`. */\n token: string\n}\n\nfunction normalizeId(id: string): string {\n return id.replace(/-/g, '').toLowerCase()\n}\n\nfunction projectLabel(projectRoot: string | undefined): string {\n if (!projectRoot)\n return ''\n const parts = projectRoot.split(/[\\\\/]/).filter(Boolean)\n return parts[parts.length - 1] ?? projectRoot\n}\n\n/**\n * Per-catalog memo for `tokenFor`, keyed on catalog reference identity.\n * Token derivation is O(catalog) per id, and both `suggest()` (every\n * keystroke) and `parseReferences()` (every submit/highlight pass) need\n * a token for EVERY entry — quadratic over catalogs that can reach\n * thousands of sessions. The catalog getters return a stable array until\n * a refresh swaps it, so a WeakMap invalidates exactly when it should.\n */\nconst tokensByCatalog = new WeakMap<readonly ChatReferenceEntry[], { normalizedIds: string[], tokens: Map<string, string> }>()\n\nfunction catalogTokens(catalog: readonly ChatReferenceEntry[]): { normalizedIds: string[], tokens: Map<string, string> } {\n let cached = tokensByCatalog.get(catalog)\n if (!cached) {\n cached = { normalizedIds: catalog.map(entry => normalizeId(entry.id)), tokens: new Map() }\n tokensByCatalog.set(catalog, cached)\n }\n return cached\n}\n\nfunction tokenFor(id: string, catalog: readonly ChatReferenceEntry[]): string {\n const { normalizedIds, tokens } = catalogTokens(catalog)\n const known = tokens.get(id)\n if (known !== undefined)\n return known\n const normalized = normalizeId(id)\n let result: string | undefined\n for (let len = Math.min(MIN_TOKEN_LENGTH, normalized.length); len <= normalized.length; len++) {\n const token = normalized.slice(0, len)\n const matches = normalizedIds.filter(candidate => candidate.startsWith(token))\n if (matches.length === 1) {\n result = token\n break\n }\n }\n result ??= normalized || shortId(id)\n tokens.set(id, result)\n return result\n}\n\nfunction stripTrailingPunctuation(candidate: string): string {\n return candidate.replace(/[.,;:)\\]}!?]+$/, '')\n}\n\nfunction descriptionFor(entry: ChatReferenceEntry, token: string, scope: ChatReferenceScope): string {\n const bits = [\n `#${token}`,\n ageString(entry.updatedAt),\n `${entry.userMessageCount} msg${entry.userMessageCount === 1 ? '' : 's'}`,\n ]\n if ((entry.artifactCount ?? 0) > 0)\n bits.push(`${entry.artifactCount} artifact${entry.artifactCount === 1 ? '' : 's'}`)\n const project = projectLabel(entry.projectRoot)\n if (scope === 'all' && project)\n bits.push(project)\n return bits.join(', ')\n}\n\nfunction scoreChats(\n catalog: readonly ChatReferenceEntry[],\n tokenCatalog: readonly ChatReferenceEntry[],\n query: string,\n limit: number,\n scope: ChatReferenceScope,\n): CompletionItem<ChatReferenceData>[] {\n const q = query.trim().toLowerCase()\n const rows = catalog.map(entry => ({\n entry,\n token: tokenFor(entry.id, tokenCatalog),\n project: projectLabel(entry.projectRoot),\n }))\n const fuzzyHits = q.length > 0\n ? new Set(new Fzf(rows, {\n selector: r => `${r.entry.title} ${r.token} ${r.project}`,\n casing: 'case-insensitive',\n }).find(q).map(r => r.item))\n : null\n const scored: { entry: ChatReferenceEntry, token: string, rank: number }[] = []\n for (const row of rows) {\n const title = row.entry.title.toLowerCase()\n const token = row.token.toLowerCase()\n const project = row.project.toLowerCase()\n if (q.length === 0) {\n scored.push({ entry: row.entry, token: row.token, rank: 5 })\n continue\n }\n if (token.startsWith(q)) {\n scored.push({ entry: row.entry, token: row.token, rank: 0 })\n continue\n }\n if (title.startsWith(q)) {\n scored.push({ entry: row.entry, token: row.token, rank: 1 })\n continue\n }\n if (title.includes(q)) {\n scored.push({ entry: row.entry, token: row.token, rank: 2 })\n continue\n }\n if (project.includes(q)) {\n scored.push({ entry: row.entry, token: row.token, rank: 3 })\n continue\n }\n if (fuzzyHits?.has(row)) {\n scored.push({ entry: row.entry, token: row.token, rank: 4 })\n }\n }\n scored.sort((a, b) => {\n if (a.rank !== b.rank)\n return a.rank - b.rank\n return b.entry.updatedAt - a.entry.updatedAt\n })\n return scored.slice(0, limit).map<CompletionItem<ChatReferenceData>>(({ entry, token }) => ({\n id: token,\n label: entry.title,\n description: descriptionFor(entry, token, scope),\n insertText: `${CHATS_TRIGGER}${token} `,\n data: { entry, token },\n }))\n}\n\n/**\n * Creates a completion provider for `#` chat references.\n * Each chat gets the shortest id prefix, at least 6 characters, that identifies it in the catalog. Suggestions are fuzzy-matched on title, token and project folder.\n *\n * @param opts - Catalog getters, an optional async catalog loader, the scope used for labels and a result limit (default 50).\n * @example\n * ```ts\n * import { createChatsCompletionProvider } from 'zidane/chat'\n *\n * const provider = createChatsCompletionProvider({\n * getCatalog: () => [\n * { id: 'c0ffee00-0000-4000-8000-000000000001', title: 'Fix login', turnCount: 4, userMessageCount: 2, runCount: 2, updatedAt: Date.now() },\n * ],\n * })\n * ```\n */\nexport function createChatsCompletionProvider(opts: {\n getCatalog: () => readonly ChatReferenceEntry[]\n getReferenceCatalog?: () => readonly ChatReferenceEntry[]\n getScope?: () => ChatReferenceScope\n ensureCatalog?: () => Promise<readonly ChatReferenceEntry[]>\n limit?: number\n}): CompletionProvider<ChatReferenceData> {\n const limit = opts.limit ?? DEFAULT_RESULT_LIMIT\n const scope = () => opts.getScope?.() ?? 'project'\n return {\n id: 'chats',\n trigger: CHATS_TRIGGER,\n label: 'Chats',\n suggest(query) {\n const current = opts.getCatalog()\n const tokenCatalog = opts.getReferenceCatalog?.() ?? current\n const effectiveTokenCatalog = tokenCatalog.length > 0 ? tokenCatalog : current\n if (current.length > 0)\n return scoreChats(current, effectiveTokenCatalog, query, limit, scope())\n if (opts.ensureCatalog) {\n return opts.ensureCatalog().then((loaded) => {\n const refreshed = opts.getReferenceCatalog?.() ?? loaded\n const refreshedTokenCatalog = refreshed.length > 0 ? refreshed : loaded\n return scoreChats(loaded, refreshedTokenCatalog, query, limit, scope())\n })\n }\n return []\n },\n parseReferences(text, _ctx: CompletionContext) {\n const catalog = opts.getReferenceCatalog?.() ?? opts.getCatalog()\n if (catalog.length === 0)\n return []\n const byToken = new Map<string, ChatReferenceData>()\n for (const entry of catalog) {\n const token = tokenFor(entry.id, catalog)\n byToken.set(token, { entry, token })\n }\n const refs: CompletionReference<ChatReferenceData>[] = []\n const rx = /(^|\\s)#(\\S+)/g\n for (const m of text.matchAll(rx)) {\n const candidate = stripTrailingPunctuation(m[2])\n const data = byToken.get(candidate.toLowerCase())\n if (!data)\n continue\n const start = m.index + m[1].length\n refs.push({\n providerId: 'chats',\n start,\n end: start + 1 + candidate.length,\n itemId: data.entry.id,\n data,\n })\n }\n return refs\n },\n }\n}\n\n/**\n * Returns the unique ids of the chats referenced in a reference list.\n * References from other providers are ignored. Order of first appearance is kept.\n *\n * @param references - References collected from a prompt.\n */\nexport function uniqueChatIdsFromReferences(\n references: readonly CompletionReference<unknown>[],\n): string[] {\n const out: string[] = []\n const seen = new Set<string>()\n for (const ref of references) {\n if (ref.providerId !== 'chats')\n continue\n if (seen.has(ref.itemId))\n continue\n seen.add(ref.itemId)\n out.push(ref.itemId)\n }\n return out\n}\n\n/**\n * Extracts the `#token` chat references from text.\n * A token starts after whitespace or at the start of the text. Trailing punctuation is stripped, tokens are lowercased and duplicates are dropped.\n *\n * @param text - The prompt text to scan.\n */\nexport function chatReferenceTokensFromText(text: string): string[] {\n const out: string[] = []\n const seen = new Set<string>()\n const rx = /(^|\\s)#(\\S+)/g\n for (const m of text.matchAll(rx)) {\n const token = stripTrailingPunctuation(m[2]).toLowerCase()\n if (!token || seen.has(token))\n continue\n seen.add(token)\n out.push(token)\n }\n return out\n}\n","// ---------------------------------------------------------------------------\n// Model lineup: which models of a provider are \"current\".\n//\n// Provider catalogs list every model they ever served (OpenRouter: ~400),\n// with no release dates, so recency is read off the ids themselves. A model\n// id is split into a FAMILY (the words: `claude-opus`, `gpt-luna`,\n// `gemini-flash-lite`), a VERSION (the numbers: `4-5`, `5.6`, `v4.1`,\n// `qwen3.5`), a SIZE (`27b`, `235b-a22b`, `16k`) and noise that marks a\n// snapshot or a routing variant of the same model (`-20251001`,\n// `-2024-05-13`, `-0731`, `-preview`, `-latest`, `:batch`, `:free`).\n//\n// The lineup keeps, per provider and family, the models at the newest\n// version: every size of that version, one entry per size (the plain alias\n// over a dated snapshot over a routing variant). Everything else is\n// \"older\". Pure and data-driven so the GUI and the TUI share one answer.\n// ---------------------------------------------------------------------------\n\n/** Parsed shape of a model id. Exported for tests and for pickers that want to show a family label. */\nexport interface ModelIdParts {\n /** `vendor/` prefix on aggregator ids (`anthropic` in `anthropic/claude-opus-5`), lowercased; '' when absent. */\n vendor: string\n /** The words of the id, lowercased and joined with `-`. */\n family: string\n /** Numeric version segments in order; empty when the id carries none. */\n version: number[]\n /** Size tokens joined with `-` (`27b`, `235b-a22b`); '' when absent. */\n size: string\n /** Dated / numbered snapshot (`20251001`, `2024-05-13` → `20240513`, `0731`); '' for the plain alias. */\n snapshot: string\n /** Routing variant after `:` (`batch`, `free`); '' when absent. */\n variant: string\n}\n\nconst DATE_DASHED = /-(\\d{4})-(\\d{2})-(\\d{2})(?=$|[-:])/\nconst SIZE = /^(?:\\d+(?:\\.\\d+)?[bkm]|a\\d+(?:\\.\\d+)?b|e\\d+b|\\d+x\\d+b)$/\nconst SNAPSHOT = /^\\d{4}$|^\\d{6,8}$/\nconst NUMBER = /^\\d+(?:\\.\\d+)*$/\n/** Zero-padded short number (`001`, `05`): a revision or a date part, never a version. */\nconst PADDED = /^0\\d{1,2}$/\n/** Bedrock's `-v1` / `-v2:0` revision suffix. Only a revision once a version was read. */\nconst REVISION = /^v\\d+$/\n/** `v4.1`, `k2.5`, `r1`, `o3`, `qwen3.5`: letters then a version. */\nconst LETTERS_VERSION = /^([a-z]+)(\\d+(?:\\.\\d+)*)$/\n/** `4o`: a version then letters. */\nconst VERSION_LETTERS = /^(\\d+(?:\\.\\d+)*)([a-z]+)$/\n/** Words that mark a pre-release or an alias of the same model, not a different one. */\nconst NOISE = new Set(['preview', 'latest', 'exp', 'experimental'])\n\n/**\n * Splits a model id into vendor, family, version, size, snapshot and routing variant. Parsing is lowercase and never throws.\n *\n * @param id - Model id, for example `anthropic/claude-sonnet-4-5:thinking`.\n */\nexport function parseModelId(id: string): ModelIdParts {\n let rest = id.toLowerCase()\n let variant = ''\n const colon = rest.indexOf(':')\n if (colon >= 0) {\n variant = rest.slice(colon + 1)\n rest = rest.slice(0, colon)\n }\n let vendor = ''\n const slash = rest.lastIndexOf('/')\n if (slash >= 0) {\n vendor = rest.slice(0, slash)\n rest = rest.slice(slash + 1)\n }\n let snapshot = ''\n const dated = DATE_DASHED.exec(rest)\n if (dated) {\n snapshot = `${dated[1]}${dated[2]}${dated[3]}`\n rest = rest.slice(0, dated.index) + rest.slice(dated.index + dated[0].length)\n }\n\n const words: string[] = []\n const version: number[] = []\n const sizes: string[] = []\n const snapParts: string[] = []\n let afterNoise = false\n const pushVersion = (v: string) => {\n for (const n of v.split('.')) version.push(Number(n))\n }\n for (const token of rest.split(/[-_\\s]+/).filter(Boolean)) {\n if (NOISE.has(token)) {\n afterNoise = true\n continue\n }\n if (SIZE.test(token)) {\n sizes.push(token)\n continue\n }\n // A bare 4 / 6-8 digit token after a version or word is a snapshot\n // (`-0731`, `-2512`, `-20251001`), never a version of its own. So is any\n // number after a pre-release word (`-preview-05-20`, `-preview-09-2025`)\n // and a zero-padded revision after a version (`gemini-2.0-flash-001`).\n const started = words.length > 0 || version.length > 0\n // Bedrock's `-v1` after a version (`claude-opus-4-6-v1`) is a revision too.\n if (started && (SNAPSHOT.test(token) || (afterNoise && NUMBER.test(token)) || (version.length > 0 && (PADDED.test(token) || REVISION.test(token))))) {\n snapParts.push(token)\n continue\n }\n if (NUMBER.test(token)) {\n pushVersion(token)\n continue\n }\n const lv = LETTERS_VERSION.exec(token)\n if (lv) {\n words.push(lv[1]!)\n pushVersion(lv[2]!)\n continue\n }\n const vl = VERSION_LETTERS.exec(token)\n if (vl) {\n pushVersion(vl[1]!)\n words.push(vl[2]!)\n continue\n }\n words.push(token)\n }\n if (!snapshot && snapParts.length > 0) {\n // `mm-yyyy` → `yyyymm`, so snapshots of that shape sort by date.\n snapshot = snapParts.length === 2 && /^\\d{2}$/.test(snapParts[0]!) && /^\\d{4}$/.test(snapParts[1]!)\n ? `${snapParts[1]}${snapParts[0]}`\n : snapParts.join('')\n }\n return { vendor, family: words.join('-'), version, size: sizes.join('-'), snapshot, variant }\n}\n\n/**\n * Numeric, segment by segment; a longer version wins a tie on the shared prefix (`5.5` > `5`).\n *\n * @param a - First version, as parsed by {@link parseModelId}.\n * @param b - Second version.\n */\nexport function compareModelVersion(a: readonly number[], b: readonly number[]): number {\n const n = Math.max(a.length, b.length)\n for (let i = 0; i < n; i++) {\n const x = a[i]\n const y = b[i]\n if (x === undefined)\n return -1\n if (y === undefined)\n return 1\n if (x !== y)\n return x - y\n }\n return 0\n}\n\n/**\n * Among ids naming the same model (family, version and size equal), the one\n * a picker should show: the plain alias over a dated snapshot, the newest\n * snapshot among snapshots, no routing variant over a variant, then the\n * shorter id. Negative when `a` is preferred.\n */\nfunction preferAlias(a: { id: string, parts: ModelIdParts }, b: { id: string, parts: ModelIdParts }): number {\n const sa = a.parts.snapshot\n const sb = b.parts.snapshot\n if (!sa !== !sb)\n return sa ? 1 : -1\n if (sa !== sb)\n return sa < sb ? 1 : -1\n if (!a.parts.variant !== !b.parts.variant)\n return a.parts.variant ? 1 : -1\n return a.id.length - b.id.length || (a.id < b.id ? -1 : a.id > b.id ? 1 : 0)\n}\n\n/** A provider and model id pair considered for the lineup. */\nexport interface LineupEntry {\n /** Provider key. */\n providerKey: string\n /** Model id. */\n modelId: string\n}\n\nconst lineupKey = (e: LineupEntry): string => `${e.providerKey}\\u0000${e.modelId}`\n\n/**\n * Keys (`provider\\0model`) of the current models: per provider and family,\n * the newest version, one entry per size. `keep` entries (the active model,\n * fallback chain, off-catalog ids) are always included.\n *\n * @param entries - Every provider and model id on offer.\n * @param keep - Entries to include regardless of age.\n */\nexport function currentModelKeys(entries: readonly LineupEntry[], keep: readonly LineupEntry[] = []): Set<string> {\n const groups = new Map<string, { id: string, key: string, parts: ModelIdParts }[]>()\n for (const entry of entries) {\n const parts = parseModelId(entry.modelId)\n const group = `${entry.providerKey}\\u0000${parts.vendor}\\u0000${parts.family}`\n const list = groups.get(group)\n const item = { id: entry.modelId, key: lineupKey(entry), parts }\n if (list)\n list.push(item)\n else\n groups.set(group, [item])\n }\n\n const kept = new Set<string>(keep.map(lineupKey))\n for (const list of groups.values()) {\n let newest: number[] = []\n for (const item of list) {\n if (compareModelVersion(item.parts.version, newest) > 0)\n newest = item.parts.version\n }\n const bySize = new Map<string, { id: string, key: string, parts: ModelIdParts }>()\n for (const item of list) {\n if (compareModelVersion(item.parts.version, newest) !== 0)\n continue\n const best = bySize.get(item.parts.size)\n if (!best || preferAlias(item, best) < 0)\n bySize.set(item.parts.size, item)\n }\n for (const item of bySize.values()) kept.add(item.key)\n }\n return kept\n}\n\n/**\n * `entries` in their original order, current models only (see {@link currentModelKeys}).\n *\n * @param entries - Every provider and model id on offer.\n * @param keep - Entries to include regardless of age.\n */\nexport function currentModels<T extends LineupEntry>(entries: readonly T[], keep: readonly LineupEntry[] = []): T[] {\n const kept = currentModelKeys(entries, keep)\n return entries.filter(entry => kept.has(lineupKey(entry)))\n}\n","/**\n * The text model: the cheap model that writes side-channel text (session\n * titles, next-prompt suggestions) instead of the session's main model.\n *\n * Stored as `Settings.textModel`: `''` = automatic, otherwise\n * `\"providerKey[@account]/modelId\"` (see `parseModelSetting`). Automatic is\n * the smallest current model of the session's provider, so the transcript\n * stays with the provider (and account) the session already talks to:\n *\n * 1. the current models (`currentModels`) of that provider that take\n * plain text, of the main model's vendor on an aggregator;\n * 2. among them the `small` tier, the cheapest input rate first;\n * 3. else the cheapest current model with a known cost;\n * 4. else the main model.\n *\n * A model's tier is its catalog entry's `tier` when set, else a heuristic on\n * the words of its family (`claude-haiku` → small). Node-free: shared by the\n * hosts and the GUI renderer's settings row.\n */\nimport type { ModelTier } from '../providers/model-catalog'\nimport { currentModels, parseModelId } from './model-lineup'\nimport { parseModelSetting } from './model-setting'\n\nexport type { ModelTier }\n\n/** Family words per tier. Small is checked first, so `flash-lite` is small while `flash` is medium. */\nconst SMALL_WORDS: ReadonlySet<string> = new Set(['haiku', 'luna', 'mini', 'small', 'lite', 'nano', 'micro'])\nconst MEDIUM_WORDS: ReadonlySet<string> = new Set(['sonnet', 'terra', 'flash', 'medium'])\nconst LARGE_WORDS: ReadonlySet<string> = new Set(['opus', 'fable', 'sol', 'pro', 'large'])\n\n/** Family words of special-purpose models that can't write a plain reply (image, audio, live, computer use, research agents). */\nconst SPECIAL_WORDS: ReadonlySet<string> = new Set([\n 'image',\n 'live',\n 'audio',\n 'tts',\n 'realtime',\n 'transcribe',\n 'embedding',\n 'embed',\n 'computer',\n 'research',\n 'customtools',\n 'moderation',\n 'guard',\n 'ocr',\n])\n\n/**\n * Tier read off a model id's family words; `undefined` when no word names one.\n *\n * @param modelId - Model id, for example `claude-haiku-4-5` or `anthropic/claude-haiku-4.5`.\n */\nexport function heuristicModelTier(modelId: string): ModelTier | undefined {\n const words = parseModelId(modelId).family.split('-')\n if (words.some(w => SMALL_WORDS.has(w)))\n return 'small'\n if (words.some(w => MEDIUM_WORDS.has(w)))\n return 'medium'\n if (words.some(w => LARGE_WORDS.has(w)))\n return 'large'\n return undefined\n}\n\n/**\n * A model's tier: its catalog value, else {@link heuristicModelTier}.\n *\n * @param model - Model id and optional catalog tier.\n * @param model.id - Model id.\n * @param model.tier - Catalog tier, when the catalog sets one.\n */\nexport function modelTier(model: { id: string, tier?: ModelTier }): ModelTier | undefined {\n return model.tier ?? heuristicModelTier(model.id)\n}\n\n/** One model of the session's provider, as the automatic pick reads it (a `ModelInfo` fits). */\nexport interface TextModelCandidate {\n /** Model id. */\n id: string\n /** Catalog tier; guessed from the id when absent. */\n tier?: ModelTier\n /** Input price; the cheapest candidate wins, and one without a price loses. */\n cost?: { input: number }\n /** Input modalities; a model without `text` is skipped. */\n input?: readonly ('text' | 'image')[]\n}\n\nfunction generalPurpose(model: TextModelCandidate): boolean {\n if (model.input && !model.input.includes('text'))\n return false\n const parts = parseModelId(model.id)\n // Routing variants (`:free`, `:thinking`) and aggregator meta-models (`auto`, `~vendor/…-latest`) aren't a model of their own.\n if (parts.variant || parts.family === 'auto' || model.id.startsWith('~'))\n return false\n return !parts.family.split('-').some(w => SPECIAL_WORDS.has(w))\n}\n\nconst inputRate = (model: TextModelCandidate): number => model.cost?.input ?? Number.POSITIVE_INFINITY\n\n/**\n * The automatic text model among one provider's models; `undefined` when\n * nothing qualifies (the caller then uses the main model).\n *\n * @param models - The provider's models, in catalog order.\n * @param mainModelId - The session's main model id (its vendor narrows an aggregator's list).\n */\nexport function autoTextModelId(models: readonly TextModelCandidate[], mainModelId: string): string | undefined {\n const usable = models.filter(generalPurpose)\n let pool = currentModels(usable.map(model => ({ providerKey: '', modelId: model.id, model }))).map(e => e.model)\n const vendor = parseModelId(mainModelId).vendor\n if (vendor) {\n const sameVendor = pool.filter(m => parseModelId(m.id).vendor === vendor)\n if (sameVendor.length > 0)\n pool = sameVendor\n }\n const cheapest = (list: readonly TextModelCandidate[]): TextModelCandidate | undefined =>\n list.reduce<TextModelCandidate | undefined>((best, m) => (!best || inputRate(m) < inputRate(best) ? m : best), undefined)\n const small = pool.filter(m => modelTier(m) === 'small')\n if (small.length > 0)\n return cheapest(small)!.id\n return cheapest(pool.filter(m => m.cost !== undefined))?.id\n}\n\n/** Where a resolved text model came from. */\nexport type TextModelSource = 'setting' | 'auto' | 'main'\n\n/** A text model to run on. */\nexport interface TextModelPick {\n /** Provider to run on. */\n providerKey: string\n /** Model id on that provider. */\n modelId: string\n /** The provider's stored account; absent = its active one. */\n account?: string\n /** Whether the pick came from `Settings.textModel`, the automatic choice or the main model. */\n source: TextModelSource\n}\n\n/** Inputs of {@link textModelChain} / {@link resolveTextModel}. */\nexport interface TextModelOptions {\n /** `Settings.textModel`: `''` / unset = automatic. */\n setting: string | undefined\n /** The session's main model (or the default model, outside a session). */\n main: { providerKey: string, modelId: string, account?: string }\n /** The models of a provider (`modelsForDescriptor`). */\n modelsOf: (providerKey: string) => readonly TextModelCandidate[]\n}\n\n/**\n * Text models to try, best first: the setting's pick, the automatic pick,\n * the main model (duplicates dropped). A host walks it until one builds, so\n * a picked provider that isn't signed in falls back instead of failing.\n *\n * @param opts - Setting, main model and model source.\n */\nexport function textModelChain(opts: TextModelOptions): TextModelPick[] {\n const { main } = opts\n const out: TextModelPick[] = []\n const push = (pick: TextModelPick) => {\n if (!out.some(p => p.providerKey === pick.providerKey && p.modelId === pick.modelId && p.account === pick.account))\n out.push(pick)\n }\n const chosen = parseModelSetting(opts.setting)\n if (chosen) {\n // No account named: the session's own on its provider, else the active one.\n const account = chosen.account ?? (chosen.providerKey === main.providerKey ? main.account : undefined)\n push({ providerKey: chosen.providerKey, modelId: chosen.modelId, ...(account ? { account } : {}), source: 'setting' })\n }\n const auto = autoTextModelId(opts.modelsOf(main.providerKey), main.modelId)\n if (auto)\n push({ providerKey: main.providerKey, modelId: auto, ...(main.account ? { account: main.account } : {}), source: 'auto' })\n push({ providerKey: main.providerKey, modelId: main.modelId, ...(main.account ? { account: main.account } : {}), source: 'main' })\n return out\n}\n\n/**\n * The text model to use: the first of {@link textModelChain} whose provider\n * is usable (all of them when `usable` is omitted).\n *\n * @param opts - Setting, main model and model source.\n * @param usable - Whether a provider can run (signed in); the main model is always kept as the last resort.\n */\nexport function resolveTextModel(opts: TextModelOptions, usable?: (providerKey: string, account?: string) => boolean): TextModelPick {\n const chain = textModelChain(opts)\n return chain.find(p => !usable || usable(p.providerKey, p.account)) ?? chain[chain.length - 1]!\n}\n\n/**\n * Build the text model's provider: walks {@link textModelChain} and returns\n * the first pick `build` doesn't throw on. The main model's error propagates\n * when nothing builds.\n *\n * @param opts - Setting, main model and model source.\n * @param build - Provider construction; throws when the provider has no credentials.\n */\nexport function buildTextModel<P>(opts: TextModelOptions, build: (providerKey: string, account?: string) => P): TextModelPick & { provider: P } {\n const chain = textModelChain(opts)\n let lastError: unknown\n for (const pick of chain) {\n try {\n return { ...pick, provider: build(pick.providerKey, pick.account) }\n }\n catch (err) {\n lastError = err\n }\n }\n throw lastError\n}\n","/**\n * Run time estimates (`Settings.runEstimates`): the math and the wire shape\n * shared by the host (which asks the decision model) and the windows (which\n * count down). Pure: no I/O, safe for `zidane/chat/pure`.\n *\n * The decision model rates the remaining work on a score over minute marks\n * spaced on a log scale ({@link ESTIMATE_LEVEL_MINUTES}). Its answer, a\n * probability-weighted level index, maps back to minutes between the marks\n * ({@link scoreToMs}), so an estimate is a real duration, not a range. The\n * level probabilities give the point past which a run counts as over\n * ({@link percentileMs}).\n */\n\nimport type { RunEstimate, RunUserWait, SessionRun } from '../session'\n\n/** The score levels, in minutes, lowest first: 1 minute to 24 hours. */\nexport const ESTIMATE_LEVEL_MINUTES: readonly number[] = [1, 2, 4, 8, 15, 30, 60, 120, 240, 480, 960, 1440]\n\n/** Percentile of the answer's distribution past which a run counts as over its estimate. */\nexport const ESTIMATE_OVER_PERCENTILE = 0.8\n\n/** Without level probabilities, the over point is the estimate times this. */\nexport const ESTIMATE_OVER_FALLBACK = 1.5\n\nconst MINUTE = 60_000\n\n/**\n * One level's label, as the decision model reads it.\n *\n * @param minutes - The level, in minutes.\n */\nexport function estimateLevelLabel(minutes: number): string {\n if (minutes < 60)\n return minutes === 1 ? '1 minute' : `${minutes} minutes`\n const hours = minutes / 60\n return hours === 1 ? '1 hour' : `${hours} hours`\n}\n\n/**\n * A score (a level index, possibly between levels) as a duration: the marks\n * are on a log scale, so a score between two levels is interpolated on it.\n *\n * @param score - Probability-weighted level index, `0` to `levels - 1`.\n */\nexport function scoreToMs(score: number): number {\n const levels = ESTIMATE_LEVEL_MINUTES\n const s = Math.min(Math.max(Number.isFinite(score) ? score : 0, 0), levels.length - 1)\n const low = Math.floor(s)\n const high = Math.min(low + 1, levels.length - 1)\n const f = s - low\n const minutes = Math.exp(Math.log(levels[low]!) + f * (Math.log(levels[high]!) - Math.log(levels[low]!)))\n return minutes * MINUTE\n}\n\n/**\n * The duration below which the answer puts probability `p` (`0..1`), from\n * the per-level probabilities. `null` when they don't add up to anything.\n *\n * @param probabilities - Probability per level, indexed like the levels.\n * @param p - The percentile.\n */\nexport function percentileMs(probabilities: readonly number[], p: number): number | null {\n const total = probabilities.reduce((sum, x) => sum + (Number.isFinite(x) && x > 0 ? x : 0), 0)\n if (total <= 0)\n return null\n let cumulative = 0\n for (let i = 0; i < probabilities.length; i++) {\n const share = Math.max(probabilities[i] ?? 0, 0) / total\n if (share > 0 && cumulative + share >= p) {\n // Within level i: spread its mass between the level below and this one.\n const within = (p - cumulative) / share\n return scoreToMs(Math.max(i - 1 + within, 0))\n }\n cumulative += share\n }\n return scoreToMs(probabilities.length - 1)\n}\n\n/** Fewest finished runs before calibration applies. */\nexport const CALIBRATION_MIN_SAMPLES = 5\n/** Most recent finished runs calibration reads, per project. */\nexport const CALIBRATION_MAX_SAMPLES = 50\n/** Smallest calibration factor: estimates are at most halved. */\nexport const CALIBRATION_MIN = 0.5\n/** Largest calibration factor: estimates are at most tripled. */\nexport const CALIBRATION_MAX = 3\n\n/**\n * The factor that corrects this project's estimates: the median of\n * actual / estimated over its recent finished runs, kept within\n * {@link CALIBRATION_MIN}..{@link CALIBRATION_MAX}. `null` with too few runs.\n *\n * @param ratios - Actual / estimated working time of finished runs, oldest first.\n */\nexport function calibrationFactor(ratios: readonly number[]): number | null {\n const recent = ratios.filter(r => Number.isFinite(r) && r > 0).slice(-CALIBRATION_MAX_SAMPLES)\n if (recent.length < CALIBRATION_MIN_SAMPLES)\n return null\n const sorted = [...recent].sort((a, b) => a - b)\n const mid = sorted.length / 2\n const median = sorted.length % 2 === 1 ? sorted[Math.floor(mid)]! : (sorted[mid - 1]! + sorted[mid]!) / 2\n return Math.min(Math.max(median, CALIBRATION_MIN), CALIBRATION_MAX)\n}\n\n/**\n * Time spent waiting on the user up to `now`, in ms.\n *\n * @param wait - The run's waits so far, and since when it waits now; absent: it never waited.\n * @param now - The time to count up to, epoch ms.\n */\nexport function waitedMs(wait: RunUserWait | undefined, now: number): number {\n if (!wait)\n return 0\n return wait.ms + (wait.since !== undefined ? Math.max(now - wait.since, 0) : 0)\n}\n\n/**\n * Working time of a run up to `now`: its time since start, less its waits on the user.\n *\n * @param startedAt - When the run started, epoch ms.\n * @param wait - Its waits on the user, as {@link waitedMs} reads them.\n * @param now - The time to count up to, epoch ms.\n */\nexport function workedMs(startedAt: number, wait: RunUserWait | undefined, now: number): number {\n return Math.max(now - startedAt - waitedMs(wait, now), 0)\n}\n\n/**\n * A run's estimate as hosts send it to windows (`run:estimate`, a session\n * row's `runEstimates`): enough to count down without the run record.\n */\nexport interface RunEstimateView {\n /** The run. */\n runId: string\n /** When the run started, epoch ms on the host's clock. */\n startedAt: number\n /** Subagent depth: 0 for the session's own run. */\n depth: number\n /** A subagent's label (`child-3`), as its spawn row names it. */\n label?: string\n /** The `spawn` call that started a subagent's run. */\n callId?: string\n /** Expected working time of the whole run, in ms. */\n totalMs: number\n /** Working time past which the run is over its estimate, in ms. */\n highMs: number\n /** When the estimate was made, epoch ms. */\n at: number\n /** Finished waits on the user, in ms. */\n waitMs: number\n /** Start of the wait going on now, epoch ms. */\n waitingSince?: number\n}\n\n/**\n * A run record's estimate as a {@link RunEstimateView}; `undefined` when it\n * has none.\n *\n * @param run - The run record.\n */\nexport function runEstimateView(run: Pick<SessionRun, 'id' | 'startedAt' | 'depth' | 'label' | 'parentCallId' | 'estimate' | 'userWait'>): RunEstimateView | undefined {\n const estimate: RunEstimate | undefined = run.estimate\n if (!estimate)\n return undefined\n return {\n runId: run.id,\n startedAt: run.startedAt,\n depth: run.depth ?? 0,\n ...(run.label ? { label: run.label } : {}),\n ...(run.parentCallId ? { callId: run.parentCallId } : {}),\n totalMs: estimate.totalMs,\n highMs: estimate.highMs,\n at: estimate.at,\n waitMs: run.userWait?.ms ?? 0,\n ...(run.userWait?.since !== undefined ? { waitingSince: run.userWait.since } : {}),\n }\n}\n\n/**\n * The estimates of a session's runs in progress, the session's own first:\n * what a session row carries.\n *\n * @param runs - The session's run records.\n */\nexport function runningEstimateViews(runs: readonly SessionRun[]): RunEstimateView[] {\n return runs\n .filter(run => run.status === 'running' && run.estimate)\n .map(run => runEstimateView(run)!)\n .sort((a, b) => a.depth - b.depth || a.startedAt - b.startedAt)\n}\n\n/** What an estimate says at a moment. */\nexport interface EstimateReadout {\n /**\n * - `live`: counting down.\n * - `due`: the estimate is spent, the run is not over yet (between its total and its over point).\n * - `paused`: the run waits on the user; the count holds.\n * - `over`: past its over point.\n */\n state: 'live' | 'due' | 'paused' | 'over'\n /** Working time left, in ms; `0` once the estimate is spent. */\n remainingMs: number\n}\n\n/**\n * What `view` says at `now`.\n *\n * @param view - The estimate.\n * @param now - Epoch ms on the host's clock (the window's is close enough).\n */\nexport function estimateReadout(view: RunEstimateView, now: number): EstimateReadout {\n const worked = workedMs(view.startedAt, { ms: view.waitMs, ...(view.waitingSince !== undefined ? { since: view.waitingSince } : {}) }, now)\n const remainingMs = Math.max(view.totalMs - worked, 0)\n if (worked > view.highMs)\n return { state: 'over', remainingMs: 0 }\n if (view.waitingSince !== undefined)\n return { state: 'paused', remainingMs }\n return { state: remainingMs > 0 ? 'live' : 'due', remainingMs }\n}\n\n/**\n * A remaining time as a chip says it: `~45s`, `~11m`, `~1h 20m`, `~12h`.\n *\n * @param ms - Time left, in ms.\n */\nexport function formatEstimate(ms: number): string {\n const seconds = Math.max(ms, 0) / 1000\n if (seconds < 60)\n return `~${Math.max(Math.ceil(seconds / 10) * 10, 10)}s`\n const minutes = Math.ceil(seconds / 60)\n if (minutes < 60)\n return `~${minutes}m`\n if (minutes < 600) {\n // Above an hour, minutes in steps of 5: a countdown that flips every minute reads as precise.\n const rounded = Math.round(minutes / 5) * 5\n const h = Math.floor(rounded / 60)\n const m = rounded % 60\n return m === 0 ? `~${h}h` : `~${h}h ${m}m`\n }\n return `~${Math.round(minutes / 60)}h`\n}\n","/**\n * How an MCP server answered a connection attempt, in the words both apps\n * show. Shared by the desktop app's Test (Settings › Agent › MCP servers) and the TUI's\n * bootstrap / test, so the same server reads the same way in either.\n *\n * ok connected and listed its tools;\n * needs-auth 401/403 with no key of its own: a browser sign-in fixes it\n * (an expired or never finished sign-in included);\n * token-rejected 401/403 on a server that sends its own headers: the key\n * in mcps.json is wrong, a browser sign-in won't help;\n * error anything else (refused, timed out, bad command).\n *\n * Pure: no I/O.\n */\nimport type { McpServerConfig } from '../types'\n\n/** Classification of an MCP connection attempt. */\nexport type McpTestOutcomeKind = 'ok' | 'needs-auth' | 'token-rejected' | 'error'\n\n/** Classified result of one MCP connection test. */\nexport interface McpTestResult {\n /** How the server answered. */\n kind: McpTestOutcomeKind\n /** Number of tools listed. Set for `ok`. */\n tools?: number\n /** User-facing explanation for non-`ok` kinds. */\n message?: string\n}\n\n/** What to do about a refused key, under the \"Key refused\" headline. */\nexport const MCP_KEY_REFUSED_HINT = 'Check the headers in mcps.json.'\n\n/**\n * A refusal for want of credentials: HTTP 401 / 403, however the SDK words it.\n *\n * @param error - The error thrown by a connection attempt.\n */\nexport function isMcpAuthRefusal(error: unknown): boolean {\n const code = (error as { code?: unknown } | null)?.code\n if (code === 401 || code === 403)\n return true\n const message = error instanceof Error ? error.message : String(error ?? '')\n return /\\b(?:401|403)\\b|unauthori[sz]ed|forbidden|invalid_token/i.test(message)\n}\n\n/**\n * The first line of a failure, never empty.\n *\n * @param error - The error thrown by a connection attempt.\n */\nexport function mcpFailureLine(error: unknown): string {\n const code = (error as { code?: unknown } | null)?.code\n const message = error instanceof Error && error.message\n ? error.message\n : code !== undefined\n ? `The server answered ${String(code)}.`\n : typeof error === 'string'\n ? error\n : ''\n return message.split('\\n')[0]?.trim() || 'Couldn\\'t connect.'\n}\n\n/**\n * Classifies a connection attempt. HTTP servers that required auth or answered\n * 401/403 become `token-rejected` when the config sends its own headers, else\n * `needs-auth`. Any other error becomes `error`. A clean connection is `ok`.\n *\n * @param config - The server's transport and headers.\n * @param result - Whether auth was required, the error if any, and the tool count.\n */\nexport function classifyMcpTest(\n config: Pick<McpServerConfig, 'transport' | 'headers'>,\n result: { authRequired: boolean, error?: unknown, tools?: number },\n): McpTestResult {\n const http = config.transport !== 'stdio'\n const ownKey = Object.keys(config.headers ?? {}).length > 0\n if (http && (result.authRequired || (result.error !== undefined && isMcpAuthRefusal(result.error)))) {\n return ownKey\n ? { kind: 'token-rejected', message: MCP_KEY_REFUSED_HINT }\n : { kind: 'needs-auth', message: 'Sign in to connect.' }\n }\n if (result.error !== undefined)\n return { kind: 'error', message: mcpFailureLine(result.error) }\n return { kind: 'ok', tools: result.tools ?? 0 }\n}\n","/**\n * Edits to the `enabledMcps` allowlist. Without an explicit list every\n * server is on, so there is nothing to change. Servers that merely appear\n * in a config file stay off once a list exists (they don't opt in\n * silently); a server the user adds in Settings is turned on with it.\n */\n\n/**\n * The list with `name` turned on, or `undefined` when it already is.\n *\n * @param list - The current `enabledMcps` list, or `undefined` when every server is on.\n * @param name - Server to turn on.\n */\nexport function enabledMcpsWith(list: readonly string[] | undefined, name: string): string[] | undefined {\n return list === undefined || list.includes(name) ? undefined : [...list, name]\n}\n\n/**\n * The list without `name`, or `undefined` when it isn't listed.\n *\n * @param list - The current `enabledMcps` list, or `undefined` when every server is on.\n * @param name - Server to turn off.\n */\nexport function enabledMcpsWithout(list: readonly string[] | undefined, name: string): string[] | undefined {\n return list?.includes(name) ? list.filter(n => n !== name) : undefined\n}\n","/**\n * The native extensions: the user-facing folders of the zidane repository's\n * `extensions/`, installable from the wizard in one click. Each installs from\n * the repository at the running version's tag, so it matches the app's API.\n * `test/chat/onboarding-native-extensions.test.ts` keeps this list and the\n * folder in sync.\n */\nimport type { NativeExtensionEntry, NativeExtensionGroup } from './types'\n\n/** The GitHub repository native extensions install from. */\nexport const NATIVE_EXTENSIONS_REPO = 'Tahul/zidane'\n\n/** Every native extension, in display order within each group. */\nexport const NATIVE_EXTENSIONS: readonly NativeExtensionEntry[] = [\n {\n name: 'git-flow',\n label: 'Git flow',\n description: 'Adds /commit, /review, /fix, /pr and /ticket. The agent writes your commits and pull requests, and reviews code with several models.',\n group: 'workflows',\n needs: [\n { kind: 'command', name: 'git', why: 'commits and branches' },\n { kind: 'command', name: 'gh', why: 'pull requests and review comments', optional: true },\n ],\n brand: 'git',\n icon: { gui: 'i-ph-git-branch' },\n },\n {\n name: 'goal',\n label: 'Goal',\n description: 'Adds /goal. The agent keeps working until the goal is done, for up to 10 runs. Esc stops it.',\n group: 'workflows',\n needs: [{ kind: 'decisions' }],\n icon: { gui: 'i-ph-target' },\n },\n {\n name: 'linear',\n label: 'Linear',\n description: 'Shows Linear issues and projects as badges in prompts and replies, and links them to the chats that use them.',\n group: 'integrations',\n needs: [{ kind: 'connector', id: 'linear' }],\n brand: 'linear',\n icon: { gui: 'i-ph-ticket' },\n },\n {\n name: 'ai-autocomplete',\n label: 'AI autocomplete',\n description: 'Suggests the end of your prompt as you type, from a local Ollama model. Press Tab to accept.',\n group: 'composer',\n needs: [\n { kind: 'command', name: 'ollama', why: 'serves the model', optional: true },\n { kind: 'command', name: 'docker', why: 'runs Ollama for you when it is not installed', optional: true },\n ],\n brand: 'ollama',\n icon: { gui: 'i-ph-sparkle' },\n },\n {\n name: 'bolt',\n label: 'Bolt',\n description: 'Adds Bolt as a provider. Sign in with your Bolt account to use its models.',\n group: 'providers',\n needs: [{ kind: 'account', label: 'Bolt' }],\n provider: 'bolt',\n icon: { gui: 'i-ph-lightning' },\n },\n]\n\n/**\n * Folders of `extensions/` left out of the wizard on purpose, with the reason.\n * Every folder is either listed above or here.\n */\nexport const EXCLUDED_NATIVE_EXTENSIONS: Readonly<Record<string, string>> = {\n 'plugin-host': 'A demo of ctx.loadAgentPlugin for the repository\\'s own tests, not something to install.',\n 'headroom': 'Not offered in the setup wizard for now.',\n 'pxpipe': 'Not offered in the setup wizard for now.',\n 'antigravity': 'Not offered in the setup wizard for now. It is not official.',\n}\n\n/** A section of the Extensions step. */\nexport interface NativeExtensionGroupMeta {\n /** The section. */\n id: NativeExtensionGroup\n /** Its heading. */\n label: string\n}\n\n/** The step's sections, in display order. */\nexport const NATIVE_EXTENSION_GROUPS: readonly NativeExtensionGroupMeta[] = [\n { id: 'workflows', label: 'Workflows' },\n { id: 'integrations', label: 'Integrations' },\n { id: 'composer', label: 'Composer' },\n { id: 'providers', label: 'Providers' },\n]\n\n/**\n * A native extension by name, or `undefined`.\n *\n * @param name - The extension name.\n */\nexport function nativeExtensionByName(name: string): NativeExtensionEntry | undefined {\n return NATIVE_EXTENSIONS.find(entry => entry.name === name)\n}\n\n/**\n * The install source of a native extension, pinned to a release tag:\n * `Tahul/zidane#v<version>:extensions/<name>`, the grammar\n * `installExtensionFromSpec` takes.\n *\n * @param entry - The extension.\n * @param version - The running zidane version (`ZIDANE_VERSION`), without the `v`.\n * @example\n * ```ts\n * import { nativeExtensionByName, nativeExtensionSource } from 'zidane/chat/pure'\n *\n * nativeExtensionSource(nativeExtensionByName('goal')!, '8.1.0')\n * // 'Tahul/zidane#v8.1.0:extensions/goal'\n * ```\n */\nexport function nativeExtensionSource(entry: NativeExtensionEntry, version: string): string {\n return `${NATIVE_EXTENSIONS_REPO}#v${version.replace(/^v/, '')}:extensions/${entry.name}`\n}\n","/**\n * What a source of extensions holds, before an install: the shape\n * `listExtensionSource` (`zidane/chat`, Node) answers and the protocol's\n * `extensions.browse` carries, plus the native catalog as a listing for\n * when the default source cannot be read. Pure and browser-safe\n * (`zidane/chat/pure`).\n */\nimport type { NativeExtensionEntry } from './onboarding/types'\nimport { NATIVE_EXTENSIONS, NATIVE_EXTENSIONS_REPO } from './onboarding/native-extensions'\n\n/** The kind of a source: a GitHub repository (or a folder in one), an npm package, another link, a folder on the host. */\nexport type ExtensionSourceKind = 'github' | 'npm' | 'url' | 'local'\n\n/** Where a listing comes from. */\nexport interface ExtensionSourceInfo {\n /** Its kind. */\n kind: ExtensionSourceKind\n /** GitHub owner. */\n owner?: string\n /** GitHub repository. */\n repo?: string\n /** The branch or tag listed (GitHub: the default branch when none was given), or the npm version listed. */\n ref?: string\n /** npm package name. */\n package?: string\n /** A page to open: the repository, the folder on GitHub, the package on npmjs.com. */\n url?: string\n /** The folder on the host (`local`). */\n path?: string\n /** A short name: `Tahul/zidane`, `Tahul/zidane@v9.0.4`, `@scope/pkg`, the folder. */\n label: string\n}\n\n/**\n * Why a listing is empty or partial.\n *\n * - `not-found`: no such repository, branch, folder or package.\n * - `private`: the source exists but needs access (or GitHub hid it: a private repository answers 404 without a token).\n * - `rate-limit`: GitHub refused more calls without a token.\n * - `network`: the connection failed, or the server answered an error.\n * - `invalid`: the text is not a source this can list.\n */\nexport type ExtensionSourceErrorCode = 'not-found' | 'private' | 'rate-limit' | 'network' | 'invalid'\n\n/** An extension a source holds, read from its `package.json` (`zidane` field) or `extension.json`. Nothing of it ran. */\nexport interface ExtensionSourceEntry {\n /** Its name: the folder it installs as (the manifest's `name`, else the folder or package name). */\n name: string\n /** Its display name (`displayName` or `label`), else the name. */\n displayName: string\n /** What it does. */\n description?: string\n /** Its version (`package.json` `version`). */\n version?: string\n /** Its icon (the manifest's `icon`). */\n icon?: { gui?: string, tui?: string }\n /** Its logo from the native catalog (a Simple Icons slug), for the native extensions only. */\n brand?: string\n /** Its folder in the source, relative to the repository or package root (`.` for the root). */\n path: string\n /** The extension API version its manifest declares. */\n apiVersion?: number\n /**\n * What installs it: `installExtensionFromSpec({ source, extension })`\n * (`catalog.install` with `spec: source`). Absent for a local folder,\n * which the installer does not take.\n */\n install?: { source: string, extension?: string }\n}\n\n/** The extensions of one source, as `listExtensionSource` lists them. */\nexport interface ExtensionSourceListing {\n /** What was asked for, trimmed. */\n input: string\n /** Where the extensions come from. */\n source: ExtensionSourceInfo\n /** The extensions found. Empty with `error` when the source could not be read, except the default source, which lists the native catalog then (`builtin`). */\n extensions: ExtensionSourceEntry[]\n /** The listing stopped early: GitHub cut the file tree, or the source holds more than the cap. */\n truncated?: boolean\n /** The rows come from the catalog shipped in the app (`nativeExtensionListing`), not from the source. */\n builtin?: boolean\n /** Why the source could not be read. */\n error?: { code: ExtensionSourceErrorCode, message: string }\n}\n\n/** The default source of extensions: the zidane repository, whose `extensions/` folder holds the native extensions. */\nexport const DEFAULT_EXTENSION_SOURCE = NATIVE_EXTENSIONS_REPO\n\n/** What a listing of the default source says when it cannot be read. */\nexport const DEFAULT_EXTENSION_SOURCE_UNREACHABLE = `${NATIVE_EXTENSIONS_REPO} is private or out of reach. You need access to install from it.`\n\n/**\n * The default source pinned to a release, `Tahul/zidane#v<version>`: the\n * native extensions that match the running app's API.\n *\n * @param version - The running zidane version (`ZIDANE_VERSION`), with or without the `v`.\n * @example\n * ```ts\n * import { defaultExtensionSource } from 'zidane/chat/pure'\n *\n * defaultExtensionSource('9.0.4') // 'Tahul/zidane#v9.0.4'\n * ```\n */\nexport function defaultExtensionSource(version: string): string {\n return `${NATIVE_EXTENSIONS_REPO}#v${version.replace(/^v/, '')}`\n}\n\n/**\n * The listing row of a native extension, from the catalog shipped in the app.\n *\n * @param entry - The catalog entry.\n * @param source - The spec that installs from the repository (`defaultExtensionSource`).\n */\nexport function nativeExtensionEntry(entry: NativeExtensionEntry, source: string): ExtensionSourceEntry {\n return {\n name: entry.name,\n displayName: entry.label,\n description: entry.description,\n ...(entry.icon ? { icon: { ...entry.icon } } : {}),\n ...(entry.brand ? { brand: entry.brand } : {}),\n path: `extensions/${entry.name}`,\n apiVersion: 1,\n install: { source, extension: entry.name },\n }\n}\n\n/**\n * The native extensions as a listing of the default source pinned to\n * `version`, built from the catalog shipped in the app (`NATIVE_EXTENSIONS`):\n * what an app shows when the repository cannot be read. No network.\n *\n * @param version - The running zidane version.\n * @returns A listing with `builtin: true` and no error.\n */\nexport function nativeExtensionListing(version: string): ExtensionSourceListing {\n const ref = `v${version.replace(/^v/, '')}`\n const [owner, repo] = NATIVE_EXTENSIONS_REPO.split('/') as [string, string]\n const spec = defaultExtensionSource(version)\n return {\n input: spec,\n source: { kind: 'github', owner, repo, ref, url: `https://github.com/${NATIVE_EXTENSIONS_REPO}/tree/${ref}/extensions`, label: `${NATIVE_EXTENSIONS_REPO}@${ref}` },\n extensions: NATIVE_EXTENSIONS.map(entry => nativeExtensionEntry(entry, spec)),\n builtin: true,\n }\n}\n","/**\n * The logos of the services the wizard names, as slugs of the kit's logo set\n * (`BrandMark` in `zidane-ui`: Simple Icons, plus LobeHub Icons for the\n * model providers that Simple Icons lacks). Connectors and native extensions\n * carry theirs in their catalog entry. The providers, the other agents and the\n * local model servers have theirs here. A service with no slug shows a letter,\n * such as Pi, Context7, DeepWiki and Playwright.\n */\nimport type { OnboardingAgentId, OnboardingLocalServer } from './types'\n\n/** The logo of each built-in provider, by provider key. */\nexport const PROVIDER_BRANDS: Readonly<Record<string, string>> = {\n 'anthropic': 'anthropic',\n 'arcee': 'arcee',\n 'baseten': 'baseten',\n 'cerebras': 'cerebras',\n 'openai': 'openai',\n 'xai': 'xai',\n 'zai': 'zai',\n 'google': 'googlegemini',\n 'google-vertex': 'googlecloud',\n 'openrouter': 'openrouter',\n 'mistral': 'mistralai',\n 'cursor': 'cursor',\n}\n\n/** The logo of each agent whose setup the wizard reads. Pi has none. */\nexport const ONBOARDING_AGENT_BRANDS: Readonly<Record<OnboardingAgentId, string | undefined>> = {\n 'claude-code': 'claudecode',\n 'codex': 'codex',\n 'pi': undefined,\n}\n\n/** The logo of each local model server the wizard looks for. */\nexport const LOCAL_SERVER_BRANDS: Readonly<Record<OnboardingLocalServer['kind'], string>> = {\n 'ollama': 'ollama',\n 'lm-studio': 'lmstudio',\n 'vllm': 'vllm',\n}\n\n/**\n * The logo of a provider, as a Simple Icons slug.\n *\n * @param key - The provider key.\n * @example\n * ```ts\n * import { providerBrand } from 'zidane/chat/pure'\n *\n * providerBrand('google') // 'googlegemini'\n * ```\n */\nexport function providerBrand(key: string): string | undefined {\n return PROVIDER_BRANDS[key]\n}\n","/**\n * The toggles that the Features step offers, picked from the settings\n * catalog. Labels and descriptions are the ones that Settings shows\n * (`SETTINGS_TOGGLES`), so the step and Settings say the same thing. Each\n * feature also has an example, one sentence that shows it at work, and what\n * it unlocks and prevents. A feature's parts turn on with it (`includes`),\n * such as the agent's use of the browser tab, so the step has one switch per\n * feature.\n */\nimport type { BooleanSettingKey } from '../settings-catalog'\nimport type { OnboardingFeature, OnboardingFeatureGroup, OnboardingMode, OnboardingSurface } from './types'\nimport { SETTINGS_TOGGLES } from '../settings-catalog'\n\nconst BOTH: readonly OnboardingSurface[] = ['tui', 'desktop']\nconst DESKTOP: readonly OnboardingSurface[] = ['desktop']\n\n/** Every feature the step can offer, in display order. */\nexport const ONBOARDING_FEATURES: readonly OnboardingFeature[] = [\n {\n key: 'codemode',\n group: 'work',\n surfaces: BOTH,\n badges: ['experimental'],\n example: 'For example, to read 12 files, the model writes one script that reads all of them, instead of 12 tool calls.',\n unlocks: 'Many tool calls in one step. Long tasks take fewer turns, so runs finish faster and cost less.',\n prevents: 'A context full of small tool results. Only what the script prints goes back to the model.',\n },\n {\n key: 'webSearch',\n group: 'work',\n surfaces: BOTH,\n example: 'For example, a build fails after a library update. The agent searches the error and reads the release notes.',\n unlocks: 'Answers from the web: current docs, error messages, and changes newer than the model.',\n prevents: 'Off, the agent never reaches a search engine, and works only from your files and what it already knows.',\n },\n {\n key: 'inlineAutocomplete',\n group: 'work',\n surfaces: BOTH,\n example: 'For example, you type `src/comp`, and `onents/` shows in gray after it.',\n unlocks: 'Press Tab to finish a path, a command or a prompt that you type often.',\n prevents: 'Typing the same long paths and prompts again and again.',\n },\n {\n key: 'autoTitle',\n group: 'work',\n surfaces: BOTH,\n badges: ['billed'],\n example: 'For example, after your first message about a failed login, the session gets the title \"Fix the login redirect\".',\n unlocks: 'Session titles that follow what each session is about, so you find it again.',\n prevents: 'A session list where every title reads the same.',\n },\n {\n key: 'runEstimates',\n group: 'work',\n surfaces: DESKTOP,\n badges: ['billed'],\n example: 'For example, a session that migrates a test suite shows \"~12m\" in the sidebar, and its subagents show their own time left.',\n unlocks: 'A countdown on each running session and subagent, so you know when to come back.',\n prevents: 'Checking a long run again and again to see whether it is done.',\n },\n {\n key: 'spaces',\n group: 'work',\n surfaces: BOTH,\n example: 'For example, a Work space starts sessions on Opus, and a Personal space starts them on a local model.',\n unlocks: 'Separate session lists, each with its own default model, such as Work and Personal.',\n prevents: 'Work and personal sessions mixed in one long list.',\n },\n {\n key: 'checkForUpdates',\n group: 'background',\n surfaces: BOTH,\n example: 'For example, when a new version comes out, the footer tells you that an update is ready.',\n unlocks: 'A note in the footer when a new version is out.',\n prevents: 'Running an old version without knowing it.',\n },\n {\n key: 'subscriptionUsage',\n group: 'background',\n surfaces: BOTH,\n example: 'For example, zidane shows how much of your weekly Claude limit you used.',\n unlocks: 'Your plan usage in the footer, such as the share of your weekly limit.',\n prevents: 'A surprise stop when you reach your plan limit.',\n },\n {\n key: 'useDaemon',\n group: 'background',\n surfaces: DESKTOP,\n example: 'For example, you close the window during a long run. The run keeps going, and the next window shows it.',\n unlocks: 'Runs that keep going after you close the window, and one session open in several windows.',\n prevents: 'Losing a long run when its window closes.',\n },\n {\n key: 'browser',\n group: 'desktop',\n surfaces: DESKTOP,\n includes: ['browserAgent'],\n example: 'For example, you open your app at `localhost:5173` next to the chat. After an edit, the agent clicks the new button and checks the result.',\n unlocks: 'A browser tab next to the chat. The agent opens pages, clicks and reads them to check its own work.',\n prevents: 'Switching to another app to check your work, and changes that the agent never saw on screen.',\n },\n {\n key: 'terminal',\n group: 'desktop',\n surfaces: DESKTOP,\n includes: ['terminalAgent'],\n example: 'For example, you run your dev server in a terminal next to the chat. The agent reads its errors and asks you before it types.',\n unlocks: 'A terminal next to the chat. The agent reads its output, such as an error from your dev server.',\n prevents: 'Switching to another app to run a command, and commands that the agent types without your approval.',\n },\n {\n key: 'notifications',\n group: 'desktop',\n surfaces: DESKTOP,\n includes: ['notificationSound'],\n example: 'For example, you work in another app. A notification and a short sound tell you that a run finished.',\n unlocks: 'A system notification with a short sound when a run finishes or needs your answer.',\n prevents: 'A run that waits for you while you work in another app or look away.',\n },\n {\n key: 'loadProjectExtensions',\n group: 'trust',\n surfaces: BOTH,\n example: 'For example, a project adds a `/deploy` command in its `.zidane/extensions` folder.',\n unlocks: 'Extensions that a project ships load when you open it, such as its own commands.',\n prevents: 'Off, code from a repository that you clone never runs on its own.',\n },\n]\n\n/** A section of the Features step. */\nexport interface OnboardingFeatureGroupMeta {\n /** The section. */\n id: OnboardingFeatureGroup\n /** Its heading. */\n label: string\n}\n\n/** The step's sections, in display order. */\nexport const ONBOARDING_FEATURE_GROUPS: readonly OnboardingFeatureGroupMeta[] = [\n { id: 'desktop', label: 'Desktop' },\n { id: 'work', label: 'Work' },\n { id: 'trust', label: 'Trust' },\n { id: 'background', label: 'In the background' },\n]\n\n/**\n * The features that a run offers: the ones of its app. An `extras` run offers\n * only the ones that no other app offers, because the other app set the\n * shared ones.\n *\n * @param surface - The app that runs the wizard.\n * @param mode - The run's mode.\n */\nexport function onboardingFeatures(surface: OnboardingSurface, mode: OnboardingMode): OnboardingFeature[] {\n return ONBOARDING_FEATURES.filter((feature) => {\n if (!feature.surfaces.includes(surface))\n return false\n return mode !== 'extras' || feature.surfaces.length === 1\n })\n}\n\n/** A feature's label and description: the setting's own. */\nexport interface OnboardingFeatureCopy {\n /** Row label. */\n label: string\n /** One line on what it does. */\n description: string\n}\n\n/**\n * The label and description of a feature, from the settings catalog.\n *\n * @param key - The setting the feature flips.\n */\nexport function onboardingFeatureCopy(key: BooleanSettingKey): OnboardingFeatureCopy {\n const row = SETTINGS_TOGGLES.find(toggle => toggle.key === key)\n if (!row)\n throw new Error(`onboarding feature \"${key}\" has no row in SETTINGS_TOGGLES`)\n return { label: row.label, description: row.description }\n}\n","/**\n * Conversation presets: how much of the agent's work the transcript shows.\n * Balanced is the defaults themselves, so a change to `DEFAULT_SETTINGS`\n * moves it too and choosing it writes nothing. Both apps also offer the\n * presets in their command palette, through the entry built here.\n */\nimport type { PaletteItem, PaletteRunResult } from '../palette'\nimport type { Settings } from '../types'\nimport type { TranscriptPreset, TranscriptPresetId, TranscriptPresetKey, TranscriptPresetSettings } from './types'\nimport { DEFAULT_SETTINGS } from '../settings-defaults'\n\n/** Every setting a conversation preset sets, in the order the fine-tuning lists them. */\nexport const TRANSCRIPT_PRESET_KEYS: readonly TranscriptPresetKey[] = [\n 'transcriptDensity',\n 'showRowIcons',\n 'showThinking',\n 'thinkingAutoExpand',\n 'toolCallDisplay',\n 'showToolResults',\n 'groupToolCalls',\n 'showEditDiffs',\n 'editDiffDisplay',\n 'showRunReceipt',\n 'showToolAttachments',\n 'hideSubagentOutput',\n 'showCacheNotices',\n 'showFullShellOutput',\n 'showCompactionSummary',\n]\n\nfunction presetValues(settings: Settings): TranscriptPresetSettings {\n return Object.fromEntries(TRANSCRIPT_PRESET_KEYS.map(key => [key, settings[key]])) as TranscriptPresetSettings\n}\n\n/** The three presets, from the quietest to the most complete. Built by a pure call, so a bundle that never reads them drops them and the settings they read. */\nexport const TRANSCRIPT_PRESETS: readonly TranscriptPreset[] = /* @__PURE__ */ buildTranscriptPresets()\n\nfunction buildTranscriptPresets(): TranscriptPreset[] {\n const BALANCED = presetValues(DEFAULT_SETTINGS)\n return [\n {\n id: 'focused',\n label: 'Focused',\n description: 'Shows the answers. The work shows as one short row. Thinking and run costs are hidden.',\n settings: {\n ...BALANCED,\n transcriptDensity: 'compact',\n // The plainest look: no icon before each row (the desktop app's).\n showRowIcons: false,\n showThinking: false,\n thinkingAutoExpand: false,\n groupToolCalls: true,\n editDiffDisplay: 'compact',\n showRunReceipt: false,\n showToolAttachments: false,\n hideSubagentOutput: true,\n showCacheNotices: false,\n showFullShellOutput: false,\n showCompactionSummary: false,\n },\n },\n {\n id: 'balanced',\n label: 'Balanced',\n description: 'Shows the answers, one short row for the work, diffs and the cost of each run. The default.',\n settings: BALANCED,\n },\n {\n id: 'detailed',\n label: 'Detailed',\n description: 'Shows every step and its output, with thinking open and full diffs.',\n settings: {\n ...BALANCED,\n transcriptDensity: 'full',\n showRowIcons: true,\n showThinking: true,\n thinkingAutoExpand: true,\n groupToolCalls: false,\n editDiffDisplay: 'full',\n showRunReceipt: true,\n showToolAttachments: true,\n hideSubagentOutput: false,\n showCacheNotices: true,\n showFullShellOutput: true,\n showCompactionSummary: true,\n },\n },\n ]\n}\n\n/**\n * A preset by id.\n *\n * @param id - The preset.\n */\nexport function transcriptPreset(id: TranscriptPresetId): TranscriptPreset {\n return TRANSCRIPT_PRESETS.find(preset => preset.id === id)!\n}\n\n/**\n * The preset a set of settings matches, or `custom` when it matches none.\n * Keys left out take their default value.\n *\n * @param settings - Settings to read, such as the stored ones or a draft.\n * @example\n * ```ts\n * import { transcriptPresetOf } from 'zidane/chat/pure'\n *\n * transcriptPresetOf({}) // 'balanced'\n * transcriptPresetOf({ showThinking: false }) // 'custom'\n * ```\n */\nexport function transcriptPresetOf(settings: Partial<Settings>): TranscriptPresetId | 'custom' {\n const value = (key: TranscriptPresetKey): unknown => settings[key] ?? DEFAULT_SETTINGS[key]\n const match = TRANSCRIPT_PRESETS.find(preset => TRANSCRIPT_PRESET_KEYS.every(key => value(key) === preset.settings[key]))\n return match?.id ?? 'custom'\n}\n\n/** The id of the palette entry for the conversation preset. Both apps use it. */\nexport const TRANSCRIPT_PRESET_PALETTE_ID = 'setting:conversation-preset'\n\nconst PALETTE_CHOICE = `${TRANSCRIPT_PRESET_PALETTE_ID}:`\nconst PALETTE_TITLE = 'Conversation preset'\n\n/**\n * The palette entry for the conversation preset. It names the preset in\n * effect, and running it opens the presets ({@link transcriptPresetPalettePicker}).\n *\n * @param settings - The settings in effect.\n * @param source - The id of the palette provider that owns the entry.\n */\nexport function transcriptPresetPaletteItem(settings: Partial<Settings>, source: string): PaletteItem {\n const current = transcriptPresetOf(settings)\n return {\n id: TRANSCRIPT_PRESET_PALETTE_ID,\n title: PALETTE_TITLE,\n description: current === 'custom' ? 'Custom' : transcriptPreset(current).label,\n group: 'Settings',\n kind: 'picker',\n icon: { gui: 'i-ph-chats-circle' },\n keywords: ['conversation', 'transcript', 'output', 'focused', 'balanced', 'detailed', 'thinking', 'tool calls'],\n source,\n }\n}\n\n/**\n * The list that the palette entry opens: the three presets, the one in effect checked.\n *\n * @param settings - The settings in effect.\n * @param source - The id of the palette provider that owns the rows.\n */\nexport function transcriptPresetPalettePicker(settings: Partial<Settings>, source: string): Extract<PaletteRunResult, { kind: 'items' }> {\n const current = transcriptPresetOf(settings)\n return {\n kind: 'items',\n title: PALETTE_TITLE,\n items: TRANSCRIPT_PRESETS.map(preset => ({\n id: `${PALETTE_CHOICE}${preset.id}`,\n title: preset.label,\n description: preset.description,\n group: PALETTE_TITLE,\n kind: 'toggle',\n checked: current === preset.id,\n source,\n })),\n }\n}\n\n/**\n * The preset that a row of {@link transcriptPresetPalettePicker} applies.\n *\n * @param itemId - The id of the palette row that ran.\n * @returns The preset, or `undefined` for any other row.\n * @example\n * ```ts\n * import { transcriptPresetOfPaletteItem } from 'zidane/chat/pure'\n *\n * transcriptPresetOfPaletteItem('setting:conversation-preset:detailed') // 'detailed'\n * ```\n */\nexport function transcriptPresetOfPaletteItem(itemId: string): TranscriptPresetId | undefined {\n if (!itemId.startsWith(PALETTE_CHOICE))\n return undefined\n const id = itemId.slice(PALETTE_CHOICE.length)\n return TRANSCRIPT_PRESETS.find(preset => preset.id === id)?.id\n}\n\n/**\n * The line that the palette shows once a preset applies.\n *\n * @param id - The preset that applied.\n */\nexport function transcriptPresetNotice(id: TranscriptPresetId): string {\n return `${PALETTE_TITLE}: ${transcriptPreset(id).label}`\n}\n","/**\n * The setup wizard's steps: their order, scope and copy, which of them a run\n * shows, and the palette entry that opens the wizard again. Pure data and\n * functions, shared by every renderer.\n */\nimport type { OnboardingPlanContext, OnboardingStepId, OnboardingStepMeta } from './types'\n\n/**\n * Version of the wizard, stored in each record it writes. A record from an\n * older version still counts as done: existing users open new steps from\n * Settings or the palette, never on their own.\n */\nexport const ONBOARDING_VERSION = 1\n\n/** Every step in flow order, with what it writes and its copy. */\nexport const ONBOARDING_STEPS: readonly OnboardingStepMeta[] = [\n { id: 'welcome', scope: 'none', label: 'Welcome', title: 'Set up zidane', lede: 'This takes about two minutes. You can change every choice later in Settings.' },\n { id: 'connect', scope: 'shared', label: 'Connect', title: 'Choose an AI provider', lede: 'The provider runs the model that answers you. Your keys stay on this computer, in a file that only you can read.' },\n { id: 'import', scope: 'shared', label: 'Bring your setup', title: 'Use your setup from other agents', lede: 'zidane copies what you pick. It does not change the files of the other agent.' },\n { id: 'connectors', scope: 'shared', label: 'Connectors', title: 'Connect your tools', lede: 'The agent can read and change data in these services. Most of them ask you to sign in through your browser.' },\n { id: 'skills', scope: 'shared', label: 'Skills', title: 'Teach zidane how you work', lede: 'A skill is a folder of instructions. The agent reads one when a task needs it.' },\n { id: 'extensions', scope: 'shared', label: 'Extensions', title: 'Add extensions', lede: 'Extensions add commands and features. zidane installs them for you from its repository.' },\n { id: 'look', scope: 'surface', label: 'Look', title: 'Choose how zidane looks', lede: 'The preview shows each choice at once.' },\n { id: 'conversation', scope: 'shared', label: 'Conversation', title: 'How much detail do you want to see?', lede: 'Pick a preset. The preview shows a sample conversation with it.' },\n { id: 'autonomy', scope: 'shared', label: 'Autonomy', title: 'What can zidane do without asking?', lede: 'Commands in your safelist always run, whatever you pick. Your safelist holds the commands that you allowed with Always allow.' },\n { id: 'features', scope: 'shared', label: 'Features', title: 'Turn on what you want', lede: 'You can change these, and more, in Settings.' },\n { id: 'ready', scope: 'none', label: 'Ready', title: 'You are ready', lede: 'Type a task and press Enter to start.' },\n]\n\nconst STEP_ORDER: readonly OnboardingStepId[] = /* @__PURE__ */ stepIds()\n\nfunction stepIds(): OnboardingStepId[] {\n return ONBOARDING_STEPS.map(step => step.id)\n}\n\n/**\n * The display data of a step.\n *\n * @param id - The step.\n */\nexport function onboardingStep(id: OnboardingStepId): OnboardingStepMeta {\n return ONBOARDING_STEPS.find(step => step.id === id)!\n}\n\n/**\n * The steps that a run shows, in order. It starts from the steps of the mode.\n * It leaves out Bring your setup when zidane found no other agent, and\n * Connectors, Skills and Extensions when installs do not run on this computer. It\n * also leaves out the steps that the host excludes. A `connect` run always\n * has Connect.\n *\n * @param ctx - The run's mode, surface and what was found.\n * @example\n * ```ts\n * import { planOnboarding } from 'zidane/chat/pure'\n *\n * planOnboarding({ mode: 'quick', surface: 'tui', importAvailable: false, installsAvailable: true })\n * // ['welcome', 'connect', 'ready']\n * ```\n */\nexport function planOnboarding(ctx: OnboardingPlanContext): OnboardingStepId[] {\n const applies = (id: OnboardingStepId): boolean => {\n if (ctx.exclude?.includes(id))\n return false\n if (id === 'import')\n return ctx.importAvailable\n if (id === 'connectors' || id === 'skills' || id === 'extensions')\n return ctx.installsAvailable\n return true\n }\n switch (ctx.mode) {\n case 'full':\n return STEP_ORDER.filter(applies)\n case 'rerun':\n return STEP_ORDER.filter(id => id !== 'welcome' && applies(id))\n case 'quick':\n return (['welcome', 'connect', 'import', 'ready'] as const).filter(applies)\n case 'extras':\n return (['look', 'features', 'ready'] as const).filter(applies)\n case 'connect':\n return ['connect']\n }\n}\n\n/** The command palette's entry for the wizard. */\nexport interface SetupPaletteItem {\n /** Palette item id, the same in both apps. */\n id: string\n /** Row title. */\n title: string\n /** Text beside the title. */\n description: string\n /** Extra search terms. */\n keywords: readonly string[]\n}\n\n/** \"Run setup\", the palette entry both apps list to open the wizard again. */\nexport const SETUP_PALETTE_ITEM: SetupPaletteItem = {\n id: 'app:setup',\n title: 'Run setup',\n description: 'Open the setup wizard: provider, connectors, skills, extensions, look, conversation, autonomy and features.',\n keywords: ['setup', 'onboarding', 'wizard', 'welcome', 'get started', 'provider', 'theme', 'connectors', 'skills', 'extensions'],\n}\n","import type { SkillPick } from '../../skills/sources'\n/**\n * The setup wizard's state machine: which step is current, what has been\n * picked, and the background tasks the steps started. One reducer, driven by\n * the TUI and by `zidane-ui` alike, so both walk the steps the same way.\n */\nimport type { BooleanSettingKey } from '../settings-catalog'\nimport type { Settings } from '../types'\nimport type {\n OnboardingAppearance,\n OnboardingDraft,\n OnboardingImportSelection,\n OnboardingPlanContext,\n OnboardingProviderPick,\n OnboardingStepId,\n OnboardingTask,\n TranscriptPresetId,\n} from './types'\nimport { enforceSettingsInvariants, SETTINGS_TOGGLES } from '../settings-catalog'\nimport { ONBOARDING_FEATURES } from './features'\nimport { TRANSCRIPT_PRESET_KEYS, transcriptPreset } from './presets'\nimport { planOnboarding } from './steps'\n\nfunction toggleRequires(key: string): BooleanSettingKey | undefined {\n return SETTINGS_TOGGLES.find(toggle => toggle.key === key)?.requires\n}\n\nfunction featureIncludes(key: string): readonly BooleanSettingKey[] {\n return ONBOARDING_FEATURES.find(feature => feature.key === key)?.includes ?? []\n}\n\n/** The settings of the Features step: each feature, what it includes, and what those need. */\nfunction featureStepSettings(): BooleanSettingKey[] {\n const keys = ONBOARDING_FEATURES.flatMap(feature => [feature.key, ...(feature.includes ?? [])])\n return [...new Set([...keys, ...keys.flatMap(key => toggleRequires(key) ?? [])])]\n}\n\n/** The settings each step edits. Skipping a step puts them back. */\nexport const ONBOARDING_STEP_SETTINGS: Partial<Record<OnboardingStepId, readonly (keyof Settings)[]>> = {\n import: ['importForeignConfigs'],\n look: ['theme', 'mouseInteractions'],\n conversation: TRANSCRIPT_PRESET_KEYS,\n autonomy: ['approvalMode', 'sandbox', 'sandboxNetwork', 'maxCostUsd'],\n features: featureStepSettings(),\n}\n\n/** Every setting the wizard can change. */\nexport const ONBOARDING_SETTING_KEYS: readonly (keyof Settings)[] = [...new Set(Object.values(ONBOARDING_STEP_SETTINGS).flatMap(keys => keys ?? []))]\n\n/** Where a run is. */\nexport interface OnboardingFlowState {\n /** What decides the steps. `mode` is the mode the run opened in. */\n context: OnboardingPlanContext\n /** The steps of this run, in order. */\n steps: readonly OnboardingStepId[]\n /** Index of the current step in `steps`. */\n index: number\n /** Everything picked so far. */\n draft: OnboardingDraft\n /** The draft the run started from: what Skip goes back to, and what the result diffs against. */\n initial: OnboardingDraft\n /** Imports and installs the steps started. */\n tasks: readonly OnboardingTask[]\n /** The last step was passed: the run is finished. */\n done: boolean\n}\n\n/**\n * What can happen in a run.\n *\n * - `next` / `back`: move one step. `next` on the last step finishes the run.\n * - `goto`: jump to a step of the run, such as from Ready's recap.\n * - `skip`: put the current step's picks back, then move on.\n * - `finish`: end the run now, keeping what was picked (Skip setup).\n * - `choose-path`: Welcome's choice. Quick start cuts the run down to Connect, Bring your setup and Ready.\n * - `set-context`: the detection finished. The steps are planned again and the current one kept.\n * - `set-settings`: change settings. Turning a setting on also turns on what it needs and, for a feature, what it includes. Turning one off turns off what needs it.\n * - `apply-preset`: set every transcript setting to a preset's values.\n * - `set-provider`, `set-imports`, `set-appearance`: Connect's, Bring your setup's and Look's picks.\n * - `toggle-connector`, `toggle-extension`: pick or unpick a connector or a native extension.\n * - `toggle-skills`: pick or unpick skills, one or a whole listing at once.\n * - `task`: add or update a background task, matched by id.\n */\nexport type OnboardingAction\n = | { type: 'next' }\n | { type: 'back' }\n | { type: 'goto', step: OnboardingStepId }\n | { type: 'skip' }\n | { type: 'finish' }\n | { type: 'choose-path', path: OnboardingDraft['path'] }\n | { type: 'set-context', context: Partial<OnboardingPlanContext> }\n | { type: 'set-settings', settings: Partial<Settings> }\n | { type: 'apply-preset', preset: TranscriptPresetId }\n | { type: 'set-provider', provider: OnboardingProviderPick | undefined }\n | { type: 'set-imports', imports: OnboardingImportSelection | undefined }\n | { type: 'set-appearance', appearance: OnboardingAppearance | undefined }\n | { type: 'toggle-connector', id: string, on: boolean }\n | { type: 'toggle-extension', name: string, on: boolean }\n | { type: 'toggle-skills', picks: readonly SkillPick[], on: boolean }\n | { type: 'task', task: OnboardingTask }\n\n/** The draft a run starts from. */\nexport interface OnboardingStart {\n /** The settings in effect now: the stored ones over the defaults. */\n settings: Settings\n /** The desktop app's current look, for the desktop app. */\n appearance?: OnboardingAppearance\n /** The provider in use now, for a re-run. */\n provider?: OnboardingProviderPick\n}\n\nfunction stepsFor(context: OnboardingPlanContext, path: OnboardingDraft['path']): OnboardingStepId[] {\n const mode = context.mode === 'full' && path === 'quick' ? 'quick' : context.mode\n return planOnboarding({ ...context, mode })\n}\n\n/**\n * A new run at its first step.\n *\n * @param context - The run's mode, surface and what was found so far.\n * @param start - The settings, look and provider in effect now.\n */\nexport function createOnboardingFlow(context: OnboardingPlanContext, start: OnboardingStart): OnboardingFlowState {\n const draft: OnboardingDraft = {\n path: 'custom',\n connectors: [],\n skills: [],\n extensions: [],\n settings: { ...start.settings },\n ...(start.provider ? { provider: start.provider } : {}),\n ...(start.appearance ? { appearance: start.appearance } : {}),\n }\n return { context, steps: stepsFor(context, draft.path), index: 0, draft, initial: draft, tasks: [], done: false }\n}\n\n/**\n * Settings with a change applied. A setting that turns on also turns on the\n * one that it needs, so Session titles turns on Decisions, and a feature\n * turns on what it includes, so Browser tab turns on Agent can use the\n * browser. A setting that turns off also turns off the ones that need it.\n *\n * @param settings - The settings before the change.\n * @param patch - The settings to change.\n */\nexport function applySettingsPatch(settings: Partial<Settings>, patch: Partial<Settings>): Partial<Settings> {\n const next: Partial<Settings> = { ...settings, ...patch }\n for (const [key, value] of Object.entries(patch)) {\n if (value !== true)\n continue\n const requires = toggleRequires(key)\n if (requires && next[requires] !== true)\n next[requires] = true\n for (const included of featureIncludes(key))\n next[included] = true\n }\n return enforceSettingsInvariants(next)\n}\n\n/**\n * The current step.\n *\n * @param state - The run.\n */\nexport function currentOnboardingStep(state: OnboardingFlowState): OnboardingStepId {\n return state.steps[state.index]!\n}\n\n/**\n * Whether the user can skip the current step. Welcome and Ready have nothing\n * to skip, and the TUI cannot run without a provider.\n *\n * @param state - The run.\n */\nexport function canSkipOnboardingStep(state: OnboardingFlowState): boolean {\n const step = currentOnboardingStep(state)\n if (step === 'welcome' || step === 'ready')\n return false\n return !(step === 'connect' && state.context.surface === 'tui')\n}\n\nfunction restoreStep(state: OnboardingFlowState, step: OnboardingStepId): OnboardingDraft {\n const { draft, initial } = state\n const keys = ONBOARDING_STEP_SETTINGS[step] ?? []\n const settings = keys.length ? { ...draft.settings, ...Object.fromEntries(keys.map(key => [key, initial.settings[key]])) } : draft.settings\n const next: OnboardingDraft = { ...draft, settings }\n if (step === 'connect')\n next.provider = initial.provider\n if (step === 'import')\n next.imports = initial.imports\n if (step === 'connectors')\n next.connectors = initial.connectors\n if (step === 'skills')\n next.skills = initial.skills\n if (step === 'extensions')\n next.extensions = initial.extensions\n if (step === 'look')\n next.appearance = initial.appearance\n return next\n}\n\nfunction toggledSkills(list: readonly SkillPick[], picks: readonly SkillPick[], on: boolean): readonly SkillPick[] {\n const keys = new Set(picks.map(pick => pick.key))\n const without = list.filter(pick => !keys.has(pick.key))\n return on ? [...without, ...picks.filter((pick, i) => picks.findIndex(other => other.key === pick.key) === i)] : without\n}\n\nfunction toggled(list: readonly string[], item: string, on: boolean): readonly string[] {\n const without = list.filter(entry => entry !== item)\n return on ? [...without, item] : without\n}\n\nfunction move(state: OnboardingFlowState, delta: 1 | -1): OnboardingFlowState {\n const index = state.index + delta\n if (index >= state.steps.length)\n return { ...state, done: true }\n return index < 0 ? state : { ...state, index }\n}\n\nfunction replan(state: OnboardingFlowState, context: OnboardingPlanContext, path: OnboardingDraft['path']): OnboardingFlowState {\n const current = currentOnboardingStep(state)\n const steps = stepsFor(context, path)\n const kept = steps.indexOf(current)\n return { ...state, context, steps, index: kept >= 0 ? kept : Math.min(state.index, steps.length - 1) }\n}\n\nfunction withDraft(state: OnboardingFlowState, draft: Partial<OnboardingDraft>): OnboardingFlowState {\n return { ...state, draft: { ...state.draft, ...draft } }\n}\n\n/**\n * The next state of a run.\n *\n * @param state - The run now.\n * @param action - What happened.\n * @example\n * ```ts\n * import { createOnboardingFlow, currentOnboardingStep, DEFAULT_SETTINGS, onboardingReducer } from 'zidane/chat/pure'\n *\n * let flow = createOnboardingFlow(\n * { mode: 'full', surface: 'tui', importAvailable: false, installsAvailable: true },\n * { settings: DEFAULT_SETTINGS },\n * )\n * flow = onboardingReducer(flow, { type: 'next' })\n * currentOnboardingStep(flow) // 'connect'\n * ```\n */\nexport function onboardingReducer(state: OnboardingFlowState, action: OnboardingAction): OnboardingFlowState {\n switch (action.type) {\n case 'next':\n return move(state, 1)\n case 'back':\n return move(state, -1)\n case 'goto': {\n const index = state.steps.indexOf(action.step)\n return index < 0 ? state : { ...state, index, done: false }\n }\n case 'skip':\n return move({ ...state, draft: restoreStep(state, currentOnboardingStep(state)) }, 1)\n case 'finish':\n return { ...state, done: true }\n case 'choose-path':\n return replan(withDraft(state, { path: action.path }), state.context, action.path)\n case 'set-context':\n return replan(state, { ...state.context, ...action.context }, state.draft.path)\n case 'set-settings':\n return withDraft(state, { settings: applySettingsPatch(state.draft.settings, action.settings) })\n case 'apply-preset':\n return withDraft(state, { settings: applySettingsPatch(state.draft.settings, transcriptPreset(action.preset).settings) })\n case 'set-provider':\n return withDraft(state, { provider: action.provider })\n case 'set-imports':\n return withDraft(state, { imports: action.imports })\n case 'set-appearance':\n return withDraft(state, { appearance: action.appearance })\n case 'toggle-connector':\n return withDraft(state, { connectors: toggled(state.draft.connectors, action.id, action.on) })\n case 'toggle-extension':\n return withDraft(state, { extensions: toggled(state.draft.extensions, action.name, action.on) })\n case 'toggle-skills':\n return withDraft(state, { skills: toggledSkills(state.draft.skills, action.picks, action.on) })\n case 'task': {\n const exists = state.tasks.some(task => task.id === action.task.id)\n const tasks = exists ? state.tasks.map(task => (task.id === action.task.id ? action.task : task)) : [...state.tasks, action.task]\n return { ...state, tasks }\n }\n }\n}\n\n/** How far a run is: the current step's position and the number of steps. */\nexport interface OnboardingProgress {\n /** Zero-based position of the current step. */\n index: number\n /** Number of steps in the run. */\n count: number\n}\n\n/**\n * How far a run is.\n *\n * @param state - The run.\n */\nexport function onboardingProgress(state: OnboardingFlowState): OnboardingProgress {\n return { index: state.index, count: state.steps.length }\n}\n","/**\n * What a run writes, and when an app opens the wizard: the sparse settings\n * change, the records in the state file, and the launch gate.\n */\nimport type { Settings } from '../types'\nimport type { OnboardingFlowState } from './flow'\nimport type {\n OnboardingGate,\n OnboardingInstallSignals,\n OnboardingMode,\n OnboardingRecord,\n OnboardingResult,\n OnboardingSettingsChange,\n OnboardingState,\n OnboardingSurface,\n} from './types'\nimport { requirementMet, SETTINGS_TOGGLES } from '../settings-catalog'\nimport { DEFAULT_SETTINGS } from '../settings-defaults'\nimport { ONBOARDING_SETTING_KEYS } from './flow'\nimport { ONBOARDING_VERSION } from './steps'\n\nfunction sameValue(a: unknown, b: unknown): boolean {\n if (Object.is(a, b))\n return true\n return typeof a === 'object' && a !== null && typeof b === 'object' && b !== null && JSON.stringify(a) === JSON.stringify(b)\n}\n\n/** Off only because the setting it needs is off, such as Agent can use your terminals with Terminals off. */\nfunction forcedOff(settings: Partial<Settings>, key: keyof Settings): boolean {\n const requires = SETTINGS_TOGGLES.find(toggle => toggle.key === key)?.requires\n return settings[key] === false && requires !== undefined && !requirementMet(settings, requires)\n}\n\n/**\n * The settings that a run writes. Only the keys that the run changed count. A\n * value that equals the default is not stored, so it keeps following the\n * default. If the key is stored, the run resets it instead of pinning it. A\n * setting that is off only because the one it needs is off is not written:\n * it comes back as it was when that one turns on again.\n *\n * @param draft - The settings the run ends with.\n * @param initial - The settings the run started with.\n * @param stored - The settings slice stored now (explicit choices only, without defaults).\n * @example\n * ```ts\n * import { DEFAULT_SETTINGS, onboardingSettingsChange } from 'zidane/chat/pure'\n *\n * onboardingSettingsChange({ ...DEFAULT_SETTINGS, spaces: true }, DEFAULT_SETTINGS, {})\n * // { set: { spaces: true }, reset: [] }\n * ```\n */\nexport function onboardingSettingsChange(draft: Partial<Settings>, initial: Partial<Settings>, stored: Partial<Settings>): OnboardingSettingsChange {\n const set: Partial<Settings> = {}\n const reset: (keyof Settings)[] = []\n for (const key of ONBOARDING_SETTING_KEYS) {\n if (!(key in draft) || sameValue(draft[key], initial[key]) || forcedOff(draft, key))\n continue\n if (sameValue(draft[key], DEFAULT_SETTINGS[key])) {\n if (key in stored)\n reset.push(key)\n continue\n }\n Object.assign(set, { [key]: draft[key] })\n }\n return { set, reset }\n}\n\n/**\n * Whether the install existed before the wizard: it has stored settings,\n * credentials, sessions or a last-used provider.\n *\n * @param signals - What the app read from this machine.\n */\nexport function isExistingInstall(signals: OnboardingInstallSignals): boolean {\n return signals.storedSettings || signals.credentials || signals.sessions || signals.lastProvider\n}\n\n/** What {@link needsOnboarding} reads. */\nexport interface OnboardingGateInput {\n /** The records in the state file. */\n state: OnboardingState | undefined\n /** What the app read from this machine. */\n signals: OnboardingInstallSignals\n /** The app asking. */\n surface: OnboardingSurface\n /** The wizard is turned off: `ZIDANE_NO_ONBOARDING=1`, an embedder's `onboarding: false`, a headless run. */\n disabled?: boolean\n}\n\n/**\n * What an app does at launch about the wizard (`docs/ONBOARDING.md`, When it opens).\n *\n * @param input - The records, the machine's signals and the app.\n * @example\n * ```ts\n * import { needsOnboarding } from 'zidane/chat/pure'\n *\n * const signals = { storedSettings: false, credentials: false, sessions: false, lastProvider: false, surfaceFirstRun: true }\n * needsOnboarding({ state: undefined, signals, surface: 'tui' }) // { kind: 'run', mode: 'full' }\n * ```\n */\nexport function needsOnboarding(input: OnboardingGateInput): OnboardingGate {\n if (input.disabled)\n return { kind: 'none' }\n const state = input.state ?? {}\n if (!state.shared) {\n if (state.inProgress || !isExistingInstall(input.signals))\n return { kind: 'run', mode: 'full' }\n return { kind: 'migrate' }\n }\n if (state[input.surface] || state.shared.migrated || state.shared.surface === input.surface)\n return { kind: 'none' }\n return input.signals.surfaceFirstRun ? { kind: 'run', mode: 'extras' } : { kind: 'none' }\n}\n\n/**\n * A record for a run that just ended.\n *\n * @param surface - The app it ran in.\n * @param now - When it ended.\n * @param migrated - Written for an install that predates the wizard.\n */\nexport function onboardingRecord(surface: OnboardingSurface, now: Date = new Date(), migrated = false): OnboardingRecord {\n return { version: ONBOARDING_VERSION, completedAt: now.toISOString(), surface, ...(migrated ? { migrated: true as const } : {}) }\n}\n\n/**\n * The records that a finished run writes. A full, quick or repeated run writes\n * the shared record and the record of this app. An extras run writes only the\n * record of this app. Connect alone writes none.\n *\n * @param mode - The mode the run ended in.\n * @param surface - The app it ran in.\n * @param now - When it ended.\n */\nexport function onboardingRecords(mode: OnboardingMode, surface: OnboardingSurface, now: Date = new Date()): OnboardingState {\n if (mode === 'connect')\n return {}\n const record = onboardingRecord(surface, now)\n const own = surfaceRecord(surface, record)\n return mode === 'extras' ? own : { shared: record, ...own }\n}\n\nfunction surfaceRecord(surface: OnboardingSurface, record: OnboardingRecord): OnboardingState {\n return surface === 'tui' ? { tui: record } : { desktop: record }\n}\n\n/**\n * The records an install that predates the wizard gets, so it never opens on its own.\n *\n * @param surface - The app that noticed.\n * @param now - When.\n */\nexport function migratedOnboardingRecords(surface: OnboardingSurface, now: Date = new Date()): OnboardingState {\n const record = onboardingRecord(surface, now, true)\n return { shared: record, ...surfaceRecord(surface, record) }\n}\n\n/**\n * Stored records with new ones merged in. A finished run (any record) clears\n * the in-progress marker.\n *\n * @param stored - The records in the state file now.\n * @param records - The records to add, such as {@link OnboardingResult.records} or an `inProgress` marker.\n */\nexport function mergeOnboardingState(stored: OnboardingState | undefined, records: OnboardingState): OnboardingState {\n const next: OnboardingState = { ...stored, ...records }\n if (records.shared || records.tui || records.desktop)\n delete next.inProgress\n return next\n}\n\n/**\n * The mode a run ends in: Quick start turns a full run into a quick one.\n *\n * @param state - The run.\n */\nexport function effectiveOnboardingMode(state: OnboardingFlowState): OnboardingMode {\n return state.context.mode === 'full' && state.draft.path === 'quick' ? 'quick' : state.context.mode\n}\n\n/**\n * What a finished run writes: the settings change, the provider pick, the\n * look and the records.\n *\n * @param state - The finished run.\n * @param stored - The settings slice stored now.\n * @param now - When it finished.\n */\nexport function buildOnboardingResult(state: OnboardingFlowState, stored: Partial<Settings>, now: Date = new Date()): OnboardingResult {\n const mode = effectiveOnboardingMode(state)\n const { draft, initial } = state\n const provider = draft.provider && !sameValue(draft.provider, initial.provider) ? draft.provider : undefined\n const appearance = draft.appearance && !sameValue(draft.appearance, initial.appearance) ? draft.appearance : undefined\n return {\n mode,\n surface: state.context.surface,\n settings: onboardingSettingsChange(draft.settings, initial.settings, stored),\n ...(provider ? { provider } : {}),\n ...(appearance ? { appearance } : {}),\n records: onboardingRecords(mode, state.context.surface, now),\n }\n}\n","/**\n * The welcome screen's usage summary (`usage.summary`, the desktop app's\n * `usage:summary`): tokens, spend, models, streaks and the activity heatmap,\n * reduced from per-day buckets of a sessions database\n * (`../session/sqlite-usage.ts`).\n *\n * - Tokens and the model split come from assistant turns (`usage.modelId`,\n * `input` / `output` / `cacheRead` / `cacheCreation`): runs carry no\n * per-model attribution.\n * - Spend comes from assistant-turn `usage.cost`, not run costs: a run that\n * died before its stats landed lost its cost, and a run can't be split by\n * provider.\n * - Messages are parent user turns only, so spawned prompts don't count.\n *\n * Days are the host's local days: the buckets are `date(…, 'localtime')` and\n * {@link localDayString} must agree with them, or streaks drift at midnight.\n * Every range is cut from the same buckets.\n */\nimport type { UsageBuckets } from '../session/sqlite-usage'\n\n/** The summary's window. */\nexport type UsageRange = 'all' | '30d' | '7d'\n\n/** The ranges a summary is asked for. */\nexport const USAGE_RANGES: readonly UsageRange[] = ['all', '30d', '7d']\n\n/**\n * Per-model usage over the summary's range. `tokensIn` counts everything\n * sent to the model, prompt input PLUS cache reads and creation: raw `input`\n * under-reports cache-heavy providers by orders of magnitude. `cost` is USD.\n */\nexport interface UsageModelRow {\n /** The model id. */\n modelId: string\n /** Display name inferred from the model id's shape ({@link providerOfModel}). */\n provider: string\n /** Assistant turns. */\n turns: number\n /** Tokens sent, cache included. */\n tokensIn: number\n /** Tokens produced. */\n tokensOut: number\n /** USD. */\n cost: number\n}\n\n/** One stacked-bar segment of the Models chart: tokens for a model on a day. */\nexport interface UsageModelDayPoint {\n /** Local `YYYY-MM-DD`. */\n day: string\n /** The model id. */\n modelId: string\n /** Tokens in and out. */\n tokens: number\n}\n\n/** Spend per provider. */\nexport interface UsageProviderRow {\n /** Display name ({@link providerOfModel}). */\n provider: string\n /** USD. */\n cost: number\n}\n\n/** One heatmap cell. Sparse: days without activity are left out. */\nexport interface UsageHeatmapDay {\n /** Local `YYYY-MM-DD`. */\n day: string\n /** Tokens in and out that day. */\n tokens: number\n /** Human messages that day. */\n messages: number\n}\n\n/** Usage and spend over a range. The heatmap always spans the last ~26 weeks, whatever the range. */\nexport interface UsageSummary {\n /** The range it covers. */\n range: UsageRange\n /** First name for the greeting; `null` when unknown. */\n userName: string | null\n /** Sessions created in range. */\n sessions: number\n /** Parent (human) user turns: spawned prompts excluded. */\n messages: number\n /** Tokens in and out. */\n totalTokens: number\n /** Tokens sent, cache included. */\n tokensIn: number\n /** Tokens produced. */\n tokensOut: number\n /** USD, from assistant-turn usage costs. */\n totalCost: number\n /** Days with a human message. */\n activeDays: number\n /** Consecutive active days up to today (or yesterday). */\n currentStreak: number\n /** The longest run of active days. */\n longestStreak: number\n /** Local hour (0-23) with the most messages; `null` without data. */\n peakHour: number | null\n /** The model with the most turns; `null` without data. */\n favoriteModel: string | null\n /** Per model, most tokens first. */\n models: UsageModelRow[]\n /** The Models chart's points (the last 120 days at most). */\n modelDaily: UsageModelDayPoint[]\n /** Spend per provider, largest first. */\n providers: UsageProviderRow[]\n /** The activity heatmap's days. */\n heatmap: UsageHeatmapDay[]\n /** When it was computed (ms). */\n generatedAt: number\n}\n\n/** Days of daily-series history the Models chart is capped at. */\nconst CHART_MAX_DAYS = 120\n/** Heatmap window: ~26 weeks. */\nconst HEATMAP_DAYS = 182\nconst DAY_MS = 86_400_000\n\n/**\n * Local `YYYY-MM-DD` of a date, as SQLite's `date(…, 'localtime')` buckets it.\n *\n * @param d - The date.\n */\nexport function localDayString(d: Date): string {\n return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`\n}\n\n/** `days - 1` back from `today`: the first day of a window of `days` days. */\nfunction cutoffDay(today: Date, days: number): string {\n return localDayString(new Date(today.getFullYear(), today.getMonth(), today.getDate() - (days - 1)))\n}\n\n/**\n * The provider a model id belongs to. A session rarely records its provider,\n * so the id's shape is the only signal that covers every turn. Slash ids are\n * OpenRouter-style routes and win over a vendor prefix inside the path.\n *\n * @param modelId - The model id a turn's usage names.\n */\nexport function providerOfModel(modelId: string): string {\n const id = modelId.toLowerCase()\n if (id === 'unknown')\n return 'Unknown'\n if (id.includes('/'))\n return 'OpenRouter'\n if (id.startsWith('claude'))\n return 'Anthropic'\n if (id.startsWith('gpt') || /^o\\d/.test(id) || id.startsWith('codex'))\n return 'OpenAI'\n if (id.startsWith('gemini'))\n return 'Google'\n if (id.startsWith('grok'))\n return 'xAI'\n if (id.startsWith('zai') || id.startsWith('glm'))\n return 'Z.AI'\n if (id.startsWith('deepseek'))\n return 'DeepSeek'\n if (id.startsWith('kimi') || id.startsWith('moonshot'))\n return 'Moonshot'\n if (id.startsWith('mistral') || id.startsWith('devstral') || id.startsWith('magistral'))\n return 'Mistral'\n return 'Other'\n}\n\n/**\n * Streaks over the active days (days with a human message). `current`\n * counts back from today and forgives a today not active yet: a streak isn't\n * broken at 9am.\n *\n * @param activeDays - Local `YYYY-MM-DD` days.\n * @param today - The day streaks count back from.\n */\nexport function computeStreaks(activeDays: ReadonlySet<string>, today: Date): { current: number, longest: number } {\n if (activeDays.size === 0)\n return { current: 0, longest: 0 }\n let current = 0\n let probe = new Date(today.getFullYear(), today.getMonth(), today.getDate()).getTime()\n if (!activeDays.has(localDayString(new Date(probe))))\n probe -= DAY_MS\n while (activeDays.has(localDayString(new Date(probe)))) {\n current += 1\n probe -= DAY_MS\n }\n // `YYYY-MM-DD` sorts chronologically; count runs of consecutive calendar days.\n let longest = 0\n let run = 0\n let prev: number | null = null\n for (const day of [...activeDays].sort()) {\n const [y, m, d] = day.split('-').map(Number)\n const t = new Date(y!, m! - 1, d!).getTime()\n run = prev !== null && t - prev === DAY_MS ? run + 1 : 1\n prev = t\n longest = Math.max(longest, run)\n }\n return { current, longest }\n}\n\n/**\n * The rows of one project.\n *\n * @param buckets - Every project's rows.\n * @param projectRoot - The project root.\n */\nexport function usageOfProject(buckets: UsageBuckets, projectRoot: string): UsageBuckets {\n const mine = (row: { project?: string }) => row.project === projectRoot\n return { assistant: buckets.assistant.filter(mine), user: buckets.user.filter(mine), sessions: buckets.sessions.filter(mine) }\n}\n\n/**\n * The summary of `range`, cut from a database's day buckets.\n *\n * @param allBuckets - The per-day aggregates (`runUsageAggregates`), every project's.\n * @param range - The window.\n * @param options - Options.\n * @param options.now - The moment the range ends. Default: now.\n * @param options.userName - The greeting's first name. Default: `null`.\n * @param options.projectRoot - Only this project's sessions (`sessions.project_root`). Default: every session.\n */\nexport function summarizeUsage(\n allBuckets: UsageBuckets,\n range: UsageRange,\n { now = new Date(), userName = null, projectRoot }: { now?: Date, userName?: string | null, projectRoot?: string } = {},\n): UsageSummary {\n const buckets = projectRoot === undefined ? allBuckets : usageOfProject(allBuckets, projectRoot)\n const cutoff = range === '7d' ? cutoffDay(now, 7) : range === '30d' ? cutoffDay(now, 30) : null\n const inRange = (day: string) => cutoff === null || day >= cutoff\n\n const models = new Map<string, UsageModelRow>()\n const chartCutoff = cutoffDay(now, CHART_MAX_DAYS)\n const daily: UsageModelDayPoint[] = []\n let tokensIn = 0\n let tokensOut = 0\n let totalCost = 0\n for (const row of buckets.assistant) {\n if (!inRange(row.day))\n continue\n const rowIn = row.input + row.cacheRead + row.cacheCreation\n tokensIn += rowIn\n tokensOut += row.output\n totalCost += row.cost\n let m = models.get(row.model)\n if (!m) {\n m = { modelId: row.model, provider: providerOfModel(row.model), turns: 0, tokensIn: 0, tokensOut: 0, cost: 0 }\n models.set(row.model, m)\n }\n m.turns += row.turns\n m.tokensIn += rowIn\n m.tokensOut += row.output\n m.cost += row.cost\n if (row.day >= chartCutoff)\n daily.push({ day: row.day, modelId: row.model, tokens: rowIn + row.output })\n }\n const modelRows = [...models.values()]\n // Zero-token, zero-cost buckets (synthetic turns) are legend noise.\n .filter(m => m.tokensIn + m.tokensOut > 0 || m.cost > 0)\n .sort((a, b) => b.tokensIn + b.tokensOut - (a.tokensIn + a.tokensOut))\n\n const providerTotals = new Map<string, number>()\n for (const m of modelRows)\n providerTotals.set(m.provider, (providerTotals.get(m.provider) ?? 0) + m.cost)\n const providers = [...providerTotals.entries()]\n .map(([provider, cost]) => ({ provider, cost }))\n .filter(p => p.cost > 0)\n .sort((a, b) => b.cost - a.cost)\n\n let messages = 0\n const activeDays = new Set<string>()\n const hourCounts = Array.from<number>({ length: 24 }).fill(0)\n for (const row of buckets.user) {\n if (!inRange(row.day))\n continue\n messages += row.messages\n activeDays.add(row.day)\n if (row.hour >= 0 && row.hour < 24)\n hourCounts[row.hour]! += row.messages\n }\n const streaks = computeStreaks(activeDays, now)\n const peak = Math.max(...hourCounts)\n const peakHour = peak > 0 ? hourCounts.indexOf(peak) : null\n\n let sessions = 0\n for (const row of buckets.sessions) {\n if (inRange(row.day))\n sessions += row.sessions\n }\n\n const heatmapCutoff = cutoffDay(now, HEATMAP_DAYS)\n const heatmapTokens = new Map<string, number>()\n const heatmapMessages = new Map<string, number>()\n for (const row of buckets.assistant) {\n if (row.day >= heatmapCutoff)\n heatmapTokens.set(row.day, (heatmapTokens.get(row.day) ?? 0) + row.input + row.cacheRead + row.cacheCreation + row.output)\n }\n for (const row of buckets.user) {\n if (row.day >= heatmapCutoff)\n heatmapMessages.set(row.day, (heatmapMessages.get(row.day) ?? 0) + row.messages)\n }\n const heatmap = [...new Set([...heatmapTokens.keys(), ...heatmapMessages.keys()])]\n .sort()\n .map(day => ({ day, tokens: heatmapTokens.get(day) ?? 0, messages: heatmapMessages.get(day) ?? 0 }))\n\n const favoriteModel = modelRows.reduce<UsageModelRow | null>((best, m) => (best === null || m.turns > best.turns ? m : best), null)\n return {\n range,\n userName,\n sessions,\n messages,\n totalTokens: tokensIn + tokensOut,\n tokensIn,\n tokensOut,\n totalCost,\n activeDays: activeDays.size,\n currentStreak: streaks.current,\n longestStreak: streaks.longest,\n peakHour,\n favoriteModel: favoriteModel?.modelId ?? null,\n models: modelRows,\n modelDaily: daily,\n providers,\n heatmap,\n generatedAt: now.getTime(),\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"usage-summary-BM7B3JdI.js","names":[],"sources":["../src/chat/feature-gate.ts","../src/chat/footer-chips.ts","../src/chat/completion-chats.ts","../src/chat/model-lineup.ts","../src/chat/text-model.ts","../src/chat/run-estimate.ts","../src/chat/mcp-test-outcome.ts","../src/chat/mcp-allowlist.ts","../src/chat/onboarding/native-extensions.ts","../src/chat/extension-sources.ts","../src/chat/onboarding/brands.ts","../src/chat/onboarding/features.ts","../src/chat/onboarding/presets.ts","../src/chat/onboarding/steps.ts","../src/chat/onboarding/flow.ts","../src/chat/onboarding/result.ts","../src/chat/usage-summary.ts"],"sourcesContent":["/**\n * Opt-in feature gates: an experimental feature is one boolean setting\n * (default off) plus a kill-switch env var that wins over it.\n *\n * The kill switch exists so a bug in the gated path can never lock the user\n * out: the ungated path is always one flag away, even when the settings UI\n * itself is unreachable. Each gate names its flag (`--no-daemon`,\n * `--no-remote`); the CLI's {@link applyKillSwitchFlags} exports the env var\n * for the process, so child processes inherit the switch too.\n *\n * Node-free on purpose (re-exported from `zidane/chat/pure`): the TUI and the\n * GUI main process share one definition per gate, so both read the same\n * setting and honor the same kill switch.\n */\n\ntype Env = Record<string, string | undefined>\n\n/** Options for {@link defineFeatureGate}. */\nexport interface FeatureGateOptions<K extends string> {\n /** Settings key holding the opt-in. Only a literal `true` enables. */\n setting: K\n /** Env var that disables the feature when set to `'1'`, whatever the setting says. */\n killSwitchEnv: string\n /** The `zidane` flag that sets {@link killSwitchEnv} for the launch (`--no-…`). */\n flag: string\n}\n\n/** A feature gate created by {@link defineFeatureGate}. */\nexport interface FeatureGate<K extends string> {\n /** Settings key holding the opt-in. */\n readonly setting: K\n /** Env var that disables the feature when set to `'1'`. */\n readonly killSwitchEnv: string\n /** CLI flag that sets the kill-switch env var for the launch. */\n readonly flag: string\n /** The kill-switch env var is set to `'1'`. */\n isKilled: (env?: Env) => boolean\n /** Setting is `true` and the kill switch is not engaged. */\n isEnabled: (settings: Partial<Record<K, unknown>>, env?: Env) => boolean\n}\n\n/** `process.env` when there is one; a renderer has no `process`. */\nfunction ambientEnv(): Env {\n return (globalThis as { process?: { env?: Env } }).process?.env ?? {}\n}\n\n/**\n * Creates a {@link FeatureGate} from its setting key, kill-switch env var, and CLI flag.\n *\n * @param options - Setting key, kill-switch env var, and flag name.\n * @example\n * ```ts\n * import { defineFeatureGate } from 'zidane/chat'\n *\n * const gate = defineFeatureGate({ setting: 'daemon', killSwitchEnv: 'ZIDANE_NO_DAEMON', flag: '--no-daemon' })\n * gate.isEnabled({ daemon: true }, {}) // true\n * gate.isEnabled({ daemon: true }, { ZIDANE_NO_DAEMON: '1' }) // false\n * ```\n */\nexport function defineFeatureGate<K extends string>({ setting, killSwitchEnv, flag }: FeatureGateOptions<K>): FeatureGate<K> {\n const isKilled = (env: Env = ambientEnv()): boolean => env[killSwitchEnv] === '1'\n return {\n setting,\n killSwitchEnv,\n flag,\n isKilled,\n isEnabled: (settings, env) => settings[setting] === true && !isKilled(env),\n }\n}\n\n/**\n * The shared daemon runtime. `ZIDANE_NO_DAEMON=1` is set by\n * `zidane --no-daemon`.\n */\nexport const daemonGate = defineFeatureGate({ setting: 'useDaemon', killSwitchEnv: 'ZIDANE_NO_DAEMON', flag: '--no-daemon' })\n\n/**\n * Remote hosts (zidane-server). The setting (\"Remote hosts\")\n * gates the saved servers: the Hosts settings, the per-host session groups\n * and picking a host for a new session. Connecting through an explicit `--host` /\n * `ZIDANE_HOST_URL` is its own opt-in, but `ZIDANE_NO_REMOTE=1` still wins\n * over it (`src/tui/host-target.ts`). `ZIDANE_NO_REMOTE=1` is set by\n * `zidane --no-remote`.\n */\nexport const remoteHostsGate = defineFeatureGate({ setting: 'remoteHosts', killSwitchEnv: 'ZIDANE_NO_REMOTE', flag: '--no-remote' })\n\n/** Every gate, so a client can report which kill switches are engaged. */\nexport const FEATURE_GATES: readonly FeatureGate<string>[] = [daemonGate, remoteHostsGate]\n\n/**\n * The kill-switch env vars engaged in `env`, sorted. The GUI main process\n * hands this to its renderer (which has no `process.env`) so settings rows\n * can say why they are inert.\n *\n * @param env - Environment to read. Defaults to `process.env`, or an empty object when there is none.\n */\nexport function engagedKillSwitches(env: Env = ambientEnv()): string[] {\n return FEATURE_GATES.filter(g => g.isKilled(env)).map(g => g.killSwitchEnv).sort()\n}\n\n/**\n * Engages the kill switch of every gate whose flag is in `args` (sets its env\n * var to `'1'` in `env`, so child processes inherit it) and returns `args`\n * without those flags.\n */\nexport function applyKillSwitchFlags(args: readonly string[], env: Env): string[] {\n const rest: string[] = []\n for (const arg of args) {\n const gate = FEATURE_GATES.find(g => g.flag === arg)\n if (gate)\n env[gate.killSwitchEnv] = '1'\n else\n rest.push(arg)\n }\n return rest\n}\n\n/**\n * `name` is set to `'1'` in `env`: the kill-switch test, for a row that only knows the variable.\n *\n * @param name - Kill-switch env var name.\n * @param env - Environment to read. Defaults to `process.env`, or an empty object when there is none.\n */\nexport function isKillSwitchEngaged(name: string, env: Env = ambientEnv()): boolean {\n return env[name] === '1'\n}\n","/**\n * Presentation logic for the transient footer status chips, shared by every\n * surface: the prompt-cache chip, the provider-retry indicator, the live run\n * readout and the compaction readout.\n *\n * The cache and retry chips both report an invisible condition with no error attached — a turn re-billing\n * a prefix the provider should have served from cache, or a stalled stream being\n * retried — and both carry real judgment in their wording: which dimensions\n * changed, when a figure is an estimate, when there is no figure to show at\n * all, whose stream is stuck. That is why they live here instead of being\n * re-derived per surface. A GUI chip that rounded differently, or printed\n * `$0.00` where the TUI printed nothing, would be reporting different facts\n * about the same event.\n *\n * Node-free, so it is re-exported from `zidane/chat/pure` for the Electron\n * renderer.\n */\nimport type { CacheBreakInfo } from '../cache-telemetry'\nimport { fmtTokens, formatDuration, formatElapsed } from './format'\n\n/**\n * `warn` = a real cache break (a named dimension changed, so it was\n * avoidable); `dim` = ordinary TTL expiry, which is not anyone's fault and\n * should not shout.\n */\nexport type CacheChipTone = 'warn' | 'dim'\n\n/** Content of the prompt-cache chip. Built by {@link cacheBreakChip}. */\nexport interface CacheChipContent {\n /** Severity: `warn` for an avoidable break, `dim` for an ordinary expiry. */\n tone: CacheChipTone\n /** Leading glyph. Kept out of `label` so a surface can style it separately. */\n glyph: string\n /** Chip text after the glyph: `cache break in system: +41.6k tok, ~$0.48`. Plain, because the TUI shows it in a hint slot. */\n label: string\n}\n\n/**\n * Abbreviate a re-billed token count: `1200` → `1.2k`, `800` → `800`.\n * Thousands only: `1500000` reads as `1500.0k`.\n */\nfunction formatTokens(tokens: number): string {\n return tokens >= 1000 ? `${(tokens / 1000).toFixed(1)}k` : String(tokens)\n}\n\n/**\n * Build the chip for a cache break.\n *\n * `costUsd` is OPTIONAL and omitted entirely on a rate miss — never rendered\n * as `$0.00`. A fabricated zero would read as \"this was free\", which is the\n * opposite of what the chip exists to say. When present it is prefixed `~`\n * because it is an estimate (registry rates × re-billed prefix), and clamped\n * to `<0.01` rather than rounding a real charge down to nothing.\n *\n * @param info - Cache break report, with an optional estimated cost in USD.\n */\nexport function cacheBreakChip(info: CacheBreakInfo & { costUsd?: number }): CacheChipContent {\n const rebilled = formatTokens(info.rebilledTokens)\n const cost = info.costUsd !== undefined\n ? `, ~$${info.costUsd < 0.01 ? '<0.01' : info.costUsd.toFixed(2)}`\n : ''\n\n // No named dimension changed → the prefix simply aged out of the provider's\n // cache window. Reported quietly: nothing was done wrong.\n if (info.changed.length === 0) {\n return { tone: 'dim', glyph: '⚡', label: `cache expired: +${rebilled} tok${cost}` }\n }\n\n return {\n tone: 'warn',\n glyph: '⚡',\n label: `cache break in ${info.changed.join('/')}: +${rebilled} tok${cost}`,\n }\n}\n\n/**\n * Transcript-row text for a persisted cache-break notice. Multi-line, one\n * fact per line, so both hosts can show a short headline and fold or list\n * the rest:\n *\n * cache break ↑116.9k tokens ~$0.48\n * messagePrefix: message 16 of 41→43 rewritten\n * likely compaction or history trimming\n *\n * The footer chip stays terse; the row carries the culprit so a break is\n * diagnosable from the transcript. Only the headline on a TTL / eviction\n * break (nothing was done wrong).\n *\n * @param info - Cache break report, with an optional estimated cost in USD.\n */\nexport function cacheBreakNoticeText(info: CacheBreakInfo & { costUsd?: number }): string {\n const rebilled = formatTokens(info.rebilledTokens)\n const cost = info.costUsd !== undefined\n ? ` ~$${info.costUsd < 0.01 ? '<0.01' : info.costUsd.toFixed(2)}`\n : ''\n const headline = `${info.changed.length === 0 ? 'cache expired' : 'cache break'} ↑${rebilled} tokens${cost}`\n // One line per changed dimension, `name: detail`; a detail may itself span\n // lines (fact, then likely cause) and only its first line takes the prefix.\n const culprits = info.changed.flatMap((dim) => {\n const detail = info.details[dim]\n if (typeof detail !== 'string' || detail.length === 0)\n return [`${dim} changed`]\n const [first, ...rest] = detail.split('\\n')\n return [`${dim}: ${first}`, ...rest]\n })\n return [headline, ...culprits].join('\\n')\n}\n\n/**\n * Provider-retry label — `retrying 2/5`, or `retrying 2/5 (child-3)` when the\n * stalled stream belongs to a subagent rather than the parent.\n *\n * Naming the source matters: with subagents in flight, an unattributed\n * \"retrying\" leaves the user unable to tell whether their own turn is stuck or\n * a delegated worker is.\n *\n * @param info - Retry attempt, attempt limit, and the source of the stalled stream (`'parent'` or a subagent id).\n */\nexport function retryLabel(info: { attempt: number, max: number, source: string }): string {\n const suffix = info.source === 'parent' ? '' : ` (${info.source})`\n return `retrying ${info.attempt}/${info.max}${suffix}`\n}\n\n/** One value of the live run readout, so a renderer can tint and badge it by meaning. */\nexport interface RunStatusCell {\n /** What the cell shows: the run's elapsed time, its output tokens, or the stream rate. */\n kind: 'elapsed' | 'tokens' | 'rate'\n /** Display text: `1m 15s`, `↓1.2k` (`↓~1.2k` while estimated), `41 tok/s`. */\n text: string\n}\n\n/**\n * The live run readout as cells, one badge each: `1m 15s` `↓1.2k` `41 tok/s`.\n * Time runs from the run's start, the count covers the whole run (settled\n * turns plus the one streaming), and the rate is the honest windowed figure\n * from `stepStreamRate` (`null` until it has one). `↓~` marks a count that is\n * still the character heuristic. A tool-only stretch shows just the time.\n * Same model as `formatRunReceiptCells`, the receipt the run ends with.\n *\n * `detail` drops the least useful cell first when space runs out:\n * `'full'` → `'count'` (no rate) → `'time'`.\n *\n * @param run - Elapsed seconds, output token count, whether the count is estimated, and the stream rate in tokens per second (`null` when unknown).\n * @param detail - How much of the readout to keep. Defaults to `'full'`.\n */\nexport function runStatusCells(\n run: { elapsedSec: number, outputTokens: number, estimated: boolean, rate: number | null },\n detail: 'full' | 'count' | 'time' = 'full',\n): RunStatusCell[] {\n const cells: RunStatusCell[] = [{ kind: 'elapsed', text: formatElapsed(Math.max(0, Math.floor(run.elapsedSec))) }]\n if (detail !== 'time' && run.outputTokens > 0)\n cells.push({ kind: 'tokens', text: `↓${run.estimated ? '~' : ''}${fmtTokens(run.outputTokens)}` })\n if (detail === 'full' && run.outputTokens > 0 && run.rate !== null && run.rate > 0)\n cells.push({ kind: 'rate', text: `${run.rate} tok/s` })\n return cells\n}\n\n/**\n * Session cost while a turn streams: settled figure plus the running estimate\n * for the in-flight turn. `estimated` tells the surface to render it dimmed\n * (the number will move, then snap to the settled value at turn end). `null`\n * when there is nothing to show — never `$0.00`.\n *\n * @param settledUsd - Cost of settled turns in USD.\n * @param progress - Streaming progress carrying the running cost estimate for the in-flight turn, if any.\n */\nexport function liveSessionCost(\n settledUsd: number,\n progress: { estCostUsd?: number } | null | undefined,\n): { usd: number, estimated: boolean } | null {\n const live = progress?.estCostUsd ?? 0\n const total = settledUsd + live\n if (total <= 0)\n return null\n return { usd: total, estimated: live > 0 }\n}\n\n/**\n * Footer readout while a compaction summary is being generated: the label,\n * then `~12k` once the loop reports a token estimate, then `8s` once a second\n * has passed, one badge each. Same rule on every surface so a minutes-long\n * compaction of a large transcript reads as progress, not a hang, in the TUI\n * and the GUI alike.\n *\n * `now` is injectable so callers driving a 1 Hz tick don't have to re-read\n * the clock, and so tests are deterministic.\n *\n * @param estTokens - Token estimate reported by the loop, if any.\n * @param startedAt - Compaction start time in epoch milliseconds, if known.\n * @param now - Current time in epoch milliseconds. Defaults to `Date.now()`.\n */\nexport function compactingParts(\n estTokens: number | null | undefined,\n startedAt: number | null | undefined,\n now: number = Date.now(),\n): { label: string, meta: string[] } {\n const elapsedMs = typeof startedAt === 'number' ? now - startedAt : 0\n const meta = [\n typeof estTokens === 'number' && estTokens > 0 ? `~${fmtTokens(estTokens)}` : '',\n elapsedMs >= 1000 ? formatDuration(elapsedMs) : '',\n ].filter(Boolean)\n return { label: 'Compacting...', meta }\n}\n\n/**\n * {@link compactingParts} as one plain string, for a slot that cannot hold\n * badges: `Compacting... ~12k, 8s`.\n *\n * @param estTokens - Token estimate reported by the loop, if any.\n * @param startedAt - Compaction start time in epoch milliseconds, if known.\n * @param now - Current time in epoch milliseconds. Defaults to `Date.now()`.\n */\nexport function compactingLabel(\n estTokens: number | null | undefined,\n startedAt: number | null | undefined,\n now: number = Date.now(),\n): string {\n const { label, meta } = compactingParts(estTokens, startedAt, now)\n return meta.length > 0 ? `${label} ${meta.join(', ')}` : label\n}\n","/**\n * Completion provider for cross-chat references. Trigger: `#`.\n *\n * The visible row is human-friendly (session title, age, project), but the\n * inserted token is id-based (`#abc123`) so titles with spaces never enter the\n * prompt grammar. The token is the shortest normalized id prefix that is unique\n * within the reference catalog, with a six-character floor for readability.\n */\n\nimport type {\n CompletionContext,\n CompletionItem,\n CompletionProvider,\n CompletionReference,\n} from './completion-core'\nimport { Fzf } from 'fzf'\nimport { ageString, shortId } from './format'\n\n/** Trigger character that starts a chat reference. */\nexport const CHATS_TRIGGER = '#'\n\nconst DEFAULT_RESULT_LIMIT = 50\nconst MIN_TOKEN_LENGTH = 6\n\n/** Whether chat suggestions cover the current project or every project. */\nexport type ChatReferenceScope = 'project' | 'all'\n\n/** A chat that can be referenced from a prompt. */\nexport interface ChatReferenceEntry {\n /** Chat id. */\n id: string\n /** Chat title. */\n title: string\n /** Number of turns in the chat. */\n turnCount: number\n /** Number of user messages in the chat. */\n userMessageCount: number\n /** Number of agent runs in the chat. */\n runCount: number\n /** Project directory the chat belongs to. */\n projectRoot?: string\n /** Last update time in ms since epoch. */\n updatedAt: number\n /** Optional (store-dependent); shown as a hint when > 0. */\n artifactCount?: number\n}\n\n/** Provider payload for a chat reference. */\nexport interface ChatReferenceData {\n /** The referenced chat. */\n entry: ChatReferenceEntry\n /** The `#token` text that identifies the chat, without the `#`. */\n token: string\n}\n\nfunction normalizeId(id: string): string {\n return id.replace(/-/g, '').toLowerCase()\n}\n\nfunction projectLabel(projectRoot: string | undefined): string {\n if (!projectRoot)\n return ''\n const parts = projectRoot.split(/[\\\\/]/).filter(Boolean)\n return parts[parts.length - 1] ?? projectRoot\n}\n\n/**\n * Per-catalog memo for `tokenFor`, keyed on catalog reference identity.\n * Token derivation is O(catalog) per id, and both `suggest()` (every\n * keystroke) and `parseReferences()` (every submit/highlight pass) need\n * a token for EVERY entry — quadratic over catalogs that can reach\n * thousands of sessions. The catalog getters return a stable array until\n * a refresh swaps it, so a WeakMap invalidates exactly when it should.\n */\nconst tokensByCatalog = new WeakMap<readonly ChatReferenceEntry[], { normalizedIds: string[], tokens: Map<string, string> }>()\n\nfunction catalogTokens(catalog: readonly ChatReferenceEntry[]): { normalizedIds: string[], tokens: Map<string, string> } {\n let cached = tokensByCatalog.get(catalog)\n if (!cached) {\n cached = { normalizedIds: catalog.map(entry => normalizeId(entry.id)), tokens: new Map() }\n tokensByCatalog.set(catalog, cached)\n }\n return cached\n}\n\nfunction tokenFor(id: string, catalog: readonly ChatReferenceEntry[]): string {\n const { normalizedIds, tokens } = catalogTokens(catalog)\n const known = tokens.get(id)\n if (known !== undefined)\n return known\n const normalized = normalizeId(id)\n let result: string | undefined\n for (let len = Math.min(MIN_TOKEN_LENGTH, normalized.length); len <= normalized.length; len++) {\n const token = normalized.slice(0, len)\n const matches = normalizedIds.filter(candidate => candidate.startsWith(token))\n if (matches.length === 1) {\n result = token\n break\n }\n }\n result ??= normalized || shortId(id)\n tokens.set(id, result)\n return result\n}\n\nfunction stripTrailingPunctuation(candidate: string): string {\n return candidate.replace(/[.,;:)\\]}!?]+$/, '')\n}\n\nfunction descriptionFor(entry: ChatReferenceEntry, token: string, scope: ChatReferenceScope): string {\n const bits = [\n `#${token}`,\n ageString(entry.updatedAt),\n `${entry.userMessageCount} msg${entry.userMessageCount === 1 ? '' : 's'}`,\n ]\n if ((entry.artifactCount ?? 0) > 0)\n bits.push(`${entry.artifactCount} artifact${entry.artifactCount === 1 ? '' : 's'}`)\n const project = projectLabel(entry.projectRoot)\n if (scope === 'all' && project)\n bits.push(project)\n return bits.join(', ')\n}\n\nfunction scoreChats(\n catalog: readonly ChatReferenceEntry[],\n tokenCatalog: readonly ChatReferenceEntry[],\n query: string,\n limit: number,\n scope: ChatReferenceScope,\n): CompletionItem<ChatReferenceData>[] {\n const q = query.trim().toLowerCase()\n const rows = catalog.map(entry => ({\n entry,\n token: tokenFor(entry.id, tokenCatalog),\n project: projectLabel(entry.projectRoot),\n }))\n const fuzzyHits = q.length > 0\n ? new Set(new Fzf(rows, {\n selector: r => `${r.entry.title} ${r.token} ${r.project}`,\n casing: 'case-insensitive',\n }).find(q).map(r => r.item))\n : null\n const scored: { entry: ChatReferenceEntry, token: string, rank: number }[] = []\n for (const row of rows) {\n const title = row.entry.title.toLowerCase()\n const token = row.token.toLowerCase()\n const project = row.project.toLowerCase()\n if (q.length === 0) {\n scored.push({ entry: row.entry, token: row.token, rank: 5 })\n continue\n }\n if (token.startsWith(q)) {\n scored.push({ entry: row.entry, token: row.token, rank: 0 })\n continue\n }\n if (title.startsWith(q)) {\n scored.push({ entry: row.entry, token: row.token, rank: 1 })\n continue\n }\n if (title.includes(q)) {\n scored.push({ entry: row.entry, token: row.token, rank: 2 })\n continue\n }\n if (project.includes(q)) {\n scored.push({ entry: row.entry, token: row.token, rank: 3 })\n continue\n }\n if (fuzzyHits?.has(row)) {\n scored.push({ entry: row.entry, token: row.token, rank: 4 })\n }\n }\n scored.sort((a, b) => {\n if (a.rank !== b.rank)\n return a.rank - b.rank\n return b.entry.updatedAt - a.entry.updatedAt\n })\n return scored.slice(0, limit).map<CompletionItem<ChatReferenceData>>(({ entry, token }) => ({\n id: token,\n label: entry.title,\n description: descriptionFor(entry, token, scope),\n insertText: `${CHATS_TRIGGER}${token} `,\n data: { entry, token },\n }))\n}\n\n/**\n * Creates a completion provider for `#` chat references.\n * Each chat gets the shortest id prefix, at least 6 characters, that identifies it in the catalog. Suggestions are fuzzy-matched on title, token and project folder.\n *\n * @param opts - Catalog getters, an optional async catalog loader, the scope used for labels and a result limit (default 50).\n * @example\n * ```ts\n * import { createChatsCompletionProvider } from 'zidane/chat'\n *\n * const provider = createChatsCompletionProvider({\n * getCatalog: () => [\n * { id: 'c0ffee00-0000-4000-8000-000000000001', title: 'Fix login', turnCount: 4, userMessageCount: 2, runCount: 2, updatedAt: Date.now() },\n * ],\n * })\n * ```\n */\nexport function createChatsCompletionProvider(opts: {\n getCatalog: () => readonly ChatReferenceEntry[]\n getReferenceCatalog?: () => readonly ChatReferenceEntry[]\n getScope?: () => ChatReferenceScope\n ensureCatalog?: () => Promise<readonly ChatReferenceEntry[]>\n limit?: number\n}): CompletionProvider<ChatReferenceData> {\n const limit = opts.limit ?? DEFAULT_RESULT_LIMIT\n const scope = () => opts.getScope?.() ?? 'project'\n return {\n id: 'chats',\n trigger: CHATS_TRIGGER,\n label: 'Chats',\n suggest(query) {\n const current = opts.getCatalog()\n const tokenCatalog = opts.getReferenceCatalog?.() ?? current\n const effectiveTokenCatalog = tokenCatalog.length > 0 ? tokenCatalog : current\n if (current.length > 0)\n return scoreChats(current, effectiveTokenCatalog, query, limit, scope())\n if (opts.ensureCatalog) {\n return opts.ensureCatalog().then((loaded) => {\n const refreshed = opts.getReferenceCatalog?.() ?? loaded\n const refreshedTokenCatalog = refreshed.length > 0 ? refreshed : loaded\n return scoreChats(loaded, refreshedTokenCatalog, query, limit, scope())\n })\n }\n return []\n },\n parseReferences(text, _ctx: CompletionContext) {\n const catalog = opts.getReferenceCatalog?.() ?? opts.getCatalog()\n if (catalog.length === 0)\n return []\n const byToken = new Map<string, ChatReferenceData>()\n for (const entry of catalog) {\n const token = tokenFor(entry.id, catalog)\n byToken.set(token, { entry, token })\n }\n const refs: CompletionReference<ChatReferenceData>[] = []\n const rx = /(^|\\s)#(\\S+)/g\n for (const m of text.matchAll(rx)) {\n const candidate = stripTrailingPunctuation(m[2])\n const data = byToken.get(candidate.toLowerCase())\n if (!data)\n continue\n const start = m.index + m[1].length\n refs.push({\n providerId: 'chats',\n start,\n end: start + 1 + candidate.length,\n itemId: data.entry.id,\n data,\n })\n }\n return refs\n },\n }\n}\n\n/**\n * Returns the unique ids of the chats referenced in a reference list.\n * References from other providers are ignored. Order of first appearance is kept.\n *\n * @param references - References collected from a prompt.\n */\nexport function uniqueChatIdsFromReferences(\n references: readonly CompletionReference<unknown>[],\n): string[] {\n const out: string[] = []\n const seen = new Set<string>()\n for (const ref of references) {\n if (ref.providerId !== 'chats')\n continue\n if (seen.has(ref.itemId))\n continue\n seen.add(ref.itemId)\n out.push(ref.itemId)\n }\n return out\n}\n\n/**\n * Extracts the `#token` chat references from text.\n * A token starts after whitespace or at the start of the text. Trailing punctuation is stripped, tokens are lowercased and duplicates are dropped.\n *\n * @param text - The prompt text to scan.\n */\nexport function chatReferenceTokensFromText(text: string): string[] {\n const out: string[] = []\n const seen = new Set<string>()\n const rx = /(^|\\s)#(\\S+)/g\n for (const m of text.matchAll(rx)) {\n const token = stripTrailingPunctuation(m[2]).toLowerCase()\n if (!token || seen.has(token))\n continue\n seen.add(token)\n out.push(token)\n }\n return out\n}\n","// ---------------------------------------------------------------------------\n// Model lineup: which models of a provider are \"current\".\n//\n// Provider catalogs list every model they ever served (OpenRouter: ~400),\n// with no release dates, so recency is read off the ids themselves. A model\n// id is split into a FAMILY (the words: `claude-opus`, `gpt-luna`,\n// `gemini-flash-lite`), a VERSION (the numbers: `4-5`, `5.6`, `v4.1`,\n// `qwen3.5`), a SIZE (`27b`, `235b-a22b`, `16k`) and noise that marks a\n// snapshot or a routing variant of the same model (`-20251001`,\n// `-2024-05-13`, `-0731`, `-preview`, `-latest`, `:batch`, `:free`).\n//\n// The lineup keeps, per provider and family, the models at the newest\n// version: every size of that version, one entry per size (the plain alias\n// over a dated snapshot over a routing variant). Everything else is\n// \"older\". Pure and data-driven so the GUI and the TUI share one answer.\n// ---------------------------------------------------------------------------\n\n/** Parsed shape of a model id. Exported for tests and for pickers that want to show a family label. */\nexport interface ModelIdParts {\n /** `vendor/` prefix on aggregator ids (`anthropic` in `anthropic/claude-opus-5`), lowercased; '' when absent. */\n vendor: string\n /** The words of the id, lowercased and joined with `-`. */\n family: string\n /** Numeric version segments in order; empty when the id carries none. */\n version: number[]\n /** Size tokens joined with `-` (`27b`, `235b-a22b`); '' when absent. */\n size: string\n /** Dated / numbered snapshot (`20251001`, `2024-05-13` → `20240513`, `0731`); '' for the plain alias. */\n snapshot: string\n /** Routing variant after `:` (`batch`, `free`); '' when absent. */\n variant: string\n}\n\nconst DATE_DASHED = /-(\\d{4})-(\\d{2})-(\\d{2})(?=$|[-:])/\nconst SIZE = /^(?:\\d+(?:\\.\\d+)?[bkm]|a\\d+(?:\\.\\d+)?b|e\\d+b|\\d+x\\d+b)$/\nconst SNAPSHOT = /^\\d{4}$|^\\d{6,8}$/\nconst NUMBER = /^\\d+(?:\\.\\d+)*$/\n/** Zero-padded short number (`001`, `05`): a revision or a date part, never a version. */\nconst PADDED = /^0\\d{1,2}$/\n/** Bedrock's `-v1` / `-v2:0` revision suffix. Only a revision once a version was read. */\nconst REVISION = /^v\\d+$/\n/** `v4.1`, `k2.5`, `r1`, `o3`, `qwen3.5`: letters then a version. */\nconst LETTERS_VERSION = /^([a-z]+)(\\d+(?:\\.\\d+)*)$/\n/** `4o`: a version then letters. */\nconst VERSION_LETTERS = /^(\\d+(?:\\.\\d+)*)([a-z]+)$/\n/** Words that mark a pre-release or an alias of the same model, not a different one. */\nconst NOISE = new Set(['preview', 'latest', 'exp', 'experimental'])\n\n/**\n * Splits a model id into vendor, family, version, size, snapshot and routing variant. Parsing is lowercase and never throws.\n *\n * @param id - Model id, for example `anthropic/claude-sonnet-4-5:thinking`.\n */\nexport function parseModelId(id: string): ModelIdParts {\n let rest = id.toLowerCase()\n let variant = ''\n const colon = rest.indexOf(':')\n if (colon >= 0) {\n variant = rest.slice(colon + 1)\n rest = rest.slice(0, colon)\n }\n let vendor = ''\n const slash = rest.lastIndexOf('/')\n if (slash >= 0) {\n vendor = rest.slice(0, slash)\n rest = rest.slice(slash + 1)\n }\n let snapshot = ''\n const dated = DATE_DASHED.exec(rest)\n if (dated) {\n snapshot = `${dated[1]}${dated[2]}${dated[3]}`\n rest = rest.slice(0, dated.index) + rest.slice(dated.index + dated[0].length)\n }\n\n const words: string[] = []\n const version: number[] = []\n const sizes: string[] = []\n const snapParts: string[] = []\n let afterNoise = false\n const pushVersion = (v: string) => {\n for (const n of v.split('.')) version.push(Number(n))\n }\n for (const token of rest.split(/[-_\\s]+/).filter(Boolean)) {\n if (NOISE.has(token)) {\n afterNoise = true\n continue\n }\n if (SIZE.test(token)) {\n sizes.push(token)\n continue\n }\n // A bare 4 / 6-8 digit token after a version or word is a snapshot\n // (`-0731`, `-2512`, `-20251001`), never a version of its own. So is any\n // number after a pre-release word (`-preview-05-20`, `-preview-09-2025`)\n // and a zero-padded revision after a version (`gemini-2.0-flash-001`).\n const started = words.length > 0 || version.length > 0\n // Bedrock's `-v1` after a version (`claude-opus-4-6-v1`) is a revision too.\n if (started && (SNAPSHOT.test(token) || (afterNoise && NUMBER.test(token)) || (version.length > 0 && (PADDED.test(token) || REVISION.test(token))))) {\n snapParts.push(token)\n continue\n }\n if (NUMBER.test(token)) {\n pushVersion(token)\n continue\n }\n const lv = LETTERS_VERSION.exec(token)\n if (lv) {\n words.push(lv[1]!)\n pushVersion(lv[2]!)\n continue\n }\n const vl = VERSION_LETTERS.exec(token)\n if (vl) {\n pushVersion(vl[1]!)\n words.push(vl[2]!)\n continue\n }\n words.push(token)\n }\n if (!snapshot && snapParts.length > 0) {\n // `mm-yyyy` → `yyyymm`, so snapshots of that shape sort by date.\n snapshot = snapParts.length === 2 && /^\\d{2}$/.test(snapParts[0]!) && /^\\d{4}$/.test(snapParts[1]!)\n ? `${snapParts[1]}${snapParts[0]}`\n : snapParts.join('')\n }\n return { vendor, family: words.join('-'), version, size: sizes.join('-'), snapshot, variant }\n}\n\n/**\n * Numeric, segment by segment; a longer version wins a tie on the shared prefix (`5.5` > `5`).\n *\n * @param a - First version, as parsed by {@link parseModelId}.\n * @param b - Second version.\n */\nexport function compareModelVersion(a: readonly number[], b: readonly number[]): number {\n const n = Math.max(a.length, b.length)\n for (let i = 0; i < n; i++) {\n const x = a[i]\n const y = b[i]\n if (x === undefined)\n return -1\n if (y === undefined)\n return 1\n if (x !== y)\n return x - y\n }\n return 0\n}\n\n/**\n * Among ids naming the same model (family, version and size equal), the one\n * a picker should show: the plain alias over a dated snapshot, the newest\n * snapshot among snapshots, no routing variant over a variant, then the\n * shorter id. Negative when `a` is preferred.\n */\nfunction preferAlias(a: { id: string, parts: ModelIdParts }, b: { id: string, parts: ModelIdParts }): number {\n const sa = a.parts.snapshot\n const sb = b.parts.snapshot\n if (!sa !== !sb)\n return sa ? 1 : -1\n if (sa !== sb)\n return sa < sb ? 1 : -1\n if (!a.parts.variant !== !b.parts.variant)\n return a.parts.variant ? 1 : -1\n return a.id.length - b.id.length || (a.id < b.id ? -1 : a.id > b.id ? 1 : 0)\n}\n\n/** A provider and model id pair considered for the lineup. */\nexport interface LineupEntry {\n /** Provider key. */\n providerKey: string\n /** Model id. */\n modelId: string\n}\n\nconst lineupKey = (e: LineupEntry): string => `${e.providerKey}\\u0000${e.modelId}`\n\n/**\n * Keys (`provider\\0model`) of the current models: per provider and family,\n * the newest version, one entry per size. `keep` entries (the active model,\n * fallback chain, off-catalog ids) are always included.\n *\n * @param entries - Every provider and model id on offer.\n * @param keep - Entries to include regardless of age.\n */\nexport function currentModelKeys(entries: readonly LineupEntry[], keep: readonly LineupEntry[] = []): Set<string> {\n const groups = new Map<string, { id: string, key: string, parts: ModelIdParts }[]>()\n for (const entry of entries) {\n const parts = parseModelId(entry.modelId)\n const group = `${entry.providerKey}\\u0000${parts.vendor}\\u0000${parts.family}`\n const list = groups.get(group)\n const item = { id: entry.modelId, key: lineupKey(entry), parts }\n if (list)\n list.push(item)\n else\n groups.set(group, [item])\n }\n\n const kept = new Set<string>(keep.map(lineupKey))\n for (const list of groups.values()) {\n let newest: number[] = []\n for (const item of list) {\n if (compareModelVersion(item.parts.version, newest) > 0)\n newest = item.parts.version\n }\n const bySize = new Map<string, { id: string, key: string, parts: ModelIdParts }>()\n for (const item of list) {\n if (compareModelVersion(item.parts.version, newest) !== 0)\n continue\n const best = bySize.get(item.parts.size)\n if (!best || preferAlias(item, best) < 0)\n bySize.set(item.parts.size, item)\n }\n for (const item of bySize.values()) kept.add(item.key)\n }\n return kept\n}\n\n/**\n * `entries` in their original order, current models only (see {@link currentModelKeys}).\n *\n * @param entries - Every provider and model id on offer.\n * @param keep - Entries to include regardless of age.\n */\nexport function currentModels<T extends LineupEntry>(entries: readonly T[], keep: readonly LineupEntry[] = []): T[] {\n const kept = currentModelKeys(entries, keep)\n return entries.filter(entry => kept.has(lineupKey(entry)))\n}\n","/**\n * The text model: the cheap model that writes side-channel text (session\n * titles, next-prompt suggestions) instead of the session's main model.\n *\n * Stored as `Settings.textModel`: `''` = automatic, otherwise\n * `\"providerKey[@account]/modelId\"` (see `parseModelSetting`). Automatic is\n * the smallest current model of the session's provider, so the transcript\n * stays with the provider (and account) the session already talks to:\n *\n * 1. the current models (`currentModels`) of that provider that take\n * plain text, of the main model's vendor on an aggregator;\n * 2. among them the `small` tier, the cheapest input rate first;\n * 3. else the cheapest current model with a known cost;\n * 4. else the main model.\n *\n * A model's tier is its catalog entry's `tier` when set, else a heuristic on\n * the words of its family (`claude-haiku` → small). Node-free: shared by the\n * hosts and the GUI renderer's settings row.\n */\nimport type { ModelTier } from '../providers/model-catalog'\nimport { currentModels, parseModelId } from './model-lineup'\nimport { parseModelSetting } from './model-setting'\n\nexport type { ModelTier }\n\n/** Family words per tier. Small is checked first, so `flash-lite` is small while `flash` is medium. */\nconst SMALL_WORDS: ReadonlySet<string> = new Set(['haiku', 'luna', 'mini', 'small', 'lite', 'nano', 'micro'])\nconst MEDIUM_WORDS: ReadonlySet<string> = new Set(['sonnet', 'terra', 'flash', 'medium'])\nconst LARGE_WORDS: ReadonlySet<string> = new Set(['opus', 'fable', 'sol', 'pro', 'large'])\n\n/** Family words of special-purpose models that can't write a plain reply (image, audio, live, computer use, research agents). */\nconst SPECIAL_WORDS: ReadonlySet<string> = new Set([\n 'image',\n 'live',\n 'audio',\n 'tts',\n 'realtime',\n 'transcribe',\n 'embedding',\n 'embed',\n 'computer',\n 'research',\n 'customtools',\n 'moderation',\n 'guard',\n 'ocr',\n])\n\n/**\n * Tier read off a model id's family words; `undefined` when no word names one.\n *\n * @param modelId - Model id, for example `claude-haiku-4-5` or `anthropic/claude-haiku-4.5`.\n */\nexport function heuristicModelTier(modelId: string): ModelTier | undefined {\n const words = parseModelId(modelId).family.split('-')\n if (words.some(w => SMALL_WORDS.has(w)))\n return 'small'\n if (words.some(w => MEDIUM_WORDS.has(w)))\n return 'medium'\n if (words.some(w => LARGE_WORDS.has(w)))\n return 'large'\n return undefined\n}\n\n/**\n * A model's tier: its catalog value, else {@link heuristicModelTier}.\n *\n * @param model - Model id and optional catalog tier.\n * @param model.id - Model id.\n * @param model.tier - Catalog tier, when the catalog sets one.\n */\nexport function modelTier(model: { id: string, tier?: ModelTier }): ModelTier | undefined {\n return model.tier ?? heuristicModelTier(model.id)\n}\n\n/** One model of the session's provider, as the automatic pick reads it (a `ModelInfo` fits). */\nexport interface TextModelCandidate {\n /** Model id. */\n id: string\n /** Catalog tier; guessed from the id when absent. */\n tier?: ModelTier\n /** Input price; the cheapest candidate wins, and one without a price loses. */\n cost?: { input: number }\n /** Input modalities; a model without `text` is skipped. */\n input?: readonly ('text' | 'image')[]\n}\n\nfunction generalPurpose(model: TextModelCandidate): boolean {\n if (model.input && !model.input.includes('text'))\n return false\n const parts = parseModelId(model.id)\n // Routing variants (`:free`, `:thinking`) and aggregator meta-models (`auto`, `~vendor/…-latest`) aren't a model of their own.\n if (parts.variant || parts.family === 'auto' || model.id.startsWith('~'))\n return false\n return !parts.family.split('-').some(w => SPECIAL_WORDS.has(w))\n}\n\nconst inputRate = (model: TextModelCandidate): number => model.cost?.input ?? Number.POSITIVE_INFINITY\n\n/**\n * The automatic text model among one provider's models; `undefined` when\n * nothing qualifies (the caller then uses the main model).\n *\n * @param models - The provider's models, in catalog order.\n * @param mainModelId - The session's main model id (its vendor narrows an aggregator's list).\n */\nexport function autoTextModelId(models: readonly TextModelCandidate[], mainModelId: string): string | undefined {\n const usable = models.filter(generalPurpose)\n let pool = currentModels(usable.map(model => ({ providerKey: '', modelId: model.id, model }))).map(e => e.model)\n const vendor = parseModelId(mainModelId).vendor\n if (vendor) {\n const sameVendor = pool.filter(m => parseModelId(m.id).vendor === vendor)\n if (sameVendor.length > 0)\n pool = sameVendor\n }\n const cheapest = (list: readonly TextModelCandidate[]): TextModelCandidate | undefined =>\n list.reduce<TextModelCandidate | undefined>((best, m) => (!best || inputRate(m) < inputRate(best) ? m : best), undefined)\n const small = pool.filter(m => modelTier(m) === 'small')\n if (small.length > 0)\n return cheapest(small)!.id\n return cheapest(pool.filter(m => m.cost !== undefined))?.id\n}\n\n/** Where a resolved text model came from. */\nexport type TextModelSource = 'setting' | 'auto' | 'main'\n\n/** A text model to run on. */\nexport interface TextModelPick {\n /** Provider to run on. */\n providerKey: string\n /** Model id on that provider. */\n modelId: string\n /** The provider's stored account; absent = its active one. */\n account?: string\n /** Whether the pick came from `Settings.textModel`, the automatic choice or the main model. */\n source: TextModelSource\n}\n\n/** Inputs of {@link textModelChain} / {@link resolveTextModel}. */\nexport interface TextModelOptions {\n /** `Settings.textModel`: `''` / unset = automatic. */\n setting: string | undefined\n /** The session's main model (or the default model, outside a session). */\n main: { providerKey: string, modelId: string, account?: string }\n /** The models of a provider (`modelsForDescriptor`). */\n modelsOf: (providerKey: string) => readonly TextModelCandidate[]\n}\n\n/**\n * Text models to try, best first: the setting's pick, the automatic pick,\n * the main model (duplicates dropped). A host walks it until one builds, so\n * a picked provider that isn't signed in falls back instead of failing.\n *\n * @param opts - Setting, main model and model source.\n */\nexport function textModelChain(opts: TextModelOptions): TextModelPick[] {\n const { main } = opts\n const out: TextModelPick[] = []\n const push = (pick: TextModelPick) => {\n if (!out.some(p => p.providerKey === pick.providerKey && p.modelId === pick.modelId && p.account === pick.account))\n out.push(pick)\n }\n const chosen = parseModelSetting(opts.setting)\n if (chosen) {\n // No account named: the session's own on its provider, else the active one.\n const account = chosen.account ?? (chosen.providerKey === main.providerKey ? main.account : undefined)\n push({ providerKey: chosen.providerKey, modelId: chosen.modelId, ...(account ? { account } : {}), source: 'setting' })\n }\n const auto = autoTextModelId(opts.modelsOf(main.providerKey), main.modelId)\n if (auto)\n push({ providerKey: main.providerKey, modelId: auto, ...(main.account ? { account: main.account } : {}), source: 'auto' })\n push({ providerKey: main.providerKey, modelId: main.modelId, ...(main.account ? { account: main.account } : {}), source: 'main' })\n return out\n}\n\n/**\n * The text model to use: the first of {@link textModelChain} whose provider\n * is usable (all of them when `usable` is omitted).\n *\n * @param opts - Setting, main model and model source.\n * @param usable - Whether a provider can run (signed in); the main model is always kept as the last resort.\n */\nexport function resolveTextModel(opts: TextModelOptions, usable?: (providerKey: string, account?: string) => boolean): TextModelPick {\n const chain = textModelChain(opts)\n return chain.find(p => !usable || usable(p.providerKey, p.account)) ?? chain[chain.length - 1]!\n}\n\n/**\n * Build the text model's provider: walks {@link textModelChain} and returns\n * the first pick `build` doesn't throw on. The main model's error propagates\n * when nothing builds.\n *\n * @param opts - Setting, main model and model source.\n * @param build - Provider construction; throws when the provider has no credentials.\n */\nexport function buildTextModel<P>(opts: TextModelOptions, build: (providerKey: string, account?: string) => P): TextModelPick & { provider: P } {\n const chain = textModelChain(opts)\n let lastError: unknown\n for (const pick of chain) {\n try {\n return { ...pick, provider: build(pick.providerKey, pick.account) }\n }\n catch (err) {\n lastError = err\n }\n }\n throw lastError\n}\n","/**\n * Run time estimates (`Settings.runEstimates`): the math and the wire shape\n * shared by the host (which asks the decision model) and the windows (which\n * count down). Pure: no I/O, safe for `zidane/chat/pure`.\n *\n * The decision model rates the remaining work on a score over minute marks\n * spaced on a log scale ({@link ESTIMATE_LEVEL_MINUTES}). Its answer, a\n * probability-weighted level index, maps back to minutes between the marks\n * ({@link scoreToMs}), so an estimate is a real duration, not a range. The\n * level probabilities give the point past which a run counts as over\n * ({@link percentileMs}).\n */\n\nimport type { RunEstimate, RunUserWait, SessionRun } from '../session'\n\n/** The score levels, in minutes, lowest first: 1 minute to 24 hours. */\nexport const ESTIMATE_LEVEL_MINUTES: readonly number[] = [1, 2, 4, 8, 15, 30, 60, 120, 240, 480, 960, 1440]\n\n/** Percentile of the answer's distribution past which a run counts as over its estimate. */\nexport const ESTIMATE_OVER_PERCENTILE = 0.8\n\n/** Without level probabilities, the over point is the estimate times this. */\nexport const ESTIMATE_OVER_FALLBACK = 1.5\n\nconst MINUTE = 60_000\n\n/**\n * One level's label, as the decision model reads it.\n *\n * @param minutes - The level, in minutes.\n */\nexport function estimateLevelLabel(minutes: number): string {\n if (minutes < 60)\n return minutes === 1 ? '1 minute' : `${minutes} minutes`\n const hours = minutes / 60\n return hours === 1 ? '1 hour' : `${hours} hours`\n}\n\n/**\n * A score (a level index, possibly between levels) as a duration: the marks\n * are on a log scale, so a score between two levels is interpolated on it.\n *\n * @param score - Probability-weighted level index, `0` to `levels - 1`.\n */\nexport function scoreToMs(score: number): number {\n const levels = ESTIMATE_LEVEL_MINUTES\n const s = Math.min(Math.max(Number.isFinite(score) ? score : 0, 0), levels.length - 1)\n const low = Math.floor(s)\n const high = Math.min(low + 1, levels.length - 1)\n const f = s - low\n const minutes = Math.exp(Math.log(levels[low]!) + f * (Math.log(levels[high]!) - Math.log(levels[low]!)))\n return minutes * MINUTE\n}\n\n/**\n * The duration below which the answer puts probability `p` (`0..1`), from\n * the per-level probabilities. `null` when they don't add up to anything.\n *\n * @param probabilities - Probability per level, indexed like the levels.\n * @param p - The percentile.\n */\nexport function percentileMs(probabilities: readonly number[], p: number): number | null {\n const total = probabilities.reduce((sum, x) => sum + (Number.isFinite(x) && x > 0 ? x : 0), 0)\n if (total <= 0)\n return null\n let cumulative = 0\n for (let i = 0; i < probabilities.length; i++) {\n const share = Math.max(probabilities[i] ?? 0, 0) / total\n if (share > 0 && cumulative + share >= p) {\n // Within level i: spread its mass between the level below and this one.\n const within = (p - cumulative) / share\n return scoreToMs(Math.max(i - 1 + within, 0))\n }\n cumulative += share\n }\n return scoreToMs(probabilities.length - 1)\n}\n\n/** Fewest finished runs before calibration applies. */\nexport const CALIBRATION_MIN_SAMPLES = 5\n/** Most recent finished runs calibration reads, per project. */\nexport const CALIBRATION_MAX_SAMPLES = 50\n/** Smallest calibration factor: estimates are at most halved. */\nexport const CALIBRATION_MIN = 0.5\n/** Largest calibration factor: estimates are at most tripled. */\nexport const CALIBRATION_MAX = 3\n\n/**\n * The factor that corrects this project's estimates: the median of\n * actual / estimated over its recent finished runs, kept within\n * {@link CALIBRATION_MIN}..{@link CALIBRATION_MAX}. `null` with too few runs.\n *\n * @param ratios - Actual / estimated working time of finished runs, oldest first.\n */\nexport function calibrationFactor(ratios: readonly number[]): number | null {\n const recent = ratios.filter(r => Number.isFinite(r) && r > 0).slice(-CALIBRATION_MAX_SAMPLES)\n if (recent.length < CALIBRATION_MIN_SAMPLES)\n return null\n const sorted = [...recent].sort((a, b) => a - b)\n const mid = sorted.length / 2\n const median = sorted.length % 2 === 1 ? sorted[Math.floor(mid)]! : (sorted[mid - 1]! + sorted[mid]!) / 2\n return Math.min(Math.max(median, CALIBRATION_MIN), CALIBRATION_MAX)\n}\n\n/**\n * Time spent waiting on the user up to `now`, in ms.\n *\n * @param wait - The run's waits so far, and since when it waits now; absent: it never waited.\n * @param now - The time to count up to, epoch ms.\n */\nexport function waitedMs(wait: RunUserWait | undefined, now: number): number {\n if (!wait)\n return 0\n return wait.ms + (wait.since !== undefined ? Math.max(now - wait.since, 0) : 0)\n}\n\n/**\n * Working time of a run up to `now`: its time since start, less its waits on the user.\n *\n * @param startedAt - When the run started, epoch ms.\n * @param wait - Its waits on the user, as {@link waitedMs} reads them.\n * @param now - The time to count up to, epoch ms.\n */\nexport function workedMs(startedAt: number, wait: RunUserWait | undefined, now: number): number {\n return Math.max(now - startedAt - waitedMs(wait, now), 0)\n}\n\n/**\n * A run's estimate as hosts send it to windows (`run:estimate`, a session\n * row's `runEstimates`): enough to count down without the run record.\n */\nexport interface RunEstimateView {\n /** The run. */\n runId: string\n /** When the run started, epoch ms on the host's clock. */\n startedAt: number\n /** Subagent depth: 0 for the session's own run. */\n depth: number\n /** A subagent's label (`child-3`), as its spawn row names it. */\n label?: string\n /** The `spawn` call that started a subagent's run. */\n callId?: string\n /** Expected working time of the whole run, in ms. */\n totalMs: number\n /** Working time past which the run is over its estimate, in ms. */\n highMs: number\n /** When the estimate was made, epoch ms. */\n at: number\n /** Finished waits on the user, in ms. */\n waitMs: number\n /** Start of the wait going on now, epoch ms. */\n waitingSince?: number\n}\n\n/**\n * A run record's estimate as a {@link RunEstimateView}; `undefined` when it\n * has none.\n *\n * @param run - The run record.\n */\nexport function runEstimateView(run: Pick<SessionRun, 'id' | 'startedAt' | 'depth' | 'label' | 'parentCallId' | 'estimate' | 'userWait'>): RunEstimateView | undefined {\n const estimate: RunEstimate | undefined = run.estimate\n if (!estimate)\n return undefined\n return {\n runId: run.id,\n startedAt: run.startedAt,\n depth: run.depth ?? 0,\n ...(run.label ? { label: run.label } : {}),\n ...(run.parentCallId ? { callId: run.parentCallId } : {}),\n totalMs: estimate.totalMs,\n highMs: estimate.highMs,\n at: estimate.at,\n waitMs: run.userWait?.ms ?? 0,\n ...(run.userWait?.since !== undefined ? { waitingSince: run.userWait.since } : {}),\n }\n}\n\n/**\n * The estimates of a session's runs in progress, the session's own first:\n * what a session row carries.\n *\n * @param runs - The session's run records.\n */\nexport function runningEstimateViews(runs: readonly SessionRun[]): RunEstimateView[] {\n return runs\n .filter(run => run.status === 'running' && run.estimate)\n .map(run => runEstimateView(run)!)\n .sort((a, b) => a.depth - b.depth || a.startedAt - b.startedAt)\n}\n\n/** What an estimate says at a moment. */\nexport interface EstimateReadout {\n /**\n * - `live`: counting down.\n * - `due`: the estimate is spent, the run is not over yet (between its total and its over point).\n * - `paused`: the run waits on the user; the count holds.\n * - `over`: past its over point.\n */\n state: 'live' | 'due' | 'paused' | 'over'\n /** Working time left, in ms; `0` once the estimate is spent. */\n remainingMs: number\n}\n\n/**\n * What `view` says at `now`.\n *\n * @param view - The estimate.\n * @param now - Epoch ms on the host's clock (the window's is close enough).\n */\nexport function estimateReadout(view: RunEstimateView, now: number): EstimateReadout {\n const worked = workedMs(view.startedAt, { ms: view.waitMs, ...(view.waitingSince !== undefined ? { since: view.waitingSince } : {}) }, now)\n const remainingMs = Math.max(view.totalMs - worked, 0)\n if (worked > view.highMs)\n return { state: 'over', remainingMs: 0 }\n if (view.waitingSince !== undefined)\n return { state: 'paused', remainingMs }\n return { state: remainingMs > 0 ? 'live' : 'due', remainingMs }\n}\n\n/**\n * A remaining time as a chip says it: `~45s`, `~11m`, `~1h 20m`, `~12h`.\n *\n * @param ms - Time left, in ms.\n */\nexport function formatEstimate(ms: number): string {\n const seconds = Math.max(ms, 0) / 1000\n if (seconds < 60)\n return `~${Math.max(Math.ceil(seconds / 10) * 10, 10)}s`\n const minutes = Math.ceil(seconds / 60)\n if (minutes < 60)\n return `~${minutes}m`\n if (minutes < 600) {\n // Above an hour, minutes in steps of 5: a countdown that flips every minute reads as precise.\n const rounded = Math.round(minutes / 5) * 5\n const h = Math.floor(rounded / 60)\n const m = rounded % 60\n return m === 0 ? `~${h}h` : `~${h}h ${m}m`\n }\n return `~${Math.round(minutes / 60)}h`\n}\n","/**\n * How an MCP server answered a connection attempt, in the words both apps\n * show. Shared by the desktop app's Test (Settings › Agent › MCP servers) and the TUI's\n * bootstrap / test, so the same server reads the same way in either.\n *\n * ok connected and listed its tools;\n * needs-auth 401/403 with no key of its own: a browser sign-in fixes it\n * (an expired or never finished sign-in included);\n * token-rejected 401/403 on a server that sends its own headers: the key\n * in mcps.json is wrong, a browser sign-in won't help;\n * error anything else (refused, timed out, bad command).\n *\n * Pure: no I/O.\n */\nimport type { McpServerConfig } from '../types'\n\n/** Classification of an MCP connection attempt. */\nexport type McpTestOutcomeKind = 'ok' | 'needs-auth' | 'token-rejected' | 'error'\n\n/** Classified result of one MCP connection test. */\nexport interface McpTestResult {\n /** How the server answered. */\n kind: McpTestOutcomeKind\n /** Number of tools listed. Set for `ok`. */\n tools?: number\n /** User-facing explanation for non-`ok` kinds. */\n message?: string\n}\n\n/** What to do about a refused key, under the \"Key refused\" headline. */\nexport const MCP_KEY_REFUSED_HINT = 'Check the headers in mcps.json.'\n\n/**\n * A refusal for want of credentials: HTTP 401 / 403, however the SDK words it.\n *\n * @param error - The error thrown by a connection attempt.\n */\nexport function isMcpAuthRefusal(error: unknown): boolean {\n const code = (error as { code?: unknown } | null)?.code\n if (code === 401 || code === 403)\n return true\n const message = error instanceof Error ? error.message : String(error ?? '')\n return /\\b(?:401|403)\\b|unauthori[sz]ed|forbidden|invalid_token/i.test(message)\n}\n\n/**\n * The first line of a failure, never empty.\n *\n * @param error - The error thrown by a connection attempt.\n */\nexport function mcpFailureLine(error: unknown): string {\n const code = (error as { code?: unknown } | null)?.code\n const message = error instanceof Error && error.message\n ? error.message\n : code !== undefined\n ? `The server answered ${String(code)}.`\n : typeof error === 'string'\n ? error\n : ''\n return message.split('\\n')[0]?.trim() || 'Couldn\\'t connect.'\n}\n\n/**\n * Classifies a connection attempt. HTTP servers that required auth or answered\n * 401/403 become `token-rejected` when the config sends its own headers, else\n * `needs-auth`. Any other error becomes `error`. A clean connection is `ok`.\n *\n * @param config - The server's transport and headers.\n * @param result - Whether auth was required, the error if any, and the tool count.\n */\nexport function classifyMcpTest(\n config: Pick<McpServerConfig, 'transport' | 'headers'>,\n result: { authRequired: boolean, error?: unknown, tools?: number },\n): McpTestResult {\n const http = config.transport !== 'stdio'\n const ownKey = Object.keys(config.headers ?? {}).length > 0\n if (http && (result.authRequired || (result.error !== undefined && isMcpAuthRefusal(result.error)))) {\n return ownKey\n ? { kind: 'token-rejected', message: MCP_KEY_REFUSED_HINT }\n : { kind: 'needs-auth', message: 'Sign in to connect.' }\n }\n if (result.error !== undefined)\n return { kind: 'error', message: mcpFailureLine(result.error) }\n return { kind: 'ok', tools: result.tools ?? 0 }\n}\n","/**\n * Edits to the `enabledMcps` allowlist. Without an explicit list every\n * server is on, so there is nothing to change. Servers that merely appear\n * in a config file stay off once a list exists (they don't opt in\n * silently); a server the user adds in Settings is turned on with it.\n */\n\n/**\n * The list with `name` turned on, or `undefined` when it already is.\n *\n * @param list - The current `enabledMcps` list, or `undefined` when every server is on.\n * @param name - Server to turn on.\n */\nexport function enabledMcpsWith(list: readonly string[] | undefined, name: string): string[] | undefined {\n return list === undefined || list.includes(name) ? undefined : [...list, name]\n}\n\n/**\n * The list without `name`, or `undefined` when it isn't listed.\n *\n * @param list - The current `enabledMcps` list, or `undefined` when every server is on.\n * @param name - Server to turn off.\n */\nexport function enabledMcpsWithout(list: readonly string[] | undefined, name: string): string[] | undefined {\n return list?.includes(name) ? list.filter(n => n !== name) : undefined\n}\n","/**\n * The native extensions: the user-facing folders of the zidane repository's\n * `extensions/`, installable from the wizard in one click. Each installs from\n * the repository at the running version's tag, so it matches the app's API.\n * `test/chat/onboarding-native-extensions.test.ts` keeps this list and the\n * folder in sync.\n */\nimport type { NativeExtensionEntry, NativeExtensionGroup } from './types'\n\n/** The GitHub repository native extensions install from. */\nexport const NATIVE_EXTENSIONS_REPO = 'Tahul/zidane'\n\n/** Every native extension, in display order within each group. */\nexport const NATIVE_EXTENSIONS: readonly NativeExtensionEntry[] = [\n {\n name: 'git-flow',\n label: 'Git flow',\n description: 'Adds /commit, /review, /fix, /pr and /ticket. The agent writes your commits and pull requests, and reviews code with several models.',\n group: 'workflows',\n needs: [\n { kind: 'command', name: 'git', why: 'commits and branches' },\n { kind: 'command', name: 'gh', why: 'pull requests and review comments', optional: true },\n ],\n brand: 'git',\n icon: { gui: 'i-ph-git-branch' },\n },\n {\n name: 'goal',\n label: 'Goal',\n description: 'Adds /goal. The agent keeps working until the goal is done, for up to 10 runs. Esc stops it.',\n group: 'workflows',\n needs: [{ kind: 'decisions' }],\n icon: { gui: 'i-ph-target' },\n },\n {\n name: 'linear',\n label: 'Linear',\n description: 'Shows Linear issues and projects as badges in prompts and replies, and links them to the chats that use them.',\n group: 'integrations',\n needs: [{ kind: 'connector', id: 'linear' }],\n brand: 'linear',\n icon: { gui: 'i-ph-ticket' },\n },\n {\n name: 'ai-autocomplete',\n label: 'AI autocomplete',\n description: 'Suggests the end of your prompt as you type, from a local Ollama model. Press Tab to accept.',\n group: 'composer',\n needs: [\n { kind: 'command', name: 'ollama', why: 'serves the model', optional: true },\n { kind: 'command', name: 'docker', why: 'runs Ollama for you when it is not installed', optional: true },\n ],\n brand: 'ollama',\n icon: { gui: 'i-ph-sparkle' },\n },\n {\n name: 'bolt',\n label: 'Bolt',\n description: 'Adds Bolt as a provider. Sign in with your Bolt account to use its models.',\n group: 'providers',\n needs: [{ kind: 'account', label: 'Bolt' }],\n provider: 'bolt',\n icon: { gui: 'i-ph-lightning' },\n },\n]\n\n/**\n * Folders of `extensions/` left out of the wizard on purpose, with the reason.\n * Every folder is either listed above or here.\n */\nexport const EXCLUDED_NATIVE_EXTENSIONS: Readonly<Record<string, string>> = {\n 'plugin-host': 'A demo of ctx.loadAgentPlugin for the repository\\'s own tests, not something to install.',\n 'headroom': 'Not offered in the setup wizard for now.',\n 'pxpipe': 'Not offered in the setup wizard for now.',\n 'antigravity': 'Not offered in the setup wizard for now. It is not official.',\n}\n\n/** A section of the Extensions step. */\nexport interface NativeExtensionGroupMeta {\n /** The section. */\n id: NativeExtensionGroup\n /** Its heading. */\n label: string\n}\n\n/** The step's sections, in display order. */\nexport const NATIVE_EXTENSION_GROUPS: readonly NativeExtensionGroupMeta[] = [\n { id: 'workflows', label: 'Workflows' },\n { id: 'integrations', label: 'Integrations' },\n { id: 'composer', label: 'Composer' },\n { id: 'providers', label: 'Providers' },\n]\n\n/**\n * A native extension by name, or `undefined`.\n *\n * @param name - The extension name.\n */\nexport function nativeExtensionByName(name: string): NativeExtensionEntry | undefined {\n return NATIVE_EXTENSIONS.find(entry => entry.name === name)\n}\n\n/**\n * The install source of a native extension, pinned to a release tag:\n * `Tahul/zidane#v<version>:extensions/<name>`, the grammar\n * `installExtensionFromSpec` takes.\n *\n * @param entry - The extension.\n * @param version - The running zidane version (`ZIDANE_VERSION`), without the `v`.\n * @example\n * ```ts\n * import { nativeExtensionByName, nativeExtensionSource } from 'zidane/chat/pure'\n *\n * nativeExtensionSource(nativeExtensionByName('goal')!, '8.1.0')\n * // 'Tahul/zidane#v8.1.0:extensions/goal'\n * ```\n */\nexport function nativeExtensionSource(entry: NativeExtensionEntry, version: string): string {\n return `${NATIVE_EXTENSIONS_REPO}#v${version.replace(/^v/, '')}:extensions/${entry.name}`\n}\n","/**\n * What a source of extensions holds, before an install: the shape\n * `listExtensionSource` (`zidane/chat`, Node) answers and the protocol's\n * `extensions.browse` carries, plus the native catalog as a listing for\n * when the default source cannot be read. Pure and browser-safe\n * (`zidane/chat/pure`).\n */\nimport type { NativeExtensionEntry } from './onboarding/types'\nimport { NATIVE_EXTENSIONS, NATIVE_EXTENSIONS_REPO } from './onboarding/native-extensions'\n\n/** The kind of a source: a GitHub repository (or a folder in one), an npm package, another link, a folder on the host. */\nexport type ExtensionSourceKind = 'github' | 'npm' | 'url' | 'local'\n\n/** Where a listing comes from. */\nexport interface ExtensionSourceInfo {\n /** Its kind. */\n kind: ExtensionSourceKind\n /** GitHub owner. */\n owner?: string\n /** GitHub repository. */\n repo?: string\n /** The branch or tag listed (GitHub: the default branch when none was given), or the npm version listed. */\n ref?: string\n /** npm package name. */\n package?: string\n /** A page to open: the repository, the folder on GitHub, the package on npmjs.com. */\n url?: string\n /** The folder on the host (`local`). */\n path?: string\n /** A short name: `Tahul/zidane`, `Tahul/zidane@v9.0.4`, `@scope/pkg`, the folder. */\n label: string\n}\n\n/**\n * Why a listing is empty or partial.\n *\n * - `not-found`: no such repository, branch, folder or package.\n * - `private`: the source exists but needs access (or GitHub hid it: a private repository answers 404 without a token).\n * - `rate-limit`: GitHub refused more calls without a token.\n * - `network`: the connection failed, or the server answered an error.\n * - `invalid`: the text is not a source this can list.\n */\nexport type ExtensionSourceErrorCode = 'not-found' | 'private' | 'rate-limit' | 'network' | 'invalid'\n\n/** An extension a source holds, read from its `package.json` (`zidane` field) or `extension.json`. Nothing of it ran. */\nexport interface ExtensionSourceEntry {\n /** Its name: the folder it installs as (the manifest's `name`, else the folder or package name). */\n name: string\n /** Its display name (`displayName` or `label`), else the name. */\n displayName: string\n /** What it does. */\n description?: string\n /** Its version (`package.json` `version`). */\n version?: string\n /** Its icon (the manifest's `icon`). */\n icon?: { gui?: string, tui?: string }\n /** Its logo from the native catalog (a Simple Icons slug), for the native extensions only. */\n brand?: string\n /** Its folder in the source, relative to the repository or package root (`.` for the root). */\n path: string\n /** The extension API version its manifest declares. */\n apiVersion?: number\n /**\n * What installs it: `installExtensionFromSpec({ source, extension })`\n * (`catalog.install` with `spec: source`). Absent for a local folder,\n * which the installer does not take.\n */\n install?: { source: string, extension?: string }\n}\n\n/** The extensions of one source, as `listExtensionSource` lists them. */\nexport interface ExtensionSourceListing {\n /** What was asked for, trimmed. */\n input: string\n /** Where the extensions come from. */\n source: ExtensionSourceInfo\n /** The extensions found. Empty with `error` when the source could not be read, except the default source, which lists the native catalog then (`builtin`). */\n extensions: ExtensionSourceEntry[]\n /** The listing stopped early: GitHub cut the file tree, or the source holds more than the cap. */\n truncated?: boolean\n /** The rows come from the catalog shipped in the app (`nativeExtensionListing`), not from the source. */\n builtin?: boolean\n /** Why the source could not be read. */\n error?: { code: ExtensionSourceErrorCode, message: string }\n}\n\n/** The default source of extensions: the zidane repository, whose `extensions/` folder holds the native extensions. */\nexport const DEFAULT_EXTENSION_SOURCE = NATIVE_EXTENSIONS_REPO\n\n/** What a listing of the default source says when it cannot be read. */\nexport const DEFAULT_EXTENSION_SOURCE_UNREACHABLE = `${NATIVE_EXTENSIONS_REPO} is private or out of reach. You need access to install from it.`\n\n/**\n * The default source pinned to a release, `Tahul/zidane#v<version>`: the\n * native extensions that match the running app's API.\n *\n * @param version - The running zidane version (`ZIDANE_VERSION`), with or without the `v`.\n * @example\n * ```ts\n * import { defaultExtensionSource } from 'zidane/chat/pure'\n *\n * defaultExtensionSource('9.0.4') // 'Tahul/zidane#v9.0.4'\n * ```\n */\nexport function defaultExtensionSource(version: string): string {\n return `${NATIVE_EXTENSIONS_REPO}#v${version.replace(/^v/, '')}`\n}\n\n/**\n * The listing row of a native extension, from the catalog shipped in the app.\n *\n * @param entry - The catalog entry.\n * @param source - The spec that installs from the repository (`defaultExtensionSource`).\n */\nexport function nativeExtensionEntry(entry: NativeExtensionEntry, source: string): ExtensionSourceEntry {\n return {\n name: entry.name,\n displayName: entry.label,\n description: entry.description,\n ...(entry.icon ? { icon: { ...entry.icon } } : {}),\n ...(entry.brand ? { brand: entry.brand } : {}),\n path: `extensions/${entry.name}`,\n apiVersion: 1,\n install: { source, extension: entry.name },\n }\n}\n\n/**\n * The native extensions as a listing of the default source pinned to\n * `version`, built from the catalog shipped in the app (`NATIVE_EXTENSIONS`):\n * what an app shows when the repository cannot be read. No network.\n *\n * @param version - The running zidane version.\n * @returns A listing with `builtin: true` and no error.\n */\nexport function nativeExtensionListing(version: string): ExtensionSourceListing {\n const ref = `v${version.replace(/^v/, '')}`\n const [owner, repo] = NATIVE_EXTENSIONS_REPO.split('/') as [string, string]\n const spec = defaultExtensionSource(version)\n return {\n input: spec,\n source: { kind: 'github', owner, repo, ref, url: `https://github.com/${NATIVE_EXTENSIONS_REPO}/tree/${ref}/extensions`, label: `${NATIVE_EXTENSIONS_REPO}@${ref}` },\n extensions: NATIVE_EXTENSIONS.map(entry => nativeExtensionEntry(entry, spec)),\n builtin: true,\n }\n}\n","/**\n * The logos of the services the wizard names, as slugs of the kit's logo set\n * (`BrandMark` in `zidane-ui`: Simple Icons, plus LobeHub Icons for the\n * model providers that Simple Icons lacks). Connectors and native extensions\n * carry theirs in their catalog entry. The providers, the other agents and the\n * local model servers have theirs here. A service with no slug shows a letter,\n * such as Pi, Context7, DeepWiki and Playwright.\n */\nimport type { OnboardingAgentId, OnboardingLocalServer } from './types'\n\n/** The logo of each built-in provider, by provider key. */\nexport const PROVIDER_BRANDS: Readonly<Record<string, string>> = {\n 'anthropic': 'anthropic',\n 'arcee': 'arcee',\n 'baseten': 'baseten',\n 'cerebras': 'cerebras',\n 'openai': 'openai',\n 'xai': 'xai',\n 'zai': 'zai',\n 'google': 'googlegemini',\n 'google-vertex': 'googlecloud',\n 'openrouter': 'openrouter',\n 'mistral': 'mistralai',\n 'cursor': 'cursor',\n}\n\n/** The logo of each agent whose setup the wizard reads. Pi has none. */\nexport const ONBOARDING_AGENT_BRANDS: Readonly<Record<OnboardingAgentId, string | undefined>> = {\n 'claude-code': 'claudecode',\n 'codex': 'codex',\n 'pi': undefined,\n}\n\n/** The logo of each local model server the wizard looks for. */\nexport const LOCAL_SERVER_BRANDS: Readonly<Record<OnboardingLocalServer['kind'], string>> = {\n 'ollama': 'ollama',\n 'lm-studio': 'lmstudio',\n 'vllm': 'vllm',\n}\n\n/**\n * The logo of a provider, as a Simple Icons slug.\n *\n * @param key - The provider key.\n * @example\n * ```ts\n * import { providerBrand } from 'zidane/chat/pure'\n *\n * providerBrand('google') // 'googlegemini'\n * ```\n */\nexport function providerBrand(key: string): string | undefined {\n return PROVIDER_BRANDS[key]\n}\n","/**\n * The toggles that the Features step offers, picked from the settings\n * catalog. Labels and descriptions are the ones that Settings shows\n * (`SETTINGS_TOGGLES`), so the step and Settings say the same thing. Each\n * feature also has an example, one sentence that shows it at work, and what\n * it unlocks and prevents. A feature's parts turn on with it (`includes`),\n * such as the agent's use of the browser tab, so the step has one switch per\n * feature.\n */\nimport type { BooleanSettingKey } from '../settings-catalog'\nimport type { OnboardingFeature, OnboardingFeatureGroup, OnboardingMode, OnboardingSurface } from './types'\nimport { SETTINGS_TOGGLES } from '../settings-catalog'\n\nconst BOTH: readonly OnboardingSurface[] = ['tui', 'desktop']\nconst DESKTOP: readonly OnboardingSurface[] = ['desktop']\n\n/** Every feature the step can offer, in display order. */\nexport const ONBOARDING_FEATURES: readonly OnboardingFeature[] = [\n {\n key: 'codemode',\n group: 'work',\n surfaces: BOTH,\n badges: ['experimental'],\n example: 'For example, to read 12 files, the model writes one script that reads all of them, instead of 12 tool calls.',\n unlocks: 'Many tool calls in one step. Long tasks take fewer turns, so runs finish faster and cost less.',\n prevents: 'A context full of small tool results. Only what the script prints goes back to the model.',\n },\n {\n key: 'webSearch',\n group: 'work',\n surfaces: BOTH,\n example: 'For example, a build fails after a library update. The agent searches the error and reads the release notes.',\n unlocks: 'Answers from the web: current docs, error messages, and changes newer than the model.',\n prevents: 'Off, the agent never reaches a search engine, and works only from your files and what it already knows.',\n },\n {\n key: 'inlineAutocomplete',\n group: 'work',\n surfaces: BOTH,\n example: 'For example, you type `src/comp`, and `onents/` shows in gray after it.',\n unlocks: 'Press Tab to finish a path, a command or a prompt that you type often.',\n prevents: 'Typing the same long paths and prompts again and again.',\n },\n {\n key: 'autoTitle',\n group: 'work',\n surfaces: BOTH,\n badges: ['billed'],\n example: 'For example, after your first message about a failed login, the session gets the title \"Fix the login redirect\".',\n unlocks: 'Session titles that follow what each session is about, so you find it again.',\n prevents: 'A session list where every title reads the same.',\n },\n {\n key: 'runEstimates',\n group: 'work',\n surfaces: DESKTOP,\n badges: ['billed'],\n example: 'For example, a session that migrates a test suite shows \"~12m\" in the sidebar, and its subagents show their own time left.',\n unlocks: 'A countdown on each running session and subagent, so you know when to come back.',\n prevents: 'Checking a long run again and again to see whether it is done.',\n },\n {\n key: 'spaces',\n group: 'work',\n surfaces: BOTH,\n example: 'For example, a Work space starts sessions on Opus, and a Personal space starts them on a local model.',\n unlocks: 'Separate session lists, each with its own default model, such as Work and Personal.',\n prevents: 'Work and personal sessions mixed in one long list.',\n },\n {\n key: 'checkForUpdates',\n group: 'background',\n surfaces: BOTH,\n example: 'For example, when a new version comes out, the footer tells you that an update is ready.',\n unlocks: 'A note in the footer when a new version is out.',\n prevents: 'Running an old version without knowing it.',\n },\n {\n key: 'subscriptionUsage',\n group: 'background',\n surfaces: BOTH,\n example: 'For example, zidane shows how much of your weekly Claude limit you used.',\n unlocks: 'Your plan usage in the footer, such as the share of your weekly limit.',\n prevents: 'A surprise stop when you reach your plan limit.',\n },\n {\n key: 'useDaemon',\n group: 'background',\n surfaces: DESKTOP,\n example: 'For example, you close the window during a long run. The run keeps going, and the next window shows it.',\n unlocks: 'Runs that keep going after you close the window, and one session open in several windows.',\n prevents: 'Losing a long run when its window closes.',\n },\n {\n key: 'browser',\n group: 'desktop',\n surfaces: DESKTOP,\n includes: ['browserAgent'],\n example: 'For example, you open your app at `localhost:5173` next to the chat. After an edit, the agent clicks the new button and checks the result.',\n unlocks: 'A browser tab next to the chat. The agent opens pages, clicks and reads them to check its own work.',\n prevents: 'Switching to another app to check your work, and changes that the agent never saw on screen.',\n },\n {\n key: 'terminal',\n group: 'desktop',\n surfaces: DESKTOP,\n includes: ['terminalAgent'],\n example: 'For example, you run your dev server in a terminal next to the chat. The agent reads its errors and asks you before it types.',\n unlocks: 'A terminal next to the chat. The agent reads its output, such as an error from your dev server.',\n prevents: 'Switching to another app to run a command, and commands that the agent types without your approval.',\n },\n {\n key: 'notifications',\n group: 'desktop',\n surfaces: DESKTOP,\n includes: ['notificationSound'],\n example: 'For example, you work in another app. A notification and a short sound tell you that a run finished.',\n unlocks: 'A system notification with a short sound when a run finishes or needs your answer.',\n prevents: 'A run that waits for you while you work in another app or look away.',\n },\n {\n key: 'loadProjectExtensions',\n group: 'trust',\n surfaces: BOTH,\n example: 'For example, a project adds a `/deploy` command in its `.zidane/extensions` folder.',\n unlocks: 'Extensions that a project ships load when you open it, such as its own commands.',\n prevents: 'Off, code from a repository that you clone never runs on its own.',\n },\n]\n\n/** A section of the Features step. */\nexport interface OnboardingFeatureGroupMeta {\n /** The section. */\n id: OnboardingFeatureGroup\n /** Its heading. */\n label: string\n}\n\n/** The step's sections, in display order. */\nexport const ONBOARDING_FEATURE_GROUPS: readonly OnboardingFeatureGroupMeta[] = [\n { id: 'desktop', label: 'Desktop' },\n { id: 'work', label: 'Work' },\n { id: 'trust', label: 'Trust' },\n { id: 'background', label: 'In the background' },\n]\n\n/**\n * The features that a run offers: the ones of its app. An `extras` run offers\n * only the ones that no other app offers, because the other app set the\n * shared ones.\n *\n * @param surface - The app that runs the wizard.\n * @param mode - The run's mode.\n */\nexport function onboardingFeatures(surface: OnboardingSurface, mode: OnboardingMode): OnboardingFeature[] {\n return ONBOARDING_FEATURES.filter((feature) => {\n if (!feature.surfaces.includes(surface))\n return false\n return mode !== 'extras' || feature.surfaces.length === 1\n })\n}\n\n/** A feature's label and description: the setting's own. */\nexport interface OnboardingFeatureCopy {\n /** Row label. */\n label: string\n /** One line on what it does. */\n description: string\n}\n\n/**\n * The label and description of a feature, from the settings catalog.\n *\n * @param key - The setting the feature flips.\n */\nexport function onboardingFeatureCopy(key: BooleanSettingKey): OnboardingFeatureCopy {\n const row = SETTINGS_TOGGLES.find(toggle => toggle.key === key)\n if (!row)\n throw new Error(`onboarding feature \"${key}\" has no row in SETTINGS_TOGGLES`)\n return { label: row.label, description: row.description }\n}\n","/**\n * Conversation presets: how much of the agent's work the transcript shows.\n * Balanced is the defaults themselves, so a change to `DEFAULT_SETTINGS`\n * moves it too and choosing it writes nothing. Both apps also offer the\n * presets in their command palette, through the entry built here.\n */\nimport type { PaletteItem, PaletteRunResult } from '../palette'\nimport type { Settings } from '../types'\nimport type { TranscriptPreset, TranscriptPresetId, TranscriptPresetKey, TranscriptPresetSettings } from './types'\nimport { DEFAULT_SETTINGS } from '../settings-defaults'\n\n/** Every setting a conversation preset sets, in the order the fine-tuning lists them. */\nexport const TRANSCRIPT_PRESET_KEYS: readonly TranscriptPresetKey[] = [\n 'transcriptDensity',\n 'showRowIcons',\n 'showThinking',\n 'thinkingAutoExpand',\n 'toolCallDisplay',\n 'showToolResults',\n 'groupToolCalls',\n 'showEditDiffs',\n 'editDiffDisplay',\n 'showRunReceipt',\n 'showToolAttachments',\n 'hideSubagentOutput',\n 'showCacheNotices',\n 'showFullShellOutput',\n 'showCompactionSummary',\n]\n\nfunction presetValues(settings: Settings): TranscriptPresetSettings {\n return Object.fromEntries(TRANSCRIPT_PRESET_KEYS.map(key => [key, settings[key]])) as TranscriptPresetSettings\n}\n\n/** The three presets, from the quietest to the most complete. Built by a pure call, so a bundle that never reads them drops them and the settings they read. */\nexport const TRANSCRIPT_PRESETS: readonly TranscriptPreset[] = /* @__PURE__ */ buildTranscriptPresets()\n\nfunction buildTranscriptPresets(): TranscriptPreset[] {\n const BALANCED = presetValues(DEFAULT_SETTINGS)\n return [\n {\n id: 'focused',\n label: 'Focused',\n description: 'Shows the answers. The work shows as one short row. Thinking and run costs are hidden.',\n settings: {\n ...BALANCED,\n transcriptDensity: 'compact',\n // The plainest look: no icon before each row (the desktop app's).\n showRowIcons: false,\n showThinking: false,\n thinkingAutoExpand: false,\n groupToolCalls: true,\n editDiffDisplay: 'compact',\n showRunReceipt: false,\n showToolAttachments: false,\n hideSubagentOutput: true,\n showCacheNotices: false,\n showFullShellOutput: false,\n showCompactionSummary: false,\n },\n },\n {\n id: 'balanced',\n label: 'Balanced',\n description: 'Shows the answers, one short row for the work, diffs and the cost of each run. The default.',\n settings: BALANCED,\n },\n {\n id: 'detailed',\n label: 'Detailed',\n description: 'Shows every step and its output, with thinking open and full diffs.',\n settings: {\n ...BALANCED,\n transcriptDensity: 'full',\n showRowIcons: true,\n showThinking: true,\n thinkingAutoExpand: true,\n groupToolCalls: false,\n editDiffDisplay: 'full',\n showRunReceipt: true,\n showToolAttachments: true,\n hideSubagentOutput: false,\n showCacheNotices: true,\n showFullShellOutput: true,\n showCompactionSummary: true,\n },\n },\n ]\n}\n\n/**\n * A preset by id.\n *\n * @param id - The preset.\n */\nexport function transcriptPreset(id: TranscriptPresetId): TranscriptPreset {\n return TRANSCRIPT_PRESETS.find(preset => preset.id === id)!\n}\n\n/**\n * The preset a set of settings matches, or `custom` when it matches none.\n * Keys left out take their default value.\n *\n * @param settings - Settings to read, such as the stored ones or a draft.\n * @example\n * ```ts\n * import { transcriptPresetOf } from 'zidane/chat/pure'\n *\n * transcriptPresetOf({}) // 'balanced'\n * transcriptPresetOf({ showThinking: false }) // 'custom'\n * ```\n */\nexport function transcriptPresetOf(settings: Partial<Settings>): TranscriptPresetId | 'custom' {\n const value = (key: TranscriptPresetKey): unknown => settings[key] ?? DEFAULT_SETTINGS[key]\n const match = TRANSCRIPT_PRESETS.find(preset => TRANSCRIPT_PRESET_KEYS.every(key => value(key) === preset.settings[key]))\n return match?.id ?? 'custom'\n}\n\n/** The id of the palette entry for the conversation preset. Both apps use it. */\nexport const TRANSCRIPT_PRESET_PALETTE_ID = 'setting:conversation-preset'\n\nconst PALETTE_CHOICE = `${TRANSCRIPT_PRESET_PALETTE_ID}:`\nconst PALETTE_TITLE = 'Conversation preset'\n\n/**\n * The palette entry for the conversation preset. It names the preset in\n * effect, and running it opens the presets ({@link transcriptPresetPalettePicker}).\n *\n * @param settings - The settings in effect.\n * @param source - The id of the palette provider that owns the entry.\n */\nexport function transcriptPresetPaletteItem(settings: Partial<Settings>, source: string): PaletteItem {\n const current = transcriptPresetOf(settings)\n return {\n id: TRANSCRIPT_PRESET_PALETTE_ID,\n title: PALETTE_TITLE,\n description: current === 'custom' ? 'Custom' : transcriptPreset(current).label,\n group: 'Settings',\n kind: 'picker',\n icon: { gui: 'i-ph-chats-circle' },\n keywords: ['conversation', 'transcript', 'output', 'focused', 'balanced', 'detailed', 'thinking', 'tool calls'],\n source,\n }\n}\n\n/**\n * The list that the palette entry opens: the three presets, the one in effect checked.\n *\n * @param settings - The settings in effect.\n * @param source - The id of the palette provider that owns the rows.\n */\nexport function transcriptPresetPalettePicker(settings: Partial<Settings>, source: string): Extract<PaletteRunResult, { kind: 'items' }> {\n const current = transcriptPresetOf(settings)\n return {\n kind: 'items',\n title: PALETTE_TITLE,\n items: TRANSCRIPT_PRESETS.map(preset => ({\n id: `${PALETTE_CHOICE}${preset.id}`,\n title: preset.label,\n description: preset.description,\n group: PALETTE_TITLE,\n kind: 'toggle',\n checked: current === preset.id,\n source,\n })),\n }\n}\n\n/**\n * The preset that a row of {@link transcriptPresetPalettePicker} applies.\n *\n * @param itemId - The id of the palette row that ran.\n * @returns The preset, or `undefined` for any other row.\n * @example\n * ```ts\n * import { transcriptPresetOfPaletteItem } from 'zidane/chat/pure'\n *\n * transcriptPresetOfPaletteItem('setting:conversation-preset:detailed') // 'detailed'\n * ```\n */\nexport function transcriptPresetOfPaletteItem(itemId: string): TranscriptPresetId | undefined {\n if (!itemId.startsWith(PALETTE_CHOICE))\n return undefined\n const id = itemId.slice(PALETTE_CHOICE.length)\n return TRANSCRIPT_PRESETS.find(preset => preset.id === id)?.id\n}\n\n/**\n * The line that the palette shows once a preset applies.\n *\n * @param id - The preset that applied.\n */\nexport function transcriptPresetNotice(id: TranscriptPresetId): string {\n return `${PALETTE_TITLE}: ${transcriptPreset(id).label}`\n}\n","/**\n * The setup wizard's steps: their order, scope and copy, which of them a run\n * shows, and the palette entry that opens the wizard again. Pure data and\n * functions, shared by every renderer.\n */\nimport type { OnboardingPlanContext, OnboardingStepId, OnboardingStepMeta } from './types'\n\n/**\n * Version of the wizard, stored in each record it writes. A record from an\n * older version still counts as done: existing users open new steps from\n * Settings or the palette, never on their own.\n */\nexport const ONBOARDING_VERSION = 1\n\n/** Every step in flow order, with what it writes and its copy. */\nexport const ONBOARDING_STEPS: readonly OnboardingStepMeta[] = [\n { id: 'welcome', scope: 'none', label: 'Welcome', title: 'Set up zidane', lede: 'This takes about two minutes. You can change every choice later in Settings.' },\n { id: 'connect', scope: 'shared', label: 'Connect', title: 'Choose an AI provider', lede: 'The provider runs the model that answers you. Your keys stay on this computer, in a file that only you can read.' },\n { id: 'import', scope: 'shared', label: 'Bring your setup', title: 'Use your setup from other agents', lede: 'zidane copies what you pick. It does not change the files of the other agent.' },\n { id: 'connectors', scope: 'shared', label: 'Connectors', title: 'Connect your tools', lede: 'The agent can read and change data in these services. Most of them ask you to sign in through your browser.' },\n { id: 'skills', scope: 'shared', label: 'Skills', title: 'Teach zidane how you work', lede: 'A skill is a folder of instructions. The agent reads one when a task needs it.' },\n { id: 'extensions', scope: 'shared', label: 'Extensions', title: 'Add extensions', lede: 'Extensions add commands and features. zidane installs them for you from its repository.' },\n { id: 'look', scope: 'surface', label: 'Look', title: 'Choose how zidane looks', lede: 'The preview shows each choice at once.' },\n { id: 'conversation', scope: 'shared', label: 'Conversation', title: 'How much detail do you want to see?', lede: 'Pick a preset. The preview shows a sample conversation with it.' },\n { id: 'autonomy', scope: 'shared', label: 'Autonomy', title: 'What can zidane do without asking?', lede: 'Commands in your safelist always run, whatever you pick. Your safelist holds the commands that you allowed with Always allow.' },\n { id: 'features', scope: 'shared', label: 'Features', title: 'Turn on what you want', lede: 'You can change these, and more, in Settings.' },\n { id: 'ready', scope: 'none', label: 'Ready', title: 'You are ready', lede: 'Type a task and press Enter to start.' },\n]\n\nconst STEP_ORDER: readonly OnboardingStepId[] = /* @__PURE__ */ stepIds()\n\nfunction stepIds(): OnboardingStepId[] {\n return ONBOARDING_STEPS.map(step => step.id)\n}\n\n/**\n * The display data of a step.\n *\n * @param id - The step.\n */\nexport function onboardingStep(id: OnboardingStepId): OnboardingStepMeta {\n return ONBOARDING_STEPS.find(step => step.id === id)!\n}\n\n/**\n * The steps that a run shows, in order. It starts from the steps of the mode.\n * It leaves out Bring your setup when zidane found no other agent, and\n * Connectors, Skills and Extensions when installs do not run on this computer. It\n * also leaves out the steps that the host excludes. A `connect` run always\n * has Connect.\n *\n * @param ctx - The run's mode, surface and what was found.\n * @example\n * ```ts\n * import { planOnboarding } from 'zidane/chat/pure'\n *\n * planOnboarding({ mode: 'quick', surface: 'tui', importAvailable: false, installsAvailable: true })\n * // ['welcome', 'connect', 'ready']\n * ```\n */\nexport function planOnboarding(ctx: OnboardingPlanContext): OnboardingStepId[] {\n const applies = (id: OnboardingStepId): boolean => {\n if (ctx.exclude?.includes(id))\n return false\n if (id === 'import')\n return ctx.importAvailable\n if (id === 'connectors' || id === 'skills' || id === 'extensions')\n return ctx.installsAvailable\n return true\n }\n switch (ctx.mode) {\n case 'full':\n return STEP_ORDER.filter(applies)\n case 'rerun':\n return STEP_ORDER.filter(id => id !== 'welcome' && applies(id))\n case 'quick':\n return (['welcome', 'connect', 'import', 'ready'] as const).filter(applies)\n case 'extras':\n return (['look', 'features', 'ready'] as const).filter(applies)\n case 'connect':\n return ['connect']\n }\n}\n\n/** The command palette's entry for the wizard. */\nexport interface SetupPaletteItem {\n /** Palette item id, the same in both apps. */\n id: string\n /** Row title. */\n title: string\n /** Text beside the title. */\n description: string\n /** Extra search terms. */\n keywords: readonly string[]\n}\n\n/** \"Run setup\", the palette entry both apps list to open the wizard again. */\nexport const SETUP_PALETTE_ITEM: SetupPaletteItem = {\n id: 'app:setup',\n title: 'Run setup',\n description: 'Open the setup wizard: provider, connectors, skills, extensions, look, conversation, autonomy and features.',\n keywords: ['setup', 'onboarding', 'wizard', 'welcome', 'get started', 'provider', 'theme', 'connectors', 'skills', 'extensions'],\n}\n","import type { SkillPick } from '../../skills/sources'\n/**\n * The setup wizard's state machine: which step is current, what has been\n * picked, and the background tasks the steps started. One reducer, driven by\n * the TUI and by `zidane-ui` alike, so both walk the steps the same way.\n */\nimport type { BooleanSettingKey } from '../settings-catalog'\nimport type { Settings } from '../types'\nimport type {\n OnboardingAppearance,\n OnboardingDraft,\n OnboardingImportSelection,\n OnboardingPlanContext,\n OnboardingProviderPick,\n OnboardingStepId,\n OnboardingTask,\n TranscriptPresetId,\n} from './types'\nimport { enforceSettingsInvariants, SETTINGS_TOGGLES } from '../settings-catalog'\nimport { ONBOARDING_FEATURES } from './features'\nimport { TRANSCRIPT_PRESET_KEYS, transcriptPreset } from './presets'\nimport { planOnboarding } from './steps'\n\nfunction toggleRequires(key: string): BooleanSettingKey | undefined {\n return SETTINGS_TOGGLES.find(toggle => toggle.key === key)?.requires\n}\n\nfunction featureIncludes(key: string): readonly BooleanSettingKey[] {\n return ONBOARDING_FEATURES.find(feature => feature.key === key)?.includes ?? []\n}\n\n/** The settings of the Features step: each feature, what it includes, and what those need. */\nfunction featureStepSettings(): BooleanSettingKey[] {\n const keys = ONBOARDING_FEATURES.flatMap(feature => [feature.key, ...(feature.includes ?? [])])\n return [...new Set([...keys, ...keys.flatMap(key => toggleRequires(key) ?? [])])]\n}\n\n/** The settings each step edits. Skipping a step puts them back. */\nexport const ONBOARDING_STEP_SETTINGS: Partial<Record<OnboardingStepId, readonly (keyof Settings)[]>> = {\n import: ['importForeignConfigs'],\n look: ['theme', 'mouseInteractions'],\n conversation: TRANSCRIPT_PRESET_KEYS,\n autonomy: ['approvalMode', 'sandbox', 'sandboxNetwork', 'maxCostUsd'],\n features: featureStepSettings(),\n}\n\n/** Every setting the wizard can change. */\nexport const ONBOARDING_SETTING_KEYS: readonly (keyof Settings)[] = [...new Set(Object.values(ONBOARDING_STEP_SETTINGS).flatMap(keys => keys ?? []))]\n\n/** Where a run is. */\nexport interface OnboardingFlowState {\n /** What decides the steps. `mode` is the mode the run opened in. */\n context: OnboardingPlanContext\n /** The steps of this run, in order. */\n steps: readonly OnboardingStepId[]\n /** Index of the current step in `steps`. */\n index: number\n /** Everything picked so far. */\n draft: OnboardingDraft\n /** The draft the run started from: what Skip goes back to, and what the result diffs against. */\n initial: OnboardingDraft\n /** Imports and installs the steps started. */\n tasks: readonly OnboardingTask[]\n /** The last step was passed: the run is finished. */\n done: boolean\n}\n\n/**\n * What can happen in a run.\n *\n * - `next` / `back`: move one step. `next` on the last step finishes the run.\n * - `goto`: jump to a step of the run, such as from Ready's recap.\n * - `skip`: put the current step's picks back, then move on.\n * - `finish`: end the run now, keeping what was picked (Skip setup).\n * - `choose-path`: Welcome's choice. Quick start cuts the run down to Connect, Bring your setup and Ready.\n * - `set-context`: the detection finished. The steps are planned again and the current one kept.\n * - `set-settings`: change settings. Turning a setting on also turns on what it needs and, for a feature, what it includes. Turning one off turns off what needs it.\n * - `apply-preset`: set every transcript setting to a preset's values.\n * - `set-provider`, `set-imports`, `set-appearance`: Connect's, Bring your setup's and Look's picks.\n * - `toggle-connector`, `toggle-extension`: pick or unpick a connector or a native extension.\n * - `toggle-skills`: pick or unpick skills, one or a whole listing at once.\n * - `task`: add or update a background task, matched by id.\n */\nexport type OnboardingAction\n = | { type: 'next' }\n | { type: 'back' }\n | { type: 'goto', step: OnboardingStepId }\n | { type: 'skip' }\n | { type: 'finish' }\n | { type: 'choose-path', path: OnboardingDraft['path'] }\n | { type: 'set-context', context: Partial<OnboardingPlanContext> }\n | { type: 'set-settings', settings: Partial<Settings> }\n | { type: 'apply-preset', preset: TranscriptPresetId }\n | { type: 'set-provider', provider: OnboardingProviderPick | undefined }\n | { type: 'set-imports', imports: OnboardingImportSelection | undefined }\n | { type: 'set-appearance', appearance: OnboardingAppearance | undefined }\n | { type: 'toggle-connector', id: string, on: boolean }\n | { type: 'toggle-extension', name: string, on: boolean }\n | { type: 'toggle-skills', picks: readonly SkillPick[], on: boolean }\n | { type: 'task', task: OnboardingTask }\n\n/** The draft a run starts from. */\nexport interface OnboardingStart {\n /** The settings in effect now: the stored ones over the defaults. */\n settings: Settings\n /** The desktop app's current look, for the desktop app. */\n appearance?: OnboardingAppearance\n /** The provider in use now, for a re-run. */\n provider?: OnboardingProviderPick\n}\n\nfunction stepsFor(context: OnboardingPlanContext, path: OnboardingDraft['path']): OnboardingStepId[] {\n const mode = context.mode === 'full' && path === 'quick' ? 'quick' : context.mode\n return planOnboarding({ ...context, mode })\n}\n\n/**\n * A new run at its first step.\n *\n * @param context - The run's mode, surface and what was found so far.\n * @param start - The settings, look and provider in effect now.\n */\nexport function createOnboardingFlow(context: OnboardingPlanContext, start: OnboardingStart): OnboardingFlowState {\n const draft: OnboardingDraft = {\n path: 'custom',\n connectors: [],\n skills: [],\n extensions: [],\n settings: { ...start.settings },\n ...(start.provider ? { provider: start.provider } : {}),\n ...(start.appearance ? { appearance: start.appearance } : {}),\n }\n return { context, steps: stepsFor(context, draft.path), index: 0, draft, initial: draft, tasks: [], done: false }\n}\n\n/**\n * Settings with a change applied. A setting that turns on also turns on the\n * one that it needs, so Session titles turns on Decisions, and a feature\n * turns on what it includes, so Browser tab turns on Agent can use the\n * browser. A setting that turns off also turns off the ones that need it.\n *\n * @param settings - The settings before the change.\n * @param patch - The settings to change.\n */\nexport function applySettingsPatch(settings: Partial<Settings>, patch: Partial<Settings>): Partial<Settings> {\n const next: Partial<Settings> = { ...settings, ...patch }\n for (const [key, value] of Object.entries(patch)) {\n if (value !== true)\n continue\n const requires = toggleRequires(key)\n if (requires && next[requires] !== true)\n next[requires] = true\n for (const included of featureIncludes(key))\n next[included] = true\n }\n return enforceSettingsInvariants(next)\n}\n\n/**\n * The current step.\n *\n * @param state - The run.\n */\nexport function currentOnboardingStep(state: OnboardingFlowState): OnboardingStepId {\n return state.steps[state.index]!\n}\n\n/**\n * Whether the user can skip the current step. Welcome and Ready have nothing\n * to skip, and the TUI cannot run without a provider.\n *\n * @param state - The run.\n */\nexport function canSkipOnboardingStep(state: OnboardingFlowState): boolean {\n const step = currentOnboardingStep(state)\n if (step === 'welcome' || step === 'ready')\n return false\n return !(step === 'connect' && state.context.surface === 'tui')\n}\n\nfunction restoreStep(state: OnboardingFlowState, step: OnboardingStepId): OnboardingDraft {\n const { draft, initial } = state\n const keys = ONBOARDING_STEP_SETTINGS[step] ?? []\n const settings = keys.length ? { ...draft.settings, ...Object.fromEntries(keys.map(key => [key, initial.settings[key]])) } : draft.settings\n const next: OnboardingDraft = { ...draft, settings }\n if (step === 'connect')\n next.provider = initial.provider\n if (step === 'import')\n next.imports = initial.imports\n if (step === 'connectors')\n next.connectors = initial.connectors\n if (step === 'skills')\n next.skills = initial.skills\n if (step === 'extensions')\n next.extensions = initial.extensions\n if (step === 'look')\n next.appearance = initial.appearance\n return next\n}\n\nfunction toggledSkills(list: readonly SkillPick[], picks: readonly SkillPick[], on: boolean): readonly SkillPick[] {\n const keys = new Set(picks.map(pick => pick.key))\n const without = list.filter(pick => !keys.has(pick.key))\n return on ? [...without, ...picks.filter((pick, i) => picks.findIndex(other => other.key === pick.key) === i)] : without\n}\n\nfunction toggled(list: readonly string[], item: string, on: boolean): readonly string[] {\n const without = list.filter(entry => entry !== item)\n return on ? [...without, item] : without\n}\n\nfunction move(state: OnboardingFlowState, delta: 1 | -1): OnboardingFlowState {\n const index = state.index + delta\n if (index >= state.steps.length)\n return { ...state, done: true }\n return index < 0 ? state : { ...state, index }\n}\n\nfunction replan(state: OnboardingFlowState, context: OnboardingPlanContext, path: OnboardingDraft['path']): OnboardingFlowState {\n const current = currentOnboardingStep(state)\n const steps = stepsFor(context, path)\n const kept = steps.indexOf(current)\n return { ...state, context, steps, index: kept >= 0 ? kept : Math.min(state.index, steps.length - 1) }\n}\n\nfunction withDraft(state: OnboardingFlowState, draft: Partial<OnboardingDraft>): OnboardingFlowState {\n return { ...state, draft: { ...state.draft, ...draft } }\n}\n\n/**\n * The next state of a run.\n *\n * @param state - The run now.\n * @param action - What happened.\n * @example\n * ```ts\n * import { createOnboardingFlow, currentOnboardingStep, DEFAULT_SETTINGS, onboardingReducer } from 'zidane/chat/pure'\n *\n * let flow = createOnboardingFlow(\n * { mode: 'full', surface: 'tui', importAvailable: false, installsAvailable: true },\n * { settings: DEFAULT_SETTINGS },\n * )\n * flow = onboardingReducer(flow, { type: 'next' })\n * currentOnboardingStep(flow) // 'connect'\n * ```\n */\nexport function onboardingReducer(state: OnboardingFlowState, action: OnboardingAction): OnboardingFlowState {\n switch (action.type) {\n case 'next':\n return move(state, 1)\n case 'back':\n return move(state, -1)\n case 'goto': {\n const index = state.steps.indexOf(action.step)\n return index < 0 ? state : { ...state, index, done: false }\n }\n case 'skip':\n return move({ ...state, draft: restoreStep(state, currentOnboardingStep(state)) }, 1)\n case 'finish':\n return { ...state, done: true }\n case 'choose-path':\n return replan(withDraft(state, { path: action.path }), state.context, action.path)\n case 'set-context':\n return replan(state, { ...state.context, ...action.context }, state.draft.path)\n case 'set-settings':\n return withDraft(state, { settings: applySettingsPatch(state.draft.settings, action.settings) })\n case 'apply-preset':\n return withDraft(state, { settings: applySettingsPatch(state.draft.settings, transcriptPreset(action.preset).settings) })\n case 'set-provider':\n return withDraft(state, { provider: action.provider })\n case 'set-imports':\n return withDraft(state, { imports: action.imports })\n case 'set-appearance':\n return withDraft(state, { appearance: action.appearance })\n case 'toggle-connector':\n return withDraft(state, { connectors: toggled(state.draft.connectors, action.id, action.on) })\n case 'toggle-extension':\n return withDraft(state, { extensions: toggled(state.draft.extensions, action.name, action.on) })\n case 'toggle-skills':\n return withDraft(state, { skills: toggledSkills(state.draft.skills, action.picks, action.on) })\n case 'task': {\n const exists = state.tasks.some(task => task.id === action.task.id)\n const tasks = exists ? state.tasks.map(task => (task.id === action.task.id ? action.task : task)) : [...state.tasks, action.task]\n return { ...state, tasks }\n }\n }\n}\n\n/** How far a run is: the current step's position and the number of steps. */\nexport interface OnboardingProgress {\n /** Zero-based position of the current step. */\n index: number\n /** Number of steps in the run. */\n count: number\n}\n\n/**\n * How far a run is.\n *\n * @param state - The run.\n */\nexport function onboardingProgress(state: OnboardingFlowState): OnboardingProgress {\n return { index: state.index, count: state.steps.length }\n}\n","/**\n * What a run writes, and when an app opens the wizard: the sparse settings\n * change, the records in the state file, and the launch gate.\n */\nimport type { Settings } from '../types'\nimport type { OnboardingFlowState } from './flow'\nimport type {\n OnboardingGate,\n OnboardingInstallSignals,\n OnboardingMode,\n OnboardingRecord,\n OnboardingResult,\n OnboardingSettingsChange,\n OnboardingState,\n OnboardingSurface,\n} from './types'\nimport { requirementMet, SETTINGS_TOGGLES } from '../settings-catalog'\nimport { DEFAULT_SETTINGS } from '../settings-defaults'\nimport { ONBOARDING_SETTING_KEYS } from './flow'\nimport { ONBOARDING_VERSION } from './steps'\n\nfunction sameValue(a: unknown, b: unknown): boolean {\n if (Object.is(a, b))\n return true\n return typeof a === 'object' && a !== null && typeof b === 'object' && b !== null && JSON.stringify(a) === JSON.stringify(b)\n}\n\n/** Off only because the setting it needs is off, such as Agent can use your terminals with Terminals off. */\nfunction forcedOff(settings: Partial<Settings>, key: keyof Settings): boolean {\n const requires = SETTINGS_TOGGLES.find(toggle => toggle.key === key)?.requires\n return settings[key] === false && requires !== undefined && !requirementMet(settings, requires)\n}\n\n/**\n * The settings that a run writes. Only the keys that the run changed count. A\n * value that equals the default is not stored, so it keeps following the\n * default. If the key is stored, the run resets it instead of pinning it. A\n * setting that is off only because the one it needs is off is not written:\n * it comes back as it was when that one turns on again.\n *\n * @param draft - The settings the run ends with.\n * @param initial - The settings the run started with.\n * @param stored - The settings slice stored now (explicit choices only, without defaults).\n * @example\n * ```ts\n * import { DEFAULT_SETTINGS, onboardingSettingsChange } from 'zidane/chat/pure'\n *\n * onboardingSettingsChange({ ...DEFAULT_SETTINGS, spaces: true }, DEFAULT_SETTINGS, {})\n * // { set: { spaces: true }, reset: [] }\n * ```\n */\nexport function onboardingSettingsChange(draft: Partial<Settings>, initial: Partial<Settings>, stored: Partial<Settings>): OnboardingSettingsChange {\n const set: Partial<Settings> = {}\n const reset: (keyof Settings)[] = []\n for (const key of ONBOARDING_SETTING_KEYS) {\n if (!(key in draft) || sameValue(draft[key], initial[key]) || forcedOff(draft, key))\n continue\n if (sameValue(draft[key], DEFAULT_SETTINGS[key])) {\n if (key in stored)\n reset.push(key)\n continue\n }\n Object.assign(set, { [key]: draft[key] })\n }\n return { set, reset }\n}\n\n/**\n * Whether the install existed before the wizard: it has stored settings,\n * credentials, sessions or a last-used provider.\n *\n * @param signals - What the app read from this machine.\n */\nexport function isExistingInstall(signals: OnboardingInstallSignals): boolean {\n return signals.storedSettings || signals.credentials || signals.sessions || signals.lastProvider\n}\n\n/** What {@link needsOnboarding} reads. */\nexport interface OnboardingGateInput {\n /** The records in the state file. */\n state: OnboardingState | undefined\n /** What the app read from this machine. */\n signals: OnboardingInstallSignals\n /** The app asking. */\n surface: OnboardingSurface\n /** The wizard is turned off: `ZIDANE_NO_ONBOARDING=1`, an embedder's `onboarding: false`, a headless run. */\n disabled?: boolean\n}\n\n/**\n * What an app does at launch about the wizard (`docs/ONBOARDING.md`, When it opens).\n *\n * @param input - The records, the machine's signals and the app.\n * @example\n * ```ts\n * import { needsOnboarding } from 'zidane/chat/pure'\n *\n * const signals = { storedSettings: false, credentials: false, sessions: false, lastProvider: false, surfaceFirstRun: true }\n * needsOnboarding({ state: undefined, signals, surface: 'tui' }) // { kind: 'run', mode: 'full' }\n * ```\n */\nexport function needsOnboarding(input: OnboardingGateInput): OnboardingGate {\n if (input.disabled)\n return { kind: 'none' }\n const state = input.state ?? {}\n if (!state.shared) {\n if (state.inProgress || !isExistingInstall(input.signals))\n return { kind: 'run', mode: 'full' }\n return { kind: 'migrate' }\n }\n if (state[input.surface] || state.shared.migrated || state.shared.surface === input.surface)\n return { kind: 'none' }\n return input.signals.surfaceFirstRun ? { kind: 'run', mode: 'extras' } : { kind: 'none' }\n}\n\n/**\n * A record for a run that just ended.\n *\n * @param surface - The app it ran in.\n * @param now - When it ended.\n * @param migrated - Written for an install that predates the wizard.\n */\nexport function onboardingRecord(surface: OnboardingSurface, now: Date = new Date(), migrated = false): OnboardingRecord {\n return { version: ONBOARDING_VERSION, completedAt: now.toISOString(), surface, ...(migrated ? { migrated: true as const } : {}) }\n}\n\n/**\n * The records that a finished run writes. A full, quick or repeated run writes\n * the shared record and the record of this app. An extras run writes only the\n * record of this app. Connect alone writes none.\n *\n * @param mode - The mode the run ended in.\n * @param surface - The app it ran in.\n * @param now - When it ended.\n */\nexport function onboardingRecords(mode: OnboardingMode, surface: OnboardingSurface, now: Date = new Date()): OnboardingState {\n if (mode === 'connect')\n return {}\n const record = onboardingRecord(surface, now)\n const own = surfaceRecord(surface, record)\n return mode === 'extras' ? own : { shared: record, ...own }\n}\n\nfunction surfaceRecord(surface: OnboardingSurface, record: OnboardingRecord): OnboardingState {\n return surface === 'tui' ? { tui: record } : { desktop: record }\n}\n\n/**\n * The records an install that predates the wizard gets, so it never opens on its own.\n *\n * @param surface - The app that noticed.\n * @param now - When.\n */\nexport function migratedOnboardingRecords(surface: OnboardingSurface, now: Date = new Date()): OnboardingState {\n const record = onboardingRecord(surface, now, true)\n return { shared: record, ...surfaceRecord(surface, record) }\n}\n\n/**\n * Stored records with new ones merged in. A finished run (any record) clears\n * the in-progress marker.\n *\n * @param stored - The records in the state file now.\n * @param records - The records to add, such as {@link OnboardingResult.records} or an `inProgress` marker.\n */\nexport function mergeOnboardingState(stored: OnboardingState | undefined, records: OnboardingState): OnboardingState {\n const next: OnboardingState = { ...stored, ...records }\n if (records.shared || records.tui || records.desktop)\n delete next.inProgress\n return next\n}\n\n/**\n * The mode a run ends in: Quick start turns a full run into a quick one.\n *\n * @param state - The run.\n */\nexport function effectiveOnboardingMode(state: OnboardingFlowState): OnboardingMode {\n return state.context.mode === 'full' && state.draft.path === 'quick' ? 'quick' : state.context.mode\n}\n\n/**\n * What a finished run writes: the settings change, the provider pick, the\n * look and the records.\n *\n * @param state - The finished run.\n * @param stored - The settings slice stored now.\n * @param now - When it finished.\n */\nexport function buildOnboardingResult(state: OnboardingFlowState, stored: Partial<Settings>, now: Date = new Date()): OnboardingResult {\n const mode = effectiveOnboardingMode(state)\n const { draft, initial } = state\n const provider = draft.provider && !sameValue(draft.provider, initial.provider) ? draft.provider : undefined\n const appearance = draft.appearance && !sameValue(draft.appearance, initial.appearance) ? draft.appearance : undefined\n return {\n mode,\n surface: state.context.surface,\n settings: onboardingSettingsChange(draft.settings, initial.settings, stored),\n ...(provider ? { provider } : {}),\n ...(appearance ? { appearance } : {}),\n records: onboardingRecords(mode, state.context.surface, now),\n }\n}\n","/**\n * The welcome screen's usage summary (`usage.summary`, the desktop app's\n * `usage:summary`): tokens, spend, models, streaks and the activity heatmap,\n * reduced from per-day buckets of a sessions database\n * (`../session/sqlite-usage.ts`).\n *\n * - Tokens and the model split come from assistant turns (`usage.modelId`,\n * `input` / `output` / `cacheRead` / `cacheCreation`): runs carry no\n * per-model attribution.\n * - Spend comes from assistant-turn `usage.cost`, not run costs: a run that\n * died before its stats landed lost its cost, and a run can't be split by\n * provider.\n * - Messages are parent user turns only, so spawned prompts don't count.\n *\n * Days are the host's local days: the buckets are `date(…, 'localtime')` and\n * {@link localDayString} must agree with them, or streaks drift at midnight.\n * Every range is cut from the same buckets.\n */\nimport type { UsageBuckets } from '../session/sqlite-usage'\n\n/** The summary's window. */\nexport type UsageRange = 'all' | '30d' | '7d'\n\n/** The ranges a summary is asked for. */\nexport const USAGE_RANGES: readonly UsageRange[] = ['all', '30d', '7d']\n\n/**\n * Per-model usage over the summary's range. `tokensIn` counts everything\n * sent to the model, prompt input PLUS cache reads and creation: raw `input`\n * under-reports cache-heavy providers by orders of magnitude. `cost` is USD.\n */\nexport interface UsageModelRow {\n /** The model id. */\n modelId: string\n /** Display name inferred from the model id's shape ({@link providerOfModel}). */\n provider: string\n /** Assistant turns. */\n turns: number\n /** Tokens sent, cache included. */\n tokensIn: number\n /** Tokens produced. */\n tokensOut: number\n /** USD. */\n cost: number\n}\n\n/** One stacked-bar segment of the Models chart: tokens for a model on a day. */\nexport interface UsageModelDayPoint {\n /** Local `YYYY-MM-DD`. */\n day: string\n /** The model id. */\n modelId: string\n /** Tokens in and out. */\n tokens: number\n}\n\n/** Spend per provider. */\nexport interface UsageProviderRow {\n /** Display name ({@link providerOfModel}). */\n provider: string\n /** USD. */\n cost: number\n}\n\n/** One heatmap cell. Sparse: days without activity are left out. */\nexport interface UsageHeatmapDay {\n /** Local `YYYY-MM-DD`. */\n day: string\n /** Tokens in and out that day. */\n tokens: number\n /** Human messages that day. */\n messages: number\n}\n\n/** Usage and spend over a range. The heatmap always spans the last ~26 weeks, whatever the range. */\nexport interface UsageSummary {\n /** The range it covers. */\n range: UsageRange\n /** First name for the greeting; `null` when unknown. */\n userName: string | null\n /** Sessions created in range. */\n sessions: number\n /** Parent (human) user turns: spawned prompts excluded. */\n messages: number\n /** Tokens in and out. */\n totalTokens: number\n /** Tokens sent, cache included. */\n tokensIn: number\n /** Tokens produced. */\n tokensOut: number\n /** USD, from assistant-turn usage costs. */\n totalCost: number\n /** Days with a human message. */\n activeDays: number\n /** Consecutive active days up to today (or yesterday). */\n currentStreak: number\n /** The longest run of active days. */\n longestStreak: number\n /** Local hour (0-23) with the most messages; `null` without data. */\n peakHour: number | null\n /** The model with the most turns; `null` without data. */\n favoriteModel: string | null\n /** Per model, most tokens first. */\n models: UsageModelRow[]\n /** The Models chart's points (the last 120 days at most). */\n modelDaily: UsageModelDayPoint[]\n /** Spend per provider, largest first. */\n providers: UsageProviderRow[]\n /** The activity heatmap's days. */\n heatmap: UsageHeatmapDay[]\n /** When it was computed (ms). */\n generatedAt: number\n}\n\n/** Days of daily-series history the Models chart is capped at. */\nconst CHART_MAX_DAYS = 120\n/** Heatmap window: ~26 weeks. */\nconst HEATMAP_DAYS = 182\nconst DAY_MS = 86_400_000\n\n/**\n * Local `YYYY-MM-DD` of a date, as SQLite's `date(…, 'localtime')` buckets it.\n *\n * @param d - The date.\n */\nexport function localDayString(d: Date): string {\n return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`\n}\n\n/** `days - 1` back from `today`: the first day of a window of `days` days. */\nfunction cutoffDay(today: Date, days: number): string {\n return localDayString(new Date(today.getFullYear(), today.getMonth(), today.getDate() - (days - 1)))\n}\n\n/**\n * The provider a model id belongs to. A session rarely records its provider,\n * so the id's shape is the only signal that covers every turn. Slash ids are\n * OpenRouter-style routes and win over a vendor prefix inside the path.\n *\n * @param modelId - The model id a turn's usage names.\n */\nexport function providerOfModel(modelId: string): string {\n const id = modelId.toLowerCase()\n if (id === 'unknown')\n return 'Unknown'\n if (id.includes('/'))\n return 'OpenRouter'\n if (id.startsWith('claude'))\n return 'Anthropic'\n if (id.startsWith('gpt') || /^o\\d/.test(id) || id.startsWith('codex'))\n return 'OpenAI'\n if (id.startsWith('gemini'))\n return 'Google'\n if (id.startsWith('grok'))\n return 'xAI'\n if (id.startsWith('zai') || id.startsWith('glm'))\n return 'Z.AI'\n if (id.startsWith('deepseek'))\n return 'DeepSeek'\n if (id.startsWith('kimi') || id.startsWith('moonshot'))\n return 'Moonshot'\n if (id.startsWith('mistral') || id.startsWith('devstral') || id.startsWith('magistral'))\n return 'Mistral'\n return 'Other'\n}\n\n/**\n * Streaks over the active days (days with a human message). `current`\n * counts back from today and forgives a today not active yet: a streak isn't\n * broken at 9am.\n *\n * @param activeDays - Local `YYYY-MM-DD` days.\n * @param today - The day streaks count back from.\n */\nexport function computeStreaks(activeDays: ReadonlySet<string>, today: Date): { current: number, longest: number } {\n if (activeDays.size === 0)\n return { current: 0, longest: 0 }\n let current = 0\n let probe = new Date(today.getFullYear(), today.getMonth(), today.getDate()).getTime()\n if (!activeDays.has(localDayString(new Date(probe))))\n probe -= DAY_MS\n while (activeDays.has(localDayString(new Date(probe)))) {\n current += 1\n probe -= DAY_MS\n }\n // `YYYY-MM-DD` sorts chronologically; count runs of consecutive calendar days.\n let longest = 0\n let run = 0\n let prev: number | null = null\n for (const day of [...activeDays].sort()) {\n const [y, m, d] = day.split('-').map(Number)\n const t = new Date(y!, m! - 1, d!).getTime()\n run = prev !== null && t - prev === DAY_MS ? run + 1 : 1\n prev = t\n longest = Math.max(longest, run)\n }\n return { current, longest }\n}\n\n/**\n * The rows of one project.\n *\n * @param buckets - Every project's rows.\n * @param projectRoot - The project root.\n */\nexport function usageOfProject(buckets: UsageBuckets, projectRoot: string): UsageBuckets {\n const mine = (row: { project?: string }) => row.project === projectRoot\n return { assistant: buckets.assistant.filter(mine), user: buckets.user.filter(mine), sessions: buckets.sessions.filter(mine) }\n}\n\n/**\n * The summary of `range`, cut from a database's day buckets.\n *\n * @param allBuckets - The per-day aggregates (`runUsageAggregates`), every project's.\n * @param range - The window.\n * @param options - Options.\n * @param options.now - The moment the range ends. Default: now.\n * @param options.userName - The greeting's first name. Default: `null`.\n * @param options.projectRoot - Only this project's sessions (`sessions.project_root`). Default: every session.\n */\nexport function summarizeUsage(\n allBuckets: UsageBuckets,\n range: UsageRange,\n { now = new Date(), userName = null, projectRoot }: { now?: Date, userName?: string | null, projectRoot?: string } = {},\n): UsageSummary {\n const buckets = projectRoot === undefined ? allBuckets : usageOfProject(allBuckets, projectRoot)\n const cutoff = range === '7d' ? cutoffDay(now, 7) : range === '30d' ? cutoffDay(now, 30) : null\n const inRange = (day: string) => cutoff === null || day >= cutoff\n\n const models = new Map<string, UsageModelRow>()\n const chartCutoff = cutoffDay(now, CHART_MAX_DAYS)\n const daily: UsageModelDayPoint[] = []\n let tokensIn = 0\n let tokensOut = 0\n let totalCost = 0\n for (const row of buckets.assistant) {\n if (!inRange(row.day))\n continue\n const rowIn = row.input + row.cacheRead + row.cacheCreation\n tokensIn += rowIn\n tokensOut += row.output\n totalCost += row.cost\n let m = models.get(row.model)\n if (!m) {\n m = { modelId: row.model, provider: providerOfModel(row.model), turns: 0, tokensIn: 0, tokensOut: 0, cost: 0 }\n models.set(row.model, m)\n }\n m.turns += row.turns\n m.tokensIn += rowIn\n m.tokensOut += row.output\n m.cost += row.cost\n if (row.day >= chartCutoff)\n daily.push({ day: row.day, modelId: row.model, tokens: rowIn + row.output })\n }\n const modelRows = [...models.values()]\n // Zero-token, zero-cost buckets (synthetic turns) are legend noise.\n .filter(m => m.tokensIn + m.tokensOut > 0 || m.cost > 0)\n .sort((a, b) => b.tokensIn + b.tokensOut - (a.tokensIn + a.tokensOut))\n\n const providerTotals = new Map<string, number>()\n for (const m of modelRows)\n providerTotals.set(m.provider, (providerTotals.get(m.provider) ?? 0) + m.cost)\n const providers = [...providerTotals.entries()]\n .map(([provider, cost]) => ({ provider, cost }))\n .filter(p => p.cost > 0)\n .sort((a, b) => b.cost - a.cost)\n\n let messages = 0\n const activeDays = new Set<string>()\n const hourCounts = Array.from<number>({ length: 24 }).fill(0)\n for (const row of buckets.user) {\n if (!inRange(row.day))\n continue\n messages += row.messages\n activeDays.add(row.day)\n if (row.hour >= 0 && row.hour < 24)\n hourCounts[row.hour]! += row.messages\n }\n const streaks = computeStreaks(activeDays, now)\n const peak = Math.max(...hourCounts)\n const peakHour = peak > 0 ? hourCounts.indexOf(peak) : null\n\n let sessions = 0\n for (const row of buckets.sessions) {\n if (inRange(row.day))\n sessions += row.sessions\n }\n\n const heatmapCutoff = cutoffDay(now, HEATMAP_DAYS)\n const heatmapTokens = new Map<string, number>()\n const heatmapMessages = new Map<string, number>()\n for (const row of buckets.assistant) {\n if (row.day >= heatmapCutoff)\n heatmapTokens.set(row.day, (heatmapTokens.get(row.day) ?? 0) + row.input + row.cacheRead + row.cacheCreation + row.output)\n }\n for (const row of buckets.user) {\n if (row.day >= heatmapCutoff)\n heatmapMessages.set(row.day, (heatmapMessages.get(row.day) ?? 0) + row.messages)\n }\n const heatmap = [...new Set([...heatmapTokens.keys(), ...heatmapMessages.keys()])]\n .sort()\n .map(day => ({ day, tokens: heatmapTokens.get(day) ?? 0, messages: heatmapMessages.get(day) ?? 0 }))\n\n const favoriteModel = modelRows.reduce<UsageModelRow | null>((best, m) => (best === null || m.turns > best.turns ? m : best), null)\n return {\n range,\n userName,\n sessions,\n messages,\n totalTokens: tokensIn + tokensOut,\n tokensIn,\n tokensOut,\n totalCost,\n activeDays: activeDays.size,\n currentStreak: streaks.current,\n longestStreak: streaks.longest,\n peakHour,\n favoriteModel: favoriteModel?.modelId ?? null,\n models: modelRows,\n modelDaily: daily,\n providers,\n heatmap,\n generatedAt: now.getTime(),\n }\n}\n"],"mappings":"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"}
|