farming-code 2.2.27 → 2.2.29

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.
Files changed (284) hide show
  1. package/README.md +15 -30
  2. package/README.zh_cn.md +15 -30
  3. package/THIRD_PARTY_NOTICES.md +28 -6
  4. package/backend/acp/packaged-claude-acp.js +13 -0
  5. package/backend/acp/packaged-codex-acp.js +2 -2
  6. package/backend/acp-runtime.js +52 -3
  7. package/backend/acp-transcript.js +174 -9
  8. package/backend/agent-manager.js +1081 -96
  9. package/backend/atomic-json-store.js +37 -0
  10. package/backend/cli-agents.js +9 -7
  11. package/backend/config-manager.js +211 -58
  12. package/backend/control-api.js +2 -1
  13. package/backend/data/LICENSE.agent-browser +201 -0
  14. package/backend/data/runtime-dependency-manifest.json +199 -0
  15. package/backend/data/runtime-dependency-sources.json +5 -0
  16. package/backend/executable-discovery.js +7 -0
  17. package/backend/farming-agent-bootstrap.js +49 -0
  18. package/backend/farming-agent-bootstrap.zh_cn.md +7 -0
  19. package/backend/farming-app-cli.js +168 -65
  20. package/backend/farming-cli.js +57 -0
  21. package/backend/farming-session-store.js +92 -89
  22. package/backend/index-html.js +13 -0
  23. package/backend/main-agent-skills.js +13 -0
  24. package/backend/npm-update-helper.js +43 -6
  25. package/backend/preview-session-manager.js +68 -0
  26. package/backend/provider-adapters.js +54 -5
  27. package/backend/review-session-store.js +32 -25
  28. package/backend/review-state-store.js +13 -14
  29. package/backend/run-history-store.js +13 -16
  30. package/backend/runtime-dependency-manager.js +759 -0
  31. package/backend/runtime-executable-invocation.js +20 -0
  32. package/backend/server-process-identity.js +47 -0
  33. package/backend/server.js +231 -223
  34. package/backend/slash-command-discovery.js +138 -1
  35. package/backend/storage-layout.js +35 -0
  36. package/backend/update-service.js +27 -1
  37. package/backend/workspace-file-router.js +141 -0
  38. package/backend/workspace-file-service.js +19 -0
  39. package/backend/workspace-root-registry.js +6 -4
  40. package/config/farming.install.env.example +4 -0
  41. package/dist/acp/LICENSE.claude-agent-acp +191 -0
  42. package/dist/acp/LICENSE.claude-agent-sdk +1 -0
  43. package/dist/acp/claude-agent-acp-0.59.0.mjs +41606 -0
  44. package/dist/assets/App-BKNUILuL.css +1 -0
  45. package/dist/assets/App-Ex2oVVXO.js +693 -0
  46. package/dist/assets/{FileEditorMarkdownPreview-Da5jguUs.js → FileEditorMarkdownPreview-Cs9cLv8a.js} +5 -5
  47. package/dist/assets/FileEditorPane-CNtuGjQ3.js +2 -0
  48. package/dist/assets/IconGlyphs-BClgag6E.js +1 -0
  49. package/dist/assets/ProjectFilesSection-Cf2BEcAN.js +12 -0
  50. package/dist/assets/ReviewPage-BRuxFaH5.js +3 -0
  51. package/dist/assets/{abnfDiagram-VRR7QNED-nnglrXFi.js → abnfDiagram-VRR7QNED-BY1qwq8A.js} +1 -1
  52. package/dist/assets/api-w2x5QfxB.js +1 -0
  53. package/dist/assets/{arc-CWoVlxJw.js → arc-DgZRAlwj.js} +1 -1
  54. package/dist/assets/{architectureDiagram-ZJ3FMSHR-BEXMaFfY.js → architectureDiagram-ZJ3FMSHR-jFn144od.js} +1 -1
  55. package/dist/assets/{blockDiagram-677ZJIJ3-1mCezSBk.js → blockDiagram-677ZJIJ3-BmRjlV0H.js} +1 -1
  56. package/dist/assets/{c4Diagram-LMCZKHZV-DhTiI6Uv.js → c4Diagram-LMCZKHZV-DUKul6tT.js} +1 -1
  57. package/dist/assets/{workspace-editor-monaco-DLlws5Ig.js → channel-CI9j3QFD.js} +1 -1
  58. package/dist/assets/chunk-2Q5K7J3B-87UnWaoh.js +1 -0
  59. package/dist/assets/{chunk-32BRIVSS-CCdsKd73.js → chunk-32BRIVSS-rGVu23o5.js} +1 -1
  60. package/dist/assets/{chunk-52WLFC77-Gncjk4LF.js → chunk-52WLFC77-CvFDKgbB.js} +1 -1
  61. package/dist/assets/{chunk-5VM5RSS4-Dlw7AXWp.js → chunk-5VM5RSS4-CFgnRW1C.js} +1 -1
  62. package/dist/assets/{chunk-7BUUIJ7U-ZBRhjLOh.js → chunk-7BUUIJ7U-Cp7bqW6s.js} +1 -1
  63. package/dist/assets/{chunk-C7G6YPKG-CiDVUP82.js → chunk-C7G6YPKG-BIS1gjZO.js} +1 -1
  64. package/dist/assets/{chunk-EX3LRPZG-z6gXP2_N.js → chunk-EX3LRPZG-BKG-cR9j.js} +1 -1
  65. package/dist/assets/{chunk-FWX5IMBZ-DMZBccQa.js → chunk-FWX5IMBZ-B8PsTr8a.js} +2 -2
  66. package/dist/assets/{chunk-HOUHSVGY-D8tIeoPk.js → chunk-HOUHSVGY-BHFOAiXT.js} +1 -1
  67. package/dist/assets/{chunk-ICXQ74PX-b6oQ2Q17.js → chunk-ICXQ74PX-CvLE1J9X.js} +1 -1
  68. package/dist/assets/{chunk-JWPE2WC7-DSyBRaRG.js → chunk-JWPE2WC7-D8misks7.js} +1 -1
  69. package/dist/assets/{chunk-MOJQB5TN-DAVH_p2a.js → chunk-MOJQB5TN-D-oNgSNH.js} +1 -1
  70. package/dist/assets/{chunk-OGEWGWER-DxoiMCI-.js → chunk-OGEWGWER-C2N-la9e.js} +1 -1
  71. package/dist/assets/{chunk-PUDLZKDR-NsOWh7HM.js → chunk-PUDLZKDR-BwI3Jz8q.js} +1 -1
  72. package/dist/assets/{chunk-Q4XR5HBZ-Di4H7L21.js → chunk-Q4XR5HBZ-BuqGfsXA.js} +1 -1
  73. package/dist/assets/{chunk-RYQCIY6F-CZA9pvFX.js → chunk-RYQCIY6F-Bq4XZcGt.js} +1 -1
  74. package/dist/assets/{chunk-V7JOEXUC-DttpZg9Y.js → chunk-V7JOEXUC-QZLzdTTJ.js} +1 -1
  75. package/dist/assets/chunk-VAUOI2AC-Cb0-kQ9g.js +1 -0
  76. package/dist/assets/{chunk-VR4S4FIN-D-Fmtd4Z.js → chunk-VR4S4FIN-DcZTpML_.js} +1 -1
  77. package/dist/assets/{chunk-WYO6CB5R-CUxMtfK2.js → chunk-WYO6CB5R-DCU8wuny.js} +2 -2
  78. package/dist/assets/chunk-XXDRQBXY-BZAC1Krv.js +1 -0
  79. package/dist/assets/{chunk-ZGVPDNZ5-CoV2QEl0.js → chunk-ZGVPDNZ5-B7r85QbY.js} +1 -1
  80. package/dist/assets/{chunk-ZIRB5QZD-DncwlQQc.js → chunk-ZIRB5QZD-BgEnDWhQ.js} +1 -1
  81. package/dist/assets/classDiagram-OUVF2IWQ-DcvGOuvi.js +1 -0
  82. package/dist/assets/classDiagram-v2-EOCWNBFH-DDYUSofQ.js +1 -0
  83. package/dist/assets/code-dark-BEZTQwYZ.css +1 -0
  84. package/dist/assets/{cose-bilkent-JH36ORCC-BNIAz0nN.js → cose-bilkent-JH36ORCC-CThXS2qG.js} +1 -1
  85. package/dist/assets/{cssMode-DDWxTPm_.js → cssMode-Bdg5RpcJ.js} +1 -1
  86. package/dist/assets/{cynefinDiagram-TSTJHNR4-0xPI92Lg.js → cynefinDiagram-TSTJHNR4-DxBgzw25.js} +1 -1
  87. package/dist/assets/{dagre-DXlcHgr3.js → dagre-BM-GvGUZ.js} +1 -1
  88. package/dist/assets/{dagre-VKFMJZFB-DfuMG4gU.js → dagre-VKFMJZFB-B7UCRmxR.js} +1 -1
  89. package/dist/assets/{diagram-FQU43EPY-CT00bLZ6.js → diagram-FQU43EPY-CeL2DwKx.js} +1 -1
  90. package/dist/assets/{diagram-G47NLZAW-Xbh7zXUx.js → diagram-G47NLZAW-BEirglCC.js} +1 -1
  91. package/dist/assets/{diagram-NH7WQ7WH-SRDVPfn7.js → diagram-NH7WQ7WH-C6wcI-Rl.js} +1 -1
  92. package/dist/assets/{diagram-OA4YK3LP-CQRli4Np.js → diagram-OA4YK3LP-DpM-XXtJ.js} +1 -1
  93. package/dist/assets/{diagram-WEI45ONY-DMf17WQi.js → diagram-WEI45ONY-BQyaut9k.js} +1 -1
  94. package/dist/assets/{ebnfDiagram-CCIWWBDH-DhpFkgJ5.js → ebnfDiagram-CCIWWBDH-UmAXsUhr.js} +1 -1
  95. package/dist/assets/{erDiagram-Q63AITRT-1IwxWmJo.js → erDiagram-Q63AITRT-CdYLEOch.js} +1 -1
  96. package/dist/assets/file-icons-BOlcJ0ZF.js +1 -0
  97. package/dist/assets/flowDiagram-23GEKE2U-CO-z9-tq.js +1 -0
  98. package/dist/assets/{freemarker2-QBsTERH-.js → freemarker2-DfMDJfnE.js} +1 -1
  99. package/dist/assets/{ganttDiagram-NO4QXBWP-BNCdfyr1.js → ganttDiagram-NO4QXBWP-DgSUNdUB.js} +1 -1
  100. package/dist/assets/{gitGraphDiagram-IHSO6WYX-pbyWZuiX.js → gitGraphDiagram-IHSO6WYX-DjwjtRtF.js} +1 -1
  101. package/dist/assets/{handlebars-CuoxPo5A.js → handlebars-BEdPtzMT.js} +1 -1
  102. package/dist/assets/{html-gX5PEu0Q.js → html-B1fPb1QD.js} +1 -1
  103. package/dist/assets/html2canvas-DsczWxBa.js +5 -0
  104. package/dist/assets/{htmlMode-DIWUbUxd.js → htmlMode-CY2ZjHxH.js} +1 -1
  105. package/dist/assets/{index-CtwKYTiC.js → index-CQNaZc3l.js} +2 -2
  106. package/dist/assets/{infoDiagram-FWYZ7A6U-BcTOgDbe.js → infoDiagram-FWYZ7A6U-Bi9076l2.js} +1 -1
  107. package/dist/assets/{ishikawaDiagram-FXEZZL3T-DCdvGKrn.js → ishikawaDiagram-FXEZZL3T-DaMaNZN7.js} +1 -1
  108. package/dist/assets/{javascript-BYKGIUGs.js → javascript-BV2OsWA0.js} +1 -1
  109. package/dist/assets/{journeyDiagram-5HDEW3XC-BZ-1q_IT.js → journeyDiagram-5HDEW3XC-CfDy69EZ.js} +1 -1
  110. package/dist/assets/{jsonMode-AFmQQP6u.js → jsonMode-zd07aeuX.js} +1 -1
  111. package/dist/assets/{kanban-definition-HUTT4EX6-BIgejCjj.js → kanban-definition-HUTT4EX6-XFvZ722B.js} +1 -1
  112. package/dist/assets/{line-CfptFMmX.js → line-BXK9PKLn.js} +1 -1
  113. package/dist/assets/{linear-Dld3_ERu.js → linear-CULvmq2Z.js} +1 -1
  114. package/dist/assets/{liquid-DBIl-f0z.js → liquid-BParI24X.js} +1 -1
  115. package/dist/assets/{lspLanguageFeatures-DK6tqicV.js → lspLanguageFeatures-BzredkoV.js} +1 -1
  116. package/dist/assets/main-DK9Sy7-c.css +1 -0
  117. package/dist/assets/{map-LZWWoeWa.js → map-C9Qw9eWh.js} +1 -1
  118. package/dist/assets/{mdx-DS-bVtZN.js → mdx-vGzUJN98.js} +1 -1
  119. package/dist/assets/{mermaid.core-gSpX30pV.js → mermaid.core-CzazaIKr.js} +3 -3
  120. package/dist/assets/{mindmap-definition-LN4V7U3C-BKBaI4LW.js → mindmap-definition-LN4V7U3C-Uoff7hZ3.js} +1 -1
  121. package/dist/assets/{monaco.contribution-BXz-lfFB.js → monaco.contribution-BmaeBQEc.js} +2 -2
  122. package/dist/assets/{ordinal-BLrOss5K.js → ordinal-r6pIwRdZ.js} +1 -1
  123. package/dist/assets/{pegDiagram-2B236MQR-sBvDY3VX.js → pegDiagram-2B236MQR-CCBiHQjk.js} +1 -1
  124. package/dist/assets/{pieDiagram-ENE6RG2P-DWlWcu08.js → pieDiagram-ENE6RG2P-D61s7l75.js} +1 -1
  125. package/dist/assets/{python-C93q3xXz.js → python-CUwdLgsI.js} +1 -1
  126. package/dist/assets/{quadrantDiagram-ABIIQ3AL-DZutUd3J.js → quadrantDiagram-ABIIQ3AL-D_CWk2DQ.js} +1 -1
  127. package/dist/assets/{railroadDiagram-RFXS5EU6-DQb3lXGM.js → railroadDiagram-RFXS5EU6-CdlCHtxN.js} +1 -1
  128. package/dist/assets/{razor-CBifNGXO.js → razor-BScebUxU.js} +1 -1
  129. package/dist/assets/{requirementDiagram-TGXJPOKE-DqFGXu2e.js → requirementDiagram-TGXJPOKE-BLpHtkoC.js} +1 -1
  130. package/dist/assets/{sankeyDiagram-HTMAVEWB-d7ECahnO.js → sankeyDiagram-HTMAVEWB-CkhYZN2j.js} +1 -1
  131. package/dist/assets/{sequenceDiagram-DBY2YBRQ-DdsbKqct.js → sequenceDiagram-DBY2YBRQ-8bploKJl.js} +1 -1
  132. package/dist/assets/{sizeCapture-X5ZJPWSS-daTB6OcF.js → sizeCapture-X5ZJPWSS-DtSdkS0d.js} +1 -1
  133. package/dist/assets/{src-WBW1QxJt.js → src-6yeq6WBA.js} +1 -1
  134. package/dist/assets/{stateDiagram-2N3HPSRC-P6iSrgwR.js → stateDiagram-2N3HPSRC-DfmVGpfA.js} +1 -1
  135. package/dist/assets/stateDiagram-v2-6OUMAXLB-B2c9CQ-J.js +1 -0
  136. package/dist/assets/{swimlanes-5IMT3BWC-BGnZJx1M.js → swimlanes-5IMT3BWC-DRLk2zRF.js} +2 -2
  137. package/dist/assets/swimlanesDiagram-G3AALYLV-B7_APh_h.js +8 -0
  138. package/dist/assets/{timeline-definition-FHXFAJF6-rA-r3aUR.js → timeline-definition-FHXFAJF6-Cngfy588.js} +1 -1
  139. package/dist/assets/{tsMode-B0oUTcXQ.js → tsMode-DzUAibXK.js} +1 -1
  140. package/dist/assets/{typescript-KJKfYO7K.js → typescript-D9EOeM7N.js} +1 -1
  141. package/dist/assets/{vennDiagram-L72KCM5P-DiVKUjA9.js → vennDiagram-L72KCM5P-DeJn63Lq.js} +1 -1
  142. package/dist/assets/{wardleyDiagram-EHGQE667-Cfxh7UOn.js → wardleyDiagram-EHGQE667-BH1nzy77.js} +1 -1
  143. package/dist/assets/{workers-BSQRIkqa.js → workers-DAiomYZM.js} +1 -1
  144. package/dist/assets/workspace-editor-model-CSGCl21x.js +1 -0
  145. package/dist/assets/{workspace-editor-monaco-BIpqcWf1.js → workspace-editor-monaco-CJNwbpCy.js} +3 -3
  146. package/dist/assets/workspace-editor-monaco-CO9w8YV0.js +1 -0
  147. package/dist/assets/workspace-view-state-BjKPYp0E.js +7 -0
  148. package/dist/assets/{xml-C8FAcN6j.js → xml-wWl1dhkQ.js} +1 -1
  149. package/dist/assets/{xychartDiagram-FW5EYKEG--hmryQeL.js → xychartDiagram-FW5EYKEG-SkO2prCU.js} +1 -1
  150. package/dist/assets/{yaml-BQ4NrRhG.js → yaml-Dc2WDaOZ.js} +1 -1
  151. package/dist/crt-markdown-renderer.js +46 -46
  152. package/dist/index.html +32 -3
  153. package/extensions/browser/backend/agent-browser-runtime.js +1041 -0
  154. package/extensions/browser/backend/agent-capability.js +55 -0
  155. package/extensions/browser/backend/browser-mcp-server.js +621 -0
  156. package/extensions/browser/backend/browser-resource-manager.js +1309 -0
  157. package/extensions/browser/backend/browser-resource-store.js +190 -0
  158. package/extensions/browser/backend/browser-router.js +133 -0
  159. package/extensions/browser/backend/executable-discovery.js +224 -0
  160. package/extensions/browser/backend/farming-browser-client.js +83 -0
  161. package/extensions/browser/backend/index.js +7 -0
  162. package/extensions/browser/bin/farming-browser +471 -0
  163. package/extensions/browser/frontend/BrowserSidebarPortals.tsx +360 -0
  164. package/extensions/browser/frontend/BrowserViewer.tsx +500 -0
  165. package/extensions/browser/frontend/browser-resource-state.ts +60 -0
  166. package/extensions/browser/frontend/browser.css +567 -0
  167. package/extensions/browser/frontend/types.ts +31 -0
  168. package/extensions/browser/frontend/useBrowserResources.ts +158 -0
  169. package/frontend/session-bridge.js +7 -6
  170. package/frontend/skins/crt/app.js +64 -4
  171. package/index.html +31 -2
  172. package/package.json +27 -18
  173. package/shared/backend-connection-status.d.ts +12 -0
  174. package/shared/backend-connection-status.js +35 -4
  175. package/shared/browser-protocol.js +1 -1
  176. package/backend/agent-activity.js +0 -35
  177. package/dist/assets/App-BrP-ENHg.css +0 -1
  178. package/dist/assets/App-Cm8qTjsc.js +0 -210
  179. package/dist/assets/FileEditorPane-fFqHA4wW.js +0 -2
  180. package/dist/assets/IconGlyphs-C2WFcoJS.js +0 -1
  181. package/dist/assets/ProjectFilesSection-Cyy3Vcn8.js +0 -12
  182. package/dist/assets/ReviewPage-L1vh1Ggs.js +0 -3
  183. package/dist/assets/api-D8nyOEbz.js +0 -1
  184. package/dist/assets/channel-BVWNatUB.js +0 -1
  185. package/dist/assets/chunk-2Q5K7J3B-d5WWh8jo.js +0 -1
  186. package/dist/assets/chunk-VAUOI2AC-UHNr_yhX.js +0 -1
  187. package/dist/assets/chunk-XXDRQBXY-BBXwjhUx.js +0 -1
  188. package/dist/assets/classDiagram-OUVF2IWQ-Db0ib5iE.js +0 -1
  189. package/dist/assets/classDiagram-v2-EOCWNBFH-Bkf69XF9.js +0 -1
  190. package/dist/assets/code-dark-BKtvf0Xc.css +0 -1
  191. package/dist/assets/file-icons-BqiH0rDI.js +0 -1
  192. package/dist/assets/flowDiagram-23GEKE2U-DYFo2xmB.js +0 -1
  193. package/dist/assets/main-XMXgyJJt.css +0 -1
  194. package/dist/assets/stateDiagram-v2-6OUMAXLB-DIYSf_qh.js +0 -1
  195. package/dist/assets/swimlanesDiagram-G3AALYLV-BIkrlKrZ.js +0 -8
  196. package/dist/assets/workspace-editor-model-D0Im34S5.js +0 -1
  197. package/dist/assets/workspace-view-state-BiuEbmRr.js +0 -7
  198. /package/dist/acp/{codex-acp-1.1.4.js → codex-acp-1.1.4.mjs} +0 -0
  199. /package/dist/assets/{abap-Cc5D5h5i.js → abap-CtX5nWyX.js} +0 -0
  200. /package/dist/assets/{apex-BKopwLmx.js → apex-DQJRAHwc.js} +0 -0
  201. /package/dist/assets/{array-HeX70jSN.js → array-BTwzYU0x.js} +0 -0
  202. /package/dist/assets/{azcli-DI-C34WA.js → azcli-9SMrOQ4q.js} +0 -0
  203. /package/dist/assets/{bat-Cbp6u42z.js → bat-CBGLUsw1.js} +0 -0
  204. /package/dist/assets/{bicep-4QA1jTCM.js → bicep-pDx8da1v.js} +0 -0
  205. /package/dist/assets/{cameligo-Dda_adPs.js → cameligo-hZV3vQ6_.js} +0 -0
  206. /package/dist/assets/{chunk-Y2CYZVJY-Di0sYzi1.js → chunk-Y2CYZVJY-BYi0W5ea.js} +0 -0
  207. /package/dist/assets/{clojure-CvY8A83L.js → clojure-BhCwU3gq.js} +0 -0
  208. /package/dist/assets/{coffee-4LX2Sv8A.js → coffee-BVB1PsgH.js} +0 -0
  209. /package/dist/assets/{core-B9MwRjDE.js → core-vPkV8P_m.js} +0 -0
  210. /package/dist/assets/{cpp-D4AzSV28.js → cpp-eTjuiNPq.js} +0 -0
  211. /package/dist/assets/{csharp-C4nWN4Yd.js → csharp-1kP-kKZT.js} +0 -0
  212. /package/dist/assets/{csp-CiEaaS5Z.js → csp-C_iQqIes.js} +0 -0
  213. /package/dist/assets/{css-DXxBRsbk.js → css-CxOs7IfP.js} +0 -0
  214. /package/dist/assets/{cypher-DOMzqyFA.js → cypher-Cd-a9Y82.js} +0 -0
  215. /package/dist/assets/{cytoscape.esm-Cl8iC3D7.js → cytoscape.esm-OLojuicg.js} +0 -0
  216. /package/dist/assets/{dart-BWS7MXt_.js → dart-whhXpexn.js} +0 -0
  217. /package/dist/assets/{defaultLocale-GJwWH1Jr.js → defaultLocale-CPimVhfE.js} +0 -0
  218. /package/dist/assets/{dist-ClAL-Tv5.js → dist-CLPYXg1u.js} +0 -0
  219. /package/dist/assets/{dockerfile-Baf4U3xT.js → dockerfile-DibBi8b6.js} +0 -0
  220. /package/dist/assets/{ecl-CT7udlkQ.js → ecl-DlF1KdoW.js} +0 -0
  221. /package/dist/assets/{editor.api2-B9h6iFPK.js → editor.api2-D74ufkbr.js} +0 -0
  222. /package/dist/assets/{elixir-CIbAZw5l.js → elixir-CHQ8uFT3.js} +0 -0
  223. /package/dist/assets/{flow9-Dqiw45WV.js → flow9-8nUnVKk1.js} +0 -0
  224. /package/dist/assets/{fsharp-E8E2L9jU.js → fsharp-DNZNZyEE.js} +0 -0
  225. /package/dist/assets/{go-CouVSN4D.js → go-_kPJjNc_.js} +0 -0
  226. /package/dist/assets/{graphlib-CbSvZcZU.js → graphlib-CBGF9gA9.js} +0 -0
  227. /package/dist/assets/{graphql-a6h6h5-t.js → graphql-4HCNVt5g.js} +0 -0
  228. /package/dist/assets/{hcl-vso3e6N-.js → hcl-C87ymY0o.js} +0 -0
  229. /package/dist/assets/{ini-SwZZkotR.js → ini-BYrrGNTc.js} +0 -0
  230. /package/dist/assets/{init-BPLPMQ3Y.js → init-8_PCa3LO.js} +0 -0
  231. /package/dist/assets/{java-CtRLC4_3.js → java-B211rJbk.js} +0 -0
  232. /package/dist/assets/{julia-CuJWFKQH.js → julia-DVPsRtBm.js} +0 -0
  233. /package/dist/assets/{katex-C8DPyI6s.js → katex-C2iYad0m.js} +0 -0
  234. /package/dist/assets/{kotlin-B_kBhsre.js → kotlin-BYyNerjp.js} +0 -0
  235. /package/dist/assets/{less-CtrHIfz3.js → less-EC9UblGE.js} +0 -0
  236. /package/dist/assets/{lexon-yVt1pJLu.js → lexon-B1yuUdqW.js} +0 -0
  237. /package/dist/assets/{lua-prOwgJ4U.js → lua-Bo6GFzxT.js} +0 -0
  238. /package/dist/assets/{m3-DhrdTQ84.js → m3-C-Y9cBVI.js} +0 -0
  239. /package/dist/assets/{markdown-DoI5HwSM.js → markdown-BHu-wLbe.js} +0 -0
  240. /package/dist/assets/{mermaid-parser.core-BQtepoLd.js → mermaid-parser.core-IxcGtNYG.js} +0 -0
  241. /package/dist/assets/{mips-Dye5jsBU.js → mips-Dkds26rX.js} +0 -0
  242. /package/dist/assets/{msdax-TkNOyIL8.js → msdax-PU4g-8_g.js} +0 -0
  243. /package/dist/assets/{mysql-CEc-RKZF.js → mysql-fPpuGpS7.js} +0 -0
  244. /package/dist/assets/{objective-c-CqqCsQz0.js → objective-c-Wy1e5JUf.js} +0 -0
  245. /package/dist/assets/{pascal-DO_pj8yg.js → pascal-BIXwvrqU.js} +0 -0
  246. /package/dist/assets/{pascaligo-1cbdZdF3.js → pascaligo-DwR0AtL_.js} +0 -0
  247. /package/dist/assets/{path-YdFzr2W6.js → path-BxQuywGl.js} +0 -0
  248. /package/dist/assets/{perl-B_N3PYfC.js → perl-CGHAl8my.js} +0 -0
  249. /package/dist/assets/{pgsql-9iYk1FQ7.js → pgsql-B91M73E2.js} +0 -0
  250. /package/dist/assets/{php-rO2sgoUM.js → php-C70ttrCx.js} +0 -0
  251. /package/dist/assets/{pla-Cbgv60kq.js → pla-BNrXaRh0.js} +0 -0
  252. /package/dist/assets/{postiats-y6xrhy_M.js → postiats-Dv2IAZR9.js} +0 -0
  253. /package/dist/assets/{powerquery-BaD8gWNE.js → powerquery-CkY5Wq7J.js} +0 -0
  254. /package/dist/assets/{powershell-BMhG4JVe.js → powershell-uGIYLU3C.js} +0 -0
  255. /package/dist/assets/{protobuf-CdOP5kN3.js → protobuf-CavfmJu-.js} +0 -0
  256. /package/dist/assets/{pug-Ck60hNF9.js → pug-Drqnyitx.js} +0 -0
  257. /package/dist/assets/{qrcode-DV-_rjU8.js → qrcode-7uGsTEmi.js} +0 -0
  258. /package/dist/assets/{qsharp-BcaKFfP-.js → qsharp-C7yF-Dti.js} +0 -0
  259. /package/dist/assets/{r-rFNes0iK.js → r-HXLGP0_-.js} +0 -0
  260. /package/dist/assets/{redis-DkpQ3K6G.js → redis-C4y6mf2p.js} +0 -0
  261. /package/dist/assets/{redshift-Di9N5TI3.js → redshift-DdQrFPrv.js} +0 -0
  262. /package/dist/assets/{restructuredtext-By5ZzLt6.js → restructuredtext-CBrf2PNj.js} +0 -0
  263. /package/dist/assets/{rough.esm-c4PR5shF.js → rough.esm-6CnTHTkH.js} +0 -0
  264. /package/dist/assets/{ruby-fF5zGiLp.js → ruby-CVtMoX2B.js} +0 -0
  265. /package/dist/assets/{rust-C5b3AYY6.js → rust-Ckq4sbMP.js} +0 -0
  266. /package/dist/assets/{sb-CD07UNRs.js → sb-er66OpE3.js} +0 -0
  267. /package/dist/assets/{scala-jtn1ip9G.js → scala-BdWZz-hH.js} +0 -0
  268. /package/dist/assets/{scheme-C4vqMkn_.js → scheme-kTV_s2kP.js} +0 -0
  269. /package/dist/assets/{scss-I4Glxjbz.js → scss-Bymy2VJQ.js} +0 -0
  270. /package/dist/assets/{shell-DubUcuns.js → shell-bwEP1LQ1.js} +0 -0
  271. /package/dist/assets/{solidity-CNy2KSl2.js → solidity-DCddeBgL.js} +0 -0
  272. /package/dist/assets/{sophia-D9_URt84.js → sophia-BMN3AQig.js} +0 -0
  273. /package/dist/assets/{sparql-DviXpm4C.js → sparql-CSXOLjzi.js} +0 -0
  274. /package/dist/assets/{sql-vDRbAZgK.js → sql-COg8ewmK.js} +0 -0
  275. /package/dist/assets/{st-CnpnRqA7.js → st-CIaZtBS_.js} +0 -0
  276. /package/dist/assets/{swift-D_g8Ganz.js → swift-DXiOhUHu.js} +0 -0
  277. /package/dist/assets/{systemverilog-OuZa5h1M.js → systemverilog-BTDE6Dr2.js} +0 -0
  278. /package/dist/assets/{tcl-DBm9ucsV.js → tcl-B0zy9NvV.js} +0 -0
  279. /package/dist/assets/{twig-BRzEqX5Y.js → twig-D594l4Z1.js} +0 -0
  280. /package/dist/assets/{typespec-D4ZgGeX8.js → typespec-XKVjaPqo.js} +0 -0
  281. /package/dist/assets/{useWorkspaceMenuKeyboard-Brws6Ar9.js → useWorkspaceMenuKeyboard-Bpy_s69Z.js} +0 -0
  282. /package/dist/assets/{vb-DcP4s6WS.js → vb-B2wYoKRV.js} +0 -0
  283. /package/dist/assets/{wgsl-CnRJ_QUf.js → wgsl-D0Yf2LbK.js} +0 -0
  284. /package/dist/assets/{workspace-working-copy-D8-s_Sgh.js → workspace-working-copy-C3FSR3N6.js} +0 -0
