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