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