@@ -0,0 +1,37 @@
1
+ const crypto = require('crypto');
2
+ const fs = require('fs');
3
+ const path = require('path');
4
+
5
+ function atomicWriteJson(file, value, options = {}) {
6
+ const fileSystem = options.fileSystem || fs;
7
+ const temporaryFile = `${file}.${process.pid}.${crypto.randomUUID()}.tmp`;
8
+ const suffix = options.trailingNewline ? '\n' : '';
9
+ let descriptor = null;
10
+
11
+ fileSystem.mkdirSync(path.dirname(file), { recursive: true });
12
+ try {
13
+ descriptor = fileSystem.openSync(temporaryFile, 'wx', options.mode);
14
+ fileSystem.writeFileSync(descriptor, `${JSON.stringify(value, null, 2)}${suffix}`, 'utf8');
15
+ fileSystem.fdatasyncSync(descriptor);
16
+ fileSystem.closeSync(descriptor);
17
+ descriptor = null;
18
+ fileSystem.renameSync(temporaryFile, file);
19
+ } finally {
20
+ if (descriptor !== null) {
21
+ try {
22
+ fileSystem.closeSync(descriptor);
23
+ } catch {
24
+ // Preserve the original write failure.
25
+ }
26
+ }
27
+ try {
28
+ fileSystem.unlinkSync(temporaryFile);
29
+ } catch {
30
+ // A successful rename already removed the temporary path.
31
+ }
32
+ }
33
+ }
34
+
35
+ module.exports = {
36
+ atomicWriteJson,
37
+ };
@@ -49,7 +49,8 @@ const CLI_AGENTS = [
49
49
  permissions: {
50
50
  supportsDangerousSkip: true,
51
51
  dangerousSkipArgs: ['--dangerously-skip-permissions']
52
- }
52
+ },
53
+ systemPromptArg: '--append-system-prompt'
53
54
  },
