farming-code 2.2.30 → 2.2.32
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/THIRD_PARTY_NOTICES.md +7 -0
- package/backend/acp/{client-services.js → client-services.cjs} +132 -134
- package/backend/acp/packaged-claude-acp.cjs +37 -0
- package/backend/acp/packaged-codex-acp.cjs +37 -0
- package/backend/acp/patch-decisions.cjs +182 -0
- package/backend/acp/permission-security.cjs +131 -0
- package/backend/{acp-checkpoint-store.js → acp-checkpoint-store.cjs} +90 -59
- package/backend/{acp-runtime.js → acp-runtime.cjs} +1220 -1112
- package/backend/acp-session-state.cjs +622 -0
- package/backend/acp-transcript.cjs +525 -0
- package/backend/agent-env.cjs +257 -0
- package/backend/agent-extension-discovery.cjs +452 -0
- package/backend/agent-extension-inventory.cjs +116 -0
- package/backend/agent-json-stream.cjs +283 -0
- package/backend/agent-lifecycle-journal.cjs +185 -0
- package/backend/{agent-manager.js → agent-manager.cjs} +3116 -4275
- package/backend/agent-order-transaction.cjs +85 -0
- package/backend/agent-order.cjs +159 -0
- package/backend/agent-provider-session.cjs +98 -0
- package/backend/agent-runtime-binding.cjs +179 -0
- package/backend/agent-session-history.cjs +1022 -0
- package/backend/agent-session-inventory.cjs +291 -0
- package/backend/async-cache.cjs +104 -0
- package/backend/atomic-json-store.cjs +67 -0
- package/backend/auth.cjs +261 -0
- package/backend/authoritative-inventory-cache.cjs +383 -0
- package/backend/business-health.cjs +68 -0
- package/backend/chat-runtime.cjs +50 -0
- package/backend/claude-settings.cjs +132 -0
- package/backend/cli-agents.cjs +435 -0
- package/backend/codex-context-window.cjs +196 -0
- package/backend/codex-models.cjs +228 -0
- package/backend/codex-session-archive.cjs +82 -0
- package/backend/codex-session-history.cjs +590 -0
- package/backend/{codex-terminal-profile.js → codex-terminal-profile.cjs} +149 -155
- package/backend/codex-transcript-sanitizer.cjs +130 -0
- package/backend/codex-transcript.cjs +2107 -0
- package/backend/command-runner-child.cjs +105 -0
- package/backend/config-manager.cjs +1263 -0
- package/backend/control-api.cjs +439 -0
- package/backend/data/LICENSE.cua +21 -0
- package/backend/executable-discovery.cjs +204 -0
- package/backend/farming-agent-bootstrap.cjs +81 -0
- package/backend/farming-agent-bootstrap.zh_cn.md +6 -2
- package/backend/{farming-app-cli.js → farming-app-cli.cjs} +441 -493
- package/backend/farming-cli.cjs +414 -0
- package/backend/farming-net-pass.cjs +319 -0
- package/backend/farming-net-registry.cjs +123 -0
- package/backend/farming-net-server.cjs +309 -0
- package/backend/farming-session-store.cjs +651 -0
- package/backend/git-worktree-info.cjs +181 -0
- package/backend/haiku-token.cjs +828 -0
- package/backend/index-html.cjs +96 -0
- package/backend/input-parts.cjs +68 -0
- package/backend/input-routing.cjs +40 -0
- package/backend/{local-session-engine.js → local-session-engine.cjs} +276 -282
- package/backend/main-agent-skills.cjs +309 -0
- package/backend/main-page-session.cjs +132 -0
- package/backend/native-pty-controller-generation.cjs +139 -0
- package/backend/{native-pty-host-client.js → native-pty-host-client.cjs} +338 -346
- package/backend/native-pty-host-identity.cjs +125 -0
- package/backend/native-pty-host-path.cjs +68 -0
- package/backend/{native-pty-host.js → native-pty-host.cjs} +480 -569
- package/backend/native-session-engine.cjs +296 -0
- package/backend/network.cjs +57 -0
- package/backend/npm-update-helper.cjs +399 -0
- package/backend/packaged-node-pty.cjs +120 -0
- package/backend/preview-session-manager.cjs +89 -0
- package/backend/provider-adapters.cjs +492 -0
- package/backend/provider-home-configuration.cjs +211 -0
- package/backend/provider-session-id.cjs +79 -0
- package/backend/provider-session-service.cjs +333 -0
- package/backend/qr-share-tickets.cjs +119 -0
- package/backend/resource-broadcast-protocol.cjs +50 -0
- package/backend/review-diff-router.cjs +202 -0
- package/backend/review-diff-service.cjs +1267 -0
- package/backend/review-session-router.cjs +93 -0
- package/backend/review-session-service.cjs +470 -0
- package/backend/review-session-store.cjs +161 -0
- package/backend/review-state-router.cjs +132 -0
- package/backend/review-state-store.cjs +299 -0
- package/backend/run-history-store.cjs +97 -0
- package/backend/runtime-dependency-manager.cjs +893 -0
- package/backend/runtime-executable-invocation.cjs +37 -0
- package/backend/runtime-observation.cjs +96 -0
- package/backend/server-process-identity.cjs +70 -0
- package/backend/server.cjs +3356 -0
- package/backend/session-engine-bridge.cjs +144 -0
- package/backend/session-engine-router.cjs +105 -0
- package/backend/session-engine.cjs +64 -0
- package/backend/session-stream-protocol.cjs +181 -0
- package/backend/shell-busy-integration.cjs +442 -0
- package/backend/slash-command-discovery.cjs +423 -0
- package/backend/storage-layout.cjs +263 -0
- package/backend/system-monitor.cjs +96 -0
- package/backend/terminal-attach-checkpoint.cjs +73 -0
- package/backend/terminal-exit-quiescence.cjs +57 -0
- package/backend/{terminal-reducer-flow-control.js → terminal-reducer-flow-control.cjs} +45 -22
- package/backend/terminal-runtime-cleanup.cjs +99 -0
- package/backend/{terminal-screen-state.js → terminal-screen-state.cjs} +100 -108
- package/backend/{terminal-screen-worker-pool.js → terminal-screen-worker-pool.cjs} +89 -79
- package/backend/{terminal-screen-worker-thread.js → terminal-screen-worker-thread.cjs} +64 -65
- package/backend/terminal-screen-worker.cjs +352 -0
- package/backend/terminal-state-serialization.cjs +147 -0
- package/backend/{terminal-status.js → terminal-status.cjs} +137 -187
- package/backend/theme-manager.cjs +185 -0
- package/backend/{update-service.js → update-service.cjs} +258 -248
- package/backend/usage-forecast.cjs +95 -0
- package/backend/{usage-history-client.js → usage-history-client.cjs} +144 -94
- package/backend/usage-history-smoke.cjs +85 -0
- package/backend/usage-history-worker.cjs +60 -0
- package/backend/{usage-monitor.js → usage-monitor.cjs} +363 -390
- package/backend/workspace-directory.cjs +239 -0
- package/backend/workspace-discovery.cjs +271 -0
- package/backend/{workspace-file-router.js → workspace-file-router.cjs} +213 -282
- package/backend/{workspace-file-service.js → workspace-file-service.cjs} +1139 -1318
- package/backend/workspace-root-registry.cjs +192 -0
- package/bin/farming +1 -1
- package/dist/acp/codex-acp-1.1.4.mjs +18 -1
- package/dist/assets/App-BCkdpWB6.css +1 -0
- package/dist/assets/App-CRjKpaGp.js +697 -0
- package/dist/assets/CodeSelect-Ci8sKZPU.js +1 -0
- package/dist/assets/CodeSelect-T2eNzrOc.css +1 -0
- package/dist/assets/{FileEditorMarkdownPreview-Cs9cLv8a.js → FileEditorMarkdownPreview-V06QHlAg.js} +55 -55
- package/dist/assets/FileEditorPane-BX-Wjg9U.js +2 -0
- package/dist/assets/IconGlyphs-YhV7tqd4.js +1 -0
- package/dist/assets/ProjectFilesSection-BDT2_r0o.js +12 -0
- package/dist/assets/ReviewPage-BqAs5sG1.js +3 -0
- package/dist/assets/code-dark-B7eM_9G9.css +1 -0
- package/dist/assets/{cssMode-Bdg5RpcJ.js → cssMode-ADdAlxTO.js} +1 -1
- package/dist/assets/{editor.api2-D74ufkbr.js → editor.api2-BFLAr0YF.js} +162 -162
- package/dist/assets/file-icons-BpeqWkIB.js +1 -0
- package/dist/assets/{freemarker2-DfMDJfnE.js → freemarker2-C90q5jrp.js} +1 -1
- package/dist/assets/{handlebars-BEdPtzMT.js → handlebars-BwUQcEDq.js} +1 -1
- package/dist/assets/{html-B1fPb1QD.js → html-1s3uPKfh.js} +1 -1
- package/dist/assets/{htmlMode-CY2ZjHxH.js → htmlMode-xtKD99de.js} +1 -1
- package/dist/assets/{index-D-zetSol.js → index-C8MchCem.js} +3 -3
- package/dist/assets/{javascript-BV2OsWA0.js → javascript-Ph7ijyny.js} +1 -1
- package/dist/assets/{jsonMode-zd07aeuX.js → jsonMode-D-jGAnFH.js} +1 -1
- package/dist/assets/linkComputer-CbGyI_qx.js +1 -0
- package/dist/assets/{liquid-BParI24X.js → liquid-0IamE_cI.js} +1 -1
- package/dist/assets/{lspLanguageFeatures-BzredkoV.js → lspLanguageFeatures-BxnQunWF.js} +1 -1
- package/dist/assets/main-CLKXa6hf.css +1 -0
- package/dist/assets/{mdx-vGzUJN98.js → mdx-DdNmfivn.js} +1 -1
- package/dist/assets/{monaco.contribution-BmaeBQEc.js → monaco.contribution-B43jsUs1.js} +2 -2
- package/dist/assets/{python-CUwdLgsI.js → python-IAt4X5TS.js} +1 -1
- package/dist/assets/{razor-BScebUxU.js → razor-DRkFYW0S.js} +1 -1
- package/dist/assets/review-C8-oPKXk.css +1 -0
- package/dist/assets/{tsMode-DzUAibXK.js → tsMode-_d_4U2gf.js} +1 -1
- package/dist/assets/{typescript-D9EOeM7N.js → typescript-C7zcMq9l.js} +1 -1
- package/dist/assets/{workers-DAiomYZM.js → workers-D0nOyJzf.js} +1 -1
- package/dist/assets/workspace-editor-model-DND_Y-Bj.js +1 -0
- package/dist/assets/workspace-editor-monaco-BPWhgcIB.js +4 -0
- package/dist/assets/workspace-editor-monaco-N641qPjX.js +1 -0
- package/dist/assets/workspace-view-state-k1bTlJ_Z.js +13 -0
- package/dist/assets/{xml-wWl1dhkQ.js → xml-BR-HvxT8.js} +1 -1
- package/dist/assets/{yaml-Dc2WDaOZ.js → yaml-YtFQwh4q.js} +1 -1
- package/dist/index.html +1 -1
- package/extensions/browser/backend/agent-browser-runtime.cjs +1040 -0
- package/extensions/browser/backend/agent-capability.cjs +85 -0
- package/extensions/browser/backend/browser-mcp-server.cjs +655 -0
- package/extensions/browser/backend/browser-resource-manager.cjs +1486 -0
- package/extensions/browser/backend/browser-resource-store.cjs +228 -0
- package/extensions/browser/backend/browser-router.cjs +203 -0
- package/extensions/browser/backend/executable-discovery.cjs +248 -0
- package/extensions/browser/backend/farming-browser-client.cjs +116 -0
- package/extensions/browser/backend/index.cjs +32 -0
- package/extensions/browser/backend/managed-chromium-installer.cjs +833 -0
- package/extensions/browser/bin/farming-browser +16 -7
- package/extensions/computer/backend/agent-capability.cjs +72 -0
- package/extensions/computer/backend/computer-constants.cjs +59 -0
- package/extensions/computer/backend/computer-mcp-server.cjs +182 -0
- package/extensions/computer/backend/computer-resource-manager.cjs +1219 -0
- package/extensions/computer/backend/computer-resource-store.cjs +190 -0
- package/extensions/computer/backend/computer-router.cjs +257 -0
- package/extensions/computer/backend/cua-tools.json +3067 -0
- package/extensions/computer/backend/farming-computer-client.cjs +84 -0
- package/extensions/computer/backend/index.cjs +35 -0
- package/extensions/computer/backend/isolated-browser-provider.cjs +157 -0
- package/extensions/computer/bin/farming-computer +131 -0
- package/frontend/farming-net/app.js +255 -262
- package/frontend/ghostty-loader.js +24 -20
- package/frontend/reading-anchor.js +210 -193
- package/frontend/runtime-paths.js +24 -26
- package/frontend/session-bridge.js +90 -111
- package/frontend/session-modal-bridge.js +184 -302
- package/frontend/skin-bridge.js +20 -23
- package/frontend/skins/crt/app.js +7686 -7901
- package/frontend/skins/crt/effects.js +7 -6
- package/frontend/skins/crt/index.html +6 -6
- package/frontend/skins/crt/styles/markdown.css +4 -4
- package/frontend/terminal-bridge.js +174 -181
- package/frontend/terminal-replay.js +320 -345
- package/frontend/theme-loader.js +55 -71
- package/package.json +64 -37
- package/shared/backend-connection-status.js +38 -69
- package/shared/browser-protocol.js +235 -110
- package/shared/websocket-liveness.js +40 -0
- package/backend/acp/packaged-claude-acp.js +0 -13
- package/backend/acp/packaged-codex-acp.js +0 -13
- package/backend/acp/patch-decisions.js +0 -153
- package/backend/acp/permission-security.js +0 -108
- package/backend/acp-session-state.js +0 -634
- package/backend/acp-transcript.js +0 -512
- package/backend/agent-env.js +0 -250
- package/backend/agent-json-stream.js +0 -268
- package/backend/agent-lifecycle-journal.js +0 -133
- package/backend/agent-order.js +0 -166
- package/backend/agent-provider-session.js +0 -76
- package/backend/agent-runtime-binding.js +0 -174
- package/backend/agent-session-history.js +0 -986
- package/backend/async-cache.js +0 -94
- package/backend/atomic-json-store.js +0 -37
- package/backend/auth.js +0 -251
- package/backend/chat-runtime.js +0 -28
- package/backend/claude-settings.js +0 -121
- package/backend/cli-agents.js +0 -486
- package/backend/codex-context-window.js +0 -189
- package/backend/codex-models.js +0 -226
- package/backend/codex-session-archive.js +0 -65
- package/backend/codex-session-history.js +0 -647
- package/backend/codex-transcript-sanitizer.js +0 -115
- package/backend/codex-transcript.js +0 -2362
- package/backend/command-runner-child.js +0 -67
- package/backend/config-manager.js +0 -1117
- package/backend/control-api.js +0 -455
- package/backend/executable-discovery.js +0 -216
- package/backend/farming-agent-bootstrap.js +0 -49
- package/backend/farming-cli.js +0 -403
- package/backend/farming-net-pass.js +0 -285
- package/backend/farming-net-registry.js +0 -112
- package/backend/farming-net-server.js +0 -298
- package/backend/farming-session-store.js +0 -677
- package/backend/git-worktree-info.js +0 -181
- package/backend/haiku-token.js +0 -772
- package/backend/index-html.js +0 -80
- package/backend/input-parts.js +0 -54
- package/backend/input-routing.js +0 -24
- package/backend/json-cli-runtime.js +0 -275
- package/backend/main-agent-skills.js +0 -288
- package/backend/main-page-session.js +0 -122
- package/backend/native-pty-controller-generation.js +0 -106
- package/backend/native-pty-host-identity.js +0 -86
- package/backend/native-pty-host-path.js +0 -42
- package/backend/native-session-engine.js +0 -268
- package/backend/network.js +0 -23
- package/backend/npm-update-helper.js +0 -374
- package/backend/packaged-node-pty.js +0 -99
- package/backend/preview-session-manager.js +0 -68
- package/backend/provider-adapters.js +0 -399
- package/backend/provider-session-id.js +0 -47
- package/backend/provider-session-service.js +0 -345
- package/backend/qr-share-tickets.js +0 -100
- package/backend/review-diff-router.js +0 -180
- package/backend/review-diff-service.js +0 -1270
- package/backend/review-session-router.js +0 -74
- package/backend/review-session-service.js +0 -446
- package/backend/review-session-store.js +0 -151
- package/backend/review-state-router.js +0 -116
- package/backend/review-state-store.js +0 -286
- package/backend/run-history-store.js +0 -67
- package/backend/runtime-dependency-manager.js +0 -759
- package/backend/runtime-executable-invocation.js +0 -20
- package/backend/runtime-observation.js +0 -81
- package/backend/server-process-identity.js +0 -47
- package/backend/server.js +0 -3225
- package/backend/session-engine-bridge.js +0 -129
- package/backend/session-engine-router.js +0 -66
- package/backend/session-engine.js +0 -39
- package/backend/session-stream-protocol.js +0 -185
- package/backend/shell-busy-integration.js +0 -472
- package/backend/slash-command-discovery.js +0 -433
- package/backend/storage-layout.js +0 -205
- package/backend/system-monitor.js +0 -77
- package/backend/terminal-attach-checkpoint.js +0 -74
- package/backend/terminal-exit-quiescence.js +0 -39
- package/backend/terminal-runtime-cleanup.js +0 -80
- package/backend/terminal-screen-worker.js +0 -337
- package/backend/terminal-state-serialization.js +0 -127
- package/backend/theme-manager.js +0 -163
- package/backend/usage-forecast.js +0 -86
- package/backend/usage-history-scanner.ts +0 -2718
- package/backend/usage-history-smoke.js +0 -64
- package/backend/usage-history-worker.js +0 -34
- package/backend/workspace-directory.js +0 -232
- package/backend/workspace-discovery.js +0 -311
- package/backend/workspace-root-registry.js +0 -166
- package/dist/assets/App-BKNUILuL.css +0 -1
- package/dist/assets/App-Z1N7T0eM.js +0 -695
- package/dist/assets/FileEditorPane-CNtuGjQ3.js +0 -2
- package/dist/assets/IconGlyphs-BClgag6E.js +0 -1
- package/dist/assets/ProjectFilesSection-K3GHuQ4U.js +0 -12
- package/dist/assets/ReviewPage-BRuxFaH5.js +0 -3
- package/dist/assets/code-dark-kLsQLvpf.css +0 -1
- package/dist/assets/file-icons-BOlcJ0ZF.js +0 -1
- package/dist/assets/main-DUTz-Rd2.css +0 -1
- package/dist/assets/review-BcnwQtJv.css +0 -1
- package/dist/assets/workspace-editor-model-CSGCl21x.js +0 -1
- package/dist/assets/workspace-editor-monaco-CJNwbpCy.js +0 -4
- package/dist/assets/workspace-editor-monaco-CO9w8YV0.js +0 -1
- package/dist/assets/workspace-view-state-BjKPYp0E.js +0 -7
- package/extensions/browser/backend/agent-browser-runtime.js +0 -1041
- package/extensions/browser/backend/agent-capability.js +0 -55
- package/extensions/browser/backend/browser-mcp-server.js +0 -621
- package/extensions/browser/backend/browser-resource-manager.js +0 -1331
- package/extensions/browser/backend/browser-resource-store.js +0 -190
- package/extensions/browser/backend/browser-router.js +0 -141
- package/extensions/browser/backend/executable-discovery.js +0 -238
- package/extensions/browser/backend/farming-browser-client.js +0 -83
- package/extensions/browser/backend/index.js +0 -7
- package/extensions/browser/backend/managed-chromium-installer.js +0 -593
- package/extensions/browser/frontend/BrowserSidebarPortals.tsx +0 -360
- package/extensions/browser/frontend/BrowserViewer.tsx +0 -500
- package/extensions/browser/frontend/browser-resource-state.ts +0 -60
- package/extensions/browser/frontend/browser.css +0 -567
- package/extensions/browser/frontend/types.ts +0 -38
- package/extensions/browser/frontend/useBrowserResources.ts +0 -166
- package/frontend/vendor/ghostty-web/index.d.ts +0 -1842
- package/shared/backend-connection-status.d.ts +0 -37
- package/shared/browser-protocol.d.ts +0 -5
- /package/dist/assets/{api-w2x5QfxB.js → api-ByCV9MEr.js} +0 -0
- /package/dist/assets/{core-vPkV8P_m.js → core-Czwdx12k.js} +0 -0
- /package/dist/assets/{fsharp-DNZNZyEE.js → fsharp-BjPpkoJe.js} +0 -0
- /package/dist/assets/{go-_kPJjNc_.js → go-BkIDmSUP.js} +0 -0
- /package/dist/assets/{graphql-4HCNVt5g.js → graphql-CCJTpqi1.js} +0 -0
- /package/dist/assets/{hcl-C87ymY0o.js → hcl-s4z8lUFg.js} +0 -0
- /package/dist/assets/{ini-BYrrGNTc.js → ini-CyUW1I7Z.js} +0 -0
- /package/dist/assets/{java-B211rJbk.js → java-CFCcGRiM.js} +0 -0
- /package/dist/assets/{julia-DVPsRtBm.js → julia-DQyH_EYb.js} +0 -0
- /package/dist/assets/{kotlin-BYyNerjp.js → kotlin-B8hA5O51.js} +0 -0
- /package/dist/assets/{less-EC9UblGE.js → less-DaVdaas0.js} +0 -0
- /package/dist/assets/{lexon-B1yuUdqW.js → lexon-DLBhPd3y.js} +0 -0
- /package/dist/assets/{lua-Bo6GFzxT.js → lua-gB1c_cif.js} +0 -0
- /package/dist/assets/{m3-C-Y9cBVI.js → m3-C2T3z3gB.js} +0 -0
- /package/dist/assets/{markdown-BHu-wLbe.js → markdown-CKq-Euj1.js} +0 -0
- /package/dist/assets/{mips-Dkds26rX.js → mips-Bcs5qIDX.js} +0 -0
- /package/dist/assets/{msdax-PU4g-8_g.js → msdax-CBP48FHF.js} +0 -0
- /package/dist/assets/{mysql-fPpuGpS7.js → mysql-c7tiPcFj.js} +0 -0
- /package/dist/assets/{objective-c-Wy1e5JUf.js → objective-c-D4BZkeEV.js} +0 -0
- /package/dist/assets/{pascal-BIXwvrqU.js → pascal-CJDZg0MN.js} +0 -0
- /package/dist/assets/{pascaligo-DwR0AtL_.js → pascaligo-Bygi8Ynl.js} +0 -0
- /package/dist/assets/{perl-CGHAl8my.js → perl-aS8bkf7E.js} +0 -0
- /package/dist/assets/{pgsql-B91M73E2.js → pgsql-CzNAYFYl.js} +0 -0
- /package/dist/assets/{php-C70ttrCx.js → php-DOtoEFTT.js} +0 -0
- /package/dist/assets/{pla-BNrXaRh0.js → pla-DugRnao1.js} +0 -0
- /package/dist/assets/{postiats-Dv2IAZR9.js → postiats-DsXwusKh.js} +0 -0
- /package/dist/assets/{powerquery-CkY5Wq7J.js → powerquery-DL3o72oD.js} +0 -0
- /package/dist/assets/{powershell-uGIYLU3C.js → powershell-C1qHekys.js} +0 -0
- /package/dist/assets/{protobuf-CavfmJu-.js → protobuf-DHgsc-cH.js} +0 -0
- /package/dist/assets/{pug-Drqnyitx.js → pug-FZXlE_Iu.js} +0 -0
- /package/dist/assets/{qrcode-7uGsTEmi.js → qrcode-DJDTdjL7.js} +0 -0
- /package/dist/assets/{qsharp-C7yF-Dti.js → qsharp-bLyhI3DT.js} +0 -0
- /package/dist/assets/{r-HXLGP0_-.js → r-DjFGXbAa.js} +0 -0
- /package/dist/assets/{redis-C4y6mf2p.js → redis-DSoZ_V6u.js} +0 -0
- /package/dist/assets/{redshift-DdQrFPrv.js → redshift-CzkdCsrg.js} +0 -0
- /package/dist/assets/{restructuredtext-CBrf2PNj.js → restructuredtext-B_XPrGCQ.js} +0 -0
- /package/dist/assets/{ruby-CVtMoX2B.js → ruby-Cry2RneJ.js} +0 -0
- /package/dist/assets/{rust-Ckq4sbMP.js → rust-CWzdbFq1.js} +0 -0
- /package/dist/assets/{sb-er66OpE3.js → sb-DmofU59h.js} +0 -0
- /package/dist/assets/{scala-BdWZz-hH.js → scala-BUC2l-vN.js} +0 -0
- /package/dist/assets/{scheme-kTV_s2kP.js → scheme-CNIB_72f.js} +0 -0
- /package/dist/assets/{scss-Bymy2VJQ.js → scss-DcL4-NbX.js} +0 -0
- /package/dist/assets/{shell-bwEP1LQ1.js → shell-CgAGvSSA.js} +0 -0
- /package/dist/assets/{solidity-DCddeBgL.js → solidity-DSkt6O_P.js} +0 -0
- /package/dist/assets/{sophia-BMN3AQig.js → sophia--5ytcjDN.js} +0 -0
- /package/dist/assets/{sparql-CSXOLjzi.js → sparql-C1Srr7cD.js} +0 -0
- /package/dist/assets/{sql-COg8ewmK.js → sql-2k_O5fj7.js} +0 -0
- /package/dist/assets/{st-CIaZtBS_.js → st-DCCCPqfz.js} +0 -0
- /package/dist/assets/{swift-DXiOhUHu.js → swift-Dd8mAfOt.js} +0 -0
- /package/dist/assets/{systemverilog-BTDE6Dr2.js → systemverilog-B0-aZYXu.js} +0 -0
- /package/dist/assets/{tcl-B0zy9NvV.js → tcl-CZAZwPVX.js} +0 -0
- /package/dist/assets/{twig-D594l4Z1.js → twig-D-Ag0tdS.js} +0 -0
- /package/dist/assets/{typespec-XKVjaPqo.js → typespec-B7kTFp0Z.js} +0 -0
- /package/dist/assets/{useWorkspaceMenuKeyboard-Bpy_s69Z.js → useWorkspaceMenuKeyboard-uYZpDxhU.js} +0 -0
- /package/dist/assets/{vb-B2wYoKRV.js → vb-20w1JPum.js} +0 -0
- /package/dist/assets/{wgsl-D0Yf2LbK.js → wgsl-CLJfqFt0.js} +0 -0
- /package/dist/assets/{workspace-working-copy-C3FSR3N6.js → workspace-working-copy-Cfz07cOd.js} +0 -0
|
@@ -1,81 +1,125 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
1
|
+
// Generated by scripts/build-backend-runtime.ts. Do not edit.
|
|
2
|
+
"use strict";
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __export = (target, all) => {
|
|
10
|
+
for (var name in all)
|
|
11
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
12
|
+
};
|
|
13
|
+
var __copyProps = (to, from, except, desc) => {
|
|
14
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
15
|
+
for (let key of __getOwnPropNames(from))
|
|
16
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
17
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
18
|
+
}
|
|
19
|
+
return to;
|
|
20
|
+
};
|
|
21
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
22
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
23
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
24
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
25
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
26
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
27
|
+
mod
|
|
28
|
+
));
|
|
29
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
30
|
+
var acp_runtime_exports = {};
|
|
31
|
+
__export(acp_runtime_exports, {
|
|
32
|
+
ADAPTER_VERSIONS: () => ADAPTER_VERSIONS,
|
|
33
|
+
AcpRuntime: () => AcpRuntime,
|
|
34
|
+
CODEX_STEER_METHOD: () => CODEX_STEER_METHOD,
|
|
35
|
+
acpErrorKind: () => acpErrorKind,
|
|
36
|
+
acpSessionRequestOptions: () => acpSessionRequestOptions,
|
|
37
|
+
autoPermissionResponse: () => autoPermissionResponse,
|
|
38
|
+
codexAcpEnvironment: () => codexAcpEnvironment,
|
|
39
|
+
deleteProviderSessionIdentity: () => deleteProviderSessionIdentity,
|
|
40
|
+
describeAcpProcessGroup: () => describeAcpProcessGroup,
|
|
41
|
+
isCodexSteerUnavailableError: () => isCodexSteerUnavailableError,
|
|
42
|
+
promptContentForCapabilities: () => promptContentForCapabilities,
|
|
43
|
+
resolveAcpLaunch: () => resolveAcpLaunch,
|
|
44
|
+
stopPersistedAcpProcessGroup: () => stopPersistedAcpProcessGroup,
|
|
45
|
+
supportsCodexSteer: () => supportsCodexSteer
|
|
46
|
+
});
|
|
47
|
+
module.exports = __toCommonJS(acp_runtime_exports);
|
|
48
|
+
var import_events = require("events");
|
|
49
|
+
var import_acp_checkpoint_store = require("./acp-checkpoint-store.cjs");
|
|
50
|
+
var import_acp_session_state = require("./acp-session-state.cjs");
|
|
51
|
+
var import_client_services = require("./acp/client-services.cjs");
|
|
52
|
+
var import_packaged_codex_acp = require("./acp/packaged-codex-acp.cjs");
|
|
53
|
+
var import_packaged_claude_acp = require("./acp/packaged-claude-acp.cjs");
|
|
54
|
+
var import_permission_security = require("./acp/permission-security.cjs");
|
|
55
|
+
var import_patch_decisions = require("./acp/patch-decisions.cjs");
|
|
56
|
+
var import_provider_adapters = require("./provider-adapters.cjs");
|
|
57
|
+
var import_provider_session_id = require("./provider-session-id.cjs");
|
|
58
|
+
const crypto = require("crypto");
|
|
59
|
+
const fs = require("fs");
|
|
60
|
+
const path = require("path");
|
|
61
|
+
const { execFile, spawn } = require("child_process");
|
|
62
|
+
const { Readable, Writable } = require("stream");
|
|
63
|
+
const { createRequire } = require("module");
|
|
64
|
+
const { promisify } = require("util");
|
|
65
|
+
const packageJson = require("../package.json");
|
|
66
|
+
function asErrorLike(error) {
|
|
67
|
+
return error && typeof error === "object" ? error : {};
|
|
68
|
+
}
|
|
20
69
|
const ADAPTER_VERSIONS = Object.freeze(Object.fromEntries(
|
|
21
|
-
listProviderAdapters().filter(adapter => adapter.acp).map(adapter => [adapter.id, adapter.acp.version])
|
|
70
|
+
(0, import_provider_adapters.listProviderAdapters)().filter((adapter) => adapter.acp).map((adapter) => [adapter.id, adapter.acp.version])
|
|
22
71
|
));
|
|
23
|
-
|
|
24
|
-
const
|
|
25
|
-
const
|
|
26
|
-
const
|
|
27
|
-
const DEFAULT_CANCEL_TIMEOUT_MS = 15_000;
|
|
72
|
+
const DEFAULT_INITIALIZE_TIMEOUT_MS = 15e3;
|
|
73
|
+
const DEFAULT_SESSION_SETUP_TIMEOUT_MS = 12e4;
|
|
74
|
+
const DEFAULT_REQUEST_TIMEOUT_MS = 3e4;
|
|
75
|
+
const DEFAULT_CANCEL_TIMEOUT_MS = 15e3;
|
|
28
76
|
const DEFAULT_HISTORY_REPLAY_MIN_WAIT_MS = 350;
|
|
29
77
|
const DEFAULT_HISTORY_REPLAY_QUIET_MS = 150;
|
|
30
|
-
const DEFAULT_HISTORY_REPLAY_MAX_WAIT_MS =
|
|
31
|
-
const IDENTITY_ADAPTER_GRACEFUL_EXIT_MS =
|
|
32
|
-
const IDENTITY_ADAPTER_TERMINATE_MS =
|
|
33
|
-
const IDENTITY_ADAPTER_KILL_MS =
|
|
34
|
-
const CODEX_SET_SESSION_MODEL_METHOD =
|
|
35
|
-
const CODEX_STEER_METHOD =
|
|
36
|
-
const CODEX_ACP_PACKAGE =
|
|
37
|
-
const CODEX_ACP_VERSION =
|
|
38
|
-
const CODEX_ACP_SHA256 =
|
|
39
|
-
const CLAUDE_ACP_PACKAGE =
|
|
40
|
-
const CLAUDE_ACP_VERSION =
|
|
41
|
-
const CLAUDE_ACP_SHA256 =
|
|
78
|
+
const DEFAULT_HISTORY_REPLAY_MAX_WAIT_MS = 5e3;
|
|
79
|
+
const IDENTITY_ADAPTER_GRACEFUL_EXIT_MS = 3e3;
|
|
80
|
+
const IDENTITY_ADAPTER_TERMINATE_MS = 1e3;
|
|
81
|
+
const IDENTITY_ADAPTER_KILL_MS = 1e3;
|
|
82
|
+
const CODEX_SET_SESSION_MODEL_METHOD = "session/set_model";
|
|
83
|
+
const CODEX_STEER_METHOD = "_codex/session/steer";
|
|
84
|
+
const CODEX_ACP_PACKAGE = "@agentclientprotocol/codex-acp";
|
|
85
|
+
const CODEX_ACP_VERSION = "1.1.4";
|
|
86
|
+
const CODEX_ACP_SHA256 = "7d9647ad2af49d47311a785bf5abd2d317d7c7438ae9d4eacfe785ba37191718";
|
|
87
|
+
const CLAUDE_ACP_PACKAGE = "@agentclientprotocol/claude-agent-acp";
|
|
88
|
+
const CLAUDE_ACP_VERSION = "0.59.0";
|
|
89
|
+
const CLAUDE_ACP_SHA256 = "a6aa515dd02382617bf46d9eac47b8a1022c6835bcf7a8d61e2c63939be2e49c";
|
|
42
90
|
const CODEX_ACP_VENDOR_ENTRY = path.join(
|
|
43
91
|
__dirname,
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
`codex-acp-${CODEX_ACP_VERSION}.mjs
|
|
92
|
+
"..",
|
|
93
|
+
"dist",
|
|
94
|
+
"acp",
|
|
95
|
+
`codex-acp-${CODEX_ACP_VERSION}.mjs`
|
|
48
96
|
);
|
|
49
97
|
const CLAUDE_ACP_VENDOR_ENTRY = path.join(
|
|
50
98
|
__dirname,
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
`claude-agent-acp-${CLAUDE_ACP_VERSION}.mjs
|
|
99
|
+
"..",
|
|
100
|
+
"dist",
|
|
101
|
+
"acp",
|
|
102
|
+
`claude-agent-acp-${CLAUDE_ACP_VERSION}.mjs`
|
|
55
103
|
);
|
|
56
104
|
const execFileAsync = promisify(execFile);
|
|
57
|
-
|
|
58
105
|
let sdkPromise;
|
|
59
106
|
const runtimeRequire = createRequire(__filename);
|
|
60
107
|
function loadAcpSdk() {
|
|
61
|
-
if (!sdkPromise) sdkPromise = import(
|
|
108
|
+
if (!sdkPromise) sdkPromise = import("@agentclientprotocol/sdk");
|
|
62
109
|
return sdkPromise;
|
|
63
110
|
}
|
|
64
|
-
|
|
65
111
|
function fileSha256(filePath) {
|
|
66
|
-
return crypto.createHash(
|
|
112
|
+
return crypto.createHash("sha256").update(fs.readFileSync(filePath)).digest("hex");
|
|
67
113
|
}
|
|
68
|
-
|
|
69
114
|
function verifiedCodexAcpEntry(entry) {
|
|
70
115
|
const actualSha256 = fileSha256(entry);
|
|
71
116
|
if (actualSha256 !== CODEX_ACP_SHA256) {
|
|
72
117
|
throw new Error(
|
|
73
|
-
`Codex ACP runtime failed integrity verification: expected ${CODEX_ACP_SHA256}, found ${actualSha256}
|
|
118
|
+
`Codex ACP runtime failed integrity verification: expected ${CODEX_ACP_SHA256}, found ${actualSha256}`
|
|
74
119
|
);
|
|
75
120
|
}
|
|
76
121
|
return entry;
|
|
77
122
|
}
|
|
78
|
-
|
|
79
123
|
function adapterEntry(packageName) {
|
|
80
124
|
if (packageName === CODEX_ACP_PACKAGE && fs.existsSync(CODEX_ACP_VENDOR_ENTRY)) {
|
|
81
125
|
return verifiedCodexAcpEntry(CODEX_ACP_VENDOR_ENTRY);
|
|
@@ -84,42 +128,40 @@ function adapterEntry(packageName) {
|
|
|
84
128
|
const actualSha256 = fileSha256(CLAUDE_ACP_VENDOR_ENTRY);
|
|
85
129
|
if (actualSha256 !== CLAUDE_ACP_SHA256) {
|
|
86
130
|
throw new Error(
|
|
87
|
-
`Claude ACP runtime failed integrity verification: expected ${CLAUDE_ACP_SHA256}, found ${actualSha256}
|
|
131
|
+
`Claude ACP runtime failed integrity verification: expected ${CLAUDE_ACP_SHA256}, found ${actualSha256}`
|
|
88
132
|
);
|
|
89
133
|
}
|
|
90
134
|
return CLAUDE_ACP_VENDOR_ENTRY;
|
|
91
135
|
}
|
|
92
136
|
let sdkDirectory;
|
|
93
137
|
try {
|
|
94
|
-
sdkDirectory = path.dirname(runtimeRequire.resolve(
|
|
138
|
+
sdkDirectory = path.dirname(runtimeRequire.resolve("@agentclientprotocol/sdk"));
|
|
95
139
|
} catch {
|
|
96
|
-
throw new Error(
|
|
140
|
+
throw new Error("ACP runtime packages are unavailable in this installation. Use the npm or app-bundle distribution.");
|
|
97
141
|
}
|
|
98
|
-
const entry = path.resolve(sdkDirectory,
|
|
142
|
+
const entry = path.resolve(sdkDirectory, "..", "..", packageName.split("/").pop(), "dist", "index.js");
|
|
99
143
|
if (!fs.existsSync(entry)) throw new Error(`ACP adapter is not installed: ${packageName}`);
|
|
100
144
|
if (packageName === CODEX_ACP_PACKAGE) return verifiedCodexAcpEntry(entry);
|
|
101
145
|
return entry;
|
|
102
146
|
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
const runtimeEnv = env && typeof env === 'object' ? env : process.env;
|
|
147
|
+
function nodeAdapterLaunch(entry, env = process.env, packagedArg = "") {
|
|
148
|
+
const runtimeEnv = env && typeof env === "object" ? env : process.env;
|
|
106
149
|
if (process.pkg && packagedArg) {
|
|
107
150
|
return { command: process.execPath, args: [packagedArg] };
|
|
108
151
|
}
|
|
109
152
|
const nodeBin = runtimeEnv.FARMING_NODE_BIN || process.execPath;
|
|
110
|
-
const ldPath = runtimeEnv.FARMING_NODE_LD ||
|
|
111
|
-
const libraryPath = runtimeEnv.FARMING_NODE_LIBRARY_PATH ||
|
|
153
|
+
const ldPath = runtimeEnv.FARMING_NODE_LD || "";
|
|
154
|
+
const libraryPath = runtimeEnv.FARMING_NODE_LIBRARY_PATH || "";
|
|
112
155
|
if (ldPath && libraryPath) {
|
|
113
156
|
return {
|
|
114
157
|
command: ldPath,
|
|
115
|
-
args: [
|
|
158
|
+
args: ["--library-path", libraryPath, nodeBin, entry]
|
|
116
159
|
};
|
|
117
160
|
}
|
|
118
161
|
return { command: nodeBin, args: [entry] };
|
|
119
162
|
}
|
|
120
|
-
|
|
121
163
|
function resolveAcpLaunch(provider, options = {}) {
|
|
122
|
-
const adapter = getProviderAdapter(provider);
|
|
164
|
+
const adapter = (0, import_provider_adapters.getProviderAdapter)(provider);
|
|
123
165
|
if (!adapter?.acp) throw new Error(`Unsupported ACP provider: ${provider}`);
|
|
124
166
|
if (adapter.acp.launch) {
|
|
125
167
|
return { ...adapter.acp.launch(options), version: adapter.acp.version };
|
|
@@ -128,25 +170,22 @@ function resolveAcpLaunch(provider, options = {}) {
|
|
|
128
170
|
const launch = nodeAdapterLaunch(
|
|
129
171
|
adapterEntry(adapter.acp.packageName),
|
|
130
172
|
options.runtimeEnv || process.env,
|
|
131
|
-
adapter.acp.packageName === CODEX_ACP_PACKAGE
|
|
132
|
-
? PACKAGED_CODEX_ACP_ARG
|
|
133
|
-
: adapter.acp.packageName === CLAUDE_ACP_PACKAGE
|
|
134
|
-
? PACKAGED_CLAUDE_ACP_ARG
|
|
135
|
-
: '',
|
|
173
|
+
adapter.acp.packageName === CODEX_ACP_PACKAGE ? import_packaged_codex_acp.PACKAGED_CODEX_ACP_ARG : adapter.acp.packageName === CLAUDE_ACP_PACKAGE ? import_packaged_claude_acp.PACKAGED_CLAUDE_ACP_ARG : ""
|
|
136
174
|
);
|
|
137
175
|
return { ...launch, version: adapter.acp.version };
|
|
138
176
|
}
|
|
139
177
|
throw new Error(`Unsupported ACP provider: ${provider}`);
|
|
140
178
|
}
|
|
141
|
-
|
|
142
179
|
function codexAcpEnvironment(options = {}) {
|
|
143
|
-
|
|
180
|
+
const adapter = (0, import_provider_adapters.getProviderAdapter)("codex");
|
|
181
|
+
if (!adapter?.prepareAcpEnvironment) {
|
|
182
|
+
throw new Error("Codex ACP environment adapter is unavailable");
|
|
183
|
+
}
|
|
184
|
+
return adapter.prepareAcpEnvironment(options);
|
|
144
185
|
}
|
|
145
|
-
|
|
146
186
|
function selectedPermission(option) {
|
|
147
|
-
return { outcome: { outcome:
|
|
187
|
+
return { outcome: { outcome: "selected", optionId: option.optionId } };
|
|
148
188
|
}
|
|
149
|
-
|
|
150
189
|
function withTimeout(promise, timeoutMs, label) {
|
|
151
190
|
let timer;
|
|
152
191
|
const timeout = new Promise((_, reject) => {
|
|
@@ -155,70 +194,144 @@ function withTimeout(promise, timeoutMs, label) {
|
|
|
155
194
|
});
|
|
156
195
|
return Promise.race([promise, timeout]).finally(() => clearTimeout(timer));
|
|
157
196
|
}
|
|
158
|
-
|
|
159
197
|
function acpErrorMessage(error) {
|
|
160
|
-
const
|
|
161
|
-
const
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
: message;
|
|
198
|
+
const failure = asErrorLike(error);
|
|
199
|
+
const message = failure.message || String(error || "ACP request failed");
|
|
200
|
+
const details = failure.data?.details;
|
|
201
|
+
return typeof details === "string" && details && details !== message ? `${message}: ${details}` : message;
|
|
165
202
|
}
|
|
166
|
-
|
|
167
203
|
function acpErrorKind(error) {
|
|
168
|
-
const
|
|
169
|
-
const
|
|
204
|
+
const failure = asErrorLike(error);
|
|
205
|
+
const message = (failure.message || String(error || "")).toLowerCase();
|
|
206
|
+
const code = String(failure.code || failure.data?.code || "").toLowerCase();
|
|
170
207
|
const text = `${code} ${message}`;
|
|
171
|
-
if (/\b(401|unauthorized|authentication|authenticate|login|sign[ -]?in|invalid api key)\b/.test(text)) return
|
|
172
|
-
if (/\b(402|payment|billing|credit|insufficient quota)\b/.test(text)) return
|
|
173
|
-
if (/context (?:window|length|limit)|too many tokens|max(?:imum)? tokens/.test(text)) return
|
|
174
|
-
if (/\b(model_not_found|unknown model|model unavailable|unsupported model)\b/.test(text)) return
|
|
175
|
-
if (/\b(429|rate[ -]?limit|too many requests)\b/.test(text)) return
|
|
176
|
-
if (/\b(econn|enet|ehost|socket|network|connection|timed? out|timeout|dns)\b/.test(text)) return
|
|
177
|
-
if (/\b(protocol|json-rpc|parse error|invalid request|method not found)\b/.test(text)) return
|
|
178
|
-
return
|
|
208
|
+
if (/\b(401|unauthorized|authentication|authenticate|login|sign[ -]?in|invalid api key)\b/.test(text)) return "authentication";
|
|
209
|
+
if (/\b(402|payment|billing|credit|insufficient quota)\b/.test(text)) return "payment";
|
|
210
|
+
if (/context (?:window|length|limit)|too many tokens|max(?:imum)? tokens/.test(text)) return "context";
|
|
211
|
+
if (/\b(model_not_found|unknown model|model unavailable|unsupported model)\b/.test(text)) return "model";
|
|
212
|
+
if (/\b(429|rate[ -]?limit|too many requests)\b/.test(text)) return "rate-limit";
|
|
213
|
+
if (/\b(econn|enet|ehost|socket|network|connection|timed? out|timeout|dns)\b/.test(text)) return "network";
|
|
214
|
+
if (/\b(protocol|json-rpc|parse error|invalid request|method not found)\b/.test(text)) return "protocol";
|
|
215
|
+
return "unknown";
|
|
179
216
|
}
|
|
180
|
-
|
|
181
217
|
function autoPermissionResponse(request, approvalMode) {
|
|
182
218
|
const options = Array.isArray(request?.options) ? request.options : [];
|
|
183
|
-
if (approvalMode ===
|
|
184
|
-
const option = options.find(item => item.kind ===
|
|
185
|
-
|
|
186
|
-
return option ? selectedPermission(option) : { outcome: { outcome: 'cancelled' } };
|
|
219
|
+
if (approvalMode === "full") {
|
|
220
|
+
const option = options.find((item) => item.kind === "allow_always") || options.find((item) => item.kind === "allow_once");
|
|
221
|
+
return option ? selectedPermission(option) : { outcome: { outcome: "cancelled" } };
|
|
187
222
|
}
|
|
188
|
-
if (approvalMode ===
|
|
189
|
-
const option = options.find(item => item.kind ===
|
|
190
|
-
|
|
191
|
-
return option ? selectedPermission(option) : { outcome: { outcome: 'cancelled' } };
|
|
223
|
+
if (approvalMode === "ask") {
|
|
224
|
+
const option = options.find((item) => item.kind === "reject_once") || options.find((item) => item.kind === "reject_always");
|
|
225
|
+
return option ? selectedPermission(option) : { outcome: { outcome: "cancelled" } };
|
|
192
226
|
}
|
|
193
227
|
return null;
|
|
194
228
|
}
|
|
195
|
-
|
|
229
|
+
const FARMING_BROWSER_SERVER_NAME = "farming-browser";
|
|
230
|
+
const FARMING_BROWSER_RESOURCE_TOOLS = /* @__PURE__ */ new Set([
|
|
231
|
+
"browser_list",
|
|
232
|
+
"browser_stop"
|
|
233
|
+
]);
|
|
234
|
+
function recordValue(value) {
|
|
235
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
236
|
+
}
|
|
237
|
+
function browserApprovalSessionState(binding, request) {
|
|
238
|
+
const requestSessionId = String(request.sessionId || "");
|
|
239
|
+
if (!requestSessionId || requestSessionId === binding.sessionId) return binding.sessionState || null;
|
|
240
|
+
return binding.subagentStates.get(requestSessionId) || null;
|
|
241
|
+
}
|
|
242
|
+
function farmingBrowserApprovalTarget(binding, request) {
|
|
243
|
+
const toolCall = recordValue(request.toolCall);
|
|
244
|
+
const toolCallId = String(request.toolCallId || toolCall.toolCallId || "");
|
|
245
|
+
const sessionState = browserApprovalSessionState(binding, request);
|
|
246
|
+
const entry = toolCallId ? sessionState?.toolEntries?.get(toolCallId) || sessionState?.entries?.find((candidate) => candidate.id === toolCallId) : null;
|
|
247
|
+
const rawInput = recordValue(entry?.rawInput || toolCall.rawInput);
|
|
248
|
+
if (rawInput.server !== FARMING_BROWSER_SERVER_NAME) return null;
|
|
249
|
+
const tool = String(rawInput.tool || "");
|
|
250
|
+
if (!tool.startsWith("browser_")) return null;
|
|
251
|
+
return {
|
|
252
|
+
tool,
|
|
253
|
+
arguments: recordValue(rawInput.arguments)
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
function browserSiteScope(value) {
|
|
257
|
+
const text = String(value || "").trim();
|
|
258
|
+
if (!text || text === "about:blank") {
|
|
259
|
+
return { requiresApproval: false, scopeKey: "", site: "" };
|
|
260
|
+
}
|
|
261
|
+
try {
|
|
262
|
+
const url = new URL(/^[A-Za-z][A-Za-z0-9+.-]*:\/\//.test(text) ? text : `https://${text}`);
|
|
263
|
+
if (!["http:", "https:"].includes(url.protocol) || !url.host) {
|
|
264
|
+
return { requiresApproval: true, scopeKey: "", site: "" };
|
|
265
|
+
}
|
|
266
|
+
return {
|
|
267
|
+
requiresApproval: true,
|
|
268
|
+
scopeKey: `site:${url.origin.toLowerCase()}`,
|
|
269
|
+
site: url.host.toLowerCase()
|
|
270
|
+
};
|
|
271
|
+
} catch {
|
|
272
|
+
return { requiresApproval: true, scopeKey: "", site: "" };
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
function defaultBrowserPermissionDecision(target) {
|
|
276
|
+
if (FARMING_BROWSER_RESOURCE_TOOLS.has(target.tool)) {
|
|
277
|
+
return { requiresApproval: false, scopeKey: "", site: "" };
|
|
278
|
+
}
|
|
279
|
+
if (target.tool === "browser_open" || target.tool === "browser_navigate") {
|
|
280
|
+
return browserSiteScope(target.arguments.url);
|
|
281
|
+
}
|
|
282
|
+
const browserId = String(target.arguments.browserId || "");
|
|
283
|
+
return {
|
|
284
|
+
requiresApproval: true,
|
|
285
|
+
scopeKey: browserId ? `browser:${browserId}` : "",
|
|
286
|
+
site: ""
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
function shouldAutoApproveFarmingBrowser(binding, decision) {
|
|
290
|
+
const scopeKey = String(decision.scopeKey || "");
|
|
291
|
+
if (decision.requiresApproval !== true) {
|
|
292
|
+
return { approve: true, scopeKey };
|
|
293
|
+
}
|
|
294
|
+
return {
|
|
295
|
+
approve: Boolean(scopeKey && binding.farmingBrowserApprovedSites.has(scopeKey)),
|
|
296
|
+
scopeKey
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
function automaticBrowserPermissionResponse(request) {
|
|
300
|
+
const options = Array.isArray(request.options) ? request.options : [];
|
|
301
|
+
const option = options.find((item) => item.kind === "allow_once") || options.find((item) => item.kind === "allow_always");
|
|
302
|
+
return option ? selectedPermission(option) : { outcome: { outcome: "cancelled" } };
|
|
303
|
+
}
|
|
304
|
+
function automaticBrowserElicitationResponse(request) {
|
|
305
|
+
const properties = recordValue(recordValue(request.requestedSchema).properties);
|
|
306
|
+
return {
|
|
307
|
+
action: "accept",
|
|
308
|
+
content: Object.prototype.hasOwnProperty.call(properties, "persist") ? { persist: "once" } : {}
|
|
309
|
+
};
|
|
310
|
+
}
|
|
196
311
|
function validateElicitationContent(request, content) {
|
|
197
|
-
if (request?.mode !==
|
|
198
|
-
const value = content && typeof content ===
|
|
199
|
-
const schema = request.requestedSchema && typeof request.requestedSchema ===
|
|
200
|
-
|
|
201
|
-
: {};
|
|
202
|
-
const properties = schema.properties && typeof schema.properties === 'object' ? schema.properties : {};
|
|
312
|
+
if (request?.mode !== "form") return void 0;
|
|
313
|
+
const value = content && typeof content === "object" && !Array.isArray(content) ? content : {};
|
|
314
|
+
const schema = request.requestedSchema && typeof request.requestedSchema === "object" ? request.requestedSchema : {};
|
|
315
|
+
const properties = schema.properties && typeof schema.properties === "object" ? schema.properties : {};
|
|
203
316
|
for (const name of Array.isArray(schema.required) ? schema.required : []) {
|
|
204
317
|
if (!Object.prototype.hasOwnProperty.call(value, name)) throw new Error(`ACP input is required: ${name}`);
|
|
205
318
|
}
|
|
206
319
|
for (const [name, fieldValue] of Object.entries(value)) {
|
|
207
320
|
const property = properties[name];
|
|
208
|
-
if (!property || typeof property !==
|
|
209
|
-
if (property.type ===
|
|
210
|
-
if (property.type ===
|
|
321
|
+
if (!property || typeof property !== "object") throw new Error(`Unknown ACP input field: ${name}`);
|
|
322
|
+
if (property.type === "string" && typeof fieldValue !== "string") throw new Error(`ACP input must be text: ${name}`);
|
|
323
|
+
if (property.type === "number" && (typeof fieldValue !== "number" || !Number.isFinite(fieldValue))) {
|
|
211
324
|
throw new Error(`ACP input must be a number: ${name}`);
|
|
212
325
|
}
|
|
213
|
-
if (property.type ===
|
|
214
|
-
if (property.type ===
|
|
215
|
-
if (property.type ===
|
|
326
|
+
if (property.type === "integer" && !Number.isInteger(fieldValue)) throw new Error(`ACP input must be an integer: ${name}`);
|
|
327
|
+
if (property.type === "boolean" && typeof fieldValue !== "boolean") throw new Error(`ACP input must be true or false: ${name}`);
|
|
328
|
+
if (property.type === "array" && (!Array.isArray(fieldValue) || fieldValue.some((item) => typeof item !== "string"))) {
|
|
216
329
|
throw new Error(`ACP input must be a text selection: ${name}`);
|
|
217
330
|
}
|
|
218
|
-
if (typeof fieldValue ===
|
|
219
|
-
if (Number.isFinite(property.minLength) && fieldValue.length < property.minLength) throw new Error(`ACP input is too short: ${name}`);
|
|
220
|
-
if (Number.isFinite(property.maxLength) && fieldValue.length > property.maxLength) throw new Error(`ACP input is too long: ${name}`);
|
|
221
|
-
if (typeof property.pattern ===
|
|
331
|
+
if (typeof fieldValue === "string") {
|
|
332
|
+
if (typeof property.minLength === "number" && Number.isFinite(property.minLength) && fieldValue.length < property.minLength) throw new Error(`ACP input is too short: ${name}`);
|
|
333
|
+
if (typeof property.maxLength === "number" && Number.isFinite(property.maxLength) && fieldValue.length > property.maxLength) throw new Error(`ACP input is too long: ${name}`);
|
|
334
|
+
if (typeof property.pattern === "string") {
|
|
222
335
|
let pattern;
|
|
223
336
|
try {
|
|
224
337
|
pattern = new RegExp(property.pattern);
|
|
@@ -227,179 +340,161 @@ function validateElicitationContent(request, content) {
|
|
|
227
340
|
}
|
|
228
341
|
if (!pattern.test(fieldValue)) throw new Error(`ACP input has an invalid format: ${name}`);
|
|
229
342
|
}
|
|
230
|
-
const allowed = Array.isArray(property.enum)
|
|
231
|
-
? property.enum
|
|
232
|
-
: Array.isArray(property.oneOf)
|
|
233
|
-
? property.oneOf.map(option => option?.const)
|
|
234
|
-
: null;
|
|
343
|
+
const allowed = Array.isArray(property.enum) ? property.enum : Array.isArray(property.oneOf) ? property.oneOf.map((option) => option?.const) : null;
|
|
235
344
|
if (allowed && !allowed.includes(fieldValue)) throw new Error(`ACP input is not an allowed choice: ${name}`);
|
|
236
345
|
}
|
|
237
|
-
if (typeof fieldValue ===
|
|
238
|
-
if (Number.isFinite(property.minimum) && fieldValue < property.minimum) throw new Error(`ACP input is below the minimum: ${name}`);
|
|
239
|
-
if (Number.isFinite(property.maximum) && fieldValue > property.maximum) throw new Error(`ACP input is above the maximum: ${name}`);
|
|
346
|
+
if (typeof fieldValue === "number") {
|
|
347
|
+
if (typeof property.minimum === "number" && Number.isFinite(property.minimum) && fieldValue < property.minimum) throw new Error(`ACP input is below the minimum: ${name}`);
|
|
348
|
+
if (typeof property.maximum === "number" && Number.isFinite(property.maximum) && fieldValue > property.maximum) throw new Error(`ACP input is above the maximum: ${name}`);
|
|
240
349
|
}
|
|
241
350
|
if (Array.isArray(fieldValue)) {
|
|
242
|
-
if (Number.isFinite(property.minItems) && fieldValue.length < property.minItems) throw new Error(`ACP input needs more selections: ${name}`);
|
|
243
|
-
if (Number.isFinite(property.maxItems) && fieldValue.length > property.maxItems) throw new Error(`ACP input has too many selections: ${name}`);
|
|
244
|
-
const allowedItems = Array.isArray(property.items?.enum)
|
|
245
|
-
|
|
246
|
-
: Array.isArray(property.items?.anyOf)
|
|
247
|
-
? property.items.anyOf.map(option => option?.const)
|
|
248
|
-
: null;
|
|
249
|
-
if (allowedItems && fieldValue.some(item => !allowedItems.includes(item))) {
|
|
351
|
+
if (typeof property.minItems === "number" && Number.isFinite(property.minItems) && fieldValue.length < property.minItems) throw new Error(`ACP input needs more selections: ${name}`);
|
|
352
|
+
if (typeof property.maxItems === "number" && Number.isFinite(property.maxItems) && fieldValue.length > property.maxItems) throw new Error(`ACP input has too many selections: ${name}`);
|
|
353
|
+
const allowedItems = Array.isArray(property.items?.enum) ? property.items.enum : Array.isArray(property.items?.anyOf) ? property.items.anyOf.map((option) => option?.const) : null;
|
|
354
|
+
if (allowedItems && fieldValue.some((item) => !allowedItems.includes(item))) {
|
|
250
355
|
throw new Error(`ACP input contains an unknown selection: ${name}`);
|
|
251
356
|
}
|
|
252
357
|
}
|
|
253
358
|
}
|
|
254
359
|
return JSON.parse(JSON.stringify(value));
|
|
255
360
|
}
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
if (binding.
|
|
259
|
-
if (binding.
|
|
260
|
-
if (binding.activeTurn?.phase ===
|
|
261
|
-
if (
|
|
262
|
-
|
|
263
|
-
return binding.sessionId ? 'idle' : 'connecting';
|
|
361
|
+
function interactiveRuntimeState(binding, fallback = "") {
|
|
362
|
+
if (binding.pendingPermissions.size > 0) return "waiting-for-permission";
|
|
363
|
+
if (binding.pendingElicitations.size > 0) return "waiting-for-input";
|
|
364
|
+
if (binding.activeTurn?.phase === "running") return "working";
|
|
365
|
+
if (binding.activeTurn?.phase === "cancelling") return "interrupting";
|
|
366
|
+
if (["connecting", "idle", "error"].includes(String(fallback || ""))) return fallback;
|
|
367
|
+
return binding.sessionId ? "idle" : "connecting";
|
|
264
368
|
}
|
|
265
|
-
|
|
266
369
|
function clone(value) {
|
|
267
370
|
return value == null ? value : JSON.parse(JSON.stringify(value));
|
|
268
371
|
}
|
|
269
|
-
|
|
270
372
|
function acpSessionRequestOptions(options = {}, cwd = process.cwd()) {
|
|
271
373
|
const root = path.resolve(cwd || process.cwd());
|
|
272
|
-
const additionalDirectories = Array.isArray(options.additionalDirectories)
|
|
273
|
-
|
|
274
|
-
.filter(directory => typeof directory === 'string' && directory.trim())
|
|
275
|
-
.map(directory => path.resolve(root, directory)))]
|
|
276
|
-
: [];
|
|
277
|
-
const mcpServers = Array.isArray(options.mcpServers)
|
|
278
|
-
? clone(options.mcpServers.filter(server => server && typeof server === 'object' && !Array.isArray(server)))
|
|
279
|
-
: [];
|
|
374
|
+
const additionalDirectories = Array.isArray(options.additionalDirectories) ? [...new Set(options.additionalDirectories.filter((directory) => typeof directory === "string" && directory.trim()).map((directory) => path.resolve(root, directory)))] : [];
|
|
375
|
+
const mcpServers = Array.isArray(options.mcpServers) ? clone(options.mcpServers.filter((server) => server && typeof server === "object" && !Array.isArray(server))) : [];
|
|
280
376
|
const result = { cwd: root, additionalDirectories, mcpServers };
|
|
281
|
-
if (
|
|
282
|
-
options.provider === 'claude'
|
|
283
|
-
&& typeof options.farmingSystemPrompt === 'string'
|
|
284
|
-
&& options.farmingSystemPrompt.trim()
|
|
285
|
-
) {
|
|
377
|
+
if (options.provider === "claude" && typeof options.farmingSystemPrompt === "string" && options.farmingSystemPrompt.trim()) {
|
|
286
378
|
result._meta = {
|
|
287
379
|
systemPrompt: {
|
|
288
|
-
type:
|
|
289
|
-
preset:
|
|
290
|
-
append: options.farmingSystemPrompt.trim()
|
|
291
|
-
}
|
|
380
|
+
type: "preset",
|
|
381
|
+
preset: "claude_code",
|
|
382
|
+
append: options.farmingSystemPrompt.trim()
|
|
383
|
+
}
|
|
292
384
|
};
|
|
293
385
|
}
|
|
294
386
|
return result;
|
|
295
387
|
}
|
|
296
|
-
|
|
297
388
|
function promptContentForCapabilities(content, capabilities = {}) {
|
|
298
389
|
const promptCapabilities = capabilities?.promptCapabilities || {};
|
|
299
|
-
return (Array.isArray(content) ? content : []).flatMap(block => {
|
|
300
|
-
if (!block || typeof block !==
|
|
301
|
-
if (![
|
|
390
|
+
return (Array.isArray(content) ? content : []).flatMap((block) => {
|
|
391
|
+
if (!block || typeof block !== "object") return [];
|
|
392
|
+
if (!block.type || !["image", "audio"].includes(block.type)) return [clone(block)];
|
|
302
393
|
if (promptCapabilities[block.type] === true) return [clone(block)];
|
|
303
|
-
|
|
304
|
-
const
|
|
305
|
-
const attachmentPath = typeof block.path === 'string' && block.path.trim()
|
|
306
|
-
? path.resolve(block.path)
|
|
307
|
-
: '';
|
|
394
|
+
const label = block.type === "audio" ? "audio" : "image";
|
|
395
|
+
const attachmentPath = typeof block.path === "string" && block.path.trim() ? path.resolve(block.path) : "";
|
|
308
396
|
const name = attachmentPath ? path.basename(attachmentPath) : `attached ${label}`;
|
|
309
|
-
const detail = attachmentPath
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
397
|
+
const detail = attachmentPath ? `${label.charAt(0).toUpperCase()}${label.slice(1)} path: ${attachmentPath}` : `[The ACP Agent does not accept native ${label} content]`;
|
|
398
|
+
return [{ type: "text", text: `Attached ${label}: ${name}
|
|
399
|
+
|
|
400
|
+
${detail}` }];
|
|
313
401
|
});
|
|
314
402
|
}
|
|
315
|
-
|
|
316
|
-
|
|
403
|
+
function restoreMissingHistoryMedia(replayedState, checkpointState) {
|
|
404
|
+
const userEntries = (state) => state.entries.filter((entry) => entry?.type === "message" && entry.role === "user");
|
|
405
|
+
const entryText = (entry) => (Array.isArray(entry.content) ? entry.content : []).filter((block) => block?.type === "text").map((block) => String(block.text || "")).join("");
|
|
406
|
+
const replayedUsers = userEntries(replayedState);
|
|
407
|
+
const checkpointUsers = userEntries(checkpointState);
|
|
408
|
+
let restored = 0;
|
|
409
|
+
for (let index = 0; index < Math.min(replayedUsers.length, checkpointUsers.length); index += 1) {
|
|
410
|
+
const target = replayedUsers[index];
|
|
411
|
+
const source = checkpointUsers[index];
|
|
412
|
+
if (entryText(target) !== entryText(source)) continue;
|
|
413
|
+
const targetContent = Array.isArray(target.content) ? target.content : [];
|
|
414
|
+
const sourceMedia = (Array.isArray(source.content) ? source.content : []).filter((block) => block?.type === "image" || block?.type === "audio");
|
|
415
|
+
for (const block of sourceMedia) {
|
|
416
|
+
const duplicate = targetContent.some((candidate) => candidate?.type === block.type && candidate?.mimeType === block.mimeType && candidate?.data === block.data && candidate?.path === block.path);
|
|
417
|
+
if (duplicate) continue;
|
|
418
|
+
targetContent.push(clone(block));
|
|
419
|
+
restored += 1;
|
|
420
|
+
}
|
|
421
|
+
target.content = targetContent;
|
|
422
|
+
}
|
|
423
|
+
return restored;
|
|
424
|
+
}
|
|
425
|
+
function supportsCodexSteer(capabilities = {} = {}) {
|
|
317
426
|
const capability = capabilities?._meta?.codex?.steer;
|
|
318
|
-
return capability?.method === CODEX_STEER_METHOD
|
|
319
|
-
&& Number.isFinite(Number(capability.version))
|
|
320
|
-
&& Number(capability.version) >= 1;
|
|
427
|
+
return capability?.method === CODEX_STEER_METHOD && Number.isFinite(Number(capability.version)) && Number(capability.version) >= 1;
|
|
321
428
|
}
|
|
322
|
-
|
|
323
429
|
function isCodexSteerUnavailableError(error) {
|
|
430
|
+
const failure = asErrorLike(error);
|
|
324
431
|
const text = [
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
].filter(Boolean).join(
|
|
330
|
-
return text.includes(
|
|
331
|
-
|| text.includes('no active turn to steer')
|
|
332
|
-
|| /expected active turn id .* but found/.test(text)
|
|
333
|
-
|| text.includes('cannot steer a review turn')
|
|
334
|
-
|| text.includes('cannot steer a compact turn');
|
|
432
|
+
failure.message,
|
|
433
|
+
failure.data?.details,
|
|
434
|
+
failure.cause?.message,
|
|
435
|
+
failure.cause?.data?.details
|
|
436
|
+
].filter(Boolean).join(" ").toLowerCase();
|
|
437
|
+
return text.includes("no active codex turn to steer") || text.includes("no active turn to steer") || /expected active turn id .* but found/.test(text) || text.includes("cannot steer a review turn") || text.includes("cannot steer a compact turn");
|
|
335
438
|
}
|
|
336
|
-
|
|
337
439
|
function childHasExited(child) {
|
|
338
|
-
return !child
|
|
339
|
-
|| (child.exitCode !== null && child.exitCode !== undefined)
|
|
340
|
-
|| (child.signalCode !== null && child.signalCode !== undefined);
|
|
440
|
+
return !child || child.exitCode !== null && child.exitCode !== void 0 || child.signalCode !== null && child.signalCode !== void 0;
|
|
341
441
|
}
|
|
342
|
-
|
|
343
442
|
function processGroupHasExited(binding) {
|
|
344
|
-
if (!binding?.ownsProcessGroup || !binding.child?.pid || process.platform ===
|
|
443
|
+
if (!binding?.ownsProcessGroup || !binding.child?.pid || process.platform === "win32") {
|
|
345
444
|
return childHasExited(binding?.child);
|
|
346
445
|
}
|
|
347
446
|
try {
|
|
348
447
|
process.kill(-binding.child.pid, 0);
|
|
349
448
|
return false;
|
|
350
449
|
} catch (error) {
|
|
351
|
-
return error
|
|
450
|
+
return asErrorLike(error).code === "ESRCH";
|
|
352
451
|
}
|
|
353
452
|
}
|
|
354
|
-
|
|
355
453
|
async function waitForProcessTreeExit(binding, timeoutMs) {
|
|
356
454
|
const deadline = Date.now() + timeoutMs;
|
|
357
455
|
while (Date.now() <= deadline) {
|
|
358
456
|
if (processGroupHasExited(binding)) return true;
|
|
359
|
-
await new Promise(resolve => setTimeout(resolve, 25));
|
|
457
|
+
await new Promise((resolve) => setTimeout(resolve, 25));
|
|
360
458
|
}
|
|
361
459
|
return processGroupHasExited(binding);
|
|
362
460
|
}
|
|
363
|
-
|
|
364
461
|
function signalProcessTree(binding, signal) {
|
|
365
462
|
if (!binding?.child || processGroupHasExited(binding)) return;
|
|
366
|
-
if (binding.ownsProcessGroup && binding.child.pid && process.platform !==
|
|
463
|
+
if (binding.ownsProcessGroup && binding.child.pid && process.platform !== "win32") {
|
|
367
464
|
try {
|
|
368
465
|
process.kill(-binding.child.pid, signal);
|
|
369
466
|
return;
|
|
370
467
|
} catch (error) {
|
|
371
|
-
if (error
|
|
468
|
+
if (asErrorLike(error).code === "ESRCH") return;
|
|
372
469
|
}
|
|
373
470
|
}
|
|
374
471
|
binding.child.kill(signal);
|
|
375
472
|
}
|
|
376
|
-
|
|
377
473
|
async function stopBindingProcessAndWait(binding) {
|
|
378
474
|
if (await waitForProcessTreeExit(binding, IDENTITY_ADAPTER_GRACEFUL_EXIT_MS)) return;
|
|
379
|
-
signalProcessTree(binding,
|
|
475
|
+
signalProcessTree(binding, "SIGTERM");
|
|
380
476
|
if (await waitForProcessTreeExit(binding, IDENTITY_ADAPTER_TERMINATE_MS)) return;
|
|
381
|
-
signalProcessTree(binding,
|
|
477
|
+
signalProcessTree(binding, "SIGKILL");
|
|
382
478
|
if (await waitForProcessTreeExit(binding, IDENTITY_ADAPTER_KILL_MS)) return;
|
|
383
|
-
throw new Error(`ACP identity adapter process tree ${binding.child?.pid ||
|
|
479
|
+
throw new Error(`ACP identity adapter process tree ${binding.child?.pid || ""} did not exit`);
|
|
384
480
|
}
|
|
385
|
-
|
|
386
481
|
async function describeAcpProcessGroup(pid) {
|
|
387
482
|
const processId = Number(pid);
|
|
388
|
-
if (!Number.isSafeInteger(processId) || processId <= 0 || process.platform ===
|
|
483
|
+
if (!Number.isSafeInteger(processId) || processId <= 0 || process.platform === "win32") {
|
|
389
484
|
return null;
|
|
390
485
|
}
|
|
391
486
|
let stdout;
|
|
392
487
|
try {
|
|
393
488
|
({ stdout } = await execFileAsync(
|
|
394
|
-
|
|
395
|
-
[
|
|
396
|
-
{ encoding:
|
|
489
|
+
"/bin/ps",
|
|
490
|
+
["-p", String(processId), "-o", "pid=", "-o", "pgid=", "-o", "lstart="],
|
|
491
|
+
{ encoding: "utf8", timeout: 1e3, maxBuffer: 16384 }
|
|
397
492
|
));
|
|
398
493
|
} catch (error) {
|
|
399
|
-
if (error
|
|
494
|
+
if (asErrorLike(error).code === 1 || asErrorLike(error).code === "ESRCH") return null;
|
|
400
495
|
throw error;
|
|
401
496
|
}
|
|
402
|
-
const match = String(stdout ||
|
|
497
|
+
const match = String(stdout || "").trim().match(/^(\d+)\s+(\d+)\s+(.+)$/);
|
|
403
498
|
if (!match || Number(match[1]) !== processId) {
|
|
404
499
|
throw new Error(`Could not read ACP process identity for pid ${processId}`);
|
|
405
500
|
}
|
|
@@ -409,18 +504,12 @@ async function describeAcpProcessGroup(pid) {
|
|
|
409
504
|
return {
|
|
410
505
|
pid: processId,
|
|
411
506
|
processGroupId: Number(match[2]),
|
|
412
|
-
startedAt: match[3].trim()
|
|
507
|
+
startedAt: match[3].trim()
|
|
413
508
|
};
|
|
414
509
|
}
|
|
415
|
-
|
|
416
510
|
async function stopPersistedAcpProcessGroup(identity) {
|
|
417
|
-
const expected = identity && typeof identity ===
|
|
418
|
-
if (
|
|
419
|
-
!expected
|
|
420
|
-
|| !Number.isSafeInteger(Number(expected.pid))
|
|
421
|
-
|| !Number.isSafeInteger(Number(expected.processGroupId))
|
|
422
|
-
|| !String(expected.startedAt || '').trim()
|
|
423
|
-
) {
|
|
511
|
+
const expected = identity && typeof identity === "object" ? identity : null;
|
|
512
|
+
if (!expected || !Number.isSafeInteger(Number(expected.pid)) || !Number.isSafeInteger(Number(expected.processGroupId)) || !String(expected.startedAt || "").trim()) {
|
|
424
513
|
return { stopped: false, missingProof: true };
|
|
425
514
|
}
|
|
426
515
|
const processGroupId = Number(expected.processGroupId);
|
|
@@ -429,79 +518,69 @@ async function stopPersistedAcpProcessGroup(identity) {
|
|
|
429
518
|
process.kill(-processGroupId, 0);
|
|
430
519
|
return false;
|
|
431
520
|
} catch (error) {
|
|
432
|
-
return error
|
|
521
|
+
return asErrorLike(error).code === "ESRCH";
|
|
433
522
|
}
|
|
434
523
|
};
|
|
435
524
|
const current = await describeAcpProcessGroup(Number(expected.pid));
|
|
436
525
|
if (!current && groupHasExited()) return { stopped: true, alreadyExited: true };
|
|
437
|
-
if (
|
|
438
|
-
current
|
|
439
|
-
&& (
|
|
440
|
-
current.processGroupId !== processGroupId
|
|
441
|
-
|| current.startedAt !== String(expected.startedAt)
|
|
442
|
-
)
|
|
443
|
-
) {
|
|
526
|
+
if (current && (current.processGroupId !== processGroupId || current.startedAt !== String(expected.startedAt))) {
|
|
444
527
|
return { stopped: false, identityMismatch: true };
|
|
445
528
|
}
|
|
446
|
-
const waitForExit = async timeoutMs => {
|
|
529
|
+
const waitForExit = async (timeoutMs) => {
|
|
447
530
|
const deadline = Date.now() + timeoutMs;
|
|
448
531
|
while (Date.now() <= deadline) {
|
|
449
532
|
if (groupHasExited()) return true;
|
|
450
|
-
await new Promise(resolve => setTimeout(resolve, 25));
|
|
533
|
+
await new Promise((resolve) => setTimeout(resolve, 25));
|
|
451
534
|
}
|
|
452
535
|
return groupHasExited();
|
|
453
536
|
};
|
|
454
|
-
const signal = value => {
|
|
537
|
+
const signal = (value) => {
|
|
455
538
|
try {
|
|
456
539
|
process.kill(-processGroupId, value);
|
|
457
540
|
} catch (error) {
|
|
458
|
-
if (error
|
|
541
|
+
if (asErrorLike(error).code !== "ESRCH") throw error;
|
|
459
542
|
}
|
|
460
543
|
};
|
|
461
|
-
|
|
462
|
-
signal('SIGTERM');
|
|
544
|
+
signal("SIGTERM");
|
|
463
545
|
if (await waitForExit(IDENTITY_ADAPTER_TERMINATE_MS)) return { stopped: true };
|
|
464
|
-
signal(
|
|
546
|
+
signal("SIGKILL");
|
|
465
547
|
if (await waitForExit(IDENTITY_ADAPTER_KILL_MS)) return { stopped: true };
|
|
466
548
|
return { stopped: false, timedOut: true };
|
|
467
549
|
}
|
|
468
|
-
|
|
469
550
|
async function deleteProviderSessionIdentity(options = {}) {
|
|
470
|
-
const provider = String(options.provider ||
|
|
471
|
-
const sessionId = String(options.sessionId ||
|
|
472
|
-
if (!isSafeProviderSessionId(sessionId)) {
|
|
473
|
-
throw new Error(
|
|
551
|
+
const provider = String(options.provider || "").trim().toLowerCase();
|
|
552
|
+
const sessionId = String(options.sessionId || "").trim();
|
|
553
|
+
if (!(0, import_provider_session_id.isSafeProviderSessionId)(sessionId)) {
|
|
554
|
+
throw new Error("Provider session rollback requires a safe exact session id");
|
|
474
555
|
}
|
|
475
556
|
let args;
|
|
476
|
-
if (provider ===
|
|
477
|
-
else if (provider ===
|
|
478
|
-
else throw new Error(`${provider ||
|
|
479
|
-
await execFileAsync(options.executable || getProviderAdapter(provider)?.executable, args, {
|
|
557
|
+
if (provider === "codex") args = ["delete", "--force", sessionId];
|
|
558
|
+
else if (provider === "opencode") args = ["session", "delete", sessionId];
|
|
559
|
+
else throw new Error(`${provider || "Provider"} does not support identity rollback`);
|
|
560
|
+
await execFileAsync(options.executable || (0, import_provider_adapters.getProviderAdapter)(provider)?.executable, args, {
|
|
480
561
|
cwd: options.cwd,
|
|
481
562
|
env: options.env,
|
|
482
|
-
timeout:
|
|
483
|
-
maxBuffer: 1024 * 1024
|
|
563
|
+
timeout: 15e3,
|
|
564
|
+
maxBuffer: 1024 * 1024
|
|
484
565
|
});
|
|
485
566
|
}
|
|
486
|
-
|
|
487
567
|
function attachProviderSessionIdentity(error, identity, rollbackError) {
|
|
488
|
-
const target = error instanceof Error ? error : new Error(String(error ||
|
|
489
|
-
Object.defineProperty(target,
|
|
568
|
+
const target = error instanceof Error ? error : new Error(String(error || "Provider session identity failed"));
|
|
569
|
+
Object.defineProperty(target, "providerSessionIdentity", {
|
|
490
570
|
value: identity,
|
|
491
571
|
enumerable: false,
|
|
492
|
-
configurable: true
|
|
572
|
+
configurable: true
|
|
493
573
|
});
|
|
494
574
|
if (rollbackError) {
|
|
495
|
-
Object.defineProperty(target,
|
|
575
|
+
Object.defineProperty(target, "providerSessionRollbackError", {
|
|
496
576
|
value: rollbackError,
|
|
497
577
|
enumerable: false,
|
|
498
|
-
configurable: true
|
|
578
|
+
configurable: true
|
|
499
579
|
});
|
|
500
580
|
}
|
|
501
581
|
return target;
|
|
502
582
|
}
|
|
503
|
-
|
|
504
|
-
class AcpRuntime extends EventEmitter {
|
|
583
|
+
class AcpRuntime extends import_events.EventEmitter {
|
|
505
584
|
constructor(options = {}) {
|
|
506
585
|
super();
|
|
507
586
|
this.spawn = options.spawn || spawn;
|
|
@@ -517,168 +596,173 @@ class AcpRuntime extends EventEmitter {
|
|
|
517
596
|
this.historyReplayMaxWaitMs = options.historyReplayMaxWaitMs ?? DEFAULT_HISTORY_REPLAY_MAX_WAIT_MS;
|
|
518
597
|
this.deleteProviderSessionIdentity = options.deleteProviderSessionIdentity || deleteProviderSessionIdentity;
|
|
519
598
|
this.describeProcessGroup = options.describeAcpProcessGroup || describeAcpProcessGroup;
|
|
520
|
-
this.checkpointStore = options.checkpointStore
|
|
521
|
-
|
|
522
|
-
this.bindings = new Map();
|
|
599
|
+
this.checkpointStore = options.checkpointStore || (options.configDir ? new import_acp_checkpoint_store.AcpCheckpointStore(options.configDir, options.checkpointOptions) : null);
|
|
600
|
+
this.bindings = /* @__PURE__ */ new Map();
|
|
523
601
|
this.disposing = false;
|
|
524
602
|
this.disposePromise = null;
|
|
525
603
|
this.disposed = false;
|
|
526
|
-
this.clientFileSystem = options.clientFileSystem || new AcpClientFileSystem();
|
|
527
|
-
this.clientTerminals = options.clientTerminals || new AcpClientTerminalManager({ spawn: options.terminalSpawn });
|
|
528
|
-
|
|
529
|
-
|
|
604
|
+
this.clientFileSystem = options.clientFileSystem || new import_client_services.AcpClientFileSystem();
|
|
605
|
+
this.clientTerminals = options.clientTerminals || new import_client_services.AcpClientTerminalManager({ spawn: options.terminalSpawn });
|
|
606
|
+
this.browserPermissionDecision = typeof options.browserPermissionDecision === "function" ? options.browserPermissionDecision : (_agentId, tool, input) => defaultBrowserPermissionDecision({ tool, arguments: input });
|
|
607
|
+
}
|
|
608
|
+
/**
|
|
609
|
+
* @param {{
|
|
610
|
+
* agentId?: string,
|
|
611
|
+
* provider?: string,
|
|
612
|
+
* forkSourceSessionId?: string,
|
|
613
|
+
* forkSourceCheckpoint?: unknown,
|
|
614
|
+
* onForkSessionCreated?: (sessionId: string) => Promise<void> | void,
|
|
615
|
+
* [key: string]: unknown,
|
|
616
|
+
* }} [options]
|
|
617
|
+
*/
|
|
530
618
|
async prepareAgent(options = {}) {
|
|
531
619
|
if (this.disposing || this.disposed) {
|
|
532
|
-
throw new Error(
|
|
620
|
+
throw new Error("ACP runtime is shutting down");
|
|
533
621
|
}
|
|
534
|
-
const agentId = String(options.agentId ||
|
|
535
|
-
if (!agentId) throw new Error(
|
|
536
|
-
if (this.bindings.has(agentId)) throw new Error(
|
|
537
|
-
const provider = String(options.provider ||
|
|
622
|
+
const agentId = String(options.agentId || "");
|
|
623
|
+
if (!agentId) throw new Error("ACP Agent id is required");
|
|
624
|
+
if (this.bindings.has(agentId)) throw new Error("ACP Agent is already registered");
|
|
625
|
+
const provider = String(options.provider || "").trim().toLowerCase();
|
|
538
626
|
const launch = this.resolveLaunch(provider, options);
|
|
539
627
|
const cwd = path.resolve(options.cwd || process.cwd());
|
|
540
|
-
const requestedSessionId = String(options.sessionId ||
|
|
541
|
-
const forkSourceSessionId = String(options.forkSourceSessionId ||
|
|
628
|
+
const requestedSessionId = String(options.sessionId || "").trim();
|
|
629
|
+
const forkSourceSessionId = String(options.forkSourceSessionId || "").trim();
|
|
542
630
|
const forkSourceCheckpoint = options.forkSourceCheckpoint || null;
|
|
543
|
-
const forkSourceSessionCheckpoint = forkSourceCheckpoint?.version === 2
|
|
544
|
-
|
|
545
|
-
: forkSourceCheckpoint;
|
|
546
|
-
const revisionBase = Number.isFinite(Number(options.revisionBase))
|
|
547
|
-
? Math.max(0, Math.floor(Number(options.revisionBase)))
|
|
548
|
-
: 0;
|
|
631
|
+
const forkSourceSessionCheckpoint = forkSourceCheckpoint?.version === 2 ? forkSourceCheckpoint.sessionState : forkSourceCheckpoint;
|
|
632
|
+
const revisionBase = Number.isFinite(Number(options.revisionBase)) ? Math.max(0, Math.floor(Number(options.revisionBase))) : 0;
|
|
549
633
|
let forkSourceCheckpointState = null;
|
|
550
634
|
if (forkSourceSessionId) {
|
|
551
635
|
if (requestedSessionId) {
|
|
552
|
-
throw new Error(
|
|
636
|
+
throw new Error("ACP fork startup cannot also load a target session");
|
|
553
637
|
}
|
|
554
|
-
if (!isSafeProviderSessionId(forkSourceSessionId)) {
|
|
555
|
-
throw new Error(
|
|
638
|
+
if (!(0, import_provider_session_id.isSafeProviderSessionId)(forkSourceSessionId)) {
|
|
639
|
+
throw new Error("ACP fork startup requires a safe exact source session id");
|
|
556
640
|
}
|
|
557
641
|
if (!forkSourceCheckpoint) {
|
|
558
|
-
throw new Error(
|
|
559
|
-
}
|
|
560
|
-
const checkpointCwd = String(forkSourceSessionCheckpoint?.cwd ||
|
|
561
|
-
if (
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|| path.resolve(checkpointCwd) !== cwd
|
|
566
|
-
) {
|
|
567
|
-
throw new Error('ACP fork source checkpoint does not match the requested source');
|
|
568
|
-
}
|
|
569
|
-
forkSourceCheckpointState = AcpSessionState.fromCheckpoint(forkSourceSessionCheckpoint, {
|
|
642
|
+
throw new Error("ACP fork startup requires an exact source checkpoint");
|
|
643
|
+
}
|
|
644
|
+
const checkpointCwd = String(forkSourceSessionCheckpoint?.cwd || "").trim();
|
|
645
|
+
if (String(forkSourceSessionCheckpoint?.provider || "") !== provider || String(forkSourceSessionCheckpoint?.sessionId || "") !== forkSourceSessionId || !checkpointCwd || path.resolve(checkpointCwd) !== cwd) {
|
|
646
|
+
throw new Error("ACP fork source checkpoint does not match the requested source");
|
|
647
|
+
}
|
|
648
|
+
forkSourceCheckpointState = import_acp_session_state.AcpSessionState.fromCheckpoint(forkSourceSessionCheckpoint, {
|
|
570
649
|
provider,
|
|
571
650
|
sessionId: forkSourceSessionId,
|
|
572
651
|
cwd,
|
|
573
|
-
maxUpdates: this.maxUpdates
|
|
652
|
+
maxUpdates: this.maxUpdates
|
|
574
653
|
});
|
|
575
654
|
if (!forkSourceCheckpointState) {
|
|
576
|
-
throw new Error(
|
|
655
|
+
throw new Error("ACP fork source checkpoint is invalid");
|
|
577
656
|
}
|
|
578
657
|
}
|
|
579
658
|
const sessionRequestOptions = acpSessionRequestOptions(options, cwd);
|
|
580
659
|
const binding = {
|
|
581
660
|
agentId,
|
|
582
661
|
provider,
|
|
583
|
-
providerHomeId: String(options.providerHomeId ||
|
|
662
|
+
providerHomeId: String(options.providerHomeId || "default"),
|
|
584
663
|
cwd,
|
|
585
664
|
sessionRequestOptions,
|
|
586
|
-
env: (getProviderAdapter(provider)?.prepareAcpEnvironment || (value => value.env || process.env))(options),
|
|
665
|
+
env: ((0, import_provider_adapters.getProviderAdapter)(provider)?.prepareAcpEnvironment || ((value) => value.env || process.env))(options),
|
|
587
666
|
launch,
|
|
588
667
|
restartOptions: { ...options, agentId, provider },
|
|
589
|
-
approvalMode: options.approvalMode ||
|
|
590
|
-
|
|
668
|
+
approvalMode: options.approvalMode || "approve",
|
|
669
|
+
farmingBrowserApprovedSites: /* @__PURE__ */ new Set(),
|
|
670
|
+
farmingBrowserApprovalScopes: /* @__PURE__ */ new Map(),
|
|
671
|
+
ownsProcessGroup: process.platform !== "win32",
|
|
591
672
|
child: null,
|
|
592
673
|
connection: null,
|
|
593
674
|
initializeResponse: null,
|
|
594
|
-
sessionId:
|
|
595
|
-
untrustedSessionId:
|
|
596
|
-
state:
|
|
597
|
-
error:
|
|
598
|
-
stopReason:
|
|
675
|
+
sessionId: "",
|
|
676
|
+
untrustedSessionId: "",
|
|
677
|
+
state: "connecting",
|
|
678
|
+
error: "",
|
|
679
|
+
stopReason: "",
|
|
599
680
|
modes: null,
|
|
600
681
|
configOptions: [],
|
|
601
|
-
pendingPermissions: new Map(),
|
|
602
|
-
permissionResolvers: new Map(),
|
|
603
|
-
pendingElicitations: new Map(),
|
|
604
|
-
elicitationResolvers: new Map(),
|
|
605
|
-
activeElicitations: new Map(),
|
|
606
|
-
subagentStates: new Map(),
|
|
607
|
-
subagentControls: new Map(),
|
|
682
|
+
pendingPermissions: /* @__PURE__ */ new Map(),
|
|
683
|
+
permissionResolvers: /* @__PURE__ */ new Map(),
|
|
684
|
+
pendingElicitations: /* @__PURE__ */ new Map(),
|
|
685
|
+
elicitationResolvers: /* @__PURE__ */ new Map(),
|
|
686
|
+
activeElicitations: /* @__PURE__ */ new Map(),
|
|
687
|
+
subagentStates: /* @__PURE__ */ new Map(),
|
|
688
|
+
subagentControls: /* @__PURE__ */ new Map(),
|
|
608
689
|
nextSubagentGeneration: 1,
|
|
609
|
-
interactionOrigins: new Map(),
|
|
690
|
+
interactionOrigins: /* @__PURE__ */ new Map(),
|
|
610
691
|
// One binding owns at most one Turn. All Turn controls and its terminal
|
|
611
692
|
// commit are fenced by this object before they may mutate shared state.
|
|
612
693
|
activeTurn: null,
|
|
613
694
|
nextTurnId: 1,
|
|
614
695
|
supportsSteer: false,
|
|
615
696
|
historyReplayActive: false,
|
|
616
|
-
sessionState:
|
|
697
|
+
sessionState: new import_acp_session_state.AcpSessionState({
|
|
698
|
+
provider,
|
|
699
|
+
sessionId: "",
|
|
700
|
+
cwd,
|
|
701
|
+
maxUpdates: this.maxUpdates
|
|
702
|
+
}),
|
|
617
703
|
authTerminal: null,
|
|
618
|
-
patchDecisions: new Map(),
|
|
619
|
-
patchDecisionInFlight: new Map(),
|
|
704
|
+
patchDecisions: /* @__PURE__ */ new Map(),
|
|
705
|
+
patchDecisionInFlight: /* @__PURE__ */ new Map(),
|
|
620
706
|
checkpointProof: null,
|
|
621
707
|
sessionMutation: null,
|
|
622
708
|
configMutationTail: null,
|
|
623
|
-
|
|
709
|
+
deferredConfigChanges: /* @__PURE__ */ new Map(),
|
|
710
|
+
deferredConfigFlush: null,
|
|
711
|
+
deferredModeGeneration: 0,
|
|
712
|
+
deferredModeId: "",
|
|
713
|
+
stderr: "",
|
|
624
714
|
exited: false,
|
|
625
|
-
updatedAt: new Date().toISOString()
|
|
715
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
626
716
|
};
|
|
627
717
|
this.bindings.set(agentId, binding);
|
|
628
718
|
this.emitRuntime(binding);
|
|
629
|
-
|
|
630
719
|
try {
|
|
631
|
-
const gatedLaunch = binding.ownsProcessGroup
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
}
|
|
642
|
-
: launch;
|
|
720
|
+
const gatedLaunch = binding.ownsProcessGroup ? {
|
|
721
|
+
command: "/bin/sh",
|
|
722
|
+
args: [
|
|
723
|
+
"-c",
|
|
724
|
+
'IFS= read -r farming_acp_start || exit 0; exec "$@"',
|
|
725
|
+
"farming-acp-start-gate",
|
|
726
|
+
launch.command,
|
|
727
|
+
...launch.args
|
|
728
|
+
]
|
|
729
|
+
} : launch;
|
|
643
730
|
const child = this.spawn(gatedLaunch.command, gatedLaunch.args, {
|
|
644
731
|
cwd: binding.cwd,
|
|
645
732
|
env: binding.env,
|
|
646
|
-
stdio: [
|
|
647
|
-
detached: binding.ownsProcessGroup
|
|
733
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
734
|
+
detached: binding.ownsProcessGroup
|
|
648
735
|
});
|
|
649
736
|
binding.child = child;
|
|
650
|
-
child.stderr.on(
|
|
651
|
-
binding.stderr = `${binding.stderr}${chunk.toString(
|
|
737
|
+
child.stderr.on("data", (chunk) => {
|
|
738
|
+
binding.stderr = `${binding.stderr}${chunk.toString("utf8")}`.slice(-16e3);
|
|
652
739
|
});
|
|
653
|
-
child.on(
|
|
654
|
-
child.on(
|
|
740
|
+
child.on("error", (error) => this.handleExit(binding, error));
|
|
741
|
+
child.on("close", (code, signal) => {
|
|
655
742
|
if (!binding.connection?.signal?.aborted) {
|
|
656
|
-
const detail = binding.stderr.trim() || `ACP adapter exited with code ${code}${signal ? ` (${signal})` :
|
|
743
|
+
const detail = binding.stderr.trim() || `ACP adapter exited with code ${code}${signal ? ` (${signal})` : ""}`;
|
|
657
744
|
this.handleExit(binding, code === 0 ? null : new Error(detail));
|
|
658
745
|
}
|
|
659
746
|
});
|
|
660
|
-
if (binding.ownsProcessGroup && typeof options.onProcessStarted ===
|
|
747
|
+
if (binding.ownsProcessGroup && typeof options.onProcessStarted === "function") {
|
|
661
748
|
const processIdentity = await this.describeProcessGroup(child.pid);
|
|
662
749
|
if (!processIdentity) {
|
|
663
|
-
throw new Error(`ACP process ${child.pid ||
|
|
750
|
+
throw new Error(`ACP process ${child.pid || ""} exited before its identity was persisted`);
|
|
664
751
|
}
|
|
665
752
|
await options.onProcessStarted(processIdentity);
|
|
666
753
|
}
|
|
667
754
|
if (binding.ownsProcessGroup) {
|
|
668
755
|
await new Promise((resolve, reject) => {
|
|
669
|
-
child.stdin.write(
|
|
756
|
+
child.stdin.write("start\n", (error) => {
|
|
670
757
|
if (error) reject(error);
|
|
671
758
|
else resolve();
|
|
672
759
|
});
|
|
673
760
|
});
|
|
674
761
|
}
|
|
675
|
-
|
|
676
|
-
const connection = this.createConnection
|
|
677
|
-
? await this.createConnection(this.clientHandlers(binding), child, binding)
|
|
678
|
-
: await this.officialConnection(this.clientHandlers(binding), child);
|
|
762
|
+
const connection = this.createConnection ? await this.createConnection(this.clientHandlers(binding), child, binding) : await this.officialConnection(this.clientHandlers(binding), child);
|
|
679
763
|
binding.connection = connection;
|
|
680
764
|
this.requireOpenBinding(binding);
|
|
681
|
-
connection.closed.catch(error => this.handleExit(binding, error));
|
|
765
|
+
connection.closed.catch((error) => this.handleExit(binding, error));
|
|
682
766
|
const sdk = await loadAcpSdk();
|
|
683
767
|
this.requireOpenBinding(binding);
|
|
684
768
|
binding.initializeResponse = await withTimeout(connection.initialize({
|
|
@@ -690,20 +774,18 @@ class AcpRuntime extends EventEmitter {
|
|
|
690
774
|
session: { configOptions: { boolean: {} } },
|
|
691
775
|
plan: {},
|
|
692
776
|
elicitation: { form: {}, url: {} },
|
|
693
|
-
_meta: { terminal_output: true }
|
|
777
|
+
_meta: { terminal_output: true }
|
|
694
778
|
},
|
|
695
|
-
clientInfo: { name:
|
|
696
|
-
}), this.initializeTimeoutMs,
|
|
779
|
+
clientInfo: { name: "farming", title: "Farming", version: packageJson.version || "0.0.0" }
|
|
780
|
+
}), this.initializeTimeoutMs, "ACP initialize");
|
|
697
781
|
this.requireOpenBinding(binding);
|
|
698
782
|
if (binding.initializeResponse.protocolVersion !== sdk.PROTOCOL_VERSION) {
|
|
699
783
|
throw new Error(`ACP protocol version mismatch: Agent selected ${binding.initializeResponse.protocolVersion}, Farming supports ${sdk.PROTOCOL_VERSION}`);
|
|
700
784
|
}
|
|
701
|
-
binding.supportsSteer = binding.provider ===
|
|
702
|
-
&& supportsCodexSteer(binding.initializeResponse.agentCapabilities);
|
|
703
|
-
|
|
785
|
+
binding.supportsSteer = binding.provider === "codex" && supportsCodexSteer(binding.initializeResponse.agentCapabilities);
|
|
704
786
|
const sessionRequest = { sessionId: requestedSessionId, ...binding.sessionRequestOptions };
|
|
705
787
|
let sessionResponse;
|
|
706
|
-
let historyMode =
|
|
788
|
+
let historyMode = "new";
|
|
707
789
|
if (forkSourceSessionId) {
|
|
708
790
|
const capabilities = binding.initializeResponse.agentCapabilities || {};
|
|
709
791
|
if (!capabilities.loadSession || !capabilities.sessionCapabilities?.fork) {
|
|
@@ -713,12 +795,12 @@ class AcpRuntime extends EventEmitter {
|
|
|
713
795
|
throw new Error(`${provider} ACP Agent cannot release the loaded fork source`);
|
|
714
796
|
}
|
|
715
797
|
binding.sessionId = forkSourceSessionId;
|
|
716
|
-
binding.sessionState = new AcpSessionState({
|
|
798
|
+
binding.sessionState = new import_acp_session_state.AcpSessionState({
|
|
717
799
|
provider,
|
|
718
800
|
sessionId: forkSourceSessionId,
|
|
719
801
|
cwd: binding.cwd,
|
|
720
802
|
maxUpdates: this.maxUpdates,
|
|
721
|
-
revisionBase
|
|
803
|
+
revisionBase
|
|
722
804
|
});
|
|
723
805
|
binding.historyReplayActive = true;
|
|
724
806
|
let loadedSource;
|
|
@@ -726,10 +808,10 @@ class AcpRuntime extends EventEmitter {
|
|
|
726
808
|
loadedSource = await withTimeout(
|
|
727
809
|
connection.loadSession({ sessionId: forkSourceSessionId, ...binding.sessionRequestOptions }),
|
|
728
810
|
this.sessionSetupTimeoutMs,
|
|
729
|
-
|
|
811
|
+
"ACP fork source session/load"
|
|
730
812
|
);
|
|
731
813
|
this.requireOpenBinding(binding);
|
|
732
|
-
if (provider ===
|
|
814
|
+
if (provider === "qoder") await this.waitForHistoryReplay(binding);
|
|
733
815
|
this.requireOpenBinding(binding);
|
|
734
816
|
} finally {
|
|
735
817
|
binding.historyReplayActive = false;
|
|
@@ -738,34 +820,31 @@ class AcpRuntime extends EventEmitter {
|
|
|
738
820
|
const forked = await withTimeout(
|
|
739
821
|
connection.unstable_forkSession({
|
|
740
822
|
sessionId: forkSourceSessionId,
|
|
741
|
-
...binding.sessionRequestOptions
|
|
823
|
+
...binding.sessionRequestOptions
|
|
742
824
|
}),
|
|
743
825
|
this.sessionSetupTimeoutMs,
|
|
744
|
-
|
|
826
|
+
"ACP owned session/fork"
|
|
745
827
|
);
|
|
746
|
-
const forkedSessionId = String(forked?.sessionId ||
|
|
747
|
-
if (
|
|
748
|
-
|
|
749
|
-
|| forkedSessionId === forkSourceSessionId
|
|
750
|
-
) {
|
|
751
|
-
throw new Error('ACP owned session/fork returned an invalid new session id');
|
|
828
|
+
const forkedSessionId = String(forked?.sessionId || "").trim();
|
|
829
|
+
if (!(0, import_provider_session_id.isSafeProviderSessionId)(forkedSessionId) || forkedSessionId === forkSourceSessionId) {
|
|
830
|
+
throw new Error("ACP owned session/fork returned an invalid new session id");
|
|
752
831
|
}
|
|
753
|
-
if (typeof options.onForkSessionCreated ===
|
|
832
|
+
if (typeof options.onForkSessionCreated === "function") {
|
|
754
833
|
await options.onForkSessionCreated(forkedSessionId);
|
|
755
834
|
}
|
|
756
835
|
this.requireOpenBinding(binding);
|
|
757
836
|
await withTimeout(
|
|
758
837
|
connection.closeSession({ sessionId: forkSourceSessionId }),
|
|
759
838
|
this.requestTimeoutMs,
|
|
760
|
-
|
|
839
|
+
"ACP fork source session/close"
|
|
761
840
|
);
|
|
762
841
|
this.requireOpenBinding(binding);
|
|
763
842
|
const forkUpdates = binding.subagentStates.get(forkedSessionId);
|
|
764
843
|
const restoredForkSource = this.restoreBindingCheckpoint(binding, forkSourceCheckpoint, {
|
|
765
|
-
sessionId: forkedSessionId
|
|
844
|
+
sessionId: forkedSessionId
|
|
766
845
|
});
|
|
767
846
|
if (!restoredForkSource) {
|
|
768
|
-
throw new Error(
|
|
847
|
+
throw new Error("ACP fork source checkpoint could not be restored");
|
|
769
848
|
}
|
|
770
849
|
binding.sessionId = forkedSessionId;
|
|
771
850
|
binding.sessionState = restoredForkSource.sessionState;
|
|
@@ -775,7 +854,9 @@ class AcpRuntime extends EventEmitter {
|
|
|
775
854
|
delete binding.restartOptions.forkSourceSessionId;
|
|
776
855
|
delete binding.restartOptions.forkSourceCheckpoint;
|
|
777
856
|
delete binding.restartOptions.onForkSessionCreated;
|
|
778
|
-
if (forkUpdates?.updates?.some(
|
|
857
|
+
if (forkUpdates?.updates?.some(
|
|
858
|
+
(item) => recordValue(item?.update).sessionUpdate === "available_commands_update"
|
|
859
|
+
)) {
|
|
779
860
|
binding.sessionState.availableCommands = clone(forkUpdates.availableCommands);
|
|
780
861
|
}
|
|
781
862
|
sessionResponse = {
|
|
@@ -783,32 +864,36 @@ class AcpRuntime extends EventEmitter {
|
|
|
783
864
|
...forked,
|
|
784
865
|
sessionId: forkedSessionId,
|
|
785
866
|
modes: forked?.modes || loadedSource?.modes,
|
|
786
|
-
configOptions: forked?.configOptions || loadedSource?.configOptions
|
|
867
|
+
configOptions: forked?.configOptions || loadedSource?.configOptions
|
|
787
868
|
};
|
|
788
|
-
historyMode =
|
|
869
|
+
historyMode = "fork";
|
|
789
870
|
} else if (requestedSessionId) {
|
|
790
871
|
const capabilities = binding.initializeResponse.agentCapabilities || {};
|
|
791
872
|
let opened = false;
|
|
792
873
|
let restoredCheckpointState = null;
|
|
793
874
|
let restoredCheckpoint = null;
|
|
794
875
|
let saved = null;
|
|
795
|
-
if (options.historyMode ===
|
|
876
|
+
if (options.historyMode === "checkpoint" && this.checkpointStore) {
|
|
796
877
|
saved = await this.checkpointStore.load(
|
|
797
878
|
this.checkpointIdentity(binding, requestedSessionId),
|
|
798
|
-
{ allowDirty: true }
|
|
879
|
+
{ allowDirty: true }
|
|
799
880
|
);
|
|
800
881
|
this.requireOpenBinding(binding);
|
|
801
|
-
restoredCheckpoint = this.restoreBindingCheckpoint(
|
|
802
|
-
|
|
803
|
-
|
|
882
|
+
restoredCheckpoint = this.restoreBindingCheckpoint(
|
|
883
|
+
binding,
|
|
884
|
+
recordValue(saved?.state),
|
|
885
|
+
{ sessionId: requestedSessionId }
|
|
886
|
+
);
|
|
804
887
|
restoredCheckpointState = restoredCheckpoint?.sessionState || null;
|
|
888
|
+
if (restoredCheckpoint?.deferredConfigChanges instanceof Map) {
|
|
889
|
+
binding.deferredConfigChanges = restoredCheckpoint.deferredConfigChanges;
|
|
890
|
+
}
|
|
891
|
+
binding.deferredModeId = String(restoredCheckpoint?.deferredModeId || "");
|
|
805
892
|
}
|
|
806
893
|
if (capabilities.sessionCapabilities?.resume && restoredCheckpoint) {
|
|
807
|
-
const providerStateMatches = restoredCheckpoint?.complete === true && saved?.exact === true
|
|
808
|
-
? await this.checkpointMatchesProviderSession(connection, capabilities, sessionRequest, saved)
|
|
809
|
-
: false;
|
|
894
|
+
const providerStateMatches = restoredCheckpoint?.complete === true && saved?.exact === true ? await this.checkpointMatchesProviderSession(connection, capabilities, sessionRequest, saved) : false;
|
|
810
895
|
this.requireOpenBinding(binding);
|
|
811
|
-
if (providerStateMatches) {
|
|
896
|
+
if (providerStateMatches && restoredCheckpointState) {
|
|
812
897
|
binding.sessionId = requestedSessionId;
|
|
813
898
|
binding.sessionState = restoredCheckpointState;
|
|
814
899
|
binding.subagentStates = restoredCheckpoint.subagentStates;
|
|
@@ -819,62 +904,65 @@ class AcpRuntime extends EventEmitter {
|
|
|
819
904
|
sessionResponse = await withTimeout(
|
|
820
905
|
connection.resumeSession(sessionRequest),
|
|
821
906
|
this.sessionSetupTimeoutMs,
|
|
822
|
-
|
|
907
|
+
"ACP session/resume"
|
|
823
908
|
);
|
|
824
909
|
this.requireOpenBinding(binding);
|
|
825
|
-
historyMode =
|
|
910
|
+
historyMode = "checkpoint";
|
|
826
911
|
opened = true;
|
|
827
912
|
} catch (error) {
|
|
828
913
|
this.requireOpenBinding(binding);
|
|
829
914
|
if (!capabilities.loadSession) throw error;
|
|
830
|
-
console.warn(`ACP checkpoint resume failed for ${provider}; replaying history:`,
|
|
915
|
+
console.warn(`ACP checkpoint resume failed for ${provider}; replaying history:`, asErrorLike(error).message || error);
|
|
831
916
|
}
|
|
832
917
|
}
|
|
833
918
|
}
|
|
834
|
-
if (!opened && options.historyMode !==
|
|
919
|
+
if (!opened && options.historyMode !== "resume" && capabilities.loadSession) {
|
|
835
920
|
const replayRevisionBase = Math.max(revisionBase, Number(restoredCheckpointState?.revision || 0));
|
|
836
921
|
binding.sessionId = requestedSessionId;
|
|
837
|
-
binding.sessionState = new AcpSessionState({
|
|
922
|
+
binding.sessionState = new import_acp_session_state.AcpSessionState({
|
|
838
923
|
provider,
|
|
839
924
|
sessionId: requestedSessionId,
|
|
840
925
|
cwd: binding.cwd,
|
|
841
926
|
maxUpdates: this.maxUpdates,
|
|
842
927
|
revisionBase: replayRevisionBase,
|
|
843
|
-
resetBeforeRevision: replayRevisionBase
|
|
928
|
+
resetBeforeRevision: replayRevisionBase
|
|
844
929
|
});
|
|
845
930
|
binding.historyReplayActive = true;
|
|
846
931
|
try {
|
|
847
932
|
sessionResponse = await withTimeout(
|
|
848
933
|
connection.loadSession(sessionRequest),
|
|
849
934
|
this.sessionSetupTimeoutMs,
|
|
850
|
-
|
|
935
|
+
"ACP session/load"
|
|
851
936
|
);
|
|
852
937
|
this.requireOpenBinding(binding);
|
|
853
|
-
if (provider ===
|
|
938
|
+
if (provider === "qoder") await this.waitForHistoryReplay(binding);
|
|
854
939
|
this.requireOpenBinding(binding);
|
|
855
940
|
} finally {
|
|
856
941
|
binding.historyReplayActive = false;
|
|
857
942
|
}
|
|
858
943
|
binding.sessionState.finishHistoryReplay();
|
|
944
|
+
if (provider === "qoder" && restoredCheckpointState) {
|
|
945
|
+
restoreMissingHistoryMedia(binding.sessionState, restoredCheckpointState);
|
|
946
|
+
}
|
|
859
947
|
this.restorePatchDecisions(binding, restoredCheckpoint?.patchDecisions);
|
|
860
|
-
historyMode =
|
|
948
|
+
historyMode = "load";
|
|
861
949
|
opened = true;
|
|
862
950
|
} else if (!opened && capabilities.sessionCapabilities?.resume) {
|
|
863
951
|
binding.sessionId = requestedSessionId;
|
|
864
|
-
binding.sessionState = new AcpSessionState({
|
|
952
|
+
binding.sessionState = new import_acp_session_state.AcpSessionState({
|
|
865
953
|
provider,
|
|
866
954
|
sessionId: requestedSessionId,
|
|
867
955
|
cwd: binding.cwd,
|
|
868
956
|
maxUpdates: this.maxUpdates,
|
|
869
|
-
revisionBase
|
|
957
|
+
revisionBase
|
|
870
958
|
});
|
|
871
959
|
sessionResponse = await withTimeout(
|
|
872
960
|
connection.resumeSession(sessionRequest),
|
|
873
961
|
this.sessionSetupTimeoutMs,
|
|
874
|
-
|
|
962
|
+
"ACP session/resume"
|
|
875
963
|
);
|
|
876
964
|
this.requireOpenBinding(binding);
|
|
877
|
-
historyMode =
|
|
965
|
+
historyMode = "resume";
|
|
878
966
|
opened = true;
|
|
879
967
|
} else {
|
|
880
968
|
if (!opened) throw new Error(`${provider} ACP Agent cannot load or resume session ${requestedSessionId}`);
|
|
@@ -883,75 +971,89 @@ class AcpRuntime extends EventEmitter {
|
|
|
883
971
|
sessionResponse = await withTimeout(
|
|
884
972
|
connection.newSession(binding.sessionRequestOptions),
|
|
885
973
|
this.sessionSetupTimeoutMs,
|
|
886
|
-
|
|
974
|
+
"ACP session/new"
|
|
887
975
|
);
|
|
888
976
|
this.requireOpenBinding(binding);
|
|
889
|
-
const returnedSessionId = String(sessionResponse.sessionId ||
|
|
890
|
-
if (!isSafeProviderSessionId(returnedSessionId)) {
|
|
977
|
+
const returnedSessionId = String(sessionResponse.sessionId || "").trim();
|
|
978
|
+
if (!(0, import_provider_session_id.isSafeProviderSessionId)(returnedSessionId)) {
|
|
891
979
|
binding.untrustedSessionId = returnedSessionId;
|
|
892
|
-
throw new Error(
|
|
980
|
+
throw new Error("ACP session/new returned an invalid resumable session id");
|
|
893
981
|
}
|
|
894
982
|
binding.sessionId = returnedSessionId;
|
|
895
|
-
binding.sessionState = new AcpSessionState({ provider, sessionId: binding.sessionId, cwd: binding.cwd, maxUpdates: this.maxUpdates });
|
|
983
|
+
binding.sessionState = new import_acp_session_state.AcpSessionState({ provider, sessionId: binding.sessionId, cwd: binding.cwd, maxUpdates: this.maxUpdates });
|
|
896
984
|
}
|
|
897
985
|
this.requireOpenBinding(binding);
|
|
898
986
|
binding.modes = sessionResponse?.modes || null;
|
|
899
987
|
binding.configOptions = sessionResponse?.configOptions || [];
|
|
900
|
-
binding.sessionState.currentModeId = String(binding.modes?.currentModeId ||
|
|
988
|
+
binding.sessionState.currentModeId = String(binding.modes?.currentModeId || "");
|
|
901
989
|
binding.sessionState.configOptions = JSON.parse(JSON.stringify(binding.configOptions));
|
|
902
|
-
if (provider ===
|
|
903
|
-
const
|
|
904
|
-
|
|
905
|
-
&& /
|
|
906
|
-
|
|
907
|
-
|
|
990
|
+
if (provider === "claude" && historyMode === "new") {
|
|
991
|
+
const changes = [];
|
|
992
|
+
if (options.model && options.model !== "config") {
|
|
993
|
+
const modelOption = binding.configOptions.find((option) => option.type === "select" && (option.category === "model" || /(^|[\s_-])model([\s_-]|$)/i.test(`${option.id} ${option.name || ""}`)));
|
|
994
|
+
if (!modelOption) {
|
|
995
|
+
throw new Error("Claude ACP Agent did not advertise a Model configuration option");
|
|
996
|
+
}
|
|
997
|
+
changes.push({ configId: modelOption.id, value: options.model });
|
|
998
|
+
}
|
|
999
|
+
if (options.reasoningEffort && options.reasoningEffort !== "config") {
|
|
1000
|
+
const reasoningOption = binding.configOptions.find((option) => option.type === "select" && (option.category === "thought_level" || /(reasoning|thought|effort)/i.test(`${option.id} ${option.name || ""}`)));
|
|
1001
|
+
if (!reasoningOption) {
|
|
1002
|
+
throw new Error("Claude ACP Agent did not advertise a Reasoning configuration option");
|
|
1003
|
+
}
|
|
1004
|
+
changes.push({ configId: reasoningOption.id, value: options.reasoningEffort });
|
|
1005
|
+
}
|
|
1006
|
+
if (changes.length > 0) await this.applySessionConfigOptionsNow(binding, changes);
|
|
1007
|
+
}
|
|
1008
|
+
if (provider === "codex" && options.serviceTier && options.serviceTier !== "config") {
|
|
1009
|
+
const fastOption = binding.configOptions.find((option) => option.type === "boolean" && /fast/i.test(`${option.id || ""} ${option.name || ""} ${option.category || ""}`));
|
|
1010
|
+
const fastEnabled = ["fast", "priority"].includes(options.serviceTier);
|
|
908
1011
|
if (fastOption && fastOption.currentValue !== fastEnabled) {
|
|
909
1012
|
await this.applySessionConfigOption(binding, fastOption.id, fastEnabled, { emit: false });
|
|
910
1013
|
}
|
|
911
1014
|
}
|
|
912
1015
|
this.requireOpenBinding(binding);
|
|
913
|
-
binding.state =
|
|
914
|
-
binding.updatedAt = new Date().toISOString();
|
|
1016
|
+
binding.state = "idle";
|
|
1017
|
+
binding.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
915
1018
|
this.scheduleCheckpoint(binding, { exact: true });
|
|
916
1019
|
this.emitRuntime(binding);
|
|
917
1020
|
this.emitSession(binding);
|
|
1021
|
+
void this.flushDeferredSessionChanges(binding);
|
|
918
1022
|
return {
|
|
919
1023
|
sessionId: binding.sessionId,
|
|
920
1024
|
historyMode,
|
|
921
1025
|
protocolVersion: binding.initializeResponse.protocolVersion,
|
|
922
1026
|
agentInfo: binding.initializeResponse.agentInfo || null,
|
|
923
1027
|
capabilities: binding.initializeResponse.agentCapabilities || {},
|
|
924
|
-
adapter: launch
|
|
1028
|
+
adapter: launch
|
|
925
1029
|
};
|
|
926
1030
|
} catch (error) {
|
|
927
1031
|
const runtimeError = new Error(acpErrorMessage(error), { cause: error });
|
|
928
1032
|
this.handleExit(binding, runtimeError);
|
|
929
1033
|
if (options.identityOnly === true) {
|
|
930
1034
|
const returnedIdentity = binding.sessionId || binding.untrustedSessionId;
|
|
931
|
-
const identity = returnedIdentity
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
}
|
|
940
|
-
: null;
|
|
1035
|
+
const identity = returnedIdentity ? {
|
|
1036
|
+
provider,
|
|
1037
|
+
executable: options.executable || (0, import_provider_adapters.getProviderAdapter)(provider)?.executable,
|
|
1038
|
+
env: binding.env,
|
|
1039
|
+
cwd: binding.cwd,
|
|
1040
|
+
sessionId: returnedIdentity,
|
|
1041
|
+
producerStopped: false
|
|
1042
|
+
} : null;
|
|
941
1043
|
let producerStopped = false;
|
|
942
1044
|
try {
|
|
943
1045
|
await this.unregisterAgentAndWait(agentId);
|
|
944
1046
|
producerStopped = true;
|
|
945
1047
|
if (identity) identity.producerStopped = true;
|
|
946
1048
|
} catch (cleanupError) {
|
|
947
|
-
Object.defineProperty(runtimeError,
|
|
1049
|
+
Object.defineProperty(runtimeError, "adapterCleanupError", {
|
|
948
1050
|
value: cleanupError,
|
|
949
1051
|
enumerable: false,
|
|
950
|
-
configurable: true
|
|
1052
|
+
configurable: true
|
|
951
1053
|
});
|
|
952
1054
|
if (identity) attachProviderSessionIdentity(runtimeError, identity);
|
|
953
1055
|
}
|
|
954
|
-
if (identity && producerStopped && isSafeProviderSessionId(identity.sessionId)) {
|
|
1056
|
+
if (identity && producerStopped && (0, import_provider_session_id.isSafeProviderSessionId)(identity.sessionId)) {
|
|
955
1057
|
try {
|
|
956
1058
|
await this.deleteProviderSessionIdentity(identity);
|
|
957
1059
|
} catch (rollbackError) {
|
|
@@ -969,42 +1071,39 @@ class AcpRuntime extends EventEmitter {
|
|
|
969
1071
|
try {
|
|
970
1072
|
binding.connection?.close();
|
|
971
1073
|
} catch {
|
|
972
|
-
// Process-tree cleanup below remains authoritative.
|
|
973
1074
|
}
|
|
974
1075
|
await stopBindingProcessAndWait(binding);
|
|
975
1076
|
runtimeCleanupVerified = true;
|
|
976
1077
|
}
|
|
977
1078
|
} catch (cleanupError) {
|
|
978
|
-
Object.defineProperty(runtimeError,
|
|
1079
|
+
Object.defineProperty(runtimeError, "adapterCleanupError", {
|
|
979
1080
|
value: cleanupError,
|
|
980
1081
|
enumerable: false,
|
|
981
|
-
configurable: true
|
|
1082
|
+
configurable: true
|
|
982
1083
|
});
|
|
983
1084
|
}
|
|
984
1085
|
Object.defineProperties(runtimeError, {
|
|
985
1086
|
runtimeCleanupAttempted: {
|
|
986
1087
|
value: true,
|
|
987
1088
|
enumerable: false,
|
|
988
|
-
configurable: true
|
|
1089
|
+
configurable: true
|
|
989
1090
|
},
|
|
990
1091
|
runtimeCleanupVerified: {
|
|
991
1092
|
value: runtimeCleanupVerified === true,
|
|
992
1093
|
enumerable: false,
|
|
993
|
-
configurable: true
|
|
994
|
-
}
|
|
1094
|
+
configurable: true
|
|
1095
|
+
}
|
|
995
1096
|
});
|
|
996
1097
|
if (!runtimeCleanupVerified && !this.isCurrentBinding(binding)) {
|
|
997
1098
|
try {
|
|
998
1099
|
binding.connection?.close();
|
|
999
1100
|
} catch {
|
|
1000
|
-
// The detached binding remains only as an error cleanup handle.
|
|
1001
1101
|
}
|
|
1002
1102
|
}
|
|
1003
1103
|
}
|
|
1004
1104
|
throw runtimeError;
|
|
1005
1105
|
}
|
|
1006
1106
|
}
|
|
1007
|
-
|
|
1008
1107
|
async createSessionIdentity(options = {}) {
|
|
1009
1108
|
const agentId = `provider-session-${crypto.randomUUID()}`;
|
|
1010
1109
|
let prepared = null;
|
|
@@ -1016,25 +1115,24 @@ class AcpRuntime extends EventEmitter {
|
|
|
1016
1115
|
...options,
|
|
1017
1116
|
agentId,
|
|
1018
1117
|
identityOnly: true,
|
|
1019
|
-
sessionId:
|
|
1118
|
+
sessionId: ""
|
|
1020
1119
|
});
|
|
1021
1120
|
const binding = this.requireBinding(agentId);
|
|
1022
1121
|
identity = {
|
|
1023
|
-
provider: String(options.provider ||
|
|
1024
|
-
executable: options.executable || getProviderAdapter(options.provider)?.executable,
|
|
1122
|
+
provider: String(options.provider || "").trim().toLowerCase(),
|
|
1123
|
+
executable: options.executable || (0, import_provider_adapters.getProviderAdapter)(options.provider)?.executable,
|
|
1025
1124
|
env: binding.env,
|
|
1026
1125
|
cwd: binding.cwd,
|
|
1027
1126
|
sessionId: prepared.sessionId,
|
|
1028
|
-
producerStopped: false
|
|
1127
|
+
producerStopped: false
|
|
1029
1128
|
};
|
|
1030
1129
|
result = {
|
|
1031
1130
|
...prepared,
|
|
1032
|
-
sessionRequestOptions: this.getSessionRequestOptions(agentId)
|
|
1131
|
+
sessionRequestOptions: this.getSessionRequestOptions(agentId)
|
|
1033
1132
|
};
|
|
1034
1133
|
} catch (error) {
|
|
1035
|
-
failure = error instanceof Error ? error : new Error(String(error ||
|
|
1134
|
+
failure = error instanceof Error ? error : new Error(String(error || "Provider session identity failed"));
|
|
1036
1135
|
}
|
|
1037
|
-
|
|
1038
1136
|
if (prepared) {
|
|
1039
1137
|
let producerStopped = false;
|
|
1040
1138
|
try {
|
|
@@ -1043,14 +1141,12 @@ class AcpRuntime extends EventEmitter {
|
|
|
1043
1141
|
if (identity) identity.producerStopped = true;
|
|
1044
1142
|
} catch (cleanupError) {
|
|
1045
1143
|
if (!failure) {
|
|
1046
|
-
failure = cleanupError instanceof Error
|
|
1047
|
-
? cleanupError
|
|
1048
|
-
: new Error(String(cleanupError || 'ACP identity adapter cleanup failed'));
|
|
1144
|
+
failure = cleanupError instanceof Error ? cleanupError : new Error(String(cleanupError || "ACP identity adapter cleanup failed"));
|
|
1049
1145
|
} else {
|
|
1050
|
-
Object.defineProperty(failure,
|
|
1146
|
+
Object.defineProperty(failure, "adapterCleanupError", {
|
|
1051
1147
|
value: cleanupError,
|
|
1052
1148
|
enumerable: false,
|
|
1053
|
-
configurable: true
|
|
1149
|
+
configurable: true
|
|
1054
1150
|
});
|
|
1055
1151
|
}
|
|
1056
1152
|
if (identity) failure = attachProviderSessionIdentity(failure, identity);
|
|
@@ -1064,88 +1160,143 @@ class AcpRuntime extends EventEmitter {
|
|
|
1064
1160
|
}
|
|
1065
1161
|
}
|
|
1066
1162
|
if (failure) throw failure;
|
|
1163
|
+
if (!result) {
|
|
1164
|
+
throw new Error("Provider session identity completed without a result");
|
|
1165
|
+
}
|
|
1067
1166
|
return result;
|
|
1068
1167
|
}
|
|
1069
|
-
|
|
1070
1168
|
isCurrentBinding(binding) {
|
|
1071
1169
|
return Boolean(binding) && this.bindings.get(binding.agentId) === binding;
|
|
1072
1170
|
}
|
|
1073
|
-
|
|
1074
1171
|
requireCurrentBinding(binding) {
|
|
1075
1172
|
if (!this.isCurrentBinding(binding)) {
|
|
1076
|
-
throw new Error(
|
|
1173
|
+
throw new Error("ACP Agent binding is no longer active");
|
|
1077
1174
|
}
|
|
1078
1175
|
return binding;
|
|
1079
1176
|
}
|
|
1080
|
-
|
|
1081
1177
|
isOpenBinding(binding) {
|
|
1082
1178
|
return this.isCurrentBinding(binding) && binding.exited !== true;
|
|
1083
1179
|
}
|
|
1084
|
-
|
|
1085
1180
|
requireOpenBinding(binding) {
|
|
1086
|
-
if (this.disposing || this.disposed) throw new Error(
|
|
1181
|
+
if (this.disposing || this.disposed) throw new Error("ACP runtime is shutting down");
|
|
1087
1182
|
this.requireCurrentBinding(binding);
|
|
1088
|
-
if (binding.exited) throw new Error(
|
|
1183
|
+
if (binding.exited) throw new Error("ACP Agent connection is closed");
|
|
1089
1184
|
return binding;
|
|
1090
1185
|
}
|
|
1091
|
-
|
|
1092
1186
|
isCurrentTurn(binding, turn) {
|
|
1093
1187
|
return this.isOpenBinding(binding) && binding.activeTurn === turn;
|
|
1094
1188
|
}
|
|
1095
|
-
|
|
1096
1189
|
requireCurrentTurn(binding, turn, phases = null) {
|
|
1097
1190
|
this.requireOpenBinding(binding);
|
|
1098
|
-
if (
|
|
1099
|
-
|
|
1100
|
-
|| (Array.isArray(phases) && !phases.includes(turn?.phase))
|
|
1101
|
-
) {
|
|
1102
|
-
throw new Error('No active Codex turn to steer');
|
|
1191
|
+
if (binding.activeTurn !== turn || Array.isArray(phases) && !phases.includes(turn?.phase)) {
|
|
1192
|
+
throw new Error("No active Codex turn to steer");
|
|
1103
1193
|
}
|
|
1104
|
-
return turn;
|
|
1105
1194
|
}
|
|
1106
|
-
|
|
1107
1195
|
beginTurn(binding) {
|
|
1108
1196
|
this.requireOpenBinding(binding);
|
|
1109
1197
|
if (binding.activeTurn) throw new Error(`ACP Agent is not ready (${binding.state})`);
|
|
1110
1198
|
let resolveCompletion;
|
|
1111
|
-
const completion = new Promise(resolve => {
|
|
1199
|
+
const completion = new Promise((resolve) => {
|
|
1112
1200
|
resolveCompletion = resolve;
|
|
1113
1201
|
});
|
|
1114
1202
|
const turn = {
|
|
1115
1203
|
id: binding.nextTurnId++,
|
|
1116
|
-
phase:
|
|
1204
|
+
phase: "admitting",
|
|
1117
1205
|
previousState: binding.state,
|
|
1118
1206
|
providerSettled: false,
|
|
1119
1207
|
controlTail: Promise.resolve(),
|
|
1120
1208
|
cancelPromise: null,
|
|
1121
1209
|
completion,
|
|
1122
|
-
resolveCompletion
|
|
1210
|
+
resolveCompletion
|
|
1123
1211
|
};
|
|
1124
1212
|
binding.activeTurn = turn;
|
|
1125
1213
|
return turn;
|
|
1126
1214
|
}
|
|
1127
|
-
|
|
1128
1215
|
finishTurn(binding, turn, result) {
|
|
1129
1216
|
if (binding.activeTurn !== turn) return false;
|
|
1130
1217
|
binding.activeTurn = null;
|
|
1131
|
-
turn.phase =
|
|
1132
|
-
turn.resolveCompletion(result);
|
|
1218
|
+
turn.phase = "completed";
|
|
1219
|
+
turn.resolveCompletion?.(result);
|
|
1220
|
+
void this.flushDeferredSessionChanges(binding);
|
|
1221
|
+
return true;
|
|
1222
|
+
}
|
|
1223
|
+
async flushDeferredSessionChanges(binding) {
|
|
1224
|
+
if (!this.isOpenBinding(binding) || binding.activeTurn || binding.deferredConfigFlush || !binding.deferredModeId && binding.deferredConfigChanges.size === 0) return;
|
|
1225
|
+
const modeId = binding.deferredModeId;
|
|
1226
|
+
const modeGeneration = binding.deferredModeGeneration;
|
|
1227
|
+
const originalModeId = String(binding.sessionState?.currentModeId || binding.modes?.currentModeId || "");
|
|
1228
|
+
const changes = [...binding.deferredConfigChanges.values()];
|
|
1229
|
+
const flush = this.enqueueSessionConfigMutation(binding, async () => {
|
|
1230
|
+
let modeApplied = false;
|
|
1231
|
+
try {
|
|
1232
|
+
if (modeId) {
|
|
1233
|
+
await this.setSessionModeNow(binding, modeId);
|
|
1234
|
+
modeApplied = true;
|
|
1235
|
+
}
|
|
1236
|
+
if (changes.length > 0) await this.setSessionConfigOptionsNow(binding, changes);
|
|
1237
|
+
} catch (error) {
|
|
1238
|
+
if (modeApplied && originalModeId && originalModeId !== modeId) {
|
|
1239
|
+
try {
|
|
1240
|
+
await this.setSessionModeNow(binding, originalModeId);
|
|
1241
|
+
} catch (rollbackError) {
|
|
1242
|
+
if (error && typeof error === "object") {
|
|
1243
|
+
Object.defineProperty(error, "modeRollbackError", {
|
|
1244
|
+
value: rollbackError,
|
|
1245
|
+
enumerable: false,
|
|
1246
|
+
configurable: true
|
|
1247
|
+
});
|
|
1248
|
+
}
|
|
1249
|
+
}
|
|
1250
|
+
}
|
|
1251
|
+
throw error;
|
|
1252
|
+
}
|
|
1253
|
+
}).then(() => {
|
|
1254
|
+
if (binding.deferredModeGeneration === modeGeneration) binding.deferredModeId = "";
|
|
1255
|
+
for (const change of changes) {
|
|
1256
|
+
if (binding.deferredConfigChanges.get(change.configId) === change) {
|
|
1257
|
+
binding.deferredConfigChanges.delete(change.configId);
|
|
1258
|
+
}
|
|
1259
|
+
}
|
|
1260
|
+
this.clearDeferredSessionError(binding);
|
|
1261
|
+
}, (error) => {
|
|
1262
|
+
if (binding.deferredModeGeneration === modeGeneration) binding.deferredModeId = "";
|
|
1263
|
+
for (const change of changes) {
|
|
1264
|
+
if (binding.deferredConfigChanges.get(change.configId) === change) {
|
|
1265
|
+
binding.deferredConfigChanges.delete(change.configId);
|
|
1266
|
+
}
|
|
1267
|
+
}
|
|
1268
|
+
const rollbackError = recordValue(error).modeRollbackError;
|
|
1269
|
+
const configRollbackError = recordValue(error).configRollbackError;
|
|
1270
|
+
binding.error = `Deferred session change was not applied: ${acpErrorMessage(error)}${configRollbackError ? ` Configuration rollback also failed: ${acpErrorMessage(configRollbackError)}` : ""}${rollbackError ? ` Permission mode rollback also failed: ${acpErrorMessage(rollbackError)}` : ""}`;
|
|
1271
|
+
}).finally(() => {
|
|
1272
|
+
if (binding.deferredConfigFlush === flush) binding.deferredConfigFlush = null;
|
|
1273
|
+
if (!this.isOpenBinding(binding)) return;
|
|
1274
|
+
binding.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1275
|
+
this.scheduleCheckpoint(binding, { exact: true });
|
|
1276
|
+
this.emitRuntime(binding);
|
|
1277
|
+
void this.flushDeferredSessionChanges(binding);
|
|
1278
|
+
});
|
|
1279
|
+
binding.deferredConfigFlush = flush;
|
|
1280
|
+
}
|
|
1281
|
+
clearDeferredSessionError(binding) {
|
|
1282
|
+
if (!binding.error.startsWith("Deferred session change was not applied:")) return false;
|
|
1283
|
+
binding.error = "";
|
|
1133
1284
|
return true;
|
|
1134
1285
|
}
|
|
1135
|
-
|
|
1136
1286
|
cancelAdmittingTurn(binding, turn) {
|
|
1137
|
-
if (binding.activeTurn !== turn || turn.phase !==
|
|
1287
|
+
if (binding.activeTurn !== turn || turn.phase !== "admitting") return false;
|
|
1138
1288
|
binding.activeTurn = null;
|
|
1139
|
-
turn.phase =
|
|
1140
|
-
binding.state = turn.previousState ||
|
|
1141
|
-
turn.resolveCompletion({ status:
|
|
1289
|
+
turn.phase = "cancelled";
|
|
1290
|
+
binding.state = String(turn.previousState || "idle");
|
|
1291
|
+
turn.resolveCompletion?.({ status: "cancelled-before-submission" });
|
|
1142
1292
|
this.emitRuntime(binding);
|
|
1293
|
+
void this.flushDeferredSessionChanges(binding);
|
|
1143
1294
|
return true;
|
|
1144
1295
|
}
|
|
1145
|
-
|
|
1146
1296
|
async enqueueTurnControl(turn, operation) {
|
|
1147
1297
|
const previous = turn.controlTail || Promise.resolve();
|
|
1148
|
-
const pending = previous.catch(() => {
|
|
1298
|
+
const pending = previous.catch(() => {
|
|
1299
|
+
}).then(operation);
|
|
1149
1300
|
turn.controlTail = pending;
|
|
1150
1301
|
try {
|
|
1151
1302
|
return await pending;
|
|
@@ -1153,134 +1304,96 @@ class AcpRuntime extends EventEmitter {
|
|
|
1153
1304
|
if (turn.controlTail === pending) turn.controlTail = Promise.resolve();
|
|
1154
1305
|
}
|
|
1155
1306
|
}
|
|
1156
|
-
|
|
1157
1307
|
ensureSubagentControl(binding, sessionId) {
|
|
1158
|
-
const id = String(sessionId ||
|
|
1159
|
-
if (!id)
|
|
1308
|
+
const id = String(sessionId || "");
|
|
1309
|
+
if (!id) throw new Error("ACP subagent control requires a session id");
|
|
1160
1310
|
let control = binding.subagentControls.get(id);
|
|
1161
1311
|
if (!control) {
|
|
1162
1312
|
control = {
|
|
1163
1313
|
sessionId: id,
|
|
1164
1314
|
generation: binding.nextSubagentGeneration++,
|
|
1165
|
-
phase:
|
|
1166
|
-
error:
|
|
1167
|
-
cancelPromise: null
|
|
1315
|
+
phase: "active",
|
|
1316
|
+
error: "",
|
|
1317
|
+
cancelPromise: null
|
|
1168
1318
|
};
|
|
1169
1319
|
binding.subagentControls.set(id, control);
|
|
1170
1320
|
}
|
|
1171
1321
|
return control;
|
|
1172
1322
|
}
|
|
1173
|
-
|
|
1174
1323
|
resetSubagentControls(binding) {
|
|
1175
1324
|
binding.subagentControls.clear();
|
|
1176
1325
|
for (const sessionId of binding.subagentStates.keys()) {
|
|
1177
1326
|
const control = this.ensureSubagentControl(binding, sessionId);
|
|
1178
|
-
const parentTool = binding.sessionState?.entries.find(entry => (
|
|
1179
|
-
entry?.type === 'tool'
|
|
1180
|
-
&& String(entry?._meta?.subagent_session_info?.session_id || '') === sessionId
|
|
1181
|
-
));
|
|
1327
|
+
const parentTool = binding.sessionState?.entries.find((entry) => entry?.type === "tool" && String(entry?._meta?.subagent_session_info?.session_id || "") === sessionId);
|
|
1182
1328
|
if (parentTool) {
|
|
1183
1329
|
this.updateSubagentControlFromParent(binding, parentTool);
|
|
1184
1330
|
} else {
|
|
1185
|
-
|
|
1186
|
-
// from the previous adapter process is still active.
|
|
1187
|
-
control.phase = 'completed';
|
|
1331
|
+
control.phase = "completed";
|
|
1188
1332
|
}
|
|
1189
1333
|
}
|
|
1190
1334
|
}
|
|
1191
|
-
|
|
1192
1335
|
activeSubagentSessionIds(binding) {
|
|
1193
|
-
return [...binding.subagentStates.keys()].filter(sessionId => {
|
|
1336
|
+
return [...binding.subagentStates.keys()].filter((sessionId) => {
|
|
1194
1337
|
const control = this.ensureSubagentControl(binding, sessionId);
|
|
1195
|
-
if (control?.cancelPromise || [
|
|
1196
|
-
const hasInteraction = [...binding.pendingPermissions.values(), ...binding.pendingElicitations.values()]
|
|
1197
|
-
.some(request => String(request?.sessionId || '') === sessionId);
|
|
1338
|
+
if (control?.cancelPromise || ["active", "cancelling"].includes(control?.phase)) return true;
|
|
1339
|
+
const hasInteraction = [...binding.pendingPermissions.values(), ...binding.pendingElicitations.values()].some((request) => String(request?.sessionId || "") === sessionId);
|
|
1198
1340
|
if (hasInteraction) return true;
|
|
1199
|
-
const parentTool = binding.sessionState?.entries.find(entry => (
|
|
1200
|
-
|
|
1201
|
-
&& String(entry?._meta?.subagent_session_info?.session_id || '') === sessionId
|
|
1202
|
-
));
|
|
1203
|
-
return ['pending', 'in_progress', 'in-progress', 'running']
|
|
1204
|
-
.includes(String(parentTool?.status || '').toLowerCase());
|
|
1341
|
+
const parentTool = binding.sessionState?.entries.find((entry) => entry?.type === "tool" && String(entry?._meta?.subagent_session_info?.session_id || "") === sessionId);
|
|
1342
|
+
return ["pending", "in_progress", "in-progress", "running"].includes(String(parentTool?.status || "").toLowerCase());
|
|
1205
1343
|
});
|
|
1206
1344
|
}
|
|
1207
|
-
|
|
1208
1345
|
updateSubagentControlFromParent(binding, update) {
|
|
1209
|
-
const sessionId = String(update?._meta?.subagent_session_info?.session_id ||
|
|
1346
|
+
const sessionId = String(update?._meta?.subagent_session_info?.session_id || "");
|
|
1210
1347
|
if (!sessionId || !binding.subagentStates.has(sessionId)) return;
|
|
1211
1348
|
const control = this.ensureSubagentControl(binding, sessionId);
|
|
1212
|
-
const status = String(update?.status ||
|
|
1213
|
-
if ([
|
|
1214
|
-
control.phase =
|
|
1215
|
-
control.error =
|
|
1216
|
-
} else if ([
|
|
1217
|
-
control.phase =
|
|
1218
|
-
control.error =
|
|
1219
|
-
} else if ([
|
|
1220
|
-
control.phase =
|
|
1221
|
-
control.error = String(update?.title ||
|
|
1222
|
-
} else if (
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
) {
|
|
1226
|
-
control.phase = 'active';
|
|
1227
|
-
control.error = '';
|
|
1349
|
+
const status = String(update?.status || "").toLowerCase();
|
|
1350
|
+
if (["cancelled", "canceled"].includes(status)) {
|
|
1351
|
+
control.phase = "cancelled";
|
|
1352
|
+
control.error = "";
|
|
1353
|
+
} else if (["completed", "complete"].includes(status)) {
|
|
1354
|
+
control.phase = "completed";
|
|
1355
|
+
control.error = "";
|
|
1356
|
+
} else if (["failed", "error"].includes(status)) {
|
|
1357
|
+
control.phase = "error";
|
|
1358
|
+
control.error = String(update?.title || "Subagent failed");
|
|
1359
|
+
} else if (["pending", "in_progress", "in-progress", "running"].includes(status) && !["cancelling", "cancelled", "completed", "error"].includes(control.phase)) {
|
|
1360
|
+
control.phase = "active";
|
|
1361
|
+
control.error = "";
|
|
1228
1362
|
}
|
|
1229
1363
|
}
|
|
1230
|
-
|
|
1231
1364
|
beginSessionMutation(binding, action) {
|
|
1232
1365
|
this.requireOpenBinding(binding);
|
|
1233
|
-
if (
|
|
1234
|
-
binding.sessionMutation
|
|
1235
|
-
|| binding.configMutationTail
|
|
1236
|
-
|| binding.patchDecisionInFlight.size > 0
|
|
1237
|
-
|| binding.activeTurn
|
|
1238
|
-
|| !['idle', 'error'].includes(binding.state)
|
|
1239
|
-
) {
|
|
1366
|
+
if (binding.sessionMutation || binding.configMutationTail || binding.patchDecisionInFlight.size > 0 || binding.activeTurn || !["idle", "error"].includes(binding.state)) {
|
|
1240
1367
|
throw new Error(`ACP Agent is not ready for ${action} (${binding.state})`);
|
|
1241
1368
|
}
|
|
1242
1369
|
const mutation = { action };
|
|
1243
1370
|
binding.sessionMutation = mutation;
|
|
1244
1371
|
return mutation;
|
|
1245
1372
|
}
|
|
1246
|
-
|
|
1247
1373
|
endSessionMutation(binding, mutation) {
|
|
1248
1374
|
if (binding.sessionMutation === mutation) binding.sessionMutation = null;
|
|
1249
1375
|
}
|
|
1250
|
-
|
|
1251
1376
|
requireConfigMutationReady(binding) {
|
|
1252
1377
|
this.requireOpenBinding(binding);
|
|
1253
|
-
if (
|
|
1254
|
-
binding.sessionMutation
|
|
1255
|
-
|| binding.patchDecisionInFlight.size > 0
|
|
1256
|
-
|| binding.activeTurn
|
|
1257
|
-
|| !['idle', 'error'].includes(binding.state)
|
|
1258
|
-
) {
|
|
1378
|
+
if (binding.sessionMutation || binding.patchDecisionInFlight.size > 0 || binding.activeTurn || !["idle", "error"].includes(binding.state)) {
|
|
1259
1379
|
throw new Error(`ACP Agent is not ready for configuration changes (${binding.state})`);
|
|
1260
1380
|
}
|
|
1261
1381
|
}
|
|
1262
|
-
|
|
1263
1382
|
checkpointIdentity(binding, sessionId = binding?.sessionId) {
|
|
1264
1383
|
if (!binding || !sessionId) return null;
|
|
1265
1384
|
return {
|
|
1266
1385
|
provider: binding.provider,
|
|
1267
|
-
providerHomeId: binding.providerHomeId ||
|
|
1386
|
+
providerHomeId: binding.providerHomeId || "default",
|
|
1268
1387
|
sessionId,
|
|
1269
|
-
cwd: binding.cwd
|
|
1388
|
+
cwd: binding.cwd
|
|
1270
1389
|
};
|
|
1271
1390
|
}
|
|
1272
|
-
|
|
1273
1391
|
scheduleCheckpoint(binding, _options = {}) {
|
|
1274
1392
|
if (!this.isOpenBinding(binding)) return;
|
|
1275
1393
|
const identity = this.checkpointIdentity(binding);
|
|
1276
1394
|
if (!this.checkpointStore || !identity || !binding.sessionState) return;
|
|
1277
|
-
// ACP currently has no conditional resume or provider-owned revision
|
|
1278
|
-
// token. A timestamp/list check has a TOCTOU window, so runtime snapshots
|
|
1279
|
-
// remain dirty and are used only as revision/reset fences. Full history
|
|
1280
|
-
// load is the authoritative recovery path until such a proof is available.
|
|
1281
1395
|
this.checkpointStore.schedule(identity, this.bindingCheckpoint(binding), { exact: false });
|
|
1282
1396
|
}
|
|
1283
|
-
|
|
1284
1397
|
async markCheckpointDirty(binding) {
|
|
1285
1398
|
this.requireOpenBinding(binding);
|
|
1286
1399
|
const identity = this.checkpointIdentity(binding);
|
|
@@ -1288,7 +1401,6 @@ class AcpRuntime extends EventEmitter {
|
|
|
1288
1401
|
await this.checkpointStore.markDirty(identity);
|
|
1289
1402
|
this.requireOpenBinding(binding);
|
|
1290
1403
|
}
|
|
1291
|
-
|
|
1292
1404
|
async writeCheckpoint(binding, _options = {}) {
|
|
1293
1405
|
this.requireOpenBinding(binding);
|
|
1294
1406
|
const identity = this.checkpointIdentity(binding);
|
|
@@ -1296,7 +1408,6 @@ class AcpRuntime extends EventEmitter {
|
|
|
1296
1408
|
await this.checkpointStore.write(identity, this.bindingCheckpoint(binding), { exact: false });
|
|
1297
1409
|
this.requireOpenBinding(binding);
|
|
1298
1410
|
}
|
|
1299
|
-
|
|
1300
1411
|
bindingCheckpoint(binding) {
|
|
1301
1412
|
return {
|
|
1302
1413
|
exportCheckpoint: () => ({
|
|
@@ -1305,125 +1416,129 @@ class AcpRuntime extends EventEmitter {
|
|
|
1305
1416
|
sessionState: binding.sessionState?.exportCheckpoint() || null,
|
|
1306
1417
|
subagentStates: [...binding.subagentStates.entries()].map(([sessionId, state]) => ({
|
|
1307
1418
|
sessionId,
|
|
1308
|
-
state: state.exportCheckpoint()
|
|
1419
|
+
state: state.exportCheckpoint()
|
|
1309
1420
|
})),
|
|
1310
1421
|
patchDecisions: [...binding.patchDecisions.entries()],
|
|
1311
|
-
|
|
1312
|
-
|
|
1422
|
+
deferredConfigChanges: [...binding.deferredConfigChanges.entries()].map(([configId, change]) => [
|
|
1423
|
+
configId,
|
|
1424
|
+
clone(change.value)
|
|
1425
|
+
]),
|
|
1426
|
+
deferredModeId: binding.deferredModeId,
|
|
1427
|
+
providerProof: binding.checkpointProof ? clone(binding.checkpointProof) : null
|
|
1428
|
+
})
|
|
1313
1429
|
};
|
|
1314
1430
|
}
|
|
1315
|
-
|
|
1316
1431
|
restoreBindingCheckpoint(binding, checkpoint, options = {}) {
|
|
1317
1432
|
if (!checkpoint) return null;
|
|
1318
|
-
const mainCheckpoint = checkpoint.version === 2
|
|
1319
|
-
|
|
1320
|
-
: checkpoint;
|
|
1321
|
-
const sessionState = AcpSessionState.fromCheckpoint(mainCheckpoint, {
|
|
1433
|
+
const mainCheckpoint = checkpoint.version === 2 ? checkpoint.sessionState : checkpoint;
|
|
1434
|
+
const sessionState = import_acp_session_state.AcpSessionState.fromCheckpoint(mainCheckpoint, {
|
|
1322
1435
|
provider: binding.provider,
|
|
1323
1436
|
sessionId: options.sessionId || binding.sessionId,
|
|
1324
1437
|
cwd: binding.cwd,
|
|
1325
|
-
maxUpdates: this.maxUpdates
|
|
1438
|
+
maxUpdates: this.maxUpdates
|
|
1326
1439
|
});
|
|
1327
1440
|
if (!sessionState) return null;
|
|
1328
|
-
const subagentStates = new Map();
|
|
1441
|
+
const subagentStates = /* @__PURE__ */ new Map();
|
|
1329
1442
|
if (checkpoint.version === 2 && Array.isArray(checkpoint.subagentStates)) {
|
|
1330
1443
|
for (const item of checkpoint.subagentStates.slice(0, 32)) {
|
|
1331
|
-
const sessionId = String(item?.sessionId ||
|
|
1444
|
+
const sessionId = String(item?.sessionId || "");
|
|
1332
1445
|
if (!sessionId) continue;
|
|
1333
|
-
const state = AcpSessionState.fromCheckpoint(item?.state, {
|
|
1446
|
+
const state = import_acp_session_state.AcpSessionState.fromCheckpoint(item?.state, {
|
|
1334
1447
|
provider: binding.provider,
|
|
1335
1448
|
sessionId,
|
|
1336
1449
|
cwd: binding.cwd,
|
|
1337
|
-
maxUpdates: this.maxUpdates
|
|
1450
|
+
maxUpdates: this.maxUpdates
|
|
1338
1451
|
});
|
|
1339
1452
|
if (state) subagentStates.set(sessionId, state);
|
|
1340
1453
|
}
|
|
1341
1454
|
}
|
|
1342
|
-
const patchDecisions = new Map();
|
|
1455
|
+
const patchDecisions = /* @__PURE__ */ new Map();
|
|
1343
1456
|
if (checkpoint.version === 2 && Array.isArray(checkpoint.patchDecisions)) {
|
|
1344
1457
|
for (const item of checkpoint.patchDecisions) {
|
|
1345
1458
|
if (!Array.isArray(item) || item.length !== 2) continue;
|
|
1346
|
-
const key = String(item[0] ||
|
|
1347
|
-
const decision = String(item[1] ||
|
|
1348
|
-
if (key && [
|
|
1459
|
+
const key = String(item[0] || "");
|
|
1460
|
+
const decision = String(item[1] || "");
|
|
1461
|
+
if (key && ["kept", "reverted"].includes(decision)) patchDecisions.set(key, decision);
|
|
1349
1462
|
}
|
|
1350
1463
|
}
|
|
1464
|
+
const deferredConfigChanges = /* @__PURE__ */ new Map();
|
|
1465
|
+
if (checkpoint.version === 2 && Array.isArray(checkpoint.deferredConfigChanges)) {
|
|
1466
|
+
for (const item of checkpoint.deferredConfigChanges.slice(0, 32)) {
|
|
1467
|
+
if (!Array.isArray(item) || item.length !== 2) continue;
|
|
1468
|
+
const configId = String(item[0] || "");
|
|
1469
|
+
const value = item[1];
|
|
1470
|
+
if (!configId || !["string", "boolean", "number"].includes(typeof value)) continue;
|
|
1471
|
+
deferredConfigChanges.set(configId, { configId, value });
|
|
1472
|
+
}
|
|
1473
|
+
}
|
|
1474
|
+
const deferredModeId = checkpoint.version === 2 && typeof checkpoint.deferredModeId === "string" ? checkpoint.deferredModeId : "";
|
|
1351
1475
|
return {
|
|
1352
1476
|
sessionState,
|
|
1353
1477
|
subagentStates,
|
|
1354
1478
|
patchDecisions,
|
|
1479
|
+
deferredConfigChanges,
|
|
1480
|
+
deferredModeId,
|
|
1355
1481
|
providerProof: checkpoint.version === 2 ? clone(checkpoint.providerProof) : null,
|
|
1356
|
-
complete: checkpoint.version === 2 && checkpoint.complete === true
|
|
1482
|
+
complete: checkpoint.version === 2 && checkpoint.complete === true
|
|
1357
1483
|
};
|
|
1358
1484
|
}
|
|
1359
|
-
|
|
1360
1485
|
restorePatchDecisions(binding, decisions) {
|
|
1361
1486
|
if (!(decisions instanceof Map) || !binding.sessionState) return;
|
|
1362
1487
|
for (const [key, decision] of decisions) {
|
|
1363
|
-
const separator = key.indexOf(
|
|
1488
|
+
const separator = key.indexOf("\n");
|
|
1364
1489
|
if (separator <= 0) continue;
|
|
1365
1490
|
const toolCallId = key.slice(0, separator);
|
|
1366
1491
|
const requestedPath = key.slice(separator + 1);
|
|
1367
1492
|
const entry = binding.sessionState.toolEntries.get(toolCallId);
|
|
1368
1493
|
if (!entry || binding.sessionState.isInternalEntry(entry)) continue;
|
|
1369
1494
|
try {
|
|
1370
|
-
patchBlock(entry, binding.cwd, requestedPath);
|
|
1495
|
+
(0, import_patch_decisions.patchBlock)(entry, binding.cwd, requestedPath);
|
|
1371
1496
|
} catch {
|
|
1372
1497
|
continue;
|
|
1373
1498
|
}
|
|
1374
1499
|
binding.patchDecisions.set(key, decision);
|
|
1375
|
-
entry._meta = { ...
|
|
1500
|
+
entry._meta = { ...entry._meta || {} };
|
|
1376
1501
|
entry._meta.farming_patch_decisions = {
|
|
1377
|
-
...
|
|
1378
|
-
[requestedPath]: decision
|
|
1502
|
+
...entry._meta.farming_patch_decisions || {},
|
|
1503
|
+
[requestedPath]: decision
|
|
1379
1504
|
};
|
|
1380
1505
|
}
|
|
1381
1506
|
}
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
!capabilities?.sessionCapabilities?.list
|
|
1387
|
-
|| !proof
|
|
1388
|
-
|| typeof proof.token !== 'string'
|
|
1389
|
-
|| !proof.token
|
|
1390
|
-
) return false;
|
|
1391
|
-
let cursor = '';
|
|
1507
|
+
async checkpointMatchesProviderSession(connection, capabilities = {}, request, saved) {
|
|
1508
|
+
const proof = recordValue(recordValue(saved?.state).providerProof);
|
|
1509
|
+
if (!capabilities?.sessionCapabilities?.list || !proof || typeof proof.token !== "string" || !proof.token) return false;
|
|
1510
|
+
let cursor = "";
|
|
1392
1511
|
try {
|
|
1393
1512
|
for (let page = 0; page < 10; page += 1) {
|
|
1394
1513
|
const response = await withTimeout(connection.listSessions({
|
|
1395
1514
|
cwd: request.cwd,
|
|
1396
|
-
...
|
|
1397
|
-
}), this.requestTimeoutMs,
|
|
1398
|
-
const session = (response?.sessions || []).find(item => item?.sessionId === request.sessionId);
|
|
1515
|
+
...cursor ? { cursor } : {}
|
|
1516
|
+
}), this.requestTimeoutMs, "ACP session/list checkpoint validation");
|
|
1517
|
+
const session = (response?.sessions || []).find((item) => item?.sessionId === request.sessionId);
|
|
1399
1518
|
if (session) {
|
|
1400
|
-
const sessionCwd = path.resolve(String(session.cwd ||
|
|
1401
|
-
const token = String(session?._meta?.checkpointRevision || session?.checkpointRevision ||
|
|
1402
|
-
return sessionCwd === path.resolve(request.cwd)
|
|
1403
|
-
&& token === proof.token
|
|
1404
|
-
&& path.resolve(String(proof.cwd || '')) === path.resolve(request.cwd);
|
|
1519
|
+
const sessionCwd = path.resolve(String(session.cwd || ""));
|
|
1520
|
+
const token = String(session?._meta?.checkpointRevision || session?.checkpointRevision || "");
|
|
1521
|
+
return sessionCwd === path.resolve(request.cwd) && token === proof.token && path.resolve(String(proof.cwd || "")) === path.resolve(request.cwd);
|
|
1405
1522
|
}
|
|
1406
|
-
cursor = String(response?.nextCursor ||
|
|
1523
|
+
cursor = String(response?.nextCursor || "");
|
|
1407
1524
|
if (!cursor) return false;
|
|
1408
1525
|
}
|
|
1409
1526
|
} catch (error) {
|
|
1410
|
-
console.warn(
|
|
1527
|
+
console.warn("Failed to validate ACP checkpoint against provider session metadata:", asErrorLike(error).message || error);
|
|
1411
1528
|
}
|
|
1412
1529
|
return false;
|
|
1413
1530
|
}
|
|
1414
|
-
|
|
1415
1531
|
async officialConnection(handlers, child) {
|
|
1416
1532
|
const sdk = await loadAcpSdk();
|
|
1417
1533
|
const stream = sdk.ndJsonStream(Writable.toWeb(child.stdin), Readable.toWeb(child.stdout));
|
|
1418
1534
|
return new sdk.ClientSideConnection(() => handlers, stream);
|
|
1419
1535
|
}
|
|
1420
|
-
|
|
1421
1536
|
async waitForHistoryReplay(binding) {
|
|
1422
1537
|
const startedAt = Date.now();
|
|
1423
1538
|
let lastUpdateCount = binding.sessionState?.updates.length || 0;
|
|
1424
1539
|
let lastChangedAt = startedAt;
|
|
1425
1540
|
while (Date.now() - startedAt < this.historyReplayMaxWaitMs) {
|
|
1426
|
-
await new Promise(resolve => setTimeout(resolve, 50));
|
|
1541
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
1427
1542
|
if (!this.isOpenBinding(binding)) return;
|
|
1428
1543
|
const now = Date.now();
|
|
1429
1544
|
const updateCount = binding.sessionState?.updates.length || 0;
|
|
@@ -1431,125 +1546,115 @@ class AcpRuntime extends EventEmitter {
|
|
|
1431
1546
|
lastUpdateCount = updateCount;
|
|
1432
1547
|
lastChangedAt = now;
|
|
1433
1548
|
}
|
|
1434
|
-
if (
|
|
1435
|
-
now - startedAt >= this.historyReplayMinWaitMs
|
|
1436
|
-
&& now - lastChangedAt >= this.historyReplayQuietMs
|
|
1437
|
-
) return;
|
|
1549
|
+
if (now - startedAt >= this.historyReplayMinWaitMs && now - lastChangedAt >= this.historyReplayQuietMs) return;
|
|
1438
1550
|
}
|
|
1439
1551
|
}
|
|
1440
|
-
|
|
1441
1552
|
clientHandlers(binding) {
|
|
1442
|
-
const openClientRequest = handler => async request => {
|
|
1553
|
+
const openClientRequest = (handler) => async (request) => {
|
|
1443
1554
|
this.requireOpenBinding(binding);
|
|
1444
|
-
if (binding.state ===
|
|
1555
|
+
if (binding.state === "closed") throw new Error("ACP session is closed");
|
|
1445
1556
|
const response = await handler(request);
|
|
1446
1557
|
this.requireOpenBinding(binding);
|
|
1447
|
-
if (binding.state ===
|
|
1558
|
+
if (binding.state === "closed") throw new Error("ACP session is closed");
|
|
1448
1559
|
return response;
|
|
1449
1560
|
};
|
|
1450
1561
|
return {
|
|
1451
|
-
sessionUpdate: notification => {
|
|
1452
|
-
if (!this.isOpenBinding(binding) || binding.state ===
|
|
1453
|
-
const notificationSessionId = String(notification?.sessionId ||
|
|
1562
|
+
sessionUpdate: (notification) => {
|
|
1563
|
+
if (!this.isOpenBinding(binding) || binding.state === "closed") return;
|
|
1564
|
+
const notificationSessionId = String(notification?.sessionId || "");
|
|
1454
1565
|
const isPrimarySession = !binding.sessionId || !notificationSessionId || notificationSessionId === binding.sessionId;
|
|
1455
1566
|
let targetState = isPrimarySession ? binding.sessionState : binding.subagentStates.get(notificationSessionId);
|
|
1456
1567
|
if (!targetState && notificationSessionId && binding.subagentStates.size < 32) {
|
|
1457
|
-
targetState = new AcpSessionState({
|
|
1568
|
+
targetState = new import_acp_session_state.AcpSessionState({
|
|
1458
1569
|
provider: binding.provider,
|
|
1459
1570
|
sessionId: notificationSessionId,
|
|
1460
1571
|
cwd: binding.cwd,
|
|
1461
|
-
maxUpdates: this.maxUpdates
|
|
1572
|
+
maxUpdates: this.maxUpdates
|
|
1462
1573
|
});
|
|
1463
1574
|
binding.subagentStates.set(notificationSessionId, targetState);
|
|
1464
1575
|
}
|
|
1465
1576
|
if (!isPrimarySession && targetState) {
|
|
1466
1577
|
this.ensureSubagentControl(binding, notificationSessionId);
|
|
1467
|
-
const parentTool = binding.sessionState?.entries.find(entry => (
|
|
1468
|
-
entry?.type === 'tool'
|
|
1469
|
-
&& String(entry?._meta?.subagent_session_info?.session_id || '') === notificationSessionId
|
|
1470
|
-
));
|
|
1578
|
+
const parentTool = binding.sessionState?.entries.find((entry) => entry?.type === "tool" && String(entry?._meta?.subagent_session_info?.session_id || "") === notificationSessionId);
|
|
1471
1579
|
if (parentTool) this.updateSubagentControlFromParent(binding, parentTool);
|
|
1472
1580
|
}
|
|
1473
1581
|
if (targetState?.apply(notification)) {
|
|
1474
|
-
const update = notification
|
|
1475
|
-
if (isPrimarySession) this.updateSubagentControlFromParent(binding, update);
|
|
1476
|
-
if (isPrimarySession && update?.sessionUpdate ===
|
|
1477
|
-
binding.modes = { ...binding.modes, currentModeId: String(update.currentModeId ||
|
|
1582
|
+
const update = notification.update;
|
|
1583
|
+
if (isPrimarySession && update) this.updateSubagentControlFromParent(binding, update);
|
|
1584
|
+
if (isPrimarySession && update?.sessionUpdate === "current_mode_update" && binding.modes) {
|
|
1585
|
+
binding.modes = { ...binding.modes, currentModeId: String(update.currentModeId || "") };
|
|
1478
1586
|
}
|
|
1479
|
-
if (isPrimarySession && update?.sessionUpdate ===
|
|
1587
|
+
if (isPrimarySession && update?.sessionUpdate === "config_option_update") {
|
|
1480
1588
|
binding.configOptions = JSON.parse(JSON.stringify(update.configOptions || []));
|
|
1481
1589
|
}
|
|
1482
1590
|
if (!isPrimarySession && binding.sessionState) {
|
|
1483
|
-
const parentTool = binding.sessionState.entries.find(entry => (
|
|
1484
|
-
entry?.type === 'tool'
|
|
1485
|
-
&& String(entry?._meta?.subagent_session_info?.session_id || '') === notificationSessionId
|
|
1486
|
-
));
|
|
1591
|
+
const parentTool = binding.sessionState.entries.find((entry) => entry?.type === "tool" && String(entry?._meta?.subagent_session_info?.session_id || "") === notificationSessionId);
|
|
1487
1592
|
if (parentTool) binding.sessionState.touchEntry(parentTool);
|
|
1488
1593
|
}
|
|
1489
|
-
binding.updatedAt = new Date().toISOString();
|
|
1594
|
+
binding.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1490
1595
|
if (isPrimarySession && !binding.historyReplayActive && !binding.activeTurn) {
|
|
1491
1596
|
this.scheduleCheckpoint(binding, { exact: true });
|
|
1492
1597
|
}
|
|
1493
|
-
// A loaded history can contain hundreds of ordered updates. Applying
|
|
1494
|
-
// them one by one is necessary, but broadcasting every replay step
|
|
1495
|
-
// makes clients repeatedly abort/refetch and remount rich content.
|
|
1496
|
-
// prepareAgent emits one complete snapshot after the replay settles.
|
|
1497
1598
|
if (!binding.historyReplayActive) this.emitSession(binding);
|
|
1498
1599
|
}
|
|
1499
1600
|
},
|
|
1500
|
-
requestPermission: request => this.requestPermission(binding, request),
|
|
1501
|
-
readTextFile: openClientRequest(request => this.clientFileSystem.readTextFile(binding, request)),
|
|
1502
|
-
writeTextFile: openClientRequest(request => this.clientFileSystem.writeTextFile(binding, request)),
|
|
1503
|
-
createTerminal: openClientRequest(request => this.clientTerminals.create(binding, request)),
|
|
1504
|
-
terminalOutput: openClientRequest(request => this.clientTerminals.output(binding, request)),
|
|
1505
|
-
waitForTerminalExit: openClientRequest(request => this.clientTerminals.waitForExit(binding, request)),
|
|
1506
|
-
killTerminal: openClientRequest(request => this.clientTerminals.kill(binding, request)),
|
|
1507
|
-
releaseTerminal: openClientRequest(request => this.clientTerminals.release(binding, request)),
|
|
1508
|
-
unstable_createElicitation: request => this.requestElicitation(binding, request),
|
|
1509
|
-
unstable_completeElicitation: notification => this.completeElicitation(binding, notification)
|
|
1601
|
+
requestPermission: (request) => this.requestPermission(binding, request),
|
|
1602
|
+
readTextFile: openClientRequest((request) => this.clientFileSystem.readTextFile(binding, request)),
|
|
1603
|
+
writeTextFile: openClientRequest((request) => this.clientFileSystem.writeTextFile(binding, request)),
|
|
1604
|
+
createTerminal: openClientRequest((request) => this.clientTerminals.create(binding, request)),
|
|
1605
|
+
terminalOutput: openClientRequest((request) => this.clientTerminals.output(binding, request)),
|
|
1606
|
+
waitForTerminalExit: openClientRequest((request) => this.clientTerminals.waitForExit(binding, request)),
|
|
1607
|
+
killTerminal: openClientRequest((request) => this.clientTerminals.kill(binding, request)),
|
|
1608
|
+
releaseTerminal: openClientRequest((request) => this.clientTerminals.release(binding, request)),
|
|
1609
|
+
unstable_createElicitation: (request) => this.requestElicitation(binding, request),
|
|
1610
|
+
unstable_completeElicitation: (notification) => this.completeElicitation(binding, notification)
|
|
1510
1611
|
};
|
|
1511
1612
|
}
|
|
1512
|
-
|
|
1513
1613
|
requestPermission(binding, request) {
|
|
1514
|
-
if (!this.isOpenBinding(binding) || binding.state ===
|
|
1515
|
-
return { outcome: { outcome:
|
|
1516
|
-
}
|
|
1517
|
-
const requestSessionId = String(request?.sessionId ||
|
|
1518
|
-
if (
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
) {
|
|
1525
|
-
return
|
|
1614
|
+
if (!this.isOpenBinding(binding) || binding.state === "closed") {
|
|
1615
|
+
return { outcome: { outcome: "cancelled" } };
|
|
1616
|
+
}
|
|
1617
|
+
const requestSessionId = String(request?.sessionId || "");
|
|
1618
|
+
if (!requestSessionId || requestSessionId !== binding.sessionId && !binding.subagentStates.has(requestSessionId)) {
|
|
1619
|
+
return { outcome: { outcome: "cancelled" } };
|
|
1620
|
+
}
|
|
1621
|
+
const browserTarget = farmingBrowserApprovalTarget(binding, request);
|
|
1622
|
+
const browserDecision = browserTarget ? this.browserPermissionDecisionFor(binding, browserTarget) : null;
|
|
1623
|
+
const browserAutomatic = browserDecision ? shouldAutoApproveFarmingBrowser(binding, browserDecision) : null;
|
|
1624
|
+
if (browserAutomatic?.approve) {
|
|
1625
|
+
return automaticBrowserPermissionResponse(request);
|
|
1526
1626
|
}
|
|
1527
1627
|
const automatic = autoPermissionResponse(request, binding.approvalMode);
|
|
1528
1628
|
if (automatic) return automatic;
|
|
1529
1629
|
const requestId = `acp-permission-${crypto.randomUUID()}`;
|
|
1530
1630
|
binding.interactionOrigins.set(requestId, binding.state);
|
|
1531
|
-
binding.state =
|
|
1631
|
+
binding.state = "waiting-for-permission";
|
|
1532
1632
|
const pending = { ...JSON.parse(JSON.stringify(request)), requestId };
|
|
1533
|
-
pending.origin = String(request?.sessionId ||
|
|
1534
|
-
|
|
1535
|
-
: 'agent';
|
|
1536
|
-
pending.securityWarnings = permissionSecurityWarnings(pending);
|
|
1633
|
+
pending.origin = String(request?.sessionId || "") && String(request.sessionId) !== binding.sessionId ? "subagent" : "agent";
|
|
1634
|
+
pending.securityWarnings = (0, import_permission_security.permissionSecurityWarnings)(pending);
|
|
1537
1635
|
binding.pendingPermissions.set(requestId, pending);
|
|
1636
|
+
if (browserAutomatic?.scopeKey) {
|
|
1637
|
+
binding.farmingBrowserApprovalScopes.set(requestId, browserAutomatic.scopeKey);
|
|
1638
|
+
}
|
|
1538
1639
|
this.emitRuntime(binding);
|
|
1539
|
-
return new Promise(resolve => binding.permissionResolvers.set(requestId, resolve));
|
|
1640
|
+
return new Promise((resolve) => binding.permissionResolvers.set(requestId, resolve));
|
|
1540
1641
|
}
|
|
1541
|
-
|
|
1542
1642
|
respondPermission(agentId, requestId, optionId, cancelled = false) {
|
|
1543
1643
|
const binding = this.requireBinding(agentId);
|
|
1544
|
-
const pending = binding.pendingPermissions.get(String(requestId ||
|
|
1545
|
-
const resolve = binding.permissionResolvers.get(String(requestId ||
|
|
1546
|
-
if (!pending || pending.requestId !== requestId || !resolve) throw new Error(
|
|
1547
|
-
let response = { outcome: { outcome:
|
|
1644
|
+
const pending = binding.pendingPermissions.get(String(requestId || ""));
|
|
1645
|
+
const resolve = binding.permissionResolvers.get(String(requestId || ""));
|
|
1646
|
+
if (!pending || pending.requestId !== requestId || !resolve) throw new Error("ACP permission request is no longer pending");
|
|
1647
|
+
let response = { outcome: { outcome: "cancelled" } };
|
|
1548
1648
|
if (!cancelled) {
|
|
1549
|
-
const option = pending.options.find(item => item.optionId === optionId);
|
|
1550
|
-
if (!option) throw new Error(
|
|
1649
|
+
const option = pending.options.find((item) => item.optionId === optionId);
|
|
1650
|
+
if (!option) throw new Error("Unknown ACP permission option");
|
|
1551
1651
|
response = selectedPermission(option);
|
|
1652
|
+
const scopeKey = binding.farmingBrowserApprovalScopes.get(requestId);
|
|
1653
|
+
if (option.kind === "allow_always" && scopeKey) {
|
|
1654
|
+
binding.farmingBrowserApprovedSites.add(scopeKey);
|
|
1655
|
+
}
|
|
1552
1656
|
}
|
|
1657
|
+
binding.farmingBrowserApprovalScopes.delete(requestId);
|
|
1553
1658
|
binding.permissionResolvers.delete(requestId);
|
|
1554
1659
|
binding.pendingPermissions.delete(requestId);
|
|
1555
1660
|
const origin = binding.interactionOrigins.get(requestId);
|
|
@@ -1559,53 +1664,63 @@ class AcpRuntime extends EventEmitter {
|
|
|
1559
1664
|
this.emitRuntime(binding);
|
|
1560
1665
|
return response;
|
|
1561
1666
|
}
|
|
1562
|
-
|
|
1563
1667
|
requestElicitation(binding, request) {
|
|
1564
|
-
if (!this.isOpenBinding(binding) || binding.state ===
|
|
1565
|
-
const hasSessionScope = typeof request?.sessionId ===
|
|
1566
|
-
const requestSessionId = hasSessionScope ? String(request.sessionId) :
|
|
1668
|
+
if (!this.isOpenBinding(binding) || binding.state === "closed") return { action: "cancel" };
|
|
1669
|
+
const hasSessionScope = typeof request?.sessionId === "string" && request.sessionId.length > 0;
|
|
1670
|
+
const requestSessionId = hasSessionScope ? String(request.sessionId) : "";
|
|
1567
1671
|
const isPrimarySession = hasSessionScope && requestSessionId === binding.sessionId;
|
|
1568
1672
|
const isRequestScoped = !hasSessionScope;
|
|
1569
1673
|
if (!isRequestScoped && !isPrimarySession && !binding.subagentStates.has(requestSessionId)) {
|
|
1570
|
-
throw new Error(
|
|
1674
|
+
throw new Error("ACP elicitation does not match an active session");
|
|
1571
1675
|
}
|
|
1572
|
-
if (![
|
|
1573
|
-
return { action:
|
|
1676
|
+
if (!["form", "url"].includes(String(request?.mode || ""))) {
|
|
1677
|
+
return { action: "cancel" };
|
|
1574
1678
|
}
|
|
1679
|
+
const browserTarget = farmingBrowserApprovalTarget(binding, request);
|
|
1680
|
+
const browserDecision = browserTarget ? this.browserPermissionDecisionFor(binding, browserTarget) : null;
|
|
1681
|
+
const browserAutomatic = browserDecision ? shouldAutoApproveFarmingBrowser(binding, browserDecision) : null;
|
|
1682
|
+
if (browserAutomatic?.approve || browserTarget && binding.approvalMode === "full") {
|
|
1683
|
+
return automaticBrowserElicitationResponse(request);
|
|
1684
|
+
}
|
|
1685
|
+
if (browserTarget && binding.approvalMode === "ask") return { action: "cancel" };
|
|
1575
1686
|
const requestId = `acp-elicitation-${crypto.randomUUID()}`;
|
|
1576
1687
|
const cloned = JSON.parse(JSON.stringify(request));
|
|
1577
|
-
const protocolRequestId = Object.prototype.hasOwnProperty.call(cloned,
|
|
1578
|
-
? cloned.requestId
|
|
1579
|
-
: undefined;
|
|
1688
|
+
const protocolRequestId = Object.prototype.hasOwnProperty.call(cloned, "requestId") ? cloned.requestId : void 0;
|
|
1580
1689
|
delete cloned.requestId;
|
|
1581
1690
|
const pending = {
|
|
1582
1691
|
...cloned,
|
|
1583
|
-
...
|
|
1692
|
+
...protocolRequestId !== void 0 ? { protocolRequestId } : {},
|
|
1584
1693
|
requestId,
|
|
1585
|
-
origin: isRequestScoped ?
|
|
1694
|
+
origin: isRequestScoped ? "request" : isPrimarySession ? "agent" : "subagent"
|
|
1586
1695
|
};
|
|
1587
1696
|
binding.interactionOrigins.set(requestId, binding.state);
|
|
1588
1697
|
binding.pendingElicitations.set(requestId, pending);
|
|
1589
|
-
|
|
1698
|
+
if (browserAutomatic?.scopeKey) {
|
|
1699
|
+
binding.farmingBrowserApprovalScopes.set(requestId, browserAutomatic.scopeKey);
|
|
1700
|
+
}
|
|
1701
|
+
binding.state = "waiting-for-input";
|
|
1590
1702
|
this.emitRuntime(binding);
|
|
1591
|
-
return new Promise(resolve => binding.elicitationResolvers.set(requestId, resolve));
|
|
1703
|
+
return new Promise((resolve) => binding.elicitationResolvers.set(requestId, resolve));
|
|
1592
1704
|
}
|
|
1593
|
-
|
|
1594
1705
|
respondElicitation(agentId, requestId, action, content) {
|
|
1595
1706
|
const binding = this.requireBinding(agentId);
|
|
1596
|
-
const id = String(requestId ||
|
|
1707
|
+
const id = String(requestId || "");
|
|
1597
1708
|
const pending = binding.pendingElicitations.get(id);
|
|
1598
1709
|
const resolve = binding.elicitationResolvers.get(id);
|
|
1599
|
-
if (!pending || !resolve) throw new Error(
|
|
1600
|
-
const normalizedAction = String(action ||
|
|
1601
|
-
if (![
|
|
1602
|
-
const response = normalizedAction ===
|
|
1603
|
-
|
|
1604
|
-
|
|
1710
|
+
if (!pending || !resolve) throw new Error("ACP input request is no longer pending");
|
|
1711
|
+
const normalizedAction = String(action || "cancel");
|
|
1712
|
+
if (!["accept", "decline", "cancel"].includes(normalizedAction)) throw new Error("Unknown ACP input action");
|
|
1713
|
+
const response = normalizedAction === "accept" ? { action: "accept", ...pending.mode === "form" ? { content: validateElicitationContent(pending, content) } : {} } : { action: normalizedAction };
|
|
1714
|
+
const scopeKey = binding.farmingBrowserApprovalScopes.get(id);
|
|
1715
|
+
const persist = String(recordValue(response.content).persist || "");
|
|
1716
|
+
if (normalizedAction === "accept" && scopeKey && ["session", "always"].includes(persist)) {
|
|
1717
|
+
binding.farmingBrowserApprovedSites.add(scopeKey);
|
|
1718
|
+
}
|
|
1719
|
+
binding.farmingBrowserApprovalScopes.delete(id);
|
|
1605
1720
|
binding.elicitationResolvers.delete(id);
|
|
1606
1721
|
binding.pendingElicitations.delete(id);
|
|
1607
|
-
if (pending.mode ===
|
|
1608
|
-
binding.activeElicitations.set(String(pending.elicitationId || id), { ...pending, status:
|
|
1722
|
+
if (pending.mode === "url" && normalizedAction === "accept") {
|
|
1723
|
+
binding.activeElicitations.set(String(pending.elicitationId || id), { ...pending, status: "accepted" });
|
|
1609
1724
|
}
|
|
1610
1725
|
const origin = binding.interactionOrigins.get(id);
|
|
1611
1726
|
binding.interactionOrigins.delete(id);
|
|
@@ -1614,28 +1729,39 @@ class AcpRuntime extends EventEmitter {
|
|
|
1614
1729
|
this.emitRuntime(binding);
|
|
1615
1730
|
return response;
|
|
1616
1731
|
}
|
|
1617
|
-
|
|
1732
|
+
browserPermissionDecisionFor(binding, target) {
|
|
1733
|
+
try {
|
|
1734
|
+
return this.browserPermissionDecision(binding.agentId, target.tool, target.arguments) || defaultBrowserPermissionDecision(target);
|
|
1735
|
+
} catch {
|
|
1736
|
+
return { requiresApproval: true, scopeKey: "", site: "" };
|
|
1737
|
+
}
|
|
1738
|
+
}
|
|
1618
1739
|
completeElicitation(binding, notification) {
|
|
1619
|
-
if (!this.isOpenBinding(binding) || binding.state ===
|
|
1620
|
-
const elicitationId = String(notification?.elicitationId ||
|
|
1740
|
+
if (!this.isOpenBinding(binding) || binding.state === "closed") return;
|
|
1741
|
+
const elicitationId = String(notification?.elicitationId || "");
|
|
1621
1742
|
if (elicitationId) binding.activeElicitations.delete(elicitationId);
|
|
1622
1743
|
this.emitRuntime(binding);
|
|
1623
1744
|
}
|
|
1624
|
-
|
|
1625
1745
|
async submitMessage(agentId, prompt, options = {}) {
|
|
1626
1746
|
const binding = this.requireBinding(agentId);
|
|
1747
|
+
const delivery = options.delivery === "prompt" || options.delivery === "steer" ? options.delivery : "auto";
|
|
1748
|
+
if (delivery === "steer") {
|
|
1749
|
+
this.requireOpenBinding(binding);
|
|
1750
|
+
const turn = binding.activeTurn;
|
|
1751
|
+
if (turn?.phase !== "running" || turn.providerSettled === true || binding.provider !== "codex" || binding.supportsSteer !== true) {
|
|
1752
|
+
throw new Error("No active Codex turn to steer");
|
|
1753
|
+
}
|
|
1754
|
+
const result = await this.steer(agentId, prompt);
|
|
1755
|
+
options.onSubmitted?.();
|
|
1756
|
+
return { steered: true, ...result };
|
|
1757
|
+
}
|
|
1627
1758
|
while (true) {
|
|
1628
1759
|
this.requireOpenBinding(binding);
|
|
1629
1760
|
const turn = binding.activeTurn;
|
|
1630
|
-
if (turn?.phase ===
|
|
1761
|
+
if (turn?.phase === "blocked") {
|
|
1631
1762
|
throw new Error(`ACP Agent is not ready (${binding.state})`);
|
|
1632
1763
|
}
|
|
1633
|
-
if (
|
|
1634
|
-
turn?.phase === 'running'
|
|
1635
|
-
&& turn.providerSettled !== true
|
|
1636
|
-
&& binding.provider === 'codex'
|
|
1637
|
-
&& binding.supportsSteer === true
|
|
1638
|
-
) {
|
|
1764
|
+
if (delivery === "auto" && turn?.phase === "running" && turn.providerSettled !== true && binding.provider === "codex" && binding.supportsSteer === true) {
|
|
1639
1765
|
try {
|
|
1640
1766
|
const result = await this.steer(agentId, prompt);
|
|
1641
1767
|
options.onSubmitted?.();
|
|
@@ -1653,49 +1779,43 @@ class AcpRuntime extends EventEmitter {
|
|
|
1653
1779
|
return this.prompt(agentId, prompt, options);
|
|
1654
1780
|
}
|
|
1655
1781
|
}
|
|
1656
|
-
|
|
1657
1782
|
async prompt(agentId, prompt, options = {}) {
|
|
1658
1783
|
const binding = this.requireBinding(agentId);
|
|
1659
|
-
if (
|
|
1660
|
-
binding.exited
|
|
1661
|
-
|| binding.sessionMutation
|
|
1662
|
-
|| binding.activeTurn
|
|
1663
|
-
|| !['idle', 'error'].includes(binding.state)
|
|
1664
|
-
) {
|
|
1784
|
+
if (binding.exited || binding.sessionMutation || binding.activeTurn || !["idle", "error"].includes(binding.state)) {
|
|
1665
1785
|
throw new Error(`ACP Agent is not ready (${binding.state})`);
|
|
1666
1786
|
}
|
|
1667
|
-
const rawContent = Array.isArray(prompt) ? prompt : [{ type:
|
|
1787
|
+
const rawContent = Array.isArray(prompt) ? prompt : [{ type: "text", text: String(prompt || "") }];
|
|
1668
1788
|
const content = promptContentForCapabilities(
|
|
1669
1789
|
rawContent,
|
|
1670
|
-
binding.initializeResponse?.agentCapabilities || {}
|
|
1790
|
+
binding.initializeResponse?.agentCapabilities || {}
|
|
1671
1791
|
);
|
|
1672
1792
|
const turn = this.beginTurn(binding);
|
|
1673
1793
|
try {
|
|
1674
1794
|
const configMutation = binding.configMutationTail;
|
|
1675
|
-
const patchDecisions = [...binding.patchDecisionInFlight.values()].map(item => item.promise);
|
|
1795
|
+
const patchDecisions = [...binding.patchDecisionInFlight.values()].map((item) => item.promise);
|
|
1676
1796
|
await Promise.allSettled([
|
|
1677
|
-
...
|
|
1678
|
-
...patchDecisions
|
|
1797
|
+
...configMutation ? [configMutation] : [],
|
|
1798
|
+
...patchDecisions
|
|
1679
1799
|
]);
|
|
1680
|
-
if (!this.isCurrentTurn(binding, turn) || turn.phase !==
|
|
1681
|
-
throw new Error(
|
|
1800
|
+
if (!this.isCurrentTurn(binding, turn) || turn.phase !== "admitting") {
|
|
1801
|
+
throw new Error("ACP prompt was cancelled before submission");
|
|
1682
1802
|
}
|
|
1683
1803
|
await this.markCheckpointDirty(binding);
|
|
1684
|
-
if (!this.isCurrentTurn(binding, turn) || turn.phase !==
|
|
1685
|
-
throw new Error(
|
|
1804
|
+
if (!this.isCurrentTurn(binding, turn) || turn.phase !== "admitting") {
|
|
1805
|
+
throw new Error("ACP prompt was cancelled before submission");
|
|
1686
1806
|
}
|
|
1687
1807
|
} catch (error) {
|
|
1688
1808
|
if (this.isCurrentTurn(binding, turn)) {
|
|
1689
1809
|
binding.state = turn.previousState;
|
|
1690
|
-
this.finishTurn(binding, turn, { status:
|
|
1810
|
+
this.finishTurn(binding, turn, { status: "admission-failed" });
|
|
1691
1811
|
}
|
|
1692
1812
|
throw error;
|
|
1693
1813
|
}
|
|
1694
1814
|
binding.sessionState.beginPrompt(content);
|
|
1695
|
-
turn.phase =
|
|
1696
|
-
binding.state =
|
|
1697
|
-
binding.error =
|
|
1698
|
-
binding.stopReason =
|
|
1815
|
+
turn.phase = "running";
|
|
1816
|
+
binding.state = "working";
|
|
1817
|
+
binding.error = "";
|
|
1818
|
+
binding.stopReason = "";
|
|
1699
1819
|
this.emitRuntime(binding);
|
|
1700
1820
|
this.emitSession(binding);
|
|
1701
1821
|
let response;
|
|
@@ -1704,7 +1824,6 @@ class AcpRuntime extends EventEmitter {
|
|
|
1704
1824
|
try {
|
|
1705
1825
|
options.onSubmitted?.();
|
|
1706
1826
|
} catch {
|
|
1707
|
-
// Submission is already owned by the provider; admission callbacks cannot roll it back.
|
|
1708
1827
|
}
|
|
1709
1828
|
response = await responsePromise;
|
|
1710
1829
|
turn.providerSettled = true;
|
|
@@ -1714,16 +1833,13 @@ class AcpRuntime extends EventEmitter {
|
|
|
1714
1833
|
if (!this.isCurrentTurn(binding, turn)) throw runtimeError;
|
|
1715
1834
|
await this.enqueueTurnControl(turn, async () => {
|
|
1716
1835
|
if (!this.isCurrentTurn(binding, turn)) return;
|
|
1717
|
-
binding.stopReason =
|
|
1718
|
-
// JSON-RPC implementations commonly move the actionable provider text
|
|
1719
|
-
// into error.data.details. Classify the normalized message so the
|
|
1720
|
-
// ordered transcript and runtime snapshot cannot disagree.
|
|
1836
|
+
binding.stopReason = "error";
|
|
1721
1837
|
binding.sessionState.recordError(runtimeError.message, acpErrorKind(runtimeError));
|
|
1722
|
-
binding.sessionState.completePrompt(
|
|
1723
|
-
binding.state =
|
|
1838
|
+
binding.sessionState.completePrompt();
|
|
1839
|
+
binding.state = "error";
|
|
1724
1840
|
binding.error = runtimeError.message;
|
|
1725
|
-
binding.updatedAt = new Date().toISOString();
|
|
1726
|
-
this.finishTurn(binding, turn, { status:
|
|
1841
|
+
binding.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1842
|
+
this.finishTurn(binding, turn, { status: "error", error: runtimeError });
|
|
1727
1843
|
this.scheduleCheckpoint(binding, { exact: true });
|
|
1728
1844
|
this.emitSession(binding);
|
|
1729
1845
|
this.emitRuntime(binding);
|
|
@@ -1732,104 +1848,101 @@ class AcpRuntime extends EventEmitter {
|
|
|
1732
1848
|
}
|
|
1733
1849
|
return this.enqueueTurnControl(turn, async () => {
|
|
1734
1850
|
this.requireCurrentTurn(binding, turn);
|
|
1735
|
-
binding.stopReason = String(response?.stopReason ||
|
|
1736
|
-
binding.sessionState.completePrompt(
|
|
1737
|
-
binding.state =
|
|
1738
|
-
binding.error =
|
|
1739
|
-
binding.updatedAt = new Date().toISOString();
|
|
1740
|
-
this.finishTurn(binding, turn, { status:
|
|
1851
|
+
binding.stopReason = String(response?.stopReason || "");
|
|
1852
|
+
binding.sessionState.completePrompt();
|
|
1853
|
+
binding.state = "idle";
|
|
1854
|
+
binding.error = "";
|
|
1855
|
+
binding.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1856
|
+
this.finishTurn(binding, turn, { status: "completed", stopReason: binding.stopReason });
|
|
1741
1857
|
this.scheduleCheckpoint(binding, { exact: true });
|
|
1742
1858
|
this.emitSession(binding);
|
|
1743
1859
|
this.emitRuntime(binding);
|
|
1744
1860
|
return { sessionId: binding.sessionId, stopReason: binding.stopReason };
|
|
1745
1861
|
});
|
|
1746
1862
|
}
|
|
1747
|
-
|
|
1748
1863
|
canSteer(agentId) {
|
|
1749
1864
|
const binding = this.bindings.get(agentId);
|
|
1750
|
-
return binding?.provider ===
|
|
1751
|
-
&& binding.supportsSteer === true
|
|
1752
|
-
&& binding.activeTurn?.phase === 'running'
|
|
1753
|
-
&& binding.activeTurn?.providerSettled !== true
|
|
1754
|
-
&& Boolean(binding.sessionId)
|
|
1755
|
-
&& Boolean(binding.connection);
|
|
1865
|
+
return binding?.provider === "codex" && binding.supportsSteer === true && binding.activeTurn?.phase === "running" && binding.activeTurn?.providerSettled !== true && Boolean(binding.sessionId) && Boolean(binding.connection);
|
|
1756
1866
|
}
|
|
1757
|
-
|
|
1758
1867
|
async steer(agentId, prompt) {
|
|
1759
1868
|
const binding = this.requireBinding(agentId);
|
|
1760
|
-
if (binding.provider !==
|
|
1869
|
+
if (binding.provider !== "codex" || binding.supportsSteer !== true) {
|
|
1761
1870
|
throw new Error(`${binding.provider} ACP Agent does not support steer`);
|
|
1762
1871
|
}
|
|
1763
|
-
const rawContent = Array.isArray(prompt) ? prompt : [{ type:
|
|
1872
|
+
const rawContent = Array.isArray(prompt) ? prompt : [{ type: "text", text: String(prompt || "") }];
|
|
1764
1873
|
const content = promptContentForCapabilities(
|
|
1765
1874
|
rawContent,
|
|
1766
|
-
binding.initializeResponse?.agentCapabilities || {}
|
|
1875
|
+
binding.initializeResponse?.agentCapabilities || {}
|
|
1767
1876
|
);
|
|
1768
1877
|
const turn = binding.activeTurn;
|
|
1769
|
-
this.requireCurrentTurn(binding, turn, [
|
|
1878
|
+
this.requireCurrentTurn(binding, turn, ["running"]);
|
|
1770
1879
|
const clientMessageId = `farming-steer-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
|
|
1771
1880
|
const operation = async () => {
|
|
1772
|
-
this.requireCurrentTurn(binding, turn, [
|
|
1773
|
-
if (turn.providerSettled) throw new Error(
|
|
1881
|
+
this.requireCurrentTurn(binding, turn, ["running"]);
|
|
1882
|
+
if (turn.providerSettled) throw new Error("No active Codex turn to steer");
|
|
1774
1883
|
await this.markCheckpointDirty(binding);
|
|
1775
|
-
this.requireCurrentTurn(binding, turn, [
|
|
1776
|
-
if (turn.providerSettled) throw new Error(
|
|
1884
|
+
this.requireCurrentTurn(binding, turn, ["running"]);
|
|
1885
|
+
if (turn.providerSettled) throw new Error("No active Codex turn to steer");
|
|
1886
|
+
const insertionIndex = binding.sessionState.entries.length;
|
|
1777
1887
|
const response = await withTimeout(
|
|
1778
1888
|
binding.connection.request(CODEX_STEER_METHOD, {
|
|
1779
1889
|
sessionId: binding.sessionId,
|
|
1780
1890
|
prompt: content,
|
|
1781
|
-
clientMessageId
|
|
1891
|
+
clientMessageId
|
|
1782
1892
|
}),
|
|
1783
1893
|
this.requestTimeoutMs,
|
|
1784
|
-
|
|
1894
|
+
"Codex ACP steer"
|
|
1785
1895
|
);
|
|
1786
|
-
this.requireCurrentTurn(binding, turn, [
|
|
1896
|
+
this.requireCurrentTurn(binding, turn, ["running"]);
|
|
1897
|
+
const turnId = String(response?.turnId || "");
|
|
1898
|
+
if (binding.sessionState.recordAcceptedSteer(content, {
|
|
1899
|
+
messageId: clientMessageId,
|
|
1900
|
+
turnId,
|
|
1901
|
+
insertionIndex
|
|
1902
|
+
})) {
|
|
1903
|
+
this.emitSession(binding);
|
|
1904
|
+
}
|
|
1787
1905
|
return {
|
|
1788
1906
|
sessionId: binding.sessionId,
|
|
1789
|
-
turnId
|
|
1790
|
-
clientMessageId
|
|
1907
|
+
turnId,
|
|
1908
|
+
clientMessageId
|
|
1791
1909
|
};
|
|
1792
1910
|
};
|
|
1793
1911
|
return this.enqueueTurnControl(turn, operation);
|
|
1794
1912
|
}
|
|
1795
|
-
|
|
1796
1913
|
async cancel(agentId) {
|
|
1797
1914
|
const binding = this.requireBinding(agentId);
|
|
1798
1915
|
this.requireOpenBinding(binding);
|
|
1799
|
-
if (binding.state ===
|
|
1916
|
+
if (binding.state === "closed") throw new Error("ACP session is closed");
|
|
1800
1917
|
if (!binding.sessionId) return false;
|
|
1801
1918
|
const turn = binding.activeTurn;
|
|
1802
|
-
if (turn?.phase ===
|
|
1919
|
+
if (turn?.phase === "admitting") {
|
|
1803
1920
|
this.cancelAdmittingTurn(binding, turn);
|
|
1804
1921
|
return true;
|
|
1805
1922
|
}
|
|
1806
|
-
if (!turn && (
|
|
1807
|
-
binding.sessionMutation
|
|
1808
|
-
|| binding.configMutationTail
|
|
1809
|
-
|| binding.patchDecisionInFlight.size > 0
|
|
1810
|
-
)) {
|
|
1923
|
+
if (!turn && (binding.sessionMutation || binding.configMutationTail || binding.patchDecisionInFlight.size > 0)) {
|
|
1811
1924
|
throw new Error(`ACP Agent is not ready for cancellation (${binding.state})`);
|
|
1812
1925
|
}
|
|
1813
1926
|
if (turn?.cancelPromise) return turn.cancelPromise;
|
|
1814
|
-
|
|
1815
1927
|
const stateBeforeCancellation = binding.state;
|
|
1816
1928
|
let cancelAcknowledged = false;
|
|
1817
1929
|
const cancelOperation = async () => {
|
|
1818
1930
|
const activeSubagentSessionIds = this.activeSubagentSessionIds(binding);
|
|
1819
1931
|
if (turn) {
|
|
1820
|
-
this.requireCurrentTurn(binding, turn, [
|
|
1821
|
-
if (turn.providerSettled) throw new Error(
|
|
1822
|
-
turn.phase =
|
|
1932
|
+
this.requireCurrentTurn(binding, turn, ["running"]);
|
|
1933
|
+
if (turn.providerSettled) throw new Error("No active turn to cancel");
|
|
1934
|
+
turn.phase = "cancelling";
|
|
1823
1935
|
}
|
|
1824
|
-
binding.state =
|
|
1936
|
+
binding.state = "interrupting";
|
|
1825
1937
|
for (const resolve of binding.permissionResolvers.values()) {
|
|
1826
|
-
resolve({ outcome: { outcome:
|
|
1938
|
+
resolve({ outcome: { outcome: "cancelled" } });
|
|
1827
1939
|
}
|
|
1828
|
-
for (const resolve of binding.elicitationResolvers.values()) resolve({ action:
|
|
1940
|
+
for (const resolve of binding.elicitationResolvers.values()) resolve({ action: "cancel" });
|
|
1829
1941
|
binding.permissionResolvers.clear();
|
|
1830
1942
|
binding.pendingPermissions.clear();
|
|
1831
1943
|
binding.elicitationResolvers.clear();
|
|
1832
1944
|
binding.pendingElicitations.clear();
|
|
1945
|
+
binding.farmingBrowserApprovalScopes.clear();
|
|
1833
1946
|
binding.activeElicitations.clear();
|
|
1834
1947
|
binding.interactionOrigins.clear();
|
|
1835
1948
|
this.emitRuntime(binding);
|
|
@@ -1837,9 +1950,9 @@ class AcpRuntime extends EventEmitter {
|
|
|
1837
1950
|
withTimeout(
|
|
1838
1951
|
binding.connection.cancel({ sessionId: binding.sessionId }),
|
|
1839
1952
|
this.cancelTimeoutMs,
|
|
1840
|
-
|
|
1953
|
+
"ACP session/cancel"
|
|
1841
1954
|
),
|
|
1842
|
-
...activeSubagentSessionIds.map(sessionId => this.cancelSubagentControl(binding, sessionId))
|
|
1955
|
+
...activeSubagentSessionIds.map((sessionId) => this.cancelSubagentControl(binding, sessionId))
|
|
1843
1956
|
]);
|
|
1844
1957
|
this.requireOpenBinding(binding);
|
|
1845
1958
|
cancelAcknowledged = true;
|
|
@@ -1850,27 +1963,25 @@ class AcpRuntime extends EventEmitter {
|
|
|
1850
1963
|
if (turn) await this.enqueueTurnControl(turn, cancelOperation);
|
|
1851
1964
|
else await cancelOperation();
|
|
1852
1965
|
if (!turn) {
|
|
1853
|
-
if (binding.state ===
|
|
1854
|
-
binding.state = [
|
|
1855
|
-
? stateBeforeCancellation
|
|
1856
|
-
: 'idle';
|
|
1966
|
+
if (binding.state === "interrupting") {
|
|
1967
|
+
binding.state = ["idle", "error"].includes(stateBeforeCancellation) ? stateBeforeCancellation : "idle";
|
|
1857
1968
|
}
|
|
1858
|
-
binding.updatedAt = new Date().toISOString();
|
|
1969
|
+
binding.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1859
1970
|
this.emitRuntime(binding);
|
|
1860
1971
|
return true;
|
|
1861
1972
|
}
|
|
1862
|
-
await withTimeout(turn.completion, this.cancelTimeoutMs,
|
|
1973
|
+
await withTimeout(turn.completion, this.cancelTimeoutMs, "ACP turn cancellation");
|
|
1863
1974
|
return true;
|
|
1864
1975
|
} catch (error) {
|
|
1865
1976
|
const runtimeError = new Error(acpErrorMessage(error), { cause: error });
|
|
1866
1977
|
if (!this.isOpenBinding(binding)) throw runtimeError;
|
|
1867
1978
|
const markBlocked = () => {
|
|
1868
1979
|
if (turn && binding.activeTurn !== turn) return;
|
|
1869
|
-
if (turn) turn.phase =
|
|
1870
|
-
binding.state =
|
|
1980
|
+
if (turn) turn.phase = "blocked";
|
|
1981
|
+
binding.state = "error";
|
|
1871
1982
|
binding.error = runtimeError.message;
|
|
1872
|
-
binding.stopReason = turn && cancelAcknowledged ?
|
|
1873
|
-
binding.updatedAt = new Date().toISOString();
|
|
1983
|
+
binding.stopReason = turn && cancelAcknowledged ? "cancel_timeout" : "cancel_error";
|
|
1984
|
+
binding.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1874
1985
|
this.emitSession(binding);
|
|
1875
1986
|
this.emitRuntime(binding);
|
|
1876
1987
|
};
|
|
@@ -1886,31 +1997,32 @@ class AcpRuntime extends EventEmitter {
|
|
|
1886
1997
|
if (turn?.cancelPromise === cancellation) turn.cancelPromise = null;
|
|
1887
1998
|
}
|
|
1888
1999
|
}
|
|
1889
|
-
|
|
1890
2000
|
cancelSubagentControl(binding, targetSessionId) {
|
|
1891
2001
|
const control = this.ensureSubagentControl(binding, targetSessionId);
|
|
1892
2002
|
if (control.cancelPromise) return control.cancelPromise;
|
|
1893
|
-
if ([
|
|
2003
|
+
if (["cancelled", "completed"].includes(control.phase)) {
|
|
1894
2004
|
return Promise.resolve({ cancelled: true, sessionId: targetSessionId });
|
|
1895
2005
|
}
|
|
1896
|
-
control.phase =
|
|
1897
|
-
control.error =
|
|
2006
|
+
control.phase = "cancelling";
|
|
2007
|
+
control.error = "";
|
|
1898
2008
|
for (const [requestId, pending] of binding.pendingPermissions.entries()) {
|
|
1899
|
-
if (String(pending?.sessionId ||
|
|
1900
|
-
binding.permissionResolvers.get(requestId)?.({ outcome: { outcome:
|
|
2009
|
+
if (String(pending?.sessionId || "") !== targetSessionId) continue;
|
|
2010
|
+
binding.permissionResolvers.get(requestId)?.({ outcome: { outcome: "cancelled" } });
|
|
1901
2011
|
binding.permissionResolvers.delete(requestId);
|
|
1902
2012
|
binding.pendingPermissions.delete(requestId);
|
|
2013
|
+
binding.farmingBrowserApprovalScopes.delete(requestId);
|
|
1903
2014
|
binding.interactionOrigins.delete(requestId);
|
|
1904
2015
|
}
|
|
1905
2016
|
for (const [requestId, pending] of binding.pendingElicitations.entries()) {
|
|
1906
|
-
if (String(pending?.sessionId ||
|
|
1907
|
-
binding.elicitationResolvers.get(requestId)?.({ action:
|
|
2017
|
+
if (String(pending?.sessionId || "") !== targetSessionId) continue;
|
|
2018
|
+
binding.elicitationResolvers.get(requestId)?.({ action: "cancel" });
|
|
1908
2019
|
binding.elicitationResolvers.delete(requestId);
|
|
1909
2020
|
binding.pendingElicitations.delete(requestId);
|
|
2021
|
+
binding.farmingBrowserApprovalScopes.delete(requestId);
|
|
1910
2022
|
binding.interactionOrigins.delete(requestId);
|
|
1911
2023
|
}
|
|
1912
2024
|
for (const [elicitationId, active] of binding.activeElicitations.entries()) {
|
|
1913
|
-
if (String(active?.sessionId ||
|
|
2025
|
+
if (String(active?.sessionId || "") === targetSessionId) binding.activeElicitations.delete(elicitationId);
|
|
1914
2026
|
}
|
|
1915
2027
|
binding.state = interactiveRuntimeState(binding, binding.state);
|
|
1916
2028
|
this.emitRuntime(binding);
|
|
@@ -1919,23 +2031,23 @@ class AcpRuntime extends EventEmitter {
|
|
|
1919
2031
|
await withTimeout(
|
|
1920
2032
|
binding.connection.cancel({ sessionId: targetSessionId }),
|
|
1921
2033
|
this.cancelTimeoutMs,
|
|
1922
|
-
|
|
2034
|
+
"ACP subagent session/cancel"
|
|
1923
2035
|
);
|
|
1924
2036
|
this.requireOpenBinding(binding);
|
|
1925
2037
|
if (binding.subagentControls.get(targetSessionId) !== control) {
|
|
1926
|
-
throw new Error(
|
|
2038
|
+
throw new Error("ACP subagent control is no longer active");
|
|
1927
2039
|
}
|
|
1928
|
-
control.phase =
|
|
1929
|
-
control.error =
|
|
1930
|
-
binding.updatedAt = new Date().toISOString();
|
|
2040
|
+
control.phase = "cancelled";
|
|
2041
|
+
control.error = "";
|
|
2042
|
+
binding.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1931
2043
|
this.emitSession(binding);
|
|
1932
2044
|
this.emitRuntime(binding);
|
|
1933
2045
|
return { cancelled: true, sessionId: targetSessionId };
|
|
1934
2046
|
} catch (error) {
|
|
1935
2047
|
if (this.isOpenBinding(binding) && binding.subagentControls.get(targetSessionId) === control) {
|
|
1936
|
-
control.phase =
|
|
2048
|
+
control.phase = "error";
|
|
1937
2049
|
control.error = acpErrorMessage(error);
|
|
1938
|
-
binding.updatedAt = new Date().toISOString();
|
|
2050
|
+
binding.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1939
2051
|
this.emitSession(binding);
|
|
1940
2052
|
this.emitRuntime(binding);
|
|
1941
2053
|
}
|
|
@@ -1947,51 +2059,48 @@ class AcpRuntime extends EventEmitter {
|
|
|
1947
2059
|
if (binding.subagentControls.get(targetSessionId) === control && control.cancelPromise === cancellation) {
|
|
1948
2060
|
control.cancelPromise = null;
|
|
1949
2061
|
}
|
|
1950
|
-
}).catch(() => {
|
|
2062
|
+
}).catch(() => {
|
|
2063
|
+
});
|
|
1951
2064
|
return cancellation;
|
|
1952
2065
|
}
|
|
1953
|
-
|
|
1954
2066
|
async cancelSubagent(agentId, sessionId) {
|
|
1955
2067
|
const binding = this.requireBinding(agentId);
|
|
1956
2068
|
this.requireOpenBinding(binding);
|
|
1957
|
-
const targetSessionId = String(sessionId ||
|
|
2069
|
+
const targetSessionId = String(sessionId || "");
|
|
1958
2070
|
if (!targetSessionId || targetSessionId === binding.sessionId || !binding.subagentStates.has(targetSessionId)) {
|
|
1959
|
-
throw new Error(
|
|
2071
|
+
throw new Error("ACP subagent session not found");
|
|
1960
2072
|
}
|
|
1961
2073
|
return this.cancelSubagentControl(binding, targetSessionId);
|
|
1962
2074
|
}
|
|
1963
|
-
|
|
1964
2075
|
async listSessions(agentId, options = {}) {
|
|
1965
2076
|
const binding = this.requireBinding(agentId);
|
|
1966
2077
|
this.requireOpenBinding(binding);
|
|
1967
2078
|
const capabilities = binding.initializeResponse?.agentCapabilities?.sessionCapabilities;
|
|
1968
2079
|
if (!capabilities?.list) throw new Error(`${binding.provider} ACP Agent does not support session/list`);
|
|
1969
2080
|
const response = await withTimeout(binding.connection.listSessions({
|
|
1970
|
-
...
|
|
1971
|
-
...
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
...(options.cursor ? { cursor: String(options.cursor) } : {}),
|
|
1975
|
-
}), this.requestTimeoutMs, 'ACP session/list');
|
|
2081
|
+
...options.cwd ? { cwd: path.resolve(options.cwd) } : {},
|
|
2082
|
+
...Array.isArray(options.additionalDirectories) ? { additionalDirectories: acpSessionRequestOptions(options, options.cwd || binding.cwd).additionalDirectories } : {},
|
|
2083
|
+
...options.cursor ? { cursor: String(options.cursor) } : {}
|
|
2084
|
+
}), this.requestTimeoutMs, "ACP session/list");
|
|
1976
2085
|
this.requireOpenBinding(binding);
|
|
1977
2086
|
return response;
|
|
1978
2087
|
}
|
|
1979
|
-
|
|
1980
2088
|
getSessionRequestOptions(agentId) {
|
|
1981
2089
|
const binding = this.requireBinding(agentId);
|
|
1982
2090
|
return clone(binding.sessionRequestOptions);
|
|
1983
2091
|
}
|
|
1984
|
-
|
|
1985
2092
|
async authenticate(agentId, methodId) {
|
|
1986
2093
|
const binding = this.requireBinding(agentId);
|
|
1987
2094
|
this.requireOpenBinding(binding);
|
|
1988
|
-
if (binding.state ===
|
|
1989
|
-
const mutation = this.beginSessionMutation(binding,
|
|
1990
|
-
const method = binding.initializeResponse?.authMethods?.find(
|
|
2095
|
+
if (binding.state === "closed") throw new Error("ACP session is closed");
|
|
2096
|
+
const mutation = this.beginSessionMutation(binding, "authentication");
|
|
2097
|
+
const method = binding.initializeResponse?.authMethods?.find(
|
|
2098
|
+
(item) => item.id === methodId
|
|
2099
|
+
);
|
|
1991
2100
|
let terminalReservationTransferred = false;
|
|
1992
2101
|
try {
|
|
1993
|
-
if (!method) throw new Error(
|
|
1994
|
-
if (method.type ===
|
|
2102
|
+
if (!method) throw new Error("Unknown ACP authentication method");
|
|
2103
|
+
if (method.type === "terminal" || recordValue(method._meta).type === "terminal" || recordValue(method._meta)["terminal-auth"]) {
|
|
1995
2104
|
const result = await this.startTerminalAuthentication(binding, method, mutation);
|
|
1996
2105
|
terminalReservationTransferred = true;
|
|
1997
2106
|
return result;
|
|
@@ -1999,13 +2108,13 @@ class AcpRuntime extends EventEmitter {
|
|
|
1999
2108
|
await withTimeout(
|
|
2000
2109
|
binding.connection.authenticate({ methodId }),
|
|
2001
2110
|
this.requestTimeoutMs,
|
|
2002
|
-
|
|
2111
|
+
"ACP authenticate"
|
|
2003
2112
|
);
|
|
2004
2113
|
this.requireOpenBinding(binding);
|
|
2005
|
-
binding.error =
|
|
2006
|
-
binding.stopReason =
|
|
2007
|
-
binding.state = interactiveRuntimeState(binding,
|
|
2008
|
-
binding.updatedAt = new Date().toISOString();
|
|
2114
|
+
binding.error = "";
|
|
2115
|
+
binding.stopReason = "";
|
|
2116
|
+
binding.state = interactiveRuntimeState(binding, "idle");
|
|
2117
|
+
binding.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2009
2118
|
this.emitRuntime(binding);
|
|
2010
2119
|
this.emitSession(binding);
|
|
2011
2120
|
return { authenticated: true, methodId };
|
|
@@ -2013,20 +2122,19 @@ class AcpRuntime extends EventEmitter {
|
|
|
2013
2122
|
if (!terminalReservationTransferred) this.endSessionMutation(binding, mutation);
|
|
2014
2123
|
}
|
|
2015
2124
|
}
|
|
2016
|
-
|
|
2017
2125
|
async logout(agentId) {
|
|
2018
2126
|
const binding = this.requireBinding(agentId);
|
|
2019
2127
|
this.requireOpenBinding(binding);
|
|
2020
|
-
const mutation = this.beginSessionMutation(binding,
|
|
2128
|
+
const mutation = this.beginSessionMutation(binding, "logout");
|
|
2021
2129
|
try {
|
|
2022
|
-
if (binding.initializeResponse?.agentCapabilities?.auth
|
|
2130
|
+
if (recordValue(binding.initializeResponse?.agentCapabilities?.auth).logout == null) {
|
|
2023
2131
|
throw new Error(`${binding.provider} ACP Agent does not support logout`);
|
|
2024
2132
|
}
|
|
2025
|
-
await withTimeout(binding.connection.logout({}), this.requestTimeoutMs,
|
|
2133
|
+
await withTimeout(binding.connection.logout({}), this.requestTimeoutMs, "ACP logout");
|
|
2026
2134
|
this.requireOpenBinding(binding);
|
|
2027
|
-
binding.error =
|
|
2028
|
-
binding.stopReason =
|
|
2029
|
-
binding.updatedAt = new Date().toISOString();
|
|
2135
|
+
binding.error = "";
|
|
2136
|
+
binding.stopReason = "";
|
|
2137
|
+
binding.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2030
2138
|
this.emitRuntime(binding);
|
|
2031
2139
|
this.emitSession(binding);
|
|
2032
2140
|
return { loggedOut: true };
|
|
@@ -2034,28 +2142,31 @@ class AcpRuntime extends EventEmitter {
|
|
|
2034
2142
|
this.endSessionMutation(binding, mutation);
|
|
2035
2143
|
}
|
|
2036
2144
|
}
|
|
2037
|
-
|
|
2038
2145
|
terminalAuthenticationLaunch(binding, method) {
|
|
2039
|
-
const
|
|
2040
|
-
|
|
2146
|
+
const metadata = method._meta && typeof method._meta === "object" ? method._meta : {};
|
|
2147
|
+
const legacy = metadata["terminal-auth"];
|
|
2148
|
+
if (legacy && typeof legacy === "object" && legacy.command) {
|
|
2041
2149
|
return {
|
|
2042
2150
|
command: String(legacy.command),
|
|
2043
2151
|
args: Array.isArray(legacy.args) ? legacy.args.map(String) : [],
|
|
2044
|
-
env: legacy.env && typeof legacy.env ===
|
|
2152
|
+
env: legacy.env && typeof legacy.env === "object" ? legacy.env : {}
|
|
2045
2153
|
};
|
|
2046
2154
|
}
|
|
2047
|
-
|
|
2155
|
+
const terminalMethod = method.type === "terminal" ? method : metadata.type === "terminal" ? metadata : null;
|
|
2156
|
+
if (!terminalMethod) throw new Error("ACP authentication method is not terminal based");
|
|
2048
2157
|
return {
|
|
2049
2158
|
command: binding.launch.command,
|
|
2050
|
-
args: [
|
|
2051
|
-
|
|
2159
|
+
args: [
|
|
2160
|
+
...binding.launch.args,
|
|
2161
|
+
...Array.isArray(terminalMethod.args) ? terminalMethod.args.map(String) : []
|
|
2162
|
+
],
|
|
2163
|
+
env: terminalMethod.env && typeof terminalMethod.env === "object" ? terminalMethod.env : {}
|
|
2052
2164
|
};
|
|
2053
2165
|
}
|
|
2054
|
-
|
|
2055
2166
|
async startTerminalAuthentication(binding, method, mutation) {
|
|
2056
2167
|
this.requireOpenBinding(binding);
|
|
2057
|
-
if (binding.sessionMutation !== mutation) throw new Error(
|
|
2058
|
-
if (binding.authTerminal?.state ===
|
|
2168
|
+
if (binding.sessionMutation !== mutation) throw new Error("ACP authentication reservation is no longer active");
|
|
2169
|
+
if (binding.authTerminal?.state === "running") throw new Error("ACP terminal authentication is already running");
|
|
2059
2170
|
const launch = this.terminalAuthenticationLaunch(binding, method);
|
|
2060
2171
|
const created = await this.clientTerminals.create(binding, {
|
|
2061
2172
|
sessionId: binding.sessionId,
|
|
@@ -2063,7 +2174,7 @@ class AcpRuntime extends EventEmitter {
|
|
|
2063
2174
|
args: launch.args,
|
|
2064
2175
|
cwd: binding.cwd,
|
|
2065
2176
|
env: Object.entries(launch.env).map(([name, value]) => ({ name, value: String(value) })),
|
|
2066
|
-
outputByteLimit: 2 * 1024 * 1024
|
|
2177
|
+
outputByteLimit: 2 * 1024 * 1024
|
|
2067
2178
|
});
|
|
2068
2179
|
try {
|
|
2069
2180
|
this.requireOpenBinding(binding);
|
|
@@ -2071,69 +2182,67 @@ class AcpRuntime extends EventEmitter {
|
|
|
2071
2182
|
try {
|
|
2072
2183
|
this.clientTerminals.release(binding, {
|
|
2073
2184
|
sessionId: binding.sessionId,
|
|
2074
|
-
terminalId: created.terminalId
|
|
2185
|
+
terminalId: created.terminalId
|
|
2075
2186
|
});
|
|
2076
2187
|
} catch {
|
|
2077
|
-
// Binding teardown may already have removed the terminal.
|
|
2078
2188
|
}
|
|
2079
2189
|
throw error;
|
|
2080
2190
|
}
|
|
2081
2191
|
binding.authTerminal = {
|
|
2082
2192
|
terminalId: created.terminalId,
|
|
2083
|
-
methodId: String(method.id ||
|
|
2084
|
-
name: String(method.name ||
|
|
2085
|
-
state:
|
|
2086
|
-
error:
|
|
2193
|
+
methodId: String(method.id || ""),
|
|
2194
|
+
name: String(method.name || "Sign in"),
|
|
2195
|
+
state: "running",
|
|
2196
|
+
error: ""
|
|
2087
2197
|
};
|
|
2088
|
-
binding.updatedAt = new Date().toISOString();
|
|
2198
|
+
binding.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2089
2199
|
this.emitRuntime(binding);
|
|
2090
2200
|
this.emitSession(binding);
|
|
2091
2201
|
void Promise.resolve(this.clientTerminals.waitForExit(binding, {
|
|
2092
2202
|
sessionId: binding.sessionId,
|
|
2093
|
-
terminalId: created.terminalId
|
|
2094
|
-
})).then(async exit => {
|
|
2203
|
+
terminalId: created.terminalId
|
|
2204
|
+
})).then(async (exit) => {
|
|
2095
2205
|
if (!this.isOpenBinding(binding) || binding.sessionMutation !== mutation) return;
|
|
2096
2206
|
if (exit.exitCode !== 0) {
|
|
2097
|
-
binding.authTerminal.state =
|
|
2098
|
-
binding.authTerminal.error = `Sign-in command exited ${exit.exitCode ?? exit.signal ??
|
|
2207
|
+
binding.authTerminal.state = "failed";
|
|
2208
|
+
binding.authTerminal.error = `Sign-in command exited ${exit.exitCode ?? exit.signal ?? ""}`.trim();
|
|
2099
2209
|
this.endSessionMutation(binding, mutation);
|
|
2100
|
-
binding.updatedAt = new Date().toISOString();
|
|
2210
|
+
binding.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2101
2211
|
this.emitRuntime(binding);
|
|
2102
2212
|
this.emitSession(binding);
|
|
2103
2213
|
return;
|
|
2104
2214
|
}
|
|
2105
|
-
binding.authTerminal.state =
|
|
2106
|
-
binding.updatedAt = new Date().toISOString();
|
|
2215
|
+
binding.authTerminal.state = "completed";
|
|
2216
|
+
binding.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2107
2217
|
this.emitSession(binding);
|
|
2108
|
-
await this.restartAgentConnection(binding.agentId, mutation).catch(error => {
|
|
2218
|
+
await this.restartAgentConnection(binding.agentId, mutation).catch((error) => {
|
|
2109
2219
|
const current = this.bindings.get(binding.agentId);
|
|
2110
2220
|
if (current !== binding || current.exited) return;
|
|
2111
2221
|
this.endSessionMutation(current, mutation);
|
|
2112
|
-
current.state =
|
|
2222
|
+
current.state = "error";
|
|
2113
2223
|
current.error = acpErrorMessage(error);
|
|
2114
|
-
current.updatedAt = new Date().toISOString();
|
|
2224
|
+
current.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2115
2225
|
this.emitRuntime(current);
|
|
2116
2226
|
});
|
|
2117
|
-
}).catch(error => {
|
|
2227
|
+
}).catch((error) => {
|
|
2118
2228
|
if (!this.isOpenBinding(binding) || binding.sessionMutation !== mutation) return;
|
|
2119
|
-
binding.authTerminal.state =
|
|
2229
|
+
binding.authTerminal.state = "failed";
|
|
2120
2230
|
binding.authTerminal.error = acpErrorMessage(error);
|
|
2121
2231
|
this.endSessionMutation(binding, mutation);
|
|
2122
|
-
binding.updatedAt = new Date().toISOString();
|
|
2232
|
+
binding.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2123
2233
|
this.emitRuntime(binding);
|
|
2124
2234
|
this.emitSession(binding);
|
|
2125
2235
|
});
|
|
2126
2236
|
return { authenticated: false, methodId: method.id, terminalId: created.terminalId };
|
|
2127
2237
|
}
|
|
2128
|
-
|
|
2129
2238
|
async restartAgentConnection(agentId, mutation = null) {
|
|
2130
2239
|
const binding = this.requireBinding(agentId);
|
|
2131
2240
|
this.requireOpenBinding(binding);
|
|
2132
|
-
if (binding.activeTurn ||
|
|
2241
|
+
if (binding.activeTurn || binding.sessionMutation && binding.sessionMutation !== mutation) {
|
|
2133
2242
|
throw new Error(`ACP Agent is not ready for restart (${binding.state})`);
|
|
2134
2243
|
}
|
|
2135
2244
|
if (mutation && binding.sessionMutation !== mutation) {
|
|
2136
|
-
throw new Error(
|
|
2245
|
+
throw new Error("ACP restart reservation is no longer active");
|
|
2137
2246
|
}
|
|
2138
2247
|
const revisionBase = Number(binding.sessionState?.revision || 0);
|
|
2139
2248
|
const options = {
|
|
@@ -2143,9 +2252,9 @@ class AcpRuntime extends EventEmitter {
|
|
|
2143
2252
|
cwd: binding.cwd,
|
|
2144
2253
|
env: binding.env,
|
|
2145
2254
|
approvalMode: binding.approvalMode,
|
|
2146
|
-
...
|
|
2147
|
-
...
|
|
2148
|
-
...
|
|
2255
|
+
...binding.sessionId ? { sessionId: binding.sessionId } : {},
|
|
2256
|
+
...revisionBase > 0 ? { revisionBase } : {},
|
|
2257
|
+
...binding.sessionId ? { historyMode: "checkpoint" } : {}
|
|
2149
2258
|
};
|
|
2150
2259
|
if (binding.sessionState && !binding.activeTurn) {
|
|
2151
2260
|
await this.writeCheckpoint(binding, { exact: true });
|
|
@@ -2154,136 +2263,152 @@ class AcpRuntime extends EventEmitter {
|
|
|
2154
2263
|
await this.unregisterAgentAndWait(agentId, binding);
|
|
2155
2264
|
return this.prepareAgent(options);
|
|
2156
2265
|
}
|
|
2157
|
-
|
|
2158
2266
|
async forkSession(agentId, options = {}) {
|
|
2159
|
-
return this.runWithForkReservation(agentId, options, async binding => {
|
|
2267
|
+
return this.runWithForkReservation(agentId, options, async (binding) => {
|
|
2160
2268
|
const sessionOptions = acpSessionRequestOptions({
|
|
2161
2269
|
additionalDirectories: options.additionalDirectories ?? binding.sessionRequestOptions.additionalDirectories,
|
|
2162
|
-
mcpServers: options.mcpServers ?? binding.sessionRequestOptions.mcpServers
|
|
2270
|
+
mcpServers: options.mcpServers ?? binding.sessionRequestOptions.mcpServers
|
|
2163
2271
|
}, options.cwd || binding.cwd);
|
|
2164
2272
|
const response = await withTimeout(binding.connection.unstable_forkSession({
|
|
2165
2273
|
sessionId: options.sessionId || binding.sessionId,
|
|
2166
|
-
...sessionOptions
|
|
2167
|
-
}), this.sessionSetupTimeoutMs,
|
|
2274
|
+
...sessionOptions
|
|
2275
|
+
}), this.sessionSetupTimeoutMs, "ACP session/fork");
|
|
2168
2276
|
this.requireOpenBinding(binding);
|
|
2169
|
-
const sessionId = String(response?.sessionId ||
|
|
2170
|
-
if (!isSafeProviderSessionId(sessionId) || sessionId === binding.sessionId) {
|
|
2171
|
-
throw new Error(
|
|
2277
|
+
const sessionId = String(response?.sessionId || "").trim();
|
|
2278
|
+
if (!(0, import_provider_session_id.isSafeProviderSessionId)(sessionId) || sessionId === binding.sessionId) {
|
|
2279
|
+
throw new Error("ACP session/fork returned an invalid new session id");
|
|
2172
2280
|
}
|
|
2173
2281
|
return { ...response, sessionId };
|
|
2174
2282
|
});
|
|
2175
2283
|
}
|
|
2176
|
-
|
|
2177
|
-
async runWithForkReservation(agentId, options = {}, operation) {
|
|
2284
|
+
async runWithForkReservation(agentId, options, operation) {
|
|
2178
2285
|
const binding = this.requireBinding(agentId);
|
|
2179
|
-
const mutation = this.beginSessionMutation(binding,
|
|
2286
|
+
const mutation = this.beginSessionMutation(binding, "fork");
|
|
2180
2287
|
try {
|
|
2181
2288
|
const capabilities = binding.initializeResponse?.agentCapabilities?.sessionCapabilities;
|
|
2182
2289
|
if (!capabilities?.fork) throw new Error(`${binding.provider} ACP Agent does not support session/fork`);
|
|
2183
2290
|
if (options.requireLoad === true && !binding.initializeResponse?.agentCapabilities?.loadSession) {
|
|
2184
2291
|
throw new Error(`${binding.provider} ACP Agent cannot load a forked conversation`);
|
|
2185
2292
|
}
|
|
2186
|
-
if (
|
|
2187
|
-
|
|
2188
|
-
&& Number(binding.sessionState?.revision || 0) !== Math.floor(options.expectedRevision)
|
|
2189
|
-
) {
|
|
2190
|
-
throw new Error('ACP conversation changed before it could be forked. Try again from the latest answer.');
|
|
2293
|
+
if (typeof options.expectedRevision === "number" && Number.isFinite(options.expectedRevision) && Number(binding.sessionState?.revision || 0) !== Math.floor(options.expectedRevision)) {
|
|
2294
|
+
throw new Error("ACP conversation changed before it could be forked. Try again from the latest answer.");
|
|
2191
2295
|
}
|
|
2192
|
-
if (typeof operation !==
|
|
2296
|
+
if (typeof operation !== "function") throw new Error("ACP fork operation is required");
|
|
2193
2297
|
return await operation(binding);
|
|
2194
2298
|
} finally {
|
|
2195
2299
|
this.endSessionMutation(binding, mutation);
|
|
2196
2300
|
}
|
|
2197
2301
|
}
|
|
2198
|
-
|
|
2199
2302
|
async deleteSession(agentId, sessionId) {
|
|
2200
2303
|
const binding = this.requireBinding(agentId);
|
|
2201
|
-
const mutation = this.beginSessionMutation(binding,
|
|
2304
|
+
const mutation = this.beginSessionMutation(binding, "session deletion");
|
|
2202
2305
|
try {
|
|
2203
2306
|
const capabilities = binding.initializeResponse?.agentCapabilities?.sessionCapabilities;
|
|
2204
2307
|
if (!capabilities?.delete) throw new Error(`${binding.provider} ACP Agent does not support session/delete`);
|
|
2205
2308
|
await withTimeout(
|
|
2206
|
-
binding.connection.deleteSession({ sessionId: String(sessionId ||
|
|
2309
|
+
binding.connection.deleteSession({ sessionId: String(sessionId || "") }),
|
|
2207
2310
|
this.requestTimeoutMs,
|
|
2208
|
-
|
|
2311
|
+
"ACP session/delete"
|
|
2209
2312
|
);
|
|
2210
2313
|
this.requireOpenBinding(binding);
|
|
2211
|
-
return { deleted: true, sessionId: String(sessionId ||
|
|
2314
|
+
return { deleted: true, sessionId: String(sessionId || "") };
|
|
2212
2315
|
} finally {
|
|
2213
2316
|
this.endSessionMutation(binding, mutation);
|
|
2214
2317
|
}
|
|
2215
2318
|
}
|
|
2216
|
-
|
|
2217
2319
|
async closeSession(agentId) {
|
|
2218
2320
|
const binding = this.requireBinding(agentId);
|
|
2219
|
-
const mutation = this.beginSessionMutation(binding,
|
|
2321
|
+
const mutation = this.beginSessionMutation(binding, "session close");
|
|
2220
2322
|
try {
|
|
2221
2323
|
const capabilities = binding.initializeResponse?.agentCapabilities?.sessionCapabilities;
|
|
2222
2324
|
if (!capabilities?.close) throw new Error(`${binding.provider} ACP Agent does not support session/close`);
|
|
2223
2325
|
await withTimeout(
|
|
2224
2326
|
binding.connection.closeSession({ sessionId: binding.sessionId }),
|
|
2225
2327
|
this.requestTimeoutMs,
|
|
2226
|
-
|
|
2328
|
+
"ACP session/close"
|
|
2227
2329
|
);
|
|
2228
2330
|
this.requireOpenBinding(binding);
|
|
2229
|
-
binding.state =
|
|
2331
|
+
binding.state = "closed";
|
|
2230
2332
|
this.emitRuntime(binding);
|
|
2231
2333
|
return { closed: true, sessionId: binding.sessionId };
|
|
2232
2334
|
} finally {
|
|
2233
2335
|
this.endSessionMutation(binding, mutation);
|
|
2234
2336
|
}
|
|
2235
2337
|
}
|
|
2236
|
-
|
|
2237
2338
|
async setSessionMode(agentId, modeId) {
|
|
2238
2339
|
const binding = this.requireBinding(agentId);
|
|
2340
|
+
if (binding.activeTurn || binding.deferredConfigFlush) {
|
|
2341
|
+
const normalizedModeId = String(modeId || "");
|
|
2342
|
+
const advertisedModes = Array.isArray(binding.modes?.availableModes) ? binding.modes.availableModes : [];
|
|
2343
|
+
if (!normalizedModeId || !advertisedModes.some((mode) => String(recordValue(mode).id || "") === normalizedModeId)) {
|
|
2344
|
+
throw new Error(`ACP Agent did not advertise mode ${normalizedModeId}`);
|
|
2345
|
+
}
|
|
2346
|
+
const previousModeId = binding.deferredModeId;
|
|
2347
|
+
const previousGeneration = binding.deferredModeGeneration;
|
|
2348
|
+
const generation = binding.deferredModeGeneration + 1;
|
|
2349
|
+
binding.deferredModeGeneration = generation;
|
|
2350
|
+
binding.deferredModeId = normalizedModeId;
|
|
2351
|
+
try {
|
|
2352
|
+
await this.writeCheckpoint(binding);
|
|
2353
|
+
} catch (error) {
|
|
2354
|
+
if (binding.deferredModeGeneration === generation) {
|
|
2355
|
+
binding.deferredModeGeneration = previousGeneration;
|
|
2356
|
+
binding.deferredModeId = previousModeId;
|
|
2357
|
+
}
|
|
2358
|
+
throw error;
|
|
2359
|
+
}
|
|
2360
|
+
binding.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2361
|
+
this.emitRuntime(binding);
|
|
2362
|
+
return {
|
|
2363
|
+
sessionId: binding.sessionId,
|
|
2364
|
+
deferred: true,
|
|
2365
|
+
modeId: normalizedModeId,
|
|
2366
|
+
deferredModeId: normalizedModeId
|
|
2367
|
+
};
|
|
2368
|
+
}
|
|
2239
2369
|
this.requireConfigMutationReady(binding);
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2370
|
+
const result = await this.enqueueSessionConfigMutation(binding, () => this.setSessionModeNow(binding, modeId));
|
|
2371
|
+
if (this.clearDeferredSessionError(binding)) {
|
|
2372
|
+
binding.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2373
|
+
this.emitRuntime(binding);
|
|
2374
|
+
}
|
|
2375
|
+
return result;
|
|
2243
2376
|
}
|
|
2244
|
-
|
|
2245
2377
|
async setSessionModeNow(binding, modeId) {
|
|
2246
2378
|
this.requireOpenBinding(binding);
|
|
2247
2379
|
await withTimeout(
|
|
2248
|
-
binding.connection.setSessionMode({ sessionId: binding.sessionId, modeId: String(modeId ||
|
|
2380
|
+
binding.connection.setSessionMode({ sessionId: binding.sessionId, modeId: String(modeId || "") }),
|
|
2249
2381
|
this.requestTimeoutMs,
|
|
2250
|
-
|
|
2382
|
+
"ACP session/set_mode"
|
|
2251
2383
|
);
|
|
2252
2384
|
this.requireOpenBinding(binding);
|
|
2253
|
-
binding.sessionState.currentModeId = String(modeId ||
|
|
2385
|
+
binding.sessionState.currentModeId = String(modeId || "");
|
|
2254
2386
|
if (binding.modes) binding.modes = { ...binding.modes, currentModeId: binding.sessionState.currentModeId };
|
|
2255
2387
|
this.scheduleCheckpoint(binding, { exact: true });
|
|
2256
2388
|
this.emitSession(binding);
|
|
2257
2389
|
return { sessionId: binding.sessionId, modeId: binding.sessionState.currentModeId };
|
|
2258
2390
|
}
|
|
2259
|
-
|
|
2260
2391
|
async setSessionConfigOption(agentId, configId, value) {
|
|
2261
2392
|
const binding = this.requireBinding(agentId);
|
|
2393
|
+
if (binding.activeTurn || binding.deferredConfigFlush) {
|
|
2394
|
+
return this.deferSessionConfigChanges(binding, [{ configId: String(configId || ""), value }]);
|
|
2395
|
+
}
|
|
2262
2396
|
this.requireConfigMutationReady(binding);
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2397
|
+
const result = await this.enqueueSessionConfigMutation(binding, () => this.setSessionConfigOptionNow(binding, configId, value));
|
|
2398
|
+
if (this.clearDeferredSessionError(binding)) {
|
|
2399
|
+
binding.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2400
|
+
this.emitRuntime(binding);
|
|
2401
|
+
}
|
|
2402
|
+
return result;
|
|
2266
2403
|
}
|
|
2267
|
-
|
|
2268
2404
|
async setSessionConfigOptionNow(binding, configId, value) {
|
|
2269
2405
|
this.requireOpenBinding(binding);
|
|
2270
|
-
const option = binding.configOptions?.find(candidate => candidate.id === String(configId ||
|
|
2271
|
-
if (
|
|
2272
|
-
binding.provider === 'codex'
|
|
2273
|
-
&& option?.type === 'select'
|
|
2274
|
-
&& /(^|[\s_-])model([\s_-]|$)/i.test(`${option.id} ${option.name || ''} ${option.category || ''}`)
|
|
2275
|
-
) {
|
|
2276
|
-
// Let the adapter choose a supported fallback effort from its current
|
|
2277
|
-
// snapshot first. The refresh extension requires an explicit effort and
|
|
2278
|
-
// would otherwise reject a valid model change (for example ultra -> a
|
|
2279
|
-
// model that tops out at max).
|
|
2406
|
+
const option = binding.configOptions?.find((candidate) => candidate.id === String(configId || ""));
|
|
2407
|
+
if (binding.provider === "codex" && option?.type === "select" && /(^|[\s_-])model([\s_-]|$)/i.test(`${option.id} ${option.name || ""} ${option.category || ""}`)) {
|
|
2280
2408
|
await this.applySessionConfigOption(binding, configId, value, { emit: false });
|
|
2281
|
-
const reasoning = binding.configOptions?.find(candidate => (
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
));
|
|
2285
|
-
if (typeof reasoning?.currentValue === 'string' && reasoning.currentValue) {
|
|
2286
|
-
await this.refreshCodexSessionModel(binding, String(value ?? ''), reasoning.currentValue);
|
|
2409
|
+
const reasoning = binding.configOptions?.find((candidate) => candidate.type === "select" && /(reasoning|thought)/i.test(`${candidate.id} ${candidate.name || ""} ${candidate.category || ""}`));
|
|
2410
|
+
if (typeof reasoning?.currentValue === "string" && reasoning.currentValue) {
|
|
2411
|
+
await this.refreshCodexSessionModel(binding, String(value ?? ""), reasoning.currentValue);
|
|
2287
2412
|
return this.applySessionConfigOption(binding, configId, value, { force: true });
|
|
2288
2413
|
}
|
|
2289
2414
|
this.emitSession(binding);
|
|
@@ -2291,27 +2416,68 @@ class AcpRuntime extends EventEmitter {
|
|
|
2291
2416
|
}
|
|
2292
2417
|
return this.applySessionConfigOption(binding, configId, value);
|
|
2293
2418
|
}
|
|
2294
|
-
|
|
2295
2419
|
async setSessionConfigOptions(agentId, changes) {
|
|
2296
2420
|
const binding = this.requireBinding(agentId);
|
|
2421
|
+
if (binding.activeTurn || binding.deferredConfigFlush) {
|
|
2422
|
+
return this.deferSessionConfigChanges(binding, changes);
|
|
2423
|
+
}
|
|
2297
2424
|
this.requireConfigMutationReady(binding);
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2425
|
+
const result = await this.enqueueSessionConfigMutation(binding, () => this.setSessionConfigOptionsNow(binding, changes));
|
|
2426
|
+
if (this.clearDeferredSessionError(binding)) {
|
|
2427
|
+
binding.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2428
|
+
this.emitRuntime(binding);
|
|
2429
|
+
}
|
|
2430
|
+
return result;
|
|
2431
|
+
}
|
|
2432
|
+
async deferSessionConfigChanges(binding, changes) {
|
|
2433
|
+
const normalized = Array.isArray(changes) ? changes.filter((change) => change && typeof change.configId === "string" && change.configId) : [];
|
|
2434
|
+
if (normalized.length === 0) throw new Error("ACP config options are required");
|
|
2435
|
+
for (const change of normalized) {
|
|
2436
|
+
const option = binding.configOptions.find((candidate) => candidate.id === change.configId);
|
|
2437
|
+
if (!option) throw new Error(`ACP Agent did not advertise config option ${change.configId}`);
|
|
2438
|
+
if (option.type === "boolean" && typeof change.value !== "boolean") {
|
|
2439
|
+
throw new Error(`ACP config option ${change.configId} requires a boolean value`);
|
|
2440
|
+
}
|
|
2441
|
+
if (option.type === "select" && typeof change.value !== "string") {
|
|
2442
|
+
throw new Error(`ACP config option ${change.configId} requires a string value`);
|
|
2443
|
+
}
|
|
2444
|
+
}
|
|
2445
|
+
const accepted = normalized.map((change) => ({ configId: change.configId, value: change.value }));
|
|
2446
|
+
const previous = /* @__PURE__ */ new Map();
|
|
2447
|
+
for (const change of accepted) {
|
|
2448
|
+
if (!previous.has(change.configId)) previous.set(change.configId, binding.deferredConfigChanges.get(change.configId));
|
|
2449
|
+
binding.deferredConfigChanges.set(change.configId, change);
|
|
2450
|
+
}
|
|
2451
|
+
try {
|
|
2452
|
+
await this.writeCheckpoint(binding);
|
|
2453
|
+
} catch (error) {
|
|
2454
|
+
for (const change of accepted) {
|
|
2455
|
+
if (binding.deferredConfigChanges.get(change.configId) !== change) continue;
|
|
2456
|
+
const previousChange = previous.get(change.configId);
|
|
2457
|
+
if (previousChange) binding.deferredConfigChanges.set(change.configId, previousChange);
|
|
2458
|
+
else binding.deferredConfigChanges.delete(change.configId);
|
|
2459
|
+
}
|
|
2460
|
+
throw error;
|
|
2461
|
+
}
|
|
2462
|
+
binding.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2463
|
+
this.emitRuntime(binding);
|
|
2464
|
+
return {
|
|
2465
|
+
sessionId: binding.sessionId,
|
|
2466
|
+
deferred: true,
|
|
2467
|
+
configOptions: binding.configOptions,
|
|
2468
|
+
deferredConfigOptions: [...binding.deferredConfigChanges.values()],
|
|
2469
|
+
deferredModeId: binding.deferredModeId
|
|
2470
|
+
};
|
|
2301
2471
|
}
|
|
2302
|
-
|
|
2303
2472
|
async setSessionConfigOptionsNow(binding, changes) {
|
|
2304
2473
|
this.requireOpenBinding(binding);
|
|
2305
|
-
const normalized = Array.isArray(changes)
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
if (normalized.length === 0) throw new Error('ACP config options are required');
|
|
2309
|
-
|
|
2310
|
-
const originalValues = new Map();
|
|
2474
|
+
const normalized = Array.isArray(changes) ? changes.filter((change) => change && typeof change.configId === "string" && Object.prototype.hasOwnProperty.call(change, "value")) : [];
|
|
2475
|
+
if (normalized.length === 0) throw new Error("ACP config options are required");
|
|
2476
|
+
const originalValues = /* @__PURE__ */ new Map();
|
|
2311
2477
|
for (const change of normalized) {
|
|
2312
2478
|
if (originalValues.has(change.configId)) continue;
|
|
2313
|
-
const option = binding.configOptions?.find(candidate => candidate.id === change.configId);
|
|
2314
|
-
if (option && Object.prototype.hasOwnProperty.call(option,
|
|
2479
|
+
const option = binding.configOptions?.find((candidate) => candidate.id === change.configId);
|
|
2480
|
+
if (option && Object.prototype.hasOwnProperty.call(option, "currentValue")) {
|
|
2315
2481
|
originalValues.set(change.configId, option.currentValue);
|
|
2316
2482
|
}
|
|
2317
2483
|
}
|
|
@@ -2320,7 +2486,7 @@ class AcpRuntime extends EventEmitter {
|
|
|
2320
2486
|
} catch (error) {
|
|
2321
2487
|
const rollbackErrors = [];
|
|
2322
2488
|
for (const [configId, value] of [...originalValues.entries()].reverse()) {
|
|
2323
|
-
const current = binding.configOptions?.find(candidate => candidate.id === configId);
|
|
2489
|
+
const current = binding.configOptions?.find((candidate) => candidate.id === configId);
|
|
2324
2490
|
if (current?.currentValue === value) continue;
|
|
2325
2491
|
try {
|
|
2326
2492
|
await this.setSessionConfigOptionNow(binding, configId, value);
|
|
@@ -2328,43 +2494,38 @@ class AcpRuntime extends EventEmitter {
|
|
|
2328
2494
|
rollbackErrors.push(rollbackError);
|
|
2329
2495
|
}
|
|
2330
2496
|
}
|
|
2331
|
-
if (rollbackErrors.length > 0 && error && typeof error ===
|
|
2332
|
-
Object.defineProperty(error,
|
|
2333
|
-
value: new AggregateError(rollbackErrors,
|
|
2497
|
+
if (rollbackErrors.length > 0 && error && typeof error === "object") {
|
|
2498
|
+
Object.defineProperty(error, "configRollbackError", {
|
|
2499
|
+
value: new AggregateError(rollbackErrors, "ACP config rollback failed"),
|
|
2334
2500
|
enumerable: false,
|
|
2335
|
-
configurable: true
|
|
2501
|
+
configurable: true
|
|
2336
2502
|
});
|
|
2337
2503
|
}
|
|
2338
2504
|
throw error;
|
|
2339
2505
|
}
|
|
2340
2506
|
}
|
|
2341
|
-
|
|
2342
2507
|
async applySessionConfigOptionsNow(binding, normalized) {
|
|
2343
|
-
const configById = new Map(
|
|
2344
|
-
|
|
2508
|
+
const configById = new Map(
|
|
2509
|
+
(binding.configOptions || []).map((option) => [option.id, option])
|
|
2510
|
+
);
|
|
2511
|
+
const modelChange = normalized.find((change) => {
|
|
2345
2512
|
const option = configById.get(change.configId);
|
|
2346
|
-
return option?.type ===
|
|
2347
|
-
&& /(^|[\s_-])model([\s_-]|$)/i.test(`${option.id} ${option.name || ''} ${option.category || ''}`);
|
|
2513
|
+
return option?.type === "select" && /(^|[\s_-])model([\s_-]|$)/i.test(`${option.id} ${option.name || ""} ${option.category || ""}`);
|
|
2348
2514
|
});
|
|
2349
|
-
const reasoningChange = normalized.find(change => {
|
|
2515
|
+
const reasoningChange = normalized.find((change) => {
|
|
2350
2516
|
const option = configById.get(change.configId);
|
|
2351
|
-
return option?.type ===
|
|
2352
|
-
&& /(reasoning|thought)/i.test(`${option.id} ${option.name || ''} ${option.category || ''}`);
|
|
2517
|
+
return option?.type === "select" && /(reasoning|thought)/i.test(`${option.id} ${option.name || ""} ${option.category || ""}`);
|
|
2353
2518
|
});
|
|
2354
|
-
|
|
2355
2519
|
let response;
|
|
2356
|
-
const handled = new Set();
|
|
2357
|
-
if (binding.provider ===
|
|
2520
|
+
const handled = /* @__PURE__ */ new Set();
|
|
2521
|
+
if (binding.provider === "codex" && modelChange && reasoningChange) {
|
|
2358
2522
|
await this.applySessionConfigOption(binding, modelChange.configId, modelChange.value, { emit: false });
|
|
2359
2523
|
await this.applySessionConfigOption(binding, reasoningChange.configId, reasoningChange.value, { emit: false });
|
|
2360
|
-
const currentReasoning = binding.configOptions?.find(candidate => (
|
|
2361
|
-
candidate.type === 'select'
|
|
2362
|
-
&& /(reasoning|thought)/i.test(`${candidate.id} ${candidate.name || ''} ${candidate.category || ''}`)
|
|
2363
|
-
));
|
|
2524
|
+
const currentReasoning = binding.configOptions?.find((candidate) => candidate.type === "select" && /(reasoning|thought)/i.test(`${candidate.id} ${candidate.name || ""} ${candidate.category || ""}`));
|
|
2364
2525
|
await this.refreshCodexSessionModel(
|
|
2365
2526
|
binding,
|
|
2366
|
-
String(modelChange.value ??
|
|
2367
|
-
String(currentReasoning?.currentValue || reasoningChange.value ||
|
|
2527
|
+
String(modelChange.value ?? ""),
|
|
2528
|
+
String(currentReasoning?.currentValue || reasoningChange.value || "")
|
|
2368
2529
|
);
|
|
2369
2530
|
response = await this.applySessionConfigOption(binding, modelChange.configId, modelChange.value, { force: true });
|
|
2370
2531
|
handled.add(modelChange);
|
|
@@ -2376,10 +2537,10 @@ class AcpRuntime extends EventEmitter {
|
|
|
2376
2537
|
}
|
|
2377
2538
|
return response;
|
|
2378
2539
|
}
|
|
2379
|
-
|
|
2380
2540
|
async enqueueSessionConfigMutation(binding, operation) {
|
|
2381
2541
|
const previous = binding.configMutationTail || Promise.resolve();
|
|
2382
|
-
const pending = previous.catch(() => {
|
|
2542
|
+
const pending = previous.catch(() => {
|
|
2543
|
+
}).then(operation);
|
|
2383
2544
|
binding.configMutationTail = pending;
|
|
2384
2545
|
try {
|
|
2385
2546
|
return await pending;
|
|
@@ -2387,41 +2548,37 @@ class AcpRuntime extends EventEmitter {
|
|
|
2387
2548
|
if (binding.configMutationTail === pending) binding.configMutationTail = null;
|
|
2388
2549
|
}
|
|
2389
2550
|
}
|
|
2390
|
-
|
|
2391
2551
|
async refreshCodexSessionModel(binding, model, effort) {
|
|
2392
2552
|
this.requireOpenBinding(binding);
|
|
2393
2553
|
await withTimeout(
|
|
2394
2554
|
binding.connection.request(CODEX_SET_SESSION_MODEL_METHOD, {
|
|
2395
2555
|
sessionId: binding.sessionId,
|
|
2396
|
-
modelId: `${model}[${effort}]
|
|
2556
|
+
modelId: `${model}[${effort}]`
|
|
2397
2557
|
}),
|
|
2398
2558
|
this.requestTimeoutMs,
|
|
2399
|
-
|
|
2559
|
+
"Codex ACP session/set_model capability refresh"
|
|
2400
2560
|
);
|
|
2401
2561
|
this.requireOpenBinding(binding);
|
|
2402
2562
|
}
|
|
2403
|
-
|
|
2404
2563
|
async applySessionConfigOption(binding, configId, value, options = {}) {
|
|
2405
2564
|
this.requireOpenBinding(binding);
|
|
2406
|
-
const normalizedConfigId = String(configId ||
|
|
2407
|
-
const currentOption = binding.configOptions?.find(candidate => candidate.id === normalizedConfigId);
|
|
2565
|
+
const normalizedConfigId = String(configId || "");
|
|
2566
|
+
const currentOption = binding.configOptions?.find((candidate) => candidate.id === normalizedConfigId);
|
|
2408
2567
|
if (options.force !== true && currentOption?.currentValue === value) {
|
|
2409
2568
|
if (options.emit !== false) this.emitSession(binding);
|
|
2410
2569
|
return { sessionId: binding.sessionId, configOptions: binding.configOptions };
|
|
2411
2570
|
}
|
|
2412
|
-
const request = typeof value ===
|
|
2413
|
-
? { sessionId: binding.sessionId, configId: normalizedConfigId, type: 'boolean', value }
|
|
2414
|
-
: { sessionId: binding.sessionId, configId: normalizedConfigId, value: String(value ?? '') };
|
|
2571
|
+
const request = typeof value === "boolean" ? { sessionId: binding.sessionId, configId: normalizedConfigId, type: "boolean", value } : { sessionId: binding.sessionId, configId: normalizedConfigId, value: String(value ?? "") };
|
|
2415
2572
|
let response;
|
|
2416
2573
|
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
2417
2574
|
response = await withTimeout(
|
|
2418
2575
|
binding.connection.setSessionConfigOption(request),
|
|
2419
2576
|
this.requestTimeoutMs,
|
|
2420
|
-
|
|
2577
|
+
"ACP session/set_config_option"
|
|
2421
2578
|
);
|
|
2422
2579
|
this.requireOpenBinding(binding);
|
|
2423
2580
|
binding.configOptions = response?.configOptions || binding.configOptions;
|
|
2424
|
-
const confirmed = binding.configOptions?.find(candidate => candidate.id === normalizedConfigId);
|
|
2581
|
+
const confirmed = binding.configOptions?.find((candidate) => candidate.id === normalizedConfigId);
|
|
2425
2582
|
if (confirmed?.currentValue === request.value) break;
|
|
2426
2583
|
if (attempt === 1) {
|
|
2427
2584
|
throw new Error(`ACP Agent did not confirm config option ${normalizedConfigId}`);
|
|
@@ -2432,18 +2589,16 @@ class AcpRuntime extends EventEmitter {
|
|
|
2432
2589
|
if (options.emit !== false) this.emitSession(binding);
|
|
2433
2590
|
return { sessionId: binding.sessionId, configOptions: binding.configOptions };
|
|
2434
2591
|
}
|
|
2435
|
-
|
|
2436
2592
|
getSession(agentId, options = {}) {
|
|
2437
2593
|
const binding = this.requireBinding(agentId);
|
|
2438
2594
|
const runtimeState = {
|
|
2439
2595
|
state: binding.state,
|
|
2440
2596
|
error: binding.error,
|
|
2441
|
-
errorKind: binding.error ? acpErrorKind(binding.error) :
|
|
2597
|
+
errorKind: binding.error ? acpErrorKind(binding.error) : "",
|
|
2442
2598
|
stopReason: binding.stopReason,
|
|
2443
2599
|
supportsSteer: binding.supportsSteer === true,
|
|
2444
2600
|
supportsFork: Boolean(
|
|
2445
|
-
binding.initializeResponse?.agentCapabilities?.sessionCapabilities?.fork != null
|
|
2446
|
-
&& binding.initializeResponse?.agentCapabilities?.loadSession
|
|
2601
|
+
binding.initializeResponse?.agentCapabilities?.sessionCapabilities?.fork != null && binding.initializeResponse?.agentCapabilities?.loadSession
|
|
2447
2602
|
),
|
|
2448
2603
|
protocolVersion: binding.initializeResponse?.protocolVersion || null,
|
|
2449
2604
|
agentInfo: binding.initializeResponse?.agentInfo || null,
|
|
@@ -2451,152 +2606,139 @@ class AcpRuntime extends EventEmitter {
|
|
|
2451
2606
|
authMethods: binding.initializeResponse?.authMethods || [],
|
|
2452
2607
|
modes: binding.modes,
|
|
2453
2608
|
configOptions: binding.configOptions,
|
|
2609
|
+
deferredConfigOptions: [...binding.deferredConfigChanges.values()],
|
|
2610
|
+
deferredModeId: binding.deferredModeId,
|
|
2454
2611
|
pendingPermission: binding.pendingPermissions.values().next().value || null,
|
|
2455
2612
|
pendingPermissions: [...binding.pendingPermissions.values()],
|
|
2456
2613
|
pendingElicitation: binding.pendingElicitations.values().next().value || null,
|
|
2457
2614
|
pendingElicitations: [...binding.pendingElicitations.values()],
|
|
2458
2615
|
activeElicitations: [...binding.activeElicitations.values()],
|
|
2459
2616
|
adapter: binding.launch,
|
|
2460
|
-
authTerminal: binding.authTerminal
|
|
2461
|
-
|
|
2462
|
-
: null,
|
|
2463
|
-
updatedAt: binding.updatedAt,
|
|
2617
|
+
authTerminal: binding.authTerminal ? { ...binding.authTerminal, terminal: this.clientTerminals.display(binding.authTerminal.terminalId) } : null,
|
|
2618
|
+
updatedAt: binding.updatedAt
|
|
2464
2619
|
};
|
|
2465
2620
|
if (!binding.sessionState) {
|
|
2466
2621
|
return {
|
|
2467
2622
|
version: 1,
|
|
2468
|
-
protocol:
|
|
2623
|
+
protocol: "acp",
|
|
2469
2624
|
provider: binding.provider,
|
|
2470
2625
|
sessionId: binding.sessionId,
|
|
2471
2626
|
cwd: binding.cwd,
|
|
2472
|
-
title:
|
|
2473
|
-
updatedAt: binding.updatedAt,
|
|
2627
|
+
title: "",
|
|
2474
2628
|
truncated: false,
|
|
2475
2629
|
entries: [],
|
|
2476
2630
|
usage: null,
|
|
2477
2631
|
availableCommands: [],
|
|
2478
|
-
currentModeId:
|
|
2479
|
-
|
|
2480
|
-
...runtimeState,
|
|
2632
|
+
currentModeId: "",
|
|
2633
|
+
...runtimeState
|
|
2481
2634
|
};
|
|
2482
2635
|
}
|
|
2483
2636
|
return binding.sessionState.snapshot(runtimeState, options);
|
|
2484
2637
|
}
|
|
2485
|
-
|
|
2486
2638
|
getTranscriptSession(agentId, options = {}) {
|
|
2487
2639
|
const binding = this.requireBinding(agentId);
|
|
2488
|
-
const slice = binding.sessionState
|
|
2489
|
-
? binding.sessionState.transcriptSlice(options)
|
|
2490
|
-
: { entries: [], revision: 0, delta: false, hasMoreBefore: false };
|
|
2640
|
+
const slice = binding.sessionState ? binding.sessionState.transcriptSlice(options) : { entries: [], revision: 0, delta: false, hasMoreBefore: false };
|
|
2491
2641
|
return {
|
|
2492
2642
|
version: 2,
|
|
2493
|
-
protocol:
|
|
2643
|
+
protocol: "acp",
|
|
2494
2644
|
provider: binding.provider,
|
|
2495
2645
|
sessionId: binding.sessionId,
|
|
2496
2646
|
cwd: binding.cwd,
|
|
2497
|
-
title: binding.sessionState?.title ||
|
|
2647
|
+
title: binding.sessionState?.title || "",
|
|
2498
2648
|
updatedAt: binding.updatedAt,
|
|
2499
2649
|
truncated: binding.sessionState?.truncated === true,
|
|
2500
2650
|
state: binding.state,
|
|
2501
2651
|
error: binding.error,
|
|
2502
|
-
errorKind: binding.error ? acpErrorKind(binding.error) :
|
|
2652
|
+
errorKind: binding.error ? acpErrorKind(binding.error) : "",
|
|
2503
2653
|
stopReason: binding.stopReason,
|
|
2504
|
-
|
|
2654
|
+
plan: binding.sessionState?.plan == null ? null : JSON.parse(JSON.stringify(binding.sessionState.plan)),
|
|
2655
|
+
...slice
|
|
2505
2656
|
};
|
|
2506
2657
|
}
|
|
2507
|
-
|
|
2508
2658
|
getToolEntry(agentId, toolCallId) {
|
|
2509
2659
|
const binding = this.requireBinding(agentId);
|
|
2510
|
-
const entry = binding.sessionState?.toolEntries.get(String(toolCallId ||
|
|
2660
|
+
const entry = binding.sessionState?.toolEntries.get(String(toolCallId || ""));
|
|
2511
2661
|
if (!entry || binding.sessionState.isInternalEntry(entry)) return null;
|
|
2512
2662
|
const visible = JSON.parse(JSON.stringify(entry));
|
|
2513
|
-
visible.content = (Array.isArray(visible.content) ? visible.content : []).map(block => {
|
|
2514
|
-
if (block?.type !==
|
|
2663
|
+
visible.content = (Array.isArray(visible.content) ? visible.content : []).map((block) => {
|
|
2664
|
+
if (block?.type !== "terminal") return block;
|
|
2515
2665
|
const terminal = this.clientTerminals.display(block.terminalId);
|
|
2516
2666
|
return terminal ? { ...block, terminal } : block;
|
|
2517
2667
|
});
|
|
2518
2668
|
return visible;
|
|
2519
2669
|
}
|
|
2520
|
-
|
|
2521
2670
|
getTranscriptEntry(agentId, entryId) {
|
|
2522
2671
|
const binding = this.requireBinding(agentId);
|
|
2523
|
-
const entries = (binding.sessionState?.entries || []).filter(candidate => (
|
|
2524
|
-
String(candidate?.id || '') === String(entryId || '')
|
|
2525
|
-
));
|
|
2672
|
+
const entries = (binding.sessionState?.entries || []).filter((candidate) => String(candidate?.id || "") === String(entryId || ""));
|
|
2526
2673
|
if (entries.length !== 1) return null;
|
|
2527
2674
|
const [entry] = entries;
|
|
2528
2675
|
if (!entry || binding.sessionState.isInternalEntry(entry)) return null;
|
|
2529
2676
|
return entry;
|
|
2530
2677
|
}
|
|
2531
|
-
|
|
2532
2678
|
getPatchDecision(agentId, toolCallId, requestedPath) {
|
|
2533
2679
|
const binding = this.requireBinding(agentId);
|
|
2534
|
-
return binding.patchDecisions.get(`${String(toolCallId ||
|
|
2680
|
+
return binding.patchDecisions.get(`${String(toolCallId || "")}
|
|
2681
|
+
${String(requestedPath || "")}`) || "";
|
|
2535
2682
|
}
|
|
2536
|
-
|
|
2537
2683
|
async decidePatch(agentId, toolCallId, requestedPath, decision) {
|
|
2538
2684
|
const binding = this.requireBinding(agentId);
|
|
2539
2685
|
this.requireOpenBinding(binding);
|
|
2540
|
-
if (
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
)
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
const
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
const normalizedDecision = String(decision || '').trim().toLowerCase();
|
|
2552
|
-
if (!['keep', 'revert'].includes(normalizedDecision)) throw new Error('ACP patch decision is invalid');
|
|
2553
|
-
const key = `${String(toolCallId || '')}\n${String(requestedPath || '')}`;
|
|
2554
|
-
const expectedAction = normalizedDecision === 'keep' ? 'kept' : 'reverted';
|
|
2686
|
+
if (binding.sessionMutation || binding.configMutationTail || binding.activeTurn || !["idle", "error"].includes(binding.state)) {
|
|
2687
|
+
throw new Error("Wait for the Agent to finish before deciding a patch");
|
|
2688
|
+
}
|
|
2689
|
+
const entry = binding.sessionState?.toolEntries.get(String(toolCallId || ""));
|
|
2690
|
+
if (entry && binding.sessionState.isInternalEntry(entry)) throw new Error("ACP tool call not found");
|
|
2691
|
+
if (!entry) throw new Error("ACP tool call not found");
|
|
2692
|
+
const normalizedDecision = String(decision || "").trim().toLowerCase();
|
|
2693
|
+
if (!["keep", "revert"].includes(normalizedDecision)) throw new Error("ACP patch decision is invalid");
|
|
2694
|
+
const key = `${String(toolCallId || "")}
|
|
2695
|
+
${String(requestedPath || "")}`;
|
|
2696
|
+
const expectedAction = normalizedDecision === "keep" ? "kept" : "reverted";
|
|
2555
2697
|
const existing = binding.patchDecisionInFlight.get(key);
|
|
2556
2698
|
if (existing) {
|
|
2557
2699
|
if (existing.decision !== normalizedDecision) {
|
|
2558
|
-
throw new Error(
|
|
2700
|
+
throw new Error("ACP patch file already has a different decision in progress");
|
|
2559
2701
|
}
|
|
2560
2702
|
return existing.promise;
|
|
2561
2703
|
}
|
|
2562
2704
|
const committed = binding.patchDecisions.get(key);
|
|
2563
2705
|
if (committed) {
|
|
2564
|
-
if (committed !== expectedAction) throw new Error(
|
|
2565
|
-
const
|
|
2706
|
+
if (committed !== expectedAction) throw new Error("ACP patch file already has a different decision");
|
|
2707
|
+
const operation2 = (async () => {
|
|
2566
2708
|
await this.writeCheckpoint(binding, { exact: true });
|
|
2567
2709
|
return {
|
|
2568
2710
|
action: committed,
|
|
2569
|
-
path: String(requestedPath ||
|
|
2570
|
-
toolCallId: String(toolCallId ||
|
|
2711
|
+
path: String(requestedPath || ""),
|
|
2712
|
+
toolCallId: String(toolCallId || "")
|
|
2571
2713
|
};
|
|
2572
2714
|
})();
|
|
2573
|
-
const
|
|
2574
|
-
binding.patchDecisionInFlight.set(key,
|
|
2715
|
+
const pending2 = { decision: normalizedDecision, promise: operation2 };
|
|
2716
|
+
binding.patchDecisionInFlight.set(key, pending2);
|
|
2575
2717
|
try {
|
|
2576
|
-
return await
|
|
2718
|
+
return await operation2;
|
|
2577
2719
|
} finally {
|
|
2578
|
-
if (binding.patchDecisionInFlight.get(key) ===
|
|
2720
|
+
if (binding.patchDecisionInFlight.get(key) === pending2) {
|
|
2579
2721
|
binding.patchDecisionInFlight.delete(key);
|
|
2580
2722
|
}
|
|
2581
2723
|
}
|
|
2582
2724
|
}
|
|
2583
2725
|
const operation = (async () => {
|
|
2584
|
-
let result = { action:
|
|
2585
|
-
if (normalizedDecision ===
|
|
2586
|
-
result = await rejectPatch({ entry, root: binding.cwd, requestedPath });
|
|
2726
|
+
let result = { action: "kept", path: String(requestedPath || "") };
|
|
2727
|
+
if (normalizedDecision === "revert") {
|
|
2728
|
+
result = await (0, import_patch_decisions.rejectPatch)({ entry, root: binding.cwd, requestedPath });
|
|
2587
2729
|
this.requireOpenBinding(binding);
|
|
2588
2730
|
}
|
|
2589
2731
|
binding.patchDecisions.set(key, result.action);
|
|
2590
|
-
entry._meta = { ...
|
|
2732
|
+
entry._meta = { ...entry._meta || {} };
|
|
2591
2733
|
entry._meta.farming_patch_decisions = {
|
|
2592
|
-
...
|
|
2593
|
-
[String(requestedPath ||
|
|
2734
|
+
...entry._meta.farming_patch_decisions || {},
|
|
2735
|
+
[String(requestedPath || "")]: result.action
|
|
2594
2736
|
};
|
|
2595
2737
|
binding.sessionState?.touchEntry(entry);
|
|
2596
|
-
binding.updatedAt = new Date().toISOString();
|
|
2738
|
+
binding.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2597
2739
|
await this.writeCheckpoint(binding, { exact: true });
|
|
2598
2740
|
this.emitSession(binding);
|
|
2599
|
-
return { ...result, toolCallId: String(toolCallId ||
|
|
2741
|
+
return { ...result, toolCallId: String(toolCallId || "") };
|
|
2600
2742
|
})();
|
|
2601
2743
|
const pending = { decision: normalizedDecision, promise: operation };
|
|
2602
2744
|
binding.patchDecisionInFlight.set(key, pending);
|
|
@@ -2608,27 +2750,24 @@ class AcpRuntime extends EventEmitter {
|
|
|
2608
2750
|
}
|
|
2609
2751
|
}
|
|
2610
2752
|
}
|
|
2611
|
-
|
|
2612
2753
|
killTerminal(agentId, terminalId) {
|
|
2613
2754
|
const binding = this.requireBinding(agentId);
|
|
2614
2755
|
this.requireOpenBinding(binding);
|
|
2615
2756
|
this.clientTerminals.kill(binding, {
|
|
2616
2757
|
sessionId: binding.sessionId,
|
|
2617
|
-
terminalId: String(terminalId ||
|
|
2758
|
+
terminalId: String(terminalId || "")
|
|
2618
2759
|
});
|
|
2619
|
-
return { killed: true, terminalId: String(terminalId ||
|
|
2760
|
+
return { killed: true, terminalId: String(terminalId || "") };
|
|
2620
2761
|
}
|
|
2621
|
-
|
|
2622
2762
|
inputTerminal(agentId, terminalId, input) {
|
|
2623
2763
|
const binding = this.requireBinding(agentId);
|
|
2624
2764
|
this.requireOpenBinding(binding);
|
|
2625
2765
|
return this.clientTerminals.input(binding, {
|
|
2626
2766
|
sessionId: binding.sessionId,
|
|
2627
2767
|
terminalId,
|
|
2628
|
-
input
|
|
2768
|
+
input
|
|
2629
2769
|
});
|
|
2630
2770
|
}
|
|
2631
|
-
|
|
2632
2771
|
resizeTerminal(agentId, terminalId, cols, rows) {
|
|
2633
2772
|
const binding = this.requireBinding(agentId);
|
|
2634
2773
|
this.requireOpenBinding(binding);
|
|
@@ -2636,136 +2775,112 @@ class AcpRuntime extends EventEmitter {
|
|
|
2636
2775
|
sessionId: binding.sessionId,
|
|
2637
2776
|
terminalId,
|
|
2638
2777
|
cols,
|
|
2639
|
-
rows
|
|
2778
|
+
rows
|
|
2640
2779
|
});
|
|
2641
2780
|
}
|
|
2642
|
-
|
|
2643
2781
|
getSubagentTranscriptSession(agentId, sessionId, options = {}) {
|
|
2644
2782
|
const binding = this.requireBinding(agentId);
|
|
2645
|
-
const id = String(sessionId ||
|
|
2783
|
+
const id = String(sessionId || "");
|
|
2646
2784
|
const state = binding.subagentStates.get(id);
|
|
2647
2785
|
if (!state) return null;
|
|
2648
|
-
const parentTool = binding.sessionState?.entries.find(entry => (
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
));
|
|
2652
|
-
const status = String(parentTool?.status || '').toLowerCase();
|
|
2653
|
-
const pendingPermission = [...binding.pendingPermissions.values()]
|
|
2654
|
-
.some(request => String(request?.sessionId || '') === id);
|
|
2655
|
-
const pendingElicitation = [...binding.pendingElicitations.values()]
|
|
2656
|
-
.some(request => String(request?.sessionId || '') === id);
|
|
2786
|
+
const parentTool = binding.sessionState?.entries.find((entry) => entry?.type === "tool" && String(entry?._meta?.subagent_session_info?.session_id || "") === id);
|
|
2787
|
+
const status = String(parentTool?.status || "").toLowerCase();
|
|
2788
|
+
const pendingPermission = [...binding.pendingPermissions.values()].some((request) => String(request?.sessionId || "") === id);
|
|
2789
|
+
const pendingElicitation = [...binding.pendingElicitations.values()].some((request) => String(request?.sessionId || "") === id);
|
|
2657
2790
|
const control = this.ensureSubagentControl(binding, id);
|
|
2658
|
-
const active = [
|
|
2659
|
-
const failed = [
|
|
2660
|
-
const stateName = pendingPermission
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
? 'waiting-for-input'
|
|
2664
|
-
: control.phase === 'cancelling'
|
|
2665
|
-
? 'interrupting'
|
|
2666
|
-
: control.phase === 'error'
|
|
2667
|
-
? 'error'
|
|
2668
|
-
: ['cancelled', 'completed'].includes(control.phase)
|
|
2669
|
-
? 'idle'
|
|
2670
|
-
: active
|
|
2671
|
-
? 'working'
|
|
2672
|
-
: failed
|
|
2673
|
-
? 'error'
|
|
2674
|
-
: 'idle';
|
|
2675
|
-
const controlFailed = control.phase === 'error';
|
|
2676
|
-
const cancelled = control.phase === 'cancelled';
|
|
2791
|
+
const active = ["pending", "in_progress", "in-progress", "running"].includes(status);
|
|
2792
|
+
const failed = ["failed", "error"].includes(status);
|
|
2793
|
+
const stateName = pendingPermission ? "waiting-for-permission" : pendingElicitation ? "waiting-for-input" : control.phase === "cancelling" ? "interrupting" : control.phase === "error" ? "error" : ["cancelled", "completed"].includes(control.phase) ? "idle" : active ? "working" : failed ? "error" : "idle";
|
|
2794
|
+
const controlFailed = control.phase === "error";
|
|
2795
|
+
const cancelled = control.phase === "cancelled";
|
|
2677
2796
|
return {
|
|
2678
2797
|
version: 2,
|
|
2679
|
-
protocol:
|
|
2798
|
+
protocol: "acp",
|
|
2680
2799
|
provider: binding.provider,
|
|
2681
2800
|
sessionId: id,
|
|
2682
2801
|
cwd: binding.cwd,
|
|
2683
|
-
title: state.title ||
|
|
2802
|
+
title: state.title || "",
|
|
2684
2803
|
updatedAt: binding.updatedAt,
|
|
2685
2804
|
truncated: state.truncated === true,
|
|
2686
2805
|
state: stateName,
|
|
2687
|
-
error: controlFailed ? control.error : failed ? String(parentTool?.title ||
|
|
2688
|
-
errorKind: controlFailed || failed ?
|
|
2689
|
-
stopReason: controlFailed || failed ?
|
|
2690
|
-
...state.transcriptSlice(options)
|
|
2806
|
+
error: controlFailed ? control.error : failed ? String(parentTool?.title || "Subagent failed") : "",
|
|
2807
|
+
errorKind: controlFailed || failed ? "agent" : "",
|
|
2808
|
+
stopReason: controlFailed || failed ? "error" : cancelled ? "cancelled" : active ? "" : "end_turn",
|
|
2809
|
+
...state.transcriptSlice(options)
|
|
2691
2810
|
};
|
|
2692
2811
|
}
|
|
2693
|
-
|
|
2694
2812
|
hasBinding(agentId) {
|
|
2695
2813
|
return this.bindings.has(agentId);
|
|
2696
2814
|
}
|
|
2697
|
-
|
|
2698
2815
|
requireBinding(agentId) {
|
|
2699
2816
|
const binding = this.bindings.get(agentId);
|
|
2700
|
-
if (!binding) throw new Error(
|
|
2817
|
+
if (!binding) throw new Error("ACP Agent is not registered");
|
|
2701
2818
|
return binding;
|
|
2702
2819
|
}
|
|
2703
|
-
|
|
2704
2820
|
emitRuntime(binding) {
|
|
2705
2821
|
if (!this.isCurrentBinding(binding)) return false;
|
|
2706
|
-
this.emit(
|
|
2822
|
+
this.emit("agent-runtime", {
|
|
2707
2823
|
agentId: binding.agentId,
|
|
2708
2824
|
provider: binding.provider,
|
|
2709
2825
|
sessionId: binding.sessionId,
|
|
2710
2826
|
state: binding.state,
|
|
2711
2827
|
error: binding.error,
|
|
2712
|
-
errorKind: binding.error ? acpErrorKind(binding.error) :
|
|
2828
|
+
errorKind: binding.error ? acpErrorKind(binding.error) : "",
|
|
2713
2829
|
stopReason: binding.stopReason,
|
|
2714
2830
|
supportsSteer: binding.supportsSteer === true,
|
|
2715
2831
|
supportsFork: Boolean(
|
|
2716
|
-
binding.initializeResponse?.agentCapabilities?.sessionCapabilities?.fork != null
|
|
2717
|
-
&& binding.initializeResponse?.agentCapabilities?.loadSession
|
|
2832
|
+
binding.initializeResponse?.agentCapabilities?.sessionCapabilities?.fork != null && binding.initializeResponse?.agentCapabilities?.loadSession
|
|
2718
2833
|
),
|
|
2719
2834
|
pendingPermission: binding.pendingPermissions.values().next().value || null,
|
|
2720
2835
|
pendingPermissions: [...binding.pendingPermissions.values()],
|
|
2721
2836
|
pendingElicitation: binding.pendingElicitations.values().next().value || null,
|
|
2722
2837
|
pendingElicitations: [...binding.pendingElicitations.values()],
|
|
2723
2838
|
activeElicitations: [...binding.activeElicitations.values()],
|
|
2724
|
-
updatedAt: binding.updatedAt
|
|
2839
|
+
updatedAt: binding.updatedAt
|
|
2725
2840
|
});
|
|
2726
2841
|
return true;
|
|
2727
2842
|
}
|
|
2728
|
-
|
|
2729
2843
|
emitSession(binding) {
|
|
2730
2844
|
if (!this.isCurrentBinding(binding)) return false;
|
|
2731
|
-
this.emit(
|
|
2845
|
+
this.emit("session", {
|
|
2732
2846
|
agentId: binding.agentId,
|
|
2733
2847
|
updatedAt: binding.updatedAt,
|
|
2734
2848
|
revision: binding.sessionState?.revision || 0,
|
|
2849
|
+
title: binding.sessionState?.title || ""
|
|
2735
2850
|
});
|
|
2736
2851
|
return true;
|
|
2737
2852
|
}
|
|
2738
|
-
|
|
2739
2853
|
handleExit(binding, error) {
|
|
2740
2854
|
if (!this.isCurrentBinding(binding) || binding.exited) return;
|
|
2741
2855
|
binding.exited = true;
|
|
2742
2856
|
const turn = binding.activeTurn;
|
|
2743
|
-
const promptWasActive = Boolean(turn && turn.phase !==
|
|
2857
|
+
const promptWasActive = Boolean(turn && turn.phase !== "admitting");
|
|
2744
2858
|
binding.activeTurn = null;
|
|
2745
2859
|
if (error) {
|
|
2746
|
-
binding.state =
|
|
2860
|
+
binding.state = "error";
|
|
2747
2861
|
binding.error = acpErrorMessage(error);
|
|
2748
|
-
binding.stopReason =
|
|
2749
|
-
} else if (binding.state !==
|
|
2750
|
-
binding.state =
|
|
2751
|
-
binding.stopReason = promptWasActive ?
|
|
2862
|
+
binding.stopReason = "error";
|
|
2863
|
+
} else if (binding.state !== "error") {
|
|
2864
|
+
binding.state = "stopped";
|
|
2865
|
+
binding.stopReason = promptWasActive ? "stopped" : binding.stopReason;
|
|
2752
2866
|
}
|
|
2753
2867
|
if (promptWasActive && binding.sessionState) {
|
|
2754
2868
|
if (error) binding.sessionState.recordError(binding.error, acpErrorKind(error));
|
|
2755
|
-
binding.sessionState.completePrompt(
|
|
2869
|
+
binding.sessionState.completePrompt();
|
|
2756
2870
|
}
|
|
2757
2871
|
if (turn) {
|
|
2758
|
-
turn.phase =
|
|
2759
|
-
turn.resolveCompletion({ status:
|
|
2872
|
+
turn.phase = "completed";
|
|
2873
|
+
turn.resolveCompletion?.({ status: "connection-closed", error: error || null });
|
|
2760
2874
|
}
|
|
2761
2875
|
for (const resolve of binding.permissionResolvers.values()) {
|
|
2762
|
-
resolve({ outcome: { outcome:
|
|
2876
|
+
resolve({ outcome: { outcome: "cancelled" } });
|
|
2763
2877
|
}
|
|
2764
|
-
for (const resolve of binding.elicitationResolvers.values()) resolve({ action:
|
|
2878
|
+
for (const resolve of binding.elicitationResolvers.values()) resolve({ action: "cancel" });
|
|
2765
2879
|
binding.permissionResolvers.clear();
|
|
2766
2880
|
binding.pendingPermissions.clear();
|
|
2767
2881
|
binding.elicitationResolvers.clear();
|
|
2768
2882
|
binding.pendingElicitations.clear();
|
|
2883
|
+
binding.farmingBrowserApprovalScopes.clear();
|
|
2769
2884
|
binding.activeElicitations.clear();
|
|
2770
2885
|
binding.interactionOrigins.clear();
|
|
2771
2886
|
binding.subagentStates.clear();
|
|
@@ -2774,7 +2889,6 @@ class AcpRuntime extends EventEmitter {
|
|
|
2774
2889
|
if (promptWasActive) this.emitSession(binding);
|
|
2775
2890
|
this.emitRuntime(binding);
|
|
2776
2891
|
}
|
|
2777
|
-
|
|
2778
2892
|
detachAgentBinding(binding, options = {}) {
|
|
2779
2893
|
if (!binding || this.bindings.get(binding.agentId) !== binding) return false;
|
|
2780
2894
|
if (binding.sessionState && !binding.activeTurn) this.scheduleCheckpoint(binding, { exact: true });
|
|
@@ -2783,15 +2897,16 @@ class AcpRuntime extends EventEmitter {
|
|
|
2783
2897
|
const turn = binding.activeTurn;
|
|
2784
2898
|
binding.activeTurn = null;
|
|
2785
2899
|
if (turn) {
|
|
2786
|
-
turn.phase =
|
|
2787
|
-
turn.resolveCompletion({ status:
|
|
2900
|
+
turn.phase = "completed";
|
|
2901
|
+
turn.resolveCompletion?.({ status: "binding-detached" });
|
|
2788
2902
|
}
|
|
2789
|
-
for (const resolve of binding.permissionResolvers.values()) resolve({ outcome: { outcome:
|
|
2790
|
-
for (const resolve of binding.elicitationResolvers.values()) resolve({ action:
|
|
2903
|
+
for (const resolve of binding.permissionResolvers.values()) resolve({ outcome: { outcome: "cancelled" } });
|
|
2904
|
+
for (const resolve of binding.elicitationResolvers.values()) resolve({ action: "cancel" });
|
|
2791
2905
|
binding.permissionResolvers.clear();
|
|
2792
2906
|
binding.pendingPermissions.clear();
|
|
2793
2907
|
binding.elicitationResolvers.clear();
|
|
2794
2908
|
binding.pendingElicitations.clear();
|
|
2909
|
+
binding.farmingBrowserApprovalScopes.clear();
|
|
2795
2910
|
binding.activeElicitations.clear();
|
|
2796
2911
|
binding.interactionOrigins.clear();
|
|
2797
2912
|
binding.subagentStates.clear();
|
|
@@ -2800,25 +2915,21 @@ class AcpRuntime extends EventEmitter {
|
|
|
2800
2915
|
try {
|
|
2801
2916
|
binding.connection?.close();
|
|
2802
2917
|
} catch {
|
|
2803
|
-
// Process cleanup below remains authoritative if transport cleanup races.
|
|
2804
2918
|
}
|
|
2805
2919
|
if (binding.child?.stdin?.writable && !binding.child.stdin.destroyed) {
|
|
2806
2920
|
try {
|
|
2807
2921
|
binding.child.stdin.end();
|
|
2808
2922
|
} catch {
|
|
2809
|
-
// Process cleanup below remains authoritative if stdin already closed.
|
|
2810
2923
|
}
|
|
2811
2924
|
}
|
|
2812
2925
|
return true;
|
|
2813
2926
|
}
|
|
2814
|
-
|
|
2815
2927
|
unregisterAgent(agentId, expectedBinding = null) {
|
|
2816
2928
|
const binding = this.bindings.get(agentId);
|
|
2817
2929
|
if (expectedBinding && binding !== expectedBinding) return;
|
|
2818
2930
|
if (!binding || !this.detachAgentBinding(binding)) return;
|
|
2819
|
-
signalProcessTree(binding,
|
|
2931
|
+
signalProcessTree(binding, "SIGTERM");
|
|
2820
2932
|
}
|
|
2821
|
-
|
|
2822
2933
|
async unregisterAgentAndWait(agentId, expectedBinding = null) {
|
|
2823
2934
|
const binding = this.bindings.get(agentId);
|
|
2824
2935
|
if (expectedBinding && binding !== expectedBinding) return false;
|
|
@@ -2827,26 +2938,23 @@ class AcpRuntime extends EventEmitter {
|
|
|
2827
2938
|
if (this.bindings.get(agentId) === binding) this.bindings.delete(agentId);
|
|
2828
2939
|
return true;
|
|
2829
2940
|
}
|
|
2830
|
-
|
|
2831
2941
|
dispose() {
|
|
2832
2942
|
if (this.disposed) return Promise.resolve();
|
|
2833
2943
|
if (this.disposePromise) return this.disposePromise;
|
|
2834
|
-
|
|
2835
2944
|
this.disposing = true;
|
|
2836
2945
|
const disposePromise = this.performDispose();
|
|
2837
2946
|
this.disposePromise = disposePromise;
|
|
2838
2947
|
void disposePromise.finally(() => {
|
|
2839
2948
|
if (this.disposePromise === disposePromise) this.disposePromise = null;
|
|
2840
2949
|
if (!this.disposed) this.disposing = false;
|
|
2841
|
-
}).catch(() => {
|
|
2950
|
+
}).catch(() => {
|
|
2951
|
+
});
|
|
2842
2952
|
return disposePromise;
|
|
2843
2953
|
}
|
|
2844
|
-
|
|
2845
2954
|
resumeAfterDisposeAbort() {
|
|
2846
2955
|
this.disposed = false;
|
|
2847
2956
|
this.disposing = false;
|
|
2848
2957
|
}
|
|
2849
|
-
|
|
2850
2958
|
async performDispose() {
|
|
2851
2959
|
for (const binding of this.bindings.values()) {
|
|
2852
2960
|
if (binding.sessionState && !binding.activeTurn) this.scheduleCheckpoint(binding, { exact: true });
|
|
@@ -2861,26 +2969,26 @@ class AcpRuntime extends EventEmitter {
|
|
|
2861
2969
|
}
|
|
2862
2970
|
}
|
|
2863
2971
|
if (failures.length > 0) {
|
|
2864
|
-
throw new AggregateError(failures,
|
|
2972
|
+
throw new AggregateError(failures, "One or more ACP Agent process trees did not exit");
|
|
2865
2973
|
}
|
|
2866
2974
|
if (this.checkpointStore) await this.checkpointStore.dispose();
|
|
2867
2975
|
this.disposed = true;
|
|
2868
2976
|
}
|
|
2869
2977
|
}
|
|
2870
|
-
|
|
2871
|
-
module.exports = {
|
|
2872
|
-
AcpRuntime,
|
|
2978
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
2979
|
+
0 && (module.exports = {
|
|
2873
2980
|
ADAPTER_VERSIONS,
|
|
2874
|
-
|
|
2981
|
+
AcpRuntime,
|
|
2982
|
+
CODEX_STEER_METHOD,
|
|
2875
2983
|
acpErrorKind,
|
|
2984
|
+
acpSessionRequestOptions,
|
|
2876
2985
|
autoPermissionResponse,
|
|
2877
2986
|
codexAcpEnvironment,
|
|
2987
|
+
deleteProviderSessionIdentity,
|
|
2878
2988
|
describeAcpProcessGroup,
|
|
2989
|
+
isCodexSteerUnavailableError,
|
|
2879
2990
|
promptContentForCapabilities,
|
|
2880
2991
|
resolveAcpLaunch,
|
|
2881
2992
|
stopPersistedAcpProcessGroup,
|
|
2882
|
-
supportsCodexSteer
|
|
2883
|
-
|
|
2884
|
-
CODEX_STEER_METHOD,
|
|
2885
|
-
deleteProviderSessionIdentity,
|
|
2886
|
-
};
|
|
2993
|
+
supportsCodexSteer
|
|
2994
|
+
});
|