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.
Files changed (264) hide show
  1. package/README.md +9 -2
  2. package/README.zh_cn.md +9 -2
  3. package/THIRD_PARTY_NOTICES.md +3 -0
  4. package/backend/acp-runtime.js +15 -1
  5. package/backend/agent-manager.js +1054 -94
  6. package/backend/atomic-json-store.js +37 -0
  7. package/backend/cli-agents.js +9 -7
  8. package/backend/config-manager.js +167 -58
  9. package/backend/control-api.js +2 -1
  10. package/backend/farming-agent-bootstrap.js +49 -0
  11. package/backend/farming-agent-bootstrap.zh_cn.md +5 -0
  12. package/backend/farming-app-cli.js +9 -2
  13. package/backend/farming-cli.js +55 -0
  14. package/backend/farming-session-store.js +92 -89
  15. package/backend/main-agent-skills.js +17 -0
  16. package/backend/native-pty-host-client.js +99 -7
  17. package/backend/native-pty-host-path.js +20 -0
  18. package/backend/native-pty-host.js +6 -5
  19. package/backend/preview-session-manager.js +68 -0
  20. package/backend/provider-adapters.js +44 -5
  21. package/backend/review-session-store.js +32 -25
  22. package/backend/review-state-store.js +13 -14
  23. package/backend/run-history-store.js +13 -16
  24. package/backend/server.js +103 -78
  25. package/backend/storage-layout.js +20 -0
  26. package/backend/workspace-file-router.js +171 -6
  27. package/backend/workspace-file-service.js +19 -0
  28. package/backend/workspace-root-registry.js +6 -4
  29. package/dist/assets/App-CM7Cy-l4.css +1 -0
  30. package/dist/assets/App-CPG4WcjO.js +658 -0
  31. package/dist/assets/{FileEditorMarkdownPreview-KbnE8VC8.js → FileEditorMarkdownPreview-DYOMm-b1.js} +5 -5
  32. package/dist/assets/FileEditorPane-DXXq82aM.js +2 -0
  33. package/dist/assets/IconGlyphs-DrdPjYXr.js +1 -0
  34. package/dist/assets/ProjectFilesSection-CP66XVPl.js +12 -0
  35. package/dist/assets/{ReviewPage-L1vh1Ggs.js → ReviewPage-w21MEPD6.js} +1 -1
  36. package/dist/assets/{abnfDiagram-VRR7QNED-nnglrXFi.js → abnfDiagram-VRR7QNED-BY1qwq8A.js} +1 -1
  37. package/dist/assets/api-w2x5QfxB.js +1 -0
  38. package/dist/assets/{arc-CWoVlxJw.js → arc-DgZRAlwj.js} +1 -1
  39. package/dist/assets/{architectureDiagram-ZJ3FMSHR-BEXMaFfY.js → architectureDiagram-ZJ3FMSHR-jFn144od.js} +1 -1
  40. package/dist/assets/{blockDiagram-677ZJIJ3-1mCezSBk.js → blockDiagram-677ZJIJ3-BmRjlV0H.js} +1 -1
  41. package/dist/assets/{c4Diagram-LMCZKHZV-DhTiI6Uv.js → c4Diagram-LMCZKHZV-DUKul6tT.js} +1 -1
  42. package/dist/assets/{workspace-editor-monaco-DLlws5Ig.js → channel-CI9j3QFD.js} +1 -1
  43. package/dist/assets/chunk-2Q5K7J3B-87UnWaoh.js +1 -0
  44. package/dist/assets/{chunk-32BRIVSS-CCdsKd73.js → chunk-32BRIVSS-rGVu23o5.js} +1 -1
  45. package/dist/assets/{chunk-52WLFC77-Gncjk4LF.js → chunk-52WLFC77-CvFDKgbB.js} +1 -1
  46. package/dist/assets/{chunk-5VM5RSS4-Dlw7AXWp.js → chunk-5VM5RSS4-CFgnRW1C.js} +1 -1
  47. package/dist/assets/{chunk-7BUUIJ7U-ZBRhjLOh.js → chunk-7BUUIJ7U-Cp7bqW6s.js} +1 -1
  48. package/dist/assets/{chunk-C7G6YPKG-CiDVUP82.js → chunk-C7G6YPKG-BIS1gjZO.js} +1 -1
  49. package/dist/assets/{chunk-EX3LRPZG-z6gXP2_N.js → chunk-EX3LRPZG-BKG-cR9j.js} +1 -1
  50. package/dist/assets/{chunk-FWX5IMBZ-DMZBccQa.js → chunk-FWX5IMBZ-B8PsTr8a.js} +2 -2
  51. package/dist/assets/{chunk-HOUHSVGY-D8tIeoPk.js → chunk-HOUHSVGY-BHFOAiXT.js} +1 -1
  52. package/dist/assets/{chunk-ICXQ74PX-b6oQ2Q17.js → chunk-ICXQ74PX-CvLE1J9X.js} +1 -1
  53. package/dist/assets/{chunk-JWPE2WC7-DSyBRaRG.js → chunk-JWPE2WC7-D8misks7.js} +1 -1
  54. package/dist/assets/{chunk-MOJQB5TN-DAVH_p2a.js → chunk-MOJQB5TN-D-oNgSNH.js} +1 -1
  55. package/dist/assets/{chunk-OGEWGWER-DxoiMCI-.js → chunk-OGEWGWER-C2N-la9e.js} +1 -1
  56. package/dist/assets/{chunk-PUDLZKDR-NsOWh7HM.js → chunk-PUDLZKDR-BwI3Jz8q.js} +1 -1
  57. package/dist/assets/{chunk-Q4XR5HBZ-Di4H7L21.js → chunk-Q4XR5HBZ-BuqGfsXA.js} +1 -1
  58. package/dist/assets/{chunk-RYQCIY6F-CZA9pvFX.js → chunk-RYQCIY6F-Bq4XZcGt.js} +1 -1
  59. package/dist/assets/{chunk-V7JOEXUC-DttpZg9Y.js → chunk-V7JOEXUC-QZLzdTTJ.js} +1 -1
  60. package/dist/assets/chunk-VAUOI2AC-Cb0-kQ9g.js +1 -0
  61. package/dist/assets/{chunk-VR4S4FIN-D-Fmtd4Z.js → chunk-VR4S4FIN-DcZTpML_.js} +1 -1
  62. package/dist/assets/{chunk-WYO6CB5R-CUxMtfK2.js → chunk-WYO6CB5R-DCU8wuny.js} +2 -2
  63. package/dist/assets/chunk-XXDRQBXY-BZAC1Krv.js +1 -0
  64. package/dist/assets/{chunk-ZGVPDNZ5-CoV2QEl0.js → chunk-ZGVPDNZ5-B7r85QbY.js} +1 -1
  65. package/dist/assets/{chunk-ZIRB5QZD-DncwlQQc.js → chunk-ZIRB5QZD-BgEnDWhQ.js} +1 -1
  66. package/dist/assets/classDiagram-OUVF2IWQ-DcvGOuvi.js +1 -0
  67. package/dist/assets/classDiagram-v2-EOCWNBFH-DDYUSofQ.js +1 -0
  68. package/dist/assets/code-dark-BDjwj7Um.css +1 -0
  69. package/dist/assets/{cose-bilkent-JH36ORCC-BNIAz0nN.js → cose-bilkent-JH36ORCC-CThXS2qG.js} +1 -1
  70. package/dist/assets/{cssMode-DDWxTPm_.js → cssMode-Bdg5RpcJ.js} +1 -1
  71. package/dist/assets/{cynefinDiagram-TSTJHNR4-0xPI92Lg.js → cynefinDiagram-TSTJHNR4-DxBgzw25.js} +1 -1
  72. package/dist/assets/{dagre-DXlcHgr3.js → dagre-BM-GvGUZ.js} +1 -1
  73. package/dist/assets/{dagre-VKFMJZFB-DfuMG4gU.js → dagre-VKFMJZFB-B7UCRmxR.js} +1 -1
  74. package/dist/assets/{diagram-FQU43EPY-CT00bLZ6.js → diagram-FQU43EPY-CeL2DwKx.js} +1 -1
  75. package/dist/assets/{diagram-G47NLZAW-Xbh7zXUx.js → diagram-G47NLZAW-BEirglCC.js} +1 -1
  76. package/dist/assets/{diagram-NH7WQ7WH-SRDVPfn7.js → diagram-NH7WQ7WH-C6wcI-Rl.js} +1 -1
  77. package/dist/assets/{diagram-OA4YK3LP-CQRli4Np.js → diagram-OA4YK3LP-DpM-XXtJ.js} +1 -1
  78. package/dist/assets/{diagram-WEI45ONY-DMf17WQi.js → diagram-WEI45ONY-BQyaut9k.js} +1 -1
  79. package/dist/assets/{ebnfDiagram-CCIWWBDH-DhpFkgJ5.js → ebnfDiagram-CCIWWBDH-UmAXsUhr.js} +1 -1
  80. package/dist/assets/{erDiagram-Q63AITRT-1IwxWmJo.js → erDiagram-Q63AITRT-CdYLEOch.js} +1 -1
  81. package/dist/assets/file-icons-BOlcJ0ZF.js +1 -0
  82. package/dist/assets/flowDiagram-23GEKE2U-CO-z9-tq.js +1 -0
  83. package/dist/assets/{freemarker2-QBsTERH-.js → freemarker2-DfMDJfnE.js} +1 -1
  84. package/dist/assets/{ganttDiagram-NO4QXBWP-BNCdfyr1.js → ganttDiagram-NO4QXBWP-DgSUNdUB.js} +1 -1
  85. package/dist/assets/{gitGraphDiagram-IHSO6WYX-pbyWZuiX.js → gitGraphDiagram-IHSO6WYX-DjwjtRtF.js} +1 -1
  86. package/dist/assets/{handlebars-CuoxPo5A.js → handlebars-BEdPtzMT.js} +1 -1
  87. package/dist/assets/{html-gX5PEu0Q.js → html-B1fPb1QD.js} +1 -1
  88. package/dist/assets/html2canvas-DsczWxBa.js +5 -0
  89. package/dist/assets/{htmlMode-DIWUbUxd.js → htmlMode-CY2ZjHxH.js} +1 -1
  90. package/dist/assets/{index-DhTUO8DA.js → index-BzQO3pUr.js} +2 -2
  91. package/dist/assets/{infoDiagram-FWYZ7A6U-BcTOgDbe.js → infoDiagram-FWYZ7A6U-Bi9076l2.js} +1 -1
  92. package/dist/assets/{ishikawaDiagram-FXEZZL3T-DCdvGKrn.js → ishikawaDiagram-FXEZZL3T-DaMaNZN7.js} +1 -1
  93. package/dist/assets/{javascript-BYKGIUGs.js → javascript-BV2OsWA0.js} +1 -1
  94. package/dist/assets/{journeyDiagram-5HDEW3XC-BZ-1q_IT.js → journeyDiagram-5HDEW3XC-CfDy69EZ.js} +1 -1
  95. package/dist/assets/{jsonMode-AFmQQP6u.js → jsonMode-zd07aeuX.js} +1 -1
  96. package/dist/assets/{kanban-definition-HUTT4EX6-BIgejCjj.js → kanban-definition-HUTT4EX6-XFvZ722B.js} +1 -1
  97. package/dist/assets/{line-CfptFMmX.js → line-BXK9PKLn.js} +1 -1
  98. package/dist/assets/{linear-Dld3_ERu.js → linear-CULvmq2Z.js} +1 -1
  99. package/dist/assets/{liquid-DBIl-f0z.js → liquid-BParI24X.js} +1 -1
  100. package/dist/assets/{lspLanguageFeatures-DK6tqicV.js → lspLanguageFeatures-BzredkoV.js} +1 -1
  101. package/dist/assets/main-BlVUXfxa.css +1 -0
  102. package/dist/assets/{map-LZWWoeWa.js → map-C9Qw9eWh.js} +1 -1
  103. package/dist/assets/{mdx-DS-bVtZN.js → mdx-vGzUJN98.js} +1 -1
  104. package/dist/assets/{mermaid.core-gSpX30pV.js → mermaid.core-CzazaIKr.js} +3 -3
  105. package/dist/assets/{mindmap-definition-LN4V7U3C-BKBaI4LW.js → mindmap-definition-LN4V7U3C-Uoff7hZ3.js} +1 -1
  106. package/dist/assets/{monaco.contribution-BXz-lfFB.js → monaco.contribution-BmaeBQEc.js} +2 -2
  107. package/dist/assets/{ordinal-BLrOss5K.js → ordinal-r6pIwRdZ.js} +1 -1
  108. package/dist/assets/{pegDiagram-2B236MQR-sBvDY3VX.js → pegDiagram-2B236MQR-CCBiHQjk.js} +1 -1
  109. package/dist/assets/{pieDiagram-ENE6RG2P-DWlWcu08.js → pieDiagram-ENE6RG2P-D61s7l75.js} +1 -1
  110. package/dist/assets/{python-C93q3xXz.js → python-CUwdLgsI.js} +1 -1
  111. package/dist/assets/{quadrantDiagram-ABIIQ3AL-DZutUd3J.js → quadrantDiagram-ABIIQ3AL-D_CWk2DQ.js} +1 -1
  112. package/dist/assets/{railroadDiagram-RFXS5EU6-DQb3lXGM.js → railroadDiagram-RFXS5EU6-CdlCHtxN.js} +1 -1
  113. package/dist/assets/{razor-CBifNGXO.js → razor-BScebUxU.js} +1 -1
  114. package/dist/assets/{requirementDiagram-TGXJPOKE-DqFGXu2e.js → requirementDiagram-TGXJPOKE-BLpHtkoC.js} +1 -1
  115. package/dist/assets/{sankeyDiagram-HTMAVEWB-d7ECahnO.js → sankeyDiagram-HTMAVEWB-CkhYZN2j.js} +1 -1
  116. package/dist/assets/{sequenceDiagram-DBY2YBRQ-DdsbKqct.js → sequenceDiagram-DBY2YBRQ-8bploKJl.js} +1 -1
  117. package/dist/assets/{sizeCapture-X5ZJPWSS-daTB6OcF.js → sizeCapture-X5ZJPWSS-DtSdkS0d.js} +1 -1
  118. package/dist/assets/{src-WBW1QxJt.js → src-6yeq6WBA.js} +1 -1
  119. package/dist/assets/{stateDiagram-2N3HPSRC-P6iSrgwR.js → stateDiagram-2N3HPSRC-DfmVGpfA.js} +1 -1
  120. package/dist/assets/stateDiagram-v2-6OUMAXLB-B2c9CQ-J.js +1 -0
  121. package/dist/assets/{swimlanes-5IMT3BWC-BGnZJx1M.js → swimlanes-5IMT3BWC-DRLk2zRF.js} +2 -2
  122. package/dist/assets/swimlanesDiagram-G3AALYLV-B7_APh_h.js +8 -0
  123. package/dist/assets/{timeline-definition-FHXFAJF6-rA-r3aUR.js → timeline-definition-FHXFAJF6-Cngfy588.js} +1 -1
  124. package/dist/assets/{tsMode-B0oUTcXQ.js → tsMode-DzUAibXK.js} +1 -1
  125. package/dist/assets/{typescript-KJKfYO7K.js → typescript-D9EOeM7N.js} +1 -1
  126. package/dist/assets/{vennDiagram-L72KCM5P-DiVKUjA9.js → vennDiagram-L72KCM5P-DeJn63Lq.js} +1 -1
  127. package/dist/assets/{wardleyDiagram-EHGQE667-Cfxh7UOn.js → wardleyDiagram-EHGQE667-BH1nzy77.js} +1 -1
  128. package/dist/assets/{workers-BSQRIkqa.js → workers-DAiomYZM.js} +1 -1
  129. package/dist/assets/workspace-editor-model-CSGCl21x.js +1 -0
  130. package/dist/assets/workspace-editor-monaco-CFEfFNcA.js +1 -0
  131. package/dist/assets/{workspace-editor-monaco-BIpqcWf1.js → workspace-editor-monaco-tXA-udGx.js} +3 -3
  132. package/dist/assets/workspace-view-state-BS_V8JVs.js +7 -0
  133. package/dist/assets/{xml-C8FAcN6j.js → xml-wWl1dhkQ.js} +1 -1
  134. package/dist/assets/{xychartDiagram-FW5EYKEG--hmryQeL.js → xychartDiagram-FW5EYKEG-SkO2prCU.js} +1 -1
  135. package/dist/assets/{yaml-BQ4NrRhG.js → yaml-Dc2WDaOZ.js} +1 -1
  136. package/dist/crt-markdown-renderer.js +46 -46
  137. package/dist/index.html +1 -1
  138. package/extensions/browser/backend/agent-capability.js +55 -0
  139. package/extensions/browser/backend/browser-mcp-server.js +269 -0
  140. package/extensions/browser/backend/browser-resource-manager.js +524 -0
  141. package/extensions/browser/backend/browser-resource-store.js +149 -0
  142. package/extensions/browser/backend/browser-router.js +128 -0
  143. package/extensions/browser/backend/cdp-browser-runtime.js +648 -0
  144. package/extensions/browser/backend/cdp-client.js +167 -0
  145. package/extensions/browser/backend/executable-discovery.js +91 -0
  146. package/extensions/browser/backend/farming-browser-client.js +83 -0
  147. package/extensions/browser/backend/index.js +7 -0
  148. package/extensions/browser/bin/farming-browser +117 -0
  149. package/extensions/browser/frontend/BrowserSidebarPortals.tsx +362 -0
  150. package/extensions/browser/frontend/BrowserViewer.tsx +329 -0
  151. package/extensions/browser/frontend/browser-resource-state.ts +60 -0
  152. package/extensions/browser/frontend/browser.css +516 -0
  153. package/extensions/browser/frontend/types.ts +25 -0
  154. package/extensions/browser/frontend/useBrowserResources.ts +151 -0
  155. package/frontend/session-bridge.js +7 -6
  156. package/frontend/skins/crt/app.js +64 -4
  157. package/package.json +18 -13
  158. package/shared/browser-protocol.js +1 -1
  159. package/dist/assets/App-BbNBDA__.js +0 -210
  160. package/dist/assets/App-BrP-ENHg.css +0 -1
  161. package/dist/assets/FileEditorPane-5_HUejjd.js +0 -2
  162. package/dist/assets/IconGlyphs-C2WFcoJS.js +0 -1
  163. package/dist/assets/ProjectFilesSection-C1RAJWOE.js +0 -12
  164. package/dist/assets/api-D8nyOEbz.js +0 -1
  165. package/dist/assets/channel-BVWNatUB.js +0 -1
  166. package/dist/assets/chunk-2Q5K7J3B-d5WWh8jo.js +0 -1
  167. package/dist/assets/chunk-VAUOI2AC-UHNr_yhX.js +0 -1
  168. package/dist/assets/chunk-XXDRQBXY-BBXwjhUx.js +0 -1
  169. package/dist/assets/classDiagram-OUVF2IWQ-Db0ib5iE.js +0 -1
  170. package/dist/assets/classDiagram-v2-EOCWNBFH-Bkf69XF9.js +0 -1
  171. package/dist/assets/code-dark-BKtvf0Xc.css +0 -1
  172. package/dist/assets/file-icons-CHWt5nSy.js +0 -1
  173. package/dist/assets/flowDiagram-23GEKE2U-DYFo2xmB.js +0 -1
  174. package/dist/assets/main-XMXgyJJt.css +0 -1
  175. package/dist/assets/stateDiagram-v2-6OUMAXLB-DIYSf_qh.js +0 -1
  176. package/dist/assets/swimlanesDiagram-G3AALYLV-BIkrlKrZ.js +0 -8
  177. package/dist/assets/workspace-editor-model-D0Im34S5.js +0 -1
  178. package/dist/assets/workspace-view-state-BqB4HM7r.js +0 -7
  179. /package/dist/assets/{abap-Cc5D5h5i.js → abap-CtX5nWyX.js} +0 -0
  180. /package/dist/assets/{apex-BKopwLmx.js → apex-DQJRAHwc.js} +0 -0
  181. /package/dist/assets/{array-HeX70jSN.js → array-BTwzYU0x.js} +0 -0
  182. /package/dist/assets/{azcli-DI-C34WA.js → azcli-9SMrOQ4q.js} +0 -0
  183. /package/dist/assets/{bat-Cbp6u42z.js → bat-CBGLUsw1.js} +0 -0
  184. /package/dist/assets/{bicep-4QA1jTCM.js → bicep-pDx8da1v.js} +0 -0
  185. /package/dist/assets/{cameligo-Dda_adPs.js → cameligo-hZV3vQ6_.js} +0 -0
  186. /package/dist/assets/{chunk-Y2CYZVJY-Di0sYzi1.js → chunk-Y2CYZVJY-BYi0W5ea.js} +0 -0
  187. /package/dist/assets/{clojure-CvY8A83L.js → clojure-BhCwU3gq.js} +0 -0
  188. /package/dist/assets/{coffee-4LX2Sv8A.js → coffee-BVB1PsgH.js} +0 -0
  189. /package/dist/assets/{core-B9MwRjDE.js → core-vPkV8P_m.js} +0 -0
  190. /package/dist/assets/{cpp-D4AzSV28.js → cpp-eTjuiNPq.js} +0 -0
  191. /package/dist/assets/{csharp-C4nWN4Yd.js → csharp-1kP-kKZT.js} +0 -0
  192. /package/dist/assets/{csp-CiEaaS5Z.js → csp-C_iQqIes.js} +0 -0
  193. /package/dist/assets/{css-DXxBRsbk.js → css-CxOs7IfP.js} +0 -0
  194. /package/dist/assets/{cypher-DOMzqyFA.js → cypher-Cd-a9Y82.js} +0 -0
  195. /package/dist/assets/{cytoscape.esm-Cl8iC3D7.js → cytoscape.esm-OLojuicg.js} +0 -0
  196. /package/dist/assets/{dart-BWS7MXt_.js → dart-whhXpexn.js} +0 -0
  197. /package/dist/assets/{defaultLocale-GJwWH1Jr.js → defaultLocale-CPimVhfE.js} +0 -0
  198. /package/dist/assets/{dist-ClAL-Tv5.js → dist-CLPYXg1u.js} +0 -0
  199. /package/dist/assets/{dockerfile-Baf4U3xT.js → dockerfile-DibBi8b6.js} +0 -0
  200. /package/dist/assets/{ecl-CT7udlkQ.js → ecl-DlF1KdoW.js} +0 -0
  201. /package/dist/assets/{editor.api2-B9h6iFPK.js → editor.api2-D74ufkbr.js} +0 -0
  202. /package/dist/assets/{elixir-CIbAZw5l.js → elixir-CHQ8uFT3.js} +0 -0
  203. /package/dist/assets/{flow9-Dqiw45WV.js → flow9-8nUnVKk1.js} +0 -0
  204. /package/dist/assets/{fsharp-E8E2L9jU.js → fsharp-DNZNZyEE.js} +0 -0
  205. /package/dist/assets/{go-CouVSN4D.js → go-_kPJjNc_.js} +0 -0
  206. /package/dist/assets/{graphlib-CbSvZcZU.js → graphlib-CBGF9gA9.js} +0 -0
  207. /package/dist/assets/{graphql-a6h6h5-t.js → graphql-4HCNVt5g.js} +0 -0
  208. /package/dist/assets/{hcl-vso3e6N-.js → hcl-C87ymY0o.js} +0 -0
  209. /package/dist/assets/{ini-SwZZkotR.js → ini-BYrrGNTc.js} +0 -0
  210. /package/dist/assets/{init-BPLPMQ3Y.js → init-8_PCa3LO.js} +0 -0
  211. /package/dist/assets/{java-CtRLC4_3.js → java-B211rJbk.js} +0 -0
  212. /package/dist/assets/{julia-CuJWFKQH.js → julia-DVPsRtBm.js} +0 -0
  213. /package/dist/assets/{katex-C8DPyI6s.js → katex-C2iYad0m.js} +0 -0
  214. /package/dist/assets/{kotlin-B_kBhsre.js → kotlin-BYyNerjp.js} +0 -0
  215. /package/dist/assets/{less-CtrHIfz3.js → less-EC9UblGE.js} +0 -0
  216. /package/dist/assets/{lexon-yVt1pJLu.js → lexon-B1yuUdqW.js} +0 -0
  217. /package/dist/assets/{lua-prOwgJ4U.js → lua-Bo6GFzxT.js} +0 -0
  218. /package/dist/assets/{m3-DhrdTQ84.js → m3-C-Y9cBVI.js} +0 -0
  219. /package/dist/assets/{markdown-DoI5HwSM.js → markdown-BHu-wLbe.js} +0 -0
  220. /package/dist/assets/{mermaid-parser.core-BQtepoLd.js → mermaid-parser.core-IxcGtNYG.js} +0 -0
  221. /package/dist/assets/{mips-Dye5jsBU.js → mips-Dkds26rX.js} +0 -0
  222. /package/dist/assets/{msdax-TkNOyIL8.js → msdax-PU4g-8_g.js} +0 -0
  223. /package/dist/assets/{mysql-CEc-RKZF.js → mysql-fPpuGpS7.js} +0 -0
  224. /package/dist/assets/{objective-c-CqqCsQz0.js → objective-c-Wy1e5JUf.js} +0 -0
  225. /package/dist/assets/{pascal-DO_pj8yg.js → pascal-BIXwvrqU.js} +0 -0
  226. /package/dist/assets/{pascaligo-1cbdZdF3.js → pascaligo-DwR0AtL_.js} +0 -0
  227. /package/dist/assets/{path-YdFzr2W6.js → path-BxQuywGl.js} +0 -0
  228. /package/dist/assets/{perl-B_N3PYfC.js → perl-CGHAl8my.js} +0 -0
  229. /package/dist/assets/{pgsql-9iYk1FQ7.js → pgsql-B91M73E2.js} +0 -0
  230. /package/dist/assets/{php-rO2sgoUM.js → php-C70ttrCx.js} +0 -0
  231. /package/dist/assets/{pla-Cbgv60kq.js → pla-BNrXaRh0.js} +0 -0
  232. /package/dist/assets/{postiats-y6xrhy_M.js → postiats-Dv2IAZR9.js} +0 -0
  233. /package/dist/assets/{powerquery-BaD8gWNE.js → powerquery-CkY5Wq7J.js} +0 -0
  234. /package/dist/assets/{powershell-BMhG4JVe.js → powershell-uGIYLU3C.js} +0 -0
  235. /package/dist/assets/{protobuf-CdOP5kN3.js → protobuf-CavfmJu-.js} +0 -0
  236. /package/dist/assets/{pug-Ck60hNF9.js → pug-Drqnyitx.js} +0 -0
  237. /package/dist/assets/{qrcode-DV-_rjU8.js → qrcode-7uGsTEmi.js} +0 -0
  238. /package/dist/assets/{qsharp-BcaKFfP-.js → qsharp-C7yF-Dti.js} +0 -0
  239. /package/dist/assets/{r-rFNes0iK.js → r-HXLGP0_-.js} +0 -0
  240. /package/dist/assets/{redis-DkpQ3K6G.js → redis-C4y6mf2p.js} +0 -0
  241. /package/dist/assets/{redshift-Di9N5TI3.js → redshift-DdQrFPrv.js} +0 -0
  242. /package/dist/assets/{restructuredtext-By5ZzLt6.js → restructuredtext-CBrf2PNj.js} +0 -0
  243. /package/dist/assets/{rough.esm-c4PR5shF.js → rough.esm-6CnTHTkH.js} +0 -0
  244. /package/dist/assets/{ruby-fF5zGiLp.js → ruby-CVtMoX2B.js} +0 -0
  245. /package/dist/assets/{rust-C5b3AYY6.js → rust-Ckq4sbMP.js} +0 -0
  246. /package/dist/assets/{sb-CD07UNRs.js → sb-er66OpE3.js} +0 -0
  247. /package/dist/assets/{scala-jtn1ip9G.js → scala-BdWZz-hH.js} +0 -0
  248. /package/dist/assets/{scheme-C4vqMkn_.js → scheme-kTV_s2kP.js} +0 -0
  249. /package/dist/assets/{scss-I4Glxjbz.js → scss-Bymy2VJQ.js} +0 -0
  250. /package/dist/assets/{shell-DubUcuns.js → shell-bwEP1LQ1.js} +0 -0
  251. /package/dist/assets/{solidity-CNy2KSl2.js → solidity-DCddeBgL.js} +0 -0
  252. /package/dist/assets/{sophia-D9_URt84.js → sophia-BMN3AQig.js} +0 -0
  253. /package/dist/assets/{sparql-DviXpm4C.js → sparql-CSXOLjzi.js} +0 -0
  254. /package/dist/assets/{sql-vDRbAZgK.js → sql-COg8ewmK.js} +0 -0
  255. /package/dist/assets/{st-CnpnRqA7.js → st-CIaZtBS_.js} +0 -0
  256. /package/dist/assets/{swift-D_g8Ganz.js → swift-DXiOhUHu.js} +0 -0
  257. /package/dist/assets/{systemverilog-OuZa5h1M.js → systemverilog-BTDE6Dr2.js} +0 -0
  258. /package/dist/assets/{tcl-DBm9ucsV.js → tcl-B0zy9NvV.js} +0 -0
  259. /package/dist/assets/{twig-BRzEqX5Y.js → twig-D594l4Z1.js} +0 -0
  260. /package/dist/assets/{typespec-D4ZgGeX8.js → typespec-XKVjaPqo.js} +0 -0
  261. /package/dist/assets/{useWorkspaceMenuKeyboard-Brws6Ar9.js → useWorkspaceMenuKeyboard-Bpy_s69Z.js} +0 -0
  262. /package/dist/assets/{vb-DcP4s6WS.js → vb-B2wYoKRV.js} +0 -0
  263. /package/dist/assets/{wgsl-CnRJ_QUf.js → wgsl-D0Yf2LbK.js} +0 -0
  264. /package/dist/assets/{workspace-working-copy-D8-s_Sgh.js → workspace-working-copy-C3FSR3N6.js} +0 -0