54
55
  {
55
56
  name: 'bash',
@@ -459,12 +460,13 @@ function resolveLaunchCommand(command, options = {}) {
459
460
  launchArgs.unshift('--permission-mode', claudePermissionMode);
460
461
  }
461
462
 
462
- if (
463
- options.mainAgentSystemPrompt &&
464
- spec &&
465
- typeof spec.systemPromptArg === 'string'
466
- ) {
467
- launchArgs.push(spec.systemPromptArg, options.mainAgentSystemPrompt);
463
+ const systemPrompt = [options.farmingSystemPrompt, options.mainAgentSystemPrompt]
464
+ .filter(value => typeof value === 'string' && value.trim())
465
+ .join('\n\n');
466
+ if (systemPrompt && spec && typeof spec.systemPromptArg === 'string') {
467
+ launchArgs.push(spec.systemPromptArg, systemPrompt);
468
+ } else if (systemPrompt && spec?.name === 'codex') {
469
+ launchArgs.unshift('-c', `developer_instructions=${JSON.stringify(systemPrompt)}`);
468
470
  }
469
471
 
470
472
  return {
@@ -1,7 +1,9 @@
1
1
  const fs = require('fs');
2
2
  const path = require('path');
3
3
  const os = require('os');
4
+ const { atomicWriteJson } = require('./atomic-json-store');
4
5
  const { ensureMainAgentSkillFiles } = require('./main-agent-skills');
6
+ const { ensureFarmingAgentBootstrapFile } = require('./farming-agent-bootstrap');
5
7
  const { normalizeClaudeModelValue } = require('./claude-settings');
6
8
  const { isTemporaryProviderSessionId } = require('./provider-session-id');
7
9
  const { FarmingSessionStore, MAX_MAIN_PAGE_SESSION_KEYS } = require('./farming-session-store');
@@ -68,6 +70,8 @@ const DEFAULT_CRT_TERMINAL_FONT_SIZE = 15;
68
70
  const MIN_CRT_TERMINAL_FONT_SIZE = 10;
69
71
  const MAX_CRT_TERMINAL_FONT_SIZE = 20;
70
72
  const MAX_INSTANCE_NAME_LENGTH = 80;
73
+ const MAX_PROJECT_OPERATIONS = 32;
74
+ const PROJECT_OPERATION_ID_PATTERN = /^[A-Za-z0-9._:-]{1,160}$/;
71
75
 
72
76
  const PERSISTED_SETTING_KEYS = new Set([
73
77
  'workspace',
@@ -76,12 +80,18 @@ const PERSISTED_SETTING_KEYS = new Set([
76
80
  'projectWorkspaces',
77
81
  'pinnedProjectWorkspaces',
78
82
  'projectNames',
83
+ 'projectOperations',
79
84
  'instanceName',
80
85
  'theme',
81
86
  'appearance',
82
87
  'language',
88
+ 'restReminderIntervalSeconds',
83
89
  'heartbeatInterval',
84
90
  'dangerouslySkipAgentPermissionsByDefault',
91
+ 'browserExtensionEnabled',
92
+ 'browserSource',
93
+ 'browserExecutablePath',
94
+ 'browserExternalCdpUrl',
85
95
  'crtSkinEffectsEnabled',
86
96
  'crtDynamicHeatEnabled',
87
97
  'crtTerminalFontSize',
@@ -113,8 +123,8 @@ function cloneAgentHomes(agentHomes) {
113
123
  }
114
124
 
115
125
  class ConfigManager {
116
- constructor() {
117
- this.farmingDir = storageLayout.farmingConfigDir();
126
+ constructor(options = {}) {
127
+ this.farmingDir = options.configDir || storageLayout.farmingConfigDir();
118
128
  this.settingsFile = storageLayout.settingsFile(this.farmingDir);
119
129
  this.sessionStore = new FarmingSessionStore(this.farmingDir, {
120
130
  normalizeMainPageSessionKeys: keys => this.normalizeMainPageSessionKeys(keys),
@@ -122,6 +132,7 @@ class ConfigManager {
122
132
  this.runHistoryStore = new RunHistoryStore(this.farmingDir, {
123
133
  normalizeTaskHistory: entries => this.normalizeTaskHistory(entries),
124
134
  });
135
+ this.writeJson = typeof options.writeJson === 'function' ? options.writeJson : atomicWriteJson;
125
136
  this.settings = null;
126
137
  }
127
138
 
@@ -221,7 +232,7 @@ class ConfigManager {
221
232
  const expanded = this.expandWorkspacePath(entry);
222
233
  if (!expanded) continue;
223
234
  const resolved = path.resolve(expanded);
224
- if (resolved === path.parse(resolved).root || this.isInternalWorkspace(resolved)) continue;
235
+ if (this.isInternalWorkspace(resolved)) continue;
225
236
  let canonical = resolved;
226
237
  try {
227
238
  canonical = fs.realpathSync(resolved);
@@ -305,12 +316,18 @@ class ConfigManager {
305
316
  projectWorkspaces: [],
306
317
  pinnedProjectWorkspaces: [],
307
318
  projectNames: {},
319
+ projectOperations: {},
308
320
  instanceName: '',
309
321
  theme: 'terminal',
310
322
  appearance: 'system',
311
323
  language: 'en',
324
+ restReminderIntervalSeconds: null,
312
325
  heartbeatInterval: 1000,
313
326
  dangerouslySkipAgentPermissionsByDefault: false,
327
+ browserExtensionEnabled: false,
328
+ browserSource: process.env.FARMING_BROWSER_CDP_URL ? 'external-cdp' : 'system',
329
+ browserExecutablePath: process.env.FARMING_BROWSER_EXECUTABLE || '',
330
+ browserExternalCdpUrl: process.env.FARMING_BROWSER_CDP_URL || 'http://127.0.0.1:9222',
314
331
  crtSkinEffectsEnabled: true,
315
332
  crtDynamicHeatEnabled: false,
316
333
  crtTerminalFontSize: DEFAULT_CRT_TERMINAL_FONT_SIZE,
@@ -329,7 +346,7 @@ class ConfigManager {
329
346
  codexModelPreset: 'gpt-5.5:xhigh',
330
347
  version: '2'
331
348
  };
332
- fs.writeFileSync(this.settingsFile, JSON.stringify(defaultSettings, null, 2));
349
+ this.writeSettingsFile(defaultSettings);
333
350
  console.log('Created default settings:', this.settingsFile);
334
351
  }
335
352
 
@@ -341,12 +358,18 @@ class ConfigManager {
341
358
  projectWorkspaces: [],
342
359
  pinnedProjectWorkspaces: [],
343
360
  projectNames: {},
361
+ projectOperations: {},
344
362
  instanceName: '',
345
363
  theme: 'terminal',
346
364
  appearance: 'system',
347
365
  language: 'en',
366
+ restReminderIntervalSeconds: null,
348
367
  heartbeatInterval: 1000,
349
368
  dangerouslySkipAgentPermissionsByDefault: false,
369
+ browserExtensionEnabled: false,
370
+ browserSource: process.env.FARMING_BROWSER_CDP_URL ? 'external-cdp' : 'system',
371
+ browserExecutablePath: process.env.FARMING_BROWSER_EXECUTABLE || '',
372
+ browserExternalCdpUrl: process.env.FARMING_BROWSER_CDP_URL || 'http://127.0.0.1:9222',
350
373
  crtSkinEffectsEnabled: true,
351
374
  crtDynamicHeatEnabled: false,
352
375
  crtTerminalFontSize: DEFAULT_CRT_TERMINAL_FONT_SIZE,
@@ -391,6 +414,7 @@ class ConfigManager {
391
414
  this.settings.projectWorkspaces = this.normalizeProjectWorkspaces(this.settings.projectWorkspaces);
392
415
  this.settings.pinnedProjectWorkspaces = this.normalizeProjectWorkspaces(this.settings.pinnedProjectWorkspaces);
393
416
  this.settings.projectNames = this.normalizeProjectNames(this.settings.projectNames);
417
+ this.settings.projectOperations = this.normalizeProjectOperations(this.settings.projectOperations);
394
418
  this.settings.instanceName = this.normalizeInstanceName(this.settings.instanceName);
395
419
  this.settings.agentHomes = this.normalizeAgentHomes(this.settings.agentHomes);
396
420
  if (this.settings.updateUrl === LEGACY_DEFAULT_UPDATE_URL || this.settings.updateUrl === API_DEFAULT_UPDATE_URL) {
@@ -407,20 +431,29 @@ class ConfigManager {
407
431
  this.runHistoryStore.init({ legacyTaskHistory });
408
432
  this.settings.appearance = this.normalizeAppearance(this.settings.appearance);
409
433
  this.settings.language = this.normalizeLanguage(this.settings.language);
434
+ this.settings.restReminderIntervalSeconds = this.normalizeRestReminderIntervalSeconds(
435
+ this.settings.restReminderIntervalSeconds,
436
+ );
437
+ this.settings.browserExtensionEnabled = this.settings.browserExtensionEnabled === true;
438
+ this.settings.browserSource = this.normalizeBrowserSource(this.settings.browserSource);
439
+ this.settings.browserExecutablePath = this.normalizeBrowserSetting(this.settings.browserExecutablePath);
440
+ this.settings.browserExternalCdpUrl = this.normalizeBrowserSetting(this.settings.browserExternalCdpUrl)
441
+ || 'http://127.0.0.1:9222';
410
442
  this.settings.crtSkinEffectsEnabled = this.settings.crtSkinEffectsEnabled !== false;
411
443
  this.settings.crtDynamicHeatEnabled = this.settings.crtDynamicHeatEnabled === true;
412
444
  this.settings.crtTerminalFontSize = this.normalizeCrtTerminalFontSize(this.settings.crtTerminalFontSize);
413
445
  this.normalizeAgentLaunchSettings(launchRawSettings);
414
446
  this.pruneUnknownSettings();
415
447
  ensureMainAgentSkillFiles(this.farmingDir);
448
+ ensureFarmingAgentBootstrapFile(this.farmingDir);
416
449
  this.writeSettingsFile();
417
450
  console.log('Loaded settings:', this.settings);
418
451
  }
419
452
 
420
- pruneUnknownSettings() {
421
- for (const key of Object.keys(this.settings || {})) {
453
+ pruneUnknownSettings(settings = this.settings) {
454
+ for (const key of Object.keys(settings || {})) {
422
455
  if (!PERSISTED_SETTING_KEYS.has(key)) {
423
- delete this.settings[key];
456
+ delete settings[key];
424
457
  }
425
458
  }
426
459
  }
@@ -438,6 +471,16 @@ class ConfigManager {
438
471
  );
439
472
  }
440
473
 
474
+ normalizeRestReminderIntervalSeconds(value) {
475
+ if (value === null || value === undefined || value === '') return null;
476
+ const seconds = Number(value);
477
+ if (!Number.isInteger(seconds)) return null;
478
+ if (seconds === 0 || seconds === 5) return seconds;
479
+ return seconds >= 60 && seconds <= 240 * 60 && seconds % 60 === 0
480
+ ? seconds
481
+ : null;
482
+ }
483
+
441
484
  normalizeCrtTerminalFontSize(value) {
442
485
  const fontSize = Number(value);
443
486
  if (!Number.isFinite(fontSize)) return DEFAULT_CRT_TERMINAL_FONT_SIZE;
@@ -498,16 +541,59 @@ class ConfigManager {
498
541
  return normalized;
499
542
  }
500
543
 
544
+ normalizeProjectOperations(projectOperations) {
545
+ if (!projectOperations || typeof projectOperations !== 'object' || Array.isArray(projectOperations)) return {};
546
+ const entries = Object.entries(projectOperations)
547
+ .filter(([id, operation]) => (
548
+ PROJECT_OPERATION_ID_PATTERN.test(id)
549
+ && operation
550
+ && typeof operation === 'object'
551
+ && ['create-worktree', 'delete-worktree'].includes(operation.type)
552
+ && ['pending', 'unknown', 'succeeded', 'failed', 'blocked'].includes(operation.state)
553
+ ))
554
+ .sort((left, right) => (Number(right[1].updatedAt) || 0) - (Number(left[1].updatedAt) || 0))
555
+ .map(([id, operation]) => [id, {
556
+ id,
557
+ type: operation.type,
558
+ state: operation.state,
559
+ signature: typeof operation.signature === 'string' ? operation.signature.slice(0, 128) : '',
560
+ request: operation.request && typeof operation.request === 'object'
561
+ ? JSON.parse(JSON.stringify(operation.request))
562
+ : {},
563
+ result: operation.result && typeof operation.result === 'object'
564
+ ? JSON.parse(JSON.stringify(operation.result))
565
+ : null,
566
+ error: typeof operation.error === 'string' ? operation.error.slice(0, 2000) : '',
567
+ startedAt: Number(operation.startedAt) || 0,
568
+ updatedAt: Number(operation.updatedAt) || 0,
569
+ finishedAt: Number(operation.finishedAt) || null,
570
+ }]);
571
+ const unresolved = entries.filter(([, operation]) => (
572
+ ['pending', 'unknown', 'blocked'].includes(operation.state)
573
+ ));
574
+ const terminal = entries.filter(([, operation]) => (
575
+ !['pending', 'unknown', 'blocked'].includes(operation.state)
576
+ ));
577
+ return Object.fromEntries([
578
+ ...unresolved,
579
+ ...terminal.slice(0, Math.max(0, MAX_PROJECT_OPERATIONS - unresolved.length)),
580
+ ]);
581
+ }
582
+
501
583
  setProjectName(workspace, name) {
502
584
  const normalized = this.normalizeProjectNames({ [workspace]: name });
503
585
  const entry = Object.entries(normalized)[0];
504
586
  if (!entry) throw new Error('Project workspace and name are required');
505
587
  const [normalizedWorkspace, normalizedName] = entry;
506
- this.settings.projectNames = {
507
- ...this.settings.projectNames,
508
- [normalizedWorkspace]: normalizedName,
588
+ const nextSettings = {
589
+ ...this.settings,
590
+ projectNames: {
591
+ ...this.settings.projectNames,
592
+ [normalizedWorkspace]: normalizedName,
593
+ },
509
594
  };
510
- this.writeSettingsFile();
595
+ this.writeSettingsFile(nextSettings);
596
+ this.settings = nextSettings;
511
597
  return {
512
598
  workspace: normalizedWorkspace,
513
599
  name: normalizedName,
@@ -522,6 +608,14 @@ class ConfigManager {
522
608
  return ['en', 'zh'].includes(language) ? language : 'en';
523
609
  }
524
610
 
611
+ normalizeBrowserSource(source) {
612
+ return source === 'external-cdp' ? source : 'system';
613
+ }
614
+
615
+ normalizeBrowserSetting(value) {
616
+ return String(value || '').trim().slice(0, 2000);
617
+ }
618
+
525
619
  normalizeUpdateUrl(value) {
526
620
  const url = String(value || '').trim();
527
621
  if (!url) return DEFAULT_UPDATE_URL;
@@ -668,23 +762,23 @@ class ConfigManager {
668
762
  return merged;
669
763
  }
670
764
 
671
- applyCodexProfileToLegacySettings(codexProfile) {
672
- this.settings.codexApprovalMode = codexProfile.approvalMode;
673
- this.settings.codexModel = codexProfile.model;
674
- this.settings.codexReasoningEffort = codexProfile.reasoningEffort;
675
- this.settings.codexServiceTier = codexProfile.serviceTier;
676
- this.settings.codexModelPreset = codexProfile.modelPreset;
765
+ applyCodexProfileToLegacySettings(codexProfile, settings = this.settings) {
766
+ settings.codexApprovalMode = codexProfile.approvalMode;
767
+ settings.codexModel = codexProfile.model;
768
+ settings.codexReasoningEffort = codexProfile.reasoningEffort;
769
+ settings.codexServiceTier = codexProfile.serviceTier;
770
+ settings.codexModelPreset = codexProfile.modelPreset;
677
771
  }
678
772
 
679
- normalizeAgentLaunchSettings(rawSettings = {}) {
773
+ normalizeAgentLaunchSettings(rawSettings = {}, settings = this.settings) {
680
774
  const changedProfiles = this.getChangedAgentLaunchProfiles(rawSettings);
681
- const mergedProfiles = this.mergeAgentLaunchProfiles(this.settings.agentLaunchProfiles, changedProfiles);
682
- this.settings.agentLaunchProfiles = {
775
+ const mergedProfiles = this.mergeAgentLaunchProfiles(settings.agentLaunchProfiles, changedProfiles);
776
+ settings.agentLaunchProfiles = {
683
777
  codex: this.normalizeCodexLaunchProfile(mergedProfiles.codex, changedProfiles.codex || {}),
684
778
  claude: this.normalizeClaudeLaunchProfile(mergedProfiles.claude),
685
779
  };
686
- this.settings.defaultLaunchAgent = this.normalizeDefaultLaunchAgent(this.settings.defaultLaunchAgent);
687
- this.applyCodexProfileToLegacySettings(this.settings.agentLaunchProfiles.codex);
780
+ settings.defaultLaunchAgent = this.normalizeDefaultLaunchAgent(settings.defaultLaunchAgent);
781
+ this.applyCodexProfileToLegacySettings(settings.agentLaunchProfiles.codex, settings);
688
782
  }
689
783
 
690
784
  getWorkspace() {
@@ -760,8 +854,10 @@ class ConfigManager {
760
854
  }
761
855
 
762
856
  getSettings() {
857
+ const publicSettings = { ...this.settings };
858
+ delete publicSettings.projectOperations;
763
859
  return {
764
- ...this.settings,
860
+ ...publicSettings,
765
861
  instanceName: this.getInstanceName(),
766
862
  workspace: this.farmingDir,
767
863
  mainPageSessionKeys: this.getMainPageSessionKeys(),
@@ -783,7 +879,6 @@ class ConfigManager {
783
879
  }
784
880
  if (
785
881
  !canonicalWorkspace
786
- || canonicalWorkspace === path.parse(canonicalWorkspace).root
787
882
  || this.isInternalWorkspace(canonicalWorkspace)
788
883
  ) {
789
884
  throw new Error('Project workspace is invalid or unavailable');
@@ -860,6 +955,64 @@ class ConfigManager {
860
955
  }
861
956
  }
862
957
 
958
+ getProjectOperation(requestId) {
959
+ const id = String(requestId || '').trim();
960
+ if (!PROJECT_OPERATION_ID_PATTERN.test(id)) return null;
961
+ const operation = this.settings.projectOperations?.[id];
962
+ return operation ? JSON.parse(JSON.stringify(operation)) : null;
963
+ }
964
+
965
+ commitProjectOperation(operation, membership = {}) {
966
+ const normalized = this.normalizeProjectOperations({ [operation?.id]: operation });
967
+ const nextOperation = normalized[operation?.id];
968
+ if (!nextOperation) throw new Error('Project operation is invalid');
969
+ const existingOperation = this.settings.projectOperations?.[nextOperation.id];
970
+ const unresolvedCount = Object.values(this.settings.projectOperations || {})
971
+ .filter(candidate => ['pending', 'unknown', 'blocked'].includes(candidate.state)).length;
972
+ if (!existingOperation && nextOperation.state === 'pending' && unresolvedCount >= MAX_PROJECT_OPERATIONS) {
973
+ throw new Error('Too many unresolved Project operations require reconciliation');
974
+ }
975
+ const nextOperations = this.normalizeProjectOperations({
976
+ ...this.settings.projectOperations,
977
+ [nextOperation.id]: nextOperation,
978
+ });
979
+ const nextSettings = {
980
+ ...this.settings,
981
+ projectOperations: nextOperations,
982
+ };
983
+ if (membership.mountWorkspace) {
984
+ const mounted = this.normalizeProjectWorkspaces([
985
+ membership.mountWorkspace,
986
+ ...(this.settings.projectWorkspaces || []),
987
+ ]);
988
+ nextSettings.projectWorkspaces = mounted;
989
+ nextSettings.pinnedProjectWorkspaces = this.normalizeProjectWorkspaces(
990
+ this.settings.pinnedProjectWorkspaces,
991
+ );
992
+ }
993
+ if (membership.removeWorkspace) {
994
+ const expanded = this.expandWorkspacePath(membership.removeWorkspace);
995
+ const resolved = expanded ? path.resolve(expanded) : '';
996
+ const candidates = new Set([expanded, resolved].filter(Boolean));
997
+ try {
998
+ candidates.add(fs.realpathSync(resolved));
999
+ } catch {
1000
+ // A deleted worktree is removed by its last canonical path.
1001
+ }
1002
+ nextSettings.projectWorkspaces = (this.settings.projectWorkspaces || [])
1003
+ .filter(workspace => !candidates.has(workspace));
1004
+ nextSettings.pinnedProjectWorkspaces = (this.settings.pinnedProjectWorkspaces || [])
1005
+ .filter(workspace => !candidates.has(workspace));
1006
+ }
1007
+ this.writeSettingsFile(nextSettings);
1008
+ this.settings = nextSettings;
1009
+ return {
1010
+ operation: JSON.parse(JSON.stringify(nextOperation)),
1011
+ projectWorkspaces: [...(nextSettings.projectWorkspaces || [])],
1012
+ pinnedProjectWorkspaces: [...(nextSettings.pinnedProjectWorkspaces || [])],
1013
+ };
1014
+ }
1015
+
863
1016
  getMainPageSessionKeys() {
864
1017
  return this.sessionStore ? this.sessionStore.getMainPageSessionKeys() : [];
865
1018
  }
@@ -904,19 +1057,8 @@ class ConfigManager {
904
1057
  return this.runHistoryStore ? this.runHistoryStore.getEntries() : [];
905
1058
  }
906
1059
 
907
- writeSettingsFile() {
908
- fs.mkdirSync(this.farmingDir, { recursive: true });
909
- const tempFile = `${this.settingsFile}.${process.pid}.${Date.now()}.tmp`;
910
- try {
911
- fs.writeFileSync(tempFile, JSON.stringify(this.settings, null, 2));
912
- fs.renameSync(tempFile, this.settingsFile);
913
- } finally {
914
- try {
915
- fs.unlinkSync(tempFile);
916
- } catch {
917
- // A successful rename already removed the temporary path.
918
- }
919
- }
1060
+ writeSettingsFile(settings = this.settings) {
1061
+ this.writeJson(this.settingsFile, settings);
920
1062
  }
921
1063
 
922
1064
  appendTaskHistory(entry) {
@@ -930,6 +1072,7 @@ class ConfigManager {
930
1072
  : undefined;
931
1073
  const settingsPatch = { ...(newSettings || {}) };
932
1074
  delete settingsPatch.mainPageSessionKeys;
1075
+ delete settingsPatch.projectOperations;
933
1076
  const incomingTaskHistory = Object.prototype.hasOwnProperty.call(settingsPatch, 'taskHistory')
934
1077
  ? settingsPatch.taskHistory
935
1078
  : undefined;
@@ -937,38 +1080,48 @@ class ConfigManager {
937
1080
  const previousMainWorkspace = this.settings.lastMainWorkspace || this.farmingDir;
938
1081
  const previousProfiles = this.settings.agentLaunchProfiles || {};
939
1082
  const incomingProfiles = settingsPatch.agentLaunchProfiles || {};
940
- this.settings = {
1083
+ const nextSettings = {
941
1084
  ...this.settings,
942
1085
  ...settingsPatch,
943
1086
  agentLaunchProfiles: this.mergeAgentLaunchProfiles(previousProfiles, incomingProfiles),
944
1087
  workspace: this.farmingDir
945
1088
  };
946
- this.settings.lastMainWorkspace = this.normalizeMainWorkspace(this.settings.lastMainWorkspace, previousMainWorkspace);
947
- this.settings.workspaceHistory = this.normalizeWorkspaceHistory(this.settings.workspaceHistory);
948
- this.settings.projectWorkspaces = this.normalizeProjectWorkspaces(this.settings.projectWorkspaces);
949
- this.settings.pinnedProjectWorkspaces = this.normalizeProjectWorkspaces(this.settings.pinnedProjectWorkspaces);
950
- this.settings.projectNames = this.normalizeProjectNames(this.settings.projectNames);
951
- this.settings.instanceName = this.normalizeInstanceName(this.settings.instanceName);
952
- this.settings.agentHomes = this.normalizeAgentHomes(this.settings.agentHomes);
953
- this.settings.updateUrl = this.normalizeUpdateUrl(this.settings.updateUrl);
954
- this.settings.searchTimeoutMs = this.normalizeSearchTimeoutMs(this.settings.searchTimeoutMs);
955
- delete this.settings.codexRuntimeMode;
956
- delete this.settings.mainPageSessionKeys;
957
- delete this.settings.taskHistory;
1089
+ nextSettings.lastMainWorkspace = this.normalizeMainWorkspace(nextSettings.lastMainWorkspace, previousMainWorkspace);
1090
+ nextSettings.workspaceHistory = this.normalizeWorkspaceHistory(nextSettings.workspaceHistory);
1091
+ nextSettings.projectWorkspaces = this.normalizeProjectWorkspaces(nextSettings.projectWorkspaces);
1092
+ nextSettings.pinnedProjectWorkspaces = this.normalizeProjectWorkspaces(nextSettings.pinnedProjectWorkspaces);
1093
+ nextSettings.projectNames = this.normalizeProjectNames(nextSettings.projectNames);
1094
+ nextSettings.projectOperations = this.normalizeProjectOperations(nextSettings.projectOperations);
1095
+ nextSettings.instanceName = this.normalizeInstanceName(nextSettings.instanceName);
1096
+ nextSettings.agentHomes = this.normalizeAgentHomes(nextSettings.agentHomes);
1097
+ nextSettings.updateUrl = this.normalizeUpdateUrl(nextSettings.updateUrl);
1098
+ nextSettings.searchTimeoutMs = this.normalizeSearchTimeoutMs(nextSettings.searchTimeoutMs);
1099
+ delete nextSettings.codexRuntimeMode;
1100
+ delete nextSettings.mainPageSessionKeys;
1101
+ delete nextSettings.taskHistory;
958
1102
  if (incomingMainPageSessionKeys !== undefined) {
959
1103
  this.setMainPageSessionKeys(incomingMainPageSessionKeys);
960
1104
  }
961
1105
  if (incomingTaskHistory !== undefined && this.runHistoryStore) {
962
1106
  this.runHistoryStore.setEntries(incomingTaskHistory);
963
1107
  }
964
- this.settings.appearance = this.normalizeAppearance(this.settings.appearance);
965
- this.settings.language = this.normalizeLanguage(this.settings.language);
966
- this.settings.crtSkinEffectsEnabled = this.settings.crtSkinEffectsEnabled !== false;
967
- this.settings.crtDynamicHeatEnabled = this.settings.crtDynamicHeatEnabled === true;
968
- this.settings.crtTerminalFontSize = this.normalizeCrtTerminalFontSize(this.settings.crtTerminalFontSize);
969
- this.normalizeAgentLaunchSettings(settingsPatch);
970
- this.pruneUnknownSettings();
971
- this.writeSettingsFile();
1108
+ nextSettings.appearance = this.normalizeAppearance(nextSettings.appearance);
1109
+ nextSettings.language = this.normalizeLanguage(nextSettings.language);
1110
+ nextSettings.restReminderIntervalSeconds = this.normalizeRestReminderIntervalSeconds(
1111
+ nextSettings.restReminderIntervalSeconds,
1112
+ );
1113
+ nextSettings.browserExtensionEnabled = nextSettings.browserExtensionEnabled === true;
1114
+ nextSettings.browserSource = this.normalizeBrowserSource(nextSettings.browserSource);
1115
+ nextSettings.browserExecutablePath = this.normalizeBrowserSetting(nextSettings.browserExecutablePath);
1116
+ nextSettings.browserExternalCdpUrl = this.normalizeBrowserSetting(nextSettings.browserExternalCdpUrl)
1117
+ || 'http://127.0.0.1:9222';
1118
+ nextSettings.crtSkinEffectsEnabled = nextSettings.crtSkinEffectsEnabled !== false;
1119
+ nextSettings.crtDynamicHeatEnabled = nextSettings.crtDynamicHeatEnabled === true;
1120
+ nextSettings.crtTerminalFontSize = this.normalizeCrtTerminalFontSize(nextSettings.crtTerminalFontSize);
1121
+ this.normalizeAgentLaunchSettings(settingsPatch, nextSettings);
1122
+ this.pruneUnknownSettings(nextSettings);
1123
+ this.writeSettingsFile(nextSettings);
1124
+ this.settings = nextSettings;
972
1125
  }
973
1126
  }
974
1127
 
@@ -419,9 +419,10 @@ function createControlRouter(agentManager, options = {}) {
419
419
 
420
420
  router.delete('/agents/:agentId', async (req, res) => {
421
421
  const agentId = req.params.agentId;
422
+ const recordHistory = req.query?.recordHistory !== '0';
422
423
  let requested;
423
424
  try {
424
- requested = await agentManager.requestKillAgent(agentId);
425
+ requested = await agentManager.requestKillAgent(agentId, { recordHistory });
425
426
  } catch (error) {
426
427
  res.status(503).json({
427
428
  error: error?.message || 'Agent lifecycle recovery is unavailable',