zidane 7.2.5 → 8.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 -27243
- 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-BKVIIOqH.js +0 -331
- package/dist/ipc-BKVIIOqH.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-DfdXrGg9.js +0 -1549
- package/dist/shell-path-DfdXrGg9.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,1549 +0,0 @@
|
|
|
1
|
-
import { Fn as serializedPayloadBytes, Pn as createExtensionBus, ct as discoverAndBuildExtensionRegistry, st as createExtensionHud, vt as createExtensionHostRuntimeBridge } from "./agent-D0zZnGbt.js";
|
|
2
|
-
import { S as resolveInlineSuggestion, b as createPathAutocompleteProvider } from "./src-CkNQKFKr.js";
|
|
3
|
-
import { l as normalizeArtifactInput } from "./artifacts-CV2hUKnL.js";
|
|
4
|
-
import { t as writeFileAtomic } from "./atomic-write-B7zr1ddF.js";
|
|
5
|
-
import { p as resolveStorageDirs } from "./plugins-gbI7SBA6.js";
|
|
6
|
-
import { a as successResponse, i as errorResponse, n as JsonRpcRemoteError, r as createJsonRpcConnection } from "./json-rpc-rSWMUh9b.js";
|
|
7
|
-
import { Vt as isMainSession } from "./turn-operations-DrbJlpq9.js";
|
|
8
|
-
import { c as searchSessionHistoryIndexAsync, n as buildSessionSearchIndexAsync, u as searchSessionIndexAsync } from "./history-search-C4bk1Wz_.js";
|
|
9
|
-
import { Bn as resolveConfig, Di as deriveSessionTitle, Ft as toTodosSnapshot, Jt as createFileMcpCredentialStore, Kt as sessionModelFallbacks, Oi as eventsFromTurns, Pt as createChatHost, fn as DEFAULT_SETTINGS, gn as SETTINGS_TOGGLES, yn as enforceSettingsInvariants } from "./approvals-DKaJZYlm.js";
|
|
10
|
-
import { h as BUILTIN_PROVIDERS, n as applyApiKeyEnv, r as credentialsPath } from "./auth-BG2rVKSt.js";
|
|
11
|
-
import { basename, delimiter, dirname, join } from "node:path";
|
|
12
|
-
import { chmodSync, existsSync, mkdirSync, readFileSync, readdirSync, statSync, unlinkSync } from "node:fs";
|
|
13
|
-
import { userInfo } from "node:os";
|
|
14
|
-
import { spawn, spawnSync } from "node:child_process";
|
|
15
|
-
import { fileURLToPath } from "node:url";
|
|
16
|
-
import { connect, createServer } from "node:net";
|
|
17
|
-
import process$1 from "node:process";
|
|
18
|
-
//#endregion
|
|
19
|
-
//#region src/version.ts
|
|
20
|
-
/**
|
|
21
|
-
* The version of the zidane code this process is running — `package.json`
|
|
22
|
-
* inlined at build time (Bun `--compile`, tsdown for `dist/`, and Vite for
|
|
23
|
-
* the GUI main all fold JSON imports into the bundle, so this never reads
|
|
24
|
-
* the filesystem at runtime and cannot report a NEIGHBOUR package's version
|
|
25
|
-
* the way a `require('../package.json')` from a bundled chunk can).
|
|
26
|
-
*
|
|
27
|
-
* Used by the daemon handshake: a client running newer code than the daemon
|
|
28
|
-
* retires it, so an upgrade can never leave windows silently executing on a
|
|
29
|
-
* process started from the previous release.
|
|
30
|
-
*/
|
|
31
|
-
const ZIDANE_VERSION = "7.2.5";
|
|
32
|
-
/**
|
|
33
|
-
* Compare two dotted versions (`7.0.19`, `7.1.0-beta.1`): negative when `a`
|
|
34
|
-
* is older, positive when newer, 0 when equal. Pre-release tags are ignored
|
|
35
|
-
* — same release train, same code as far as the daemon fence is concerned.
|
|
36
|
-
* Unparseable input compares equal (never fences on garbage).
|
|
37
|
-
*/
|
|
38
|
-
function compareVersions(a, b) {
|
|
39
|
-
const pa = parse(a);
|
|
40
|
-
const pb = parse(b);
|
|
41
|
-
if (!pa || !pb) return 0;
|
|
42
|
-
for (let i = 0; i < 3; i++) {
|
|
43
|
-
const d = (pa[i] ?? 0) - (pb[i] ?? 0);
|
|
44
|
-
if (d !== 0) return d;
|
|
45
|
-
}
|
|
46
|
-
return 0;
|
|
47
|
-
}
|
|
48
|
-
function parse(v) {
|
|
49
|
-
const m = /^v?(\d+)\.(\d+)\.(\d+)/.exec(v.trim());
|
|
50
|
-
return m ? [
|
|
51
|
-
Number(m[1]),
|
|
52
|
-
Number(m[2]),
|
|
53
|
-
Number(m[3])
|
|
54
|
-
] : null;
|
|
55
|
-
}
|
|
56
|
-
//#endregion
|
|
57
|
-
//#region src/ipc/daemon/lifecycle.ts
|
|
58
|
-
/**
|
|
59
|
-
* Daemon lifecycle: socket-path resolution, liveness probing, stale-socket
|
|
60
|
-
* cleanup, and spawn-on-demand.
|
|
61
|
-
*
|
|
62
|
-
* The socket lives in a dedicated daemon subdir of the resolved state dir
|
|
63
|
-
* (`~/.zidane/daemon/daemon.sock` by default, honoring the same
|
|
64
|
-
* `ZIDANE_STORAGE_DIR` / `ZIDANE_PREFIX` env knobs every other surface
|
|
65
|
-
* uses), overridable via `ZIDANE_DAEMON_SOCKET` for parallel installs and
|
|
66
|
-
* tests.
|
|
67
|
-
*/
|
|
68
|
-
/**
|
|
69
|
-
* Build fingerprint for the handshake — the mtime of this module's file
|
|
70
|
-
* at import time. Client and daemon load the same file from disk, so a
|
|
71
|
-
* daemon spawned before a rebuild (or a source edit in dev) reports a
|
|
72
|
-
* different id than a freshly-started client, and the client drains +
|
|
73
|
-
* respawns it. This closes the "same protocol version, different code"
|
|
74
|
-
* gap that a hand-bumped version constant can't: during development the
|
|
75
|
-
* daemon process is frozen at spawn while the tree keeps moving.
|
|
76
|
-
*
|
|
77
|
-
* Compiled binaries (`/$bunfs/…` virtual paths) can't be stat'ed and
|
|
78
|
-
* report `'unknown'`; the check is skipped when EITHER side is unknown,
|
|
79
|
-
* so released builds rely on `IPC_PROTOCOL_VERSION` alone — correct,
|
|
80
|
-
* since their code only changes when the version does.
|
|
81
|
-
*/
|
|
82
|
-
const IPC_BUILD_ID = (() => {
|
|
83
|
-
try {
|
|
84
|
-
const selfPath = fileURLToPath(import.meta.url);
|
|
85
|
-
const ipcRoot = dirname(dirname(selfPath));
|
|
86
|
-
if (!existsSync(join(ipcRoot, "protocol.ts"))) return String(Math.floor(statSync(selfPath).mtimeMs));
|
|
87
|
-
const srcRoot = dirname(ipcRoot);
|
|
88
|
-
let newest = 0;
|
|
89
|
-
const walk = (dir) => {
|
|
90
|
-
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
91
|
-
const full = join(dir, entry.name);
|
|
92
|
-
if (entry.isDirectory()) walk(full);
|
|
93
|
-
else newest = Math.max(newest, statSync(full).mtimeMs);
|
|
94
|
-
}
|
|
95
|
-
};
|
|
96
|
-
try {
|
|
97
|
-
walk(srcRoot);
|
|
98
|
-
} catch {
|
|
99
|
-
newest = statSync(selfPath).mtimeMs;
|
|
100
|
-
}
|
|
101
|
-
return String(Math.floor(newest));
|
|
102
|
-
} catch {
|
|
103
|
-
return "unknown";
|
|
104
|
-
}
|
|
105
|
-
})();
|
|
106
|
-
/**
|
|
107
|
-
* Socket file name. Dev builds (`ZIDANE_DEV=1`, set by every `dev` script
|
|
108
|
-
* in the repo) get their OWN socket so a checkout's TUI, GUI and `zidane
|
|
109
|
-
* daemon` pair up among themselves and never attach to — or get refused by
|
|
110
|
-
* — the installed daemon. Without this, a protocol bump in the working
|
|
111
|
-
* tree turned the dev GUI's dot red against the stable daemon, and a dev
|
|
112
|
-
* daemon could drain the one the installed TUI relies on.
|
|
113
|
-
*/
|
|
114
|
-
function daemonSocketName(env = process.env) {
|
|
115
|
-
return env.ZIDANE_DEV && env.ZIDANE_DEV !== "0" ? "dev.sock" : "daemon.sock";
|
|
116
|
-
}
|
|
117
|
-
function defaultSocketPath() {
|
|
118
|
-
const explicit = process.env.ZIDANE_DAEMON_SOCKET;
|
|
119
|
-
if (explicit) return explicit;
|
|
120
|
-
return join(resolveStorageDirs({ ...process.env.ZIDANE_PREFIX !== void 0 ? { prefix: process.env.ZIDANE_PREFIX } : {} }).stateDir, "daemon", daemonSocketName());
|
|
121
|
-
}
|
|
122
|
-
/**
|
|
123
|
-
* Pre-v6 socket location for the given (v6) socket path, or null when the
|
|
124
|
-
* path isn't the default `<stateDir>/daemon/daemon.sock` layout (explicit
|
|
125
|
-
* `ZIDANE_DAEMON_SOCKET` users never had a migration to run). A daemon
|
|
126
|
-
* still serving the old path would be ORPHANED by the move — new clients
|
|
127
|
-
* never probe it, and a service-style `--idle-exit 0` daemon would hold
|
|
128
|
-
* agents and run-guard markers against the same sessions.db forever —
|
|
129
|
-
* so daemon startup probes this path and drains any survivor.
|
|
130
|
-
*/
|
|
131
|
-
function legacySocketPath(socketPath) {
|
|
132
|
-
const dir = dirname(socketPath);
|
|
133
|
-
if (basename(socketPath) !== "daemon.sock" || basename(dir) !== "daemon") return null;
|
|
134
|
-
return join(dirname(dir), "daemon.sock");
|
|
135
|
-
}
|
|
136
|
-
/**
|
|
137
|
-
* Spawn command that re-enters the CURRENT zidane program as `… daemon`.
|
|
138
|
-
*
|
|
139
|
-
* Three launch shapes:
|
|
140
|
-
* - compiled Bun binary — `Bun.main` is the virtual `/$bunfs/…` entry the
|
|
141
|
-
* binary self-inserts as argv[1], so the spawn must NOT repeat it
|
|
142
|
-
* (verified: repeating it makes the child parse the junk path as its
|
|
143
|
-
* subcommand and boot the full TUI instead of the daemon).
|
|
144
|
-
* - dev (`bun run cli.ts`) — the script path must ride along.
|
|
145
|
-
* - Node hosts (no `Bun` global) — fall back to the published bin.
|
|
146
|
-
*/
|
|
147
|
-
function selfDaemonSpawnCommand() {
|
|
148
|
-
const bunMain = globalThis.Bun?.main;
|
|
149
|
-
if (bunMain?.startsWith("/$bunfs")) return [process.execPath, "daemon"];
|
|
150
|
-
if (bunMain && /(?:^|[/\\])cli\.[cm]?[jt]s$/.test(bunMain)) return [
|
|
151
|
-
process.execPath,
|
|
152
|
-
bunMain,
|
|
153
|
-
"daemon"
|
|
154
|
-
];
|
|
155
|
-
return ["zidane-daemon"];
|
|
156
|
-
}
|
|
157
|
-
/**
|
|
158
|
-
* True when something accepts connections on `socketPath`. Distinguishes a
|
|
159
|
-
* live daemon from a stale socket file left by a crashed one.
|
|
160
|
-
*/
|
|
161
|
-
function probeSocket(socketPath, timeoutMs = 1e3) {
|
|
162
|
-
return new Promise((resolve) => {
|
|
163
|
-
try {
|
|
164
|
-
if (!existsSync(socketPath) || !statSync(socketPath).isSocket()) {
|
|
165
|
-
resolve(false);
|
|
166
|
-
return;
|
|
167
|
-
}
|
|
168
|
-
} catch {
|
|
169
|
-
resolve(false);
|
|
170
|
-
return;
|
|
171
|
-
}
|
|
172
|
-
let socket;
|
|
173
|
-
try {
|
|
174
|
-
socket = connect(socketPath);
|
|
175
|
-
} catch {
|
|
176
|
-
resolve(false);
|
|
177
|
-
return;
|
|
178
|
-
}
|
|
179
|
-
const done = (alive) => {
|
|
180
|
-
socket.destroy();
|
|
181
|
-
resolve(alive);
|
|
182
|
-
};
|
|
183
|
-
socket.once("connect", () => done(true));
|
|
184
|
-
socket.once("error", () => done(false));
|
|
185
|
-
socket.setTimeout(timeoutMs, () => done(false));
|
|
186
|
-
});
|
|
187
|
-
}
|
|
188
|
-
/**
|
|
189
|
-
* Make sure a daemon is listening, spawning one if needed. Resolves with
|
|
190
|
-
* the socket path once a connection succeeds; rejects when the daemon
|
|
191
|
-
* failed to come up within `timeoutMs`.
|
|
192
|
-
*/
|
|
193
|
-
const pendingStarts = /* @__PURE__ */ new Map();
|
|
194
|
-
function ensureDaemonRunning(options = {}) {
|
|
195
|
-
const socketPath = options.socketPath ?? defaultSocketPath();
|
|
196
|
-
const pending = pendingStarts.get(socketPath);
|
|
197
|
-
if (pending) return pending;
|
|
198
|
-
const start = startDaemon({
|
|
199
|
-
...options,
|
|
200
|
-
socketPath
|
|
201
|
-
}).finally(() => pendingStarts.delete(socketPath));
|
|
202
|
-
pendingStarts.set(socketPath, start);
|
|
203
|
-
return start;
|
|
204
|
-
}
|
|
205
|
-
async function startDaemon(options) {
|
|
206
|
-
const socketPath = options.socketPath;
|
|
207
|
-
if (await probeSocket(socketPath)) return socketPath;
|
|
208
|
-
const [cmd, ...args] = options.spawnCommand ?? ["zidane-daemon"];
|
|
209
|
-
const child = spawn(cmd, args, {
|
|
210
|
-
detached: true,
|
|
211
|
-
stdio: "ignore",
|
|
212
|
-
env: {
|
|
213
|
-
...process.env,
|
|
214
|
-
ZIDANE_DAEMON_SOCKET: socketPath
|
|
215
|
-
}
|
|
216
|
-
});
|
|
217
|
-
let spawnError;
|
|
218
|
-
child.once("error", (err) => {
|
|
219
|
-
spawnError = err;
|
|
220
|
-
});
|
|
221
|
-
child.unref();
|
|
222
|
-
const deadline = Date.now() + (options.timeoutMs ?? 8e3);
|
|
223
|
-
for (;;) {
|
|
224
|
-
if (spawnError !== void 0) throw new Error(`Failed to spawn zidane daemon (${cmd}): ${spawnError.message}`);
|
|
225
|
-
if (await probeSocket(socketPath)) return socketPath;
|
|
226
|
-
if (Date.now() > deadline) throw new Error(`zidane daemon did not come up on ${socketPath} within ${options.timeoutMs ?? 8e3}ms.`);
|
|
227
|
-
await new Promise((r) => setTimeout(r, 150));
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
//#endregion
|
|
231
|
-
//#region src/ipc/protocol.ts
|
|
232
|
-
/**
|
|
233
|
-
* v6 (changes since v5):
|
|
234
|
-
* - `event` signals carry an optional `echo` flag marking host-generated
|
|
235
|
-
* SEND/STEER echoes of the user's own message.
|
|
236
|
-
* - `chat.pushQueued` result gained the `'queued'` delivered variant.
|
|
237
|
-
* - `sessions.setModel` now honors `fallbacks`: it persists the canonical
|
|
238
|
-
* `metadata.modelChain` and CLEARS a previously persisted chain when
|
|
239
|
-
* omitted/empty. It also accepts `effort` / `modelOptions` and resets
|
|
240
|
-
* both when omitted (a pick is atomic — no stale per-model knobs).
|
|
241
|
-
* - additive: `extension.emit` notification + `extension:signal` fan-out
|
|
242
|
-
* (the permission-gated per-extension cross-window bus, `ctx.bus`).
|
|
243
|
-
* Older daemons ignore the unknown notification; older clients drop the
|
|
244
|
-
* unknown signal kind — no version bump needed.
|
|
245
|
-
* - additive: `extension.emit` / `extension:signal` carry an optional
|
|
246
|
-
* `nonce` (the emitter's per-process random id) so receivers dedupe on
|
|
247
|
-
* `(extensionId, nonce, seq)` instead of the collision-prone
|
|
248
|
-
* `(extensionId, seq)`. Older peers omit it and fall back to the
|
|
249
|
-
* seq-only key — no version bump needed.
|
|
250
|
-
* - `SessionRow.modelFallbacks` is now populated from the session's
|
|
251
|
-
* persisted `metadata.modelChain`.
|
|
252
|
-
* - Default socket path moved from `<stateDir>/daemon.sock` to the
|
|
253
|
-
* dedicated `<stateDir>/daemon/daemon.sock` (dir chmod'd 0700) so the
|
|
254
|
-
* daemon can harden its socket dir without touching shared state files.
|
|
255
|
-
* - ADDITIVE (no version bump — older daemons answer "method not
|
|
256
|
-
* found", clients degrade to local compute): `suggest.resolve` RPC
|
|
257
|
-
* resolves inline ghost-text autocomplete daemon-side, and the
|
|
258
|
-
* `suggest:changed` signal fans the result out to every OTHER peer
|
|
259
|
-
* on the session so mirroring windows paint the same ghost without
|
|
260
|
-
* recomputing.
|
|
261
|
-
* - ADDITIVE (no version bump — optional field, older daemons simply
|
|
262
|
-
* omit it and clients fall back to the single `suggestion`):
|
|
263
|
-
* `suggest.resolve` results and `suggest:changed` signals carry the
|
|
264
|
-
* resolver's full ordered `candidates` list so daemon mode keeps
|
|
265
|
-
* ↑/↓ candidate rotation instead of degrading to one suggestion.
|
|
266
|
-
* - ADDITIVE (no version bump — `optional()` swallows "method not
|
|
267
|
-
* found" on an older daemon, which then simply keeps its stale
|
|
268
|
-
* registries as before): `runtime.reload` RPC drops the daemon's
|
|
269
|
-
* cached extension registries and retires live agents after a window
|
|
270
|
-
* reloaded its extensions.
|
|
271
|
-
*/
|
|
272
|
-
const IPC_PROTOCOL_VERSION = 9;
|
|
273
|
-
/**
|
|
274
|
-
* JSON-RPC error codes specific to the zidane daemon. Lives here (not in
|
|
275
|
-
* `daemon/server.ts`) so clients can import the codes without pulling the
|
|
276
|
-
* server's module graph.
|
|
277
|
-
*/
|
|
278
|
-
const IpcErrorCode = {
|
|
279
|
-
VersionMismatch: -32010,
|
|
280
|
-
NotHandshaken: -32011
|
|
281
|
-
};
|
|
282
|
-
//#endregion
|
|
283
|
-
//#region src/ipc/client.ts
|
|
284
|
-
function daemonRelease(hello) {
|
|
285
|
-
for (const value of [hello.codeVersion, hello.appVersion]) {
|
|
286
|
-
if (typeof value !== "string") continue;
|
|
287
|
-
const match = /^(?:zidane-gui\/)?(v?\d+\.\d+\.\d+(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?)$/.exec(value.trim());
|
|
288
|
-
if (match) return match[1];
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
var IpcVersionMismatchError = class extends Error {
|
|
292
|
-
daemon;
|
|
293
|
-
constructor(message, daemon) {
|
|
294
|
-
super(message);
|
|
295
|
-
this.daemon = daemon;
|
|
296
|
-
this.name = "IpcVersionMismatchError";
|
|
297
|
-
}
|
|
298
|
-
};
|
|
299
|
-
async function connectIpcClient(options) {
|
|
300
|
-
const socket = await new Promise((resolvePromise, reject) => {
|
|
301
|
-
const s = connect(options.socketPath);
|
|
302
|
-
s.once("connect", () => {
|
|
303
|
-
s.off("error", reject);
|
|
304
|
-
resolvePromise(s);
|
|
305
|
-
});
|
|
306
|
-
s.once("error", reject);
|
|
307
|
-
});
|
|
308
|
-
const listeners = /* @__PURE__ */ new Set();
|
|
309
|
-
let closed = false;
|
|
310
|
-
const connection = createJsonRpcConnection({
|
|
311
|
-
input: socket,
|
|
312
|
-
output: socket,
|
|
313
|
-
onRequest: (message) => errorResponse(message.id, -32601, "Clients do not accept requests."),
|
|
314
|
-
onNotification: (message) => {
|
|
315
|
-
if (message.method !== "ipc.signal") return;
|
|
316
|
-
const signal = message.params;
|
|
317
|
-
if (!signal) return;
|
|
318
|
-
for (const cb of listeners) cb(signal);
|
|
319
|
-
}
|
|
320
|
-
});
|
|
321
|
-
connection.start();
|
|
322
|
-
const close = () => {
|
|
323
|
-
if (closed) return;
|
|
324
|
-
closed = true;
|
|
325
|
-
connection.close();
|
|
326
|
-
socket.destroy();
|
|
327
|
-
};
|
|
328
|
-
socket.on("close", () => {
|
|
329
|
-
if (closed) return;
|
|
330
|
-
closed = true;
|
|
331
|
-
connection.close();
|
|
332
|
-
options.onClose?.();
|
|
333
|
-
});
|
|
334
|
-
socket.on("error", () => {});
|
|
335
|
-
const HELLO_TIMEOUT_MS = 5e3;
|
|
336
|
-
const REQUEST_TIMEOUT_MS = 3e4;
|
|
337
|
-
const LONG_REQUEST_TIMEOUT_MS = 600 * 1e3;
|
|
338
|
-
/** RPCs that legitimately run long — full compaction, index-backed cross-session search. */
|
|
339
|
-
const LONG_REQUEST_METHODS = /* @__PURE__ */ new Set([
|
|
340
|
-
"sessions.compact",
|
|
341
|
-
"history.searchAll",
|
|
342
|
-
"history.searchSession"
|
|
343
|
-
]);
|
|
344
|
-
let helloTimer;
|
|
345
|
-
let hello;
|
|
346
|
-
try {
|
|
347
|
-
hello = await Promise.race([connection.sendRequest("ipc.hello", {
|
|
348
|
-
protocolVersion: 9,
|
|
349
|
-
appVersion: options.appVersion ?? "dev",
|
|
350
|
-
pid: process.pid,
|
|
351
|
-
buildId: options.drainOnMismatch === false ? "unknown" : IPC_BUILD_ID,
|
|
352
|
-
codeVersion: ZIDANE_VERSION
|
|
353
|
-
}), new Promise((_, reject) => {
|
|
354
|
-
helloTimer = setTimeout(() => reject(/* @__PURE__ */ new Error(`Daemon handshake timed out after ${HELLO_TIMEOUT_MS}ms on ${options.socketPath}.`)), HELLO_TIMEOUT_MS);
|
|
355
|
-
})]);
|
|
356
|
-
} catch (err) {
|
|
357
|
-
if (err instanceof JsonRpcRemoteError && err.code === IpcErrorCode.VersionMismatch) {
|
|
358
|
-
const daemon = err.data?.daemon;
|
|
359
|
-
const daemonIsOlderRelease = typeof daemon?.codeVersion === "string" && compareVersions(daemon.codeVersion, ZIDANE_VERSION) < 0;
|
|
360
|
-
const release = daemon && daemonRelease(daemon);
|
|
361
|
-
if (!(release !== void 0 && compareVersions(release, ZIDANE_VERSION) > 0) && (options.drainOnMismatch !== false || daemonIsOlderRelease)) await Promise.race([connection.sendRequest("daemon.shutdown", {}).catch(() => void 0), new Promise((resolve) => setTimeout(resolve, 1500))]);
|
|
362
|
-
close();
|
|
363
|
-
throw new IpcVersionMismatchError(err.message, daemon);
|
|
364
|
-
}
|
|
365
|
-
close();
|
|
366
|
-
throw err;
|
|
367
|
-
} finally {
|
|
368
|
-
clearTimeout(helloTimer);
|
|
369
|
-
}
|
|
370
|
-
const release = daemonRelease(hello);
|
|
371
|
-
if (release !== void 0 && compareVersions(release, ZIDANE_VERSION) < 0) {
|
|
372
|
-
close();
|
|
373
|
-
throw new IpcVersionMismatchError(`Daemon release ${release} is older than client ${ZIDANE_VERSION}. Restart the daemon when active runs have finished.`, hello);
|
|
374
|
-
}
|
|
375
|
-
return {
|
|
376
|
-
hello,
|
|
377
|
-
notify: (method, params) => {
|
|
378
|
-
try {
|
|
379
|
-
connection.sendNotification(method, params);
|
|
380
|
-
} catch {}
|
|
381
|
-
},
|
|
382
|
-
request: async (method, params) => {
|
|
383
|
-
const timeoutMs = LONG_REQUEST_METHODS.has(method) ? LONG_REQUEST_TIMEOUT_MS : REQUEST_TIMEOUT_MS;
|
|
384
|
-
let timer;
|
|
385
|
-
try {
|
|
386
|
-
return await Promise.race([connection.sendRequest(method, params), new Promise((_, reject) => {
|
|
387
|
-
timer = setTimeout(() => {
|
|
388
|
-
reject(/* @__PURE__ */ new Error(`Daemon request ${method} timed out after ${timeoutMs}ms — tearing the connection down.`));
|
|
389
|
-
socket.destroy();
|
|
390
|
-
}, timeoutMs);
|
|
391
|
-
timer.unref?.();
|
|
392
|
-
})]);
|
|
393
|
-
} finally {
|
|
394
|
-
clearTimeout(timer);
|
|
395
|
-
}
|
|
396
|
-
},
|
|
397
|
-
onSignal: (cb) => {
|
|
398
|
-
listeners.add(cb);
|
|
399
|
-
return () => listeners.delete(cb);
|
|
400
|
-
},
|
|
401
|
-
close
|
|
402
|
-
};
|
|
403
|
-
}
|
|
404
|
-
/**
|
|
405
|
-
* One-call convenience for windows: make sure a daemon is up (spawning if
|
|
406
|
-
* needed), connect, and — when the running daemon speaks an older protocol
|
|
407
|
-
* — drain it and spawn a fresh one before retrying once.
|
|
408
|
-
*/
|
|
409
|
-
async function connectOrSpawnDaemon(options = {}) {
|
|
410
|
-
const socketPath = await ensureDaemonRunning(options);
|
|
411
|
-
try {
|
|
412
|
-
return await connectIpcClient({
|
|
413
|
-
...options,
|
|
414
|
-
socketPath
|
|
415
|
-
});
|
|
416
|
-
} catch (err) {
|
|
417
|
-
if (!(err instanceof IpcVersionMismatchError)) throw err;
|
|
418
|
-
const drainDeadline = Date.now() + 2e3;
|
|
419
|
-
while (await probeSocket(socketPath)) {
|
|
420
|
-
if (Date.now() > drainDeadline) break;
|
|
421
|
-
await new Promise((r) => setTimeout(r, 100));
|
|
422
|
-
}
|
|
423
|
-
const retryPath = await ensureDaemonRunning(options);
|
|
424
|
-
return connectIpcClient({
|
|
425
|
-
...options,
|
|
426
|
-
socketPath: retryPath
|
|
427
|
-
});
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
//#endregion
|
|
431
|
-
//#region src/ipc/daemon/environment.ts
|
|
432
|
-
/**
|
|
433
|
-
* File-backed pending-approvals persistence (`pending-approvals.json` next
|
|
434
|
-
* to `state.json`). Small, rewritten atomically enough for its purpose —
|
|
435
|
-
* the queue holds at most a handful of rows.
|
|
436
|
-
*/
|
|
437
|
-
function createFileApprovalsStore(path) {
|
|
438
|
-
function read() {
|
|
439
|
-
if (!existsSync(path)) return [];
|
|
440
|
-
try {
|
|
441
|
-
const parsed = JSON.parse(readFileSync(path, "utf8"));
|
|
442
|
-
return Array.isArray(parsed) ? parsed : [];
|
|
443
|
-
} catch {
|
|
444
|
-
return [];
|
|
445
|
-
}
|
|
446
|
-
}
|
|
447
|
-
function write(rows) {
|
|
448
|
-
if (rows.length === 0) {
|
|
449
|
-
try {
|
|
450
|
-
unlinkSync(path);
|
|
451
|
-
} catch {}
|
|
452
|
-
return;
|
|
453
|
-
}
|
|
454
|
-
mkdirSync(dirname(path), { recursive: true });
|
|
455
|
-
writeFileAtomic(path, JSON.stringify(rows, null, 2));
|
|
456
|
-
}
|
|
457
|
-
return {
|
|
458
|
-
insert(request) {
|
|
459
|
-
const rows = read().filter((r) => r.id !== request.id);
|
|
460
|
-
rows.push(request);
|
|
461
|
-
write(rows);
|
|
462
|
-
},
|
|
463
|
-
delete(id) {
|
|
464
|
-
write(read().filter((r) => r.id !== id));
|
|
465
|
-
},
|
|
466
|
-
clear() {
|
|
467
|
-
write([]);
|
|
468
|
-
},
|
|
469
|
-
list() {
|
|
470
|
-
return read();
|
|
471
|
-
}
|
|
472
|
-
};
|
|
473
|
-
}
|
|
474
|
-
async function createDaemonEnvironment(options) {
|
|
475
|
-
const { createTuiStore } = await import("./session/sqlite.js");
|
|
476
|
-
const config = resolveConfig({
|
|
477
|
-
...options.cwd !== void 0 ? { cwd: options.cwd } : {},
|
|
478
|
-
store: (resolved) => createTuiStore(resolved.db)
|
|
479
|
-
});
|
|
480
|
-
const store = config.store;
|
|
481
|
-
if (config.paths.projectDir) process.stderr.write(`[zidane-daemon] warning: project-DB mode is active — this daemon is bound to ${config.paths.db}. Windows opened in other projects will share this project's sessions. Run a per-project daemon (ZIDANE_DAEMON_SOCKET) to isolate them.\n`);
|
|
482
|
-
const readSettings = () => ({
|
|
483
|
-
...DEFAULT_SETTINGS,
|
|
484
|
-
...config.initialSettings,
|
|
485
|
-
...config.stateStore.load().settings ?? {}
|
|
486
|
-
});
|
|
487
|
-
/**
|
|
488
|
-
* Fields the invariant forced (only ever `false`), so the stored file records
|
|
489
|
-
* them too — otherwise a later read of `state.settings` would resurrect the
|
|
490
|
-
* value the invariant just rejected.
|
|
491
|
-
*/
|
|
492
|
-
const pickInvariantFields = (merged, patch) => {
|
|
493
|
-
const out = {};
|
|
494
|
-
for (const row of SETTINGS_TOGGLES) if (row.requires && merged[row.key] === false && patch[row.key] !== false) out[row.key] = false;
|
|
495
|
-
return out;
|
|
496
|
-
};
|
|
497
|
-
const writeSettings = (patch) => {
|
|
498
|
-
const state = config.stateStore.load();
|
|
499
|
-
const merged = enforceSettingsInvariants({
|
|
500
|
-
...DEFAULT_SETTINGS,
|
|
501
|
-
...config.initialSettings,
|
|
502
|
-
...state.settings ?? {},
|
|
503
|
-
...patch
|
|
504
|
-
});
|
|
505
|
-
const next = {
|
|
506
|
-
...state.settings ?? {},
|
|
507
|
-
...patch,
|
|
508
|
-
...pickInvariantFields(merged, patch)
|
|
509
|
-
};
|
|
510
|
-
config.stateStore.save({
|
|
511
|
-
...state,
|
|
512
|
-
settings: next
|
|
513
|
-
});
|
|
514
|
-
return merged;
|
|
515
|
-
};
|
|
516
|
-
const hud = createExtensionHud();
|
|
517
|
-
hud.subscribe(() => {
|
|
518
|
-
const state = hud.getState();
|
|
519
|
-
options.broadcast({
|
|
520
|
-
kind: "hud:state",
|
|
521
|
-
statuses: Object.fromEntries(Object.entries(state.statuses).map(([name, s]) => [name, {
|
|
522
|
-
text: s.text,
|
|
523
|
-
loader: s.loader
|
|
524
|
-
}])),
|
|
525
|
-
banners: Object.fromEntries(Object.entries(state.banners).map(([name, lines]) => [name, [...lines]]))
|
|
526
|
-
});
|
|
527
|
-
});
|
|
528
|
-
hud.onNotify((n) => {
|
|
529
|
-
options.broadcast({
|
|
530
|
-
kind: "hud:notify",
|
|
531
|
-
extension: n.extension,
|
|
532
|
-
text: n.text,
|
|
533
|
-
tone: n.tone
|
|
534
|
-
});
|
|
535
|
-
});
|
|
536
|
-
const extensionBus = createExtensionBus();
|
|
537
|
-
extensionBus.onEmit((envelope) => {
|
|
538
|
-
options.broadcast({
|
|
539
|
-
kind: "extension:signal",
|
|
540
|
-
...envelope
|
|
541
|
-
});
|
|
542
|
-
});
|
|
543
|
-
const RUNTIME_CACHE_MAX = 16;
|
|
544
|
-
const runtimeCache = /* @__PURE__ */ new Map();
|
|
545
|
-
const hostRuntime = createExtensionHostRuntimeBridge();
|
|
546
|
-
/**
|
|
547
|
-
* Best-effort release of an evicted entry's extension registry
|
|
548
|
-
* (teardown hooks, HUD writers — `registry.dispose()` is idempotent).
|
|
549
|
-
* Fire-and-forget: eviction must never block or throw on a broken
|
|
550
|
-
* extension teardown, and a rejected resolve has nothing to release.
|
|
551
|
-
*/
|
|
552
|
-
const disposeRuntime = (promise) => {
|
|
553
|
-
promise.then((config) => config.extensions.dispose()).catch(() => void 0);
|
|
554
|
-
};
|
|
555
|
-
const runtimeConfig = (cwd) => {
|
|
556
|
-
const settings = readSettings();
|
|
557
|
-
const key = [
|
|
558
|
-
cwd,
|
|
559
|
-
JSON.stringify(settings.enabledExtensions ?? null),
|
|
560
|
-
String(settings.loadProjectExtensions === true)
|
|
561
|
-
].join("\0");
|
|
562
|
-
const existing = runtimeCache.get(key);
|
|
563
|
-
if (existing) {
|
|
564
|
-
runtimeCache.delete(key);
|
|
565
|
-
runtimeCache.set(key, existing);
|
|
566
|
-
return existing;
|
|
567
|
-
}
|
|
568
|
-
const promise = (async () => {
|
|
569
|
-
return resolveConfig({
|
|
570
|
-
cwd,
|
|
571
|
-
extensionRegistry: await discoverAndBuildExtensionRegistry({
|
|
572
|
-
cwd,
|
|
573
|
-
hud,
|
|
574
|
-
bus: extensionBus,
|
|
575
|
-
hostRuntime,
|
|
576
|
-
prefix: config.prefix,
|
|
577
|
-
...settings.enabledExtensions !== void 0 ? { enabledAllowlist: [...settings.enabledExtensions] } : {},
|
|
578
|
-
trustProjectExtensions: settings.loadProjectExtensions === true,
|
|
579
|
-
readSettings
|
|
580
|
-
}),
|
|
581
|
-
store: () => store
|
|
582
|
-
});
|
|
583
|
-
})();
|
|
584
|
-
runtimeCache.set(key, promise);
|
|
585
|
-
while (runtimeCache.size > RUNTIME_CACHE_MAX) {
|
|
586
|
-
const oldest = runtimeCache.keys().next().value;
|
|
587
|
-
if (oldest === void 0) break;
|
|
588
|
-
const evicted = runtimeCache.get(oldest);
|
|
589
|
-
runtimeCache.delete(oldest);
|
|
590
|
-
if (evicted) disposeRuntime(evicted);
|
|
591
|
-
}
|
|
592
|
-
promise.catch(() => {
|
|
593
|
-
if (runtimeCache.get(key) === promise) runtimeCache.delete(key);
|
|
594
|
-
});
|
|
595
|
-
return promise;
|
|
596
|
-
};
|
|
597
|
-
const userDataDir = () => config.paths.userDir;
|
|
598
|
-
const applyAuthEnv = () => {
|
|
599
|
-
const dir = userDataDir();
|
|
600
|
-
process.env.ZIDANE_CREDENTIALS_PATH = credentialsPath(dir);
|
|
601
|
-
applyApiKeyEnv(dir, BUILTIN_PROVIDERS);
|
|
602
|
-
};
|
|
603
|
-
return {
|
|
604
|
-
multiProcess: true,
|
|
605
|
-
broadcast: options.broadcast,
|
|
606
|
-
store,
|
|
607
|
-
readSettings,
|
|
608
|
-
writeSettings,
|
|
609
|
-
runtimeConfig,
|
|
610
|
-
hostRuntime,
|
|
611
|
-
userDataDir,
|
|
612
|
-
applyAuthEnv,
|
|
613
|
-
mcpCredentialStore: () => createFileMcpCredentialStore(userDataDir()),
|
|
614
|
-
approvalsPersistence: createFileApprovalsStore(join(dirname(config.paths.state), "pending-approvals.json")),
|
|
615
|
-
...options.debugStream !== void 0 ? { debugStream: options.debugStream } : {},
|
|
616
|
-
invalidateRuntime: () => {
|
|
617
|
-
for (const entry of runtimeCache.values()) disposeRuntime(entry);
|
|
618
|
-
runtimeCache.clear();
|
|
619
|
-
},
|
|
620
|
-
config,
|
|
621
|
-
extensionBus
|
|
622
|
-
};
|
|
623
|
-
}
|
|
624
|
-
//#endregion
|
|
625
|
-
//#region src/ipc/daemon/server.ts
|
|
626
|
-
/**
|
|
627
|
-
* Per-peer write backpressure bound. A wedged peer (e.g. a SIGSTOP'd
|
|
628
|
-
* terminal) stops reading its socket, so fan-out writes pile up in
|
|
629
|
-
* `socket.writableLength` without bound and can OOM the daemon. A peer
|
|
630
|
-
* buffering more than this is destroyed — the protocol supports re-attach
|
|
631
|
-
* + backfill, so killing a wedged peer is safe and self-healing.
|
|
632
|
-
*/
|
|
633
|
-
const PEER_WRITE_HIGH_WATERMARK = 8 * 1024 * 1024;
|
|
634
|
-
async function startIpcDaemon(options) {
|
|
635
|
-
const { socketPath } = options;
|
|
636
|
-
const log = options.onLog ?? (() => void 0);
|
|
637
|
-
const startedAt = Date.now();
|
|
638
|
-
const appVersion = options.appVersion ?? "dev";
|
|
639
|
-
const peers = /* @__PURE__ */ new Set();
|
|
640
|
-
/** Sessions with a run in flight, tracked from the run signals themselves. */
|
|
641
|
-
const runningSessions = /* @__PURE__ */ new Set();
|
|
642
|
-
const broadcast = (signal) => {
|
|
643
|
-
broadcastExcept(null, signal);
|
|
644
|
-
switch (signal.kind) {
|
|
645
|
-
case "run:start":
|
|
646
|
-
runningSessions.add(signal.sessionId);
|
|
647
|
-
broadcastPresence();
|
|
648
|
-
break;
|
|
649
|
-
case "run:end":
|
|
650
|
-
runningSessions.delete(signal.sessionId);
|
|
651
|
-
broadcastPresence();
|
|
652
|
-
break;
|
|
653
|
-
case "approval:request":
|
|
654
|
-
case "approval:resolved":
|
|
655
|
-
case "interaction:request":
|
|
656
|
-
case "interaction:resolved":
|
|
657
|
-
broadcastPresence();
|
|
658
|
-
break;
|
|
659
|
-
default: break;
|
|
660
|
-
}
|
|
661
|
-
};
|
|
662
|
-
/**
|
|
663
|
-
* Fan out to every handshaken peer except `origin`. Draft signals use
|
|
664
|
-
* this so a window's own keystrokes never bounce back and fight its
|
|
665
|
-
* cursor — echo-safety by construction, no client-side dedupe.
|
|
666
|
-
*/
|
|
667
|
-
function broadcastExcept(origin, signal) {
|
|
668
|
-
for (const peer of peers) {
|
|
669
|
-
if (!peer.ready || peer === origin) continue;
|
|
670
|
-
if (peer.socket.writableLength > PEER_WRITE_HIGH_WATERMARK) {
|
|
671
|
-
console.warn(`[ipc-daemon] peer buffered ${peer.socket.writableLength} bytes (> ${PEER_WRITE_HIGH_WATERMARK}) — destroying wedged connection`);
|
|
672
|
-
peer.socket.destroy();
|
|
673
|
-
continue;
|
|
674
|
-
}
|
|
675
|
-
try {
|
|
676
|
-
peer.connection.sendNotification("ipc.signal", signal);
|
|
677
|
-
} catch {}
|
|
678
|
-
}
|
|
679
|
-
}
|
|
680
|
-
/**
|
|
681
|
-
* Shared per-session prompt drafts. Ephemeral by design — in-memory
|
|
682
|
-
* only, cleared when any window submits, gone on daemon restart.
|
|
683
|
-
*/
|
|
684
|
-
const drafts = /* @__PURE__ */ new Map();
|
|
685
|
-
/**
|
|
686
|
-
* Bound a peer-supplied attachment list: shape-guarded entries only,
|
|
687
|
-
* 24 MiB of base64 per list. Shared by `queue.add` and
|
|
688
|
-
* `drafts.setAttachments`.
|
|
689
|
-
*/
|
|
690
|
-
function boundAttachments(list) {
|
|
691
|
-
let budget = 24 * 1024 * 1024;
|
|
692
|
-
const out = [];
|
|
693
|
-
for (const entry of list) {
|
|
694
|
-
const a = entry;
|
|
695
|
-
if (typeof a?.mediaType !== "string" || typeof a?.data !== "string") continue;
|
|
696
|
-
budget -= a.data.length;
|
|
697
|
-
if (budget < 0) break;
|
|
698
|
-
out.push({
|
|
699
|
-
mediaType: a.mediaType,
|
|
700
|
-
data: a.data,
|
|
701
|
-
...typeof a.name === "string" ? { name: a.name.slice(0, 256) } : {}
|
|
702
|
-
});
|
|
703
|
-
}
|
|
704
|
-
return out;
|
|
705
|
-
}
|
|
706
|
-
/** Total base64 bytes currently pinned by ALL draft attachments. */
|
|
707
|
-
function draftAttachmentBytes() {
|
|
708
|
-
let total = 0;
|
|
709
|
-
for (const entry of drafts.values()) for (const attachment of entry.attachments ?? []) total += attachment.data.length;
|
|
710
|
-
return total;
|
|
711
|
-
}
|
|
712
|
-
/** Consume the draft after a submit — every window except the sender clears. */
|
|
713
|
-
function clearDraft(sessionId, origin) {
|
|
714
|
-
const entry = drafts.get(sessionId);
|
|
715
|
-
if (!entry) return;
|
|
716
|
-
drafts.delete(sessionId);
|
|
717
|
-
if (entry.text) broadcastExcept(origin, {
|
|
718
|
-
kind: "draft:changed",
|
|
719
|
-
sessionId,
|
|
720
|
-
text: ""
|
|
721
|
-
});
|
|
722
|
-
if (entry.attachments?.length) broadcastExcept(origin, {
|
|
723
|
-
kind: "draft:attachments",
|
|
724
|
-
sessionId,
|
|
725
|
-
attachments: []
|
|
726
|
-
});
|
|
727
|
-
}
|
|
728
|
-
/**
|
|
729
|
-
* Shared inline-autocomplete state — per-session latest-wins
|
|
730
|
-
* coalescing for `suggest.resolve`. `seq` is the newest sequence seen
|
|
731
|
-
* (stale requests return without computing); `controller` aborts an
|
|
732
|
-
* in-flight compute when a newer request supersedes it (the GUI's
|
|
733
|
-
* controller-map pattern, daemon-side). Bounded like `drafts`: keys
|
|
734
|
-
* only evict on session delete, so cap the map at 256 sessions.
|
|
735
|
-
*/
|
|
736
|
-
const suggests = /* @__PURE__ */ new Map();
|
|
737
|
-
/** Drop empty draft entries so the map only holds live composing state. */
|
|
738
|
-
function compactDraft(sessionId, entry) {
|
|
739
|
-
if (!entry.text && !entry.shell && !entry.attachments?.length && !entry.search?.active) drafts.delete(sessionId);
|
|
740
|
-
else drafts.set(sessionId, entry);
|
|
741
|
-
}
|
|
742
|
-
const env = await (options.createEnvironment ? options.createEnvironment(broadcast) : createDaemonEnvironment({ broadcast }));
|
|
743
|
-
const host = createChatHost(env);
|
|
744
|
-
/**
|
|
745
|
-
* Live presence: which sessions have attached windows, and their
|
|
746
|
-
* run / awaiting-input state. Union of watched, live-agent, and
|
|
747
|
-
* pending-HITL sessions — small by construction.
|
|
748
|
-
*/
|
|
749
|
-
function computePresence() {
|
|
750
|
-
const windowCounts = /* @__PURE__ */ new Map();
|
|
751
|
-
for (const peer of peers) if (peer.ready && peer.watching) windowCounts.set(peer.watching, (windowCounts.get(peer.watching) ?? 0) + 1);
|
|
752
|
-
const attention = /* @__PURE__ */ new Set();
|
|
753
|
-
for (const request of host.approvals.listPending()) attention.add(request.sessionId);
|
|
754
|
-
for (const pending of host.interactions.list()) attention.add(pending.sessionId);
|
|
755
|
-
const live = new Map(host.listLiveSessions().map((entry) => [entry.sessionId, entry.running]));
|
|
756
|
-
return [.../* @__PURE__ */ new Set([
|
|
757
|
-
...windowCounts.keys(),
|
|
758
|
-
...attention,
|
|
759
|
-
...live.keys(),
|
|
760
|
-
...runningSessions
|
|
761
|
-
])].map((sessionId) => ({
|
|
762
|
-
sessionId,
|
|
763
|
-
windows: windowCounts.get(sessionId) ?? 0,
|
|
764
|
-
running: runningSessions.has(sessionId) || live.get(sessionId) === true,
|
|
765
|
-
attention: attention.has(sessionId)
|
|
766
|
-
}));
|
|
767
|
-
}
|
|
768
|
-
function broadcastPresence() {
|
|
769
|
-
broadcastExcept(null, {
|
|
770
|
-
kind: "presence:changed",
|
|
771
|
-
sessions: computePresence()
|
|
772
|
-
});
|
|
773
|
-
}
|
|
774
|
-
const SEARCH_INDEX_CACHE_MAX = 64;
|
|
775
|
-
/** Latest `history.searchAll` generation per peer — see the handler. */
|
|
776
|
-
const searchGenerations = /* @__PURE__ */ new WeakMap();
|
|
777
|
-
const searchIndexCache = /* @__PURE__ */ new Map();
|
|
778
|
-
async function sessionSearchIndex(sessionId) {
|
|
779
|
-
const liveTurns = host.getLiveTurns(sessionId);
|
|
780
|
-
let turns;
|
|
781
|
-
let key;
|
|
782
|
-
if (liveTurns) {
|
|
783
|
-
turns = [...liveTurns];
|
|
784
|
-
key = `live:${turns.length}:${turns.at(-1)?.id ?? ""}`;
|
|
785
|
-
} else {
|
|
786
|
-
const data = await env.store.load(sessionId);
|
|
787
|
-
if (!data) return null;
|
|
788
|
-
turns = data.turns;
|
|
789
|
-
key = `store:${data.updatedAt}:${data.turns.length}`;
|
|
790
|
-
}
|
|
791
|
-
const cached = searchIndexCache.get(sessionId);
|
|
792
|
-
if (cached?.key === key) {
|
|
793
|
-
searchIndexCache.delete(sessionId);
|
|
794
|
-
searchIndexCache.set(sessionId, cached);
|
|
795
|
-
return cached.index;
|
|
796
|
-
}
|
|
797
|
-
const index = await buildSessionSearchIndexAsync(turns);
|
|
798
|
-
searchIndexCache.set(sessionId, {
|
|
799
|
-
key,
|
|
800
|
-
index
|
|
801
|
-
});
|
|
802
|
-
while (searchIndexCache.size > SEARCH_INDEX_CACHE_MAX) {
|
|
803
|
-
const oldest = searchIndexCache.keys().next().value;
|
|
804
|
-
if (oldest === void 0) break;
|
|
805
|
-
searchIndexCache.delete(oldest);
|
|
806
|
-
}
|
|
807
|
-
return index;
|
|
808
|
-
}
|
|
809
|
-
host.approvals.rehydrate();
|
|
810
|
-
const store = env.store;
|
|
811
|
-
/** Wire-supplied limit → sane positive integer capped at 1000, else `undefined` (no limit). */
|
|
812
|
-
function boundedLimit(raw) {
|
|
813
|
-
return Number.isInteger(raw) && raw > 0 ? Math.min(raw, 1e3) : void 0;
|
|
814
|
-
}
|
|
815
|
-
function toRow(data) {
|
|
816
|
-
const meta = data.metadata ?? {};
|
|
817
|
-
const title = deriveSessionTitle(data.turns, meta);
|
|
818
|
-
return {
|
|
819
|
-
id: data.id,
|
|
820
|
-
title,
|
|
821
|
-
...isMainSession(meta) ? { isMain: true } : {},
|
|
822
|
-
projectRoot: data.projectRoot ?? null,
|
|
823
|
-
cwd: typeof meta.cwd === "string" ? meta.cwd : data.projectRoot ?? "",
|
|
824
|
-
...typeof data.agentId === "string" ? { agentId: data.agentId } : {},
|
|
825
|
-
status: host.isSessionRunning(data.id) ? "running" : data.status === "running" && Date.now() - data.updatedAt >= 3e5 ? "idle" : data.status,
|
|
826
|
-
createdAt: data.createdAt,
|
|
827
|
-
updatedAt: data.updatedAt,
|
|
828
|
-
archived: meta.archived === true,
|
|
829
|
-
...typeof meta.providerKey === "string" ? { providerKey: meta.providerKey } : {},
|
|
830
|
-
...typeof meta.modelId === "string" ? { modelId: meta.modelId } : {},
|
|
831
|
-
...typeof meta.effort === "string" ? { effort: meta.effort } : {},
|
|
832
|
-
...meta.modelOptions && typeof meta.modelOptions === "object" ? { modelOptions: meta.modelOptions } : {},
|
|
833
|
-
...(() => {
|
|
834
|
-
const fallbacks = sessionModelFallbacks(meta);
|
|
835
|
-
return fallbacks ? { modelFallbacks: fallbacks } : {};
|
|
836
|
-
})()
|
|
837
|
-
};
|
|
838
|
-
}
|
|
839
|
-
async function loadOrThrow(sessionId) {
|
|
840
|
-
const data = await store.load(sessionId);
|
|
841
|
-
if (!data) throw new Error(`Session "${sessionId}" not found.`);
|
|
842
|
-
return data;
|
|
843
|
-
}
|
|
844
|
-
/**
|
|
845
|
-
* Apply a metadata-only patch. Prefers the host's live-session path (safe
|
|
846
|
-
* mid-run — routes through the in-memory `Session` whose turns are the
|
|
847
|
-
* source of truth); only falls back to a store-level full-document
|
|
848
|
-
* `save()` when the session is idle, where no run is appending turns so
|
|
849
|
-
* `load → mutate → save` can't drop concurrently-persisted turns.
|
|
850
|
-
*
|
|
851
|
-
* `bumpUpdatedAt: false` skips the recency bump so a rename doesn't yank
|
|
852
|
-
* the row to the top of recency-sorted lists (mirrors the GUI).
|
|
853
|
-
*/
|
|
854
|
-
async function patchSessionMeta(sessionId, patch, opts = {}) {
|
|
855
|
-
await host.patchSessionMeta(sessionId, patch, opts);
|
|
856
|
-
broadcast({
|
|
857
|
-
kind: "sessions:changed",
|
|
858
|
-
op: "update",
|
|
859
|
-
sessionId
|
|
860
|
-
});
|
|
861
|
-
}
|
|
862
|
-
async function patchSession(sessionId, mutate) {
|
|
863
|
-
const data = await loadOrThrow(sessionId);
|
|
864
|
-
mutate(data);
|
|
865
|
-
data.updatedAt = Date.now();
|
|
866
|
-
await store.save(data);
|
|
867
|
-
broadcast({
|
|
868
|
-
kind: "sessions:changed",
|
|
869
|
-
op: "update",
|
|
870
|
-
sessionId
|
|
871
|
-
});
|
|
872
|
-
}
|
|
873
|
-
async function createSessionRow(params) {
|
|
874
|
-
const id = await host.createSession(params);
|
|
875
|
-
const data = await loadOrThrow(id);
|
|
876
|
-
broadcast({
|
|
877
|
-
kind: "sessions:changed",
|
|
878
|
-
op: "create",
|
|
879
|
-
sessionId: id
|
|
880
|
-
});
|
|
881
|
-
return toRow(data);
|
|
882
|
-
}
|
|
883
|
-
const methods = {
|
|
884
|
-
"chat.send": async (p, peer) => {
|
|
885
|
-
const { wake: _stripped, ...params } = p;
|
|
886
|
-
const result = await host.sendPrompt(params);
|
|
887
|
-
clearDraft(p.sessionId, peer);
|
|
888
|
-
return result;
|
|
889
|
-
},
|
|
890
|
-
"chat.interrupt": (p) => host.interrupt(p.sessionId),
|
|
891
|
-
"chat.steer": (p) => host.steer(p.sessionId, p.message),
|
|
892
|
-
"chat.followUp": (p) => host.followUp(p.sessionId, p.message),
|
|
893
|
-
"chat.pushQueued": async (p, peer) => {
|
|
894
|
-
const result = await host.pushQueued(p.sessionId, p.message, p.activatedSkills);
|
|
895
|
-
clearDraft(p.sessionId, peer);
|
|
896
|
-
return result;
|
|
897
|
-
},
|
|
898
|
-
"commands.list": (p) => host.listCommands(p.cwd),
|
|
899
|
-
"commands.completeArgs": (p) => host.completeCommandArgs(p.cwd, p.name, p.argsPrefix),
|
|
900
|
-
"queue.list": (p) => host.queueList(p.sessionId),
|
|
901
|
-
"sessions.systemMd": (p) => host.sessionSystemMd(p.sessionId),
|
|
902
|
-
"queue.add": async (p, peer) => {
|
|
903
|
-
const text = typeof p.text === "string" ? p.text.slice(0, 64 * 1024) : "";
|
|
904
|
-
const activatedSkills = Array.isArray(p.activatedSkills) ? p.activatedSkills.filter((name) => typeof name === "string") : void 0;
|
|
905
|
-
const attachments = Array.isArray(p.attachments) ? boundAttachments(p.attachments) : void 0;
|
|
906
|
-
const asCommand = typeof p.asCommand === "string" ? p.asCommand.slice(0, 128) : void 0;
|
|
907
|
-
const result = await host.queueAdd(p.sessionId, text, activatedSkills, attachments, asCommand);
|
|
908
|
-
clearDraft(p.sessionId, peer);
|
|
909
|
-
return result;
|
|
910
|
-
},
|
|
911
|
-
"sessions.contextSegments": (p) => host.getContextSegments(p.sessionId),
|
|
912
|
-
"sessions.createSystemOverride": (p) => host.createSystemOverride(p.sessionId),
|
|
913
|
-
"queue.remove": (p) => host.queueRemove(p.sessionId, p.id),
|
|
914
|
-
"queue.update": (p) => host.queueUpdate(p.sessionId, p.id, typeof p.text === "string" ? p.text.slice(0, 64 * 1024) : ""),
|
|
915
|
-
"queue.pushNow": (p) => host.queuePushNow(p.sessionId, p.id),
|
|
916
|
-
"drafts.get": (p) => {
|
|
917
|
-
const entry = drafts.get(p.sessionId);
|
|
918
|
-
return {
|
|
919
|
-
text: entry?.text ?? "",
|
|
920
|
-
...entry?.shell ? { shell: true } : {},
|
|
921
|
-
...entry?.attachments?.length ? { attachments: entry.attachments } : {},
|
|
922
|
-
...entry?.search?.active ? { search: entry.search } : {}
|
|
923
|
-
};
|
|
924
|
-
},
|
|
925
|
-
"drafts.set": (p, peer) => {
|
|
926
|
-
const text = typeof p.text === "string" ? p.text.slice(0, 64 * 1024) : "";
|
|
927
|
-
const truncated = typeof p.text === "string" && p.text.length > text.length;
|
|
928
|
-
if (!drafts.has(p.sessionId) && drafts.size >= 256) return;
|
|
929
|
-
const entry = drafts.get(p.sessionId) ?? {};
|
|
930
|
-
entry.text = text.length > 0 ? text : void 0;
|
|
931
|
-
entry.shell = p.shell === true ? true : void 0;
|
|
932
|
-
compactDraft(p.sessionId, entry);
|
|
933
|
-
broadcastExcept(truncated ? null : peer, {
|
|
934
|
-
kind: "draft:changed",
|
|
935
|
-
sessionId: p.sessionId,
|
|
936
|
-
text,
|
|
937
|
-
...p.shell === true ? { shell: true } : {}
|
|
938
|
-
});
|
|
939
|
-
},
|
|
940
|
-
"search.set": (p, peer) => {
|
|
941
|
-
const active = p.active === true;
|
|
942
|
-
const query = typeof p.query === "string" ? p.query.slice(0, 4 * 1024) : "";
|
|
943
|
-
if (!drafts.has(p.sessionId) && drafts.size >= 256) return;
|
|
944
|
-
const entry = drafts.get(p.sessionId) ?? {};
|
|
945
|
-
entry.search = active || query.length > 0 ? {
|
|
946
|
-
active,
|
|
947
|
-
query
|
|
948
|
-
} : void 0;
|
|
949
|
-
compactDraft(p.sessionId, entry);
|
|
950
|
-
broadcastExcept(peer, {
|
|
951
|
-
kind: "search:changed",
|
|
952
|
-
sessionId: p.sessionId,
|
|
953
|
-
active,
|
|
954
|
-
query
|
|
955
|
-
});
|
|
956
|
-
},
|
|
957
|
-
"extension.emit": async (p, peer) => {
|
|
958
|
-
const extensionId = typeof p.extensionId === "string" ? p.extensionId : "";
|
|
959
|
-
if (extensionId.length === 0 || extensionId.length > 256) return;
|
|
960
|
-
if (typeof p.seq !== "number" || !Number.isFinite(p.seq)) return;
|
|
961
|
-
const sessionId = typeof p.sessionId === "string" ? p.sessionId : void 0;
|
|
962
|
-
const bytes = serializedPayloadBytes(p.payload);
|
|
963
|
-
if (bytes === null || bytes > 65536) return;
|
|
964
|
-
let registry = env.config.extensions;
|
|
965
|
-
if (sessionId !== void 0) try {
|
|
966
|
-
const data = await loadOrThrow(sessionId);
|
|
967
|
-
const meta = data.metadata ?? {};
|
|
968
|
-
const cwd = typeof meta.cwd === "string" ? meta.cwd : data.projectRoot ?? "";
|
|
969
|
-
registry = (await env.runtimeConfig(cwd)).extensions;
|
|
970
|
-
} catch {
|
|
971
|
-
return;
|
|
972
|
-
}
|
|
973
|
-
if (!registry.loaded.some((ext) => !ext.error && ext.name === extensionId && ext.definition?.permissions?.includes("ipc") === true)) return;
|
|
974
|
-
const nonce = typeof p.nonce === "string" ? p.nonce : void 0;
|
|
975
|
-
const envelope = {
|
|
976
|
-
extensionId,
|
|
977
|
-
...sessionId !== void 0 ? { sessionId } : {},
|
|
978
|
-
payload: p.payload,
|
|
979
|
-
seq: p.seq,
|
|
980
|
-
...nonce !== void 0 ? { nonce } : {}
|
|
981
|
-
};
|
|
982
|
-
env.extensionBus?.deliver(envelope);
|
|
983
|
-
broadcastExcept(peer, {
|
|
984
|
-
kind: "extension:signal",
|
|
985
|
-
...envelope
|
|
986
|
-
});
|
|
987
|
-
},
|
|
988
|
-
"suggest.resolve": async (p, peer) => {
|
|
989
|
-
const sessionId = String(p.sessionId);
|
|
990
|
-
const seq = typeof p.seq === "number" ? p.seq : 0;
|
|
991
|
-
const text = typeof p.text === "string" ? p.text.slice(0, 64 * 1024) : "";
|
|
992
|
-
const caret = typeof p.caret === "number" ? Math.max(0, Math.min(Math.floor(p.caret), text.length)) : text.length;
|
|
993
|
-
const prev = suggests.get(sessionId);
|
|
994
|
-
if (prev && seq < prev.seq) return {
|
|
995
|
-
suggestion: null,
|
|
996
|
-
seq
|
|
997
|
-
};
|
|
998
|
-
if (!prev && suggests.size >= 256) return {
|
|
999
|
-
suggestion: null,
|
|
1000
|
-
seq
|
|
1001
|
-
};
|
|
1002
|
-
prev?.controller?.abort();
|
|
1003
|
-
const controller = new AbortController();
|
|
1004
|
-
const entry = {
|
|
1005
|
-
seq,
|
|
1006
|
-
controller
|
|
1007
|
-
};
|
|
1008
|
-
suggests.set(sessionId, entry);
|
|
1009
|
-
try {
|
|
1010
|
-
const data = await loadOrThrow(sessionId);
|
|
1011
|
-
const meta = data.metadata ?? {};
|
|
1012
|
-
const cwd = typeof meta.cwd === "string" ? meta.cwd : data.projectRoot ?? "";
|
|
1013
|
-
const config = await env.runtimeConfig(cwd);
|
|
1014
|
-
const result = await resolveInlineSuggestion([createPathAutocompleteProvider({ cwd: () => cwd }), ...config.extensions.inlineAutocompleteProviders], {
|
|
1015
|
-
text,
|
|
1016
|
-
cursor: caret,
|
|
1017
|
-
cwd,
|
|
1018
|
-
signal: controller.signal
|
|
1019
|
-
});
|
|
1020
|
-
if (controller.signal.aborted || suggests.get(sessionId) !== entry) return {
|
|
1021
|
-
suggestion: null,
|
|
1022
|
-
seq
|
|
1023
|
-
};
|
|
1024
|
-
const candidates = result ? [...result.suffixes] : [];
|
|
1025
|
-
const suggestion = candidates[0] ?? null;
|
|
1026
|
-
broadcastExcept(peer, {
|
|
1027
|
-
kind: "suggest:changed",
|
|
1028
|
-
sessionId,
|
|
1029
|
-
seq,
|
|
1030
|
-
basis: text,
|
|
1031
|
-
caret,
|
|
1032
|
-
suggestion,
|
|
1033
|
-
...candidates.length > 0 ? { candidates } : {}
|
|
1034
|
-
});
|
|
1035
|
-
return {
|
|
1036
|
-
suggestion,
|
|
1037
|
-
seq,
|
|
1038
|
-
...candidates.length > 0 ? { candidates } : {}
|
|
1039
|
-
};
|
|
1040
|
-
} finally {
|
|
1041
|
-
if (suggests.get(sessionId) === entry) entry.controller = null;
|
|
1042
|
-
}
|
|
1043
|
-
},
|
|
1044
|
-
"drafts.setAttachments": (p, peer) => {
|
|
1045
|
-
const bounded = Array.isArray(p.attachments) ? boundAttachments(p.attachments) : [];
|
|
1046
|
-
if (!drafts.has(p.sessionId) && drafts.size >= 256) return;
|
|
1047
|
-
const currentBytes = (drafts.get(p.sessionId)?.attachments ?? []).reduce((sum, a) => sum + a.data.length, 0);
|
|
1048
|
-
const incomingBytes = bounded.reduce((sum, a) => sum + a.data.length, 0);
|
|
1049
|
-
if (draftAttachmentBytes() - currentBytes + incomingBytes > 96 * 1024 * 1024) return;
|
|
1050
|
-
const entry = drafts.get(p.sessionId) ?? {};
|
|
1051
|
-
entry.attachments = bounded.length > 0 ? bounded : void 0;
|
|
1052
|
-
compactDraft(p.sessionId, entry);
|
|
1053
|
-
broadcastExcept(peer, {
|
|
1054
|
-
kind: "draft:attachments",
|
|
1055
|
-
sessionId: p.sessionId,
|
|
1056
|
-
attachments: bounded
|
|
1057
|
-
});
|
|
1058
|
-
},
|
|
1059
|
-
"chat.isRunning": (p) => host.isSessionRunning(p.sessionId),
|
|
1060
|
-
"chat.rebuildAgent": (p) => host.disposeAgentForRebuild(p.sessionId),
|
|
1061
|
-
"runtime.reload": () => {
|
|
1062
|
-
env.invalidateRuntime();
|
|
1063
|
-
host.rebuildAllAgents();
|
|
1064
|
-
},
|
|
1065
|
-
"chat.listWork": (p) => host.listRunningWork(p.sessionId),
|
|
1066
|
-
"chat.cancelWork": (p) => host.cancelRunningWork(p.sessionId, p.id, p.reason),
|
|
1067
|
-
"sessions.list": async (p) => {
|
|
1068
|
-
const filter = p?.projectRoot !== void 0 ? { projectRoot: p.projectRoot } : void 0;
|
|
1069
|
-
const applyArchive = (rows) => {
|
|
1070
|
-
const filtered = rows.filter((row) => {
|
|
1071
|
-
if (p?.archive === "active") return !row.archived;
|
|
1072
|
-
if (p?.archive === "archived") return row.archived;
|
|
1073
|
-
return true;
|
|
1074
|
-
});
|
|
1075
|
-
filtered.sort((a, b) => b.updatedAt - a.updatedAt);
|
|
1076
|
-
return filtered;
|
|
1077
|
-
};
|
|
1078
|
-
if (store.listMeta) return applyArchive((await store.listMeta(filter)).map((meta) => {
|
|
1079
|
-
const md = meta.metadata;
|
|
1080
|
-
const title = deriveSessionTitle(meta.firstUserTurn ? [meta.firstUserTurn] : [], md);
|
|
1081
|
-
return {
|
|
1082
|
-
id: meta.id,
|
|
1083
|
-
title,
|
|
1084
|
-
projectRoot: meta.projectRoot ?? null,
|
|
1085
|
-
cwd: typeof md.cwd === "string" ? md.cwd : meta.projectRoot ?? "",
|
|
1086
|
-
...meta.agentId ? { agentId: meta.agentId } : {},
|
|
1087
|
-
status: host.isSessionRunning(meta.id) ? "running" : meta.status === "running" && Date.now() - meta.updatedAt >= 3e5 ? "idle" : meta.status ?? "idle",
|
|
1088
|
-
createdAt: meta.createdAt ?? 0,
|
|
1089
|
-
updatedAt: meta.updatedAt,
|
|
1090
|
-
archived: md.archived === true,
|
|
1091
|
-
...typeof md.providerKey === "string" ? { providerKey: md.providerKey } : {},
|
|
1092
|
-
...typeof md.modelId === "string" ? { modelId: md.modelId } : {},
|
|
1093
|
-
...typeof md.effort === "string" ? { effort: md.effort } : {},
|
|
1094
|
-
...md.modelOptions && typeof md.modelOptions === "object" ? { modelOptions: md.modelOptions } : {},
|
|
1095
|
-
...(() => {
|
|
1096
|
-
const fallbacks = sessionModelFallbacks(md);
|
|
1097
|
-
return fallbacks ? { modelFallbacks: fallbacks } : {};
|
|
1098
|
-
})()
|
|
1099
|
-
};
|
|
1100
|
-
}));
|
|
1101
|
-
const ids = await store.list(filter);
|
|
1102
|
-
const rows = [];
|
|
1103
|
-
for (const id of ids) {
|
|
1104
|
-
const data = await store.load(id);
|
|
1105
|
-
if (!data) continue;
|
|
1106
|
-
rows.push(toRow(data));
|
|
1107
|
-
}
|
|
1108
|
-
return applyArchive(rows);
|
|
1109
|
-
},
|
|
1110
|
-
"sessions.create": (p) => createSessionRow(p),
|
|
1111
|
-
"sessions.delete": async (p) => {
|
|
1112
|
-
drafts.delete(p.sessionId);
|
|
1113
|
-
suggests.get(p.sessionId)?.controller?.abort();
|
|
1114
|
-
suggests.delete(p.sessionId);
|
|
1115
|
-
host.disposeSession(p.sessionId);
|
|
1116
|
-
await store.delete(p.sessionId);
|
|
1117
|
-
broadcast({
|
|
1118
|
-
kind: "sessions:changed",
|
|
1119
|
-
op: "delete",
|
|
1120
|
-
sessionId: p.sessionId
|
|
1121
|
-
});
|
|
1122
|
-
},
|
|
1123
|
-
"sessions.setTitle": (p) => patchSessionMeta(p.sessionId, { title: p.title }, { bumpUpdatedAt: false }),
|
|
1124
|
-
"sessions.setArchived": (p) => patchSessionMeta(p.sessionId, { archived: p.archived }),
|
|
1125
|
-
"sessions.setAgentId": async (p) => {
|
|
1126
|
-
if (await host.setLiveSessionAgentId(p.sessionId, p.agentId)) broadcast({
|
|
1127
|
-
kind: "sessions:changed",
|
|
1128
|
-
op: "update",
|
|
1129
|
-
sessionId: p.sessionId
|
|
1130
|
-
});
|
|
1131
|
-
else await patchSession(p.sessionId, (d) => {
|
|
1132
|
-
d.agentId = p.agentId;
|
|
1133
|
-
});
|
|
1134
|
-
host.disposeAgentForRebuild(p.sessionId);
|
|
1135
|
-
},
|
|
1136
|
-
"sessions.setEffort": (p) => patchSessionMeta(p.sessionId, { effort: p.effort }),
|
|
1137
|
-
"sessions.setModel": async (p) => {
|
|
1138
|
-
await host.setSessionModel(p.sessionId, {
|
|
1139
|
-
providerKey: p.providerKey,
|
|
1140
|
-
modelId: p.modelId,
|
|
1141
|
-
...p.fallbacks ? { fallbacks: p.fallbacks } : {},
|
|
1142
|
-
...p.effort ? { effort: p.effort } : {},
|
|
1143
|
-
...p.modelOptions ? { modelOptions: p.modelOptions } : {}
|
|
1144
|
-
});
|
|
1145
|
-
broadcast({
|
|
1146
|
-
kind: "sessions:changed",
|
|
1147
|
-
op: "update",
|
|
1148
|
-
sessionId: p.sessionId
|
|
1149
|
-
});
|
|
1150
|
-
},
|
|
1151
|
-
"sessions.setModelOptions": (p) => patchSessionMeta(p.sessionId, { modelOptions: p.options }),
|
|
1152
|
-
"sessions.loadEvents": async (p) => {
|
|
1153
|
-
const data = await loadOrThrow(p.sessionId);
|
|
1154
|
-
return eventsFromTurns(data.turns, data.runs);
|
|
1155
|
-
},
|
|
1156
|
-
"sessions.getTurn": async (p) => {
|
|
1157
|
-
return (await store.load(p.sessionId))?.turns.find((t) => t.id === p.turnId) ?? null;
|
|
1158
|
-
},
|
|
1159
|
-
"sessions.contextBreakdown": (p) => host.getContextBreakdown(p.sessionId),
|
|
1160
|
-
"sessions.artifacts": async (p) => {
|
|
1161
|
-
if (!store.listArtifacts) return [];
|
|
1162
|
-
return store.listArtifacts({
|
|
1163
|
-
sessionId: p.sessionId,
|
|
1164
|
-
...typeof p.kind === "string" ? { kind: p.kind } : {},
|
|
1165
|
-
...boundedLimit(p.limit) !== void 0 ? { limit: boundedLimit(p.limit) } : {}
|
|
1166
|
-
});
|
|
1167
|
-
},
|
|
1168
|
-
"artifacts.list": async (p) => {
|
|
1169
|
-
if (!store.listArtifacts) return [];
|
|
1170
|
-
return store.listArtifacts({
|
|
1171
|
-
...p.projectRoot === null || typeof p.projectRoot === "string" ? { projectRoot: p.projectRoot } : {},
|
|
1172
|
-
...typeof p.kind === "string" ? { kind: p.kind } : {},
|
|
1173
|
-
...typeof p.uri === "string" ? { uri: p.uri } : {},
|
|
1174
|
-
...typeof p.since === "number" && Number.isFinite(p.since) ? { since: p.since } : {},
|
|
1175
|
-
...boundedLimit(p.limit) !== void 0 ? { limit: boundedLimit(p.limit) } : {}
|
|
1176
|
-
});
|
|
1177
|
-
},
|
|
1178
|
-
"artifacts.delete": async (p) => {
|
|
1179
|
-
if (!store.deleteArtifact) return false;
|
|
1180
|
-
await store.deleteArtifact(p.sessionId, p.artifactId);
|
|
1181
|
-
broadcast({
|
|
1182
|
-
kind: "artifact:changed",
|
|
1183
|
-
sessionId: p.sessionId,
|
|
1184
|
-
op: "delete",
|
|
1185
|
-
artifactId: p.artifactId
|
|
1186
|
-
});
|
|
1187
|
-
return true;
|
|
1188
|
-
},
|
|
1189
|
-
"artifacts.upsert": async (p) => {
|
|
1190
|
-
if (!store.upsertArtifact) return null;
|
|
1191
|
-
const normalized = normalizeArtifactInput(p.artifact);
|
|
1192
|
-
if (!normalized || typeof p.artifact?.sessionId !== "string" || typeof p.artifact?.id !== "string") return null;
|
|
1193
|
-
const now = Date.now();
|
|
1194
|
-
const stored = await store.upsertArtifact({
|
|
1195
|
-
...normalized,
|
|
1196
|
-
id: p.artifact.id,
|
|
1197
|
-
sessionId: p.artifact.sessionId,
|
|
1198
|
-
...typeof p.artifact.runId === "string" ? { runId: p.artifact.runId } : {},
|
|
1199
|
-
...typeof p.artifact.turnId === "string" ? { turnId: p.artifact.turnId } : {},
|
|
1200
|
-
createdAt: typeof p.artifact.createdAt === "number" ? p.artifact.createdAt : now,
|
|
1201
|
-
updatedAt: now
|
|
1202
|
-
});
|
|
1203
|
-
if (stored) broadcast({
|
|
1204
|
-
kind: "artifact:changed",
|
|
1205
|
-
sessionId: stored.sessionId,
|
|
1206
|
-
op: "upsert",
|
|
1207
|
-
artifactId: stored.id,
|
|
1208
|
-
artifact: stored
|
|
1209
|
-
});
|
|
1210
|
-
return stored;
|
|
1211
|
-
},
|
|
1212
|
-
"history.searchSession": async (p) => {
|
|
1213
|
-
const index = await sessionSearchIndex(p.sessionId);
|
|
1214
|
-
if (!index) return [];
|
|
1215
|
-
const limit = Number.isInteger(p.limit) && p.limit > 0 ? Math.min(p.limit, index.turns.length) : index.turns.length;
|
|
1216
|
-
const matches = await searchSessionIndexAsync(index, p.query, { limit });
|
|
1217
|
-
const order = new Map(index.turns.map((entry, i) => [entry.turn.id, i]));
|
|
1218
|
-
matches.sort((a, b) => (order.get(a.turn.id) ?? 0) - (order.get(b.turn.id) ?? 0));
|
|
1219
|
-
return matches;
|
|
1220
|
-
},
|
|
1221
|
-
"history.searchAll": async (p, peer) => {
|
|
1222
|
-
const generation = (searchGenerations.get(peer) ?? 0) + 1;
|
|
1223
|
-
searchGenerations.set(peer, generation);
|
|
1224
|
-
const results = {};
|
|
1225
|
-
const ids = Array.isArray(p.sessionIds) ? p.sessionIds.slice(0, 500) : [];
|
|
1226
|
-
for (const sessionId of ids) {
|
|
1227
|
-
if (searchGenerations.get(peer) !== generation) break;
|
|
1228
|
-
try {
|
|
1229
|
-
const index = await sessionSearchIndex(sessionId);
|
|
1230
|
-
if (!index) continue;
|
|
1231
|
-
const match = await searchSessionHistoryIndexAsync(index, p.query, p.excerptLimit ?? 3);
|
|
1232
|
-
if (match) results[sessionId] = match;
|
|
1233
|
-
} catch {}
|
|
1234
|
-
}
|
|
1235
|
-
return results;
|
|
1236
|
-
},
|
|
1237
|
-
"sessions.touch": (p) => {
|
|
1238
|
-
broadcast({
|
|
1239
|
-
kind: "sessions:changed",
|
|
1240
|
-
op: "update",
|
|
1241
|
-
sessionId: p.sessionId
|
|
1242
|
-
});
|
|
1243
|
-
},
|
|
1244
|
-
"sessions.compact": (p) => host.compactSessionNow(p.sessionId),
|
|
1245
|
-
"todos.get": (p) => {
|
|
1246
|
-
const state = host.getTodosSnapshot(p.sessionId);
|
|
1247
|
-
return state ? toTodosSnapshot(state) : null;
|
|
1248
|
-
},
|
|
1249
|
-
"todos.set": async (p) => {
|
|
1250
|
-
const state = await host.setTodos(p.sessionId, p.items);
|
|
1251
|
-
return state ? toTodosSnapshot(state) : null;
|
|
1252
|
-
},
|
|
1253
|
-
"orchestration.main": () => host.orchestration.ensureMain(),
|
|
1254
|
-
"orchestration.createWorker": ({ mainId, ...params }) => host.orchestration.createWorker(mainId, params),
|
|
1255
|
-
"orchestration.list": (p) => host.orchestration.list(p.mainId, p.limit),
|
|
1256
|
-
"orchestration.read": async (p) => await host.orchestration.canOrchestrate(p.mainId, p.sessionId) ? host.orchestration.read(p.sessionId) : null,
|
|
1257
|
-
"orchestration.send": (p) => host.orchestration.send(p.mainId, p.sessionId, p.prompt, p.wake),
|
|
1258
|
-
"orchestration.steer": (p) => host.orchestration.steer(p.mainId, p.sessionId, p.message),
|
|
1259
|
-
"orchestration.cancel": (p) => host.orchestration.cancel(p.mainId, p.sessionId),
|
|
1260
|
-
"orchestration.transcript": (p) => host.orchestration.transcript(p.mainId, p.sessionId, p.limit),
|
|
1261
|
-
"interactions.list": (p) => host.interactions.list(p?.sessionId),
|
|
1262
|
-
"interactions.respond": (p) => host.interactions.respond(p.requestId, p.response),
|
|
1263
|
-
"interactions.cancel": (p) => host.interactions.cancel(p.requestId, p.reason),
|
|
1264
|
-
"interactions.rehydrate": (p) => host.interactions.rehydrate(p.sessionId),
|
|
1265
|
-
"approvals.resolve": (p) => host.approvals.resolve(p.requestId, p.decision),
|
|
1266
|
-
"approvals.denyAll": (p) => host.approvals.denyAll(p?.sessionId),
|
|
1267
|
-
"approvals.listPending": () => host.approvals.listPending(),
|
|
1268
|
-
"settings.get": () => env.readSettings(),
|
|
1269
|
-
"settings.set": (p) => {
|
|
1270
|
-
const before = env.readSettings();
|
|
1271
|
-
const next = env.writeSettings(p.patch);
|
|
1272
|
-
const changed = (key) => JSON.stringify(before[key]) !== JSON.stringify(next[key]);
|
|
1273
|
-
if (changed("enabledExtensions") || changed("loadProjectExtensions")) {
|
|
1274
|
-
env.invalidateRuntime();
|
|
1275
|
-
host.shutdown();
|
|
1276
|
-
} else if (changed("enabledMcps") || changed("disabledMcpTools") || changed("enabledSkills") || changed("enabledPlugins") || changed("importForeignConfigs") || changed("importRulerConfigs")) host.shutdown();
|
|
1277
|
-
if (changed("interactionMode") && next.interactionMode === "skip") host.interactions.resolveAutomatically();
|
|
1278
|
-
broadcast({
|
|
1279
|
-
kind: "settings:changed",
|
|
1280
|
-
settings: next
|
|
1281
|
-
});
|
|
1282
|
-
return next;
|
|
1283
|
-
},
|
|
1284
|
-
"presence.list": () => computePresence(),
|
|
1285
|
-
"presence.watch": (p, peer) => {
|
|
1286
|
-
peer.watching = p.sessionId;
|
|
1287
|
-
broadcastPresence();
|
|
1288
|
-
store.load(p.sessionId).then((data) => {
|
|
1289
|
-
if (!data) return;
|
|
1290
|
-
const meta = data.metadata ?? {};
|
|
1291
|
-
const cwd = typeof meta.cwd === "string" ? meta.cwd : data.projectRoot ?? "";
|
|
1292
|
-
return env.runtimeConfig(cwd);
|
|
1293
|
-
}).catch(() => void 0);
|
|
1294
|
-
},
|
|
1295
|
-
"daemon.info": () => ({
|
|
1296
|
-
protocolVersion: 9,
|
|
1297
|
-
appVersion,
|
|
1298
|
-
pid: process.pid,
|
|
1299
|
-
startedAt
|
|
1300
|
-
}),
|
|
1301
|
-
"daemon.shutdown": async () => {
|
|
1302
|
-
setImmediate(() => {
|
|
1303
|
-
close().then(() => options.onIdle?.());
|
|
1304
|
-
});
|
|
1305
|
-
}
|
|
1306
|
-
};
|
|
1307
|
-
let idleTimer;
|
|
1308
|
-
function armIdleTimer() {
|
|
1309
|
-
if (!options.idleShutdownMs || !options.onIdle) return;
|
|
1310
|
-
clearTimeout(idleTimer);
|
|
1311
|
-
idleTimer = setTimeout(() => {
|
|
1312
|
-
if (peers.size > 0) return;
|
|
1313
|
-
if (host.hasActiveWork()) {
|
|
1314
|
-
armIdleTimer();
|
|
1315
|
-
return;
|
|
1316
|
-
}
|
|
1317
|
-
options.onIdle?.();
|
|
1318
|
-
}, options.idleShutdownMs);
|
|
1319
|
-
}
|
|
1320
|
-
function handleConnection(socket) {
|
|
1321
|
-
const peer = {
|
|
1322
|
-
socket,
|
|
1323
|
-
connection: void 0,
|
|
1324
|
-
ready: false,
|
|
1325
|
-
watching: null
|
|
1326
|
-
};
|
|
1327
|
-
const connection = createJsonRpcConnection({
|
|
1328
|
-
input: socket,
|
|
1329
|
-
output: socket,
|
|
1330
|
-
onRequest: async (message) => {
|
|
1331
|
-
if (message.method === "ipc.hello") {
|
|
1332
|
-
const params = message.params ?? {};
|
|
1333
|
-
const clientBuild = Number(params.buildId);
|
|
1334
|
-
const daemonBuild = Number(IPC_BUILD_ID);
|
|
1335
|
-
const staleBuild = params.buildId !== void 0 && params.buildId !== "unknown" && IPC_BUILD_ID !== "unknown" && Number.isFinite(clientBuild) && Number.isFinite(daemonBuild) && clientBuild > daemonBuild;
|
|
1336
|
-
const staleRelease = typeof params.codeVersion === "string" && compareVersions(params.codeVersion, ZIDANE_VERSION) > 0;
|
|
1337
|
-
if (params.protocolVersion !== 9 || staleBuild || staleRelease) return errorResponse(message.id, IpcErrorCode.VersionMismatch, staleRelease ? `Stale daemon: it runs zidane ${ZIDANE_VERSION} but the client runs ${params.codeVersion}. Draining for respawn.` : staleBuild ? `Build mismatch: the daemon was started from an older build (daemon ${IPC_BUILD_ID}, client ${params.buildId}). Draining for respawn.` : `Protocol version mismatch: daemon speaks v9, client sent v${params.protocolVersion ?? "?"}.`, { daemon: {
|
|
1338
|
-
protocolVersion: 9,
|
|
1339
|
-
appVersion,
|
|
1340
|
-
pid: process.pid,
|
|
1341
|
-
startedAt,
|
|
1342
|
-
buildId: IPC_BUILD_ID,
|
|
1343
|
-
codeVersion: ZIDANE_VERSION
|
|
1344
|
-
} });
|
|
1345
|
-
peer.ready = true;
|
|
1346
|
-
const result = {
|
|
1347
|
-
protocolVersion: 9,
|
|
1348
|
-
appVersion,
|
|
1349
|
-
pid: process.pid,
|
|
1350
|
-
startedAt,
|
|
1351
|
-
buildId: IPC_BUILD_ID,
|
|
1352
|
-
codeVersion: ZIDANE_VERSION
|
|
1353
|
-
};
|
|
1354
|
-
return successResponse(message.id, result);
|
|
1355
|
-
}
|
|
1356
|
-
if (!peer.ready && message.method !== "daemon.shutdown" && message.method !== "daemon.info") return errorResponse(message.id, IpcErrorCode.NotHandshaken, "Handshake required: send ipc.hello first.");
|
|
1357
|
-
const handler = methods[message.method];
|
|
1358
|
-
if (!handler) return errorResponse(message.id, -32601, `Unknown method "${message.method}".`);
|
|
1359
|
-
try {
|
|
1360
|
-
const result = await handler(message.params, peer);
|
|
1361
|
-
return successResponse(message.id, result ?? null);
|
|
1362
|
-
} catch (err) {
|
|
1363
|
-
return errorResponse(message.id, -32e3, err instanceof Error ? err.message : String(err));
|
|
1364
|
-
}
|
|
1365
|
-
},
|
|
1366
|
-
onNotification: async (message) => {
|
|
1367
|
-
if (!peer.ready) return;
|
|
1368
|
-
const handler = methods[message.method];
|
|
1369
|
-
if (!handler) return;
|
|
1370
|
-
try {
|
|
1371
|
-
await handler(message.params, peer);
|
|
1372
|
-
} catch {}
|
|
1373
|
-
},
|
|
1374
|
-
onError: (err) => log(`[ipc-daemon] peer error: ${err.message}`)
|
|
1375
|
-
});
|
|
1376
|
-
peer.connection = connection;
|
|
1377
|
-
peers.add(peer);
|
|
1378
|
-
connection.start();
|
|
1379
|
-
log(`[ipc-daemon] peer connected (${peers.size} total)`);
|
|
1380
|
-
const drop = () => {
|
|
1381
|
-
if (!peers.delete(peer)) return;
|
|
1382
|
-
connection.close();
|
|
1383
|
-
log(`[ipc-daemon] peer disconnected (${peers.size} left)`);
|
|
1384
|
-
if (peer.watching) broadcastPresence();
|
|
1385
|
-
if (peers.size === 0) armIdleTimer();
|
|
1386
|
-
};
|
|
1387
|
-
socket.on("close", drop);
|
|
1388
|
-
socket.on("error", drop);
|
|
1389
|
-
}
|
|
1390
|
-
if (existsSync(socketPath)) {
|
|
1391
|
-
if (await probeSocket(socketPath)) throw new Error(`A zidane daemon is already listening on ${socketPath}.`);
|
|
1392
|
-
if (await probeSocket(socketPath)) throw new Error(`A zidane daemon is already listening on ${socketPath}.`);
|
|
1393
|
-
unlinkSync(socketPath);
|
|
1394
|
-
}
|
|
1395
|
-
const socketDir = dirname(socketPath);
|
|
1396
|
-
const dedicatedSocketDir = basename(socketDir) === "daemon" || !existsSync(socketDir);
|
|
1397
|
-
mkdirSync(socketDir, {
|
|
1398
|
-
recursive: true,
|
|
1399
|
-
mode: 448
|
|
1400
|
-
});
|
|
1401
|
-
if (dedicatedSocketDir) try {
|
|
1402
|
-
chmodSync(socketDir, 448);
|
|
1403
|
-
} catch {}
|
|
1404
|
-
const server = createServer(handleConnection);
|
|
1405
|
-
await new Promise((resolvePromise, reject) => {
|
|
1406
|
-
server.once("error", reject);
|
|
1407
|
-
server.listen(socketPath, () => {
|
|
1408
|
-
server.off("error", reject);
|
|
1409
|
-
resolvePromise();
|
|
1410
|
-
});
|
|
1411
|
-
});
|
|
1412
|
-
try {
|
|
1413
|
-
chmodSync(socketPath, 384);
|
|
1414
|
-
} catch {}
|
|
1415
|
-
log(`[ipc-daemon] listening on ${socketPath}`);
|
|
1416
|
-
let ownsSocketFile = true;
|
|
1417
|
-
armIdleTimer();
|
|
1418
|
-
const legacyPath = legacySocketPath(socketPath);
|
|
1419
|
-
if (legacyPath) (async () => {
|
|
1420
|
-
if (!await probeSocket(legacyPath)) return;
|
|
1421
|
-
log(`[ipc-daemon] draining legacy daemon on ${legacyPath}`);
|
|
1422
|
-
try {
|
|
1423
|
-
const survivor = await connectIpcClient({
|
|
1424
|
-
socketPath: legacyPath,
|
|
1425
|
-
appVersion: `${appVersion}/legacy-drain`,
|
|
1426
|
-
drainOnMismatch: true
|
|
1427
|
-
});
|
|
1428
|
-
await survivor.request("daemon.shutdown", {}).catch(() => void 0);
|
|
1429
|
-
survivor.close();
|
|
1430
|
-
} catch {}
|
|
1431
|
-
})();
|
|
1432
|
-
async function doClose() {
|
|
1433
|
-
clearTimeout(idleTimer);
|
|
1434
|
-
await host.drain();
|
|
1435
|
-
for (const peer of peers) {
|
|
1436
|
-
peer.connection.close();
|
|
1437
|
-
peer.socket.destroy();
|
|
1438
|
-
}
|
|
1439
|
-
peers.clear();
|
|
1440
|
-
await new Promise((resolvePromise) => {
|
|
1441
|
-
server.close(() => resolvePromise());
|
|
1442
|
-
});
|
|
1443
|
-
if (ownsSocketFile) {
|
|
1444
|
-
ownsSocketFile = false;
|
|
1445
|
-
try {
|
|
1446
|
-
unlinkSync(socketPath);
|
|
1447
|
-
} catch {}
|
|
1448
|
-
}
|
|
1449
|
-
}
|
|
1450
|
-
let closePromise = null;
|
|
1451
|
-
function close() {
|
|
1452
|
-
closePromise ??= doClose();
|
|
1453
|
-
return closePromise;
|
|
1454
|
-
}
|
|
1455
|
-
return {
|
|
1456
|
-
socketPath,
|
|
1457
|
-
host,
|
|
1458
|
-
peerCount: () => peers.size,
|
|
1459
|
-
close
|
|
1460
|
-
};
|
|
1461
|
-
}
|
|
1462
|
-
//#endregion
|
|
1463
|
-
//#region src/ipc/daemon/shell-path.ts
|
|
1464
|
-
/**
|
|
1465
|
-
* Login-shell PATH for processes not started from a terminal.
|
|
1466
|
-
*
|
|
1467
|
-
* A Dock/Finder-launched GUI, or a daemon it spawns, inherits launchd's PATH
|
|
1468
|
-
* (`/usr/bin:/bin:/usr/sbin:/sbin`), so `node` / `bun` / `yarn` are invisible
|
|
1469
|
-
* to agent shell commands. Ask the user's login shell for its PATH and merge
|
|
1470
|
-
* it into `process.env.PATH`.
|
|
1471
|
-
*
|
|
1472
|
-
* Merge, don't replace: a terminal launch may have prepended entries the rc
|
|
1473
|
-
* files know nothing about (`nvm use`, direnv, `PATH=... zidane`), and those
|
|
1474
|
-
* must survive. Idempotent, so every surface (GUI, daemon) can call it and a
|
|
1475
|
-
* daemon spawned by an already-fixed GUI ends up with the same PATH.
|
|
1476
|
-
*/
|
|
1477
|
-
const DELIMITER = "_ZIDANE_SHELL_ENV_";
|
|
1478
|
-
/** A slow rc file costs startup time; a prompting one must never hang the process. */
|
|
1479
|
-
const DEFAULT_TIMEOUT_MS = 3e3;
|
|
1480
|
-
function defaultShell(env) {
|
|
1481
|
-
try {
|
|
1482
|
-
const { shell } = userInfo();
|
|
1483
|
-
if (shell) return shell;
|
|
1484
|
-
} catch {}
|
|
1485
|
-
return env.SHELL || (process$1.platform === "darwin" ? "/bin/zsh" : "/bin/sh");
|
|
1486
|
-
}
|
|
1487
|
-
function parsePath(stdout) {
|
|
1488
|
-
const body = stdout.split(DELIMITER)[1];
|
|
1489
|
-
if (body === void 0) return void 0;
|
|
1490
|
-
const clean = body.replace(/\u001B\[[0-9;]*[A-Z]/gi, "");
|
|
1491
|
-
for (const line of clean.split("\n")) if (line.startsWith("PATH=")) return line.slice(5) || void 0;
|
|
1492
|
-
}
|
|
1493
|
-
/**
|
|
1494
|
-
* PATH as seen by an interactive login shell, or `undefined` when no shell
|
|
1495
|
-
* answers in time. Tries the user's shell first, then POSIX fallbacks for
|
|
1496
|
-
* non-POSIX defaults (nushell, xonsh) whose `-ilc` differs. An explicit
|
|
1497
|
-
* `shell` is tried alone.
|
|
1498
|
-
*/
|
|
1499
|
-
function shellPath(options = {}) {
|
|
1500
|
-
if (process$1.platform === "win32") return void 0;
|
|
1501
|
-
const env = options.env ?? process$1.env;
|
|
1502
|
-
const timeout = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
1503
|
-
const shells = options.shell ? [options.shell] : [.../* @__PURE__ */ new Set([
|
|
1504
|
-
defaultShell(env),
|
|
1505
|
-
"/bin/zsh",
|
|
1506
|
-
"/bin/bash"
|
|
1507
|
-
])];
|
|
1508
|
-
for (const shell of shells) {
|
|
1509
|
-
const result = spawnSync(shell, ["-ilc", `echo -n "${DELIMITER}"; command env; echo -n "${DELIMITER}"; exit`], {
|
|
1510
|
-
encoding: "utf8",
|
|
1511
|
-
timeout,
|
|
1512
|
-
stdio: [
|
|
1513
|
-
"ignore",
|
|
1514
|
-
"pipe",
|
|
1515
|
-
"ignore"
|
|
1516
|
-
],
|
|
1517
|
-
env: {
|
|
1518
|
-
...env,
|
|
1519
|
-
DISABLE_AUTO_UPDATE: "true",
|
|
1520
|
-
ZSH_TMUX_AUTOSTARTED: "true",
|
|
1521
|
-
ZSH_TMUX_AUTOSTART: "false"
|
|
1522
|
-
}
|
|
1523
|
-
});
|
|
1524
|
-
if (result.error || result.status !== 0) continue;
|
|
1525
|
-
const path = parsePath(result.stdout);
|
|
1526
|
-
if (path) return path;
|
|
1527
|
-
}
|
|
1528
|
-
}
|
|
1529
|
-
/**
|
|
1530
|
-
* Entries of `current` the shell doesn't know about (ad-hoc prepends from the
|
|
1531
|
-
* launching terminal) stay in front; then the shell's PATH in its own order.
|
|
1532
|
-
* From the Dock every launchd entry is already in the shell PATH, so the
|
|
1533
|
-
* result is exactly the shell PATH, with the shell's precedence intact.
|
|
1534
|
-
*/
|
|
1535
|
-
function mergePath(current, shell) {
|
|
1536
|
-
const split = (path) => (path ?? "").split(delimiter).filter(Boolean);
|
|
1537
|
-
const fromShell = split(shell);
|
|
1538
|
-
const known = new Set(fromShell);
|
|
1539
|
-
return [.../* @__PURE__ */ new Set([...split(current).filter((entry) => !known.has(entry)), ...fromShell])].join(delimiter);
|
|
1540
|
-
}
|
|
1541
|
-
/** Merge the login shell's PATH into `process.env.PATH`. No-op on Windows or when no shell answers. */
|
|
1542
|
-
function fixPath() {
|
|
1543
|
-
const extra = shellPath();
|
|
1544
|
-
if (extra) process$1.env.PATH = mergePath(process$1.env.PATH, extra);
|
|
1545
|
-
}
|
|
1546
|
-
//#endregion
|
|
1547
|
-
export { selfDaemonSpawnCommand as _, createDaemonEnvironment as a, connectIpcClient as c, IpcErrorCode as d, IPC_BUILD_ID as f, probeSocket as g, ensureDaemonRunning as h, startIpcDaemon as i, connectOrSpawnDaemon as l, defaultSocketPath as m, mergePath as n, createFileApprovalsStore as o, daemonSocketName as p, shellPath as r, IpcVersionMismatchError as s, fixPath as t, IPC_PROTOCOL_VERSION as u };
|
|
1548
|
-
|
|
1549
|
-
//# sourceMappingURL=shell-path-DfdXrGg9.js.map
|