farming-code 2.2.23 → 2.2.28
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 +9 -2
- package/README.zh_cn.md +9 -2
- package/THIRD_PARTY_NOTICES.md +3 -0
- package/backend/acp-runtime.js +15 -1
- package/backend/agent-manager.js +1054 -94
- package/backend/atomic-json-store.js +37 -0
- package/backend/cli-agents.js +9 -7
- package/backend/config-manager.js +167 -58
- package/backend/control-api.js +2 -1
- package/backend/farming-agent-bootstrap.js +49 -0
- package/backend/farming-agent-bootstrap.zh_cn.md +5 -0
- package/backend/farming-app-cli.js +9 -2
- package/backend/farming-cli.js +55 -0
- package/backend/farming-session-store.js +92 -89
- package/backend/main-agent-skills.js +17 -0
- package/backend/native-pty-host-client.js +99 -7
- package/backend/native-pty-host-path.js +20 -0
- package/backend/native-pty-host.js +6 -5
- package/backend/preview-session-manager.js +68 -0
- package/backend/provider-adapters.js +44 -5
- package/backend/review-session-store.js +32 -25
- package/backend/review-state-store.js +13 -14
- package/backend/run-history-store.js +13 -16
- package/backend/server.js +103 -78
- package/backend/storage-layout.js +20 -0
- package/backend/workspace-file-router.js +171 -6
- package/backend/workspace-file-service.js +19 -0
- package/backend/workspace-root-registry.js +6 -4
- package/dist/assets/App-CM7Cy-l4.css +1 -0
- package/dist/assets/App-CPG4WcjO.js +658 -0
- package/dist/assets/{FileEditorMarkdownPreview-KbnE8VC8.js → FileEditorMarkdownPreview-DYOMm-b1.js} +5 -5
- package/dist/assets/FileEditorPane-DXXq82aM.js +2 -0
- package/dist/assets/IconGlyphs-DrdPjYXr.js +1 -0
- package/dist/assets/ProjectFilesSection-CP66XVPl.js +12 -0
- package/dist/assets/{ReviewPage-L1vh1Ggs.js → ReviewPage-w21MEPD6.js} +1 -1
- package/dist/assets/{abnfDiagram-VRR7QNED-nnglrXFi.js → abnfDiagram-VRR7QNED-BY1qwq8A.js} +1 -1
- package/dist/assets/api-w2x5QfxB.js +1 -0
- package/dist/assets/{arc-CWoVlxJw.js → arc-DgZRAlwj.js} +1 -1
- package/dist/assets/{architectureDiagram-ZJ3FMSHR-BEXMaFfY.js → architectureDiagram-ZJ3FMSHR-jFn144od.js} +1 -1
- package/dist/assets/{blockDiagram-677ZJIJ3-1mCezSBk.js → blockDiagram-677ZJIJ3-BmRjlV0H.js} +1 -1
- package/dist/assets/{c4Diagram-LMCZKHZV-DhTiI6Uv.js → c4Diagram-LMCZKHZV-DUKul6tT.js} +1 -1
- package/dist/assets/{workspace-editor-monaco-DLlws5Ig.js → channel-CI9j3QFD.js} +1 -1
- package/dist/assets/chunk-2Q5K7J3B-87UnWaoh.js +1 -0
- package/dist/assets/{chunk-32BRIVSS-CCdsKd73.js → chunk-32BRIVSS-rGVu23o5.js} +1 -1
- package/dist/assets/{chunk-52WLFC77-Gncjk4LF.js → chunk-52WLFC77-CvFDKgbB.js} +1 -1
- package/dist/assets/{chunk-5VM5RSS4-Dlw7AXWp.js → chunk-5VM5RSS4-CFgnRW1C.js} +1 -1
- package/dist/assets/{chunk-7BUUIJ7U-ZBRhjLOh.js → chunk-7BUUIJ7U-Cp7bqW6s.js} +1 -1
- package/dist/assets/{chunk-C7G6YPKG-CiDVUP82.js → chunk-C7G6YPKG-BIS1gjZO.js} +1 -1
- package/dist/assets/{chunk-EX3LRPZG-z6gXP2_N.js → chunk-EX3LRPZG-BKG-cR9j.js} +1 -1
- package/dist/assets/{chunk-FWX5IMBZ-DMZBccQa.js → chunk-FWX5IMBZ-B8PsTr8a.js} +2 -2
- package/dist/assets/{chunk-HOUHSVGY-D8tIeoPk.js → chunk-HOUHSVGY-BHFOAiXT.js} +1 -1
- package/dist/assets/{chunk-ICXQ74PX-b6oQ2Q17.js → chunk-ICXQ74PX-CvLE1J9X.js} +1 -1
- package/dist/assets/{chunk-JWPE2WC7-DSyBRaRG.js → chunk-JWPE2WC7-D8misks7.js} +1 -1
- package/dist/assets/{chunk-MOJQB5TN-DAVH_p2a.js → chunk-MOJQB5TN-D-oNgSNH.js} +1 -1
- package/dist/assets/{chunk-OGEWGWER-DxoiMCI-.js → chunk-OGEWGWER-C2N-la9e.js} +1 -1
- package/dist/assets/{chunk-PUDLZKDR-NsOWh7HM.js → chunk-PUDLZKDR-BwI3Jz8q.js} +1 -1
- package/dist/assets/{chunk-Q4XR5HBZ-Di4H7L21.js → chunk-Q4XR5HBZ-BuqGfsXA.js} +1 -1
- package/dist/assets/{chunk-RYQCIY6F-CZA9pvFX.js → chunk-RYQCIY6F-Bq4XZcGt.js} +1 -1
- package/dist/assets/{chunk-V7JOEXUC-DttpZg9Y.js → chunk-V7JOEXUC-QZLzdTTJ.js} +1 -1
- package/dist/assets/chunk-VAUOI2AC-Cb0-kQ9g.js +1 -0
- package/dist/assets/{chunk-VR4S4FIN-D-Fmtd4Z.js → chunk-VR4S4FIN-DcZTpML_.js} +1 -1
- package/dist/assets/{chunk-WYO6CB5R-CUxMtfK2.js → chunk-WYO6CB5R-DCU8wuny.js} +2 -2
- package/dist/assets/chunk-XXDRQBXY-BZAC1Krv.js +1 -0
- package/dist/assets/{chunk-ZGVPDNZ5-CoV2QEl0.js → chunk-ZGVPDNZ5-B7r85QbY.js} +1 -1
- package/dist/assets/{chunk-ZIRB5QZD-DncwlQQc.js → chunk-ZIRB5QZD-BgEnDWhQ.js} +1 -1
- package/dist/assets/classDiagram-OUVF2IWQ-DcvGOuvi.js +1 -0
- package/dist/assets/classDiagram-v2-EOCWNBFH-DDYUSofQ.js +1 -0
- package/dist/assets/code-dark-BDjwj7Um.css +1 -0
- package/dist/assets/{cose-bilkent-JH36ORCC-BNIAz0nN.js → cose-bilkent-JH36ORCC-CThXS2qG.js} +1 -1
- package/dist/assets/{cssMode-DDWxTPm_.js → cssMode-Bdg5RpcJ.js} +1 -1
- package/dist/assets/{cynefinDiagram-TSTJHNR4-0xPI92Lg.js → cynefinDiagram-TSTJHNR4-DxBgzw25.js} +1 -1
- package/dist/assets/{dagre-DXlcHgr3.js → dagre-BM-GvGUZ.js} +1 -1
- package/dist/assets/{dagre-VKFMJZFB-DfuMG4gU.js → dagre-VKFMJZFB-B7UCRmxR.js} +1 -1
- package/dist/assets/{diagram-FQU43EPY-CT00bLZ6.js → diagram-FQU43EPY-CeL2DwKx.js} +1 -1
- package/dist/assets/{diagram-G47NLZAW-Xbh7zXUx.js → diagram-G47NLZAW-BEirglCC.js} +1 -1
- package/dist/assets/{diagram-NH7WQ7WH-SRDVPfn7.js → diagram-NH7WQ7WH-C6wcI-Rl.js} +1 -1
- package/dist/assets/{diagram-OA4YK3LP-CQRli4Np.js → diagram-OA4YK3LP-DpM-XXtJ.js} +1 -1
- package/dist/assets/{diagram-WEI45ONY-DMf17WQi.js → diagram-WEI45ONY-BQyaut9k.js} +1 -1
- package/dist/assets/{ebnfDiagram-CCIWWBDH-DhpFkgJ5.js → ebnfDiagram-CCIWWBDH-UmAXsUhr.js} +1 -1
- package/dist/assets/{erDiagram-Q63AITRT-1IwxWmJo.js → erDiagram-Q63AITRT-CdYLEOch.js} +1 -1
- package/dist/assets/file-icons-BOlcJ0ZF.js +1 -0
- package/dist/assets/flowDiagram-23GEKE2U-CO-z9-tq.js +1 -0
- package/dist/assets/{freemarker2-QBsTERH-.js → freemarker2-DfMDJfnE.js} +1 -1
- package/dist/assets/{ganttDiagram-NO4QXBWP-BNCdfyr1.js → ganttDiagram-NO4QXBWP-DgSUNdUB.js} +1 -1
- package/dist/assets/{gitGraphDiagram-IHSO6WYX-pbyWZuiX.js → gitGraphDiagram-IHSO6WYX-DjwjtRtF.js} +1 -1
- package/dist/assets/{handlebars-CuoxPo5A.js → handlebars-BEdPtzMT.js} +1 -1
- package/dist/assets/{html-gX5PEu0Q.js → html-B1fPb1QD.js} +1 -1
- package/dist/assets/html2canvas-DsczWxBa.js +5 -0
- package/dist/assets/{htmlMode-DIWUbUxd.js → htmlMode-CY2ZjHxH.js} +1 -1
- package/dist/assets/{index-DhTUO8DA.js → index-BzQO3pUr.js} +2 -2
- package/dist/assets/{infoDiagram-FWYZ7A6U-BcTOgDbe.js → infoDiagram-FWYZ7A6U-Bi9076l2.js} +1 -1
- package/dist/assets/{ishikawaDiagram-FXEZZL3T-DCdvGKrn.js → ishikawaDiagram-FXEZZL3T-DaMaNZN7.js} +1 -1
- package/dist/assets/{javascript-BYKGIUGs.js → javascript-BV2OsWA0.js} +1 -1
- package/dist/assets/{journeyDiagram-5HDEW3XC-BZ-1q_IT.js → journeyDiagram-5HDEW3XC-CfDy69EZ.js} +1 -1
- package/dist/assets/{jsonMode-AFmQQP6u.js → jsonMode-zd07aeuX.js} +1 -1
- package/dist/assets/{kanban-definition-HUTT4EX6-BIgejCjj.js → kanban-definition-HUTT4EX6-XFvZ722B.js} +1 -1
- package/dist/assets/{line-CfptFMmX.js → line-BXK9PKLn.js} +1 -1
- package/dist/assets/{linear-Dld3_ERu.js → linear-CULvmq2Z.js} +1 -1
- package/dist/assets/{liquid-DBIl-f0z.js → liquid-BParI24X.js} +1 -1
- package/dist/assets/{lspLanguageFeatures-DK6tqicV.js → lspLanguageFeatures-BzredkoV.js} +1 -1
- package/dist/assets/main-BlVUXfxa.css +1 -0
- package/dist/assets/{map-LZWWoeWa.js → map-C9Qw9eWh.js} +1 -1
- package/dist/assets/{mdx-DS-bVtZN.js → mdx-vGzUJN98.js} +1 -1
- package/dist/assets/{mermaid.core-gSpX30pV.js → mermaid.core-CzazaIKr.js} +3 -3
- package/dist/assets/{mindmap-definition-LN4V7U3C-BKBaI4LW.js → mindmap-definition-LN4V7U3C-Uoff7hZ3.js} +1 -1
- package/dist/assets/{monaco.contribution-BXz-lfFB.js → monaco.contribution-BmaeBQEc.js} +2 -2
- package/dist/assets/{ordinal-BLrOss5K.js → ordinal-r6pIwRdZ.js} +1 -1
- package/dist/assets/{pegDiagram-2B236MQR-sBvDY3VX.js → pegDiagram-2B236MQR-CCBiHQjk.js} +1 -1
- package/dist/assets/{pieDiagram-ENE6RG2P-DWlWcu08.js → pieDiagram-ENE6RG2P-D61s7l75.js} +1 -1
- package/dist/assets/{python-C93q3xXz.js → python-CUwdLgsI.js} +1 -1
- package/dist/assets/{quadrantDiagram-ABIIQ3AL-DZutUd3J.js → quadrantDiagram-ABIIQ3AL-D_CWk2DQ.js} +1 -1
- package/dist/assets/{railroadDiagram-RFXS5EU6-DQb3lXGM.js → railroadDiagram-RFXS5EU6-CdlCHtxN.js} +1 -1
- package/dist/assets/{razor-CBifNGXO.js → razor-BScebUxU.js} +1 -1
- package/dist/assets/{requirementDiagram-TGXJPOKE-DqFGXu2e.js → requirementDiagram-TGXJPOKE-BLpHtkoC.js} +1 -1
- package/dist/assets/{sankeyDiagram-HTMAVEWB-d7ECahnO.js → sankeyDiagram-HTMAVEWB-CkhYZN2j.js} +1 -1
- package/dist/assets/{sequenceDiagram-DBY2YBRQ-DdsbKqct.js → sequenceDiagram-DBY2YBRQ-8bploKJl.js} +1 -1
- package/dist/assets/{sizeCapture-X5ZJPWSS-daTB6OcF.js → sizeCapture-X5ZJPWSS-DtSdkS0d.js} +1 -1
- package/dist/assets/{src-WBW1QxJt.js → src-6yeq6WBA.js} +1 -1
- package/dist/assets/{stateDiagram-2N3HPSRC-P6iSrgwR.js → stateDiagram-2N3HPSRC-DfmVGpfA.js} +1 -1
- package/dist/assets/stateDiagram-v2-6OUMAXLB-B2c9CQ-J.js +1 -0
- package/dist/assets/{swimlanes-5IMT3BWC-BGnZJx1M.js → swimlanes-5IMT3BWC-DRLk2zRF.js} +2 -2
- package/dist/assets/swimlanesDiagram-G3AALYLV-B7_APh_h.js +8 -0
- package/dist/assets/{timeline-definition-FHXFAJF6-rA-r3aUR.js → timeline-definition-FHXFAJF6-Cngfy588.js} +1 -1
- package/dist/assets/{tsMode-B0oUTcXQ.js → tsMode-DzUAibXK.js} +1 -1
- package/dist/assets/{typescript-KJKfYO7K.js → typescript-D9EOeM7N.js} +1 -1
- package/dist/assets/{vennDiagram-L72KCM5P-DiVKUjA9.js → vennDiagram-L72KCM5P-DeJn63Lq.js} +1 -1
- package/dist/assets/{wardleyDiagram-EHGQE667-Cfxh7UOn.js → wardleyDiagram-EHGQE667-BH1nzy77.js} +1 -1
- package/dist/assets/{workers-BSQRIkqa.js → workers-DAiomYZM.js} +1 -1
- package/dist/assets/workspace-editor-model-CSGCl21x.js +1 -0
- package/dist/assets/workspace-editor-monaco-CFEfFNcA.js +1 -0
- package/dist/assets/{workspace-editor-monaco-BIpqcWf1.js → workspace-editor-monaco-tXA-udGx.js} +3 -3
- package/dist/assets/workspace-view-state-BS_V8JVs.js +7 -0
- package/dist/assets/{xml-C8FAcN6j.js → xml-wWl1dhkQ.js} +1 -1
- package/dist/assets/{xychartDiagram-FW5EYKEG--hmryQeL.js → xychartDiagram-FW5EYKEG-SkO2prCU.js} +1 -1
- package/dist/assets/{yaml-BQ4NrRhG.js → yaml-Dc2WDaOZ.js} +1 -1
- package/dist/crt-markdown-renderer.js +46 -46
- package/dist/index.html +1 -1
- package/extensions/browser/backend/agent-capability.js +55 -0
- package/extensions/browser/backend/browser-mcp-server.js +269 -0
- package/extensions/browser/backend/browser-resource-manager.js +524 -0
- package/extensions/browser/backend/browser-resource-store.js +149 -0
- package/extensions/browser/backend/browser-router.js +128 -0
- package/extensions/browser/backend/cdp-browser-runtime.js +648 -0
- package/extensions/browser/backend/cdp-client.js +167 -0
- package/extensions/browser/backend/executable-discovery.js +91 -0
- package/extensions/browser/backend/farming-browser-client.js +83 -0
- package/extensions/browser/backend/index.js +7 -0
- package/extensions/browser/bin/farming-browser +117 -0
- package/extensions/browser/frontend/BrowserSidebarPortals.tsx +362 -0
- package/extensions/browser/frontend/BrowserViewer.tsx +329 -0
- package/extensions/browser/frontend/browser-resource-state.ts +60 -0
- package/extensions/browser/frontend/browser.css +516 -0
- package/extensions/browser/frontend/types.ts +25 -0
- package/extensions/browser/frontend/useBrowserResources.ts +151 -0
- package/frontend/session-bridge.js +7 -6
- package/frontend/skins/crt/app.js +64 -4
- package/package.json +18 -13
- package/shared/browser-protocol.js +1 -1
- package/dist/assets/App-BbNBDA__.js +0 -210
- package/dist/assets/App-BrP-ENHg.css +0 -1
- package/dist/assets/FileEditorPane-5_HUejjd.js +0 -2
- package/dist/assets/IconGlyphs-C2WFcoJS.js +0 -1
- package/dist/assets/ProjectFilesSection-C1RAJWOE.js +0 -12
- package/dist/assets/api-D8nyOEbz.js +0 -1
- package/dist/assets/channel-BVWNatUB.js +0 -1
- package/dist/assets/chunk-2Q5K7J3B-d5WWh8jo.js +0 -1
- package/dist/assets/chunk-VAUOI2AC-UHNr_yhX.js +0 -1
- package/dist/assets/chunk-XXDRQBXY-BBXwjhUx.js +0 -1
- package/dist/assets/classDiagram-OUVF2IWQ-Db0ib5iE.js +0 -1
- package/dist/assets/classDiagram-v2-EOCWNBFH-Bkf69XF9.js +0 -1
- package/dist/assets/code-dark-BKtvf0Xc.css +0 -1
- package/dist/assets/file-icons-CHWt5nSy.js +0 -1
- package/dist/assets/flowDiagram-23GEKE2U-DYFo2xmB.js +0 -1
- package/dist/assets/main-XMXgyJJt.css +0 -1
- package/dist/assets/stateDiagram-v2-6OUMAXLB-DIYSf_qh.js +0 -1
- package/dist/assets/swimlanesDiagram-G3AALYLV-BIkrlKrZ.js +0 -8
- package/dist/assets/workspace-editor-model-D0Im34S5.js +0 -1
- package/dist/assets/workspace-view-state-BqB4HM7r.js +0 -7
- /package/dist/assets/{abap-Cc5D5h5i.js → abap-CtX5nWyX.js} +0 -0
- /package/dist/assets/{apex-BKopwLmx.js → apex-DQJRAHwc.js} +0 -0
- /package/dist/assets/{array-HeX70jSN.js → array-BTwzYU0x.js} +0 -0
- /package/dist/assets/{azcli-DI-C34WA.js → azcli-9SMrOQ4q.js} +0 -0
- /package/dist/assets/{bat-Cbp6u42z.js → bat-CBGLUsw1.js} +0 -0
- /package/dist/assets/{bicep-4QA1jTCM.js → bicep-pDx8da1v.js} +0 -0
- /package/dist/assets/{cameligo-Dda_adPs.js → cameligo-hZV3vQ6_.js} +0 -0
- /package/dist/assets/{chunk-Y2CYZVJY-Di0sYzi1.js → chunk-Y2CYZVJY-BYi0W5ea.js} +0 -0
- /package/dist/assets/{clojure-CvY8A83L.js → clojure-BhCwU3gq.js} +0 -0
- /package/dist/assets/{coffee-4LX2Sv8A.js → coffee-BVB1PsgH.js} +0 -0
- /package/dist/assets/{core-B9MwRjDE.js → core-vPkV8P_m.js} +0 -0
- /package/dist/assets/{cpp-D4AzSV28.js → cpp-eTjuiNPq.js} +0 -0
- /package/dist/assets/{csharp-C4nWN4Yd.js → csharp-1kP-kKZT.js} +0 -0
- /package/dist/assets/{csp-CiEaaS5Z.js → csp-C_iQqIes.js} +0 -0
- /package/dist/assets/{css-DXxBRsbk.js → css-CxOs7IfP.js} +0 -0
- /package/dist/assets/{cypher-DOMzqyFA.js → cypher-Cd-a9Y82.js} +0 -0
- /package/dist/assets/{cytoscape.esm-Cl8iC3D7.js → cytoscape.esm-OLojuicg.js} +0 -0
- /package/dist/assets/{dart-BWS7MXt_.js → dart-whhXpexn.js} +0 -0
- /package/dist/assets/{defaultLocale-GJwWH1Jr.js → defaultLocale-CPimVhfE.js} +0 -0
- /package/dist/assets/{dist-ClAL-Tv5.js → dist-CLPYXg1u.js} +0 -0
- /package/dist/assets/{dockerfile-Baf4U3xT.js → dockerfile-DibBi8b6.js} +0 -0
- /package/dist/assets/{ecl-CT7udlkQ.js → ecl-DlF1KdoW.js} +0 -0
- /package/dist/assets/{editor.api2-B9h6iFPK.js → editor.api2-D74ufkbr.js} +0 -0
- /package/dist/assets/{elixir-CIbAZw5l.js → elixir-CHQ8uFT3.js} +0 -0
- /package/dist/assets/{flow9-Dqiw45WV.js → flow9-8nUnVKk1.js} +0 -0
- /package/dist/assets/{fsharp-E8E2L9jU.js → fsharp-DNZNZyEE.js} +0 -0
- /package/dist/assets/{go-CouVSN4D.js → go-_kPJjNc_.js} +0 -0
- /package/dist/assets/{graphlib-CbSvZcZU.js → graphlib-CBGF9gA9.js} +0 -0
- /package/dist/assets/{graphql-a6h6h5-t.js → graphql-4HCNVt5g.js} +0 -0
- /package/dist/assets/{hcl-vso3e6N-.js → hcl-C87ymY0o.js} +0 -0
- /package/dist/assets/{ini-SwZZkotR.js → ini-BYrrGNTc.js} +0 -0
- /package/dist/assets/{init-BPLPMQ3Y.js → init-8_PCa3LO.js} +0 -0
- /package/dist/assets/{java-CtRLC4_3.js → java-B211rJbk.js} +0 -0
- /package/dist/assets/{julia-CuJWFKQH.js → julia-DVPsRtBm.js} +0 -0
- /package/dist/assets/{katex-C8DPyI6s.js → katex-C2iYad0m.js} +0 -0
- /package/dist/assets/{kotlin-B_kBhsre.js → kotlin-BYyNerjp.js} +0 -0
- /package/dist/assets/{less-CtrHIfz3.js → less-EC9UblGE.js} +0 -0
- /package/dist/assets/{lexon-yVt1pJLu.js → lexon-B1yuUdqW.js} +0 -0
- /package/dist/assets/{lua-prOwgJ4U.js → lua-Bo6GFzxT.js} +0 -0
- /package/dist/assets/{m3-DhrdTQ84.js → m3-C-Y9cBVI.js} +0 -0
- /package/dist/assets/{markdown-DoI5HwSM.js → markdown-BHu-wLbe.js} +0 -0
- /package/dist/assets/{mermaid-parser.core-BQtepoLd.js → mermaid-parser.core-IxcGtNYG.js} +0 -0
- /package/dist/assets/{mips-Dye5jsBU.js → mips-Dkds26rX.js} +0 -0
- /package/dist/assets/{msdax-TkNOyIL8.js → msdax-PU4g-8_g.js} +0 -0
- /package/dist/assets/{mysql-CEc-RKZF.js → mysql-fPpuGpS7.js} +0 -0
- /package/dist/assets/{objective-c-CqqCsQz0.js → objective-c-Wy1e5JUf.js} +0 -0
- /package/dist/assets/{pascal-DO_pj8yg.js → pascal-BIXwvrqU.js} +0 -0
- /package/dist/assets/{pascaligo-1cbdZdF3.js → pascaligo-DwR0AtL_.js} +0 -0
- /package/dist/assets/{path-YdFzr2W6.js → path-BxQuywGl.js} +0 -0
- /package/dist/assets/{perl-B_N3PYfC.js → perl-CGHAl8my.js} +0 -0
- /package/dist/assets/{pgsql-9iYk1FQ7.js → pgsql-B91M73E2.js} +0 -0
- /package/dist/assets/{php-rO2sgoUM.js → php-C70ttrCx.js} +0 -0
- /package/dist/assets/{pla-Cbgv60kq.js → pla-BNrXaRh0.js} +0 -0
- /package/dist/assets/{postiats-y6xrhy_M.js → postiats-Dv2IAZR9.js} +0 -0
- /package/dist/assets/{powerquery-BaD8gWNE.js → powerquery-CkY5Wq7J.js} +0 -0
- /package/dist/assets/{powershell-BMhG4JVe.js → powershell-uGIYLU3C.js} +0 -0
- /package/dist/assets/{protobuf-CdOP5kN3.js → protobuf-CavfmJu-.js} +0 -0
- /package/dist/assets/{pug-Ck60hNF9.js → pug-Drqnyitx.js} +0 -0
- /package/dist/assets/{qrcode-DV-_rjU8.js → qrcode-7uGsTEmi.js} +0 -0
- /package/dist/assets/{qsharp-BcaKFfP-.js → qsharp-C7yF-Dti.js} +0 -0
- /package/dist/assets/{r-rFNes0iK.js → r-HXLGP0_-.js} +0 -0
- /package/dist/assets/{redis-DkpQ3K6G.js → redis-C4y6mf2p.js} +0 -0
- /package/dist/assets/{redshift-Di9N5TI3.js → redshift-DdQrFPrv.js} +0 -0
- /package/dist/assets/{restructuredtext-By5ZzLt6.js → restructuredtext-CBrf2PNj.js} +0 -0
- /package/dist/assets/{rough.esm-c4PR5shF.js → rough.esm-6CnTHTkH.js} +0 -0
- /package/dist/assets/{ruby-fF5zGiLp.js → ruby-CVtMoX2B.js} +0 -0
- /package/dist/assets/{rust-C5b3AYY6.js → rust-Ckq4sbMP.js} +0 -0
- /package/dist/assets/{sb-CD07UNRs.js → sb-er66OpE3.js} +0 -0
- /package/dist/assets/{scala-jtn1ip9G.js → scala-BdWZz-hH.js} +0 -0
- /package/dist/assets/{scheme-C4vqMkn_.js → scheme-kTV_s2kP.js} +0 -0
- /package/dist/assets/{scss-I4Glxjbz.js → scss-Bymy2VJQ.js} +0 -0
- /package/dist/assets/{shell-DubUcuns.js → shell-bwEP1LQ1.js} +0 -0
- /package/dist/assets/{solidity-CNy2KSl2.js → solidity-DCddeBgL.js} +0 -0
- /package/dist/assets/{sophia-D9_URt84.js → sophia-BMN3AQig.js} +0 -0
- /package/dist/assets/{sparql-DviXpm4C.js → sparql-CSXOLjzi.js} +0 -0
- /package/dist/assets/{sql-vDRbAZgK.js → sql-COg8ewmK.js} +0 -0
- /package/dist/assets/{st-CnpnRqA7.js → st-CIaZtBS_.js} +0 -0
- /package/dist/assets/{swift-D_g8Ganz.js → swift-DXiOhUHu.js} +0 -0
- /package/dist/assets/{systemverilog-OuZa5h1M.js → systemverilog-BTDE6Dr2.js} +0 -0
- /package/dist/assets/{tcl-DBm9ucsV.js → tcl-B0zy9NvV.js} +0 -0
- /package/dist/assets/{twig-BRzEqX5Y.js → twig-D594l4Z1.js} +0 -0
- /package/dist/assets/{typespec-D4ZgGeX8.js → typespec-XKVjaPqo.js} +0 -0
- /package/dist/assets/{useWorkspaceMenuKeyboard-Brws6Ar9.js → useWorkspaceMenuKeyboard-Bpy_s69Z.js} +0 -0
- /package/dist/assets/{vb-DcP4s6WS.js → vb-B2wYoKRV.js} +0 -0
- /package/dist/assets/{wgsl-CnRJ_QUf.js → wgsl-D0Yf2LbK.js} +0 -0
- /package/dist/assets/{workspace-working-copy-D8-s_Sgh.js → workspace-working-copy-C3FSR3N6.js} +0 -0
package/backend/agent-manager.js
CHANGED
|
@@ -16,7 +16,12 @@ const { archiveCodexSession, unarchiveCodexSession } = require('./codex-session-
|
|
|
16
16
|
const { buildAgentProviderSessionPlan, sessionFromExactResumeSource } = require('./agent-provider-session');
|
|
17
17
|
const { resolveAgentExecutable, resolveCompatibleCodexExecutable } = require('./executable-discovery');
|
|
18
18
|
const { ensureMainAgentSkillFiles, renderMainAgentBootstrap } = require('./main-agent-skills');
|
|
19
|
+
const {
|
|
20
|
+
appendOpenCodeBootstrap,
|
|
21
|
+
renderFarmingAgentBootstrap,
|
|
22
|
+
} = require('./farming-agent-bootstrap');
|
|
19
23
|
const { mainPageAgentSessionKey, resumedAgentSource } = require('./main-page-session');
|
|
24
|
+
const storageLayout = require('./storage-layout');
|
|
20
25
|
const { isSafeProviderSessionId, isTemporaryProviderSessionId } = require('./provider-session-id');
|
|
21
26
|
const { ProviderSessionService } = require('./provider-session-service');
|
|
22
27
|
const {
|
|
@@ -67,6 +72,7 @@ const { inspectGitWorktree } = require('./git-worktree-info');
|
|
|
67
72
|
const { deserializeTerminalState } = require('./terminal-state-serialization');
|
|
68
73
|
const { compareNativePtyRuntimeEpochs } = require('./native-pty-controller-generation');
|
|
69
74
|
const { canonicalWorkspacePath } = require('./workspace-root-registry');
|
|
75
|
+
const { mergeBrowserMcpServer } = require('../extensions/browser/backend/agent-capability');
|
|
70
76
|
const {
|
|
71
77
|
TERMINAL_OPERATION_STATES,
|
|
72
78
|
activeLifecycleOperation,
|
|
@@ -83,6 +89,7 @@ const AGENT_USAGE_RATE_REFRESH_MS = 5 * 1000;
|
|
|
83
89
|
const ACTIVITY_UPDATE_INTERVAL_MS = 1000;
|
|
84
90
|
const ACTIVITY_HOT_SEC = 30 * 60;
|
|
85
91
|
const ACTIVITY_WARM_SEC = 3 * 60 * 60;
|
|
92
|
+
const MAX_COMPOSER_COMMANDS = 64;
|
|
86
93
|
const ACTIVITY_COOL_SEC = 12 * 60 * 60;
|
|
87
94
|
const ZOMBIE_IDLE_MS = 72 * 60 * 60 * 1000;
|
|
88
95
|
const ZOMBIE_SWEEP_INTERVAL_MS = 60 * 1000;
|
|
@@ -369,6 +376,41 @@ function normalizedComposerPrompt(message) {
|
|
|
369
376
|
return prompt;
|
|
370
377
|
}
|
|
371
378
|
|
|
379
|
+
function composerCommandHash(prompt) {
|
|
380
|
+
return crypto.createHash('sha256')
|
|
381
|
+
.update(JSON.stringify(stableJsonValue(prompt)))
|
|
382
|
+
.digest('hex');
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
function composerAdmissionError(message, uncertain = false) {
|
|
386
|
+
const error = new Error(message);
|
|
387
|
+
if (uncertain) error.uncertain = true;
|
|
388
|
+
return error;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
function normalizedComposerCommands(commands) {
|
|
392
|
+
return (Array.isArray(commands) ? commands : [])
|
|
393
|
+
.filter(command => (
|
|
394
|
+
command
|
|
395
|
+
&& typeof command === 'object'
|
|
396
|
+
&& /^[A-Za-z0-9._:-]{1,160}$/.test(command.requestId)
|
|
397
|
+
&& typeof command.contentHash === 'string'
|
|
398
|
+
&& ['intent', 'accepted', 'unknown', 'failed'].includes(command.state)
|
|
399
|
+
))
|
|
400
|
+
.slice(-MAX_COMPOSER_COMMANDS)
|
|
401
|
+
.map(command => ({
|
|
402
|
+
requestId: command.requestId,
|
|
403
|
+
contentHash: command.contentHash,
|
|
404
|
+
state: command.state,
|
|
405
|
+
result: command.result && typeof command.result === 'object'
|
|
406
|
+
? JSON.parse(JSON.stringify(command.result))
|
|
407
|
+
: null,
|
|
408
|
+
error: typeof command.error === 'string' ? command.error.slice(0, 2000) : '',
|
|
409
|
+
createdAt: Number(command.createdAt) || 0,
|
|
410
|
+
updatedAt: Number(command.updatedAt) || 0,
|
|
411
|
+
}));
|
|
412
|
+
}
|
|
413
|
+
|
|
372
414
|
function isShellProgram(command) {
|
|
373
415
|
return ['bash', 'zsh', 'sh', 'fish'].includes(agentProgramName(command).toLowerCase());
|
|
374
416
|
}
|
|
@@ -738,6 +780,27 @@ function timestampSlug(now = new Date()) {
|
|
|
738
780
|
].join('');
|
|
739
781
|
}
|
|
740
782
|
|
|
783
|
+
function projectOperationSignature(value) {
|
|
784
|
+
return crypto.createHash('sha256')
|
|
785
|
+
.update(JSON.stringify(stableJsonValue(value)))
|
|
786
|
+
.digest('hex');
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
function worktreesFromPorcelain(output) {
|
|
790
|
+
const worktrees = [];
|
|
791
|
+
let current = null;
|
|
792
|
+
for (const line of String(output || '').split(/\r?\n/)) {
|
|
793
|
+
if (line.startsWith('worktree ')) {
|
|
794
|
+
if (current) worktrees.push(current);
|
|
795
|
+
current = { workspace: path.resolve(line.slice('worktree '.length)), branch: '' };
|
|
796
|
+
} else if (current && line.startsWith('branch refs/heads/')) {
|
|
797
|
+
current.branch = line.slice('branch refs/heads/'.length);
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
if (current) worktrees.push(current);
|
|
801
|
+
return worktrees;
|
|
802
|
+
}
|
|
803
|
+
|
|
741
804
|
function isFarmingForkWorktreePath(workspace) {
|
|
742
805
|
const basename = path.basename(String(workspace || '').replace(/[\\/]+$/, ''));
|
|
743
806
|
return /-farming-fork-\d{8}-\d{6}(?:-\d+)?$/.test(basename);
|
|
@@ -757,6 +820,7 @@ class AgentManager extends EventEmitter {
|
|
|
757
820
|
this.controlUrl = options.controlUrl || '';
|
|
758
821
|
this.tokenFile = options.tokenFile || '';
|
|
759
822
|
this.authDisabled = options.authDisabled === true;
|
|
823
|
+
this.browserMcpEnabled = options.browserMcpEnabled === true;
|
|
760
824
|
this.skipExecutablePreflight = options.skipExecutablePreflight === true;
|
|
761
825
|
this.cliBinDir = options.cliBinDir || path.join(__dirname, '..', 'bin');
|
|
762
826
|
this.agentShellEnvProvider = typeof options.agentShellEnvProvider === 'function'
|
|
@@ -780,6 +844,7 @@ class AgentManager extends EventEmitter {
|
|
|
780
844
|
this.pendingResizeByAgent = new Map();
|
|
781
845
|
this.resizeDrains = new Map();
|
|
782
846
|
this.inputQueues = new Map();
|
|
847
|
+
this.composerAdmissions = new Map();
|
|
783
848
|
this.codexTerminalProfileQueues = new Map();
|
|
784
849
|
this.codexTerminalStartQueues = new Map();
|
|
785
850
|
this.codexTerminalStartOutput = new Map();
|
|
@@ -787,6 +852,7 @@ class AgentManager extends EventEmitter {
|
|
|
787
852
|
this.agentLifecycleOperations = new Map();
|
|
788
853
|
this.agentStartAdmissions = new Map();
|
|
789
854
|
this.createRequestAdmissions = new Map();
|
|
855
|
+
this.projectOperationAdmissions = new Map();
|
|
790
856
|
this.projectWorkspaceDeleteAdmissions = new Map();
|
|
791
857
|
this.activeInputOperations = new Set();
|
|
792
858
|
this.verifiedStoppedAgentIds = new Set();
|
|
@@ -1427,9 +1493,12 @@ class AgentManager extends EventEmitter {
|
|
|
1427
1493
|
const recoveredLifecycleOperation = activeLifecycleOperation(agentRecord);
|
|
1428
1494
|
if (
|
|
1429
1495
|
recoveredLifecycleOperation?.type === 'create'
|
|
1430
|
-
&&
|
|
1496
|
+
&& ['pending', 'membership-pending'].includes(recoveredLifecycleOperation.state)
|
|
1431
1497
|
) {
|
|
1432
1498
|
try {
|
|
1499
|
+
if (recoveredLifecycleOperation.state === 'membership-pending') {
|
|
1500
|
+
this.rememberMainPageProviderSession(agentRecord);
|
|
1501
|
+
}
|
|
1433
1502
|
this.transitionPersistentAgentOperation(
|
|
1434
1503
|
agentRecord,
|
|
1435
1504
|
recoveredLifecycleOperation.id,
|
|
@@ -1482,7 +1551,7 @@ class AgentManager extends EventEmitter {
|
|
|
1482
1551
|
changed = true;
|
|
1483
1552
|
}
|
|
1484
1553
|
|
|
1485
|
-
if (this.reconcileMissingTerminalLifecycleOperations(
|
|
1554
|
+
if (await this.reconcileMissingTerminalLifecycleOperations(
|
|
1486
1555
|
persistedRecords,
|
|
1487
1556
|
recoveredRuntimeAgentIds,
|
|
1488
1557
|
)) {
|
|
@@ -1519,7 +1588,7 @@ class AgentManager extends EventEmitter {
|
|
|
1519
1588
|
}
|
|
1520
1589
|
}
|
|
1521
1590
|
|
|
1522
|
-
reconcileMissingTerminalLifecycleOperations(records, recoveredRuntimeAgentIds) {
|
|
1591
|
+
async reconcileMissingTerminalLifecycleOperations(records, recoveredRuntimeAgentIds) {
|
|
1523
1592
|
let changed = false;
|
|
1524
1593
|
for (const record of Array.isArray(records) ? records : []) {
|
|
1525
1594
|
const agentId = String(record?.runtimeAgentId || '').trim();
|
|
@@ -1553,7 +1622,7 @@ class AgentManager extends EventEmitter {
|
|
|
1553
1622
|
operation.request?.previousRuntimeAgentId,
|
|
1554
1623
|
),
|
|
1555
1624
|
);
|
|
1556
|
-
} else {
|
|
1625
|
+
} else if (operation.type === 'delete') {
|
|
1557
1626
|
this.transitionPersistentAgentOperation(
|
|
1558
1627
|
recoveredAgent,
|
|
1559
1628
|
operation.id,
|
|
@@ -1567,7 +1636,27 @@ class AgentManager extends EventEmitter {
|
|
|
1567
1636
|
},
|
|
1568
1637
|
);
|
|
1569
1638
|
this.removeMainPageProviderSessionsForAgents([recoveredAgent]);
|
|
1570
|
-
|
|
1639
|
+
} else {
|
|
1640
|
+
this.transitionPersistentAgentOperation(
|
|
1641
|
+
recoveredAgent,
|
|
1642
|
+
operation.id,
|
|
1643
|
+
'provider-archive-pending',
|
|
1644
|
+
'',
|
|
1645
|
+
{
|
|
1646
|
+
visibleOnMainPage: false,
|
|
1647
|
+
archived: true,
|
|
1648
|
+
archivedAt: Date.now(),
|
|
1649
|
+
runtimeAgentId: '',
|
|
1650
|
+
},
|
|
1651
|
+
);
|
|
1652
|
+
this.removeMainPageProviderSessionsForAgents([recoveredAgent]);
|
|
1653
|
+
const providerArchive = await this.archiveCodexProviderSession(recoveredAgent);
|
|
1654
|
+
this.transitionPersistentAgentOperation(
|
|
1655
|
+
recoveredAgent,
|
|
1656
|
+
operation.id,
|
|
1657
|
+
providerArchive?.error ? 'blocked' : 'succeeded',
|
|
1658
|
+
providerArchive?.error || '',
|
|
1659
|
+
);
|
|
1571
1660
|
}
|
|
1572
1661
|
changed = true;
|
|
1573
1662
|
} catch (error) {
|
|
@@ -1936,11 +2025,16 @@ class AgentManager extends EventEmitter {
|
|
|
1936
2025
|
? this.configManager.getCodexApprovalMode()
|
|
1937
2026
|
: 'approve'
|
|
1938
2027
|
);
|
|
2028
|
+
const recoveryEnv = this.buildAgentEnv(agentId, agent);
|
|
2029
|
+
const recoveryMcpServers = this.projectAcpMcpServers(
|
|
2030
|
+
Array.isArray(record.acpMcpServers) ? record.acpMcpServers : [],
|
|
2031
|
+
recoveryEnv,
|
|
2032
|
+
);
|
|
1939
2033
|
const prepared = await this.acpRuntime.prepareAgent({
|
|
1940
2034
|
agentId,
|
|
1941
2035
|
provider,
|
|
1942
2036
|
executable,
|
|
1943
|
-
env:
|
|
2037
|
+
env: recoveryEnv,
|
|
1944
2038
|
cwd: agent.cwd,
|
|
1945
2039
|
sessionId,
|
|
1946
2040
|
historyMode: 'checkpoint',
|
|
@@ -1951,8 +2045,9 @@ class AgentManager extends EventEmitter {
|
|
|
1951
2045
|
model: 'config',
|
|
1952
2046
|
reasoningEffort: 'config',
|
|
1953
2047
|
serviceTier: 'config',
|
|
2048
|
+
farmingSystemPrompt: renderFarmingAgentBootstrap(),
|
|
1954
2049
|
additionalDirectories: Array.isArray(record.acpAdditionalDirectories) ? record.acpAdditionalDirectories : [],
|
|
1955
|
-
mcpServers:
|
|
2050
|
+
mcpServers: recoveryMcpServers,
|
|
1956
2051
|
onProcessStarted: async processIdentity => {
|
|
1957
2052
|
agent.structuredRuntimeProcess = {
|
|
1958
2053
|
kind: 'acp-process-group',
|
|
@@ -1967,13 +2062,21 @@ class AgentManager extends EventEmitter {
|
|
|
1967
2062
|
prepared.sessionId,
|
|
1968
2063
|
agent.providerHomeId || record.providerHomeId || 'default'
|
|
1969
2064
|
);
|
|
1970
|
-
|
|
2065
|
+
let recoveredSessionOptions = {
|
|
1971
2066
|
additionalDirectories: Array.isArray(record.acpAdditionalDirectories)
|
|
1972
|
-
?
|
|
1973
|
-
: [],
|
|
1974
|
-
mcpServers: Array.isArray(record.acpMcpServers)
|
|
1975
|
-
? JSON.parse(JSON.stringify(record.acpMcpServers))
|
|
2067
|
+
? record.acpAdditionalDirectories
|
|
1976
2068
|
: [],
|
|
2069
|
+
mcpServers: recoveryMcpServers,
|
|
2070
|
+
};
|
|
2071
|
+
try {
|
|
2072
|
+
recoveredSessionOptions = this.acpRuntime.getSessionRequestOptions(agentId);
|
|
2073
|
+
} catch {
|
|
2074
|
+
// The live binding already validated these options. Retain the
|
|
2075
|
+
// projected copy for custom runtimes that do not expose it.
|
|
2076
|
+
}
|
|
2077
|
+
this.acpSessionOptionsByKey.set(agent.providerSessionKey, {
|
|
2078
|
+
additionalDirectories: [...recoveredSessionOptions.additionalDirectories],
|
|
2079
|
+
mcpServers: JSON.parse(JSON.stringify(recoveredSessionOptions.mcpServers)),
|
|
1977
2080
|
});
|
|
1978
2081
|
agent.providerSessionTemporary = false;
|
|
1979
2082
|
agent.providerSessionSource = `acp-${prepared.historyMode}`;
|
|
@@ -2053,7 +2156,16 @@ class AgentManager extends EventEmitter {
|
|
|
2053
2156
|
setAgentRecordId(staged, record.id || '');
|
|
2054
2157
|
staged.structuredRuntimeProcess = null;
|
|
2055
2158
|
try {
|
|
2056
|
-
if (operation.type === 'create') {
|
|
2159
|
+
if (operation.type === 'create' && operation.state === 'membership-pending') {
|
|
2160
|
+
this.rememberMainPageProviderSession(staged);
|
|
2161
|
+
this.transitionPersistentAgentOperation(
|
|
2162
|
+
staged,
|
|
2163
|
+
operation.id,
|
|
2164
|
+
'succeeded',
|
|
2165
|
+
'',
|
|
2166
|
+
{ archived: false },
|
|
2167
|
+
);
|
|
2168
|
+
} else if (operation.type === 'create') {
|
|
2057
2169
|
this.transitionPersistentAgentOperation(
|
|
2058
2170
|
staged,
|
|
2059
2171
|
operation.id,
|
|
@@ -2061,7 +2173,7 @@ class AgentManager extends EventEmitter {
|
|
|
2061
2173
|
'Create ACP process was stopped during restart recovery',
|
|
2062
2174
|
createFailurePatch(operation, operation.request?.previousRuntimeAgentId),
|
|
2063
2175
|
);
|
|
2064
|
-
} else {
|
|
2176
|
+
} else if (operation.type === 'delete') {
|
|
2065
2177
|
this.transitionPersistentAgentOperation(
|
|
2066
2178
|
staged,
|
|
2067
2179
|
operation.id,
|
|
@@ -2076,7 +2188,28 @@ class AgentManager extends EventEmitter {
|
|
|
2076
2188
|
},
|
|
2077
2189
|
);
|
|
2078
2190
|
this.removeMainPageProviderSessionsForAgents([staged]);
|
|
2079
|
-
|
|
2191
|
+
} else {
|
|
2192
|
+
this.transitionPersistentAgentOperation(
|
|
2193
|
+
staged,
|
|
2194
|
+
operation.id,
|
|
2195
|
+
'provider-archive-pending',
|
|
2196
|
+
'',
|
|
2197
|
+
{
|
|
2198
|
+
visibleOnMainPage: false,
|
|
2199
|
+
archived: true,
|
|
2200
|
+
archivedAt: Date.now(),
|
|
2201
|
+
runtimeAgentId: '',
|
|
2202
|
+
structuredRuntimeProcess: null,
|
|
2203
|
+
},
|
|
2204
|
+
);
|
|
2205
|
+
this.removeMainPageProviderSessionsForAgents([staged]);
|
|
2206
|
+
const providerArchive = await this.archiveCodexProviderSession(staged);
|
|
2207
|
+
this.transitionPersistentAgentOperation(
|
|
2208
|
+
staged,
|
|
2209
|
+
operation.id,
|
|
2210
|
+
providerArchive?.error ? 'blocked' : 'succeeded',
|
|
2211
|
+
providerArchive?.error || '',
|
|
2212
|
+
);
|
|
2080
2213
|
}
|
|
2081
2214
|
} catch (error) {
|
|
2082
2215
|
console.warn(
|
|
@@ -2201,6 +2334,7 @@ class AgentManager extends EventEmitter {
|
|
|
2201
2334
|
category: metadata.category || 'coding',
|
|
2202
2335
|
launchPermissionMode: metadata.launchPermissionMode || '',
|
|
2203
2336
|
parentAgentId: metadata.parentAgentId || '',
|
|
2337
|
+
forkRequestId: metadata.forkRequestId || '',
|
|
2204
2338
|
task: metadata.task || '',
|
|
2205
2339
|
workflowTemplate: metadata.workflowTemplate || '',
|
|
2206
2340
|
source: metadata.source || 'recovered',
|
|
@@ -2241,6 +2375,7 @@ class AgentManager extends EventEmitter {
|
|
|
2241
2375
|
agentRecordId,
|
|
2242
2376
|
persistentSessionId: agentRecordId,
|
|
2243
2377
|
lifecycleJournal: lifecycleJournal(metadata),
|
|
2378
|
+
composerCommands: normalizedComposerCommands(metadata.composerCommands),
|
|
2244
2379
|
customTitle: metadata.customTitle || '',
|
|
2245
2380
|
terminalBusy: typeof state.terminalBusy === 'boolean' ? state.terminalBusy : null,
|
|
2246
2381
|
shellCwd: state.shellCwd || metadata.cwd || '',
|
|
@@ -2469,6 +2604,26 @@ class AgentManager extends EventEmitter {
|
|
|
2469
2604
|
return operation;
|
|
2470
2605
|
}
|
|
2471
2606
|
|
|
2607
|
+
completePersistentAgentOperation(agent, operationId, result, patch = {}) {
|
|
2608
|
+
const staged = {
|
|
2609
|
+
...agent,
|
|
2610
|
+
lifecycleJournal: lifecycleJournal(agent),
|
|
2611
|
+
};
|
|
2612
|
+
const operation = setLifecycleOperationResult(staged, operationId, result);
|
|
2613
|
+
if (!operation) throw new Error(`Agent operation ${operationId} was not found`);
|
|
2614
|
+
transitionLifecycleOperation(staged, operationId, 'succeeded');
|
|
2615
|
+
const persistentSessionId = this.ensurePersistentAgentSession(staged, patch);
|
|
2616
|
+
if (
|
|
2617
|
+
typeof this.configManager?.ensureAgentSessionRecord === 'function'
|
|
2618
|
+
&& !persistentSessionId
|
|
2619
|
+
) {
|
|
2620
|
+
throw new Error('Agent session store did not return a persistent id');
|
|
2621
|
+
}
|
|
2622
|
+
agent.lifecycleJournal = staged.lifecycleJournal;
|
|
2623
|
+
setAgentRecordId(agent, staged.agentRecordId || staged.persistentSessionId || '');
|
|
2624
|
+
return operation;
|
|
2625
|
+
}
|
|
2626
|
+
|
|
2472
2627
|
beginPersistentAgentUpdate(agent, requestKey, request) {
|
|
2473
2628
|
const latest = latestLifecycleOperation(agent);
|
|
2474
2629
|
if (
|
|
@@ -2975,6 +3130,7 @@ class AgentManager extends EventEmitter {
|
|
|
2975
3130
|
env.FARMING_AGENT_ID = agentId;
|
|
2976
3131
|
env.FARMING_IS_MAIN_AGENT = agent.wantsMain ? '1' : '0';
|
|
2977
3132
|
env.FARMING_SKILLS_COMMAND = 'farming skills';
|
|
3133
|
+
env.FARMING_CAPABILITIES_COMMAND = 'farming capabilities';
|
|
2978
3134
|
env.FARMING_MAIN_WORKSPACE = agent.mainWorkspace || '';
|
|
2979
3135
|
env.FARMING_PROJECT_WORKSPACE = effectiveAgentWorkspaceRoot(agent);
|
|
2980
3136
|
|
|
@@ -2992,6 +3148,7 @@ class AgentManager extends EventEmitter {
|
|
|
2992
3148
|
}
|
|
2993
3149
|
if (this.configManager && this.configManager.farmingDir) {
|
|
2994
3150
|
env.FARMING_CONFIG_DIR = this.configManager.farmingDir;
|
|
3151
|
+
env.FARMING_STARTUP_PROMPT_FILE = storageLayout.farmingAgentBootstrapFile(this.configManager.farmingDir);
|
|
2995
3152
|
}
|
|
2996
3153
|
if (agent.mainWorkspace) {
|
|
2997
3154
|
env.FARMING_SKILLS_FILE = path.join(agent.mainWorkspace, 'FARMING_MAIN_AGENT_SKILLS.md');
|
|
@@ -3004,10 +3161,22 @@ class AgentManager extends EventEmitter {
|
|
|
3004
3161
|
agent.providerHomePath
|
|
3005
3162
|
);
|
|
3006
3163
|
}
|
|
3164
|
+
const provider = agent.providerSessionProvider || agentHomeProviderForProgram(agent.forkCommand || agent.command);
|
|
3165
|
+
if (provider === 'opencode' && env.FARMING_STARTUP_PROMPT_FILE) {
|
|
3166
|
+
Object.assign(env, appendOpenCodeBootstrap(env, env.FARMING_STARTUP_PROMPT_FILE));
|
|
3167
|
+
}
|
|
3007
3168
|
|
|
3008
3169
|
return env;
|
|
3009
3170
|
}
|
|
3010
3171
|
|
|
3172
|
+
projectAcpMcpServers(mcpServers, agentEnv) {
|
|
3173
|
+
if (!this.browserMcpEnabled) return Array.isArray(mcpServers) ? mcpServers : [];
|
|
3174
|
+
return mergeBrowserMcpServer(mcpServers, {
|
|
3175
|
+
cliBinDir: this.cliBinDir,
|
|
3176
|
+
agentEnv,
|
|
3177
|
+
});
|
|
3178
|
+
}
|
|
3179
|
+
|
|
3011
3180
|
expandWorkspacePath(workspace) {
|
|
3012
3181
|
if (typeof workspace !== 'string') return '';
|
|
3013
3182
|
const value = workspace.trim();
|
|
@@ -3253,6 +3422,7 @@ class AgentManager extends EventEmitter {
|
|
|
3253
3422
|
category: agent.category,
|
|
3254
3423
|
launchPermissionMode: agent.launchPermissionMode,
|
|
3255
3424
|
parentAgentId: agent.parentAgentId || '',
|
|
3425
|
+
forkRequestId: agent.forkRequestId || '',
|
|
3256
3426
|
task: agent.task,
|
|
3257
3427
|
workflowTemplate: agent.workflowTemplate,
|
|
3258
3428
|
source: agent.source,
|
|
@@ -3615,6 +3785,7 @@ class AgentManager extends EventEmitter {
|
|
|
3615
3785
|
codexModel,
|
|
3616
3786
|
codexReasoningEffort,
|
|
3617
3787
|
codexServiceTier,
|
|
3788
|
+
farmingSystemPrompt: renderFarmingAgentBootstrap(),
|
|
3618
3789
|
mainAgentSystemPrompt: wantsMain ? renderMainAgentBootstrap() : '',
|
|
3619
3790
|
});
|
|
3620
3791
|
const program = launch.program;
|
|
@@ -3863,6 +4034,7 @@ class AgentManager extends EventEmitter {
|
|
|
3863
4034
|
category: resolution.spec ? resolution.spec.category : 'other',
|
|
3864
4035
|
launchPermissionMode: launch.permissionMode || '',
|
|
3865
4036
|
parentAgentId,
|
|
4037
|
+
forkRequestId: typeof options.forkRequestId === 'string' ? options.forkRequestId : '',
|
|
3866
4038
|
task: typeof options.task === 'string' ? options.task : '',
|
|
3867
4039
|
workflowTemplate: typeof options.workflowTemplate === 'string' ? options.workflowTemplate : '',
|
|
3868
4040
|
source: typeof options.source === 'string' ? options.source : 'ui',
|
|
@@ -4076,7 +4248,10 @@ class AgentManager extends EventEmitter {
|
|
|
4076
4248
|
const requestedAdditionalDirectories = Array.isArray(options.additionalDirectories)
|
|
4077
4249
|
? options.additionalDirectories
|
|
4078
4250
|
: [];
|
|
4079
|
-
const requestedMcpServers =
|
|
4251
|
+
const requestedMcpServers = this.projectAcpMcpServers(
|
|
4252
|
+
Array.isArray(options.mcpServers) ? options.mcpServers : [],
|
|
4253
|
+
identityEnv,
|
|
4254
|
+
);
|
|
4080
4255
|
const created = await this.createProviderSessionIdentity({
|
|
4081
4256
|
provider: providerSessionPlan.provider,
|
|
4082
4257
|
executable: spawnProgram,
|
|
@@ -4087,6 +4262,7 @@ class AgentManager extends EventEmitter {
|
|
|
4087
4262
|
model: codexModel,
|
|
4088
4263
|
reasoningEffort: codexReasoningEffort,
|
|
4089
4264
|
serviceTier: codexServiceTier,
|
|
4265
|
+
farmingSystemPrompt: renderFarmingAgentBootstrap(),
|
|
4090
4266
|
additionalDirectories: requestedAdditionalDirectories,
|
|
4091
4267
|
mcpServers: requestedMcpServers,
|
|
4092
4268
|
});
|
|
@@ -4230,14 +4406,16 @@ class AgentManager extends EventEmitter {
|
|
|
4230
4406
|
const additionalDirectories = Array.isArray(options.additionalDirectories)
|
|
4231
4407
|
? options.additionalDirectories
|
|
4232
4408
|
: rememberedSessionOptions.additionalDirectories || [];
|
|
4233
|
-
const
|
|
4409
|
+
const requestedMcpServers = Array.isArray(options.mcpServers)
|
|
4234
4410
|
? options.mcpServers
|
|
4235
4411
|
: rememberedSessionOptions.mcpServers || [];
|
|
4412
|
+
const acpEnv = this.buildAgentEnv(agentId, agentRecord);
|
|
4413
|
+
const mcpServers = this.projectAcpMcpServers(requestedMcpServers, acpEnv);
|
|
4236
4414
|
const prepared = await this.acpRuntime.prepareAgent({
|
|
4237
4415
|
agentId,
|
|
4238
4416
|
provider: structuredRuntimeProvider,
|
|
4239
4417
|
executable: spawnProgram,
|
|
4240
|
-
env:
|
|
4418
|
+
env: acpEnv,
|
|
4241
4419
|
cwd: workspace,
|
|
4242
4420
|
sessionId: options.acpStartFresh === true || agentRecord.providerSessionTemporary || acpGeneratedFreshSession
|
|
4243
4421
|
? ''
|
|
@@ -4250,6 +4428,7 @@ class AgentManager extends EventEmitter {
|
|
|
4250
4428
|
model: codexModel,
|
|
4251
4429
|
reasoningEffort: codexReasoningEffort,
|
|
4252
4430
|
serviceTier: codexServiceTier,
|
|
4431
|
+
farmingSystemPrompt: renderFarmingAgentBootstrap(),
|
|
4253
4432
|
additionalDirectories,
|
|
4254
4433
|
mcpServers,
|
|
4255
4434
|
forkSourceSessionId: options.acpForkSourceSessionId || '',
|
|
@@ -4350,13 +4529,18 @@ class AgentManager extends EventEmitter {
|
|
|
4350
4529
|
await startTerminal();
|
|
4351
4530
|
}
|
|
4352
4531
|
}
|
|
4353
|
-
this.transitionPersistentAgentOperation(agentRecord, createOperationId, '
|
|
4532
|
+
this.transitionPersistentAgentOperation(agentRecord, createOperationId, 'membership-pending', '', {
|
|
4354
4533
|
visibleOnMainPage: true,
|
|
4355
4534
|
archived: false,
|
|
4356
4535
|
...(options.customTitleExplicit === true
|
|
4357
4536
|
? { customTitle: agentRecord.customTitle }
|
|
4358
4537
|
: {}),
|
|
4359
4538
|
});
|
|
4539
|
+
this.rememberMainPageProviderSession(agentRecord);
|
|
4540
|
+
this.transitionPersistentAgentOperation(agentRecord, createOperationId, 'succeeded', '', {
|
|
4541
|
+
visibleOnMainPage: true,
|
|
4542
|
+
archived: false,
|
|
4543
|
+
});
|
|
4360
4544
|
if (
|
|
4361
4545
|
previousPersistentRuntimeAgentId
|
|
4362
4546
|
&& previousPersistentRuntimeAgentId !== agentId
|
|
@@ -4576,7 +4760,171 @@ class AgentManager extends EventEmitter {
|
|
|
4576
4760
|
return this.enqueueInputOperation(agentId, () => this.sendInputNow(agentId, input, options));
|
|
4577
4761
|
}
|
|
4578
4762
|
|
|
4579
|
-
|
|
4763
|
+
commitComposerCommand(agent, command) {
|
|
4764
|
+
const commands = normalizedComposerCommands(agent.composerCommands)
|
|
4765
|
+
.filter(candidate => candidate.requestId !== command.requestId);
|
|
4766
|
+
commands.push(command);
|
|
4767
|
+
const staged = {
|
|
4768
|
+
...agent,
|
|
4769
|
+
composerCommands: normalizedComposerCommands(commands),
|
|
4770
|
+
};
|
|
4771
|
+
const persistentSessionId = this.ensurePersistentAgentSession(staged);
|
|
4772
|
+
if (
|
|
4773
|
+
typeof this.configManager?.ensureAgentSessionRecord === 'function'
|
|
4774
|
+
&& !persistentSessionId
|
|
4775
|
+
) {
|
|
4776
|
+
throw new Error('Agent session store did not return a persistent id');
|
|
4777
|
+
}
|
|
4778
|
+
agent.composerCommands = staged.composerCommands;
|
|
4779
|
+
setAgentRecordId(agent, staged.agentRecordId || staged.persistentSessionId || '');
|
|
4780
|
+
return command;
|
|
4781
|
+
}
|
|
4782
|
+
|
|
4783
|
+
setComposerCommandInMemory(agent, command) {
|
|
4784
|
+
agent.composerCommands = normalizedComposerCommands([
|
|
4785
|
+
...normalizedComposerCommands(agent.composerCommands)
|
|
4786
|
+
.filter(candidate => candidate.requestId !== command.requestId),
|
|
4787
|
+
command,
|
|
4788
|
+
]);
|
|
4789
|
+
}
|
|
4790
|
+
|
|
4791
|
+
sendPersistentComposerMessage(agentId, message, requestId) {
|
|
4792
|
+
const agent = this.agents.get(agentId);
|
|
4793
|
+
if (!agent) return Promise.reject(new Error('Agent not found'));
|
|
4794
|
+
if (runtimeKind(agent) === 'terminal') {
|
|
4795
|
+
return Promise.reject(new Error('Persistent Composer admission requires a structured runtime'));
|
|
4796
|
+
}
|
|
4797
|
+
const prompt = normalizedComposerPrompt(message);
|
|
4798
|
+
const contentHash = composerCommandHash(prompt);
|
|
4799
|
+
const commands = normalizedComposerCommands(agent.composerCommands);
|
|
4800
|
+
const existing = commands.find(command => command.requestId === requestId);
|
|
4801
|
+
const admissionKey = `${agentId}:${requestId}`;
|
|
4802
|
+
const inFlight = this.composerAdmissions.get(admissionKey);
|
|
4803
|
+
if (existing?.contentHash && existing.contentHash !== contentHash) {
|
|
4804
|
+
return Promise.reject(new Error(`Composer request ${requestId} was already used for different content`));
|
|
4805
|
+
}
|
|
4806
|
+
if (existing?.state === 'accepted') {
|
|
4807
|
+
return Promise.resolve({ ...(existing.result || {}), accepted: true, deduplicated: true });
|
|
4808
|
+
}
|
|
4809
|
+
if (inFlight) return inFlight.promise;
|
|
4810
|
+
if (existing?.state === 'unknown' || existing?.state === 'intent') {
|
|
4811
|
+
const detail = existing.error || `Composer request ${requestId} has an uncertain outcome and will not be replayed automatically`;
|
|
4812
|
+
if (existing.state === 'intent') {
|
|
4813
|
+
const unknown = { ...existing, state: 'unknown', error: detail, updatedAt: Date.now() };
|
|
4814
|
+
try {
|
|
4815
|
+
this.commitComposerCommand(agent, unknown);
|
|
4816
|
+
} catch {
|
|
4817
|
+
this.setComposerCommandInMemory(agent, unknown);
|
|
4818
|
+
}
|
|
4819
|
+
}
|
|
4820
|
+
return Promise.reject(composerAdmissionError(detail, true));
|
|
4821
|
+
}
|
|
4822
|
+
if (existing?.state === 'failed') {
|
|
4823
|
+
return Promise.reject(new Error(existing.error || `Composer request ${requestId} was not accepted`));
|
|
4824
|
+
}
|
|
4825
|
+
|
|
4826
|
+
const intent = {
|
|
4827
|
+
requestId,
|
|
4828
|
+
contentHash,
|
|
4829
|
+
state: 'intent',
|
|
4830
|
+
result: null,
|
|
4831
|
+
error: '',
|
|
4832
|
+
createdAt: Date.now(),
|
|
4833
|
+
updatedAt: Date.now(),
|
|
4834
|
+
};
|
|
4835
|
+
try {
|
|
4836
|
+
this.commitComposerCommand(agent, intent);
|
|
4837
|
+
} catch (error) {
|
|
4838
|
+
return Promise.reject(new Error(`Failed to persist Composer intent: ${error.message || error}`));
|
|
4839
|
+
}
|
|
4840
|
+
|
|
4841
|
+
let resolveAdmission;
|
|
4842
|
+
let rejectAdmission;
|
|
4843
|
+
const admissionPromise = new Promise((resolve, reject) => {
|
|
4844
|
+
resolveAdmission = resolve;
|
|
4845
|
+
rejectAdmission = reject;
|
|
4846
|
+
});
|
|
4847
|
+
const entry = { contentHash, promise: admissionPromise };
|
|
4848
|
+
this.composerAdmissions.set(admissionKey, entry);
|
|
4849
|
+
let submitted = false;
|
|
4850
|
+
const onSubmitted = (result = { kind: runtimeKind(agent) }) => {
|
|
4851
|
+
if (submitted) return;
|
|
4852
|
+
submitted = true;
|
|
4853
|
+
const accepted = {
|
|
4854
|
+
...intent,
|
|
4855
|
+
state: 'accepted',
|
|
4856
|
+
result,
|
|
4857
|
+
updatedAt: Date.now(),
|
|
4858
|
+
};
|
|
4859
|
+
try {
|
|
4860
|
+
this.commitComposerCommand(agent, accepted);
|
|
4861
|
+
resolveAdmission({ ...result, accepted: true });
|
|
4862
|
+
} catch (error) {
|
|
4863
|
+
const unknown = {
|
|
4864
|
+
...intent,
|
|
4865
|
+
state: 'unknown',
|
|
4866
|
+
error: `Provider accepted Composer request, but admission could not be saved: ${error.message || error}`,
|
|
4867
|
+
updatedAt: Date.now(),
|
|
4868
|
+
};
|
|
4869
|
+
this.setComposerCommandInMemory(agent, unknown);
|
|
4870
|
+
rejectAdmission(composerAdmissionError(unknown.error, true));
|
|
4871
|
+
}
|
|
4872
|
+
};
|
|
4873
|
+
|
|
4874
|
+
const completion = isAcpAgent(agent)
|
|
4875
|
+
? this.enqueueInputOperationUntilReleased(
|
|
4876
|
+
agentId,
|
|
4877
|
+
releaseInput => this.sendComposerMessageNow(agentId, prompt, {
|
|
4878
|
+
onSubmitted: () => {
|
|
4879
|
+
try {
|
|
4880
|
+
onSubmitted({ kind: 'acp' });
|
|
4881
|
+
} finally {
|
|
4882
|
+
releaseInput();
|
|
4883
|
+
}
|
|
4884
|
+
},
|
|
4885
|
+
}),
|
|
4886
|
+
)
|
|
4887
|
+
: this.enqueueInputOperation(
|
|
4888
|
+
agentId,
|
|
4889
|
+
() => this.sendComposerMessageNow(agentId, prompt, {
|
|
4890
|
+
onSubmitted: result => onSubmitted(result),
|
|
4891
|
+
}),
|
|
4892
|
+
);
|
|
4893
|
+
void Promise.resolve(completion).then(result => {
|
|
4894
|
+
if (!submitted) onSubmitted(result);
|
|
4895
|
+
}).catch(error => {
|
|
4896
|
+
if (submitted) return;
|
|
4897
|
+
const failed = {
|
|
4898
|
+
...intent,
|
|
4899
|
+
state: 'failed',
|
|
4900
|
+
error: error.message || String(error),
|
|
4901
|
+
updatedAt: Date.now(),
|
|
4902
|
+
};
|
|
4903
|
+
let uncertain = false;
|
|
4904
|
+
try {
|
|
4905
|
+
this.commitComposerCommand(agent, failed);
|
|
4906
|
+
} catch (persistError) {
|
|
4907
|
+
failed.state = 'unknown';
|
|
4908
|
+
failed.error = `${failed.error}; failed to persist rejection: ${persistError.message || persistError}`;
|
|
4909
|
+
this.setComposerCommandInMemory(agent, failed);
|
|
4910
|
+
uncertain = true;
|
|
4911
|
+
}
|
|
4912
|
+
rejectAdmission(composerAdmissionError(failed.error, uncertain));
|
|
4913
|
+
});
|
|
4914
|
+
void admissionPromise.finally(() => {
|
|
4915
|
+
if (this.composerAdmissions.get(admissionKey) === entry) {
|
|
4916
|
+
this.composerAdmissions.delete(admissionKey);
|
|
4917
|
+
}
|
|
4918
|
+
}).catch(() => {});
|
|
4919
|
+
return admissionPromise;
|
|
4920
|
+
}
|
|
4921
|
+
|
|
4922
|
+
async sendComposerMessage(agentId, message, options = {}) {
|
|
4923
|
+
const requestId = String(options.requestId || '').trim();
|
|
4924
|
+
if (requestId) {
|
|
4925
|
+
if (!/^[A-Za-z0-9._:-]{1,160}$/.test(requestId)) throw new Error('Composer requestId is invalid');
|
|
4926
|
+
return this.sendPersistentComposerMessage(agentId, message, requestId);
|
|
4927
|
+
}
|
|
4580
4928
|
const agent = this.agents.get(agentId);
|
|
4581
4929
|
if (agent && isAcpAgent(agent)) {
|
|
4582
4930
|
return this.enqueueInputOperationUntilReleased(
|
|
@@ -4695,13 +5043,15 @@ class AgentManager extends EventEmitter {
|
|
|
4695
5043
|
agent.providerSessionId = result.sessionId || agent.providerSessionId;
|
|
4696
5044
|
agent.providerSessionTemporary = !agent.providerSessionId;
|
|
4697
5045
|
this.ensurePersistentAgentSession(agent);
|
|
4698
|
-
|
|
5046
|
+
const submitted = { kind: 'json', sessionId: agent.providerSessionId };
|
|
5047
|
+
options.onSubmitted?.(submitted);
|
|
5048
|
+
return submitted;
|
|
4699
5049
|
}
|
|
4700
5050
|
|
|
4701
5051
|
if (isAcpAgent(agent)) {
|
|
4702
5052
|
this.requireLiveAcpAgent(agentId);
|
|
4703
5053
|
const result = await this.acpRuntime.submitMessage(agentId, prompt, {
|
|
4704
|
-
onSubmitted: options.releaseInput,
|
|
5054
|
+
onSubmitted: options.onSubmitted || options.releaseInput,
|
|
4705
5055
|
});
|
|
4706
5056
|
if (result.steered !== true) {
|
|
4707
5057
|
const runtime = runtimeBindingOf(agent, 'acp');
|
|
@@ -4713,7 +5063,9 @@ class AgentManager extends EventEmitter {
|
|
|
4713
5063
|
}
|
|
4714
5064
|
|
|
4715
5065
|
await this.sendInputNow(agentId, [{ type: 'paste', text }, '\r']);
|
|
4716
|
-
|
|
5066
|
+
const submitted = { kind: 'terminal' };
|
|
5067
|
+
options.onSubmitted?.(submitted);
|
|
5068
|
+
return submitted;
|
|
4717
5069
|
}
|
|
4718
5070
|
|
|
4719
5071
|
getJsonCliTranscript(agentId, options = {}) {
|
|
@@ -5942,6 +6294,104 @@ class AgentManager extends EventEmitter {
|
|
|
5942
6294
|
: this.markAgentReadCursor(agentId);
|
|
5943
6295
|
}
|
|
5944
6296
|
|
|
6297
|
+
persistentProjectOperation(requestId, type, signature, request) {
|
|
6298
|
+
if (
|
|
6299
|
+
!requestId
|
|
6300
|
+
|| typeof this.configManager?.getProjectOperation !== 'function'
|
|
6301
|
+
|| typeof this.configManager?.commitProjectOperation !== 'function'
|
|
6302
|
+
) {
|
|
6303
|
+
return { operation: null, created: true };
|
|
6304
|
+
}
|
|
6305
|
+
const existing = this.configManager.getProjectOperation(requestId);
|
|
6306
|
+
if (existing) {
|
|
6307
|
+
if (existing.type !== type || existing.signature !== signature) {
|
|
6308
|
+
return { error: `Project operation request ${requestId} was already used for different parameters` };
|
|
6309
|
+
}
|
|
6310
|
+
return { operation: existing, created: false };
|
|
6311
|
+
}
|
|
6312
|
+
const operation = {
|
|
6313
|
+
id: requestId,
|
|
6314
|
+
type,
|
|
6315
|
+
state: 'pending',
|
|
6316
|
+
signature,
|
|
6317
|
+
request,
|
|
6318
|
+
result: null,
|
|
6319
|
+
error: '',
|
|
6320
|
+
startedAt: Date.now(),
|
|
6321
|
+
updatedAt: Date.now(),
|
|
6322
|
+
finishedAt: null,
|
|
6323
|
+
};
|
|
6324
|
+
try {
|
|
6325
|
+
this.configManager.commitProjectOperation(operation);
|
|
6326
|
+
return { operation, created: true };
|
|
6327
|
+
} catch (error) {
|
|
6328
|
+
return { error: `Failed to persist Project operation intent: ${error.message || error}` };
|
|
6329
|
+
}
|
|
6330
|
+
}
|
|
6331
|
+
|
|
6332
|
+
commitPersistentProjectOperation(operation, state, result = null, error = '', membership = {}) {
|
|
6333
|
+
if (!operation || typeof this.configManager?.commitProjectOperation !== 'function') {
|
|
6334
|
+
return { operation: null, projectWorkspaces: null, pinnedProjectWorkspaces: null };
|
|
6335
|
+
}
|
|
6336
|
+
const terminal = ['succeeded', 'failed', 'blocked'].includes(state);
|
|
6337
|
+
const nextOperation = {
|
|
6338
|
+
...operation,
|
|
6339
|
+
state,
|
|
6340
|
+
result,
|
|
6341
|
+
error,
|
|
6342
|
+
updatedAt: Date.now(),
|
|
6343
|
+
finishedAt: terminal ? Date.now() : null,
|
|
6344
|
+
};
|
|
6345
|
+
return this.configManager.commitProjectOperation(nextOperation, membership);
|
|
6346
|
+
}
|
|
6347
|
+
|
|
6348
|
+
async listGitWorktrees(sourceWorkspace) {
|
|
6349
|
+
const { stdout } = await execFileAsync('git', ['-C', sourceWorkspace, 'worktree', 'list', '--porcelain'], {
|
|
6350
|
+
timeout: 15000,
|
|
6351
|
+
maxBuffer: 1024 * 1024 * 4,
|
|
6352
|
+
});
|
|
6353
|
+
return worktreesFromPorcelain(stdout);
|
|
6354
|
+
}
|
|
6355
|
+
|
|
6356
|
+
async inspectGitWorktreePostcondition(sourceWorkspace, workspace, branch = '') {
|
|
6357
|
+
const target = path.resolve(workspace);
|
|
6358
|
+
let worktrees;
|
|
6359
|
+
try {
|
|
6360
|
+
worktrees = await this.listGitWorktrees(sourceWorkspace);
|
|
6361
|
+
} catch (error) {
|
|
6362
|
+
return { proven: false, error: error.message || String(error) };
|
|
6363
|
+
}
|
|
6364
|
+
const registered = worktrees.find(entry => entry.workspace === target) || null;
|
|
6365
|
+
let exists = false;
|
|
6366
|
+
try {
|
|
6367
|
+
exists = fs.statSync(target).isDirectory();
|
|
6368
|
+
} catch {
|
|
6369
|
+
exists = false;
|
|
6370
|
+
}
|
|
6371
|
+
let branchExists = false;
|
|
6372
|
+
if (branch) {
|
|
6373
|
+
try {
|
|
6374
|
+
await execFileAsync('git', ['-C', sourceWorkspace, 'show-ref', '--verify', '--quiet', `refs/heads/${branch}`], {
|
|
6375
|
+
timeout: 15000,
|
|
6376
|
+
maxBuffer: 1024 * 1024,
|
|
6377
|
+
});
|
|
6378
|
+
branchExists = true;
|
|
6379
|
+
} catch (error) {
|
|
6380
|
+
if (error?.code !== 1) {
|
|
6381
|
+
return { proven: false, error: error.message || String(error) };
|
|
6382
|
+
}
|
|
6383
|
+
}
|
|
6384
|
+
}
|
|
6385
|
+
return {
|
|
6386
|
+
proven: true,
|
|
6387
|
+
exists,
|
|
6388
|
+
registered: Boolean(registered),
|
|
6389
|
+
branchMatches: !branch || registered?.branch === branch,
|
|
6390
|
+
branchExists,
|
|
6391
|
+
worktree: registered,
|
|
6392
|
+
};
|
|
6393
|
+
}
|
|
6394
|
+
|
|
5945
6395
|
async resolveGitWorktreeSourceRoot(workspace) {
|
|
5946
6396
|
const sourceWorkspace = this.expandWorkspacePath(workspace);
|
|
5947
6397
|
if (!sourceWorkspace) {
|
|
@@ -5986,47 +6436,182 @@ class AgentManager extends EventEmitter {
|
|
|
5986
6436
|
return target;
|
|
5987
6437
|
}
|
|
5988
6438
|
|
|
5989
|
-
|
|
6439
|
+
createPermanentWorktree(workspace, options = {}) {
|
|
6440
|
+
const requestId = String(options.requestId || '').trim().slice(0, 160);
|
|
6441
|
+
if (!requestId) return this.createPermanentWorktreeAdmitted(workspace, options);
|
|
6442
|
+
const workspaceKey = canonicalWorkspacePath(this.expandWorkspacePath(workspace));
|
|
6443
|
+
const current = this.projectOperationAdmissions.get(requestId);
|
|
6444
|
+
if (current) {
|
|
6445
|
+
if (current.workspaceKey === workspaceKey) return current.promise;
|
|
6446
|
+
return Promise.reject(new Error(`Project operation request ${requestId} was already used for different parameters`));
|
|
6447
|
+
}
|
|
6448
|
+
const promise = this.createPermanentWorktreeAdmitted(workspace, options);
|
|
6449
|
+
const admission = { workspaceKey, promise };
|
|
6450
|
+
this.projectOperationAdmissions.set(requestId, admission);
|
|
6451
|
+
void promise.finally(() => {
|
|
6452
|
+
if (this.projectOperationAdmissions.get(requestId) === admission) {
|
|
6453
|
+
this.projectOperationAdmissions.delete(requestId);
|
|
6454
|
+
}
|
|
6455
|
+
}).catch(() => {});
|
|
6456
|
+
return promise;
|
|
6457
|
+
}
|
|
6458
|
+
|
|
6459
|
+
async createPermanentWorktreeAdmitted(workspace, options = {}) {
|
|
5990
6460
|
const root = await this.resolveGitWorktreeSourceRoot(workspace);
|
|
6461
|
+
const requestId = String(options.requestId || '').trim().slice(0, 160);
|
|
6462
|
+
const signature = projectOperationSignature({ sourceWorkspace: root, type: 'create-worktree' });
|
|
6463
|
+
const existingOperation = requestId && typeof this.configManager?.getProjectOperation === 'function'
|
|
6464
|
+
? this.configManager.getProjectOperation(requestId)
|
|
6465
|
+
: null;
|
|
6466
|
+
const operationWasExisting = Boolean(existingOperation);
|
|
6467
|
+
if (
|
|
6468
|
+
existingOperation
|
|
6469
|
+
&& (
|
|
6470
|
+
existingOperation.type !== 'create-worktree'
|
|
6471
|
+
|| existingOperation.signature !== signature
|
|
6472
|
+
)
|
|
6473
|
+
) {
|
|
6474
|
+
throw new Error(`Project operation request ${requestId} was already used for different parameters`);
|
|
6475
|
+
}
|
|
6476
|
+
if (existingOperation?.state === 'succeeded' && existingOperation.result) {
|
|
6477
|
+
const settings = this.configManager.getSettings?.() || {};
|
|
6478
|
+
return {
|
|
6479
|
+
...existingOperation.result,
|
|
6480
|
+
deduplicated: true,
|
|
6481
|
+
projectWorkspaces: settings.projectWorkspaces || [],
|
|
6482
|
+
pinnedProjectWorkspaces: settings.pinnedProjectWorkspaces || [],
|
|
6483
|
+
};
|
|
6484
|
+
}
|
|
6485
|
+
|
|
5991
6486
|
const parentDir = path.dirname(root);
|
|
5992
6487
|
const baseName = path.basename(root);
|
|
5993
|
-
|
|
5994
|
-
let
|
|
5995
|
-
|
|
5996
|
-
|
|
5997
|
-
const
|
|
5998
|
-
|
|
5999
|
-
|
|
6000
|
-
|
|
6001
|
-
|
|
6002
|
-
|
|
6003
|
-
|
|
6004
|
-
maxBuffer: 1024 * 1024,
|
|
6005
|
-
});
|
|
6006
|
-
branchExists = true;
|
|
6007
|
-
} catch (error) {
|
|
6008
|
-
if (error?.code !== 1) {
|
|
6009
|
-
const message = error && error.stderr ? String(error.stderr).trim() : '';
|
|
6010
|
-
throw new Error(message || 'Failed to inspect git branches', { cause: error });
|
|
6011
|
-
}
|
|
6012
|
-
}
|
|
6013
|
-
|
|
6014
|
-
if (!fs.existsSync(target) && !branchExists) {
|
|
6488
|
+
let target = String(existingOperation?.request?.workspace || '');
|
|
6489
|
+
let branch = String(existingOperation?.request?.branch || '');
|
|
6490
|
+
let operation = existingOperation;
|
|
6491
|
+
if (!operation) {
|
|
6492
|
+
const slug = timestampSlug();
|
|
6493
|
+
let suffix = 1;
|
|
6494
|
+
while (suffix < 1000) {
|
|
6495
|
+
const suffixText = suffix === 1 ? '' : `-${suffix}`;
|
|
6496
|
+
const candidateTarget = path.join(parentDir, `${baseName}-farming-worktree-${slug}${suffixText}`);
|
|
6497
|
+
const candidateBranch = `farming/worktree-${slug}${suffixText}`;
|
|
6498
|
+
let branchExists = false;
|
|
6015
6499
|
try {
|
|
6016
|
-
await execFileAsync('git', ['-C', root, '
|
|
6017
|
-
timeout:
|
|
6018
|
-
maxBuffer: 1024 * 1024
|
|
6500
|
+
await execFileAsync('git', ['-C', root, 'show-ref', '--verify', '--quiet', `refs/heads/${candidateBranch}`], {
|
|
6501
|
+
timeout: 15000,
|
|
6502
|
+
maxBuffer: 1024 * 1024,
|
|
6019
6503
|
});
|
|
6504
|
+
branchExists = true;
|
|
6020
6505
|
} catch (error) {
|
|
6021
|
-
|
|
6022
|
-
|
|
6506
|
+
if (error?.code !== 1) {
|
|
6507
|
+
const message = error && error.stderr ? String(error.stderr).trim() : '';
|
|
6508
|
+
throw new Error(message || 'Failed to inspect git branches', { cause: error });
|
|
6509
|
+
}
|
|
6510
|
+
}
|
|
6511
|
+
|
|
6512
|
+
if (!fs.existsSync(candidateTarget) && !branchExists) {
|
|
6513
|
+
target = candidateTarget;
|
|
6514
|
+
branch = candidateBranch;
|
|
6515
|
+
break;
|
|
6023
6516
|
}
|
|
6024
|
-
|
|
6517
|
+
suffix += 1;
|
|
6025
6518
|
}
|
|
6026
|
-
|
|
6519
|
+
if (!target || !branch) throw new Error('Unable to allocate a permanent worktree name');
|
|
6520
|
+
const admission = this.persistentProjectOperation(requestId, 'create-worktree', signature, {
|
|
6521
|
+
sourceWorkspace: root,
|
|
6522
|
+
workspace: target,
|
|
6523
|
+
branch,
|
|
6524
|
+
});
|
|
6525
|
+
if (admission.error) throw new Error(admission.error);
|
|
6526
|
+
operation = admission.operation;
|
|
6527
|
+
}
|
|
6528
|
+
|
|
6529
|
+
const commitSuccess = () => {
|
|
6530
|
+
const result = { workspace: target, branch, sourceWorkspace: root, ...(requestId ? { requestId } : {}) };
|
|
6531
|
+
if (!operation) return result;
|
|
6532
|
+
const committed = this.commitPersistentProjectOperation(
|
|
6533
|
+
operation,
|
|
6534
|
+
'succeeded',
|
|
6535
|
+
result,
|
|
6536
|
+
'',
|
|
6537
|
+
{ mountWorkspace: target },
|
|
6538
|
+
);
|
|
6539
|
+
return {
|
|
6540
|
+
...result,
|
|
6541
|
+
projectWorkspaces: committed.projectWorkspaces,
|
|
6542
|
+
pinnedProjectWorkspaces: committed.pinnedProjectWorkspaces,
|
|
6543
|
+
};
|
|
6544
|
+
};
|
|
6545
|
+
|
|
6546
|
+
if (operation && !['pending', 'unknown'].includes(operation.state)) {
|
|
6547
|
+
throw new Error(operation.error || `Project operation ${requestId} already finished with state ${operation.state}`);
|
|
6548
|
+
}
|
|
6549
|
+
if (operation && operationWasExisting) {
|
|
6550
|
+
const postcondition = await this.inspectGitWorktreePostcondition(root, target, branch);
|
|
6551
|
+
if (
|
|
6552
|
+
postcondition.proven
|
|
6553
|
+
&& postcondition.exists
|
|
6554
|
+
&& postcondition.registered
|
|
6555
|
+
&& postcondition.branchMatches
|
|
6556
|
+
) {
|
|
6557
|
+
return commitSuccess();
|
|
6558
|
+
}
|
|
6559
|
+
if (operation.state === 'unknown') {
|
|
6560
|
+
throw new Error(operation.error || 'Permanent worktree creation has an uncertain outcome and will not be replayed automatically');
|
|
6561
|
+
}
|
|
6562
|
+
if (
|
|
6563
|
+
!postcondition.proven
|
|
6564
|
+
|| postcondition.exists
|
|
6565
|
+
|| postcondition.registered
|
|
6566
|
+
|| postcondition.branchExists
|
|
6567
|
+
) {
|
|
6568
|
+
const detail = postcondition.error || 'Permanent worktree creation has a partial or unverifiable result';
|
|
6569
|
+
this.commitPersistentProjectOperation(operation, 'unknown', null, detail);
|
|
6570
|
+
throw new Error(`${detail}; the operation will not be replayed automatically`);
|
|
6571
|
+
}
|
|
6572
|
+
}
|
|
6573
|
+
|
|
6574
|
+
try {
|
|
6575
|
+
await execFileAsync('git', ['-C', root, 'worktree', 'add', '-b', branch, target, 'HEAD'], {
|
|
6576
|
+
timeout: 60000,
|
|
6577
|
+
maxBuffer: 1024 * 1024 * 4,
|
|
6578
|
+
});
|
|
6579
|
+
} catch (error) {
|
|
6580
|
+
const postcondition = await this.inspectGitWorktreePostcondition(root, target, branch);
|
|
6581
|
+
if (
|
|
6582
|
+
postcondition.proven
|
|
6583
|
+
&& postcondition.exists
|
|
6584
|
+
&& postcondition.registered
|
|
6585
|
+
&& postcondition.branchMatches
|
|
6586
|
+
) {
|
|
6587
|
+
return commitSuccess();
|
|
6588
|
+
}
|
|
6589
|
+
const message = error && error.stderr ? String(error.stderr).trim() : '';
|
|
6590
|
+
const detail = message || 'Failed to create permanent git worktree';
|
|
6591
|
+
if (operation) {
|
|
6592
|
+
const state = postcondition.proven
|
|
6593
|
+
&& !postcondition.exists
|
|
6594
|
+
&& !postcondition.registered
|
|
6595
|
+
&& !postcondition.branchExists
|
|
6596
|
+
? 'failed'
|
|
6597
|
+
: 'unknown';
|
|
6598
|
+
this.commitPersistentProjectOperation(operation, state, null, detail);
|
|
6599
|
+
}
|
|
6600
|
+
throw new Error(detail, { cause: error });
|
|
6027
6601
|
}
|
|
6028
6602
|
|
|
6029
|
-
|
|
6603
|
+
const postcondition = await this.inspectGitWorktreePostcondition(root, target, branch);
|
|
6604
|
+
if (
|
|
6605
|
+
!postcondition.proven
|
|
6606
|
+
|| !postcondition.exists
|
|
6607
|
+
|| !postcondition.registered
|
|
6608
|
+
|| !postcondition.branchMatches
|
|
6609
|
+
) {
|
|
6610
|
+
const detail = postcondition.error || 'Permanent worktree creation could not be proven';
|
|
6611
|
+
if (operation) this.commitPersistentProjectOperation(operation, 'unknown', null, detail);
|
|
6612
|
+
throw new Error(`${detail}; the operation will not be replayed automatically`);
|
|
6613
|
+
}
|
|
6614
|
+
return commitSuccess();
|
|
6030
6615
|
}
|
|
6031
6616
|
|
|
6032
6617
|
async rollbackPermanentWorktree(created) {
|
|
@@ -6093,6 +6678,8 @@ class AgentManager extends EventEmitter {
|
|
|
6093
6678
|
}
|
|
6094
6679
|
|
|
6095
6680
|
try {
|
|
6681
|
+
const worktrees = await this.listGitWorktrees(resolvedWorkspace);
|
|
6682
|
+
const sourceWorkspace = worktrees[0]?.workspace || resolvedWorkspace;
|
|
6096
6683
|
const { stdout } = await execFileAsync('git', ['-C', resolvedWorkspace, 'status', '--porcelain', '--untracked-files=all'], {
|
|
6097
6684
|
timeout: 30000,
|
|
6098
6685
|
maxBuffer: 1024 * 1024 * 4,
|
|
@@ -6100,6 +6687,7 @@ class AgentManager extends EventEmitter {
|
|
|
6100
6687
|
const dirtyEntries = statusEntriesFromPorcelain(stdout);
|
|
6101
6688
|
return {
|
|
6102
6689
|
workspace: resolvedWorkspace,
|
|
6690
|
+
sourceWorkspace,
|
|
6103
6691
|
dirtyEntries,
|
|
6104
6692
|
requiresForce: dirtyEntries.length > 0,
|
|
6105
6693
|
};
|
|
@@ -6151,21 +6739,118 @@ class AgentManager extends EventEmitter {
|
|
|
6151
6739
|
const workspaceKey = canonicalWorkspacePath(this.expandWorkspacePath(workspace));
|
|
6152
6740
|
if (!workspaceKey) return this.deleteForkWorktreeProjectAdmitted(workspace, options);
|
|
6153
6741
|
const inFlight = this.projectWorkspaceDeleteAdmissions.get(workspaceKey);
|
|
6154
|
-
|
|
6742
|
+
const requestId = String(options.requestId || '').trim();
|
|
6743
|
+
if (inFlight) {
|
|
6744
|
+
if (requestId && inFlight.requestId === requestId) return inFlight.promise;
|
|
6745
|
+
return inFlight.promise
|
|
6746
|
+
.catch(() => {})
|
|
6747
|
+
.then(() => this.deleteForkWorktreeProject(workspace, options));
|
|
6748
|
+
}
|
|
6155
6749
|
|
|
6156
|
-
const
|
|
6157
|
-
|
|
6158
|
-
|
|
6159
|
-
|
|
6750
|
+
const promise = this.deleteForkWorktreeProjectAdmitted(workspace, options);
|
|
6751
|
+
const admission = { requestId, promise };
|
|
6752
|
+
this.projectWorkspaceDeleteAdmissions.set(workspaceKey, admission);
|
|
6753
|
+
void promise.finally(() => {
|
|
6754
|
+
if (this.projectWorkspaceDeleteAdmissions.get(workspaceKey) === admission) {
|
|
6160
6755
|
this.projectWorkspaceDeleteAdmissions.delete(workspaceKey);
|
|
6161
6756
|
}
|
|
6162
6757
|
}).catch(() => {});
|
|
6163
|
-
return
|
|
6758
|
+
return promise;
|
|
6164
6759
|
}
|
|
6165
6760
|
|
|
6166
6761
|
async deleteForkWorktreeProjectAdmitted(workspace, options = {}) {
|
|
6167
6762
|
await this.whenRecovered();
|
|
6168
6763
|
const workspaceKey = canonicalWorkspacePath(this.expandWorkspacePath(workspace));
|
|
6764
|
+
const requestId = String(options.requestId || '').trim().slice(0, 160);
|
|
6765
|
+
const signature = projectOperationSignature({
|
|
6766
|
+
force: options.force === true,
|
|
6767
|
+
type: 'delete-worktree',
|
|
6768
|
+
workspace: workspaceKey,
|
|
6769
|
+
});
|
|
6770
|
+
const existingOperation = requestId && typeof this.configManager?.getProjectOperation === 'function'
|
|
6771
|
+
? this.configManager.getProjectOperation(requestId)
|
|
6772
|
+
: null;
|
|
6773
|
+
if (
|
|
6774
|
+
existingOperation
|
|
6775
|
+
&& (
|
|
6776
|
+
existingOperation.type !== 'delete-worktree'
|
|
6777
|
+
|| existingOperation.signature !== signature
|
|
6778
|
+
)
|
|
6779
|
+
) {
|
|
6780
|
+
return { workspace: workspaceKey, error: `Project operation request ${requestId} was already used for different parameters` };
|
|
6781
|
+
}
|
|
6782
|
+
if (existingOperation?.state === 'succeeded' && existingOperation.result) {
|
|
6783
|
+
const settings = this.configManager.getSettings?.() || {};
|
|
6784
|
+
return {
|
|
6785
|
+
...existingOperation.result,
|
|
6786
|
+
deduplicated: true,
|
|
6787
|
+
projectWorkspaces: settings.projectWorkspaces || [],
|
|
6788
|
+
pinnedProjectWorkspaces: settings.pinnedProjectWorkspaces || [],
|
|
6789
|
+
};
|
|
6790
|
+
}
|
|
6791
|
+
let operation = existingOperation;
|
|
6792
|
+
const storedSourceWorkspace = String(operation?.request?.sourceWorkspace || '');
|
|
6793
|
+
const commitDeleted = (baseResult) => {
|
|
6794
|
+
const result = { ...baseResult, ...(requestId ? { requestId } : {}) };
|
|
6795
|
+
if (!operation) return result;
|
|
6796
|
+
const committed = this.commitPersistentProjectOperation(
|
|
6797
|
+
operation,
|
|
6798
|
+
'succeeded',
|
|
6799
|
+
result,
|
|
6800
|
+
'',
|
|
6801
|
+
{ removeWorkspace: workspaceKey },
|
|
6802
|
+
);
|
|
6803
|
+
return {
|
|
6804
|
+
...result,
|
|
6805
|
+
projectWorkspaces: committed.projectWorkspaces,
|
|
6806
|
+
pinnedProjectWorkspaces: committed.pinnedProjectWorkspaces,
|
|
6807
|
+
};
|
|
6808
|
+
};
|
|
6809
|
+
if (operation && ['pending', 'unknown'].includes(operation.state) && storedSourceWorkspace) {
|
|
6810
|
+
const postcondition = await this.inspectGitWorktreePostcondition(
|
|
6811
|
+
storedSourceWorkspace,
|
|
6812
|
+
operation.request.workspace || workspaceKey,
|
|
6813
|
+
);
|
|
6814
|
+
if (postcondition.proven && !postcondition.exists && !postcondition.registered) {
|
|
6815
|
+
try {
|
|
6816
|
+
return commitDeleted({
|
|
6817
|
+
workspace: workspaceKey,
|
|
6818
|
+
deleted: true,
|
|
6819
|
+
forced: operation.request.force === true,
|
|
6820
|
+
archivedAgentIds: [],
|
|
6821
|
+
removedMainPageSessionKeys: [],
|
|
6822
|
+
});
|
|
6823
|
+
} catch (error) {
|
|
6824
|
+
return {
|
|
6825
|
+
workspace: workspaceKey,
|
|
6826
|
+
deleted: true,
|
|
6827
|
+
retryable: true,
|
|
6828
|
+
error: `Worktree was deleted, but Project operation commit failed: ${error.message || error}`,
|
|
6829
|
+
};
|
|
6830
|
+
}
|
|
6831
|
+
}
|
|
6832
|
+
if (operation.state === 'unknown') {
|
|
6833
|
+
return {
|
|
6834
|
+
workspace: workspaceKey,
|
|
6835
|
+
error: operation.error || 'Worktree deletion has an uncertain outcome and will not be replayed automatically',
|
|
6836
|
+
uncertain: true,
|
|
6837
|
+
};
|
|
6838
|
+
}
|
|
6839
|
+
if (!postcondition.proven || postcondition.exists !== postcondition.registered) {
|
|
6840
|
+
const detail = postcondition.error || 'Worktree deletion has a partial or unverifiable result';
|
|
6841
|
+
try {
|
|
6842
|
+
this.commitPersistentProjectOperation(operation, 'unknown', null, detail);
|
|
6843
|
+
} catch {
|
|
6844
|
+
// The previously persisted pending intent still prevents blind replay.
|
|
6845
|
+
}
|
|
6846
|
+
return { workspace: workspaceKey, error: `${detail}; the operation will not be replayed automatically`, uncertain: true };
|
|
6847
|
+
}
|
|
6848
|
+
} else if (operation && !['pending', 'unknown'].includes(operation.state)) {
|
|
6849
|
+
return {
|
|
6850
|
+
workspace: workspaceKey,
|
|
6851
|
+
error: operation.error || `Project operation ${requestId} already finished with state ${operation.state}`,
|
|
6852
|
+
};
|
|
6853
|
+
}
|
|
6169
6854
|
const relatedStarts = workspaceKey
|
|
6170
6855
|
? [...this.agentStartAdmissions.values()].filter(admission => (
|
|
6171
6856
|
!admission.workspaceKey
|
|
@@ -6193,6 +6878,16 @@ class AgentManager extends EventEmitter {
|
|
|
6193
6878
|
};
|
|
6194
6879
|
}
|
|
6195
6880
|
|
|
6881
|
+
if (!operation) {
|
|
6882
|
+
const admission = this.persistentProjectOperation(requestId, 'delete-worktree', signature, {
|
|
6883
|
+
workspace: inspected.workspace,
|
|
6884
|
+
sourceWorkspace: inspected.sourceWorkspace,
|
|
6885
|
+
force: options.force === true,
|
|
6886
|
+
});
|
|
6887
|
+
if (admission.error) return { ...inspected, error: admission.error };
|
|
6888
|
+
operation = admission.operation;
|
|
6889
|
+
}
|
|
6890
|
+
|
|
6196
6891
|
const archivedAgentIds = [];
|
|
6197
6892
|
const removedMainPageSessionKeys = [];
|
|
6198
6893
|
const projectAgents = this.agentsForProjectWorkspace(inspected.workspace);
|
|
@@ -6220,25 +6915,227 @@ class AgentManager extends EventEmitter {
|
|
|
6220
6915
|
maxBuffer: 1024 * 1024 * 4,
|
|
6221
6916
|
});
|
|
6222
6917
|
} catch (error) {
|
|
6918
|
+
const postcondition = await this.inspectGitWorktreePostcondition(
|
|
6919
|
+
inspected.sourceWorkspace,
|
|
6920
|
+
inspected.workspace,
|
|
6921
|
+
);
|
|
6922
|
+
if (postcondition.proven && !postcondition.exists && !postcondition.registered) {
|
|
6923
|
+
try {
|
|
6924
|
+
return commitDeleted({
|
|
6925
|
+
workspace: inspected.workspace,
|
|
6926
|
+
deleted: true,
|
|
6927
|
+
forced: options.force === true,
|
|
6928
|
+
archivedAgentIds,
|
|
6929
|
+
removedMainPageSessionKeys: Array.from(new Set(removedMainPageSessionKeys)),
|
|
6930
|
+
});
|
|
6931
|
+
} catch (commitError) {
|
|
6932
|
+
return {
|
|
6933
|
+
...inspected,
|
|
6934
|
+
deleted: true,
|
|
6935
|
+
retryable: true,
|
|
6936
|
+
error: `Worktree was deleted, but Project operation commit failed: ${commitError.message || commitError}`,
|
|
6937
|
+
archivedAgentIds,
|
|
6938
|
+
removedMainPageSessionKeys: Array.from(new Set(removedMainPageSessionKeys)),
|
|
6939
|
+
};
|
|
6940
|
+
}
|
|
6941
|
+
}
|
|
6223
6942
|
const message = error && error.stderr ? String(error.stderr).trim() : '';
|
|
6943
|
+
const detail = message || 'Failed to delete git worktree';
|
|
6944
|
+
if (operation) {
|
|
6945
|
+
const state = postcondition.proven && postcondition.exists && postcondition.registered
|
|
6946
|
+
? 'failed'
|
|
6947
|
+
: 'unknown';
|
|
6948
|
+
try {
|
|
6949
|
+
this.commitPersistentProjectOperation(operation, state, null, detail);
|
|
6950
|
+
} catch {
|
|
6951
|
+
// Preserve the earlier durable pending intent when the result write fails.
|
|
6952
|
+
}
|
|
6953
|
+
}
|
|
6224
6954
|
return {
|
|
6225
6955
|
...inspected,
|
|
6226
|
-
error:
|
|
6956
|
+
error: detail,
|
|
6957
|
+
...(postcondition.proven && postcondition.exists && postcondition.registered
|
|
6958
|
+
? {}
|
|
6959
|
+
: { uncertain: true }),
|
|
6227
6960
|
archivedAgentIds,
|
|
6228
6961
|
removedMainPageSessionKeys: Array.from(new Set(removedMainPageSessionKeys)),
|
|
6229
6962
|
};
|
|
6230
6963
|
}
|
|
6231
6964
|
|
|
6232
|
-
|
|
6233
|
-
|
|
6234
|
-
|
|
6235
|
-
|
|
6236
|
-
|
|
6237
|
-
|
|
6238
|
-
|
|
6965
|
+
const postcondition = await this.inspectGitWorktreePostcondition(
|
|
6966
|
+
inspected.sourceWorkspace,
|
|
6967
|
+
inspected.workspace,
|
|
6968
|
+
);
|
|
6969
|
+
if (!postcondition.proven || postcondition.exists || postcondition.registered) {
|
|
6970
|
+
const detail = postcondition.error || 'Worktree deletion could not be proven';
|
|
6971
|
+
if (operation) {
|
|
6972
|
+
try {
|
|
6973
|
+
this.commitPersistentProjectOperation(operation, 'unknown', null, detail);
|
|
6974
|
+
} catch {
|
|
6975
|
+
// Preserve the earlier durable pending intent when the result write fails.
|
|
6976
|
+
}
|
|
6977
|
+
}
|
|
6978
|
+
return {
|
|
6979
|
+
...inspected,
|
|
6980
|
+
error: `${detail}; the operation will not be replayed automatically`,
|
|
6981
|
+
uncertain: true,
|
|
6982
|
+
archivedAgentIds,
|
|
6983
|
+
removedMainPageSessionKeys: Array.from(new Set(removedMainPageSessionKeys)),
|
|
6984
|
+
};
|
|
6985
|
+
}
|
|
6986
|
+
try {
|
|
6987
|
+
return commitDeleted({
|
|
6988
|
+
workspace: inspected.workspace,
|
|
6989
|
+
deleted: true,
|
|
6990
|
+
forced: options.force === true,
|
|
6991
|
+
archivedAgentIds,
|
|
6992
|
+
removedMainPageSessionKeys: Array.from(new Set(removedMainPageSessionKeys)),
|
|
6993
|
+
});
|
|
6994
|
+
} catch (error) {
|
|
6995
|
+
return {
|
|
6996
|
+
...inspected,
|
|
6997
|
+
deleted: true,
|
|
6998
|
+
retryable: true,
|
|
6999
|
+
error: `Worktree was deleted, but Project operation commit failed: ${error.message || error}`,
|
|
7000
|
+
archivedAgentIds,
|
|
7001
|
+
removedMainPageSessionKeys: Array.from(new Set(removedMainPageSessionKeys)),
|
|
7002
|
+
};
|
|
7003
|
+
}
|
|
7004
|
+
}
|
|
7005
|
+
|
|
7006
|
+
async replayPersistentForkRequest(agent, requestId, signature) {
|
|
7007
|
+
const requestKey = `fork-request:${requestId}`;
|
|
7008
|
+
const operation = lifecycleJournal(agent).entries.find(candidate => (
|
|
7009
|
+
candidate.type === 'fork' && candidate.requestKey === requestKey
|
|
7010
|
+
));
|
|
7011
|
+
if (!operation) return null;
|
|
7012
|
+
if (operation.request?.signature && operation.request.signature !== signature) {
|
|
7013
|
+
return { error: `Fork request ${requestId} was already used for different parameters` };
|
|
7014
|
+
}
|
|
7015
|
+
if (operation.state === 'succeeded' && operation.result) {
|
|
7016
|
+
return { ...operation.result, deduplicated: true };
|
|
7017
|
+
}
|
|
7018
|
+
if (TERMINAL_OPERATION_STATES.has(operation.state)) {
|
|
7019
|
+
return { error: operation.error || `Fork request ${requestId} finished with state ${operation.state}` };
|
|
7020
|
+
}
|
|
7021
|
+
|
|
7022
|
+
const children = typeof this.configManager?.listAgentSessionRecords === 'function'
|
|
7023
|
+
? this.configManager.listAgentSessionRecords().filter(record => (
|
|
7024
|
+
record?.parentAgentId === agent.id
|
|
7025
|
+
&& record?.forkRequestId === requestId
|
|
7026
|
+
&& record?.archived !== true
|
|
7027
|
+
))
|
|
7028
|
+
: [];
|
|
7029
|
+
if (children.length === 1) {
|
|
7030
|
+
const child = children[0];
|
|
7031
|
+
const request = operation.request || {};
|
|
7032
|
+
const result = {
|
|
7033
|
+
agentId: child.runtimeAgentId,
|
|
7034
|
+
workspace: child.projectWorkspace || child.cwd || '',
|
|
7035
|
+
mode: request.mode || 'same-worktree',
|
|
7036
|
+
...(request.targetRuntime ? { targetRuntime: request.targetRuntime } : {}),
|
|
7037
|
+
...(child.providerSessionId ? { providerSessionId: child.providerSessionId } : {}),
|
|
7038
|
+
requestId,
|
|
7039
|
+
};
|
|
7040
|
+
try {
|
|
7041
|
+
this.completePersistentAgentOperation(agent, operation.id, result);
|
|
7042
|
+
return { ...result, deduplicated: true, reconciled: true };
|
|
7043
|
+
} catch (error) {
|
|
7044
|
+
return {
|
|
7045
|
+
...result,
|
|
7046
|
+
error: `Fork exists, but its result could not be committed: ${error.message || error}`,
|
|
7047
|
+
retryable: true,
|
|
7048
|
+
};
|
|
7049
|
+
}
|
|
7050
|
+
}
|
|
7051
|
+
|
|
7052
|
+
const detail = children.length > 1
|
|
7053
|
+
? `Fork request ${requestId} has multiple child Agent records and cannot be reconciled safely`
|
|
7054
|
+
: `Fork request ${requestId} has an uncertain outcome and will not be replayed automatically`;
|
|
7055
|
+
if (operation.state !== 'blocked') {
|
|
7056
|
+
try {
|
|
7057
|
+
this.transitionPersistentAgentOperation(agent, operation.id, 'blocked', detail);
|
|
7058
|
+
} catch (error) {
|
|
7059
|
+
return { error: `${detail}; failed to persist blocked state: ${error.message || error}`, uncertain: true };
|
|
7060
|
+
}
|
|
7061
|
+
}
|
|
7062
|
+
return { error: detail, uncertain: true };
|
|
6239
7063
|
}
|
|
6240
7064
|
|
|
6241
7065
|
async forkAgent(agentId, mode = 'same-worktree', options = {}) {
|
|
7066
|
+
const requestId = String(options.requestId || '').trim().slice(0, 160);
|
|
7067
|
+
if (!requestId) return this.forkAgentUntracked(agentId, mode, options);
|
|
7068
|
+
if (!/^[A-Za-z0-9._:-]{1,160}$/.test(requestId)) {
|
|
7069
|
+
return { error: 'Fork requires a valid requestId' };
|
|
7070
|
+
}
|
|
7071
|
+
await this.whenRecovered();
|
|
7072
|
+
const agent = this.agents.get(agentId);
|
|
7073
|
+
if (!agent) return { error: 'Agent not found' };
|
|
7074
|
+
const signature = projectOperationSignature({
|
|
7075
|
+
agentRecordId: agent.agentRecordId || agent.persistentSessionId || '',
|
|
7076
|
+
expectedRevision: Number.isSafeInteger(options.expectedRevision) ? options.expectedRevision : null,
|
|
7077
|
+
mode,
|
|
7078
|
+
targetRuntime: options.targetRuntime || '',
|
|
7079
|
+
});
|
|
7080
|
+
const key = `fork:${requestId}:${signature}`;
|
|
7081
|
+
const inFlight = this.agentLifecycleOperations.get(agentId);
|
|
7082
|
+
if (inFlight?.key === key) return inFlight.promise;
|
|
7083
|
+
const replay = await this.replayPersistentForkRequest(agent, requestId, signature);
|
|
7084
|
+
if (replay) return replay;
|
|
7085
|
+
return this.runAgentLifecycleOperation(
|
|
7086
|
+
agentId,
|
|
7087
|
+
key,
|
|
7088
|
+
'fork',
|
|
7089
|
+
'fork',
|
|
7090
|
+
async lifecycleToken => {
|
|
7091
|
+
const admission = this.beginPersistentAgentOperation(
|
|
7092
|
+
agent,
|
|
7093
|
+
'fork',
|
|
7094
|
+
`fork-request:${requestId}`,
|
|
7095
|
+
{
|
|
7096
|
+
signature,
|
|
7097
|
+
mode,
|
|
7098
|
+
targetRuntime: options.targetRuntime || '',
|
|
7099
|
+
expectedRevision: Number.isSafeInteger(options.expectedRevision) ? options.expectedRevision : null,
|
|
7100
|
+
},
|
|
7101
|
+
);
|
|
7102
|
+
if (admission.error) return { error: admission.error };
|
|
7103
|
+
const result = await this.forkAgentUntracked(agentId, mode, {
|
|
7104
|
+
...options,
|
|
7105
|
+
requestId: '',
|
|
7106
|
+
forkRequestId: requestId,
|
|
7107
|
+
lifecycleToken,
|
|
7108
|
+
});
|
|
7109
|
+
if (result?.error) {
|
|
7110
|
+
try {
|
|
7111
|
+
this.transitionPersistentAgentOperation(
|
|
7112
|
+
agent,
|
|
7113
|
+
admission.operation.id,
|
|
7114
|
+
'blocked',
|
|
7115
|
+
result.error,
|
|
7116
|
+
);
|
|
7117
|
+
} catch (error) {
|
|
7118
|
+
return { ...result, error: `${result.error}; failed to persist Fork outcome: ${error.message || error}` };
|
|
7119
|
+
}
|
|
7120
|
+
return { ...result, requestId, uncertain: true };
|
|
7121
|
+
}
|
|
7122
|
+
const committedResult = { ...result, requestId };
|
|
7123
|
+
try {
|
|
7124
|
+
this.completePersistentAgentOperation(agent, admission.operation.id, committedResult);
|
|
7125
|
+
return committedResult;
|
|
7126
|
+
} catch (error) {
|
|
7127
|
+
return {
|
|
7128
|
+
...committedResult,
|
|
7129
|
+
error: `Fork was created, but its result could not be committed: ${error.message || error}`,
|
|
7130
|
+
retainedAgentId: result.agentId,
|
|
7131
|
+
retryable: true,
|
|
7132
|
+
};
|
|
7133
|
+
}
|
|
7134
|
+
},
|
|
7135
|
+
);
|
|
7136
|
+
}
|
|
7137
|
+
|
|
7138
|
+
async forkAgentUntracked(agentId, mode = 'same-worktree', options = {}) {
|
|
6242
7139
|
await this.whenRecovered();
|
|
6243
7140
|
if (this.disposing) {
|
|
6244
7141
|
return { error: 'Farming is shutting down; Agent lifecycle changes are not accepted' };
|
|
@@ -6258,12 +7155,20 @@ class AgentManager extends EventEmitter {
|
|
|
6258
7155
|
if (!Number.isSafeInteger(expectedRevision) || expectedRevision < 0) {
|
|
6259
7156
|
return { error: 'Conversation Fork requires an exact transcript revision' };
|
|
6260
7157
|
}
|
|
7158
|
+
if (options.lifecycleToken) {
|
|
7159
|
+
return this.performAcpConversationFork(
|
|
7160
|
+
agentId,
|
|
7161
|
+
expectedRevision,
|
|
7162
|
+
options.lifecycleToken,
|
|
7163
|
+
options.forkRequestId || '',
|
|
7164
|
+
);
|
|
7165
|
+
}
|
|
6261
7166
|
return this.runAgentLifecycleOperation(
|
|
6262
7167
|
agentId,
|
|
6263
7168
|
`conversation-fork:${expectedRevision}`,
|
|
6264
7169
|
'conversation-fork',
|
|
6265
7170
|
'conversation fork',
|
|
6266
|
-
lifecycleToken => this.performAcpConversationFork(agentId, expectedRevision, lifecycleToken),
|
|
7171
|
+
lifecycleToken => this.performAcpConversationFork(agentId, expectedRevision, lifecycleToken, ''),
|
|
6267
7172
|
);
|
|
6268
7173
|
}
|
|
6269
7174
|
if (agent.providerSessionProvider === 'codex' && agent.providerSessionTemporary === true) {
|
|
@@ -6319,6 +7224,7 @@ class AgentManager extends EventEmitter {
|
|
|
6319
7224
|
}, {
|
|
6320
7225
|
wantsMain: false,
|
|
6321
7226
|
parentAgentId: agent.id,
|
|
7227
|
+
forkRequestId: options.forkRequestId || '',
|
|
6322
7228
|
task: agent.task ? `Fork: ${agent.task}` : `Fork of ${agent.command}`,
|
|
6323
7229
|
workflowTemplate: agent.workflowTemplate || '',
|
|
6324
7230
|
source: mode === 'new-worktree' ? 'ui-fork-new-worktree' : 'ui-fork-same-worktree',
|
|
@@ -6331,7 +7237,7 @@ class AgentManager extends EventEmitter {
|
|
|
6331
7237
|
});
|
|
6332
7238
|
}
|
|
6333
7239
|
|
|
6334
|
-
async performAcpConversationFork(agentId, expectedRevision, lifecycleToken) {
|
|
7240
|
+
async performAcpConversationFork(agentId, expectedRevision, lifecycleToken, forkRequestId = '') {
|
|
6335
7241
|
const agent = this.agents.get(agentId);
|
|
6336
7242
|
if (!agent) return { error: 'Agent not found' };
|
|
6337
7243
|
if (runtimeKind(agent) !== 'acp') {
|
|
@@ -6360,6 +7266,7 @@ class AgentManager extends EventEmitter {
|
|
|
6360
7266
|
expectedRevision,
|
|
6361
7267
|
lifecycleToken,
|
|
6362
7268
|
acpSessionOptions,
|
|
7269
|
+
forkRequestId,
|
|
6363
7270
|
});
|
|
6364
7271
|
}
|
|
6365
7272
|
|
|
@@ -6423,6 +7330,7 @@ class AgentManager extends EventEmitter {
|
|
|
6423
7330
|
const started = this.startAgent(command, workspace, finish, {
|
|
6424
7331
|
wantsMain: false,
|
|
6425
7332
|
parentAgentId: agent.id,
|
|
7333
|
+
forkRequestId,
|
|
6426
7334
|
task: agent.task ? `Fork: ${agent.task}` : `Fork of ${agent.command}`,
|
|
6427
7335
|
workflowTemplate: agent.workflowTemplate || '',
|
|
6428
7336
|
source: 'ui-fork-acp-chat',
|
|
@@ -6467,6 +7375,7 @@ class AgentManager extends EventEmitter {
|
|
|
6467
7375
|
expectedRevision,
|
|
6468
7376
|
lifecycleToken,
|
|
6469
7377
|
acpSessionOptions,
|
|
7378
|
+
forkRequestId,
|
|
6470
7379
|
} = options;
|
|
6471
7380
|
const command = getProviderAdapter(provider)?.executable || provider;
|
|
6472
7381
|
let preparedSessionId = '';
|
|
@@ -6507,6 +7416,7 @@ class AgentManager extends EventEmitter {
|
|
|
6507
7416
|
const started = this.startAgent(command, workspace, finish, {
|
|
6508
7417
|
wantsMain: false,
|
|
6509
7418
|
parentAgentId: agent.id,
|
|
7419
|
+
forkRequestId,
|
|
6510
7420
|
task: agent.task ? `Fork: ${agent.task}` : `Fork of ${agent.command}`,
|
|
6511
7421
|
workflowTemplate: agent.workflowTemplate || '',
|
|
6512
7422
|
source: 'ui-fork-acp-chat',
|
|
@@ -6732,7 +7642,7 @@ class AgentManager extends EventEmitter {
|
|
|
6732
7642
|
}
|
|
6733
7643
|
let removedMainPageSessionKeys = [];
|
|
6734
7644
|
try {
|
|
6735
|
-
this.transitionPersistentAgentOperation(agent, operationId, '
|
|
7645
|
+
this.transitionPersistentAgentOperation(agent, operationId, 'provider-archive-pending', '', {
|
|
6736
7646
|
visibleOnMainPage: false,
|
|
6737
7647
|
archived: true,
|
|
6738
7648
|
archivedAt: Date.now(),
|
|
@@ -6750,6 +7660,8 @@ class AgentManager extends EventEmitter {
|
|
|
6750
7660
|
removedMainPageSessionKeys,
|
|
6751
7661
|
};
|
|
6752
7662
|
}
|
|
7663
|
+
agent.archived = true;
|
|
7664
|
+
agent.archivedAt = Date.now();
|
|
6753
7665
|
let metadataWarning = '';
|
|
6754
7666
|
try {
|
|
6755
7667
|
removedMainPageSessionKeys = this.removeMainPageProviderSessionsForAgents([agent]);
|
|
@@ -6759,7 +7671,7 @@ class AgentManager extends EventEmitter {
|
|
|
6759
7671
|
}
|
|
6760
7672
|
|
|
6761
7673
|
let historyWarning = '';
|
|
6762
|
-
if (options.recordHistory !== false && !isEphemeralShellAgent(agent)) {
|
|
7674
|
+
if (!admission.joined && options.recordHistory !== false && !isEphemeralShellAgent(agent)) {
|
|
6763
7675
|
const previousTaskHistory = this.taskHistory;
|
|
6764
7676
|
try {
|
|
6765
7677
|
this.recordTaskHistory(agent, {
|
|
@@ -6772,8 +7684,59 @@ class AgentManager extends EventEmitter {
|
|
|
6772
7684
|
console.error(historyWarning);
|
|
6773
7685
|
}
|
|
6774
7686
|
}
|
|
7687
|
+
if (options.scheduleProviderArchive !== false) {
|
|
7688
|
+
const providerArchive = await this.archiveCodexProviderSession(agent);
|
|
7689
|
+
if (providerArchive?.error) {
|
|
7690
|
+
try {
|
|
7691
|
+
this.transitionPersistentAgentOperation(agent, operationId, 'blocked', providerArchive.error, {
|
|
7692
|
+
visibleOnMainPage: false,
|
|
7693
|
+
archived: true,
|
|
7694
|
+
runtimeAgentId: '',
|
|
7695
|
+
});
|
|
7696
|
+
} catch (error) {
|
|
7697
|
+
return {
|
|
7698
|
+
agentId,
|
|
7699
|
+
archived: true,
|
|
7700
|
+
stopped: true,
|
|
7701
|
+
retryable: true,
|
|
7702
|
+
operationId,
|
|
7703
|
+
removedMainPageSessionKeys,
|
|
7704
|
+
error: `Provider archive failed: ${providerArchive.error}; failed to persist blocked Archive: ${error.message || error}`,
|
|
7705
|
+
};
|
|
7706
|
+
}
|
|
7707
|
+
this.emit('update');
|
|
7708
|
+
return {
|
|
7709
|
+
agentId,
|
|
7710
|
+
archived: true,
|
|
7711
|
+
stopped: true,
|
|
7712
|
+
providerArchived: false,
|
|
7713
|
+
retryable: true,
|
|
7714
|
+
operationId,
|
|
7715
|
+
removedMainPageSessionKeys,
|
|
7716
|
+
error: `Provider archive failed: ${providerArchive.error}`,
|
|
7717
|
+
};
|
|
7718
|
+
}
|
|
7719
|
+
}
|
|
7720
|
+
try {
|
|
7721
|
+
this.transitionPersistentAgentOperation(agent, operationId, 'succeeded', '', {
|
|
7722
|
+
visibleOnMainPage: false,
|
|
7723
|
+
archived: true,
|
|
7724
|
+
runtimeAgentId: '',
|
|
7725
|
+
});
|
|
7726
|
+
} catch (error) {
|
|
7727
|
+
this.emit('update');
|
|
7728
|
+
return {
|
|
7729
|
+
agentId,
|
|
7730
|
+
archived: true,
|
|
7731
|
+
stopped: true,
|
|
7732
|
+
providerArchived: true,
|
|
7733
|
+
retryable: true,
|
|
7734
|
+
operationId,
|
|
7735
|
+
removedMainPageSessionKeys,
|
|
7736
|
+
error: `Provider archive succeeded, but terminal Archive result could not be saved: ${error.message || error}`,
|
|
7737
|
+
};
|
|
7738
|
+
}
|
|
6775
7739
|
this.forgetStoppedAgentRecord(agentId);
|
|
6776
|
-
if (options.scheduleProviderArchive !== false) this.scheduleCodexSessionArchive(agent);
|
|
6777
7740
|
return {
|
|
6778
7741
|
agentId,
|
|
6779
7742
|
archived: true,
|
|
@@ -6788,14 +7751,14 @@ class AgentManager extends EventEmitter {
|
|
|
6788
7751
|
};
|
|
6789
7752
|
}
|
|
6790
7753
|
|
|
6791
|
-
|
|
7754
|
+
async archiveCodexProviderSession(agent) {
|
|
6792
7755
|
if (
|
|
6793
7756
|
!agent
|
|
6794
7757
|
|| agent.providerSessionProvider !== 'codex'
|
|
6795
7758
|
|| !agent.providerSessionId
|
|
6796
7759
|
|| agent.providerSessionTemporary === true
|
|
6797
7760
|
) {
|
|
6798
|
-
return;
|
|
7761
|
+
return null;
|
|
6799
7762
|
}
|
|
6800
7763
|
|
|
6801
7764
|
const sessionId = agent.providerSessionId;
|
|
@@ -6805,26 +7768,23 @@ class AgentManager extends EventEmitter {
|
|
|
6805
7768
|
workspace: agent.projectWorkspace || '',
|
|
6806
7769
|
providerHomePath: agent.providerHomePath || '',
|
|
6807
7770
|
};
|
|
6808
|
-
|
|
6809
|
-
|
|
6810
|
-
|
|
6811
|
-
|
|
6812
|
-
|
|
6813
|
-
|
|
6814
|
-
|
|
6815
|
-
|
|
6816
|
-
|
|
6817
|
-
|
|
6818
|
-
|
|
6819
|
-
|
|
6820
|
-
|
|
6821
|
-
|
|
6822
|
-
|
|
6823
|
-
.catch(error => {
|
|
6824
|
-
console.error(`Failed to archive Codex session ${sessionId}:`, error);
|
|
6825
|
-
});
|
|
7771
|
+
try {
|
|
7772
|
+
const result = await this.enqueueCodexSessionMutation(
|
|
7773
|
+
sessionId,
|
|
7774
|
+
{
|
|
7775
|
+
providerHomeId: agent.providerHomeId || 'default',
|
|
7776
|
+
providerHomePath: agent.providerHomePath || '',
|
|
7777
|
+
},
|
|
7778
|
+
'archive',
|
|
7779
|
+
() => this.archiveCodexSession(sessionId, session),
|
|
7780
|
+
true,
|
|
7781
|
+
);
|
|
7782
|
+
return result?.error ? { error: result.error } : { archived: true };
|
|
7783
|
+
} catch (error) {
|
|
7784
|
+
return { error: error.message || String(error) };
|
|
7785
|
+
}
|
|
6826
7786
|
}
|
|
6827
|
-
|
|
7787
|
+
|
|
6828
7788
|
admitPersistentDelete(agent, options = {}) {
|
|
6829
7789
|
const activeOperation = activeLifecycleOperation(agent);
|
|
6830
7790
|
if (
|