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
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contexts-d7UolYot.js","names":[],"sources":["../src/contexts/types.ts","../src/contexts/isolation.ts","../src/contexts/process-state.ts","../src/contexts/process-background.ts","../src/contexts/process-exec.ts","../src/contexts/git-ls-files.ts","../src/contexts/git-ls-files-local.ts","../src/contexts/glob.ts","../src/contexts/process-files.ts","../src/contexts/ansi-strip.ts","../src/contexts/process-shell.ts","../src/contexts/process-stream.ts","../src/contexts/process-tasks.ts","../src/contexts/process.ts"],"sourcesContent":["/**\n * Execution context types.\n *\n * An execution context defines *where* and *how* an agent's tools run.\n * The agent loop and tools interact through this interface without knowing\n * whether they're running in-process, in a Docker container, or in a\n * remote sandbox.\n */\n\nimport type { Buffer } from 'node:buffer'\nimport type { IsolationPolicy } from './isolation'\n\n// ---------------------------------------------------------------------------\n// Capabilities\n// ---------------------------------------------------------------------------\n\n/**\n * Lifetime guarantee of background tasks started through\n * {@link ExecutionContext.execBackground}.\n *\n * - `'none'` — the context cannot detach tasks at all.\n * - `'process-lifetime'` — tasks live as long as the HOST process; a\n * crash/restart of the host orphans or kills them (`ProcessContext`).\n * - `'durable'` — tasks live on a remote runner and survive host process\n * death (remote execution contexts driven by durable runtimes).\n *\n * Durable-execution adapters (e.g. `zidane/restate`) consult this to\n * decide whether backgrounding is safe to expose: a `'durable'` context\n * keeps its tasks across worker crashes, so there is no reason to strip\n * the capability from the model.\n */\nexport type DetachedTasksCapability = 'none' | 'process-lifetime' | 'durable'\n\nexport interface ContextCapabilities {\n /** Can execute shell commands */\n shell: boolean\n /** Can read/write files in a workspace */\n filesystem: boolean\n /** Can enumerate glob matches through `ExecutionContext.listFiles(..., { glob: true })`. */\n glob?: boolean\n /** Can make outbound network requests */\n network: boolean\n /** Has GPU access */\n gpu: boolean\n /**\n * Can open a bidirectional byte stream to a long-lived process through\n * {@link ExecutionContext.execStream}. Absent / `false` = the context\n * only supports the buffered {@link ExecutionContext.exec} and (maybe)\n * the write-to-a-log-file {@link ExecutionContext.execBackground}.\n *\n * Consumers that NEED live stdin (stdio MCP servers routed into the\n * context) must check this before calling; the seam is optional so\n * contexts backed by a request/response-only API stay valid.\n */\n stream?: boolean\n /**\n * Shell commands run under an ENFORCED OS-level sandbox (macOS Seatbelt /\n * Linux bwrap) — see `SpawnConfig.isolation`. `false` when isolation was\n * not requested AND when it was requested but the backend is unavailable\n * (that context fails every command closed instead). Absent / `false` =\n * commands do not run sandboxed.\n */\n isolated?: boolean\n /**\n * Background-task lifetime guarantee. Optional for backward\n * compatibility — when absent, callers infer `'process-lifetime'`\n * if the context implements `execBackground`, `'none'` otherwise\n * (see {@link resolveDetachedTasksCapability}).\n */\n detachedTasks?: DetachedTasksCapability\n}\n\n/**\n * Effective {@link DetachedTasksCapability} of a context, with the\n * backward-compatible inference for contexts that predate the field.\n */\nexport function resolveDetachedTasksCapability(context: ExecutionContext): DetachedTasksCapability {\n return context.capabilities.detachedTasks\n ?? (context.execBackground ? 'process-lifetime' : 'none')\n}\n\n// ---------------------------------------------------------------------------\n// Execution handle\n// ---------------------------------------------------------------------------\n\n/** Opaque handle to a running execution context instance */\nexport interface ExecutionHandle {\n id: string\n type: ContextType\n /** Working directory within the context */\n cwd: string\n}\n\n// ---------------------------------------------------------------------------\n// Exec result\n// ---------------------------------------------------------------------------\n\nexport interface ExecResult {\n stdout: string\n stderr: string\n exitCode: number\n}\n\nexport interface ListFileMetadata {\n path: string\n size?: number\n mtimeMs?: number\n}\n\nexport type ListFilesEntry = string | ListFileMetadata\n\nexport interface ListFilesOptions {\n /** Treat `path` as a glob match pattern instead of a literal directory path. */\n glob?: boolean\n limit?: number\n metadata?: boolean\n /**\n * Skip git-ignored files when globbing. Default `true`. Contexts that can\n * resolve git's ignore rules (e.g. the local process context) honor it; the\n * rest treat it as a no-op. Set `false` to match every file, ignored or not.\n */\n gitignore?: boolean\n /** Abort the listing early — forwarded from the calling tool's `ctx.signal`. */\n signal?: AbortSignal\n}\n\n// ---------------------------------------------------------------------------\n// Spawn config\n// ---------------------------------------------------------------------------\n\nexport interface SpawnConfig {\n /** Working directory (created if it doesn't exist) */\n cwd?: string\n /**\n * Optional workspace-containment root for the file methods (`readFile`,\n * `writeFile`, `listFiles`) of the process context.\n *\n * When set, a resolved path that escapes this root (via absolute path,\n * `..`, or a symlink pointing outside) is rejected before any I/O. Default\n * unset = no containment (back-compat; the agent can read/write anywhere\n * the host process can). Set this for untrusted workloads to confine file\n * tools to a directory. Shell commands are not covered — use a sandbox /\n * docker context for full isolation.\n */\n workspaceRoot?: string\n /** Environment variables */\n env?: Record<string, string>\n /**\n * Whether spawned shells inherit the parent `process.env` (process context).\n *\n * Default `true` (preserves existing behavior — children see the parent\n * environment including anything Bun auto-loaded from `.env`). Set `false`\n * for untrusted workloads so secrets in the parent environment aren't\n * readable by tool commands; only `env` / per-call env plus a minimal base\n * (`PATH`, `HOME`, `SHELL`, `LANG`, `LC_ALL`, `USER`, `TERM`, `TMPDIR`) are\n * passed through.\n */\n inheritEnv?: boolean\n /**\n * OS-level sandboxing of shell commands (process context only). When set,\n * every `exec` / `execStream` / `execBackground` runs `/bin/sh -c` under\n * macOS `sandbox-exec` or Linux `bwrap`: filesystem writes are limited to\n * `writableRoots`, credential directories are hidden, and network is\n * `allow`ed or `deny`ed as a whole. `writeFile` applies the same\n * `writableRoots` check in-process (reads are unrestricted — this is the\n * Claude Code / Codex boundary, not a full jail; use `workspaceRoot` for\n * read containment).\n *\n * Fails closed: when the backend is unavailable on the host, every command\n * returns exit 127 with the reason instead of running unsandboxed.\n *\n * Build the policy with `resolveWritableRoots` from `contexts/isolation`.\n */\n isolation?: IsolationPolicy\n /** Docker image (only for 'docker' context) */\n image?: string\n /** Docker container name prefix (only for 'docker' context) */\n name?: string\n /** Host paths mounted into the context (only for 'docker' context today) */\n mounts?: ContextMount[]\n /** Resource limits */\n limits?: {\n /** Memory limit in MB */\n memory?: number\n /** CPU limit (e.g. '1.0' = 1 core) */\n cpu?: string\n /** Timeout in seconds for the entire context lifetime */\n timeout?: number\n }\n /** Sandbox provider config (only for 'sandbox' context) */\n sandbox?: {\n provider: string\n apiKey?: string\n /**\n * Attach to a pre-existing sandbox by id rather than creating one\n * (provider-specific; E2B maps it to `Sandbox.connect`). The provider\n * leaves a connected sandbox running on teardown.\n */\n sandboxId?: string\n [key: string]: unknown\n }\n\n /**\n * How long `destroy()` waits for a SIGTERM'd background task to settle\n * before escalating to SIGKILL and abandoning the wait (process context\n * only). Bounds `agent.destroy()` against tasks that trap SIGTERM or\n * whose stdio pipes are held open by escaped grandchildren.\n *\n * Default: `5000`.\n */\n destroyGraceMs?: number\n\n /**\n * Publish container ports on the host (docker context only).\n *\n * Each entry maps a container port to either an explicit host port or\n * (when `host` is omitted) a Docker-assigned random port. Retrieve the\n * actual host port at runtime via `getMappedPort(container)` on the\n * docker context.\n */\n ports?: Array<{ container: number, host?: number, proto?: 'tcp' | 'udp' }>\n\n /**\n * UID/GID the container should run as (docker context only).\n *\n * Accepts the same forms Docker's `--user` does: `uid`, `uid:gid`, or a\n * named user that exists in the image. Default is the image's default\n * user (typically root). Setting this to the host user's `uid:gid`\n * avoids the EACCES-on-cleanup problem when sharing a workspace via\n * a `shared` mount.\n */\n user?: string\n\n /**\n * User-defined Docker network to join (docker context only).\n *\n * Defaults to Docker's default bridge. Use a user network when you\n * need multiple sibling containers (e.g. agent + database + dev\n * server) to discover each other by name.\n */\n network?: string\n\n /**\n * Docker labels to attach to the container (docker context only).\n *\n * Useful for ownership tracking — callers can sweep abandoned\n * containers (e.g. from a crashed parent process) by filtering on\n * a label they own: `docker ps -aq --filter label=my-app=true`.\n */\n labels?: Record<string, string>\n\n /**\n * Container hardening options (docker context only).\n *\n * All fields are opt-in and OFF by default to preserve existing behavior\n * (containers run with the image's default user/capabilities). Enable them\n * for untrusted workloads. `dropAllCapabilities` and a non-root `user`\n * (see {@link SpawnConfig.user}) can break images that expect root or\n * specific capabilities, so they are not applied unless requested.\n */\n hardening?: ContextHardening\n}\n\n/**\n * Opt-in container hardening for the docker context. Every field defaults to\n * \"unset\" so an omitted `hardening` (or omitted field) reproduces the prior,\n * unrestricted behavior — this keeps the option purely additive.\n */\nexport interface ContextHardening {\n /**\n * Drop all Linux capabilities (`CapDrop: ['ALL']`). Strong isolation, but\n * breaks images needing capabilities (e.g. binding low ports, `ping`).\n * Default: `false`.\n */\n dropAllCapabilities?: boolean\n /**\n * Set `no-new-privileges` so processes can't gain privileges via setuid\n * binaries. Low blast radius; safe for most workloads. Default: `false`.\n */\n noNewPrivileges?: boolean\n /**\n * Mount the container root filesystem read-only. Pair with writable mounts\n * for scratch space. Default: `false`.\n */\n readonlyRootfs?: boolean\n /**\n * Max number of processes (`PidsLimit`) — caps fork-bombs. Default: unset\n * (no limit). A few hundred is usually plenty for an agent sandbox.\n */\n pidsLimit?: number\n}\n\nexport interface ContextMount {\n /** Absolute host path to mount. */\n source: string\n /** Absolute path inside the execution context. */\n target: string\n /** Mount read-only. Defaults to false for Docker's native bind behavior. */\n readonly?: boolean\n /**\n * Apply the SELinux shared label (`:z`) so the host user and the container\n * user can both read/write the mount (docker context only). No-op on\n * non-SELinux hosts. Combine with `SpawnConfig.user` to avoid root-owned\n * files leaking onto the host. Mutually exclusive with `readonly`.\n */\n shared?: boolean\n}\n\n// ---------------------------------------------------------------------------\n// Execution context interface\n// ---------------------------------------------------------------------------\n\nexport type ContextType = 'process' | 'docker' | 'sandbox'\n\nexport interface ExecutionContext {\n /** Context type identifier */\n readonly type: ContextType\n\n /** What this context supports */\n readonly capabilities: ContextCapabilities\n\n /** Spawn a new execution environment */\n spawn: (config?: SpawnConfig) => Promise<ExecutionHandle>\n\n /**\n * Execute a shell command in the context.\n *\n * `signal` propagates abort all the way down to the underlying child\n * process — the implementation is expected to wire it into whatever\n * spawn primitive it uses so the OS receives a SIGTERM (or equivalent)\n * when the caller aborts. Without it, a cancelled tool returns its\n * cancellation marker to the model but the underlying process keeps\n * running in the background, orphaning compute / locks / IO. Pass\n * `ctx.signal` from a tool body to inherit the per-call + run-level\n * abort union; pass a freshly-built one for a host-driven kill.\n *\n * Implementations are free to ignore `signal` (the contract degrades\n * gracefully — the process simply won't be killed), but the in-process\n * default DOES honor it via `child_process.exec`'s native `signal`\n * option.\n */\n /**\n * Run a shell command. `timeout` is in SECONDS (default 30) — every\n * implementation converts internally (process ×1000 to ms, docker wraps\n * with coreutils `timeout`, sandbox SDKs take seconds/ms per their API).\n * Documented here because the unit is invisible at call sites and a\n * caller assuming milliseconds would set an instant-kill timeout.\n */\n exec: (handle: ExecutionHandle, command: string, options?: { cwd?: string, env?: Record<string, string>, timeout?: number, signal?: AbortSignal }) => Promise<ExecResult>\n\n /**\n * Start a process in the background. Settles as soon as `spawn` returns\n * — does NOT wait for the child to exit. Stdout + stderr stream\n * interleaved to the file at the returned `outputPath`. The caller\n * (typically the agent) reads incremental output via the regular\n * {@link ExecutionContext.readFile} seam.\n *\n * Optional — contexts without background support (some remote sandboxes)\n * just don't implement it. The shell tool surfaces a clean\n * \"background mode is not supported in this execution context\" error\n * when this is undefined.\n *\n * `onExit` is called once when the child terminates (natural, killed,\n * or error). The same instance that called `execBackground` is the\n * exclusive owner of the callback — it's not a multi-cast bus. Hosts\n * wire this to the agent's pending-notification queue so the model\n * gets a `<task-notification>` on its next turn.\n *\n * `onExit` is OPTIONAL: remote / durable contexts may have no legal\n * way to push a callback from a timer back into the host (a Restate\n * journal, for instance, forbids out-of-band writes). Such contexts\n * simply record the exit in their registry; the agent loop reconciles\n * by polling {@link ExecutionContext.listBackground} at run\n * boundaries, where a host journal wrapper is legal (the\n * `background:reconcile` hook is the journalable seam).\n *\n * See `docs/RUN_IN_BACKGROUND.md` for the broader design contract\n * (file location, replay semantics, suppression rules).\n */\n execBackground?: (\n handle: ExecutionHandle,\n command: string,\n options: {\n cwd?: string\n env?: Record<string, string>\n /**\n * Absolute directory the context appends `<task-id>.log` to. The\n * agent owns this path because it carries session-shaped knowledge\n * (`<userDir>/tasks/<sessionId>/`) the context can't synthesize.\n * Must already exist OR be creatable by the context — the\n * implementation handles `mkdir -p` defensively.\n */\n outputDir: string\n /** Push-style exit callback. Optional — see the method doc. */\n onExit?: (info: TaskExitInfo) => void\n /**\n * Cap on bytes written to the output file. Beyond the cap the\n * process KEEPS RUNNING; further output is counted but dropped,\n * and a `<output-truncated bytes-dropped=\"N\"/>` marker is\n * appended when the task settles. Unset / non-positive = no cap.\n */\n maxOutputBytes?: number\n /**\n * Stall watchdog: when the task produces no output for this many\n * milliseconds, `onStall` fires ONCE (one-shot — re-arms only\n * after fresh output arrives). The process is NOT killed; the\n * consumer decides (typically by telling the model the task may\n * be stuck at an interactive prompt). Unset = no watchdog.\n */\n stallTimeoutMs?: number\n /** One-shot stall callback — see `stallTimeoutMs`. */\n onStall?: (info: TaskStallInfo) => void\n /**\n * Run the task in a terminal — see {@link BackgroundTerminalOptions}.\n * Contexts without terminal support ignore it and use pipes; check\n * {@link TaskHandle.terminal} to know which one happened.\n */\n terminal?: BackgroundTerminalOptions\n },\n ) => Promise<TaskHandle>\n\n /**\n * Open a LIVE bidirectional byte stream to a long-lived process in the\n * context. The third execution mode, alongside the buffered `exec`\n * (request/response) and `execBackground` (fire-and-forget into a log\n * file): neither of those can carry a protocol that interleaves reads\n * and writes over the child's lifetime.\n *\n * Motivating consumer: stdio MCP servers. An MCP server is an ordinary\n * process doing `fs.readFileSync` / `fs.writeFile` against whatever\n * filesystem it was spawned on. Spawned on the host (the default — see\n * `McpServerConfig.scope`) it sees the HOST filesystem while the agent's\n * own tools see the context's, so the two disagree about what a path\n * means. Routing the server through this seam puts both on the same\n * filesystem.\n *\n * Optional — a context backed by a request/response-only API can't\n * implement it. Advertise support via `capabilities.stream`; callers\n * check that (or `execStream !== undefined`) and degrade with a clear\n * error rather than silently falling back to a host spawn, which would\n * reintroduce exactly the split this seam exists to close.\n *\n * IMPLEMENTOR CONTRACT — the shape is deliberately the SUPERSET of what\n * the backing APIs can do, because they do not agree:\n *\n * - `stdout` / `stderr` are byte streams, never pre-decoded strings.\n * A provider SDK that hands back `string` has already destroyed any\n * multibyte codepoint split across a chunk boundary; the caller\n * needs the raw bytes to run its own streaming decoder.\n * - `stderr` is OPTIONAL because a PTY-backed context physically\n * cannot separate it — one terminal, one stream. Such contexts\n * redirect the child's stderr to a file; they may then surface it\n * here out-of-band (the Daytona context polls that file back), but\n * they are not obliged to, so consumers must tolerate its absence.\n * - A PTY-backed context MUST also neutralise terminal semantics\n * before the child's first byte (echo of everything written to\n * stdin, `\\n`→`\\r\\n` rewriting, and the ~4096-byte canonical-mode\n * line cap that arbitrary protocol frames exceed). In practice:\n * launch through `stty raw -echo; exec <cmd> 2><file>`.\n * - `exited` resolves with the exit code. On a PTY the child is an\n * interactive shell that never exits on its own, so `kill()` is\n * the normal termination path, not an abnormal one.\n * - Providers that impose a default command timeout (E2B and Daytona\n * both default to 60s) MUST disable or maximise it here. A stream\n * opened by this method is expected to live for the whole session.\n */\n execStream?: (\n handle: ExecutionHandle,\n command: string,\n options?: {\n cwd?: string\n env?: Record<string, string>\n /** Aborts startup and kills the child once running. */\n signal?: AbortSignal\n },\n ) => Promise<StreamHandle>\n\n /**\n * Open an interactive terminal (a PTY running the user's login shell) in\n * the context: on the host, in the container, in the sandbox. For people,\n * not protocols: the opposite of {@link execStream}, which defeats every\n * terminal semantic this one keeps (echo, line editing, job control,\n * window size).\n *\n * Optional: a context without a PTY primitive (or a runtime without one:\n * the local contexts need Bun) leaves it undefined, and callers offer no\n * terminal rather than falling back to a host shell.\n */\n openTerminal?: (handle: ExecutionHandle, options: TerminalOptions) => Promise<TerminalProcess>\n\n /**\n * Re-attach to a terminal opened earlier, by its {@link TerminalProcess.id},\n * after the host lost the connection (daemon restart, paused sandbox).\n * Resolves `null` when it is gone. Only contexts whose terminals outlive\n * the host (remote sandboxes) implement it.\n */\n reconnectTerminal?: (handle: ExecutionHandle, id: string, options: TerminalOptions) => Promise<TerminalProcess | null>\n\n /**\n * SIGTERM the whole process group of a running background task.\n * Idempotent — second call returns `null` (or the cached exit info).\n * Resolves once the process has exited AND its output stream has\n * been flushed + closed.\n *\n * `null` return on miss (unknown id, already cleaned up) so the\n * shell_kill tool can surface a clean \"no such task\" message\n * without throwing.\n */\n killBackground?: (\n handle: ExecutionHandle,\n taskId: string,\n ) => Promise<TaskExitInfo | null>\n\n /**\n * Snapshot of every task in the context's registry — running AND\n * terminated (entries remain until the next context destroy, so\n * the model can still read output of exited tasks).\n */\n listBackground?: (\n handle: ExecutionHandle,\n ) => Promise<readonly TaskEntry[]>\n\n /**\n * Block until a background task terminates, then resolve with its\n * exit info. Resolves immediately for already-terminated tasks.\n *\n * Returns `null` when:\n * - the task id is unknown (or owned by another handle — same\n * isolation contract as `killBackground`), OR\n * - `timeoutMs` elapsed / `signal` aborted before the task exited.\n *\n * Callers that need to distinguish \"unknown\" from \"still running\"\n * should consult `listBackground` first (the `wait_task` tool does).\n *\n * This is the injectable wait seam for the `wait_task` tool:\n * in-process contexts implement it on the existing `onExit`\n * machinery; durable hosts implement it as an awakeable park\n * (runner fires task-exit over their bridge → resolveAwakeable).\n */\n waitBackground?: (\n handle: ExecutionHandle,\n taskId: string,\n options?: { timeoutMs?: number, signal?: AbortSignal },\n ) => Promise<TaskExitInfo | null>\n\n /**\n * Transfer ownership of every still-running task from `fromHandle`\n * to `toHandle`. Used by the spawn tool to \"promote\" a subagent's\n * background tasks up to the parent's handle so they outlive the\n * subagent's destroy() — matching shell semantics, where a `&`-ed\n * command outlives the parent process.\n *\n * With `fromHandle === toHandle` ownership stays put and only the\n * callbacks are rebound: a host that keeps one handle across agent\n * rebuilds (`chat/execution-cache.ts`) hands the running tasks to the\n * freshly built agent this way.\n *\n * Side effects:\n * - The task's `handleId` is rewritten, so subsequent\n * `listBackground(toHandle)` / `killBackground(toHandle, …)` /\n * `destroy(toHandle)` see it (and `fromHandle`-scoped operations\n * don't).\n * - When `newOnExit` / `newOnStall` are provided, the task's OWNER\n * callbacks are REPLACED with them. Critical: they were captured\n * against the spawning agent's hook bus, which is about to be\n * destroyed; without rewiring, the new owner never learns when the\n * task terminates or stalls. Pass closures that fire the new owner's\n * `background:exit` / `background:stall` hooks. In-flight\n * `killBackground` / `waitBackground` resolvers are not owner\n * callbacks and keep firing.\n *\n * Returns the entries that were actually reassigned (running tasks\n * only; terminated ones stay where they are). Implementations that\n * don't support reassignment can leave this undefined; the caller\n * (spawn.ts) falls back to the current behavior of killing child\n * tasks at subagent shutdown.\n */\n reassignBackgroundTasks?: (\n fromHandle: ExecutionHandle,\n toHandle: ExecutionHandle,\n newOnExit?: (info: TaskExitInfo) => void,\n newOnStall?: (info: TaskStallInfo) => void,\n ) => Promise<readonly TaskEntry[]>\n\n /** Read a file from the context's filesystem */\n readFile: (handle: ExecutionHandle, path: string) => Promise<string>\n\n /**\n * Read a file from the context's filesystem as raw bytes.\n *\n * Used by `read_file` to dispatch image / binary files into the multimodal\n * `ToolResultContent[]` route. Optional — when not implemented, the tool\n * falls back to `base64 < path` via the `exec` seam, which works in any\n * shell-capable context. Implementations that already have a native\n * binary read (in-process `fs.readFile` without encoding, container API,\n * sandbox SDK) should override for the latency win.\n */\n readFileBinary?: (handle: ExecutionHandle, path: string) => Promise<Uint8Array>\n\n /**\n * Cheap size probe for a file in the context's filesystem, in bytes.\n * `null` = unknowable (missing file, not a regular file, probe failed,\n * or containment rejected the path — a probe must not leak even the\n * size of files outside the workspace root).\n *\n * Exists because `readFile` has no partial-read form: tools that must\n * materialize a whole file (read_file's text path, edit/multi_edit,\n * grep's no-rg fallback) consult this first to refuse multi-GB targets\n * instead of OOMing the HOST — the read lands in host memory no matter\n * where the file lives. Per-context posture:\n *\n * - `process` — implements it (local stat, µs).\n * - `docker` — implements it via `wc -c` through its exec; its\n * `readFile` gets transport headroom above the tool ceiling so\n * the probe (tool policy), not the 10MB exec cap, decides read\n * limits — same experience as every other context.\n * - `sandbox` (and the e2b / daytona providers behind it) —\n * implements it via `wc -c` through the provider's exec: one extra\n * round-trip per whole-read tool call, paid so a multi-GB sandbox\n * file is refused BEFORE the SDK transfers it into host memory.\n *\n * KNOWN RESIDUAL (sandbox providers): this guards the `readFile` path\n * only. Their `exec` output is buffered inside the provider SDKs\n * (e2b `commands.run`, daytona `executeCommand`), which we cannot cap\n * from this side — a command that streams GBs to stdout within its\n * timeout window can still balloon host memory. Docker doesn't share\n * the residual (we own its transport; see `output-cap.ts`). A shell\n * wrapper (`| head -c`) was considered and rejected: preserving exit\n * codes, stderr separation, and timeout semantics through a pipe for\n * EVERY command is fragile, and the timeout already time-bounds the\n * exposure.\n */\n fileSize?: (handle: ExecutionHandle, path: string) => Promise<number | null>\n\n /**\n * Write a file to the context's filesystem.\n *\n * `encoding` applies only when `content` is a string (how its bytes are\n * produced; defaults to utf-8) and is ignored for a Buffer, which is\n * written verbatim.\n */\n writeFile: (handle: ExecutionHandle, path: string, content: string | Buffer, encoding?: BufferEncoding) => Promise<void>\n\n /**\n * List files in the context filesystem.\n *\n * Plain paths list immediate directory entries (legacy behavior). Paths that\n * contain glob metacharacters (`*`, `?`, `[]`, `{}`) request matched file\n * enumeration inside the target context. Contexts that cannot support\n * globbing should throw a clear error rather than silently returning no\n * matches. `metadata` is best-effort and only required when the context can\n * provide it without expensive per-file shell calls.\n */\n listFiles: (handle: ExecutionHandle, path: string, options?: ListFilesOptions) => Promise<ListFilesEntry[]>\n\n /**\n * Resolve the host-side port that a container port was published on\n * (docker context only, and only when the matching entry was created\n * with `host` omitted in `SpawnConfig.ports`).\n *\n * Resolves to `null` if the container port isn't published. Other\n * contexts (process, sandbox) don't implement this; they don't have\n * a port-mapping concept.\n */\n getMappedPort?: (handle: ExecutionHandle, containerPort: number) => Promise<number | null>\n\n /** Destroy the execution environment and clean up resources */\n destroy: (handle: ExecutionHandle) => Promise<void>\n}\n\n// ---------------------------------------------------------------------------\n// Background task types\n// ---------------------------------------------------------------------------\n\n/**\n * Lifecycle status of a background task.\n *\n * - `'running'` — process is still live; `exitCode` / `signal` unset.\n * - `'exited'` — process terminated on its own (clean or non-zero exit).\n * - `'killed'` — the host issued `killBackground` (SIGTERM to the group).\n *\n * The status is a coarse-grained signal; the exit code carries the\n * fine-grained detail (e.g. `143 = SIGTERM` for `'killed'`).\n */\nexport interface TerminalOptions {\n /** Working directory; defaults to the handle's cwd. */\n cwd?: string\n cols: number\n rows: number\n /** Extra environment on top of what the context's commands get. */\n env?: Record<string, string>\n /** Variables to drop from the inherited environment (local contexts). */\n unsetEnv?: readonly string[]\n /** Raw terminal output. Called synchronously; never slowed by the consumer. */\n onData: (data: Uint8Array) => void\n}\n\nexport interface TerminalProcess {\n /** Opaque id for {@link ExecutionContext.reconnectTerminal}. */\n id: string\n /** Whether {@link ExecutionContext.reconnectTerminal} can find it again later. */\n reconnectable: boolean\n /** Host pid of the shell, when it runs on this machine (for labels). */\n localPid?: number\n write: (data: string | Uint8Array) => void\n resize: (cols: number, rows: number) => void\n /** End the shell and everything it started. Idempotent. */\n kill: () => Promise<void>\n /** Let go without ending it (a remote PTY keeps running for a reconnect). */\n detach: () => Promise<void>\n /** Resolves once with the exit code; `-1` when the connection was lost instead. */\n exited: Promise<number>\n}\n\n/**\n * Terminal mode for {@link ExecutionContext.execBackground}: run the task in\n * a PTY instead of pipes, so it behaves as it would for a person (colors,\n * progress, tools that insist on a TTY) and can be watched and typed into.\n * The log file still receives the output, with terminal control sequences\n * removed, and the registry (kill, wait, list, reassign) is unchanged.\n */\nexport interface BackgroundTerminalOptions {\n cols: number\n rows: number\n /** Raw output as it arrives (the log gets the stripped text). */\n onData: (data: Uint8Array) => void\n /** Receives the functions to type into and resize the task's terminal. */\n onReady?: (io: { write: (data: string | Uint8Array) => void, resize: (cols: number, rows: number) => void }) => void\n}\n\nexport type BackgroundTaskStatus = 'running' | 'exited' | 'killed'\n\n/**\n * Returned synchronously by `execBackground` — the handle the model and\n * the framework use to refer to the task until it terminates.\n *\n * `outputPath` is an absolute path to the log file the context is\n * appending stdout + stderr to (interleaved by emit order). The model\n * reads it via the normal `read_file` tool; no special tool is required.\n */\nexport interface TaskHandle {\n /**\n * Stable id minted by the context — typically `bash_<n>` for\n * `ProcessContext`. Sequential within a single context instance,\n * resets when a new context is constructed. Forwarded to `killBackground` /\n * `listBackground` and stamped into every `<task-notification>` block.\n */\n taskId: string\n /** OS pid of the spawned shell wrapper (process-group leader on POSIX). */\n pid: number\n /** Absolute path to the log file the context is streaming output into. */\n outputPath: string\n /** True when the task runs in a terminal (`execBackground`'s `terminal` option was honored). */\n terminal?: boolean\n}\n\n/**\n * A live bidirectional stream to a process in the context. Returned by\n * {@link ExecutionContext.execStream}; see that method for the full\n * implementor contract (why the streams are bytes, why `stderr` is\n * optional, what PTY-backed contexts must do before the first byte).\n *\n * Ownership: the caller owns the lifetime. Nothing reaps this handle\n * automatically, so `kill()` belongs in the consumer's teardown path —\n * for MCP that is the transport's `close()`, reached via the agent's\n * `mcpConnection.close()` during `destroy()`.\n */\nexport interface StreamHandle {\n /** Process id inside the context — NOT meaningful on the host. */\n pid: number\n /**\n * Child's stdout as raw byte chunks, in emit order.\n *\n * CONSUMER OBLIGATION: drain this (and {@link stderr}) for the stream's\n * lifetime. Nothing here is bounded, and the consequence of ignoring it\n * varies by backend and by host runtime, none of it good:\n *\n * - `process` / `docker` hand back real OS pipes. Under Node an unread\n * stream stops draining the fd and the child BLOCKS once the pipe\n * fills (~64 KiB) — a deadlock, not a slowdown. Under Bun the runtime\n * buffers eagerly instead, so it presents as unbounded host memory\n * (measured: 32 MiB of unread stderr never blocked the child).\n * - `e2b` / `daytona` buffer in a `StreamQueue`, which grows without\n * limit. It cannot do otherwise: both SDKs push output through a\n * synchronous `(data) => void` callback, so there is no channel to\n * apply backpressure, and dropping bytes would desynchronise a framed\n * protocol permanently. The queue logs a one-shot warning once the\n * unread backlog passes 8 MiB so a stalled consumer is diagnosable.\n *\n * `createContextStdioTransport` satisfies this for both streams.\n */\n stdout: AsyncIterable<Uint8Array>\n /**\n * Child's stderr, when the implementation can provide it separately.\n * Present on pipe-backed contexts. A PTY-backed context cannot split it\n * from the terminal stream, so it redirects stderr to a file and either\n * surfaces it here out-of-band or leaves this unset — expect a lower\n * latency guarantee there than on stdout.\n */\n stderr?: AsyncIterable<Uint8Array>\n /** Write to the child's stdin. Resolves once the bytes are handed off. */\n write: (data: Uint8Array) => Promise<void>\n /** Terminate the child. Idempotent — killing a dead child is a no-op. */\n kill: () => Promise<void>\n /**\n * Resolves with the exit code when the child terminates, including\n * termination via {@link kill}. Never rejects.\n */\n exited: Promise<number>\n}\n\n/**\n * Fired exactly once per task when the child process terminates, via\n * `execBackground`'s `onExit` callback. The agent layer translates this\n * into a queued `<task-notification>` for the next turn.\n *\n * `signal` is set when the child was terminated by a signal (e.g.\n * SIGTERM from our own kill-tree, SIGKILL from oom-killer); absent on\n * natural exit. `exitCode` is `128 + signal-number` on signal-killed\n * children, matching POSIX shell conventions — `143` for SIGTERM, etc.\n */\nexport interface TaskExitInfo {\n taskId: string\n status: Exclude<BackgroundTaskStatus, 'running'>\n exitCode: number\n signal?: NodeJS.Signals\n outputPath: string\n /** `Date.now()` delta between spawn and exit. */\n durationMs: number\n /** The original command string the model invoked — useful for telemetry / banner summary. */\n command: string\n}\n\n/**\n * One row in `listBackground`'s snapshot. Living entries (status `'running'`)\n * have `exitCode` / `signal` / `endedAt` unset; terminated entries carry the\n * same data `TaskExitInfo` returned at exit time.\n */\nexport interface TaskEntry {\n taskId: string\n pid: number\n command: string\n cwd: string\n startedAt: number\n /**\n * Set when the task terminated. Lets pull-based consumers (the agent's\n * run-boundary reconcile) derive `durationMs = endedAt - startedAt`\n * without depending on when the snapshot was taken.\n */\n endedAt?: number\n outputPath: string\n status: BackgroundTaskStatus\n exitCode?: number\n signal?: NodeJS.Signals\n /** Total bytes written to the output file so far — useful for \"task X has produced N KB\" UX hints. */\n bytesWritten: number\n}\n\n/**\n * Fired by the optional stall watchdog (see `execBackground`'s\n * `stallTimeoutMs`) when a running task has produced no output for the\n * configured window. The process is still alive — this is a signal, not\n * a state transition.\n */\nexport interface TaskStallInfo {\n taskId: string\n command: string\n outputPath: string\n /** Milliseconds since the last output chunk (>= the configured window). */\n stalledForMs: number\n /** Total bytes the task has written so far. */\n bytesWritten: number\n}\n","/**\n * OS-level isolation for the process execution context.\n *\n * Wraps the model's shell commands in a kernel-enforced sandbox so writes\n * are confined to an explicit set of roots and credential directories are\n * unreadable. Two backends, one policy:\n *\n * - macOS: `/usr/bin/sandbox-exec` with a generated Seatbelt (SBPL) profile.\n * The profile is derived from Codex's `seatbelt_base_policy.sbpl` (itself\n * Chromium-derived), which is the most battle-tested deny-default profile\n * for developer tooling. Paths reach the profile through `-D KEY=value`\n * params (`(param \"KEY\")`) — never inlined — so no SBPL string escaping\n * is ever needed.\n * - Linux: `bwrap` (bubblewrap) with a read-only root bind, per-root\n * read-write binds, and an optional network namespace.\n *\n * Scope is the SUBPROCESS only — this is the same boundary Claude Code and\n * Codex draw. The agent process itself needs the network (provider APIs)\n * and its own state directory, so in-process tools are gated by the\n * process context's own path checks (`writableRoots` on `writeFile`), not\n * the kernel. Everything in here is pure / side-effect free except\n * `resolveWritableRoots` / `resolveWritableRootsAsync` (filesystem\n * `realpath` + one `git` probe) and `isolationAvailability` (a stat of the\n * backend binary).\n */\n\nimport type { ExecResult } from './types'\nimport { accessSync, constants, realpathSync, statSync } from 'node:fs'\nimport { homedir } from 'node:os'\nimport { delimiter, dirname, isAbsolute, join, relative, resolve } from 'node:path'\n\n// ---------------------------------------------------------------------------\n// Policy\n// ---------------------------------------------------------------------------\n\nexport interface IsolationPolicy {\n /**\n * Directories the sandboxed command may write to (create, modify,\n * delete). Absolute paths; callers should pass them through\n * {@link resolveWritableRoots} so symlinked locations (`/tmp` →\n * `/private/tmp` on macOS) match what the kernel sees.\n */\n writableRoots: string[]\n /**\n * `'allow'` = unrestricted network (the default: `bun install`, `git\n * fetch`, `gh` keep working). `'deny'` = no sockets at all. Without network\n * isolation a compromised command can still exfiltrate what it can read —\n * the setting description says so.\n */\n network: 'allow' | 'deny'\n /**\n * Directories hidden from the sandboxed command (read AND write denied).\n * Defaults to {@link DEFAULT_UNREADABLE_DIRS} when omitted.\n */\n unreadableDirs?: string[]\n}\n\nexport type IsolationBackend = 'seatbelt' | 'bwrap'\n\n/**\n * Only `/usr/bin/sandbox-exec` is trusted — never a `PATH` lookup — so a\n * tampered binary earlier in `PATH` cannot silently replace the sandbox.\n */\nexport const SEATBELT_EXECUTABLE = '/usr/bin/sandbox-exec'\n\n/**\n * Credential material hidden from sandboxed commands by default. Relative\n * to `$HOME`; resolved at policy-build time. Covers the usual suspects plus\n * zidane's own OAuth / API-key store.\n */\nexport const DEFAULT_UNREADABLE_DIRS: readonly string[] = [\n '.ssh',\n '.aws',\n '.gnupg',\n '.azure',\n '.config/gcloud',\n '.kube',\n '.docker/config.json',\n '.netrc',\n // zidane's own stores at the DEFAULT data dir. Hosts that know the real\n // data dir (`ZIDANE_STORAGE_DIR` moves it) pass the resolved paths via\n // `IsolationPolicy.unreadableDirs`; these stay as a fallback.\n '.zidane/credentials.json',\n '.zidane/mcp-credentials.json',\n '.credentials.json',\n]\n\n// ---------------------------------------------------------------------------\n// Backend detection\n// ---------------------------------------------------------------------------\n\n/** Backend for `platform` (defaults to the current process). `null` = unsupported OS. */\nexport function detectIsolationBackend(platform: NodeJS.Platform = process.platform): IsolationBackend | null {\n if (platform === 'darwin')\n return 'seatbelt'\n if (platform === 'linux')\n return 'bwrap'\n return null\n}\n\nexport type IsolationAvailability = { ok: true, backend: IsolationBackend, executable: string } | { ok: false, reason: string }\n\nfunction isExecutable(path: string): boolean {\n try {\n accessSync(path, constants.X_OK)\n return true\n }\n catch {\n return false\n }\n}\n\nfunction whichSync(name: string, env: NodeJS.ProcessEnv = process.env): string | null {\n for (const dir of (env.PATH ?? '').split(delimiter)) {\n if (!dir)\n continue\n const candidate = join(dir, name)\n if (isExecutable(candidate))\n return candidate\n }\n return null\n}\n\n/**\n * Whether the sandbox can actually be enforced on this host. Callers that\n * requested isolation and get `ok: false` MUST fail closed (refuse to run\n * the command) — silently running unsandboxed would turn a security\n * setting into a placebo.\n */\nexport function isolationAvailability(\n platform: NodeJS.Platform = process.platform,\n env: NodeJS.ProcessEnv = process.env,\n): IsolationAvailability {\n const backend = detectIsolationBackend(platform)\n if (backend === null)\n return { ok: false, reason: `OS sandboxing is not supported on ${platform} (macOS Seatbelt and Linux bwrap only)` }\n if (backend === 'seatbelt') {\n return isExecutable(SEATBELT_EXECUTABLE)\n ? { ok: true, backend, executable: SEATBELT_EXECUTABLE }\n : { ok: false, reason: `${SEATBELT_EXECUTABLE} is missing or not executable` }\n }\n const bwrap = whichSync('bwrap', env)\n return bwrap\n ? { ok: true, backend, executable: bwrap }\n : { ok: false, reason: 'bwrap (bubblewrap) is not installed. Install it with your package manager (e.g. `apt install bubblewrap`)' }\n}\n\n// ---------------------------------------------------------------------------\n// Writable roots\n// ---------------------------------------------------------------------------\n\n/** Expand a leading `~` / `~/` to `home`. Other paths returned unchanged. */\nexport function expandHome(path: string, home: string = homedir()): string {\n if (path === '~')\n return home\n if (path.startsWith('~/'))\n return join(home, path.slice(2))\n return path\n}\n\n/**\n * Parse the user-facing \"extra writable paths\" setting: comma- or\n * newline-separated, `~` expanded, relative entries resolved against `cwd`,\n * blanks dropped.\n */\nexport function parseAllowWriteSetting(raw: string, cwd: string, home: string = homedir()): string[] {\n return raw\n .split(/[,\\n]/)\n .map(s => s.trim())\n .filter(Boolean)\n .map(p => resolve(cwd, expandHome(p, home)))\n}\n\n/**\n * `realpath` that also works for paths that don't exist yet (an `extra`\n * root the user hasn't created): the deepest existing ancestor is\n * canonicalized and the missing tail re-appended. Without this, `/tmp/x`\n * and `/private/tmp/x` would look like different roots on macOS.\n */\nexport function canonical(path: string): string {\n let existing = resolve(path)\n const tail: string[] = []\n while (true) {\n try {\n existing = realpathSync(existing)\n break\n }\n catch {\n const parent = dirname(existing)\n if (parent === existing)\n break\n tail.unshift(relative(parent, existing))\n existing = parent\n }\n }\n return tail.length ? resolve(existing, ...tail) : existing\n}\n\n/** True when `target` is `root` or lives under it. */\nexport function isWithinRoot(root: string, target: string): boolean {\n const rel = relative(root, target)\n return rel === '' || (!rel.startsWith('..') && !isAbsolute(rel))\n}\n\n/** Drop roots already covered by another root in the list; keeps first-seen order. */\nexport function dedupeRoots(roots: string[]): string[] {\n const out: string[] = []\n for (const root of roots) {\n if (out.some(existing => isWithinRoot(existing, root)))\n continue\n // A broader root later in the list subsumes earlier narrower ones.\n for (let i = out.length - 1; i >= 0; i--) {\n if (isWithinRoot(root, out[i]!))\n out.splice(i, 1)\n }\n out.push(root)\n }\n return out\n}\n\nexport interface ResolveWritableRootsOptions {\n /** Extra roots from user settings (already absolute; see {@link parseAllowWriteSetting}). */\n extra?: string[]\n env?: NodeJS.ProcessEnv\n platform?: NodeJS.Platform\n /**\n * Resolves git's common dir for `cwd` (the `.git` directory a worktree\n * points at). Injected so the pure part is testable; the default runs\n * `git rev-parse --git-common-dir`. Return `null` when not a repo.\n */\n gitCommonDir?: (cwd: string) => string | null\n}\n\n/**\n * Why git's common dir matters: `git rev-parse --git-common-dir` prints a\n * path relative to cwd for the primary checkout (`.git`) and an absolute\n * path for linked worktrees. Without the worktree's common dir writable,\n * `git commit` / `git add` inside a worktree fail under the sandbox —\n * the index and objects live in the main repo's `.git`.\n *\n * Two probes, one contract (`null` on any failure, including no `git`\n * binary, a non-repo, or a slow git — a wedged git must not wedge the\n * host, so slow reads as \"not a worktree\"):\n * - {@link defaultGitCommonDir}: synchronous, for the in-process TUI whose\n * `buildAgent` is sync. Blocks that window only; 1 s cap.\n * - {@link defaultGitCommonDirAsync}: for the daemon, where a sync probe\n * would stall every window and session on the shared event loop.\n *\n * `node:child_process` is resolved through `process.getBuiltinModule` (not a\n * static import) for the same reason `process.ts` lazy-loads `spawn`: this\n * module sits in the default execution context's import graph, and merely\n * importing zidane must stay possible on shell-less runtimes (edge isolates)\n * where that builtin is gated. Node 20.16+ / Bun; `undefined` → `null`.\n */\nconst GIT_PROBE_ARGS = ['rev-parse', '--git-common-dir']\nconst GIT_PROBE_SYNC_TIMEOUT_MS = 1_000\nconst GIT_PROBE_ASYNC_TIMEOUT_MS = 3_000\n\nfunction childProcess(): typeof import('node:child_process') | undefined {\n return process.getBuiltinModule?.('node:child_process') as typeof import('node:child_process') | undefined\n}\n\nexport function defaultGitCommonDir(cwd: string): string | null {\n try {\n const cp = childProcess()\n if (!cp)\n return null\n const out = cp.execFileSync('git', GIT_PROBE_ARGS, {\n cwd,\n timeout: GIT_PROBE_SYNC_TIMEOUT_MS,\n encoding: 'utf8',\n stdio: ['ignore', 'pipe', 'ignore'],\n }).trim()\n return out ? resolve(cwd, out) : null\n }\n catch {\n return null\n }\n}\n\nexport function defaultGitCommonDirAsync(cwd: string): Promise<string | null> {\n const cp = childProcess()\n if (!cp)\n return Promise.resolve(null)\n return new Promise((resolveP) => {\n try {\n cp.execFile('git', GIT_PROBE_ARGS, {\n cwd,\n timeout: GIT_PROBE_ASYNC_TIMEOUT_MS,\n encoding: 'utf8',\n }, (err, stdout) => {\n if (err)\n return resolveP(null)\n const out = stdout.trim()\n resolveP(out ? resolve(cwd, out) : null)\n })\n }\n catch {\n resolveP(null)\n }\n })\n}\n\n/**\n * The set of directories a sandboxed command may write to: the workspace\n * (`cwd`), the system temp dirs, git's common dir when `cwd` is a linked\n * worktree, plus any user-configured extras. All canonicalized.\n */\nexport function resolveWritableRoots(cwd: string, options: ResolveWritableRootsOptions = {}): string[] {\n const gitCommonDir = options.gitCommonDir ?? defaultGitCommonDir\n return assembleWritableRoots(cwd, gitCommonDir(cwd), options)\n}\n\n/** Async twin of {@link resolveWritableRoots} for hosts that must not block (the daemon). */\nexport async function resolveWritableRootsAsync(\n cwd: string,\n options: Omit<ResolveWritableRootsOptions, 'gitCommonDir'> & { gitCommonDir?: (cwd: string) => Promise<string | null> } = {},\n): Promise<string[]> {\n const gitCommonDir = options.gitCommonDir ?? defaultGitCommonDirAsync\n return assembleWritableRoots(cwd, await gitCommonDir(cwd), options)\n}\n\nfunction assembleWritableRoots(cwd: string, gitCommonDir: string | null, options: Omit<ResolveWritableRootsOptions, 'gitCommonDir'>): string[] {\n const env = options.env ?? process.env\n const platform = options.platform ?? process.platform\n\n const raw: string[] = [cwd]\n if (env.TMPDIR)\n raw.push(env.TMPDIR)\n raw.push('/tmp', '/var/tmp')\n if (platform === 'darwin')\n raw.push('/private/tmp', '/private/var/tmp')\n\n if (gitCommonDir)\n raw.push(gitCommonDir)\n\n raw.push(...(options.extra ?? []))\n\n // A git common dir INSIDE cwd (the normal, non-worktree case) is\n // subsumed by cwd via dedupe; only linked worktrees add a root.\n return dedupeRoots(raw.map(canonical))\n}\n\n/** Resolve {@link DEFAULT_UNREADABLE_DIRS} (or a custom list) against `home`. */\nexport function resolveUnreadableDirs(dirs: readonly string[] = DEFAULT_UNREADABLE_DIRS, home: string = homedir()): string[] {\n return dirs.map(d => (isAbsolute(d) ? d : join(home, d)))\n}\n\n// ---------------------------------------------------------------------------\n// Seatbelt (macOS)\n// ---------------------------------------------------------------------------\n\n/**\n * Deny-default base. Derived from Codex's `seatbelt_base_policy.sbpl`\n * (Apache-2.0), itself from Chromium's renderer/common sandbox policies.\n * Filesystem READ is allowed broadly (below); WRITE and NETWORK are added\n * per policy in {@link buildSeatbeltProfile}.\n */\nexport const SEATBELT_BASE_POLICY = `(version 1)\n\n; deny-default; everything below is an explicit allow\n(deny default)\n\n; child processes inherit the policy of their parent\n(allow process-exec)\n(allow process-fork)\n(allow signal (target same-sandbox))\n(allow process-info* (target same-sandbox))\n\n; reads are allowed everywhere except the unreadable roots. SBPL is\n; LAST-MATCH-WINS: the deny block for those roots is emitted at the very\n; end of the profile by buildSeatbeltProfile and must stay there.\n(allow file-read*)\n(allow file-write* (literal \"/dev/null\"))\n(allow file-read* file-write* (literal \"/dev/stdin\"))\n(allow file-read* file-write* (literal \"/dev/stdout\"))\n(allow file-read* file-write* (literal \"/dev/stderr\"))\n(allow file-read* file-write* (literal \"/dev/tty\"))\n(allow file-read* file-write* (literal \"/dev/random\"))\n(allow file-read* file-write* (literal \"/dev/urandom\"))\n(allow file-read* file-write* (literal \"/dev/zero\"))\n(allow file-ioctl (literal \"/dev/dtracehelper\"))\n(allow file-read* file-write* (literal \"/dev/dtracehelper\"))\n\n; sysctls\n(allow sysctl-read\n (sysctl-name \"hw.activecpu\")\n (sysctl-name \"hw.busfrequency_compat\")\n (sysctl-name \"hw.byteorder\")\n (sysctl-name \"hw.cacheconfig\")\n (sysctl-name \"hw.cachelinesize_compat\")\n (sysctl-name \"hw.cpufamily\")\n (sysctl-name \"hw.cpufrequency_compat\")\n (sysctl-name \"hw.cputype\")\n (sysctl-name \"hw.l1dcachesize_compat\")\n (sysctl-name \"hw.l1icachesize_compat\")\n (sysctl-name \"hw.l2cachesize_compat\")\n (sysctl-name \"hw.l3cachesize_compat\")\n (sysctl-name \"hw.logicalcpu_max\")\n (sysctl-name \"hw.machine\")\n (sysctl-name \"hw.model\")\n (sysctl-name \"hw.memsize\")\n (sysctl-name \"hw.ncpu\")\n (sysctl-name \"hw.nperflevels\")\n (sysctl-name-prefix \"hw.optional.arm.\")\n (sysctl-name-prefix \"hw.optional.armv8_\")\n (sysctl-name \"hw.packages\")\n (sysctl-name \"hw.pagesize_compat\")\n (sysctl-name \"hw.pagesize\")\n (sysctl-name \"hw.physicalcpu\")\n (sysctl-name \"hw.physicalcpu_max\")\n (sysctl-name \"hw.logicalcpu\")\n (sysctl-name \"hw.cpufrequency\")\n (sysctl-name \"hw.tbfrequency_compat\")\n (sysctl-name \"hw.vectorunit\")\n (sysctl-name \"machdep.cpu.brand_string\")\n (sysctl-name \"kern.argmax\")\n (sysctl-name \"kern.hostname\")\n (sysctl-name \"kern.maxfilesperproc\")\n (sysctl-name \"kern.maxproc\")\n (sysctl-name \"kern.osproductversion\")\n (sysctl-name \"kern.osrelease\")\n (sysctl-name \"kern.ostype\")\n (sysctl-name \"kern.osvariant_status\")\n (sysctl-name \"kern.osversion\")\n (sysctl-name \"kern.secure_kernel\")\n (sysctl-name \"kern.sysv.semmns\")\n (sysctl-name \"kern.usrstack64\")\n (sysctl-name \"kern.version\")\n (sysctl-name \"sysctl.proc_cputype\")\n (sysctl-name \"vm.loadavg\")\n (sysctl-name-prefix \"hw.perflevel\")\n (sysctl-name-prefix \"kern.proc.pgrp.\")\n (sysctl-name-prefix \"kern.proc.pid.\")\n (sysctl-name-prefix \"net.routetable.\")\n)\n; Java CPU probe (a read that the kernel classifies as a write)\n(allow sysctl-write (sysctl-name \"kern.grade_cputype\"))\n\n(allow iokit-open (iokit-registry-entry-class \"RootDomainUserClient\"))\n\n; user lookup (getpwuid & co)\n(allow mach-lookup (global-name \"com.apple.system.opendirectoryd.libinfo\"))\n(allow mach-lookup (global-name \"com.apple.PowerManagement.control\"))\n\n; python multiprocessing / OpenMP\n(allow ipc-posix-sem)\n(allow ipc-posix-shm-read-data\n ipc-posix-shm-write-create\n ipc-posix-shm-write-unlink\n (ipc-posix-name-regex #\"^/__KMP_REGISTERED_LIB_[0-9]+$\"))\n\n; openpty()\n(allow pseudo-tty)\n(allow file-read* file-write* file-ioctl (literal \"/dev/ptmx\"))\n(allow file-read* file-write*\n (require-all\n (regex #\"^/dev/ttys[0-9]+\")\n (extension \"com.apple.sandbox.pty\")))\n(allow file-ioctl (regex #\"^/dev/ttys[0-9]+\"))\n`\n\n/**\n * Network allow block. Mirrors Codex's `seatbelt_network_policy.sbpl`: full\n * outbound/inbound plus the mach services TLS + DNS resolution need.\n */\nexport const SEATBELT_NETWORK_POLICY = `\n; ---- network: allow ----\n(allow network-outbound)\n(allow network-inbound)\n(allow network-bind)\n(allow system-socket\n (require-all\n (socket-domain AF_SYSTEM)\n (socket-protocol 2)))\n(allow mach-lookup\n (global-name \"com.apple.bsd.dirhelper\")\n (global-name \"com.apple.system.opendirectoryd.membership\")\n (global-name \"com.apple.SecurityServer\")\n (global-name \"com.apple.networkd\")\n (global-name \"com.apple.ocspd\")\n (global-name \"com.apple.trustd.agent\")\n (global-name \"com.apple.SystemConfiguration.DNSConfiguration\")\n (global-name \"com.apple.SystemConfiguration.configd\"))\n(allow sysctl-read (sysctl-name-regex #\"^net.routetable\"))\n`\n\nexport const WRITABLE_ROOT_PARAM = 'WRITABLE_ROOT_'\nexport const UNREADABLE_ROOT_PARAM = 'UNREADABLE_ROOT_'\n\nexport interface SeatbeltProfile {\n /** Full SBPL text for `sandbox-exec -p`. */\n profile: string\n /** `KEY=value` pairs for `-D`, one per path referenced through `(param \"KEY\")`. */\n params: Array<[key: string, value: string]>\n}\n\n/**\n * Build the SBPL profile + params for a policy. Paths are NEVER inlined in\n * the profile text: each is bound to a `-D` param and referenced via\n * `(param \"…\")`, so spaces, quotes and parentheses in paths need no\n * escaping and the profile text is identical across machines (only the\n * params differ) — which makes it unit-testable as a string.\n *\n * ORDERING IS A SECURITY INVARIANT. Seatbelt resolves overlapping rules by\n * position — the LAST matching rule wins; there is no deny-over-allow\n * precedence. The credential mask (`deny file-read*` on the unreadable\n * roots) only holds because it is the final block, after `(allow\n * file-read*)` in the base policy, the writable-root allows and the\n * network block. Never hoist or reorder it; the profile test asserts every\n * `(deny` line follows every `(allow` line.\n */\nexport function buildSeatbeltProfile(policy: IsolationPolicy, home: string = homedir()): SeatbeltProfile {\n const params: Array<[string, string]> = []\n const lines: string[] = [SEATBELT_BASE_POLICY]\n\n lines.push('; ---- writable roots ----')\n policy.writableRoots.forEach((root, i) => {\n const key = `${WRITABLE_ROOT_PARAM}${i}`\n params.push([key, root])\n lines.push(`(allow file-write* (subpath (param \"${key}\")))`)\n })\n\n if (policy.network === 'allow')\n lines.push(SEATBELT_NETWORK_POLICY)\n else\n lines.push('; ---- network: deny (default deny covers all socket ops) ----')\n\n // LAST on purpose — see the ordering note above.\n const unreadable = resolveUnreadableDirs(policy.unreadableDirs, home)\n if (unreadable.length > 0) {\n lines.push('; ---- unreadable roots (must be the final block: last match wins) ----')\n unreadable.forEach((dir, i) => {\n const key = `${UNREADABLE_ROOT_PARAM}${i}`\n params.push([key, dir])\n lines.push(`(deny file-read* file-write* (subpath (param \"${key}\")))`)\n })\n }\n\n return { profile: `${lines.join('\\n')}\\n`, params }\n}\n\n// ---------------------------------------------------------------------------\n// bwrap (Linux)\n// ---------------------------------------------------------------------------\n\n/**\n * bwrap argv for a policy (without the trailing command). The whole host FS\n * is bound read-only, writable roots are re-bound read-write on top, and\n * unreadable dirs are masked with an empty tmpfs. `--die-with-parent` ties\n * the sandbox to the zidane process; no `--new-session`/`--unshare-pid` so\n * the child stays in the caller's process group (the process context kills\n * by `-pgid`) and `ps`/`kill` inside the sandbox behave as usual.\n */\nexport function buildBwrapArgs(\n policy: IsolationPolicy,\n home: string = homedir(),\n kind: (path: string) => 'dir' | 'file' | null = statKind,\n): string[] {\n const args = [\n '--ro-bind',\n '/',\n '/',\n '--dev',\n '/dev',\n '--proc',\n '/proc',\n '--die-with-parent',\n ]\n for (const root of policy.writableRoots)\n args.push('--bind-try', root, root)\n // Masks need an existing mount point (the root is read-only, so bwrap\n // cannot create one); a missing path is already unreadable, skip it.\n for (const path of resolveUnreadableDirs(policy.unreadableDirs, home)) {\n const k = kind(path)\n if (k === 'dir')\n args.push('--tmpfs', path)\n else if (k === 'file')\n args.push('--ro-bind', '/dev/null', path)\n }\n if (policy.network === 'deny')\n args.push('--unshare-net')\n return args\n}\n\nfunction statKind(path: string): 'dir' | 'file' | null {\n try {\n return statSync(path).isDirectory() ? 'dir' : 'file'\n }\n catch {\n return null\n }\n}\n\n// ---------------------------------------------------------------------------\n// Command wrapping\n// ---------------------------------------------------------------------------\n\nexport interface WrappedCommand {\n file: string\n args: string[]\n}\n\n/**\n * Produce the `{ file, args }` that runs `/bin/sh -c command` under the\n * sandbox. `home` is injectable for tests.\n */\nexport function wrapCommand(\n backend: IsolationBackend,\n executable: string,\n policy: IsolationPolicy,\n command: string,\n home: string = homedir(),\n): WrappedCommand {\n if (backend === 'seatbelt') {\n const { profile, params } = buildSeatbeltProfile(policy, home)\n const args = ['-p', profile]\n for (const [key, value] of params)\n args.push(`-D${key}=${value}`)\n args.push('--', '/bin/sh', '-c', command)\n return { file: executable, args }\n }\n return { file: executable, args: [...buildBwrapArgs(policy, home), '--', '/bin/sh', '-c', command] }\n}\n\n// ---------------------------------------------------------------------------\n// Denial hint\n// ---------------------------------------------------------------------------\n\n/**\n * Patterns a sandboxed command emits when the kernel refuses an operation.\n * Seatbelt surfaces `Operation not permitted` (EPERM); bwrap's read-only\n * root yields `Read-only file system` (EROFS); an unshared netns gives\n * DNS / connect failures that vary by tool, so only the two FS ones are\n * matched — network denial is reported through `network: deny` text.\n */\nconst DENIAL_RE = /Operation not permitted|Read-only file system|sandbox-exec: |deny\\(1\\)|bwrap: /i\n\n/**\n * Append a one-line explanation to a failed result when the failure looks\n * like a sandbox denial. The model otherwise sees a bare EPERM and guesses;\n * this names the mechanism and the exact setting that widens it.\n */\nexport function annotateDenial(result: ExecResult, policy: IsolationPolicy): ExecResult {\n if (result.exitCode === 0 || !DENIAL_RE.test(result.stderr))\n return result\n const roots = policy.writableRoots.join(', ')\n const net = policy.network === 'deny' ? '; network is blocked' : ''\n const hint = `[sandbox] this command ran under zidane's OS sandbox: writes are limited to ${roots}${net}. `\n + 'If the failure is a sandbox denial, ask the user to add the path under Settings → \"Sandbox writable paths\" or turn off \"Sandbox shell commands\".'\n return { ...result, stderr: result.stderr ? `${result.stderr}\\n${hint}` : hint }\n}\n\n/** Result returned for every command when isolation was requested but cannot be enforced. */\nexport function unavailableResult(reason: string): ExecResult {\n return {\n stdout: '',\n stderr: `[sandbox] \"Sandbox shell commands\" is on but cannot be enforced: ${reason}. `\n + 'Refusing to run unsandboxed — ask the user to fix the sandbox or turn the setting off.',\n exitCode: 127,\n }\n}\n","/**\n * Shared state + task-registry primitives for the in-process execution\n * context (`createProcessContext`). Internal module.\n */\n\nimport type { ChildProcess } from 'node:child_process'\nimport type { WriteStream } from 'node:fs'\nimport type { ExecutionHandle, SpawnConfig, TaskEntry, TaskExitInfo, TaskStallInfo } from './types'\nimport { resolve } from 'node:path'\nimport { lazyAsync } from '../lazy'\nimport { isolationAvailability } from './isolation'\n\n/**\n * Lazily load `node:child_process`'s `spawn` (memoized after first use).\n *\n * Deliberately NOT a static top-level import. `createAgent` pulls this module\n * into the graph as the default execution context, so a static\n * `import … from 'node:child_process'` would make merely importing zidane\n * depend on subprocess support. Runtimes that gate that builtin at import time\n * — Supabase Edge Functions, Cloudflare `nodejs_compat`, other shell-less V8\n * isolates — would then fail to load the package at all. Deferring the import\n * to the first `exec` keeps `import 'zidane'` + the provider loop runnable\n * there; only actually invoking a shell command touches subprocess, which is\n * exactly where an unsupported host should fail (the agent loop turns that\n * throw into a clean tool-result error).\n */\nexport const loadSpawn = lazyAsync(async () => (await import('node:child_process')).spawn)\n\n/**\n * Whether the host supports POSIX process groups (the `detached: true` +\n * `process.kill(-pid)` combination). Windows doesn't — its job-object\n * model is shaped differently — so on win32 we fall back to killing the\n * shell wrapper alone (matches pre-fix behavior; better than nothing).\n */\nexport const SUPPORTS_PROCESS_GROUPS = process.platform !== 'win32'\n\n/**\n * Default cap on captured stdout / stderr per child. Matches the\n * pre-fix `child_process.exec` setting so existing callers see the\n * same buffer envelope. Output beyond this is truncated and a\n * marker is appended to stderr.\n */\n\n/**\n * How long `destroy()` waits for a SIGTERM'd background task to settle\n * before escalating to SIGKILL and abandoning the wait. Matches the MCP\n * connection's default `closeTimeout` so `agent.destroy()` stays bounded\n * by a single grace period regardless of which leg is slowest.\n */\nexport const DESTROY_SIGTERM_GRACE_MS = 5_000\n\n/**\n * Background tasks on a PTY see a terminal where they used to see pipes:\n * keep pagers and git's credential prompt from waiting on input nobody\n * types. The command's own env still wins.\n */\nexport const TERMINAL_TASK_ENV: Readonly<Record<string, string>> = {\n PAGER: 'cat',\n GIT_PAGER: 'cat',\n GIT_TERMINAL_PROMPT: '0',\n}\n\n/**\n * How long an `exec` kill (timeout / abort / output-overflow) waits after\n * SIGTERM before escalating the process group to SIGKILL. A child that\n * traps or ignores TERM (docker CLI parked on a container, a shell trap,\n * an uninterruptible wait) otherwise never exits — the exec promise never\n * settles and the caller's transport deadline fires instead of a clean\n * `command timed out` result (prod incident 2026-07-17).\n */\nexport const EXEC_KILL_ESCALATION_GRACE_MS = 5_000\n\n/**\n * How long after the child's `exit` event we keep waiting for `close`\n * before settling with the output collected so far.\n *\n * `close` fires only once every stdio pipe has closed — and a\n * daemonized grandchild that inherited stdout/stderr (a dev server\n * started with `&`, `nohup` without redirection, simulator helpers, …)\n * keeps those pipes open indefinitely. Pre-fix, a foreground `exec`\n * whose command finished in milliseconds would hang until its timeout,\n * then `killProcessGroup` took the intentionally-backgrounded daemon\n * down with it. The process itself is done at `exit`; this grace only\n * exists to let normally-closing pipes flush their tail bytes.\n */\nexport const EXIT_PIPE_DRAIN_GRACE_MS = 1_500\n\n/**\n * Sanitize a task id before it's joined into a filesystem path.\n *\n * We mint `bash_<n>` ids ourselves (no user input flows into the path\n * for `ProcessContext`), so this is defensive — but third-party contexts\n * MAY accept caller-provided ids, so the helper exists for them too.\n * Anything that doesn't match the expected shape is rejected — never\n * coerced — so the call site sees a clear error rather than a\n * traversal-shaped path.\n */\nconst TASK_ID_RE = /^[a-z][\\w-]*$/i\n\nexport function assertSafeTaskId(taskId: string): void {\n if (!TASK_ID_RE.test(taskId))\n throw new Error(`Invalid task id \"${taskId}\": it must match ${TASK_ID_RE}.`)\n}\n\n/**\n * Format `date` as `YYYYMMDD-HHMMSS-mmm` in UTC.\n *\n * Pinned to UTC so the lexical sort of two timestamps always matches\n * their chronological order (local time + DST does not). Used as the\n * per-context suffix on background-task log filenames; see the field\n * doc on `contextTimestamp` for the why.\n */\nexport function formatContextTimestamp(date: Date): string {\n const pad2 = (n: number): string => n.toString().padStart(2, '0')\n const pad3 = (n: number): string => n.toString().padStart(3, '0')\n const y = date.getUTCFullYear()\n const M = pad2(date.getUTCMonth() + 1)\n const d = pad2(date.getUTCDate())\n const h = pad2(date.getUTCHours())\n const m = pad2(date.getUTCMinutes())\n const s = pad2(date.getUTCSeconds())\n const ms = pad3(date.getUTCMilliseconds())\n return `${y}${M}${d}-${h}${m}${s}-${ms}`\n}\n\n/** Pattern of a background-task log filename. Used by tests + tooling. */\nexport const TASK_LOG_FILENAME_RE = /^(bash_\\d+)\\.(\\d{8}-\\d{6}-\\d{3})\\.log$/\n\n/** Copy only the named keys (when present) from an env object. */\nfunction pickEnv(env: NodeJS.ProcessEnv, keys: string[]): NodeJS.ProcessEnv {\n const out: NodeJS.ProcessEnv = {}\n for (const key of keys) {\n if (env[key] !== undefined)\n out[key] = env[key]\n }\n return out\n}\n\n/**\n * Mutable per-context state of `createProcessContext`, shared by the method\n * modules. Scalars that change after construction (`counter`, `taskCounter`,\n * `exitHandlerRegistered`) are read through this object so reads stay live.\n */\nexport interface ProcessContextState {\n config: SpawnConfig | undefined\n counter: number\n handles: Map<string, ExecutionHandle>\n defaultCwd: string\n defaultEnv: Record<string, string> | undefined\n destroyGraceMs: number\n baseEnv: NodeJS.ProcessEnv\n workspaceRoot: string | undefined\n isolation: SpawnConfig['isolation']\n isolationState: ReturnType<typeof isolationAvailability> | undefined\n tasks: Map<string, TaskState>\n taskCounter: number\n contextTimestamp: string\n streamChildren: Map<ChildProcess, string>\n exitHandlerRegistered: boolean\n exitHandler: () => void\n registerExitHandler: () => void\n}\n\nexport function createProcessState(config?: SpawnConfig): ProcessContextState {\n const handles = new Map<string, ExecutionHandle>()\n const defaultCwd = config?.cwd ?? process.cwd()\n const defaultEnv = config?.env\n const destroyGraceMs = config?.destroyGraceMs ?? DESTROY_SIGTERM_GRACE_MS\n\n // Base environment children inherit. Defaults to the full parent env\n // (back-compat); when `inheritEnv: false`, only a minimal allow-list is\n // forwarded so parent secrets (e.g. API keys Bun loaded from `.env`) aren't\n // exposed to tool commands. Explicit `env` / per-call env always apply on top.\n const baseEnv: NodeJS.ProcessEnv = config?.inheritEnv === false\n ? pickEnv(process.env, ['PATH', 'HOME', 'SHELL', 'LANG', 'LC_ALL', 'USER', 'TERM', 'TMPDIR'])\n : process.env\n\n // Optional workspace containment for the file methods. Unset = no checks.\n const workspaceRoot = config?.workspaceRoot !== undefined\n ? resolve(defaultCwd, config.workspaceRoot)\n : undefined\n\n // Optional OS-level sandbox for shell commands (see `SpawnConfig.isolation`).\n // Availability is probed ONCE here: a missing backend turns every shell\n // call into a fail-closed error result rather than a silent unsandboxed\n // run. Off (the default) leaves the spawn path byte-identical to before.\n const isolation = config?.isolation\n const isolationState = isolation ? isolationAvailability() : undefined\n\n /**\n * Per-context background-task registry. Entries live for the context's\n * lifetime — even after the child exits — so the model can read output\n * of completed tasks until `destroy()` tears everything down. Kept as\n * a plain Map (not a class) per code-quality checklist #7: no premature\n * abstraction. The state and the operations on it live inline.\n */\n const tasks = new Map<string, TaskState>()\n\n /**\n * Per-context UTC timestamp segment baked into every background task's\n * log filename. Same `taskCounter` value across two contexts (e.g. a\n * TUI restart) would otherwise re-open the SAME `bash_<n>.log` file —\n * we open with `flags: 'a'` so the new task would APPEND into the old\n * log, producing scrambled output. The timestamp guarantees each\n * context owns a distinct log filename without forcing the\n * model-facing task id (`bash_<n>`) to grow longer.\n *\n * Format: `YYYYMMDD-HHMMSS-mmm` in UTC.\n * - Sortable (lexical sort = chronological sort).\n * - Unambiguous (UTC sidesteps DST / locale shifts).\n * - Filesystem-safe (digits + hyphens only).\n * - Millisecond precision avoids same-second-restart collisions.\n *\n * The timestamp is computed once per context, NOT per task — all of a\n * context's tasks share the same suffix so a directory listing groups\n * cleanly by \"which run produced these\".\n */\n const contextTimestamp = formatContextTimestamp(new Date())\n\n /**\n * Last-resort orphan reaper. With `detached: true`, background tasks\n * are in their OWN process group — when the parent (zidane TUI) dies,\n * the OS does NOT send them SIGHUP and they keep running indefinitely.\n * The user's \"Ctrl+C the TUI\" intent is \"stop everything I started\",\n * not \"leak a `sleep 60` into the background\".\n *\n * `process.on('exit')` fires SYNCHRONOUSLY on `process.exit()` AND on\n * natural shutdown — exactly the seam we need. The handler can only\n * do synchronous work (Node ignores async), but `process.kill` IS\n * synchronous, so a SIGTERM-the-group sweep lands cleanly. The kill\n * is best-effort: already-dead children throw ESRCH (swallowed).\n *\n * Registered lazily on first `execBackground` so contexts that never\n * background a task don't pay the listener cost. Deregistered in\n * `destroy()` so reconstructed contexts don't accumulate listeners\n * (Node warns past 10 — a long-running session that switches sessions\n * frequently would otherwise hit that).\n */\n /** Live `execStream` children → owning handle id. See `exitHandler`. */\n const streamChildren = new Map<ChildProcess, string>()\n\n const s: ProcessContextState = {\n config,\n counter: 0,\n handles,\n defaultCwd,\n defaultEnv,\n destroyGraceMs,\n baseEnv,\n workspaceRoot,\n isolation,\n isolationState,\n tasks,\n taskCounter: 0,\n contextTimestamp,\n streamChildren,\n exitHandlerRegistered: false,\n exitHandler: () => {},\n registerExitHandler: () => {},\n }\n const exitHandler = (): void => {\n for (const task of tasks.values()) {\n if (task.status !== 'running')\n continue\n const pid = task.child?.pid\n if (pid === undefined)\n continue\n try {\n if (SUPPORTS_PROCESS_GROUPS)\n process.kill(-pid, 'SIGTERM')\n else\n process.kill(pid, 'SIGTERM')\n }\n catch {\n // ESRCH (already dead) / EPERM (lost ownership). Swallow —\n // the process either won't kill cleanly OR is already gone,\n // both acceptable at shutdown.\n }\n }\n // Stream children (execStream) share the reaper but NOT the `tasks`\n // registry: they're caller-owned (an MCP transport's close()), not\n // model-facing. They still spawn detached for kill-tree semantics,\n // which is exactly what makes them survive an unclean host exit —\n // a leaked long-lived MCP server per crash without this sweep.\n for (const child of streamChildren.keys())\n killProcessGroup(child, 'SIGTERM')\n }\n const registerExitHandler = (): void => {\n if (!s.exitHandlerRegistered) {\n process.on('exit', exitHandler)\n s.exitHandlerRegistered = true\n }\n }\n s.exitHandler = exitHandler\n s.registerExitHandler = registerExitHandler\n return s\n}\n\n/**\n * Per-task state. Lives in the context's `tasks` registry. Fields are\n * mutated in place by the spawn / close / kill / destroy code paths —\n * the registry isn't immutable. Treat the type as a record-of-cells,\n * not a value.\n */\nexport interface TaskState {\n taskId: string\n /**\n * `ExecutionHandle.id` of the spawning agent. The registry is\n * context-scoped (one Map shared across all handles a context minted),\n * so a per-task owner tag is what scopes `listBackground` /\n * `killBackground` / `destroy` to the calling handle's slice.\n *\n * Without this, a subagent spawned via `spawn` tool — which inherits\n * the parent's `ExecutionContext` but mints its OWN handle — would\n * see (and accidentally kill on `destroy()`) every task the parent\n * had running. Came up the first time the model spawned a subagent\n * that ran a background task: the subagent's run-end `destroy()`\n * SIGTERMed the parent's `npm run dev` mid-flight.\n */\n handleId: string\n pid: number\n command: string\n cwd: string\n startedAt: number\n outputPath: string\n /**\n * Live-only references, released (set `undefined`) at settle so the\n * registry doesn't retain dead `ChildProcess` and ended `WriteStream`\n * objects until `destroy()`. Present iff `settled` is false.\n */\n outputStream?: WriteStream\n child?: ChildProcess\n status: 'running' | 'exited' | 'killed'\n exitCode?: number\n signal?: NodeJS.Signals\n bytesWritten: number\n /**\n * `at-most-once` settle latch. Multiple trigger sources (`close`,\n * `error`, `kill`) can race; the flag dedupes so `onExit` fires\n * exactly once per task (checklist #14).\n */\n settled: boolean\n /**\n * Set by `killBackground` / `destroy` before issuing SIGTERM so the\n * close handler classifies the exit as `'killed'` even when the\n * platform delivers the close event ahead of our intent record.\n */\n killRequested?: boolean\n /**\n * `Date.now()` at settle time. Lets `durationMs` (and pull-based\n * reconcile consumers reading `TaskEntry.endedAt`) report the task's\n * real lifetime instead of a projection-time delta.\n */\n endedAt?: number\n /**\n * The owner's push-style exit callback. Undefined for pull-based\n * consumers (remote / durable hosts reconcile via `listBackground`).\n * Replaced (never wrapped) by `reassignBackgroundTasks` when ownership\n * or the owning agent changes.\n */\n onExit?: (info: TaskExitInfo) => void\n /** The owner's stall callback — replaceable, like {@link onExit}. */\n onStall?: (info: TaskStallInfo) => void\n /**\n * One-shot exit watchers registered by `killBackground` / `waitBackground`\n * / `destroy`, fired after {@link onExit} at settle. Kept apart from the\n * owner callback so rebinding the owner can't drop an in-flight kill or\n * wait.\n */\n exitWatchers: Array<(info: TaskExitInfo) => void>\n}\n\n/**\n * Send `signal` to the child's whole process group. Falls back to a\n * single-process kill on Windows (no POSIX process groups). Shared\n * across the foreground `exec` path, the background spawn / kill\n * paths, and the shutdown-time orphan reaper — keeping one definition\n * so the kill semantics can't drift between them.\n */\nexport function killProcessGroup(child: ChildProcess | undefined, signal: NodeJS.Signals): void {\n const pid = child?.pid\n if (pid === undefined)\n return\n try {\n if (SUPPORTS_PROCESS_GROUPS)\n process.kill(-pid, signal)\n else\n process.kill(pid, signal)\n }\n catch {\n // ESRCH / EPERM — process is already gone (race with natural exit)\n // or we lost the right to kill it. Both are safe to swallow.\n }\n}\n\n/**\n * Project a `TaskState` to the `TaskEntry` shape `listBackground` and\n * `reassignBackgroundTasks` return. Single helper keeps the shape\n * consistent across both call sites (and a future addition of fields\n * to `TaskEntry` only needs to land here).\n */\nexport function stateToTaskEntry(state: TaskState): TaskEntry {\n return {\n taskId: state.taskId,\n pid: state.pid,\n command: state.command,\n cwd: state.cwd,\n startedAt: state.startedAt,\n ...(state.endedAt !== undefined ? { endedAt: state.endedAt } : {}),\n outputPath: state.outputPath,\n status: state.status,\n ...(state.exitCode !== undefined ? { exitCode: state.exitCode } : {}),\n ...(state.signal ? { signal: state.signal } : {}),\n bytesWritten: state.bytesWritten,\n }\n}\n\n/**\n * Project a settled `TaskState` to the `TaskExitInfo` shape `settle()`\n * fires from `onExit` and `killBackground` returns on the\n * cached-exit path. Pre-condition: `state.status !== 'running'`\n * (callers gate on this).\n */\nexport function stateToTaskExitInfo(state: TaskState): TaskExitInfo {\n return {\n taskId: state.taskId,\n status: state.status as Exclude<TaskState['status'], 'running'>,\n exitCode: state.exitCode ?? 0,\n ...(state.signal ? { signal: state.signal } : {}),\n outputPath: state.outputPath,\n // Settle-time delta when available (always set since `endedAt` was\n // introduced); the `Date.now()` fallback covers defensive callers\n // projecting an unsettled state.\n durationMs: (state.endedAt ?? Date.now()) - state.startedAt,\n command: state.command,\n }\n}\n","/**\n * `execBackground` (background task spawn + settle) of the in-process\n * execution context. Internal module.\n */\n\nimport type { Buffer } from 'node:buffer'\nimport type { WriteStream } from 'node:fs'\nimport type { ProcessShell } from './process-shell'\nimport type { ProcessContextState, TaskState } from './process-state'\nimport type { BackgroundTerminalOptions, ExecutionContext, ExecutionHandle, TaskExitInfo, TaskHandle, TaskStallInfo } from './types'\nimport { constants, createWriteStream, openSync } from 'node:fs'\nimport { mkdir } from 'node:fs/promises'\nimport { resolve } from 'node:path'\nimport { hasLocalPty } from './local-terminal'\nimport { assertSafeTaskId, EXIT_PIPE_DRAIN_GRACE_MS, loadSpawn, stateToTaskExitInfo, SUPPORTS_PROCESS_GROUPS, TERMINAL_TASK_ENV } from './process-state'\n\nconst NO_FOLLOW = constants.O_NOFOLLOW ?? 0\n\n/**\n * The log's fd: a new file (exclusive), never through a symlink planted at\n * its path. A name collision keeps the old append behavior on the existing\n * regular file (still not following a link), as the comment at the call says.\n */\nfunction openTaskLog(path: string): number {\n const write = constants.O_WRONLY | constants.O_APPEND | NO_FOLLOW\n try {\n return openSync(path, write | constants.O_CREAT | constants.O_EXCL, 0o600)\n }\n catch (err) {\n if ((err as NodeJS.ErrnoException).code !== 'EEXIST')\n throw err\n return openSync(path, write)\n }\n}\n\nexport function processBackgroundMethods(s: ProcessContextState, sh: ProcessShell): Required<Pick<ExecutionContext, 'execBackground'>> {\n const { baseEnv, defaultEnv, tasks, contextTimestamp, registerExitHandler } = s\n const { shellCommand, isolationBlocker, spawnTerminalTask } = sh\n\n return {\n async execBackground(\n handle: ExecutionHandle,\n command: string,\n options: {\n cwd?: string\n env?: Record<string, string>\n outputDir: string\n onExit?: (info: TaskExitInfo) => void\n maxOutputBytes?: number\n stallTimeoutMs?: number\n onStall?: (info: TaskStallInfo) => void\n terminal?: BackgroundTerminalOptions\n },\n ): Promise<TaskHandle> {\n const cwd = options.cwd ? resolve(handle.cwd, options.cwd) : handle.cwd\n\n // Resolve the subprocess primitive up front (see `loadSpawn`) so a runtime\n // without `node:child_process` fails BEFORE we allocate the log dir +\n // output stream — no orphaned fd / log file on an unsupported host.\n const spawnChild = await loadSpawn()\n const blocked = isolationBlocker()\n if (blocked)\n throw new Error(blocked.stderr)\n\n await mkdir(options.outputDir, { recursive: true })\n\n // Mint id + path. The id is sequential per context (model-facing,\n // short, ergonomic for `shell_kill`). The log FILENAME embeds the\n // context's start timestamp so two contexts sharing an `outputDir`\n // (TUI restart on the same session, concurrent zidane instances,\n // …) never resolve to the same file — we open with `flags: 'a'`\n // and a name collision would interleave their output. Path\n // validation is defensive — we mint our own ids so it never trips\n // today, but it pins the invariant for forks / third parties.\n const taskId = `bash_${++s.taskCounter}`\n assertSafeTaskId(taskId)\n const outputPath = resolve(options.outputDir, `${taskId}.${contextTimestamp}.log`)\n\n // Install the orphan reaper on first task. See `exitHandler`'s\n // JSDoc for the kill-on-shutdown rationale.\n registerExitHandler()\n\n // Open the output file. The timestamped path is unique per context\n // so a brand-new file is the expected outcome; `flags: 'a'` is kept\n // as the safe default (preserves bytes if the path collides for any\n // reason — same-millisecond context creation, manual pre-population,\n // etc.) rather than blindly truncating. Streams are opened BEFORE\n // the spawn to avoid a race where the child writes before the stream\n // is ready — `child_process` buffers stdio until the consumer\n // attaches, but the FS handle has to exist either way for our pipe.\n const outputStream: WriteStream = createWriteStream(outputPath, { fd: openTaskLog(outputPath) })\n // Surface FS errors (ENOSPC, EACCES on a remounted FS, etc.)\n // under ZIDANE_DEBUG instead of crashing the host via an\n // unhandled 'error' event. Without a listener Node escalates\n // any stream-level error to an uncaughtException and the whole\n // process exits — the model and the user would lose every\n // unrelated in-flight piece of work to one bad task's disk\n // hiccup. Swallow + log is the safer default for a fire-and-\n // forget log writer; the task's exit code still reports.\n outputStream.on('error', (err) => {\n if (process.env.ZIDANE_DEBUG)\n process.stderr.write(`[zidane/contexts] task ${taskId} log stream error: ${err.message}\\n`)\n })\n\n // Spawn as a NEW process group leader so we can kill the whole\n // subtree on demand. Same primitive `exec` uses for foreground\n // shells — see the long comment in that method for the\n // process-group rationale.\n const shell = shellCommand(command)\n // Terminal mode: same registry, a PTY instead of pipes. The log gets\n // plain text (control sequences stripped, redraws collapsed); the raw\n // bytes go to whoever shows the terminal.\n const tty = options.terminal && hasLocalPty() ? options.terminal : undefined\n const child = tty\n ? spawnTerminalTask([shell.file, ...shell.args], cwd, { ...TERMINAL_TASK_ENV, ...options.env }, tty)\n : spawnChild(shell.file, shell.args, {\n cwd,\n env: { ...baseEnv, ...defaultEnv, ...options.env },\n stdio: ['ignore', 'pipe', 'pipe'],\n detached: SUPPORTS_PROCESS_GROUPS,\n })\n\n const state: TaskState = {\n taskId,\n handleId: handle.id,\n pid: child.pid ?? -1,\n command,\n cwd,\n startedAt: Date.now(),\n outputPath,\n outputStream,\n child,\n status: 'running',\n bytesWritten: 0,\n settled: false,\n onExit: options.onExit,\n onStall: options.onStall,\n exitWatchers: [],\n }\n tasks.set(taskId, state)\n\n // Output cap (optional). Beyond the cap the process keeps\n // running — head-priority retention: bytes already on disk stay,\n // subsequent bytes are counted but dropped, and a structured\n // truncation marker is appended at settle so the model can\n // pattern-match the loss. Killing on overflow (what the\n // foreground `exec` does) would be wrong here: long-running\n // servers legitimately log forever.\n const maxOutputBytes = typeof options.maxOutputBytes === 'number' && options.maxOutputBytes > 0\n ? options.maxOutputBytes\n : undefined\n let droppedBytes = 0\n // Latched on the first overflowing chunk. Without it, a codepoint-\n // boundary backoff (below) leaves a few bytes of \"room\" that later\n // chunks would dribble into — appending disjoint fragments from\n // much-later output right after the cut point.\n let capExhausted = false\n\n // Stall watchdog (optional). One-shot per quiet period: fires\n // `onStall` after `stallTimeoutMs` of no output, then stays quiet\n // until fresh output re-arms it. `unref()` so a pending timer\n // never holds the host process open.\n const stallTimeoutMs = typeof options.stallTimeoutMs === 'number' && options.stallTimeoutMs > 0\n ? options.stallTimeoutMs\n : undefined\n let stallTimer: NodeJS.Timeout | undefined\n let lastOutputAt = Date.now()\n const armStallTimer = (): void => {\n // Read through `state` (not a captured option) so a rebind via\n // `reassignBackgroundTasks` redirects later stalls too.\n if (!stallTimeoutMs || !state.onStall)\n return\n if (stallTimer)\n clearTimeout(stallTimer)\n stallTimer = setTimeout(() => {\n stallTimer = undefined\n if (state.settled)\n return\n try {\n state.onStall?.({\n taskId,\n command,\n outputPath,\n stalledForMs: Date.now() - lastOutputAt,\n bytesWritten: state.bytesWritten,\n })\n }\n catch (err) {\n if (process.env.ZIDANE_DEBUG)\n process.stderr.write(`[zidane/contexts] task ${taskId} onStall threw: ${err instanceof Error ? err.message : String(err)}\\n`)\n }\n }, stallTimeoutMs)\n stallTimer.unref?.()\n }\n const clearStallTimer = (): void => {\n if (stallTimer) {\n clearTimeout(stallTimer)\n stallTimer = undefined\n }\n }\n armStallTimer()\n\n // Pipe both streams into the same file. Order between stdout and\n // stderr is preserved per-stream; cross-stream ordering depends on\n // Node's event loop — acceptable interleaving for log-shaped\n // output. Tracked-bytes is updated on every chunk for the\n // listBackground UX (counted even past the cap, so the UX shows\n // the task's TRUE output volume).\n const appendChunk = (chunk: Buffer): void => {\n state.bytesWritten += chunk.length\n lastOutputAt = Date.now()\n armStallTimer()\n if (maxOutputBytes !== undefined) {\n const onDisk = state.bytesWritten - droppedBytes - chunk.length\n if (capExhausted || onDisk >= maxOutputBytes) {\n capExhausted = true\n droppedBytes += chunk.length\n return\n }\n const room = maxOutputBytes - onDisk\n if (chunk.length > room) {\n capExhausted = true\n // Cut on a UTF-8 codepoint boundary, same as the foreground\n // exec cap: back off while the byte after the cut is a\n // continuation byte (0b10xxxxxx) so the log never ends in a\n // mangled half-codepoint right before the truncation marker.\n let end = room\n while (end > 0 && (chunk[end] & 0xC0) === 0x80)\n end--\n droppedBytes += chunk.length - end\n if (end > 0)\n outputStream.write(chunk.subarray(0, end))\n return\n }\n }\n outputStream.write(chunk)\n }\n child.stdout?.on('data', chunk => appendChunk(chunk as Buffer))\n child.stderr?.on('data', chunk => appendChunk(chunk as Buffer))\n\n // Settle path — at-most-once via `settled` flag (checklist #14).\n // Three trigger sources: `close` (natural OR signal-killed),\n // `error` (spawn failure), explicit `killBackground` (which\n // routes through `close` itself after the SIGTERM lands).\n const settle = (cause: 'close' | 'error', code: number | null, signal: NodeJS.Signals | null, errMessage?: string): void => {\n if (state.settled)\n return\n state.settled = true\n clearStallTimer()\n state.endedAt = Date.now()\n\n // Determine final status from cause + signal.\n const status: TaskExitInfo['status']\n = signal === 'SIGTERM' || state.killRequested\n ? 'killed'\n : 'exited'\n // Signal-killed children report null `code` from Node; map back\n // to the POSIX `128 + signum` convention so consumers can read\n // an integer either way.\n const exitCode = code !== null\n ? code\n : signal === 'SIGTERM'\n ? 143\n : signal\n ? 128\n : 1\n state.status = status\n state.exitCode = exitCode\n if (signal)\n state.signal = signal\n\n // Flush + close the WriteStream BEFORE firing onExit — model\n // may read the file in the same turn it receives the\n // notification, and a still-open stream can hold tail bytes\n // back from disk. `stream.end(callback)` is the documented\n // \"all queued writes are flushed when this fires\" idiom.\n //\n // ORDER MATTERS: any error preamble we want in the log file\n // (spawn failures with no stdout, buffer overflows) MUST be\n // written BEFORE `end()` — once `end()` is called the stream\n // is closed for writing and subsequent `.write()` calls are\n // dropped. Earlier revisions had this reversed and silently\n // lost ENOENT-on-`/bin/sh` messages.\n if (errMessage) {\n try {\n outputStream.write(`\\n${errMessage}\\n`)\n }\n catch {\n // Stream may have errored before this — best-effort only.\n }\n }\n if (droppedBytes > 0) {\n try {\n // Structured + loud — the model can pattern-match the tag\n // rather than misread a truncated log as the full output.\n outputStream.write(`\\n<output-truncated bytes-dropped=\"${droppedBytes}\"/>\\n`)\n }\n catch {\n // Best-effort only, same as the error preamble above.\n }\n }\n outputStream.end(() => {\n // `stateToTaskExitInfo` reads the same fields we just set\n // on `state`, so the snapshot the consumer gets matches the\n // post-settle state exactly. `onExit` is optional — pull-based\n // consumers reconcile via `listBackground` instead. The owner's\n // callback fires first, then every kill/wait/destroy watcher.\n const info = stateToTaskExitInfo(state)\n for (const callback of [state.onExit, ...state.exitWatchers.splice(0)]) {\n try {\n callback?.(info)\n }\n catch (err) {\n // Defensive — a buggy onExit callback shouldn't crash the\n // host. Surface via stderr under ZIDANE_DEBUG; otherwise\n // swallow. Matches the spawn-tool's bubbleError pattern.\n if (process.env.ZIDANE_DEBUG)\n process.stderr.write(`[zidane/contexts] task ${taskId} onExit threw: ${err instanceof Error ? err.message : String(err)}\\n`)\n }\n }\n })\n // Release the process + stream references at settle: entries\n // outlive their processes in the registry (until explicit\n // `destroy`), and a session that backgrounds many tasks would\n // otherwise retain every dead ChildProcess and ended\n // WriteStream — plus the closures they anchor — for its whole\n // lifetime. Everything the post-exit readers (`listBackground` /\n // `waitBackground` / `killBackground`) project stays on `state`.\n state.child = undefined\n state.outputStream = undefined\n }\n\n child.on('close', (code, signal) => settle('close', code, signal))\n child.on('error', err => settle('error', null, null, `[spawn error] ${err.message}`))\n\n child.on('exit', (code, signal) => {\n // Same pipe-hostage hazard as foreground `exec`: a grandchild\n // that inherited the pipes blocks `close` forever, leaving the\n // task `running` (and any waiter parked) long after the\n // command exited. Settle after a short drain grace; `settle`'s\n // latch makes this a no-op when `close` already fired.\n const exitGraceTimer = setTimeout(() => {\n if (state.settled)\n return\n child.stdout?.destroy()\n child.stderr?.destroy()\n settle('close', code, signal)\n }, EXIT_PIPE_DRAIN_GRACE_MS)\n exitGraceTimer.unref?.()\n })\n\n return { taskId, pid: state.pid, outputPath, ...(tty ? { terminal: true } : {}) }\n },\n }\n}\n","/**\n * Foreground `exec` of the in-process execution context. Internal module.\n */\n\nimport type { Buffer } from 'node:buffer'\nimport type { CappedSlot } from './output-cap'\nimport type { ProcessShell } from './process-shell'\nimport type { ProcessContextState } from './process-state'\nimport type { ExecResult, ExecutionContext, ExecutionHandle } from './types'\nimport { resolve } from 'node:path'\nimport { annotateDenial } from './isolation'\nimport { appendCapped, DEFAULT_MAX_OUTPUT_BUFFER, outputExceededMessage } from './output-cap'\nimport { EXEC_KILL_ESCALATION_GRACE_MS, EXIT_PIPE_DRAIN_GRACE_MS, killProcessGroup, loadSpawn, SUPPORTS_PROCESS_GROUPS } from './process-state'\n\nexport function processExecMethods(s: ProcessContextState, sh: ProcessShell): Required<Pick<ExecutionContext, 'exec'>> {\n const { config, baseEnv, defaultEnv, isolation } = s\n const { shellCommand, isolationBlocker } = sh\n\n return {\n async exec(\n handle: ExecutionHandle,\n command: string,\n options?: { cwd?: string, env?: Record<string, string>, timeout?: number, signal?: AbortSignal },\n ): Promise<ExecResult> {\n const cwd = options?.cwd ? resolve(handle.cwd, options.cwd) : handle.cwd\n\n // Pre-aborted fast path: skip the spawn entirely and synthesize a\n // killed-by-signal result. Saves a spawn round-trip + dodges Node\n // emitting an immediate `AbortError`.\n if (options?.signal?.aborted) {\n return { stdout: '', stderr: 'aborted by signal before spawn', exitCode: 143 }\n }\n\n const blocked = isolationBlocker()\n if (blocked)\n return blocked\n\n const timeoutMs = (options?.timeout ?? config?.limits?.timeout ?? 30) * 1000\n const maxBuffer = DEFAULT_MAX_OUTPUT_BUFFER\n\n // Lazy subprocess load (see `loadSpawn`) — awaited before the Promise\n // executor so the spawn primitive is ready synchronously inside it.\n const spawnChild = await loadSpawn()\n\n return new Promise<ExecResult>((resolveP) => {\n // Spawn as a NEW process group leader so we can kill the whole\n // subtree on abort. Without `detached: true`, sending SIGTERM to\n // the shell's pid only kills the shell wrapper — its child\n // processes (the actual `sleep`, `npm`, `python`, …) get\n // reparented to init and keep running. `process.kill(-pid, …)`\n // with a NEGATIVE pid targets the whole process group; that's\n // the POSIX idiom for \"shut down everything I started\".\n //\n // On Windows there are no process groups in the POSIX sense, so\n // we leave `detached` off and accept the shell-only kill — the\n // platform's job-object machinery is the path forward there if\n // we ever need it, but it's not the bug zidane's users are\n // hitting today.\n const shell = shellCommand(command)\n const child = spawnChild(shell.file, shell.args, {\n cwd,\n env: { ...baseEnv, ...defaultEnv, ...options?.env },\n stdio: ['ignore', 'pipe', 'pipe'],\n detached: SUPPORTS_PROCESS_GROUPS,\n })\n\n // Byte-capped accumulation via the shared exec-transport primitive\n // (see contexts/output-cap.ts) — byte accounting + codepoint-safe\n // cuts. Overflow orchestration (kill the process group, matching\n // `execAsync`'s maxBuffer behavior) stays here.\n const stdoutSlot: CappedSlot = { text: '', bytes: 0 }\n const stderrSlot: CappedSlot = { text: '', bytes: 0 }\n let bufferTruncated = false\n let timedOut = false\n let killedByAbort = false\n let settled = false\n\n // Every exec-initiated kill escalates: SIGTERM now, SIGKILL after a\n // short grace unless the child settled in between. Without this, a\n // TERM-ignoring child leaves the promise pending forever.\n const killWithEscalation = (): void => {\n killProcessGroup(child, 'SIGTERM')\n const escalation = setTimeout(() => {\n if (!settled)\n killProcessGroup(child, 'SIGKILL')\n }, EXEC_KILL_ESCALATION_GRACE_MS)\n escalation.unref?.()\n }\n\n const onChunk = (slot: CappedSlot, chunk: Buffer): void => {\n const outcome = appendCapped(slot, chunk, maxBuffer)\n if (outcome !== 'ok' && !bufferTruncated) {\n bufferTruncated = true\n killWithEscalation()\n }\n }\n\n child.stdout?.on('data', chunk => onChunk(stdoutSlot, chunk as Buffer))\n child.stderr?.on('data', chunk => onChunk(stderrSlot, chunk as Buffer))\n\n const timeoutTimer = timeoutMs > 0\n ? setTimeout(() => {\n timedOut = true\n killWithEscalation()\n }, timeoutMs)\n : undefined\n\n const onAbort = (): void => {\n killedByAbort = true\n killWithEscalation()\n }\n const userSignal = options?.signal\n if (userSignal)\n userSignal.addEventListener('abort', onAbort, { once: true })\n\n let exitGraceTimer: NodeJS.Timeout | undefined\n\n const settle = (exitCode: number, extraStderr?: string): void => {\n if (settled)\n return\n settled = true\n if (timeoutTimer)\n clearTimeout(timeoutTimer)\n if (exitGraceTimer)\n clearTimeout(exitGraceTimer)\n if (userSignal)\n userSignal.removeEventListener('abort', onAbort)\n const finalStderr = extraStderr\n ? (stderrSlot.text ? `${stderrSlot.text}\\n${extraStderr}` : extraStderr)\n : stderrSlot.text\n const result: ExecResult = { stdout: stdoutSlot.text, stderr: finalStderr, exitCode }\n // Name the sandbox when a failure looks like a kernel denial so the\n // model asks the user to widen the policy instead of guessing.\n resolveP(isolation ? annotateDenial(result, isolation) : result)\n }\n\n // Shared classification ladder for `close` (fast path: pipes\n // drained normally) and the post-`exit` grace (pipes held open\n // by a daemonized grandchild). Order matters: killed-by-our-\n // abort wins over timeout wins over natural exit, because the\n // abort listener fires first when the user cancels mid-\n // timeout-window.\n const settleFromStatus = (code: number | null, signal: NodeJS.Signals | null): void => {\n if (killedByAbort) {\n settle(143, 'aborted by signal')\n return\n }\n if (timedOut) {\n settle(124, `command timed out after ${timeoutMs}ms`)\n return\n }\n if (bufferTruncated) {\n settle(143, outputExceededMessage(maxBuffer, 'process killed'))\n return\n }\n if (signal) {\n // Killed by some other signal we didn't issue. Treat as\n // signal-killed for consumer compat.\n settle(128 + 15, `terminated by signal ${signal}`)\n return\n }\n settle(typeof code === 'number' ? code : 1)\n }\n\n child.on('error', (err) => {\n // Spawn failure (ENOENT on `/bin/sh`, EACCES, …). Mirror\n // `execAsync`'s \"rejects with an Error\" shape by surfacing\n // the message on stderr and a non-zero exit.\n settle(1, err.message)\n })\n\n child.on('close', (code, signal) => settleFromStatus(code, signal))\n\n child.on('exit', (code, signal) => {\n // The command is DONE here — `close` only adds \"every stdio\n // pipe closed\", which a daemonized grandchild can block\n // forever (see EXIT_PIPE_DRAIN_GRACE_MS). Give the pipes a\n // short drain grace, then settle with what we have and\n // release them. The daemon keeps running — that was the\n // command's intent; killing the group here would be wrong.\n exitGraceTimer = setTimeout(() => {\n if (settled)\n return\n child.stdout?.destroy()\n child.stderr?.destroy()\n settleFromStatus(code, signal)\n }, EXIT_PIPE_DRAIN_GRACE_MS)\n exitGraceTimer.unref?.()\n })\n })\n },\n }\n}\n","/**\n * Pure (node-free) `git ls-files` helpers shared by the gitignore-aware paths\n * of the `glob` and `grep` tools.\n *\n * `git ls-files --cached --others --exclude-standard` lists tracked files plus\n * untracked-but-not-ignored files, applying `.gitignore`, `.git/info/exclude`,\n * and the user's global excludes. Git never descends ignored directories\n * (`node_modules`, build output, …), so this is both correct and sub-second on\n * large repos — unlike a raw filesystem walk.\n *\n * This module has ZERO node imports so it can be pulled into edge/workerd\n * bundles (e.g. via the `grep` tool, which drives git through the execution\n * context's `exec` seam). The node-spawn variant lives in\n * `./git-ls-files-local`.\n */\n\n/** Argv for `git ls-files`, NUL-delimited so paths with newlines stay intact. */\nexport const GIT_LS_FILES_ARGS = ['ls-files', '--cached', '--others', '--exclude-standard', '-z'] as const\n\n/** The same invocation as a shell command string for the `exec` seam. */\nexport const GIT_LS_FILES_COMMAND = `git ${GIT_LS_FILES_ARGS.join(' ')}`\n\n/** Split NUL-delimited `git ls-files -z` output into paths. */\nexport function parseGitLsFiles(stdout: string): string[] {\n return stdout.split('\\0').filter(p => p.length > 0)\n}\n","/**\n * Node-spawn `git ls-files` runner used by the local process context's glob.\n *\n * Kept separate from `./git-ls-files` (which stays node-free for edge bundles)\n * because this leg imports `node:child_process`.\n */\n\nimport { spawn } from 'node:child_process'\nimport { GIT_LS_FILES_ARGS, parseGitLsFiles } from './git-ls-files'\n\n/**\n * Run `git ls-files` in `cwd` and return its non-ignored paths (relative to\n * `cwd`). Returns `null` when the directory is not a git repo, git is missing,\n * or the call aborts — callers treat `null` as \"fall back to a filesystem\n * walk\". Never throws.\n */\nexport async function gitListFilesLocal(cwd: string, signal?: AbortSignal): Promise<string[] | null> {\n return new Promise<string[] | null>((resolveP) => {\n let child: ReturnType<typeof spawn>\n try {\n child = spawn('git', [...GIT_LS_FILES_ARGS], { cwd, stdio: ['ignore', 'pipe', 'pipe'] })\n }\n catch {\n resolveP(null)\n return\n }\n if (signal?.aborted) {\n child.kill('SIGTERM')\n resolveP(null)\n return\n }\n const stdoutChunks: string[] = []\n const onAbort = () => child.kill('SIGTERM')\n signal?.addEventListener('abort', onAbort, { once: true })\n child.stdout?.setEncoding('utf8')\n child.stdout?.on('data', chunk => stdoutChunks.push(chunk as string))\n child.on('error', () => {\n signal?.removeEventListener('abort', onAbort)\n resolveP(null)\n })\n child.on('close', (code) => {\n signal?.removeEventListener('abort', onAbort)\n if (code !== 0 || signal?.aborted) {\n resolveP(null)\n return\n }\n resolveP(parseGitLsFiles(stdoutChunks.join('')))\n })\n })\n}\n","import type { ListFileMetadata } from './types'\nimport { readdir, stat } from 'node:fs/promises'\nimport { dirname, resolve, sep } from 'node:path'\nimport { lazyAsync } from '../lazy'\nimport { gitListFilesLocal } from './git-ls-files-local'\n\ntype NodeGlob = (pattern: string, options: { cwd: string }) => AsyncIterable<string>\n\ninterface BunGlobInstance { scan: (options: { cwd: string, onlyFiles?: boolean, dot?: boolean }) => AsyncIterable<string> }\ntype BunGlobCtor = new (pattern: string) => BunGlobInstance\n\nconst loadNativeNodeGlob = lazyAsync(async (): Promise<NodeGlob | undefined> => {\n const candidate = (await import('node:fs/promises') as { glob?: unknown }).glob\n return typeof candidate === 'function' ? candidate as NodeGlob : undefined\n})\n\nfunction forcePortable(): boolean {\n return (globalThis as { __zidaneForcePortableGlobForTests?: boolean }).__zidaneForcePortableGlobForTests === true\n}\n\nasync function loadNodeGlob(): Promise<NodeGlob | undefined> {\n if (forcePortable())\n return undefined\n return loadNativeNodeGlob()\n}\n\n/**\n * Bun's `node:fs/promises.glob` buffers the entire directory walk before\n * yielding (so an early `limit` break never fires — a broad `**` pattern over\n * a large `node_modules` hangs). `Bun.Glob().scan()` streams incrementally and\n * is the fast path under Bun. Returns undefined off Bun or when tests force the\n * portable walker.\n */\nfunction loadBunGlob(): BunGlobCtor | undefined {\n if (forcePortable())\n return undefined\n const bun = (globalThis as { Bun?: { Glob?: unknown } }).Bun\n return bun && typeof bun.Glob === 'function' ? bun.Glob as BunGlobCtor : undefined\n}\n\nfunction escapeRegexChar(char: string): string {\n return /[\\\\^$.*+?()[\\]{}|]/.test(char) ? `\\\\${char}` : char\n}\n\nexport function expandBraces(pattern: string): string[] {\n const start = pattern.indexOf('{')\n if (start === -1)\n return [pattern]\n const end = pattern.indexOf('}', start + 1)\n if (end === -1)\n return [pattern]\n const body = pattern.slice(start + 1, end)\n if (!body || !body.includes(','))\n return [pattern]\n return body\n .split(',')\n .flatMap(part => expandBraces(`${pattern.slice(0, start)}${part}${pattern.slice(end + 1)}`))\n}\n\nexport function globPatternToRegex(pattern: string): RegExp {\n let out = '^'\n for (let i = 0; i < pattern.length; i++) {\n const char = pattern[i]\n const next = pattern[i + 1]\n if (char === '*') {\n if (next === '*') {\n const after = pattern[i + 2]\n if (after === '/') {\n out += '(?:[^/]+/)*'\n i += 2\n }\n else {\n out += '.*'\n i += 1\n }\n }\n else {\n out += '[^/]*'\n }\n }\n else if (char === '?') {\n out += '[^/]'\n }\n else if (char === '[') {\n const end = pattern.indexOf(']', i + 1)\n if (end === -1) {\n out += '\\\\['\n }\n else {\n let cls = pattern.slice(i + 1, end)\n if (cls.startsWith('!'))\n cls = `^${cls.slice(1)}`\n out += `[${cls}]`\n i = end\n }\n }\n else {\n out += escapeRegexChar(char)\n }\n }\n return new RegExp(`${out}$`)\n}\n\nexport function globLiteralPrefix(pattern: string): string {\n const firstMeta = pattern.search(/[*?[\\]{}]/)\n const prefix = firstMeta === -1 ? dirname(pattern) : pattern.slice(0, firstMeta)\n const slash = prefix.lastIndexOf('/')\n if (slash === -1)\n return ''\n return prefix.slice(0, slash)\n}\n\n/**\n * True when a pattern can match files outside `cwd` — an absolute path, a\n * Windows drive, or any `..` segment. Such patterns can't use the `git\n * ls-files` path (git only knows files under the repo) and fall back to the\n * filesystem walk.\n */\nfunction patternEscapesCwd(pattern: string): boolean {\n const norm = pattern.split(sep).join('/')\n if (norm.startsWith('/') || /^[a-z]:/i.test(norm))\n return true\n return norm.split('/').includes('..')\n}\n\nasync function addMetadata(cwd: string, rel: string, includeMetadata: boolean): Promise<ListFileMetadata | null> {\n try {\n const s = await stat(resolve(cwd, rel))\n if (!s.isFile())\n return null\n return includeMetadata ? { path: rel, size: s.size, mtimeMs: s.mtimeMs } : { path: rel }\n }\n catch {\n return null\n }\n}\n\nfunction compilePattern(pattern: string): RegExp[] {\n return expandBraces(pattern.split(sep).join('/')).map(globPatternToRegex)\n}\n\n/** Match the (already non-ignored) `git ls-files` set against the glob pattern. */\nasync function matchGitFiles(\n gitFiles: readonly string[],\n pattern: string,\n cwd: string,\n limit: number,\n includeMetadata: boolean,\n signal: AbortSignal | undefined,\n): Promise<ListFileMetadata[]> {\n const regexes = compilePattern(pattern)\n const out: ListFileMetadata[] = []\n for (const rel of gitFiles) {\n if (signal?.aborted || out.length >= limit)\n break\n if (!regexes.some(regex => regex.test(rel)))\n continue\n out.push(includeMetadata ? await addMetadata(cwd, rel, true) ?? { path: rel } : { path: rel })\n }\n return out.sort((a, b) => a.path.localeCompare(b.path))\n}\n\n/** Depth-first walk that matches against `regexes` and stops at `limit`. */\nasync function walkMatch(\n root: string,\n prefix: string,\n regexes: RegExp[],\n cwd: string,\n limit: number,\n includeMetadata: boolean,\n signal: AbortSignal | undefined,\n out: ListFileMetadata[],\n): Promise<void> {\n if (out.length >= limit || signal?.aborted)\n return\n let entries: Array<{ name: string, isDirectory: () => boolean, isFile: () => boolean }>\n try {\n entries = await readdir(resolve(root, prefix), { encoding: 'utf8', withFileTypes: true })\n }\n catch {\n return\n }\n for (const entry of entries) {\n if (out.length >= limit || signal?.aborted)\n return\n const rel = prefix ? `${prefix}/${entry.name}` : entry.name\n if (entry.isDirectory()) {\n await walkMatch(root, rel, regexes, cwd, limit, includeMetadata, signal, out)\n }\n else if (entry.isFile() && regexes.some(regex => regex.test(rel))) {\n out.push(includeMetadata ? await addMetadata(cwd, rel, true) ?? { path: rel } : { path: rel })\n }\n }\n}\n\nasync function globPortable(\n pattern: string,\n cwd: string,\n limit: number,\n includeMetadata: boolean,\n signal: AbortSignal | undefined,\n): Promise<ListFileMetadata[]> {\n const normalized = pattern.split(sep).join('/')\n const startPrefix = globLiteralPrefix(normalized)\n const regexes = expandBraces(normalized).map(globPatternToRegex)\n const out: ListFileMetadata[] = []\n await walkMatch(cwd, startPrefix, regexes, cwd, limit, includeMetadata, signal, out)\n return out.sort((a, b) => a.path.localeCompare(b.path))\n}\n\nasync function collectFromIterable(\n iter: AsyncIterable<string>,\n cwd: string,\n limit: number,\n includeMetadata: boolean,\n signal: AbortSignal | undefined,\n): Promise<ListFileMetadata[] | null> {\n const results: ListFileMetadata[] = []\n // A native scan can throw mid-iteration on a pathological tree (e.g. a\n // recursive symlink cycle → ENAMETOOLONG) and there's no way to resume it.\n // Signal the caller (null) to recover via the portable walk, which recurses\n // only into real directories and isolates per-directory errors.\n try {\n for await (const rel of iter) {\n if (signal?.aborted)\n break\n const entry = await addMetadata(cwd, rel, includeMetadata)\n if (!entry)\n continue\n results.push(entry)\n if (results.length >= limit)\n break\n }\n }\n catch {\n return null\n }\n return results.sort((a, b) => a.path.localeCompare(b.path))\n}\n\nfunction globViaBun(\n BunGlob: BunGlobCtor,\n pattern: string,\n cwd: string,\n limit: number,\n includeMetadata: boolean,\n signal: AbortSignal | undefined,\n): Promise<ListFileMetadata[] | null> {\n // `dot: true` keeps dotfile matching consistent with the Node + portable\n // paths; `onlyFiles: true` mirrors their file-only results.\n const scan = new BunGlob(pattern).scan({ cwd, onlyFiles: true, dot: true })\n return collectFromIterable(scan, cwd, limit, includeMetadata, signal)\n}\n\nexport async function globFilesLocal(\n pattern: string,\n cwd: string,\n options: { limit: number, metadata?: boolean, gitignore?: boolean, signal?: AbortSignal },\n): Promise<ListFileMetadata[]> {\n const includeMetadata = options.metadata === true\n const { limit, signal } = options\n\n // Default path: intersect the pattern with git's non-ignored file set. Git\n // skips `.gitignore`d trees (`node_modules`, build dirs) entirely, so this is\n // both correct and fast — and never triggers the Bun `fs.glob` hang. The\n // test-only force-portable flag also bypasses it so the filesystem walker\n // stays directly exercisable.\n if (options.gitignore !== false && !forcePortable() && !patternEscapesCwd(pattern)) {\n const gitFiles = await gitListFilesLocal(cwd, signal)\n if (gitFiles)\n return matchGitFiles(gitFiles, pattern, cwd, limit, includeMetadata, signal)\n }\n\n // Fallback path: filesystem glob (gitignore disabled, not a git repo, or the\n // pattern escapes cwd). Prefer a fast native scanner; if it throws on a\n // pathological tree (null), recover via the portable walk.\n const bunGlob = loadBunGlob()\n if (bunGlob) {\n const viaBun = await globViaBun(bunGlob, pattern, cwd, limit, includeMetadata, signal)\n if (viaBun)\n return viaBun\n }\n else {\n const nodeGlob = await loadNodeGlob()\n if (nodeGlob) {\n const viaNode = await collectFromIterable(nodeGlob(pattern, { cwd }), cwd, limit, includeMetadata, signal)\n if (viaNode)\n return viaNode\n }\n }\n\n return globPortable(pattern, cwd, limit, includeMetadata, signal)\n}\n\n// `globFilesViaShell` moved to `./glob-shell` (node-free) so remote-only\n// contexts can import it without pulling this module's `node:fs` imports.\n// Re-exported here for back-compat.\nexport { globFilesViaShell } from './glob-shell'\n","/**\n * File methods (with workspace containment + sandbox write policy) of the\n * in-process execution context. Internal module.\n */\n\nimport type { Buffer } from 'node:buffer'\nimport type { ProcessContextState } from './process-state'\nimport type { ExecutionContext, ExecutionHandle, ListFilesEntry } from './types'\nimport { mkdir, readdir, readFile, realpath, stat, writeFile } from 'node:fs/promises'\nimport { dirname, relative, resolve } from 'node:path'\nimport { globFilesLocal, globLiteralPrefix } from './glob'\nimport { isWithinRoot } from './isolation'\n\nexport function processFileMethods(s: ProcessContextState): Required<Pick<ExecutionContext, 'readFile' | 'fileSize' | 'readFileBinary' | 'writeFile' | 'listFiles'>> {\n const { workspaceRoot, isolation } = s\n\n /**\n * Resolve `path` against the handle cwd and, when a `workspaceRoot` is\n * configured, reject anything that escapes it. Uses `realpath` on the\n * nearest existing ancestor so symlink escapes (and `..`) are caught for\n * both existing and not-yet-created targets.\n */\n async function resolveContained(handle: ExecutionHandle, path: string): Promise<string> {\n const full = resolve(handle.cwd, path)\n if (workspaceRoot === undefined)\n return full\n\n const canonical = await canonicalizeForCheck(full)\n const canonicalRoot = await realpath(workspaceRoot).catch(() => workspaceRoot)\n if (!isWithinRoot(canonicalRoot, canonical)) {\n throw new Error(\n `Path escapes the workspace root: ${path} (resolved outside ${canonicalRoot})`,\n )\n }\n return full\n }\n\n /**\n * Canonicalize the deepest existing ancestor of `full` to defeat symlink\n * escapes; the non-existent tail (for writes) is appended back verbatim.\n */\n async function canonicalizeForCheck(full: string): Promise<string> {\n let existing = full\n const tail: string[] = []\n while (true) {\n try {\n existing = await realpath(existing)\n break\n }\n catch {\n const parent = dirname(existing)\n if (parent === existing) {\n // Reached the filesystem root without an existing ancestor.\n existing = parent\n break\n }\n // `relative` (not slice) so the segment is correct even on Windows\n // drive roots where `dirname('C:\\\\foo')` is `C:\\\\` (no 1-char sep).\n tail.unshift(relative(parent, existing))\n existing = parent\n }\n }\n return tail.length ? resolve(existing, ...tail) : existing\n }\n\n /**\n * In-process counterpart of the sandbox's write policy: `writeFile` (and\n * so the `write_file` / `edit` tools) may only target `isolation.writableRoots`.\n * Reads are deliberately unrestricted — same boundary the kernel enforces\n * for shell commands, so the two never disagree about where writes land.\n */\n async function assertWritable(full: string, path: string): Promise<void> {\n if (!isolation)\n return\n const canonical = await canonicalizeForCheck(full)\n if (!isolation.writableRoots.some(root => isWithinRoot(root, canonical))) {\n throw new Error(\n `[sandbox] write denied: ${path} is outside the writable roots (${isolation.writableRoots.join(', ')}). `\n + 'Ask the user to add it under Settings → \"Sandbox writable paths\" or turn off \"Sandbox shell commands\".',\n )\n }\n }\n\n return {\n async readFile(handle: ExecutionHandle, path: string): Promise<string> {\n return readFile(await resolveContained(handle, path), 'utf-8')\n },\n\n async fileSize(handle: ExecutionHandle, path: string): Promise<number | null> {\n try {\n // Same containment as every other file method. `stat` (NOT lstat):\n // the caller wants the TARGET's size — an lstat of a symlink\n // reports the link itself and a link to a 10GB file would sail\n // past the whole-read guard this probe exists to feed.\n const info = await stat(await resolveContained(handle, path))\n return info.isFile() ? info.size : null\n }\n catch {\n return null\n }\n },\n\n async readFileBinary(handle: ExecutionHandle, path: string): Promise<Uint8Array> {\n // No encoding → returns a Buffer (which is a Uint8Array). Used by\n // read_file to ferry image / binary content into the multimodal route.\n const buf = await readFile(await resolveContained(handle, path))\n return new Uint8Array(buf)\n },\n\n async writeFile(handle: ExecutionHandle, path: string, content: string | Buffer, encoding: BufferEncoding = 'utf-8'): Promise<void> {\n const fullPath = await resolveContained(handle, path)\n await assertWritable(fullPath, path)\n await mkdir(dirname(fullPath), { recursive: true })\n if (typeof content === 'string')\n await writeFile(fullPath, content, encoding)\n else\n await writeFile(fullPath, content)\n },\n\n async listFiles(handle: ExecutionHandle, path: string, options): Promise<ListFilesEntry[]> {\n if (options?.glob) {\n await resolveContained(handle, globLiteralPrefix(path) || '.')\n const entries = await globFilesLocal(path, handle.cwd, {\n limit: options?.limit ?? 1000,\n metadata: options?.metadata,\n gitignore: options?.gitignore,\n signal: options?.signal,\n })\n if (workspaceRoot === undefined)\n return entries\n const contained: ListFilesEntry[] = []\n for (const entry of entries) {\n try {\n await resolveContained(handle, entry.path)\n contained.push(entry)\n }\n catch {\n // Symlink escapes can be discovered by broad globs like `**/*`.\n // Keep the process context's workspaceRoot invariant by filtering\n // them exactly as read/write paths are filtered.\n }\n }\n return contained\n }\n // Literal listing — suffix directories with `/` so consumers (the\n // `list_files` tool, path suggestions) can tell files from dirs\n // without a second probe. Mirrors `ls -p` in the docker context.\n const dirents = await readdir(await resolveContained(handle, path), { withFileTypes: true })\n return dirents.map(d => (d.isDirectory() ? `${d.name}/` : d.name))\n },\n }\n}\n","/**\n * Terminal output → plain text, incrementally. A PTY emits colors, cursor\n * moves, titles and carriage-return redraws; a model (or a log file) wants\n * the words. Sequences may be split across chunks, so the parser keeps its\n * state between `push` calls.\n *\n * Line semantics follow what a person sees on a terminal, roughly: `\\r\\n`\n * ends a line, a lone `\\r` rewinds it (a progress bar redraws in place, so\n * only its last state survives), backspace erases a character. Anything\n * fancier (cursor addressing in full-screen apps) is dropped, not replayed.\n */\n\nexport interface AnsiStripper {\n /** Feed raw output; returns the lines it completed (each ends with `\\n`). */\n push: (chunk: string) => string\n /** The line being written, not yet ended. */\n partial: () => string\n /**\n * Hand out the unfinished line and treat it as written: a later rewind\n * starts a fresh line instead of editing text that is already out. For\n * logs that must show a prompt waiting on the same line.\n */\n takePartial: () => string\n}\n\nexport interface AnsiStripperOptions {\n /** Window title set by the program (OSC 0 / 2), e.g. the running command. */\n onTitle?: (title: string) => void\n}\n\n/** Parser states: plain text, after ESC, in a CSI, in an OSC/DCS string, after ESC inside one. */\nconst State = { Text: 0, Esc: 1, Csi: 2, Str: 3, StrEsc: 4 } as const\n\nexport function createAnsiStripper(options: AnsiStripperOptions = {}): AnsiStripper {\n let state: (typeof State)[keyof typeof State] = State.Text\n /** Body of the OSC / DCS / APC string being read; OSC only is kept. */\n let str = ''\n let strIsOsc = false\n let line = ''\n /** A `\\r` arrived; the next printable rewinds the line unless `\\n` follows. */\n let pendingCr = false\n /** Part of the current line was handed out by `takePartial`: a rewind must start a new line. */\n let taken = false\n\n const endString = () => {\n if (strIsOsc && options.onTitle) {\n const semi = str.indexOf(';')\n const code = semi === -1 ? str : str.slice(0, semi)\n if ((code === '0' || code === '2') && semi !== -1)\n options.onTitle(str.slice(semi + 1))\n }\n str = ''\n state = State.Text\n }\n\n return {\n push(chunk) {\n let out = ''\n for (let i = 0; i < chunk.length; i++) {\n const ch = chunk[i]!\n const code = ch.charCodeAt(0)\n switch (state) {\n case State.Text:\n if (code === 0x1B) {\n state = State.Esc\n }\n else if (ch === '\\n') {\n out += `${line}\\n`\n line = ''\n pendingCr = false\n taken = false\n }\n else if (ch === '\\r') {\n pendingCr = true\n }\n else if (code === 0x08) {\n line = line.slice(0, -1)\n }\n else if (code === 0x9B) {\n state = State.Csi\n }\n else if (code === 0x9D) {\n state = State.Str\n strIsOsc = true\n }\n else if (ch === '\\t' || code >= 0x20) {\n if (code >= 0x7F && code < 0xA0)\n break\n if (pendingCr) {\n if (taken)\n out += '\\n'\n taken = false\n line = ''\n pendingCr = false\n }\n line += ch\n }\n break\n case State.Esc:\n if (ch === '[') {\n state = State.Csi\n }\n else if (ch === ']' || ch === 'P' || ch === 'X' || ch === '^' || ch === '_') {\n state = State.Str\n strIsOsc = ch === ']'\n str = ''\n }\n else if (code >= 0x20 && code <= 0x2F) {\n // intermediate (e.g. `ESC ( B` charset): swallow the final byte next\n }\n else {\n state = State.Text\n }\n break\n case State.Csi:\n // parameters / intermediates until a final byte in @..~\n if (code >= 0x40 && code <= 0x7E)\n state = State.Text\n break\n case State.Str:\n if (code === 0x07) {\n endString()\n }\n else if (code === 0x1B) {\n state = State.StrEsc\n }\n else if (code === 0x9C) {\n endString()\n }\n else if (str.length < 4096) {\n str += ch\n }\n break\n case State.StrEsc:\n if (ch === '\\\\') {\n endString()\n }\n else {\n str += `\\x1B${ch}`\n state = State.Str\n }\n break\n }\n }\n return out\n },\n partial: () => line,\n takePartial() {\n const text = line\n if (text)\n taken = true\n line = ''\n return text\n },\n }\n}\n\n/** One-shot convenience: the plain text of a complete chunk of terminal output. */\nexport function stripAnsi(text: string): string {\n const s = createAnsiStripper()\n return s.push(text) + s.partial()\n}\n","/**\n * Shell / terminal spawn helpers of the in-process execution context.\n * Internal module.\n */\n\nimport type { ChildProcess } from 'node:child_process'\nimport type { ProcessContextState } from './process-state'\nimport type { BackgroundTerminalOptions, ExecResult } from './types'\nimport { createAnsiStripper } from './ansi-strip'\nimport { unavailableResult, wrapCommand } from './isolation'\nimport { ptyChildProcess, spawnLocalPty, TERMINAL_ENV } from './local-terminal'\n\nexport interface ProcessShell {\n shellCommand: (command: string) => { file: string, args: string[] }\n isolationBlocker: () => ExecResult | undefined\n spawnTerminalTask: (argv: string[], cwd: string, extraEnv: Record<string, string> | undefined, tty: BackgroundTerminalOptions) => ChildProcess\n terminalEnv: (extra?: Record<string, string>, unset?: readonly string[]) => Record<string, string>\n}\n\nexport function createProcessShell(s: ProcessContextState): ProcessShell {\n const { baseEnv, defaultEnv, isolation, isolationState } = s\n\n /**\n * `{ file, args }` for `/bin/sh -c command`, wrapped in the sandbox when\n * isolation is on. Callers check `isolationState.ok` first (the three\n * shell entry points return `unavailableResult` / throw before spawning).\n */\n function shellCommand(command: string): { file: string, args: string[] } {\n if (isolation && isolationState?.ok)\n return wrapCommand(isolationState.backend, isolationState.executable, isolation, command)\n return { file: '/bin/sh', args: ['-c', command] }\n }\n\n /** Failure result when isolation was requested but cannot be enforced; `undefined` = proceed. */\n function isolationBlocker(): ExecResult | undefined {\n return isolationState && !isolationState.ok ? unavailableResult(isolationState.reason) : undefined\n }\n\n /**\n * A background task in a PTY, as a `ChildProcess` look-alike whose stdout\n * carries the log text. A line still unfinished after a quiet moment (a\n * prompt waiting for input) is written out too, so the log shows it.\n */\n function spawnTerminalTask(argv: string[], cwd: string, extraEnv: Record<string, string> | undefined, tty: BackgroundTerminalOptions): ChildProcess {\n const stripper = createAnsiStripper()\n const decoder = new TextDecoder()\n let quiet: NodeJS.Timeout | undefined\n let emitLog: (text: string) => void = () => {}\n const flushPartial = () => {\n const rest = stripper.takePartial()\n if (rest)\n emitLog(rest)\n }\n const pty = spawnLocalPty(argv, {\n cwd,\n env: terminalEnv(extraEnv),\n cols: tty.cols,\n rows: tty.rows,\n onData: (data) => {\n tty.onData(data)\n const text = stripper.push(decoder.decode(data, { stream: true }))\n if (text)\n emitLog(text)\n if (quiet)\n clearTimeout(quiet)\n quiet = setTimeout(flushPartial, 300)\n quiet.unref?.()\n },\n })\n const { child, stdout } = ptyChildProcess(pty)\n const encoder = new TextEncoder()\n emitLog = text => stdout.emit('data', encoder.encode(text))\n void pty.exited.then(() => {\n if (quiet)\n clearTimeout(quiet)\n flushPartial()\n })\n tty.onReady?.({ write: pty.write, resize: pty.resize })\n return child\n }\n\n /** The environment of a terminal: what commands get, plus terminal identity, minus `unset`. */\n function terminalEnv(extra?: Record<string, string>, unset: readonly string[] = []): Record<string, string> {\n const env: Record<string, string> = {}\n for (const [key, value] of Object.entries({ ...baseEnv, ...defaultEnv, ...extra, ...TERMINAL_ENV })) {\n if (typeof value === 'string' && !unset.includes(key))\n env[key] = value\n }\n return env\n }\n\n return { shellCommand, isolationBlocker, spawnTerminalTask, terminalEnv }\n}\n","/**\n * Live bidirectional `execStream` of the in-process execution context.\n * Internal module.\n */\n\nimport type { ProcessShell } from './process-shell'\nimport type { ProcessContextState } from './process-state'\nimport type { ExecutionContext, ExecutionHandle, StreamHandle } from './types'\nimport { resolve } from 'node:path'\nimport { EXEC_KILL_ESCALATION_GRACE_MS, killProcessGroup, loadSpawn, SUPPORTS_PROCESS_GROUPS } from './process-state'\n\nexport function processStreamMethods(s: ProcessContextState, sh: ProcessShell): Required<Pick<ExecutionContext, 'execStream'>> {\n const { baseEnv, defaultEnv, registerExitHandler, streamChildren } = s\n const { shellCommand, isolationBlocker } = sh\n\n return {\n /**\n * Live bidirectional stream to a child process — see\n * `ExecutionContext.execStream` for the seam contract.\n *\n * The reference implementation: an ordinary piped `spawn`, so both\n * streams stay separate and no terminal line discipline sits between\n * the caller and the child. Sandbox-backed contexts have to work\n * harder to reach the same shape.\n *\n * Unlike `execBackground`, nothing here lands in the `tasks` registry:\n * this handle is owned by its caller (an MCP transport's `close()`),\n * not by the model-facing background-task list that `shell_kill` /\n * `wait_task` walk. It IS tracked in `streamChildren` so the shared\n * orphan reaper and `destroy()` can sweep it — detached process-group\n * semantics (needed so `kill()` takes the child's own children with\n * it) are exactly what would otherwise leak a live server per crash.\n */\n async execStream(\n handle: ExecutionHandle,\n command: string,\n options?: { cwd?: string, env?: Record<string, string>, signal?: AbortSignal },\n ): Promise<StreamHandle> {\n const cwd = options?.cwd ? resolve(handle.cwd, options.cwd) : handle.cwd\n const spawnChild = await loadSpawn()\n\n options?.signal?.throwIfAborted()\n const blocked = isolationBlocker()\n if (blocked)\n throw new Error(blocked.stderr)\n\n const shell = shellCommand(command)\n const child = spawnChild(shell.file, shell.args, {\n cwd,\n env: { ...baseEnv, ...defaultEnv, ...options?.env },\n stdio: ['pipe', 'pipe', 'pipe'],\n detached: SUPPORTS_PROCESS_GROUPS,\n })\n\n // Same orphan-reaper coverage as `execBackground`: the detached\n // spawn (needed for kill-tree) means an unclean host exit (Ctrl+C,\n // uncaught exception, OOM) would otherwise leave the server running\n // indefinitely. Tracked in a side map rather than `tasks` — these\n // are caller-owned, invisible to `shell_kill` / `wait_task`.\n registerExitHandler()\n streamChildren.set(child, handle.id)\n\n // A spawn failure (ENOENT on /bin/sh, EACCES on cwd) arrives as an\n // async 'error' event, never a throw. Without a listener Node\n // escalates it to an uncaughtException and takes the host down.\n let spawnError: Error | null = null\n child.on('error', (err) => { spawnError = err })\n\n let closed = false\n let escalation: ReturnType<typeof setTimeout> | undefined\n const exited = new Promise<number>((resolveExit) => {\n // 'close' rather than 'exit': it fires after the stdio streams\n // have flushed and ended, so a consumer awaiting `exited` can\n // trust that it has already seen every byte. `exit` can beat the\n // final stdout chunk and truncate the last protocol frame.\n child.once('close', (code, signalName) => {\n closed = true\n clearTimeout(escalation)\n streamChildren.delete(child)\n // Signal-killed children report a null code. Same mapping as\n // `execBackground`'s exit handler below — SIGTERM (our own\n // `kill()`) is the common case and lands on 143.\n resolveExit(\n code !== null\n ? code\n : signalName === 'SIGTERM'\n ? 143\n : signalName\n ? 128\n : 1,\n )\n })\n })\n\n /*\n * TERM the group, then KILL it if its pipes are still open after the\n * grace. Keyed on `close`, not on the shell's own exit: a TERM that\n * lands while the shell is forking (`sh -c 'echo up; sleep 30'`) kills\n * the shell but not the child it was starting, which keeps the pipes\n * (and `exited`) open for its whole life. Idempotent by contract.\n */\n const kill = async (): Promise<void> => {\n if (closed || child.pid === undefined)\n return\n killProcessGroup(child, 'SIGTERM')\n if (escalation)\n return\n escalation = setTimeout(killProcessGroup, EXEC_KILL_ESCALATION_GRACE_MS, child, 'SIGKILL')\n escalation.unref?.()\n }\n\n if (options?.signal) {\n if (options.signal.aborted) {\n void kill()\n }\n else {\n // Detach on exit: the signal is typically the RUN-long abort\n // signal, so a listener left behind after the child exits retains\n // the `kill` closure (and through it the ChildProcess) for the\n // rest of the run — one dead closure per exec on shell-heavy runs.\n const onAbort = () => { void kill() }\n const detach = () => options.signal!.removeEventListener('abort', onAbort)\n options.signal.addEventListener('abort', onAbort, { once: true })\n exited.then(detach, detach)\n }\n }\n\n return {\n pid: child.pid ?? -1,\n // Node's Readable is an async iterable of Buffer, and Buffer is a\n // Uint8Array — no adaptation needed on this context.\n stdout: child.stdout!,\n stderr: child.stderr!,\n write: async (data: Uint8Array): Promise<void> => {\n if (spawnError)\n throw spawnError\n const stdin = child.stdin\n if (!stdin || stdin.destroyed)\n throw new Error(`execStream: stdin is closed (pid ${child.pid ?? -1})`)\n // Resolve from the write callback, which fires once the chunk is\n // actually flushed to the pipe — that IS the backpressure signal,\n // so awaiting it pushes backpressure out to the caller. (Watching\n // the boolean return + 'drain' instead is a trap: for a small\n // write the callback runs synchronously, before the return value\n // is even bound.)\n await new Promise<void>((resolveWrite, rejectWrite) => {\n stdin.write(data, err => err ? rejectWrite(err) : resolveWrite())\n })\n },\n kill,\n exited,\n }\n },\n }\n}\n","/**\n * Background-task registry operations (kill / wait / reassign / list) and\n * `destroy` of the in-process execution context. Internal module.\n */\n\nimport type { ProcessContextState } from './process-state'\nimport type { ExecutionContext, ExecutionHandle, TaskEntry, TaskExitInfo, TaskStallInfo } from './types'\nimport { killProcessGroup, stateToTaskEntry, stateToTaskExitInfo } from './process-state'\n\nexport function processTaskMethods(s: ProcessContextState): Required<Pick<ExecutionContext, 'killBackground' | 'waitBackground' | 'reassignBackgroundTasks' | 'listBackground' | 'destroy'>> {\n const { tasks, destroyGraceMs, streamChildren, handles } = s\n\n return {\n async killBackground(handle: ExecutionHandle, taskId: string): Promise<TaskExitInfo | null> {\n const state = tasks.get(taskId)\n // Two miss cases collapse into one `null` return: unknown id, AND\n // known-id-but-not-owned-by-this-handle. The second case is the\n // subagent-can't-kill-parent-tasks defense; surfacing it as a\n // distinct error would leak the existence of the parent's task\n // to the subagent's model, which violates the per-handle\n // isolation contract.\n if (!state || state.handleId !== handle.id)\n return null\n // Already exited — return the cached info. We don't keep a\n // separate cached exit; the state itself carries every field\n // `TaskExitInfo` needs and `stateToTaskExitInfo` projects it.\n if (state.status !== 'running')\n return stateToTaskExitInfo(state)\n\n // Mark the intent BEFORE issuing the kill so the close handler\n // classifies the exit as `'killed'` even on platforms where the\n // SIGTERM-via-group lands faster than the close event drains.\n // (Checklist #14: at-most-once settle, plus correct status\n // classification regardless of event ordering.)\n state.killRequested = true\n\n // Wait for the existing close listener to fire — `settle()` does\n // all the flushing + onExit work. We just sit on a one-shot\n // promise tied to the `child.on('close')` we already registered\n // at spawn time.\n const closed = new Promise<void>((resolveP) => {\n if (state.settled) {\n resolveP()\n return\n }\n state.exitWatchers.push(() => resolveP())\n })\n\n killProcessGroup(state.child, 'SIGTERM')\n await closed\n return stateToTaskExitInfo(state)\n },\n\n async waitBackground(\n handle: ExecutionHandle,\n taskId: string,\n options?: { timeoutMs?: number, signal?: AbortSignal },\n ): Promise<TaskExitInfo | null> {\n const state = tasks.get(taskId)\n // Same two-misses-collapse-to-null contract as `killBackground`:\n // unknown id AND known-but-other-handle both return `null` so a\n // subagent can't observe the parent's tasks through the wait seam.\n if (!state || state.handleId !== handle.id)\n return null\n if (state.settled)\n return stateToTaskExitInfo(state)\n\n return new Promise<TaskExitInfo | null>((resolveP) => {\n let done = false\n let timer: NodeJS.Timeout | undefined\n const signal = options?.signal\n const finish = (value: TaskExitInfo | null): void => {\n if (done)\n return\n done = true\n if (timer)\n clearTimeout(timer)\n signal?.removeEventListener('abort', onAbort)\n const index = state.exitWatchers.indexOf(watcher)\n if (index >= 0)\n state.exitWatchers.splice(index, 1)\n resolveP(value)\n }\n function onAbort(): void {\n finish(null)\n }\n // A watcher alongside the owner's `onExit` (never replacing it —\n // the notification enqueue, a kill-waiter, a sibling waiter must\n // keep firing). Removed on timeout/abort so abandoned waits don't\n // pile up on a long-running task.\n const task = state\n function watcher(): void {\n finish(stateToTaskExitInfo(task))\n }\n state.exitWatchers.push(watcher)\n\n if (signal) {\n if (signal.aborted) {\n finish(null)\n return\n }\n signal.addEventListener('abort', onAbort, { once: true })\n }\n const timeoutMs = options?.timeoutMs\n if (typeof timeoutMs === 'number' && Number.isFinite(timeoutMs) && timeoutMs > 0) {\n timer = setTimeout(finish, timeoutMs, null)\n timer.unref?.()\n }\n })\n },\n\n async reassignBackgroundTasks(\n fromHandle: ExecutionHandle,\n toHandle: ExecutionHandle,\n newOnExit?: (info: TaskExitInfo) => void,\n newOnStall?: (info: TaskStallInfo) => void,\n ): Promise<readonly TaskEntry[]> {\n // `fromHandle === toHandle` keeps ownership and only rebinds the\n // callbacks — a host rebuilding the agent on a handle it keeps.\n const promoted: TaskEntry[] = []\n for (const state of tasks.values()) {\n if (state.handleId !== fromHandle.id || state.status !== 'running')\n continue\n state.handleId = toHandle.id\n // Replace the OWNER callbacks. The originals closed over the\n // spawning agent's hook bus, which is about to be destroyed —\n // without rewiring, the task's eventual `background:exit` (or a\n // stall) fires into a torn-down hookable and the new owner never\n // learns. In-flight kill/wait watchers are separate and survive.\n if (newOnExit)\n state.onExit = newOnExit\n if (newOnStall)\n state.onStall = newOnStall\n promoted.push(stateToTaskEntry(state))\n }\n return promoted\n },\n\n async listBackground(handle: ExecutionHandle): Promise<readonly TaskEntry[]> {\n // Snapshot — callers must not assume the returned array stays\n // in sync with the live registry. Sorted by startedAt so the\n // model / UI sees consistent ordering across calls. Scoped to\n // the calling handle so subagents don't see the parent's tasks\n // (and vice versa) in their listing.\n return [...tasks.values()]\n .filter(s => s.handleId === handle.id)\n .sort((a, b) => a.startedAt - b.startedAt)\n .map(stateToTaskEntry)\n },\n\n async destroy(handle: ExecutionHandle): Promise<void> {\n // Kill every still-running background task SPAWNED THROUGH THIS\n // HANDLE before tearing the handle down. SIGTERM the groups,\n // await the close + flush, THEN drop the registry entries.\n // Sequential — destroy is one-shot teardown, the few ms of extra\n // latency aren't worth the synchronization complexity.\n //\n // The handle scope matters when the same `ExecutionContext` is\n // shared across a parent agent and its `spawn`-ed subagents (the\n // default — `spawn.ts` passes `execution: ctx.execution`). Each\n // agent mints its own `ExecutionHandle` and registers its\n // background tasks under that handle's id. Without the filter,\n // a child agent's `destroy()` (fired by `spawn.ts`'s `finally`\n // when the subagent finishes / is cancelled) would walk the\n // shared registry and SIGTERM the parent's tasks too. So\n // cancelling a subagent that has its own background shells now\n // correctly kills JUST those subagent shells, leaving the\n // parent's intact.\n const survivors = [...tasks.values()].filter(s => s.handleId === handle.id && !s.settled)\n await Promise.all(survivors.map(async (state) => {\n state.killRequested = true\n await new Promise<void>((resolveP) => {\n let graceTimer: NodeJS.Timeout | undefined\n state.exitWatchers.push(() => {\n if (graceTimer)\n clearTimeout(graceTimer)\n resolveP()\n })\n killProcessGroup(state.child, 'SIGTERM')\n // Bounded drain: a task that traps/ignores SIGTERM — or whose\n // `close` event is held back by an escaped grandchild (setsid'd\n // out of the process group) keeping the inherited stdio pipe\n // open — must not wedge `agent.destroy()` forever. After the\n // grace period, SIGKILL the group and stop waiting: `close`\n // may STILL never fire (the pipe holder isn't in the group),\n // so resolving here is the only way destroy() stays bounded.\n graceTimer = setTimeout(() => {\n killProcessGroup(state.child, 'SIGKILL')\n // settle() normally flushes + ends the log stream, but it\n // only runs off `close` — which we just gave up on. Tear the\n // stream down so the fd doesn't leak into a long-lived host.\n state.outputStream?.destroy()\n resolveP()\n }, destroyGraceMs)\n graceTimer.unref?.()\n })\n }))\n // Drop only this handle's tasks from the registry. Other handles\n // (siblings, parent) keep their entries.\n for (const [taskId, state] of tasks) {\n if (state.handleId === handle.id)\n tasks.delete(taskId)\n }\n // Stream children are caller-owned (the MCP transport's close(), which\n // the agent runs BEFORE executionContext.destroy()), so live entries\n // here mean the owner leaked them. Sweep this handle's slice — same\n // scoping rationale as the task loop above.\n for (const [child, ownerId] of streamChildren) {\n if (ownerId === handle.id) {\n killProcessGroup(child, 'SIGTERM')\n streamChildren.delete(child)\n }\n }\n handles.delete(handle.id)\n // Drop the orphan reaper ONLY when no handles remain — otherwise\n // a child's `destroy()` would strip the parent's safety net. The\n // reaper protects every still-tracked task in the context, so it\n // sticks around until the LAST handle is gone.\n //\n // Without this guard, the spawn-tool sequence\n // parent.spawn(child) → child.run() → child.destroy() (auto)\n // would deregister the handler mid-parent-lifetime. The parent's\n // own subsequent Ctrl+C orphan-kill safety would silently degrade.\n if (s.exitHandlerRegistered && handles.size === 0) {\n process.off('exit', s.exitHandler)\n s.exitHandlerRegistered = false\n }\n },\n }\n}\n","/**\n * In-process execution context.\n *\n * Runs everything in the current Node/Bun process.\n * No isolation — fastest, used as the default.\n */\n\nimport type { ContextCapabilities, ExecutionContext, ExecutionHandle, SpawnConfig, TerminalOptions, TerminalProcess } from './types'\nimport { mkdir, stat } from 'node:fs/promises'\nimport { resolve } from 'node:path'\nimport { hasLocalPty, localTerminalProcess, loginShell, spawnLocalPty } from './local-terminal'\nimport { processBackgroundMethods } from './process-background'\nimport { processExecMethods } from './process-exec'\nimport { processFileMethods } from './process-files'\nimport { createProcessShell } from './process-shell'\nimport { createProcessState } from './process-state'\nimport { processStreamMethods } from './process-stream'\nimport { processTaskMethods } from './process-tasks'\n\nexport { formatContextTimestamp, killProcessGroup, SUPPORTS_PROCESS_GROUPS, TASK_LOG_FILENAME_RE } from './process-state'\n\nexport function createProcessContext(config?: SpawnConfig): ExecutionContext {\n const s = createProcessState(config)\n const { handles, defaultCwd, isolation, isolationState } = s\n const sh = createProcessShell(s)\n const { shellCommand, isolationBlocker, terminalEnv } = sh\n const files = processFileMethods(s)\n const execM = processExecMethods(s, sh)\n const streamM = processStreamMethods(s, sh)\n const bg = processBackgroundMethods(s, sh)\n const taskM = processTaskMethods(s)\n\n return {\n type: 'process',\n\n capabilities: {\n shell: true,\n filesystem: true,\n glob: true,\n network: true,\n gpu: false,\n stream: true,\n // Enforced, not requested: a requested sandbox whose backend is\n // missing fails every command closed, which is not \"isolated\".\n isolated: isolation !== undefined && isolationState?.ok === true,\n // Background tasks are OS children of this process's machine —\n // they survive `agent.destroy()` reassignment games but NOT a\n // host machine reboot, and the orphan reaper kills them on\n // process exit. That's the 'process-lifetime' tier.\n detachedTasks: 'process-lifetime',\n } satisfies ContextCapabilities,\n\n async spawn(overrides?: SpawnConfig): Promise<ExecutionHandle> {\n const id = `process-${++s.counter}`\n const cwd = overrides?.cwd ?? defaultCwd\n\n // Only create `cwd` when it is genuinely missing. An unconditional\n // `mkdir(cwd, { recursive: true })` on an already-existing directory is a\n // wasted write that needlessly demands FS-write permission — which breaks\n // read-only / no-write runtimes (Supabase Edge Functions write only to\n // `/tmp`; Deno edge isolates) where the cwd already exists. `stat` is a\n // read (permitted there); the write is attempted only on a confirmed\n // ENOENT. Any other stat failure (e.g. EACCES on a sandbox-restricted\n // path) must NOT escalate to a mkdir we'd also be denied — assume present\n // and let a later file op surface a precise error if it truly isn't.\n let needsCreate = false\n try {\n await stat(cwd)\n }\n catch (err) {\n needsCreate = (err as NodeJS.ErrnoException)?.code === 'ENOENT'\n }\n if (needsCreate)\n await mkdir(cwd, { recursive: true })\n\n const handle: ExecutionHandle = { id, type: 'process', cwd }\n handles.set(id, handle)\n return handle\n },\n\n exec: execM.exec,\n\n readFile: files.readFile,\n\n fileSize: files.fileSize,\n\n readFileBinary: files.readFileBinary,\n\n writeFile: files.writeFile,\n\n listFiles: files.listFiles,\n\n execStream: streamM.execStream,\n\n // Only where a PTY exists (Bun): no terminal beats a pretend one.\n ...(hasLocalPty()\n ? {\n async openTerminal(handle: ExecutionHandle, options: TerminalOptions): Promise<TerminalProcess> {\n const blocked = isolationBlocker()\n if (blocked)\n throw new Error(blocked.stderr)\n const env = terminalEnv(options.env, options.unsetEnv)\n const shell = loginShell(env)\n // Under OS isolation the shell runs inside the same sandbox as the agent's commands.\n const argv = isolation && isolationState?.ok\n ? (({ file, args }) => [file, ...args])(shellCommand(`exec ${JSON.stringify(shell)} -l`))\n : [shell, '-l']\n const pty = spawnLocalPty(argv, {\n cwd: options.cwd ? resolve(handle.cwd, options.cwd) : handle.cwd,\n env,\n cols: options.cols,\n rows: options.rows,\n onData: options.onData,\n })\n return localTerminalProcess(pty, { id: `pty-${pty.pid}` })\n },\n }\n : {}),\n\n execBackground: bg.execBackground,\n\n killBackground: taskM.killBackground,\n\n waitBackground: taskM.waitBackground,\n\n reassignBackgroundTasks: taskM.reassignBackgroundTasks,\n\n listBackground: taskM.listBackground,\n\n destroy: taskM.destroy,\n }\n}\n"],"mappings":"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"}
|
package/dist/contexts.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { C as
|
|
2
|
-
import { t as createProcessContext } from "./index-
|
|
3
|
-
import { n as createSandboxContext, t as SandboxProvider } from "./sandbox-
|
|
1
|
+
import { C as TaskExitInfo, O as resolveDetachedTasksCapability, S as TaskEntry, T as TaskStallInfo, _ as ListFileMetadata, a as resolveWritableRoots, b as SpawnConfig, d as ContextMount, f as ContextType, g as ExecutionHandle, h as ExecutionContext, i as parseAllowWriteSetting, l as ContextCapabilities, m as ExecResult, n as IsolationPolicy, o as resolveWritableRootsAsync, p as DetachedTasksCapability, r as isolationAvailability, s as BackgroundTaskStatus, t as IsolationBackend, u as ContextHardening, v as ListFilesEntry, w as TaskHandle, x as StreamHandle, y as ListFilesOptions } from "./isolation-D9L650W5.js";
|
|
2
|
+
import { t as createProcessContext } from "./index-D6a7qXpg.js";
|
|
3
|
+
import { n as createSandboxContext, t as SandboxProvider } from "./sandbox-C5dR5DwQ.js";
|
|
4
4
|
export { type BackgroundTaskStatus, type ContextCapabilities, type ContextHardening, type ContextMount, type ContextType, type DetachedTasksCapability, type ExecResult, type ExecutionContext, type ExecutionHandle, type IsolationBackend, type IsolationPolicy, type ListFileMetadata, type ListFilesEntry, type ListFilesOptions, type SandboxProvider, type SpawnConfig, type StreamHandle, type TaskEntry, type TaskExitInfo, type TaskHandle, type TaskStallInfo, createProcessContext, createSandboxContext, isolationAvailability, parseAllowWriteSetting, resolveDetachedTasksCapability, resolveWritableRoots, resolveWritableRootsAsync };
|
package/dist/contexts.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { c as isolationAvailability, d as resolveWritableRoots, f as resolveWritableRootsAsync, l as parseAllowWriteSetting, p as resolveDetachedTasksCapability, t as createProcessContext } from "./contexts-d7UolYot.js";
|
|
2
2
|
import { createSandboxContext } from "./contexts/sandbox.js";
|
|
3
3
|
export { createProcessContext, createSandboxContext, isolationAvailability, parseAllowWriteSetting, resolveDetachedTasksCapability, resolveWritableRoots, resolveWritableRootsAsync };
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
//#region src/chat/cred-keys.ts
|
|
2
|
+
/**
|
|
3
|
+
* Credential-file key arithmetic, split from `./credentials` so the rules can
|
|
4
|
+
* be enforced where credentials themselves must not be pulled in.
|
|
5
|
+
*
|
|
6
|
+
* `./credentials` reaches the provider registry (and through it the pi-ai
|
|
7
|
+
* factories) for `credKeyOf`; the extension loader — an edge entrypoint that
|
|
8
|
+
* validates descriptor keys at registration time — must stay clear of that
|
|
9
|
+
* graph. This module is string arithmetic with no imports.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Separator between a provider's canonical key and a slot qualifier. Every
|
|
13
|
+
* non-canonical slot (`#alt`, `#account:N`) starts with it, so the character is
|
|
14
|
+
* reserved in descriptor credential keys outright.
|
|
15
|
+
*/
|
|
16
|
+
const CRED_KEY_SLOT_SEPARATOR = "#";
|
|
17
|
+
/**
|
|
18
|
+
* Reserved suffix marking a provider's SECOND credential slot.
|
|
19
|
+
*
|
|
20
|
+
* Not usable as (or as the tail of) a descriptor's credential key: `foo#alt`
|
|
21
|
+
* holds `foo`'s alternate credential, so a provider claiming that name would
|
|
22
|
+
* share storage with it.
|
|
23
|
+
*/
|
|
24
|
+
const ALTERNATE_CRED_KEY_SUFFIX = "#alt";
|
|
25
|
+
/**
|
|
26
|
+
* Reserved infix marking one of a provider's ADDITIONAL OAuth accounts:
|
|
27
|
+
* `foo#account:1`, `foo#account:2`, …
|
|
28
|
+
*/
|
|
29
|
+
const ACCOUNT_CRED_KEY_INFIX = "#account:";
|
|
30
|
+
/**
|
|
31
|
+
* File key holding a provider's second credential, when it has one of each kind.
|
|
32
|
+
*
|
|
33
|
+
* The canonical key always holds the credential the user prefers; this sibling
|
|
34
|
+
* holds the other. That one invariant is what makes preference selection free:
|
|
35
|
+
* every existing reader (`applyApiKeyEnv`, the harness provider factories)
|
|
36
|
+
* already resolves the canonical key, so "switch priority" is just moving two
|
|
37
|
+
* objects between two keys — no precedence flag to interpret, no resolution
|
|
38
|
+
* order to keep in sync across layers.
|
|
39
|
+
*
|
|
40
|
+
* A sibling key rather than a nested field because the canonical entry gets
|
|
41
|
+
* REPLACED wholesale on token refresh; anything nested inside it would be
|
|
42
|
+
* dropped by the next rotation. And older zidane versions look credentials up
|
|
43
|
+
* by `credKeyOf`, so a suffixed key is simply invisible to them — they read the
|
|
44
|
+
* preferred credential and behave exactly as before, which is why this needs no
|
|
45
|
+
* migration or schema version.
|
|
46
|
+
*/
|
|
47
|
+
function alternateCredKey(fileKey) {
|
|
48
|
+
return `${fileKey}${ALTERNATE_CRED_KEY_SUFFIX}`;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* File key holding one of a provider's non-active OAuth accounts.
|
|
52
|
+
*
|
|
53
|
+
* Same sibling-key reasoning as {@link alternateCredKey}: the canonical key is
|
|
54
|
+
* the active one, so a provider with three OAuth grants stores the active one
|
|
55
|
+
* canonically and the other two under `#account:N`. `#alt` stays dedicated to
|
|
56
|
+
* the other-kind (API key) alternate so files written by builds that predate
|
|
57
|
+
* multi-account keep meaning the same thing.
|
|
58
|
+
*/
|
|
59
|
+
function accountCredKey(fileKey, n) {
|
|
60
|
+
return `${fileKey}${ACCOUNT_CRED_KEY_INFIX}${n}`;
|
|
61
|
+
}
|
|
62
|
+
/** True when `key` is one of `fileKey`'s `#account:N` slots. */
|
|
63
|
+
function isAccountCredKey(fileKey, key) {
|
|
64
|
+
const prefix = `${fileKey}${ACCOUNT_CRED_KEY_INFIX}`;
|
|
65
|
+
return key.startsWith(prefix) && /^\d+$/.test(key.slice(prefix.length));
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Smallest `#account:N` key of `fileKey` not present in `existing`.
|
|
69
|
+
*
|
|
70
|
+
* Deterministic rather than random so a file hand-edited or written by two
|
|
71
|
+
* processes in sequence stays readable; the write lock keeps it collision-free.
|
|
72
|
+
*/
|
|
73
|
+
function nextAccountCredKey(fileKey, existing) {
|
|
74
|
+
const taken = new Set(existing);
|
|
75
|
+
for (let n = 1;; n++) {
|
|
76
|
+
const key = accountCredKey(fileKey, n);
|
|
77
|
+
if (!taken.has(key)) return key;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Name shown for an OAuth account that was never given one.
|
|
82
|
+
*
|
|
83
|
+
* Also what a first OAuth login is stored under, so a second account has
|
|
84
|
+
* something to be told apart from. Pre-multi-account files hold unnamed
|
|
85
|
+
* grants; {@link oauthAccountLabel} makes them read the same.
|
|
86
|
+
*/
|
|
87
|
+
const DEFAULT_OAUTH_ACCOUNT_LABEL = "Default";
|
|
88
|
+
/** Trimmed user-given account name, or {@link DEFAULT_OAUTH_ACCOUNT_LABEL}. */
|
|
89
|
+
function oauthAccountLabel(credential) {
|
|
90
|
+
return (typeof credential.label === "string" ? credential.label.trim() : "") || "Default";
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* True when a key claims a reserved slot namespace.
|
|
94
|
+
*
|
|
95
|
+
* Built-in keys are plain identifiers (`anthropic`, `openai-codex`), but
|
|
96
|
+
* `ProviderDescriptor.key` / `credentialFileKey` are open strings — so the
|
|
97
|
+
* reservation is checked, not assumed.
|
|
98
|
+
*/
|
|
99
|
+
function isReservedCredKey(fileKey) {
|
|
100
|
+
return fileKey.includes("#");
|
|
101
|
+
}
|
|
102
|
+
//#endregion
|
|
103
|
+
export { accountCredKey as a, isReservedCredKey as c, DEFAULT_OAUTH_ACCOUNT_LABEL as i, nextAccountCredKey as l, ALTERNATE_CRED_KEY_SUFFIX as n, alternateCredKey as o, CRED_KEY_SLOT_SEPARATOR as r, isAccountCredKey as s, ACCOUNT_CRED_KEY_INFIX as t, oauthAccountLabel as u };
|
|
104
|
+
|
|
105
|
+
//# sourceMappingURL=cred-keys-GpBg71cP.js.map
|