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
package/docs/MACHINES.md
ADDED
|
@@ -0,0 +1,456 @@
|
|
|
1
|
+
# Machines
|
|
2
|
+
|
|
3
|
+
A **machine** lends execution (files, shell, background tasks, terminals,
|
|
4
|
+
live processes) to a **host** over a WebSocket it opens itself, or, for a
|
|
5
|
+
local host that can't be dialed, over the host's own client connection
|
|
6
|
+
(the reverse direction). As built: the protocol, the host registry, the
|
|
7
|
+
remote execution context, the machine agent and the offline semantics
|
|
8
|
+
(slice 4A), then the reverse direction, `execStream`, project files read
|
|
9
|
+
on the machine, the machine's own sandbox, and the TUI / desktop app
|
|
10
|
+
surfaces (slice 4B). Everything here is part of remote hosts: off, nothing
|
|
11
|
+
of it loads.
|
|
12
|
+
|
|
13
|
+
Contents: [Roles](#roles) · [Connection](#connection) · [Protocol](#protocol) · [Reverse direction](#reverse-direction) · [Host side](#host-side) · [Folders](#folders) · [Project files](#project-files) · [Machine side](#machine-side) · [Sandbox](#sandbox) · [Offline](#offline) · [Client UI](#client-ui) · [Security model](#security-model) · [Limits](#limits) · [Not in v1](#not-in-v1)
|
|
14
|
+
|
|
15
|
+
## Roles
|
|
16
|
+
|
|
17
|
+
| Role | Owns | Runs |
|
|
18
|
+
|---|---|---|
|
|
19
|
+
| **Host** | sessions, settings, credentials, approvals, the transcript | the agent loop, the model calls, every tool's logic |
|
|
20
|
+
| **Machine** | its own declared folders (*roots*) and its data dir | the tools' I/O only: `exec`, file reads/writes/listings, background tasks, PTYs |
|
|
21
|
+
|
|
22
|
+
Any zidane process can play either role for another. A session whose
|
|
23
|
+
environment is `local@laptop` or `worktree@laptop` runs on the host, and every
|
|
24
|
+
`ExecutionContext` call its tools make goes to the machine named `laptop`.
|
|
25
|
+
No credential crosses to a machine: it sees commands, paths and file contents,
|
|
26
|
+
never a provider key, a token or the transcript.
|
|
27
|
+
|
|
28
|
+
## Connection
|
|
29
|
+
|
|
30
|
+
- **The machine dials the host** (`wss://host/…`), with a token of scope
|
|
31
|
+
`machine` created on the host (`zidane server token create --name laptop --scope machine`).
|
|
32
|
+
A laptop needs no inbound port; NAT and firewalls are irrelevant.
|
|
33
|
+
- Same WebSocket transport, same JSON-RPC framing, heartbeat (`$/ping`, WS
|
|
34
|
+
pings) and `ipc.hello` handshake as a client (the machine pins the host id
|
|
35
|
+
after its first hello, like a saved host). Then the direction of the
|
|
36
|
+
execution calls is reversed: the **host sends requests to the machine** over
|
|
37
|
+
the machine's connection (`createJsonRpcEndpoint` is bidirectional).
|
|
38
|
+
- A machine connection is **not a client**: it is never added to the host's
|
|
39
|
+
peer set (it receives no signal, no session data) and may call only
|
|
40
|
+
`ipc.hello`, `$/ping` and `machine.*`. Anything else is refused with
|
|
41
|
+
`MethodNotAllowed`. A client connection calling `machine.*` gets the same.
|
|
42
|
+
- Reconnect: the machine agent reuses the hosts' connection loop
|
|
43
|
+
(`keepHostConnected`: backoff 1 s → 30 s; a refused token or another host
|
|
44
|
+
answering is "for good" until restarted).
|
|
45
|
+
|
|
46
|
+
A zidane-server can be a machine of another zidane-server with the same
|
|
47
|
+
agent (it dials). A **local** host (daemon, in-process) has no listener, so
|
|
48
|
+
for `local@<server>` the roles are kept and the dialing is turned around:
|
|
49
|
+
see [Reverse direction](#reverse-direction).
|
|
50
|
+
|
|
51
|
+
## Protocol
|
|
52
|
+
|
|
53
|
+
Wire types: `src/chat/machine-protocol.ts`, re-exported by `src/ipc/protocol.ts`.
|
|
54
|
+
Versioned on its own: `MACHINE_PROTOCOL_VERSION` (currently `1`). The host
|
|
55
|
+
refuses a `machine.register` with another major version
|
|
56
|
+
(`MachineErrorCode.VersionSkew`), naming both versions.
|
|
57
|
+
|
|
58
|
+
**Machine → host** (requests on the machine's connection):
|
|
59
|
+
|
|
60
|
+
| Method | Params → result |
|
|
61
|
+
|---|---|
|
|
62
|
+
| `ipc.hello`, `$/ping` | as for clients |
|
|
63
|
+
| `machine.register` | `{ machineId, name, roots: [{ path, label? }], capabilities, version, protocolVersion }` → `{ machineId, hostId? }` |
|
|
64
|
+
|
|
65
|
+
`machineId` is stable, generated once and kept in `<dataDir>/machine-id`.
|
|
66
|
+
`name` is the display name and the suffix of its environment kinds; one name
|
|
67
|
+
belongs to one machine id per host (a second machine claiming it is refused).
|
|
68
|
+
|
|
69
|
+
`capabilities`: `shell`, `terminals`, `background`, `worktrees`, `stream`
|
|
70
|
+
(`exec.stream*`; absent on an older machine, whose context then has no
|
|
71
|
+
`execStream`) and `sandboxed` (it runs every command under its own OS
|
|
72
|
+
sandbox: the context reports `isolated`).
|
|
73
|
+
|
|
74
|
+
**Machine → host** (notifications): `terminal.data { terminalId, data (base64) }`, `terminal.exit { terminalId, code }`, `task.exit { taskId, exit: TaskExitInfo }`, `exec.stream.data { callId, stream: 'stdout' | 'stderr', data (base64) }`, `exec.stream.exit { callId, code }`.
|
|
75
|
+
|
|
76
|
+
**Host → machine** notifications: `exec.cancel { callId }` (the abort of a sent `exec.run`, or the kill of a live `exec.stream`).
|
|
77
|
+
|
|
78
|
+
**Host → machine** (requests), one per `ExecutionContext` method; every call
|
|
79
|
+
names its handle as `{ id, cwd }` so a machine that restarted rebuilds it:
|
|
80
|
+
|
|
81
|
+
| Method | Mirrors |
|
|
82
|
+
|---|---|
|
|
83
|
+
| `machine.ping` | liveness |
|
|
84
|
+
| `exec.spawn { handle }` | `spawn` (checks `cwd` against the roots) |
|
|
85
|
+
| `exec.run { handle, callId, command, cwd?, env?, timeout? }` | `exec` (timeout in seconds, like the interface) |
|
|
86
|
+
| `exec.cancel { callId }` | the `signal` of `exec`; kills an `exec.stream` |
|
|
87
|
+
| `exec.stream { handle, callId, command, cwd?, env? }` → `{ pid }` | `execStream`: output as `exec.stream.data`, then one `exec.stream.exit` once stdout and stderr are drained |
|
|
88
|
+
| `exec.stream.write { callId, data (base64) }` | the stream's stdin |
|
|
89
|
+
| `exec.destroy { handle }` | `destroy` |
|
|
90
|
+
| `fs.read { handle, path, binary? }` | `readFile` / `readFileBinary` (binary as base64) |
|
|
91
|
+
| `fs.write { handle, path, content (base64) }` | `writeFile` |
|
|
92
|
+
| `fs.list { handle, path, options? }` | `listFiles` |
|
|
93
|
+
| `fs.size { handle, path }` | `fileSize` |
|
|
94
|
+
| `bg.start { handle, command, cwd?, env?, maxOutputBytes? }` | `execBackground` (output under the machine's data dir) |
|
|
95
|
+
| `bg.list` / `bg.kill { taskId }` / `bg.wait { taskId, timeoutMs? }` | `listBackground` / `killBackground` / `waitBackground` |
|
|
96
|
+
| `terminal.open { handle, cols, rows, cwd? }` | `openTerminal` |
|
|
97
|
+
| `terminal.attach { terminalId }` | `reconnectTerminal` (returns the buffered output and the byte total, so an automatic reattach after a reconnect replays only what the host missed) |
|
|
98
|
+
| `terminal.write` / `terminal.resize` / `terminal.kill` / `terminal.detach` | `TerminalProcess` |
|
|
99
|
+
| `env.checkFolder { cwd }` | the machine's verdict on a session folder |
|
|
100
|
+
| `env.prepareWorktree { cwd, branch, base?, config }` / `env.teardownWorktree { ref, config }` | the worktree provider's `prepare` / `teardown`, run machine-side |
|
|
101
|
+
|
|
102
|
+
`execStream` is what context-scoped MCP servers (`scope: 'context'`) run
|
|
103
|
+
on: a machine session's stdio servers run on the machine. The host side
|
|
104
|
+
registers a stream's queues before the call, so no early byte is lost; a
|
|
105
|
+
stream whose link drops ends with exit `-1` (the machine kills it: a live
|
|
106
|
+
protocol can't survive lost output). Not mirrored: `getMappedPort`;
|
|
107
|
+
`reassignBackgroundTasks` is done host-side (same-handle rebinding).
|
|
108
|
+
|
|
109
|
+
## Reverse direction
|
|
110
|
+
|
|
111
|
+
`local@<server>` on a local host (the laptop is the host, a saved
|
|
112
|
+
zidane-server the machine). The local host already has what a window has
|
|
113
|
+
for that server (its saved entry in `hosts.json`, its client token under
|
|
114
|
+
`hosts:<id>`), so it dials it as a client and asks it to lend itself:
|
|
115
|
+
|
|
116
|
+
1. `machine.attach { hostId }` (a client method): the server answers with
|
|
117
|
+
the registration a machine would send (`MachineRegisterParams`: its id,
|
|
118
|
+
its name made machine-safe, its workspace roots as roots, its
|
|
119
|
+
capabilities). `hostId` names the asking host process, so a reconnect
|
|
120
|
+
finds its tasks and terminals again (each local host process uses its
|
|
121
|
+
own instance id: two windows' in-process hosts never take over each
|
|
122
|
+
other's executor). The server keys an executor by the client's token
|
|
123
|
+
AND that id: the id is the client's word, so another token naming it
|
|
124
|
+
gets an executor of its own, never the first one's tasks, terminals or
|
|
125
|
+
exits. A token holds at most 4 executors there and only ever evicts its
|
|
126
|
+
own idle ones.
|
|
127
|
+
2. The local host registers that in its own machine registry, with a
|
|
128
|
+
connection that sends every host→machine request as
|
|
129
|
+
`machine.call { method, params }` (no client deadline: a call lasts as
|
|
130
|
+
long as its command; a dead server is the heartbeat's to catch) and
|
|
131
|
+
every notification as a `machine.call` notification.
|
|
132
|
+
3. The server serves them with the same confined executor a shared
|
|
133
|
+
machine runs (`createMachineExecutor`, loaded on the first attach),
|
|
134
|
+
inside its workspace roots, its data dir at `<data dir>/machine-self`.
|
|
135
|
+
Its notifications go back to that connection only, as
|
|
136
|
+
`machine:notification` signals (sent before the attach reply too: the
|
|
137
|
+
local host buffers them until it has registered).
|
|
138
|
+
4. A dropped connection is the machine going offline (same semantics as
|
|
139
|
+
below); the loop reconnects (backoff), a refused token, another server
|
|
140
|
+
at the URL, or a refused attach (`allowAsMachine` off, a name taken
|
|
141
|
+
here) stop it until the user acts. A busy server (no room for this
|
|
142
|
+
token's executor now: `MachineErrorCode.Busy`) or a dropped link is
|
|
143
|
+
retried with the backoff.
|
|
144
|
+
|
|
145
|
+
The server must allow it: `allowAsMachine` (`--allow-as-machine`,
|
|
146
|
+
`ZIDANE_SERVER_ALLOW_AS_MACHINE=1`, `server.json`), off by default.
|
|
147
|
+
`ipc.hello` says so (`machineServe: true`) and the windows offer "use as a
|
|
148
|
+
machine" only then. Security: [SERVER.md § Security model](./SERVER.md#security-model).
|
|
149
|
+
|
|
150
|
+
## Host side
|
|
151
|
+
|
|
152
|
+
- `src/chat/host/machines/` (engine feature, `host.machines`): the registry of
|
|
153
|
+
known machines (persisted in `<dataDir>/machines.json`: id, name, roots,
|
|
154
|
+
token id, last seen) and their live links. Made only where machines
|
|
155
|
+
exist: always on a zidane-server (remote by nature), on a local host only
|
|
156
|
+
while remote hosts are on (off: `machines.json` is never read and no
|
|
157
|
+
machine kind is registered; turned off later, the known machines read
|
|
158
|
+
as absent) (`online` / `offline`), the remote
|
|
159
|
+
context (`createMachineContext`), the offline wait, and one
|
|
160
|
+
`local@<name>` + `worktree@<name>` environment provider per known machine,
|
|
161
|
+
registered on the host's environment registry when the machine is first
|
|
162
|
+
known. A forgotten machine's providers report unavailable and refuse to run.
|
|
163
|
+
- Transport-free: the engine sees a `MachineLink` (`request`, `notify`,
|
|
164
|
+
`close`, `onNotification`); `src/ipc/daemon/machine-gateway.ts` builds it
|
|
165
|
+
over the WS connection and enforces the method allowlist.
|
|
166
|
+
- `environments.list` lists the machine kinds with `machine: { id, name, status }`
|
|
167
|
+
(a forgotten machine's kinds are not listed); `machines.list` /
|
|
168
|
+
`machines.forget` are client methods; `machine:status { machineId, name, status }`
|
|
169
|
+
is broadcast to clients.
|
|
170
|
+
- Execution: a machine context is `sandbox`-typed (its filesystem is not the
|
|
171
|
+
host's), so the host's own workspace checks on `sessions.workspace*` leave
|
|
172
|
+
paths to the machine, and the session's cwd is not "movable" like a local one.
|
|
173
|
+
- Session metadata: the environment's `ref` holds `{ machineId }` (and the
|
|
174
|
+
worktree's `path` / `repoRoot` on the machine).
|
|
175
|
+
- `sessions.create` with a machine kind: the host's own workspace confinement
|
|
176
|
+
does not apply (the folder is on the machine); instead the folder must be
|
|
177
|
+
absolute and lexically inside a declared root (pre-check), then the machine
|
|
178
|
+
re-checks it on its filesystem (`env.checkFolder`, real paths) during
|
|
179
|
+
provisioning.
|
|
180
|
+
|
|
181
|
+
## Folders
|
|
182
|
+
|
|
183
|
+
A machine kind's folders are the machine's: every entry point taking a
|
|
184
|
+
folder checks one against that machine's roots (lexically here, then on
|
|
185
|
+
the machine with real paths where it touches it), never against this
|
|
186
|
+
host's own workspace confinement (a confined server would refuse every
|
|
187
|
+
machine path; an unconfined local host would read its own filesystem at a
|
|
188
|
+
path that only means something on the machine). `ChatHost.resolveFolder`
|
|
189
|
+
decides: a machine kind given (`kind` on `environments.list`,
|
|
190
|
+
`commands.list` / `commands.completeArgs`, `catalog.*`, `models.list`),
|
|
191
|
+
else this host's confinement, else (an older client naming no kind) the
|
|
192
|
+
one known machine whose roots hold it.
|
|
193
|
+
|
|
194
|
+
- `environments.list` for a machine's folder: its machine's kinds as
|
|
195
|
+
usual, this host's own kinds unavailable ("The folder is on laptop."). A
|
|
196
|
+
machine kind's availability does not depend on the folder (pickers ask
|
|
197
|
+
the kind first, then a folder there, `machines.listDirs`).
|
|
198
|
+
- `commands.list`, `catalog.*`, `models.list`: this host's user scope,
|
|
199
|
+
read from a folder of its own (below), plus for skills and MCP servers
|
|
200
|
+
the project scope read on the machine.
|
|
201
|
+
- `sessions.setFolder` on a machine session: checked by its machine
|
|
202
|
+
(`env.checkFolder`, never waits for an offline one); launch overrides'
|
|
203
|
+
`cwd` likewise.
|
|
204
|
+
|
|
205
|
+
## Project files
|
|
206
|
+
|
|
207
|
+
A machine session's folder is on the machine, so is its project. The host
|
|
208
|
+
reads its **user scope** (`~/.agents`, `~/.zidane`, user extensions and
|
|
209
|
+
plugins, user skills and MCP servers, `~/.zidane/SYSTEM.md`) as for any
|
|
210
|
+
session, but from a folder of its own with no project
|
|
211
|
+
(`<data dir>/machine-sessions`, `ActiveRun.discoveryCwd`), never the
|
|
212
|
+
machine's path on this filesystem. The **project scope** is read on the
|
|
213
|
+
machine, through the session's own execution (`readMachineProject`:
|
|
214
|
+
`fs.size` / `fs.read` / `fs.list`, so the machine's roots and data-dir
|
|
215
|
+
rules apply), at agent build:
|
|
216
|
+
|
|
217
|
+
| What | Where on the machine |
|
|
218
|
+
|---|---|
|
|
219
|
+
| project root | `git rev-parse --show-toplevel` when inside the roots, else the session folder |
|
|
220
|
+
| instructions | `AGENTS.md`, `.agents/AGENTS.md`, `.<prefix>/AGENTS.md`, `CLAUDE.md` (64 KiB each), after the user's, under `userInstructionsScope` |
|
|
221
|
+
| `SYSTEM.md` | `<root>/SYSTEM.md`, beating the user's |
|
|
222
|
+
| skills | `.agents/skills/*/SKILL.md`, `.<prefix>/skills/*/SKILL.md` (64 at most, 256 KiB each), ahead of the user's on a name |
|
|
223
|
+
| MCP servers | `.agents/mcp(s).json`, `.<prefix>/mcp(s).json` (256 KiB each) |
|
|
224
|
+
|
|
225
|
+
- **No code from a machine.** Project extensions and Agent Plugins are
|
|
226
|
+
never loaded for a machine's folder (they are code; the host never runs
|
|
227
|
+
what a machine provides), so project `/` commands, which extensions
|
|
228
|
+
contribute, are not either: a machine session has the host's built-in
|
|
229
|
+
and user-extension commands.
|
|
230
|
+
- **MCP servers run on the machine.** A project stdio server is kept with
|
|
231
|
+
`scope: 'context'` (it runs over `execStream`, on the machine); every
|
|
232
|
+
`${VAR}` placeholder in it is resolved first to its `:-default` or to
|
|
233
|
+
nothing, so this host's environment (provider keys) is never expanded
|
|
234
|
+
into anything a machine named. A URL server is dropped with a notice
|
|
235
|
+
(connecting would happen from the host).
|
|
236
|
+
- **Offline at build**: the run is not held. The session gets
|
|
237
|
+
"laptop is offline: its project instructions, skills and MCP servers
|
|
238
|
+
load when it is back.", the agent is built with the user scope, and is
|
|
239
|
+
rebuilt (at run end when busy) once the machine is back.
|
|
240
|
+
- Not read on a machine: Ruler and foreign agents' configs (`.ruler/`,
|
|
241
|
+
`.claude/…`), skill resources besides `SKILL.md` (their paths are the
|
|
242
|
+
machine's; `skills_read` of one is not proxied).
|
|
243
|
+
|
|
244
|
+
## Machine side
|
|
245
|
+
|
|
246
|
+
`startMachineAgent({ hostUrl, token, name, roots, dataDir })` (`src/ipc/machine/`),
|
|
247
|
+
CLI `zidane machine share --host wss://… --token-file … --name laptop --root ~/code [--root …]`.
|
|
248
|
+
|
|
249
|
+
- Serves execution from one `process` context per handle, through a guard
|
|
250
|
+
(`createMachineExecutor`) that never trusts the host: every path and `cwd` is
|
|
251
|
+
resolved against the handle's cwd, then **real-path contained** in a root
|
|
252
|
+
(`createWorkspaceConfinement`, the same rule as a zidane-server's workspace
|
|
253
|
+
roots): `..`, absolute escapes and symlinks pointing out are refused; the
|
|
254
|
+
machine's data dir (its `machine-id`, token file, task logs) is unreachable
|
|
255
|
+
by file ops even when a root contains it. Exception: `<dataDir>/worktrees/`
|
|
256
|
+
(an implicit root: `worktree@<machine>` makes its worktrees there).
|
|
257
|
+
- **Private folders**, unreachable by file ops whatever the roots (sharing
|
|
258
|
+
`--root ~` included; `machinePrivateDirs`): zidane's own folders as the
|
|
259
|
+
machine's process resolves them (data, config, state and cache dirs, and
|
|
260
|
+
`~/.zidane`: credentials, tokens, extensions, plugins, skills, `mcps.json`,
|
|
261
|
+
the user-scope `SYSTEM.md`, the machine's data, sync files), the zidane
|
|
262
|
+
data dir it was given (a server's `--data-dir`), and the credential
|
|
263
|
+
folders the sandbox hides (`~/.ssh`, `~/.aws`, `~/.gnupg`, …). A root
|
|
264
|
+
that is, or is inside, one of them is refused when sharing (`zidane
|
|
265
|
+
machine share`, a share link); a server's own default root
|
|
266
|
+
(`<data dir>/workspaces`) stays usable when it lends itself. A listing
|
|
267
|
+
(`fs.list`, globs) only returns entries whose real path is reachable, and
|
|
268
|
+
a pattern can't climb with `..`.
|
|
269
|
+
- **Task logs** (`<dataDir>/tasks/`, owner-only): a log of a background task
|
|
270
|
+
this machine started is readable by its path, opened without following a
|
|
271
|
+
symlink, and only when it is still that regular file inside the tasks
|
|
272
|
+
folder (`./task-logs.ts`); logs are created exclusively, never through a
|
|
273
|
+
link planted at their path. A command run for the host can't make the
|
|
274
|
+
machine process read anything else through "its log".
|
|
275
|
+
- A handle id named again with another folder destroys the old handle (its
|
|
276
|
+
background tasks stop); a second call under a `callId` still running is
|
|
277
|
+
refused.
|
|
278
|
+
- Bounded: `exec` output by the process context's own cap, background
|
|
279
|
+
output capped per task (8 MiB), at most 64 running tasks and 16
|
|
280
|
+
terminals, terminal backlogs 256 KiB, at most 256 queued task exits.
|
|
281
|
+
- Path checks and use are not atomic (a shell command racing a symlink in
|
|
282
|
+
between could redirect one file op); the shell can reach as much anyway.
|
|
283
|
+
- Live processes (`exec.stream`): at most 16, output sent in chunks of at
|
|
284
|
+
most 256 KiB, stdin written 256 KiB at a time; all killed when the host
|
|
285
|
+
link drops. A kill sends TERM to the process group, and KILL after the
|
|
286
|
+
grace if its pipes are still open (a TERM landing while the shell
|
|
287
|
+
forks a child would otherwise leave that child, and the stream, alive).
|
|
288
|
+
- Background tasks keep running across a disconnect (they're the machine's
|
|
289
|
+
children); their exits are queued and re-sent on reconnect, and `bg.list`
|
|
290
|
+
answers by id after a reconnect. Terminals keep running for a grace period
|
|
291
|
+
(default 5 min) after their host link drops, buffering output; `terminal.attach`
|
|
292
|
+
reattaches with the backlog; past the grace they are killed.
|
|
293
|
+
|
|
294
|
+
## Sandbox
|
|
295
|
+
|
|
296
|
+
The machine owner decides: a machine runs every command it is sent
|
|
297
|
+
(`exec`, background tasks, streams, terminals) under **its own user's**
|
|
298
|
+
`sandbox*` settings (`sandbox`, `sandboxNetwork`, `sandboxAllowWrite`:
|
|
299
|
+
the same OS sandbox as local sessions, macOS Seatbelt / Linux bwrap,
|
|
300
|
+
failing closed when unavailable), over every root it shares (not its task
|
|
301
|
+
logs: the machine process writes them), with its user's zidane credential
|
|
302
|
+
files hidden. A host can't turn
|
|
303
|
+
it off, and its own `sandbox*` settings don't apply to a machine session
|
|
304
|
+
(they describe the host's computer). `zidane machine share` reads its
|
|
305
|
+
user's `state.json`; a share run from a local host uses that host's
|
|
306
|
+
settings; a server lending itself uses its own. The register says
|
|
307
|
+
`sandboxed`, the host's context reports `isolated`.
|
|
308
|
+
|
|
309
|
+
## Offline
|
|
310
|
+
|
|
311
|
+
A machine is `offline` from the moment its connection drops (close, or two
|
|
312
|
+
missed heartbeats) until it registers again. Every host→machine call goes
|
|
313
|
+
through `MachineLink.call`:
|
|
314
|
+
|
|
315
|
+
| When | What happens |
|
|
316
|
+
|---|---|
|
|
317
|
+
| Offline before the call is sent | The call **waits** for the machine (not sent, so safe to send later). The session gets a notice "waiting for laptop…" once, and `machine:status` is broadcast. On reconnect the call is sent; notice "laptop is back". |
|
|
318
|
+
| Sent, then the connection drops before the reply | **Not re-sent** (it may have run). The tool fails with `result lost: laptop disconnected during the call` and the model decides. Idempotent calls (`MACHINE_IDEMPOTENT_METHODS`: reads `fs.read` / `fs.list` / `fs.size` / `bg.list` / `bg.wait` / `env.checkFolder` / `terminal.attach` / `machine.ping`, and the handle calls `exec.spawn` / `exec.destroy` / `exec.cancel`, keyed by the host's handle id) are the exception: they are re-sent after the reconnect like an unsent call. |
|
|
319
|
+
| Still offline after `machineWaitMinutes` (setting, default 15) | The waiting calls fail with `laptop is unavailable`, the session's run is interrupted and ends with the notice "laptop stayed offline for 15 min: run stopped". |
|
|
320
|
+
| Aborted while waiting (user interrupt) | The call is dropped, nothing is sent. |
|
|
321
|
+
| The host drains (shutdown) | Waiting calls fail at once (`MachineRegistry.close`), so a run or a provisioning waiting on an offline machine never holds the drain. |
|
|
322
|
+
|
|
323
|
+
The wait is the run pausing: the tool call is simply still in flight, so the
|
|
324
|
+
model never sees a broken tool. Timers are injectable (`MachineRegistryOptions.timers`) for tests.
|
|
325
|
+
|
|
326
|
+
## Client UI
|
|
327
|
+
|
|
328
|
+
Behind remote hosts (Settings → Remote hosts, `ZIDANE_NO_REMOTE` beats
|
|
329
|
+
it): off, no surface, and This computer's host loads no machine code,
|
|
330
|
+
reads no `machine-links.json`, no `machine:` token, dials nothing
|
|
331
|
+
(`test/tui/app-remote-containment.test.tsx`, `test/tui/remote-lazy-graph.test.ts`,
|
|
332
|
+
`gui/src/main/chat/hosts/gate.containment.test.ts`). `zidane machine share`
|
|
333
|
+
refuses under `ZIDANE_NO_REMOTE=1` / `--no-remote` too.
|
|
334
|
+
|
|
335
|
+
**This computer's host** (in-process or the daemon; the dispatcher's
|
|
336
|
+
handlers, which the desktop app's embedded host calls directly) offers:
|
|
337
|
+
|
|
338
|
+
| Method | What |
|
|
339
|
+
|---|---|
|
|
340
|
+
| `machines.share.start { hostId, name, roots, token? }` | share this computer with a saved server (`token`: a machine token to keep, as `machine:<hostId>` in its credentials; else the one kept) |
|
|
341
|
+
| `machines.share.stop { hostId }` / `machines.useServer { hostId, enabled }` | stop sharing / use a saved server as a machine ([Reverse direction](#reverse-direction)) |
|
|
342
|
+
| `machines.links.status` | both, with their state (`connecting`, `connected`, `reconnecting`, `offline`, `auth-refused`, `identity`, `refused`) and why; `machine:links` signals each change |
|
|
343
|
+
| `machines.list` / `machines.forget` / `machines.listDirs { machineId, path? }` | the machines it knows, forget one, a machine's folders (absent `path`: its first root) through it |
|
|
344
|
+
|
|
345
|
+
and a server `tokens.createMachineToken { name }` ([SERVER.md § Tokens](./SERVER.md#tokens)), so sharing is one step: the window
|
|
346
|
+
asks the local host, which has no token for that server, so the window
|
|
347
|
+
mints one there and hands it over (a server that can't asks for one to be
|
|
348
|
+
pasted).
|
|
349
|
+
|
|
350
|
+
The wanted links live in `<data dir>/machine-links.json` (no token in it),
|
|
351
|
+
any process writes it, and they resume with the host while remote hosts
|
|
352
|
+
are on. **Shares** run in one process for the computer (the holder of
|
|
353
|
+
`machine-links.lock`, re-checked every 2 s: the daemon, or the first
|
|
354
|
+
window's in-process host; the others show its state from
|
|
355
|
+
`machine-links-status.json`): two processes sharing one machine id would
|
|
356
|
+
keep replacing each other's connection. The lock is exclusive
|
|
357
|
+
(`src/ipc/machine/share-lock.ts`): taken by an exclusive create; its record
|
|
358
|
+
names the holder's pid and a per-process nonce; the holder touches it on
|
|
359
|
+
every check, so it is stale once its pid is gone or it went untouched for
|
|
360
|
+
30 s (a reused pid); a stale lock is moved aside atomically and checked
|
|
361
|
+
before it is retaken. A process that finds another nonce in it stops its
|
|
362
|
+
shares at once, and only the holder removes it. A share's folders are
|
|
363
|
+
checked like the CLI's: absolute, existing, and not zidane's own data or a
|
|
364
|
+
credentials folder (real paths). **Uses** run in every host process
|
|
365
|
+
(each has its own machine registry). Turning remote hosts off stops them
|
|
366
|
+
all.
|
|
367
|
+
|
|
368
|
+
**TUI**: Settings → Hosts → `ctrl+b` on a saved server opens its machines
|
|
369
|
+
(`src/tui/machines/`): share this computer (name, folders, start / stop),
|
|
370
|
+
use it as a machine (inert unless the server lends itself), its machines
|
|
371
|
+
(status, folders, last seen; `ctrl+x` forgets, asks first). New session:
|
|
372
|
+
a server's machine kinds (and This computer's, when it uses a server) come
|
|
373
|
+
after the host's own, grouped by machine with its status (an offline one
|
|
374
|
+
listed, not pickable), then a folder on that machine. The chat title shows
|
|
375
|
+
`local@laptop` / `worktree@laptop ⎇ branch`, and `waiting for laptop…`
|
|
376
|
+
while a run is paused on it (from the session's notices, cleared by
|
|
377
|
+
`machine:status` online).
|
|
378
|
+
|
|
379
|
+
**Desktop app**: the same, in Settings › Hosts (a server card's
|
|
380
|
+
*Machines*: switches, folders from the native dialog), the composer's
|
|
381
|
+
environment picker and a machine folder picker, and a "Waiting for
|
|
382
|
+
laptop…" banner over the composer; main relays `machine:status` /
|
|
383
|
+
`machine:links` as `machines:status` / `machines:links`
|
|
384
|
+
([IPC.md § Desktop app](./IPC.md)).
|
|
385
|
+
|
|
386
|
+
## Security model
|
|
387
|
+
|
|
388
|
+
- **Sharing a machine gives the host a shell on it**, with the machine user's
|
|
389
|
+
permissions. Root containment covers file operations and every `cwd`; a
|
|
390
|
+
shell command can still read anything the machine user can (`cat ~/.ssh/id_ed25519`),
|
|
391
|
+
exactly like a zidane-server's own shell (see `SERVER.md` § Security). Share
|
|
392
|
+
with hosts you trust, as a user that owns little.
|
|
393
|
+
- What the machine enforces itself (a malicious host can't bypass it): roots
|
|
394
|
+
for paths and `cwd`, data-dir exclusion, symlink escapes, size and count
|
|
395
|
+
bounds, the method set it serves.
|
|
396
|
+
- What the host enforces: machine tokens can only register (never read
|
|
397
|
+
sessions, settings or signals); client tokens can't register; a revoked
|
|
398
|
+
token drops the machine's connection (it goes offline, its sessions wait
|
|
399
|
+
then stop); environments on unknown or forgotten machines are refused.
|
|
400
|
+
- **A machine id is bound to its token.** Ids are not secrets (the CLI
|
|
401
|
+
prints them, `machines.list` and `machine:status` show them), so the
|
|
402
|
+
first registration binds the id to the token it used (`principal` in
|
|
403
|
+
`machines.json`). Another machine token registering that id is refused
|
|
404
|
+
(`MachineErrorCode.IdBound`) and the machine's connection is left alone;
|
|
405
|
+
once the bound token is revoked, or the machine forgotten, a new token
|
|
406
|
+
may take the id.
|
|
407
|
+
- **Forgetting sticks.** `machines.forget` records the (machine id, token)
|
|
408
|
+
pair: that token registering the id again is refused for good (the agent
|
|
409
|
+
stops retrying), across restarts, until the share uses a new machine
|
|
410
|
+
token. The token itself stays (it may serve other machines): revoke it
|
|
411
|
+
on the server when it was only that machine's.
|
|
412
|
+
- A window mints at most 16 live machine tokens per client token
|
|
413
|
+
(`tokens.createMachineToken`, each recorded with `createdBy` and
|
|
414
|
+
audited, refusals included); revoke some to mint more.
|
|
415
|
+
- Safe-mode approvals are unchanged: they are asked of the host's clients
|
|
416
|
+
before the call ever reaches the machine.
|
|
417
|
+
- Nothing a machine provides runs on the host: no project extension,
|
|
418
|
+
plugin or URL MCP server from a machine's folder, and its stdio MCP
|
|
419
|
+
servers run on the machine without this host's environment
|
|
420
|
+
([Project files](#project-files)).
|
|
421
|
+
- Reverse direction: the server's own executor confines a client's
|
|
422
|
+
`machine.call` like a machine confines its host's (its data dir private
|
|
423
|
+
even when an operator root contains it); with `allowAsMachine` on, a
|
|
424
|
+
client token can do there what terminals already let it do.
|
|
425
|
+
- A zidane-server has no machine links: `machines.share.*`,
|
|
426
|
+
`machines.useServer` and `machines.links.status` are refused there.
|
|
427
|
+
- A machine token minted by a window is shown to that window only, kept by
|
|
428
|
+
its local host in its credentials store, never in `machine-links.json`,
|
|
429
|
+
never in a log or a status detail.
|
|
430
|
+
|
|
431
|
+
## Limits
|
|
432
|
+
|
|
433
|
+
| What | Bound |
|
|
434
|
+
|---|---|
|
|
435
|
+
| Reply / request size | the WS transport's (32 MiB frame); file content base64 |
|
|
436
|
+
| Machine connection rate | not rate-limited per message (terminal output and task exits stream as notifications; the connection can only register) |
|
|
437
|
+
| Roots per machine | 32 |
|
|
438
|
+
| `exec` output | the process context's cap (combined stdout+stderr) |
|
|
439
|
+
| Background tasks | 64 running per machine, output file capped at 8 MiB |
|
|
440
|
+
| Live processes (`exec.stream`) | 16 per machine, 256 KiB per chunk |
|
|
441
|
+
| `!` in a machine session | on any host (a local one too), streamed from the machine (`sessions.workspaceExecStream` runs the session's machine context's `execStream`, i.e. `exec.stream*`; esc kills it on the machine, `exec.cancel`); counts against the 16 live processes; 1 MiB of output per command on the host's side |
|
|
442
|
+
| Project files read from a machine | 64 KiB per instruction file, 256 KiB per `SKILL.md` / MCP file, 64 skills |
|
|
443
|
+
| Hosts a server lends itself to at once | 32, at most 4 per client token (a token's idle one is let go for its new one; full: `Busy`, retried) |
|
|
444
|
+
| Terminals | 16 per machine, 256 KiB backlog, 5 min grace |
|
|
445
|
+
| Offline wait | `machineWaitMinutes` (default 15) |
|
|
446
|
+
|
|
447
|
+
## Not in v1
|
|
448
|
+
|
|
449
|
+
- Durable background tasks across a machine **restart** (they die with the
|
|
450
|
+
machine agent's process).
|
|
451
|
+
- Project scope from a machine for Ruler and foreign agents' configs, and
|
|
452
|
+
skill resources other than `SKILL.md` (above).
|
|
453
|
+
- A machine's URL MCP servers (dropped: the host would connect).
|
|
454
|
+
- Moving a share between the processes of one computer while it runs: the
|
|
455
|
+
lock holder keeps it until it exits; another process takes over within
|
|
456
|
+
2 s after (30 s if it died without releasing it).
|
package/docs/PANELS.md
CHANGED
|
@@ -9,6 +9,7 @@ Side-panel tabs next to the transcript, in both the TUI (docked rail) and the GU
|
|
|
9
9
|
| Artifacts | What the session produced (PRs, branches, commits, recorded files); see [ARTIFACTS.md](./ARTIFACTS.md) | TUI + GUI |
|
|
10
10
|
| Diffs | Two sources: **Session** (edits the agent made, from the transcript) and **Working tree** (`git diff HEAD` plus untracked files, live). Unified / split, wrap. On working-tree diffs, selecting lines opens a review-comment draft; `Mod+Enter` attaches `file + range + fenced excerpt + note` to the prompt | GUI |
|
|
11
11
|
| Files | The session workspace as a tree with git status; opening a file shows a read-only viewer (or the editor, per the setting below) | GUI |
|
|
12
|
+
| Terminal | The session's terminals (also in the bottom drawer), running in the session's environment; see [TERMINAL.md](./TERMINAL.md) | GUI |
|
|
12
13
|
|
|
13
14
|
Diffs and Files render with [`@pierre/diffs`](https://diffs.com) and [`@pierre/trees`](https://trees.software) (Apache-2.0), themed from the `--zg-*` tokens through `gui/src/renderer/src/components/code/pierre-theme.ts`; `DiffView` / `FileView` / `FileTree` are the only places the packages are touched. Filesystem and git access is read-only, on the host at the session cwd, confined to it (`gui/src/main/chat/workspace.ts`).
|
|
14
15
|
|
|
@@ -57,6 +58,8 @@ ctx.registerPanel({
|
|
|
57
58
|
});
|
|
58
59
|
```
|
|
59
60
|
|
|
61
|
+
`load` and `onAction` get `{ sessionId?, cwd, focus? }`. `focus` (`{ kind?, uri? }`) is set when a session badge opened the panel (`badge.panel`, see [EXTENSIONS.md → Session badges](./EXTENSIONS.md#session-badges)) and stays on the panel's reloads until it is opened for something else. Both run scoped to `sessionId`: `ctx.artifacts.list()` answers that chat's artifacts (GUI main and TUI alike).
|
|
62
|
+
|
|
60
63
|
### `PanelContent`
|
|
61
64
|
|
|
62
65
|
| Field | Meaning |
|
package/docs/README.md
CHANGED
|
@@ -15,7 +15,10 @@ Each topic has exactly one owner doc; the others link to it. If two docs seem to
|
|
|
15
15
|
| Chat + surfaces | [CHAT.md](./CHAT.md) | `zidane/chat`: `resolveConfig`, session store + metadata, settings, safe mode, per-edit approval, todos, built-in commands, completion providers, theme, OAuth. |
|
|
16
16
|
| Chat + surfaces | [TUI.md](./TUI.md) | `zidane/tui`: screens, **keybindings table**, modals, transcript rendering, code highlighting, embedding, file layout. |
|
|
17
17
|
| Chat + surfaces | [IPC.md](./IPC.md) | `zidane/ipc`: daemon lifecycle, **every RPC and signal**, multi-window convergence, TUI/GUI parity matrix, `zidane attach`. |
|
|
18
|
+
| Chat + surfaces | [SERVER.md](./SERVER.md) | `zidane-server`: run the host on a remote machine. Docker, configuration, TLS / proxies, tokens, security model, operations. |
|
|
19
|
+
| Chat + surfaces | [MACHINES.md](./MACHINES.md) | Machines: computers lending a host their folders and shell (`local@laptop`), and a server lending itself to a local host (`local@server`). Protocol, offline semantics, project files, sandbox, client UI, security. |
|
|
18
20
|
| Chat + surfaces | [ARTIFACTS.md](./ARTIFACTS.md) | Session artifacts: model, producers, storage, extension kinds, surfaces, RPCs, lazy refresh. |
|
|
21
|
+
| Chat + surfaces | [TERMINAL.md](./TERMINAL.md) | Terminals: PTYs in the session's environment (host, Docker, E2B, Daytona), host manager + pacing, IPC, agent `terminal_*` tools, background tasks in terminals, GUI drawer/tab. |
|
|
19
22
|
| Chat + surfaces | [PANELS.md](./PANELS.md) | Side panels: the declarative content model, `ctx.registerPanel`, catalog + open-state rules shared by the TUI rail and the GUI side panel. |
|
|
20
23
|
| Chat + surfaces | [EXTENSIONS.md](./EXTENSIONS.md) | Extensions: `defineExtension`, **the full `ctx` API**, discovery, install, collisions, settings UI, authoring patterns. |
|
|
21
24
|
| Integrations | [PLUGINS.md](./PLUGINS.md) | Agent Plugins 1.0.0 loader: manifest, validation, filesystem boundary. |
|
|
@@ -33,6 +36,7 @@ Each topic has exactly one owner doc; the others link to it. If two docs seem to
|
|
|
33
36
|
| Session persistence, `SessionStore` backends, sqlite schema | CHAT.md § Sessions |
|
|
34
37
|
| Artifacts (PRs, files a session produced) | ARTIFACTS.md |
|
|
35
38
|
| A side-panel tab (extension or built-in) | PANELS.md |
|
|
39
|
+
| Terminals (drawer, `terminal_*` tools, `openTerminal`) | TERMINAL.md |
|
|
36
40
|
| Settings keys and defaults | CHAT.md § Settings |
|
|
37
41
|
| Safe mode, approvals, per-edit approval | CHAT.md § Safe mode / Per-edit approval |
|
|
38
42
|
| Todos (`todowrite`) contract | CHAT.md § Todos |
|
package/docs/RESTATE.md
CHANGED
|
@@ -307,7 +307,7 @@ createAgent({
|
|
|
307
307
|
|
|
308
308
|
### Child events
|
|
309
309
|
|
|
310
|
-
Hook bubbling can't cross an invocation boundary, so each child's transcript is **replayed onto the parent's bus when it merges** (`child:stream:text`, `child:tool:before/after/error`, `child:turn:after`, `child:stream:end`) — existing `child:tool:*` listeners need no changes. Every event carries `childId`, `depth`, and (tool events) `runId`/`parentRunId`/`parentCallId`; blocks stay contiguous per child in submission order. Not synthesized: `tool:gate`, `tool:transform`, `tool:dispatched`, `tool:cancelled`, `background:*` (decisions taken inside another process).
|
|
310
|
+
Hook bubbling can't cross an invocation boundary, so each child's transcript is **replayed onto the parent's bus when it merges** (`child:stream:text`, `child:tool:before/after/error`, `child:turn:after`, `child:stream:end`) — existing `child:tool:*` listeners need no changes. Every event carries `childId`, `depth`, and (tool events) `runId`/`parentRunId`/`parentCallId`; blocks stay contiguous per child in submission order. Not synthesized: `tool:gate`, `tool:transform`, `tool:dispatched`, `tool:cancelled`, `background:*` (decisions taken inside another process). A child's own merged descendants are announced as `child:spawn:before` → their events → (`child:spawn:error`) → `child:spawn:complete`, with `parentChildId` set to the merged label of their spawner — the lifecycle an in-process nested spawn bubbles.
|
|
311
311
|
|
|
312
312
|
They **re-fire on a parent replay** — dedupe by `callId`/`turnId`, or gate on `createReplayTracker`. Disable with `emitChildEvents: false`.
|
|
313
313
|
|
|
@@ -367,7 +367,8 @@ createAgent({
|
|
|
367
367
|
| `disableBackgroundTasks: 'non-durable'` | Background tasks (`shell` with `run_in_background`) stay enabled only when the context declares `capabilities.detachedTasks: 'durable'` — host-local tasks aren't journaled. |
|
|
368
368
|
| `cache: true` | Stable system/tool prefixes reward prompt-cache breakpoints, cutting cold-start cost on retries. |
|
|
369
369
|
| `shouldRethrowToolError: shouldRethrowRestateControlError` | Restate terminal/suspension control errors escape the tool-result path instead of becoming a fake tool error. |
|
|
370
|
-
| `compactStrategy: 'tail'`, `compactThreshold: 128 KiB`, `compactKeepTurns: 8`, `elideStaleReads: 'stub'`, `
|
|
370
|
+
| `compactStrategy: 'tail'`, `compactThreshold: 128 KiB`, `compactKeepTurns: 8`, `elideStaleReads: 'stub'`, `toolOutputBudget: 64 KiB`, `autoCompact: { threshold: 0.85 }` | Shrinks the per-turn provider payload — journal weight and latency, not just tokens. |
|
|
371
|
+
| `mediaBudget: { maxItems: 80, maxBytes: 20 MiB }`, `maxImageEdge: 2000` | Media stays inline until near the provider's caps, so a long image-heavy run isn't re-billed once per aged screenshot; oversize PNGs shrink once when they enter the session. Requests carry more media bytes than the old 8-message window; pass `mediaKeepTurns: 8` to get that window back (it takes precedence). |
|
|
371
372
|
|
|
372
373
|
Add `maxCostUsd`/`maxTotalTokens` as a server-side budget breaker: throws `AgentBudgetExceededError` and finalizes the run as `aborted` — durable replay+retry otherwise turns a runaway loop into a runaway bill. See `docs/ARCHITECTURE.md` § run-level budget circuit breaker.
|
|
373
374
|
|
|
@@ -99,7 +99,7 @@ All optional on `ExecutionContext` (`src/contexts/types.ts`) — a context imple
|
|
|
99
99
|
| `killBackground(handle, taskId)` | SIGTERMs the whole process group; resolves once the process has exited and its output stream is flushed+closed. Returns `null` on an unknown/already-cleaned-up id — idempotent |
|
|
100
100
|
| `listBackground(handle)` | Snapshot of every task (running + terminated) owned by `handle`. Terminated entries stay until context `destroy()` |
|
|
101
101
|
| `waitBackground(handle, taskId, { timeoutMs?, signal? })` | Resolves with `TaskExitInfo` on exit, or `null` on timeout/abort/unknown id. In-process contexts implement it on `onExit`; durable hosts implement it as an awakeable park |
|
|
102
|
-
| `reassignBackgroundTasks(fromHandle, toHandle, newOnExit?)` | Transfers ownership of every still-running task between handles,
|
|
102
|
+
| `reassignBackgroundTasks(fromHandle, toHandle, newOnExit?, newOnStall?)` | Transfers ownership of every still-running task between handles, replacing the owner's `onExit` / `onStall` with the new owner's callbacks (in-flight kill/wait resolvers are kept). With `fromHandle === toHandle` only the callbacks move — how a host hands tasks to a rebuilt agent. Returns the reassigned entries. Optional — without it, `destroy()` kills the source handle's tasks instead |
|
|
103
103
|
|
|
104
104
|
`TaskExitInfo`: `{ taskId, status: 'exited' | 'killed', exitCode, signal?, outputPath, durationMs, command }`. `exitCode` is `128 + signal-number` on signal-killed children (POSIX convention — `143` for SIGTERM).
|
|
105
105
|
|
|
@@ -138,7 +138,15 @@ wake → next agent.run() drains the queue → <task-notification> prepended t
|
|
|
138
138
|
|
|
139
139
|
Every step is bounded by `destroyTimeoutMs`; a step that doesn't settle in time is abandoned (logged under `ZIDANE_DEBUG`) rather than hanging teardown.
|
|
140
140
|
|
|
141
|
-
No detach-on-destroy option:
|
|
141
|
+
No detach-on-destroy option: destroying the handle always kills its background tasks. Tasks that should outlive the zidane process are the user's job (`tmux`, `nohup`), not this feature's.
|
|
142
|
+
|
|
143
|
+
### Surviving agent rebuilds (chat hosts)
|
|
144
|
+
|
|
145
|
+
The daemon / GUI host and the TUI rebuild a session's agent for reasons unrelated to execution: model or profile swap, settings changes (extensions, MCPs, skills, plugins), MCP login, idle eviction. They build agents on a **host-owned handle** from `src/chat/execution-cache.ts` (`AgentOptions.handle`, lazy form), so step 4 above is skipped and running tasks survive the rebuild:
|
|
146
|
+
|
|
147
|
+
- The rebuilt agent `adopt`s them: exit/stall callbacks are rebound to its hooks and each task is announced as `background:adopt`, which hosts register like `background:start`. A task that exited while no agent owned it reaches the model through the next run's pull reconcile.
|
|
148
|
+
- The handle is released, killing its tasks, when the **session** is done with it: deleted, idle-evicted with nothing running, host drain (process exit), or — in the TUI — switching sessions.
|
|
149
|
+
- A changed context configuration (cwd, sandbox policy) replaces the context and ends its tasks: they were started under the old configuration.
|
|
142
150
|
|
|
143
151
|
## Subagents
|
|
144
152
|
|