@@ -2,6 +2,7 @@ const crypto = require('crypto');
2
2
  const fs = require('fs');
3
3
  const path = require('path');
4
4
  const { isDeepStrictEqual } = require('util');
5
+ const { atomicWriteJson } = require('./atomic-json-store');
5
6
  const { legacyRuntimeMetadata } = require('./agent-runtime-binding');
6
7
  const { lifecycleJournal } = require('./agent-lifecycle-journal');
7
8
  const storageLayout = require('./storage-layout');
@@ -9,6 +10,7 @@ const storageLayout = require('./storage-layout');
9
10
  const AGENT_RECORD_ID_PREFIX = 'agent';
10
11
  const AGENT_RECORD_VERSION = 1;
11
12
  const AGENT_STATE_VERSION = 1;
13
+ const SESSION_INDEX_VERSION = 2;
12
14
  const MAX_MAIN_PAGE_SESSION_KEYS = 50;
13
15
  const AGENT_STATE_FIELDS = [
14
16
  'acpState',
@@ -34,6 +36,7 @@ const AGENT_STATE_FIELDS = [
34
36
  'readOutputEpoch',
35
37
  'readOutputSeq',
36
38
  'unread',
39
+ 'composerCommands',
37
40
  ];
38
41
  const AGENT_STATE_FIELD_SET = new Set(AGENT_STATE_FIELDS);
39
42
  const PRODUCT_STATE_FIELDS = [
@@ -100,14 +103,6 @@ function withoutUpdatedAt(value) {
100
103
  return copy;
101
104
  }
102
105
 
103
- function atomicWriteJson(file, value) {
104
- fs.mkdirSync(path.dirname(file), { recursive: true });
105
- const tmpFile = `${file}.${process.pid}.${Date.now()}.tmp`;
106
- fs.writeFileSync(tmpFile, JSON.stringify(value, null, 2), { mode: 0o600 });
107
- fs.renameSync(tmpFile, file);
108
- fs.chmodSync(file, 0o600);
109
- }
110
-
111
106
  function parseProviderSessionKey(key) {
112
107
  const match = String(key || '').match(/^agent-session:([^:]+):(.+)$/);
113
108
  if (!match) return null;
@@ -131,28 +126,38 @@ class FarmingSessionStore {
131
126
  this.normalizeMainPageSessionKeys = typeof options.normalizeMainPageSessionKeys === 'function'
132
127
  ? options.normalizeMainPageSessionKeys
133
128
  : keys => (Array.isArray(keys) ? keys : []).slice(0, MAX_MAIN_PAGE_SESSION_KEYS);
129
+ this.writeJson = typeof options.writeJson === 'function'
130
+ ? options.writeJson
131
+ : (file, value) => atomicWriteJson(file, value, { mode: 0o600 });
134
132
  this.index = null;
133
+ this.legacyProviderSessionRecords = {};
134
+ this.providerSessionRecords = new Map();
135
135
  }
136
136
 
137
137
  init({ legacyMainPageSessionKeys = [] } = {}) {
138
138
  fs.mkdirSync(this.sessionsDir, { recursive: true });
139
139
  this.index = this.readIndex();
140
140
  this.reconcileProviderSessionIndex();
141
+ let nextMainPageSessionKeys = this.index.mainPageSessionKeys;
141
142
  if (!Array.isArray(this.index.mainPageSessionKeys) || this.index.mainPageSessionKeys.length === 0) {
142
143
  const migrated = this.filterRecoverableMainPageSessionKeys(legacyMainPageSessionKeys);
143
144
  if (migrated.length > 0) {
144
- this.setMainPageSessionKeys(migrated);
145
- return;
145
+ nextMainPageSessionKeys = migrated;
146
146
  }
147
147
  }
148
- this.index.mainPageSessionKeys = this.filterRecoverableMainPageSessionKeys(
149
- this.index.mainPageSessionKeys,
150
- );
151
- this.writeIndex();
148
+ nextMainPageSessionKeys = this.filterRecoverableMainPageSessionKeys(nextMainPageSessionKeys);
149
+ nextMainPageSessionKeys.forEach(sessionKey => {
150
+ if (this.getRecordForProviderSessionKey(sessionKey)) return;
151
+ this.ensureRecordForProviderSessionKey(sessionKey, {
152
+ archived: false,
153
+ lastSeenAt: now(),
154
+ });
155
+ });
156
+ this.writeIndex({ ...this.index, mainPageSessionKeys: nextMainPageSessionKeys });
157
+ this.legacyProviderSessionRecords = {};
152
158
  }
153
159
 
154
160
  reconcileProviderSessionIndex() {
155
- const index = this.ensureIndex();
156
161
  const recordsByProviderKey = new Map();
157
162
  const records = this.listStoredAgentRecords();
158
163
  const successors = this.legacySuccessors(records);
@@ -166,16 +171,23 @@ class FarmingSessionStore {
166
171
  }
167
172
 
168
173
  for (const [sessionKey, records] of recordsByProviderKey) {
169
- const indexedId = index.providerSessionRecords[sessionKey];
170
- const indexedRecord = indexedId ? records.find(record => record.id === indexedId) : null;
171
174
  if (records.length > 1) {
172
175
  const ids = records.map(record => record.id).sort().join(', ');
173
176
  throw new Error(`Conflicting Farming session records for ${sessionKey}: ${ids}`);
174
177
  }
175
- if (!indexedRecord || isLegacySessionId(indexedId)) {
176
- index.providerSessionRecords[sessionKey] = records[0].id;
178
+ recordsByProviderKey.set(sessionKey, records[0].id);
179
+ }
180
+
181
+ for (const [sessionKey, id] of Object.entries(this.legacyProviderSessionRecords)) {
182
+ if (recordsByProviderKey.has(sessionKey)) continue;
183
+ const record = this.readRecord(id);
184
+ if (!record || successors.has(record.id)) continue;
185
+ if (record.providerSessionKey && record.providerSessionKey !== sessionKey) {
186
+ throw new Error(`Farming session ${id} is indexed as ${sessionKey} but bound to ${record.providerSessionKey}`);
177
187
  }
188
+ recordsByProviderKey.set(sessionKey, record.id);
178
189
  }
190
+ this.providerSessionRecords = recordsByProviderKey;
179
191
  }
180
192
 
181
193
  filterRecoverableMainPageSessionKeys(keys) {
@@ -197,6 +209,7 @@ class FarmingSessionStore {
197
209
  try {
198
210
  if (fs.existsSync(this.indexFile)) {
199
211
  const parsed = JSON.parse(fs.readFileSync(this.indexFile, 'utf8'));
212
+ this.legacyProviderSessionRecords = this.normalizeProviderSessionRecords(parsed?.providerSessionRecords);
200
213
  return this.normalizeIndex(parsed);
201
214
  }
202
215
  } catch (error) {
@@ -206,22 +219,25 @@ class FarmingSessionStore {
206
219
  }
207
220
 
208
221
  normalizeIndex(index) {
209
- const providerSessionRecords = index && typeof index.providerSessionRecords === 'object' && !Array.isArray(index.providerSessionRecords)
210
- ? index.providerSessionRecords
211
- : {};
222
+ return {
223
+ version: SESSION_INDEX_VERSION,
224
+ mainPageSessionKeys: this.normalizeMainPageSessionKeys(index?.mainPageSessionKeys),
225
+ updatedAt: typeof index?.updatedAt === 'number' ? index.updatedAt : now(),
226
+ };
227
+ }
228
+
229
+ normalizeProviderSessionRecords(providerSessionRecords) {
212
230
  const normalizedProviderSessionRecords = {};
213
- Object.entries(providerSessionRecords).forEach(([key, id]) => {
231
+ Object.entries(
232
+ providerSessionRecords && typeof providerSessionRecords === 'object' && !Array.isArray(providerSessionRecords)
233
+ ? providerSessionRecords
234
+ : {},
235
+ ).forEach(([key, id]) => {
214
236
  if (!parseProviderSessionKey(key)) return;
215
237
  if (!safeSessionFileName(id)) return;
216
238
  normalizedProviderSessionRecords[key] = id;
217
239
  });
218
-
219
- return {
220
- version: 1,
221
- mainPageSessionKeys: this.normalizeMainPageSessionKeys(index?.mainPageSessionKeys),
222
- providerSessionRecords: normalizedProviderSessionRecords,
223
- updatedAt: typeof index?.updatedAt === 'number' ? index.updatedAt : now(),
224
- };
240
+ return normalizedProviderSessionRecords;
225
241
  }
226
242
 
227
243
  ensureIndex() {
@@ -229,10 +245,11 @@ class FarmingSessionStore {
229
245
  return this.index;
230
246
  }
231
247
 
232
- writeIndex() {
233
- const index = this.ensureIndex();
234
- index.updatedAt = now();
235
- atomicWriteJson(this.indexFile, index);
248
+ writeIndex(index = this.ensureIndex()) {
249
+ const nextIndex = this.normalizeIndex({ ...index, updatedAt: now() });
250
+ this.writeJson(this.indexFile, nextIndex);
251
+ this.index = nextIndex;
252
+ return nextIndex;
236
253
  }
237
254
 
238
255
  sessionFile(id) {
@@ -308,10 +325,10 @@ class FarmingSessionStore {
308
325
  }
309
326
 
310
327
  promoteIndexRecordId(previousId, nextId) {
311
- if (!this.index || !previousId || previousId === nextId) return;
312
- Object.entries(this.index.providerSessionRecords).forEach(([key, id]) => {
313
- if (id === previousId) this.index.providerSessionRecords[key] = nextId;
314
- });
328
+ if (!previousId || previousId === nextId) return;
329
+ for (const [key, id] of this.providerSessionRecords) {
330
+ if (id === previousId) this.providerSessionRecords.set(key, nextId);
331
+ }
315
332
  }
316
333
 
317
334
  writeRecord(record) {
@@ -335,11 +352,11 @@ class FarmingSessionStore {
335
352
  || !sameJson(withoutUpdatedAt(existingState), withoutUpdatedAt(state));
336
353
  if (stateChanged) {
337
354
  state.updatedAt = now();
338
- atomicWriteJson(this.agentStateFile(id), state);
355
+ this.writeJson(this.agentStateFile(id), state);
339
356
  }
340
357
  if (metadataChanged) {
341
358
  metadata.updatedAt = now();
342
- atomicWriteJson(this.sessionFile(id), metadata);
359
+ this.writeJson(this.sessionFile(id), metadata);
343
360
  }
344
361
  if (promoted) this.promoteIndexRecordId(previousId, id);
345
362
  return id;
@@ -397,6 +414,8 @@ class FarmingSessionStore {
397
414
  projectWorkspace: typeof agent.projectWorkspace === 'string' ? agent.projectWorkspace : '',
398
415
  mainWorkspace: typeof agent.mainWorkspace === 'string' ? agent.mainWorkspace : '',
399
416
  source: typeof agent.source === 'string' ? agent.source : '',
417
+ parentAgentId: typeof agent.parentAgentId === 'string' ? agent.parentAgentId : '',
418
+ forkRequestId: typeof agent.forkRequestId === 'string' ? agent.forkRequestId : '',
400
419
  provider: parsed ? parsed.provider : (typeof agent.providerSessionProvider === 'string' ? agent.providerSessionProvider : ''),
401
420
  providerHomeId: parsed ? parsed.providerHomeId : (typeof agent.providerHomeId === 'string' ? agent.providerHomeId : ''),
402
421
  providerHomePath: typeof agent.providerHomePath === 'string' ? agent.providerHomePath : '',
@@ -407,6 +426,9 @@ class FarmingSessionStore {
407
426
  providerSessionResolvedAt: typeof agent.providerSessionResolvedAt === 'number' ? agent.providerSessionResolvedAt : null,
408
427
  providerSessionTitle: typeof agent.providerSessionTitle === 'string' ? agent.providerSessionTitle : '',
409
428
  providerSessionWorkspace: typeof agent.providerSessionWorkspace === 'string' ? agent.providerSessionWorkspace : '',
429
+ forkedFromProviderSessionId: typeof agent.forkedFromProviderSessionId === 'string'
430
+ ? agent.forkedFromProviderSessionId
431
+ : '',
410
432
  terminalInputReceived: agent.terminalInputReceived === true,
411
433
  structuredRuntimeProcess: agent.structuredRuntimeProcess
412
434
  && typeof agent.structuredRuntimeProcess === 'object'
@@ -435,6 +457,9 @@ class FarmingSessionStore {
435
457
  attentionOutputSeq: typeof agent.attentionOutputSeq === 'number' ? agent.attentionOutputSeq : null,
436
458
  readOutputEpoch: typeof agent.readOutputEpoch === 'string' ? agent.readOutputEpoch : '',
437
459
  readOutputSeq: typeof agent.readOutputSeq === 'number' ? agent.readOutputSeq : null,
460
+ composerCommands: Array.isArray(agent.composerCommands)
461
+ ? cloneJson(agent.composerCommands)
462
+ : [],
438
463
  archived: agent.archived === true,
439
464
  archivedAt: typeof agent.archivedAt === 'number' ? agent.archivedAt : null,
440
465
  ...(typeof agent.customTitle === 'string' && agent.customTitle
@@ -452,8 +477,8 @@ class FarmingSessionStore {
452
477
  ensureRecordForProviderSessionKey(sessionKey, patch = {}, preferredId = '') {
453
478
  const parsed = parseProviderSessionKey(sessionKey);
454
479
  if (!parsed) return '';
455
- const index = this.ensureIndex();
456
- const existingId = index.providerSessionRecords[sessionKey];
480
+ this.ensureIndex();
481
+ const existingId = this.providerSessionRecords.get(sessionKey);
457
482
  const normalizedPreferredId = safeSessionFileName(preferredId) ? preferredId : '';
458
483
  const preferredRecord = normalizedPreferredId ? this.readRecord(normalizedPreferredId) : null;
459
484
  if (
@@ -485,15 +510,15 @@ class FarmingSessionStore {
485
510
  providerSessionTemporary: false,
486
511
  updatedAt: now(),
487
512
  };
513
+ delete record.visibleOnMainPage;
488
514
  if (typeof record.customTitle === 'string' && record.customTitle) {
489
515
  record.title = record.customTitle;
490
516
  }
491
- Object.entries(index.providerSessionRecords).forEach(([key, recordId]) => {
492
- if (key !== sessionKey && recordId === id) delete index.providerSessionRecords[key];
493
- });
494
517
  const writtenId = this.writeRecord(record);
495
- index.providerSessionRecords[sessionKey] = writtenId;
496
- this.writeIndex();
518
+ for (const [key, recordId] of this.providerSessionRecords) {
519
+ if (key !== sessionKey && recordId === writtenId) this.providerSessionRecords.delete(key);
520
+ }
521
+ this.providerSessionRecords.set(sessionKey, writtenId);
497
522
  return writtenId;
498
523
  }
499
524
 
@@ -536,7 +561,7 @@ class FarmingSessionStore {
536
561
  mergedInto: id,
537
562
  updatedAt: now(),
538
563
  });
539
- if (mergedId && mergedId !== previousId) this.writeIndex();
564
+ if (mergedId && mergedId !== previousId) this.promoteIndexRecordId(previousId, mergedId);
540
565
  }
541
566
  }
542
567
  return id;
@@ -569,23 +594,21 @@ class FarmingSessionStore {
569
594
  rememberMainPageSessionKey(sessionKey, patch = {}) {
570
595
  const id = this.ensureRecordForProviderSessionKey(sessionKey, {
571
596
  ...patch,
572
- visibleOnMainPage: true,
573
597
  archived: false,
574
598
  lastSeenAt: now(),
575
599
  });
576
600
  if (!id) return this.getMainPageSessionKeys();
577
601
  const index = this.ensureIndex();
578
- index.mainPageSessionKeys = this.normalizeMainPageSessionKeys([
602
+ const nextIndex = this.writeIndex({ ...index, mainPageSessionKeys: this.normalizeMainPageSessionKeys([
579
603
  sessionKey,
580
604
  ...index.mainPageSessionKeys.filter(key => key !== sessionKey),
581
- ]);
582
- this.writeIndex();
583
- return index.mainPageSessionKeys.slice();
605
+ ]) });
606
+ return nextIndex.mainPageSessionKeys.slice();
584
607
  }
585
608
 
586
609
  rememberAgent(agent) {
587
610
  const providerSessionKey = this.providerSessionKeyForAgent(agent);
588
- const id = this.ensureRecordForAgent(agent, providerSessionKey ? { visibleOnMainPage: true, archived: false } : {});
611
+ const id = this.ensureRecordForAgent(agent, providerSessionKey ? { archived: false } : {});
589
612
  if (providerSessionKey) {
590
613
  this.rememberMainPageSessionKey(providerSessionKey, this.recordPatchFromAgent(agent));
591
614
  }
@@ -595,54 +618,33 @@ class FarmingSessionStore {
595
618
  setMainPageSessionKeys(keys) {
596
619
  const normalized = this.normalizeMainPageSessionKeys(keys);
597
620
  const index = this.ensureIndex();
598
- const visible = new Set(normalized);
599
621
  normalized.forEach(key => {
600
622
  this.ensureRecordForProviderSessionKey(key, {
601
- visibleOnMainPage: true,
602
623
  archived: false,
603
624
  lastSeenAt: now(),
604
625
  });
605
626
  });
606
- index.mainPageSessionKeys.forEach(key => {
607
- if (visible.has(key)) return;
608
- const id = index.providerSessionRecords[key];
609
- const existing = this.readRecord(id);
610
- if (existing) {
611
- const writtenId = this.writeRecord({
612
- ...existing,
613
- visibleOnMainPage: false,
614
- updatedAt: now(),
615
- });
616
- if (writtenId) index.providerSessionRecords[key] = writtenId;
617
- }
618
- });
619
- index.mainPageSessionKeys = normalized;
620
- this.writeIndex();
621
- return normalized.slice();
627
+ return this.writeIndex({ ...index, mainPageSessionKeys: normalized }).mainPageSessionKeys.slice();
622
628
  }
623
629
 
624
630
  removeMainPageSessionKey(sessionKey) {
625
631
  const index = this.ensureIndex();
626
632
  if (!index.mainPageSessionKeys.includes(sessionKey)) return false;
627
- index.mainPageSessionKeys = index.mainPageSessionKeys.filter(key => key !== sessionKey);
628
- const id = index.providerSessionRecords[sessionKey];
629
- const existing = this.readRecord(id);
630
- if (existing) {
631
- const writtenId = this.writeRecord({
632
- ...existing,
633
- visibleOnMainPage: false,
634
- updatedAt: now(),
635
- });
636
- if (writtenId) index.providerSessionRecords[sessionKey] = writtenId;
637
- }
638
- this.writeIndex();
633
+ this.writeIndex({
634
+ ...index,
635
+ mainPageSessionKeys: index.mainPageSessionKeys.filter(key => key !== sessionKey),
636
+ });
639
637
  return true;
640
638
  }
641
639
 
642
640
  removeMainPageSessionKeys(keys) {
643
- const removed = [];
644
- keys.forEach(key => {
645
- if (this.removeMainPageSessionKey(key)) removed.push(key);
641
+ const index = this.ensureIndex();
642
+ const requested = new Set(Array.isArray(keys) ? keys : []);
643
+ const removed = index.mainPageSessionKeys.filter(key => requested.has(key));
644
+ if (removed.length === 0) return [];
645
+ this.writeIndex({
646
+ ...index,
647
+ mainPageSessionKeys: index.mainPageSessionKeys.filter(key => !requested.has(key)),
646
648
  });
647
649
  return removed;
648
650
  }
@@ -652,8 +654,8 @@ class FarmingSessionStore {
652
654
  }
653
655
 
654
656
  getRecordForProviderSessionKey(sessionKey) {
655
- const index = this.ensureIndex();
656
- const id = index.providerSessionRecords[sessionKey];
657
+ this.ensureIndex();
658
+ const id = this.providerSessionRecords.get(sessionKey);
657
659
  return safeSessionFileName(id) ? this.readRecord(id) : null;
658
660
  }
659
661
 
@@ -670,5 +672,6 @@ module.exports = {
670
672
  AGENT_STATE_VERSION,
671
673
  FarmingSessionStore,
672
674
  MAX_MAIN_PAGE_SESSION_KEYS,
675
+ SESSION_INDEX_VERSION,
673
676
  parseProviderSessionKey,
674
677
  };
@@ -57,6 +57,22 @@ const MAIN_AGENT_OPERATING_GUIDE = [
57
57
  ];
58
58
 
59
59
  const MAIN_AGENT_SKILLS = [
60
+ {
61
+ id: 'browser-resource',
62
+ name: '系统浏览器操作',
63
+ trigger: '用户要求打开、查看或操作 Farming Project 下的 Browser Resource,或者需要绕过静态 HTML Viewer 限制验证真实网页时',
64
+ summary: '通过 Farming Browser ID 操作用户当前看到的同一个 Headless 系统浏览器;先 Snapshot 获取当前 Ref,再进行点击或输入,页面变化后重新 Snapshot。',
65
+ commands: [
66
+ 'farming browser list --workspace <repo>',
67
+ 'farming browser start <browser-id>',
68
+ 'farming browser snapshot <browser-id>',
69
+ 'farming browser navigate <browser-id> <url>',
70
+ 'farming browser click <browser-id> <ref-or-css=selector>',
71
+ 'farming browser fill <browser-id> <ref-or-css=selector> <text>',
72
+ 'farming browser screenshot <browser-id> <output.png>',
73
+ 'farming browser stop <browser-id>',
74
+ ],
75
+ },
60
76
  {
61
77
  id: 'memory-report',
62
78
  name: '记忆读取总结',
@@ -137,6 +153,7 @@ function renderMainAgentSkills() {
137
153
  });
138
154
 
139
155
  lines.push('Rules:');
156
+ lines.push('- Use `farming browser` only for Browser Resources already in the user-selected Project scope; take a new snapshot after navigation or DOM-changing actions.');
140
157
  lines.push('- Use `farming memory report` when you need recent context from local agent memories.');
141
158
  lines.push('- Use “牧场除虫计划” when the user asks for systematic bug hunting across a directory or module tree.');
142
159
  lines.push('- Before spawning child agents for pest control, map modules and module protocols first; do not send overlapping or vague tasks.');
@@ -4,7 +4,10 @@ const crypto = require('crypto');
4
4
  const fs = require('fs');
5
5
  const net = require('net');
6
6
  const path = require('path');
7
- const { nativePtyHostSocketPath } = require('./native-pty-host-path');
7
+ const {
8
+ nativePtyHostPrivateSocketNamePattern,
9
+ nativePtyHostSocketPath,
10
+ } = require('./native-pty-host-path');
8
11
  const {
9
12
  nativePtyHostRuntimeIdentity,
10
13
  nativePtyHostRuntimeIdentityMatches,
@@ -17,6 +20,7 @@ const {
17
20
  const { isTemporaryProviderSessionId } = require('./provider-session-id');
18
21
  const storageLayout = require('./storage-layout');
19
22
  const { deserializeTerminalState } = require('./terminal-state-serialization');
23
+ const { probeUnixSocket } = require('./terminal-runtime-cleanup');
20
24
 
21
25
  const DEFAULT_CONNECT_RETRIES = 300;
22
26
  const DEFAULT_CONNECT_RETRY_MS = 50;
@@ -184,6 +188,7 @@ class NativePtyHostClient extends EventEmitter {
184
188
  this.controllerIdentityReady = null;
185
189
  this.hostRotationTimeoutMs = options.hostRotationTimeoutMs || DEFAULT_HOST_ROTATION_TIMEOUT_MS;
186
190
  this.connectedHostInfo = null;
191
+ this.connectedSocketPath = '';
187
192
  this.runtimeRotationInfo = null;
188
193
  this.socket = null;
189
194
  this.socketGeneration = 0;
@@ -235,7 +240,85 @@ class NativePtyHostClient extends EventEmitter {
235
240
 
236
241
  canConnectWithoutStartingHost() {
237
242
  if (process.platform === 'win32') return true;
238
- return fs.existsSync(this.socketPath);
243
+ return fs.existsSync(this.socketPath) || this.privateSocketCandidates().length > 0;
244
+ }
245
+
246
+ privateSocketCandidates() {
247
+ if (process.platform === 'win32') return [];
248
+ const directory = path.dirname(this.socketPath);
249
+ const pattern = nativePtyHostPrivateSocketNamePattern(this.socketPath);
250
+ try {
251
+ return fs.readdirSync(directory)
252
+ .filter(name => pattern.test(name))
253
+ .map(name => path.join(directory, name));
254
+ } catch (error) {
255
+ if (error?.code === 'ENOENT') return [];
256
+ throw error;
257
+ }
258
+ }
259
+
260
+ connectedPrivateSocketPath(hostInfo = {}) {
261
+ if (process.platform === 'win32') return '';
262
+ if (typeof hostInfo.privateSocketPath === 'string' && hostInfo.privateSocketPath) {
263
+ return hostInfo.privateSocketPath;
264
+ }
265
+ if (!this.connectedSocketPath || this.connectedSocketPath !== this.socketPath) {
266
+ return this.connectedSocketPath;
267
+ }
268
+
269
+ let publicIdentity;
270
+ try {
271
+ const stat = fs.statSync(this.socketPath, { bigint: true });
272
+ publicIdentity = { dev: stat.dev, ino: stat.ino };
273
+ } catch (error) {
274
+ if (error?.code === 'ENOENT') return '';
275
+ throw error;
276
+ }
277
+
278
+ const matching = this.privateSocketCandidates().filter((candidate) => {
279
+ try {
280
+ const stat = fs.statSync(candidate, { bigint: true });
281
+ return stat.dev === publicIdentity.dev && stat.ino === publicIdentity.ino;
282
+ } catch (error) {
283
+ if (error?.code === 'ENOENT') return false;
284
+ throw error;
285
+ }
286
+ });
287
+ return matching.length === 1 ? matching[0] : '';
288
+ }
289
+
290
+ async resolveConnectSocketPath() {
291
+ if (process.platform === 'win32' || fs.existsSync(this.socketPath)) {
292
+ return this.socketPath;
293
+ }
294
+ const active = [];
295
+ for (const candidate of this.privateSocketCandidates()) {
296
+ const probe = await probeUnixSocket(candidate);
297
+ if (probe.active) active.push(candidate);
298
+ }
299
+ if (active.length > 1) {
300
+ const error = new Error('Multiple live native PTY hosts were found for this Farming config');
301
+ error.code = 'FARMING_NATIVE_HOST_AMBIGUOUS';
302
+ error.socketPaths = active;
303
+ throw error;
304
+ }
305
+ return active[0] || this.socketPath;
306
+ }
307
+
308
+ restorePublicSocketPath(connectedPath) {
309
+ if (
310
+ process.platform === 'win32'
311
+ || !connectedPath
312
+ || connectedPath === this.socketPath
313
+ || fs.existsSync(this.socketPath)
314
+ ) {
315
+ return;
316
+ }
317
+ try {
318
+ fs.linkSync(connectedPath, this.socketPath);
319
+ } catch (error) {
320
+ if (!error || error.code !== 'EEXIST') throw error;
321
+ }
239
322
  }
240
323
 
241
324
  spawnHost() {
@@ -288,9 +371,10 @@ class NativePtyHostClient extends EventEmitter {
288
371
  });
289
372
  }
290
373
 
291
- connectOnce() {
374
+ async connectOnce() {
375
+ const connectedPath = await this.resolveConnectSocketPath();
292
376
  return new Promise((resolve, reject) => {
293
- const socket = net.createConnection(this.socketPath);
377
+ const socket = net.createConnection(connectedPath);
294
378
  const onError = (error) => {
295
379
  socket.destroy();
296
380
  reject(error);
@@ -298,6 +382,8 @@ class NativePtyHostClient extends EventEmitter {
298
382
  socket.once('error', onError);
299
383
  socket.once('connect', () => {
300
384
  socket.off('error', onError);
385
+ this.restorePublicSocketPath(connectedPath);
386
+ this.connectedSocketPath = connectedPath;
301
387
  this.attachSocket(socket);
302
388
  resolve();
303
389
  });
@@ -373,6 +459,9 @@ class NativePtyHostClient extends EventEmitter {
373
459
  if (error && error.code === 'FARMING_NATIVE_HOST_RUNTIME_MISMATCH') {
374
460
  throw error;
375
461
  }
462
+ if (error && error.code === 'FARMING_NATIVE_HOST_AMBIGUOUS') {
463
+ throw error;
464
+ }
376
465
  if (this.hostStartError) {
377
466
  lastError = this.hostStartError;
378
467
  }
@@ -427,7 +516,7 @@ class NativePtyHostClient extends EventEmitter {
427
516
  return this.controllerIdentityReady;
428
517
  }
429
518
 
430
- async waitForHostRelease() {
519
+ async waitForHostRelease(privateSocketPath = '') {
431
520
  const deadline = Date.now() + this.hostRotationTimeoutMs;
432
521
  if (process.platform === 'win32') {
433
522
  await delay(Math.min(250, this.hostRotationTimeoutMs));
@@ -437,7 +526,8 @@ class NativePtyHostClient extends EventEmitter {
437
526
  const childReleased = !this.hostChild ||
438
527
  this.hostChild.exitCode !== null ||
439
528
  this.hostChild.signalCode !== null;
440
- if (!fs.existsSync(this.socketPath) && childReleased) return;
529
+ const privateReleased = !privateSocketPath || !fs.existsSync(privateSocketPath);
530
+ if (!fs.existsSync(this.socketPath) && privateReleased && childReleased) return;
441
531
  await delay(50);
442
532
  }
443
533
  const error = new Error('Timed out waiting for the previous native PTY host to stop');
@@ -589,6 +679,7 @@ class NativePtyHostClient extends EventEmitter {
589
679
  throw mismatchError;
590
680
  }
591
681
 
682
+ const privateSocketPath = this.connectedPrivateSocketPath(hostInfo);
592
683
  const socket = this.socket;
593
684
  if (socket) this.suppressedDisconnectSockets.add(socket);
594
685
  let shutdownUncertain = false;
@@ -622,7 +713,7 @@ class NativePtyHostClient extends EventEmitter {
622
713
  }
623
714
 
624
715
  try {
625
- await this.waitForHostRelease();
716
+ await this.waitForHostRelease(privateSocketPath || this.connectedSocketPath);
626
717
  } catch (error) {
627
718
  if (shutdownUncertain) {
628
719
  try {
@@ -674,6 +765,7 @@ class NativePtyHostClient extends EventEmitter {
674
765
  this.socket = null;
675
766
  this.buffer = '';
676
767
  this.connectedHostInfo = null;
768
+ this.connectedSocketPath = '';
677
769
  if (this.disposed) return;
678
770
  const error = new Error('Native pty host disconnected');
679
771
  error.code = 'ECONNRESET';
@@ -17,6 +17,26 @@ function nativePtyHostSocketPath(configDir) {
17
17
  return path.join(os.tmpdir(), `farming-native-pty-${process.getuid ? process.getuid() : 'user'}-${hash}.sock`);
18
18
  }
19
19
 
20
+ function nativePtyHostPrivateSocketHash(socketPath) {
21
+ return crypto.createHash('sha256').update(socketPath).digest('hex').slice(0, 8);
22
+ }
23
+
24
+ function nativePtyHostPrivateSocketPath(socketPath, options = {}) {
25
+ const pid = Number(options.pid || process.pid);
26
+ const nonce = String(options.nonce || crypto.randomBytes(4).toString('hex'));
27
+ return path.join(
28
+ path.dirname(socketPath),
29
+ `.fpty-${pid}-${nativePtyHostPrivateSocketHash(socketPath)}-${nonce}.sock`,
30
+ );
31
+ }
32
+
33
+ function nativePtyHostPrivateSocketNamePattern(socketPath) {
34
+ const hash = nativePtyHostPrivateSocketHash(socketPath);
35
+ return new RegExp(`^\\.fpty-\\d+-${hash}-[a-f0-9]+\\.sock$`);
36
+ }
37
+
20
38
  module.exports = {
39
+ nativePtyHostPrivateSocketNamePattern,
40
+ nativePtyHostPrivateSocketPath,
21
41
  nativePtyHostSocketPath,
22
42
  };
@@ -7,7 +7,10 @@ const os = require('os');
7
7
  const path = require('path');
8
8
 
9
9
  const TerminalScreenWorkerPool = require('./terminal-screen-worker-pool');
10
- const { nativePtyHostSocketPath } = require('./native-pty-host-path');
10
+ const {
11
+ nativePtyHostPrivateSocketPath,
12
+ nativePtyHostSocketPath,
13
+ } = require('./native-pty-host-path');
11
14
  const { terminalInputToPtyString } = require('./input-parts');
12
15
  const { normalizeInteractiveTerminalEnv } = require('./agent-env');
13
16
  const {
@@ -95,10 +98,7 @@ class NativePtyHost {
95
98
  this.exitOnShutdown = options.exitOnShutdown !== false;
96
99
  this.boundSocketPath = process.platform === 'win32'
97
100
  ? this.socketPath
98
- : path.join(
99
- path.dirname(this.socketPath),
100
- `.fpty-${process.pid}-${crypto.createHash('sha256').update(this.socketPath).digest('hex').slice(0, 8)}-${crypto.randomBytes(4).toString('hex')}.sock`,
101
- );
101
+ : nativePtyHostPrivateSocketPath(this.socketPath);
102
102
  this.boundSocketIdentity = null;
103
103
  this.sessions = new Map();
104
104
  this.clients = new Set();
@@ -347,6 +347,7 @@ class NativePtyHost {
347
347
  return {
348
348
  ok: true,
349
349
  pid: process.pid,
350
+ privateSocketPath: process.platform === 'win32' ? '' : this.boundSocketPath,
350
351
  runtimeIdentity: this.runtimeIdentity,
351
352
  };
352
353
  case 'registerController':