farming-code 2.2.6

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 (453) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +287 -0
  3. package/README.zh_cn.md +329 -0
  4. package/SECURITY.md +29 -0
  5. package/THIRD_PARTY_NOTICES.md +106 -0
  6. package/backend/acp/client-services.js +312 -0
  7. package/backend/acp/patch-decisions.js +137 -0
  8. package/backend/acp/permission-security.js +108 -0
  9. package/backend/acp-runtime.js +1144 -0
  10. package/backend/acp-session-state.js +448 -0
  11. package/backend/acp-transcript.js +169 -0
  12. package/backend/agent-activity.js +186 -0
  13. package/backend/agent-env.js +250 -0
  14. package/backend/agent-json-stream.js +268 -0
  15. package/backend/agent-manager.js +4259 -0
  16. package/backend/agent-memory-report.js +583 -0
  17. package/backend/agent-order.js +162 -0
  18. package/backend/agent-provider-session.js +287 -0
  19. package/backend/agent-session-history.js +888 -0
  20. package/backend/app-server-api.js +917 -0
  21. package/backend/async-cache.js +94 -0
  22. package/backend/auth.js +178 -0
  23. package/backend/claude-settings.js +121 -0
  24. package/backend/cli-agents.js +484 -0
  25. package/backend/codex-app-server-home.js +39 -0
  26. package/backend/codex-app-server-runtime.js +901 -0
  27. package/backend/codex-context-window.js +189 -0
  28. package/backend/codex-models.js +229 -0
  29. package/backend/codex-rollout-follower.js +243 -0
  30. package/backend/codex-session-history.js +553 -0
  31. package/backend/codex-transcript-sanitizer.js +103 -0
  32. package/backend/codex-transcript.js +2341 -0
  33. package/backend/command-runner-child.js +67 -0
  34. package/backend/config-manager.js +815 -0
  35. package/backend/control-api.js +151 -0
  36. package/backend/data/chinese-poetic-words.json +8223 -0
  37. package/backend/data/poetic-word-sources/china.json +49209 -0
  38. package/backend/data/poetic-word-sources/india.json +9763 -0
  39. package/backend/data/poetic-word-sources/japan.json +8455 -0
  40. package/backend/executable-discovery.js +207 -0
  41. package/backend/farming-app-cli.js +790 -0
  42. package/backend/farming-cli.js +400 -0
  43. package/backend/farming-session-store.js +373 -0
  44. package/backend/haiku-token.js +772 -0
  45. package/backend/index-html.js +49 -0
  46. package/backend/input-parts.js +54 -0
  47. package/backend/input-routing.js +24 -0
  48. package/backend/json-cli-runtime.js +161 -0
  49. package/backend/local-session-engine.js +620 -0
  50. package/backend/main-agent-skills.js +292 -0
  51. package/backend/main-page-session.js +119 -0
  52. package/backend/native-pty-host-client.js +516 -0
  53. package/backend/native-pty-host-path.js +22 -0
  54. package/backend/native-pty-host.js +816 -0
  55. package/backend/native-session-engine.js +196 -0
  56. package/backend/network.js +23 -0
  57. package/backend/npm-update-helper.js +196 -0
  58. package/backend/packaged-node-pty.js +79 -0
  59. package/backend/provider-session-id.js +47 -0
  60. package/backend/qr-share-tickets.js +100 -0
  61. package/backend/review-diff-router.js +180 -0
  62. package/backend/review-diff-service.js +1270 -0
  63. package/backend/review-session-router.js +74 -0
  64. package/backend/review-session-service.js +432 -0
  65. package/backend/review-session-store.js +144 -0
  66. package/backend/review-state-router.js +116 -0
  67. package/backend/review-state-store.js +287 -0
  68. package/backend/run-history-store.js +70 -0
  69. package/backend/server.js +2446 -0
  70. package/backend/session-engine-bridge.js +110 -0
  71. package/backend/session-engine-router.js +66 -0
  72. package/backend/session-engine.js +39 -0
  73. package/backend/shell-busy-integration.js +472 -0
  74. package/backend/slash-command-discovery.js +296 -0
  75. package/backend/storage-layout.js +101 -0
  76. package/backend/system-monitor.js +77 -0
  77. package/backend/terminal-runtime-cleanup.js +80 -0
  78. package/backend/terminal-screen-state.js +240 -0
  79. package/backend/terminal-screen-worker-pool.js +165 -0
  80. package/backend/terminal-screen-worker-thread.js +161 -0
  81. package/backend/terminal-screen-worker.js +255 -0
  82. package/backend/terminal-status.js +356 -0
  83. package/backend/theme-manager.js +145 -0
  84. package/backend/update-service.js +1183 -0
  85. package/backend/usage-forecast.js +86 -0
  86. package/backend/usage-monitor.js +1357 -0
  87. package/backend/workspace-discovery.js +311 -0
  88. package/backend/workspace-file-router.js +431 -0
  89. package/backend/workspace-file-service.js +2765 -0
  90. package/bin/farming +5 -0
  91. package/config/farming.deploy.env.example +13 -0
  92. package/config/farming.install.env.example +17 -0
  93. package/dist/assets/App-BrP-ENHg.css +1 -0
  94. package/dist/assets/App-Dza5yEDe.js +124 -0
  95. package/dist/assets/FileEditorMarkdownPreview-DRDNQ8mX.css +1 -0
  96. package/dist/assets/FileEditorMarkdownPreview-elKWc8Im.js +404 -0
  97. package/dist/assets/FileEditorPane-BQLocXOp.js +5 -0
  98. package/dist/assets/IconGlyphs-RkMAdBXF.js +1 -0
  99. package/dist/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  100. package/dist/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  101. package/dist/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  102. package/dist/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  103. package/dist/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  104. package/dist/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  105. package/dist/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  106. package/dist/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  107. package/dist/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  108. package/dist/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  109. package/dist/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  110. package/dist/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  111. package/dist/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  112. package/dist/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  113. package/dist/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  114. package/dist/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  115. package/dist/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  116. package/dist/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  117. package/dist/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  118. package/dist/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  119. package/dist/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  120. package/dist/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  121. package/dist/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  122. package/dist/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  123. package/dist/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  124. package/dist/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  125. package/dist/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  126. package/dist/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  127. package/dist/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  128. package/dist/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  129. package/dist/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  130. package/dist/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  131. package/dist/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  132. package/dist/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  133. package/dist/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  134. package/dist/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  135. package/dist/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  136. package/dist/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  137. package/dist/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  138. package/dist/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  139. package/dist/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  140. package/dist/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  141. package/dist/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  142. package/dist/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  143. package/dist/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  144. package/dist/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  145. package/dist/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  146. package/dist/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  147. package/dist/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  148. package/dist/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  149. package/dist/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  150. package/dist/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  151. package/dist/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  152. package/dist/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  153. package/dist/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  154. package/dist/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  155. package/dist/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  156. package/dist/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  157. package/dist/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  158. package/dist/assets/ProjectFilesSection-lS1azbgj.js +12 -0
  159. package/dist/assets/ReviewPage-DgnmgU2T.js +3 -0
  160. package/dist/assets/abap-Cc5D5h5i.js +1 -0
  161. package/dist/assets/abnfDiagram-VRR7QNED-BgawzDQT.js +1 -0
  162. package/dist/assets/apex-BKopwLmx.js +1 -0
  163. package/dist/assets/api-D1lyBYIQ.js +1 -0
  164. package/dist/assets/arc-CWoVlxJw.js +1 -0
  165. package/dist/assets/architecture-TIHT7OUA-CbJBFh3w.js +1 -0
  166. package/dist/assets/architectureDiagram-ZJ3FMSHR-DUShwPg0.js +36 -0
  167. package/dist/assets/array-HeX70jSN.js +1 -0
  168. package/dist/assets/azcli-DI-C34WA.js +1 -0
  169. package/dist/assets/bat-Cbp6u42z.js +1 -0
  170. package/dist/assets/bicep-4QA1jTCM.js +2 -0
  171. package/dist/assets/blockDiagram-677ZJIJ3-CeGZviXR.js +132 -0
  172. package/dist/assets/c4Diagram-LMCZKHZV-CLhYht6S.js +10 -0
  173. package/dist/assets/cameligo-Dda_adPs.js +1 -0
  174. package/dist/assets/channel-Uw90AO0b.js +1 -0
  175. package/dist/assets/chunk-2Q5K7J3B-d5WWh8jo.js +1 -0
  176. package/dist/assets/chunk-32BRIVSS-LFYpT-Cu.js +1 -0
  177. package/dist/assets/chunk-52WLFC77-BH6jDw9J.js +10 -0
  178. package/dist/assets/chunk-5FCAYU7R-C7AmIhYv.js +1 -0
  179. package/dist/assets/chunk-5HE753X5-C8cXaouP.js +1 -0
  180. package/dist/assets/chunk-5JV3BV7I-C1PtimcA.js +1 -0
  181. package/dist/assets/chunk-5TONJI2A-_81zUME8.js +2 -0
  182. package/dist/assets/chunk-5VM5RSS4-Dlw7AXWp.js +15 -0
  183. package/dist/assets/chunk-7BUUIJ7U-ZBRhjLOh.js +1 -0
  184. package/dist/assets/chunk-BIQX33UG-CWmzxeAw.js +1 -0
  185. package/dist/assets/chunk-C7G6YPKG-B_mIUlb7.js +1 -0
  186. package/dist/assets/chunk-CQNSW5MT-CMayolwI.js +1 -0
  187. package/dist/assets/chunk-CYSBUYHQ-lSPbrL3Z.js +1 -0
  188. package/dist/assets/chunk-DECur_0Z.js +1 -0
  189. package/dist/assets/chunk-EMLP6XTP-D9-jtCnM.js +1 -0
  190. package/dist/assets/chunk-EX3LRPZG-C63hVuAJ.js +231 -0
  191. package/dist/assets/chunk-FWX5IMBZ-CboX-WF_.js +2 -0
  192. package/dist/assets/chunk-HOUHSVGY-BOgSd6Un.js +1 -0
  193. package/dist/assets/chunk-ICXQ74PX-C0K-pgZt.js +2 -0
  194. package/dist/assets/chunk-JG7HCLWE-Cib606u_.js +2 -0
  195. package/dist/assets/chunk-JWPE2WC7-DSyBRaRG.js +1 -0
  196. package/dist/assets/chunk-KEIR6QF5-CNOHik0T.js +161 -0
  197. package/dist/assets/chunk-MOJQB5TN-CVMfwN_7.js +88 -0
  198. package/dist/assets/chunk-MOZMSUNE-D6JeZqmE.js +1 -0
  199. package/dist/assets/chunk-OGEWGWER-DLT3pxyw.js +1 -0
  200. package/dist/assets/chunk-OSBZ3O6U-C0W8KSeg.js +1 -0
  201. package/dist/assets/chunk-PUDLZKDR-Cd079yIQ.js +156 -0
  202. package/dist/assets/chunk-Q4XR5HBZ-D0jHa6SO.js +70 -0
  203. package/dist/assets/chunk-QBLGF6JB-p4omp7uO.js +1 -0
  204. package/dist/assets/chunk-R7FJI6CG-CZ_p9hSr.js +1 -0
  205. package/dist/assets/chunk-RYQCIY6F-CZA9pvFX.js +1 -0
  206. package/dist/assets/chunk-U6XO7XAA-CcXKsWxp.js +2 -0
  207. package/dist/assets/chunk-V7JOEXUC-CVDtR75H.js +206 -0
  208. package/dist/assets/chunk-VAUOI2AC-B-W2_9Ot.js +1 -0
  209. package/dist/assets/chunk-VR4S4FIN-FD3kWh32.js +1 -0
  210. package/dist/assets/chunk-WYO6CB5R-C2bGaS5r.js +127 -0
  211. package/dist/assets/chunk-XXDRQBXY-BBXwjhUx.js +1 -0
  212. package/dist/assets/chunk-Y2CYZVJY-Di0sYzi1.js +1 -0
  213. package/dist/assets/chunk-YOTPTUD7-BXHmEk89.js +1 -0
  214. package/dist/assets/chunk-ZGVPDNZ5-ymgts0zo.js +62 -0
  215. package/dist/assets/chunk-ZIRB5QZD-DncwlQQc.js +32 -0
  216. package/dist/assets/classDiagram-OUVF2IWQ-DKW_dtku.js +1 -0
  217. package/dist/assets/classDiagram-v2-EOCWNBFH-C8Fi7-yv.js +1 -0
  218. package/dist/assets/clojure-CvY8A83L.js +1 -0
  219. package/dist/assets/code-dark-Cjb1QGhu.css +1 -0
  220. package/dist/assets/code-mobile-3C2Rr-CN.css +1 -0
  221. package/dist/assets/codicon-ngg6Pgfi.ttf +0 -0
  222. package/dist/assets/coffee-4LX2Sv8A.js +1 -0
  223. package/dist/assets/core-ZlAPicox.js +3 -0
  224. package/dist/assets/cose-bilkent-JH36ORCC-BNIAz0nN.js +1 -0
  225. package/dist/assets/cpp-D4AzSV28.js +1 -0
  226. package/dist/assets/csharp-C4nWN4Yd.js +1 -0
  227. package/dist/assets/csp-CiEaaS5Z.js +1 -0
  228. package/dist/assets/css-DXxBRsbk.js +3 -0
  229. package/dist/assets/css.worker-DyDu5ynT.js +89 -0
  230. package/dist/assets/cssMode-DDWxTPm_.js +1 -0
  231. package/dist/assets/cynefin-VYW2F7L2-DgXGHlRt.js +1 -0
  232. package/dist/assets/cynefinDiagram-TSTJHNR4-CwpobXIl.js +62 -0
  233. package/dist/assets/cypher-DOMzqyFA.js +1 -0
  234. package/dist/assets/cytoscape.esm-Cl8iC3D7.js +321 -0
  235. package/dist/assets/dagre-DXlcHgr3.js +1 -0
  236. package/dist/assets/dagre-VKFMJZFB-Dkhlfjna.js +4 -0
  237. package/dist/assets/dart-BWS7MXt_.js +1 -0
  238. package/dist/assets/defaultLocale-GJwWH1Jr.js +1 -0
  239. package/dist/assets/diagram-FQU43EPY-hA8dicp6.js +3 -0
  240. package/dist/assets/diagram-G47NLZAW-BTY6JTzO.js +24 -0
  241. package/dist/assets/diagram-NH7WQ7WH-xkaHHX9M.js +24 -0
  242. package/dist/assets/diagram-OA4YK3LP-gk_fg9UU.js +30 -0
  243. package/dist/assets/diagram-WEI45ONY-BvC8xd6T.js +41 -0
  244. package/dist/assets/dist-ClAL-Tv5.js +1 -0
  245. package/dist/assets/dockerfile-Baf4U3xT.js +1 -0
  246. package/dist/assets/ebnfDiagram-CCIWWBDH-CR7RaNat.js +1 -0
  247. package/dist/assets/ecl-CT7udlkQ.js +1 -0
  248. package/dist/assets/editor-CtbFZlk0.css +1 -0
  249. package/dist/assets/editor.api2-B9h6iFPK.js +872 -0
  250. package/dist/assets/editor.worker-Dpt_xPrs.js +26 -0
  251. package/dist/assets/elixir-CIbAZw5l.js +1 -0
  252. package/dist/assets/erDiagram-Q63AITRT-_M-JFPbj.js +85 -0
  253. package/dist/assets/eventmodeling-45OFAUF4-DXYTQ2nt.js +1 -0
  254. package/dist/assets/file-icons-EFUGSSwf.js +1 -0
  255. package/dist/assets/flow9-Dqiw45WV.js +1 -0
  256. package/dist/assets/flowDiagram-23GEKE2U-DAiS5Y7G.js +1 -0
  257. package/dist/assets/freemarker2-QBsTERH-.js +3 -0
  258. package/dist/assets/fsharp-E8E2L9jU.js +1 -0
  259. package/dist/assets/ganttDiagram-NO4QXBWP-Bl8P1yf_.js +292 -0
  260. package/dist/assets/gitGraph-TEB2WS4Q-CzTBjEvV.js +1 -0
  261. package/dist/assets/gitGraphDiagram-IHSO6WYX-DoHa5ysz.js +106 -0
  262. package/dist/assets/go-CouVSN4D.js +1 -0
  263. package/dist/assets/graphlib-CbSvZcZU.js +1 -0
  264. package/dist/assets/graphql-a6h6h5-t.js +1 -0
  265. package/dist/assets/handlebars-CuoxPo5A.js +1 -0
  266. package/dist/assets/hcl-vso3e6N-.js +1 -0
  267. package/dist/assets/html-gX5PEu0Q.js +1 -0
  268. package/dist/assets/html.worker-IWNXhnIC.js +502 -0
  269. package/dist/assets/htmlMode-DIWUbUxd.js +1 -0
  270. package/dist/assets/index-BpHPeJf0.js +10 -0
  271. package/dist/assets/info-DKCQHKI2-DznP7j3s.js +1 -0
  272. package/dist/assets/infoDiagram-FWYZ7A6U-DSFcSuUs.js +2 -0
  273. package/dist/assets/ini-SwZZkotR.js +1 -0
  274. package/dist/assets/init-BPLPMQ3Y.js +1 -0
  275. package/dist/assets/ishikawaDiagram-FXEZZL3T-DdkqullI.js +70 -0
  276. package/dist/assets/java-CtRLC4_3.js +1 -0
  277. package/dist/assets/javascript-BYKGIUGs.js +1 -0
  278. package/dist/assets/journeyDiagram-5HDEW3XC-B4hRnQJr.js +139 -0
  279. package/dist/assets/json.worker-c4h5s80L.js +58 -0
  280. package/dist/assets/jsonMode-AFmQQP6u.js +7 -0
  281. package/dist/assets/jsx-runtime-BthL93pI.js +1 -0
  282. package/dist/assets/julia-CuJWFKQH.js +1 -0
  283. package/dist/assets/kanban-definition-HUTT4EX6-tm2FZB2E.js +89 -0
  284. package/dist/assets/katex-C8DPyI6s.js +257 -0
  285. package/dist/assets/kotlin-B_kBhsre.js +1 -0
  286. package/dist/assets/less-CtrHIfz3.js +2 -0
  287. package/dist/assets/lexon-yVt1pJLu.js +1 -0
  288. package/dist/assets/line-CmsjK4kn.js +1 -0
  289. package/dist/assets/linear-Dld3_ERu.js +1 -0
  290. package/dist/assets/liquid-DBIl-f0z.js +1 -0
  291. package/dist/assets/lspLanguageFeatures-DK6tqicV.js +4 -0
  292. package/dist/assets/lua-prOwgJ4U.js +1 -0
  293. package/dist/assets/m3-DhrdTQ84.js +1 -0
  294. package/dist/assets/main-DTJwH51M.css +1 -0
  295. package/dist/assets/map-LZWWoeWa.js +1 -0
  296. package/dist/assets/markdown-DoI5HwSM.js +1 -0
  297. package/dist/assets/mdx-DS-bVtZN.js +1 -0
  298. package/dist/assets/mermaid-parser.core-BQtepoLd.js +4 -0
  299. package/dist/assets/mermaid.core-C7UT_EEL.js +11 -0
  300. package/dist/assets/mindmap-definition-LN4V7U3C-DplfjGms.js +96 -0
  301. package/dist/assets/mips-Dye5jsBU.js +1 -0
  302. package/dist/assets/monaco.contribution-CyLosfZI.js +2 -0
  303. package/dist/assets/msdax-TkNOyIL8.js +1 -0
  304. package/dist/assets/mysql-CEc-RKZF.js +1 -0
  305. package/dist/assets/objective-c-CqqCsQz0.js +1 -0
  306. package/dist/assets/ordinal-BLrOss5K.js +1 -0
  307. package/dist/assets/packet-7NZHBO7P-BDEPOPha.js +1 -0
  308. package/dist/assets/pascal-DO_pj8yg.js +1 -0
  309. package/dist/assets/pascaligo-1cbdZdF3.js +1 -0
  310. package/dist/assets/path-YdFzr2W6.js +1 -0
  311. package/dist/assets/pegDiagram-2B236MQR-lu3SdkQs.js +1 -0
  312. package/dist/assets/perl-B_N3PYfC.js +1 -0
  313. package/dist/assets/pgsql-9iYk1FQ7.js +1 -0
  314. package/dist/assets/php-rO2sgoUM.js +1 -0
  315. package/dist/assets/pie-RZYD4A2V-DWIrwPJQ.js +1 -0
  316. package/dist/assets/pieDiagram-ENE6RG2P-3fHbmDf9.js +39 -0
  317. package/dist/assets/pla-Cbgv60kq.js +1 -0
  318. package/dist/assets/postiats-y6xrhy_M.js +1 -0
  319. package/dist/assets/powerquery-BaD8gWNE.js +1 -0
  320. package/dist/assets/powershell-BMhG4JVe.js +1 -0
  321. package/dist/assets/preload-helper-mJowh_hw.js +1 -0
  322. package/dist/assets/protobuf-CdOP5kN3.js +2 -0
  323. package/dist/assets/pug-Ck60hNF9.js +1 -0
  324. package/dist/assets/python-C93q3xXz.js +1 -0
  325. package/dist/assets/qoder-C9LmmOSf.svg +1 -0
  326. package/dist/assets/qoder-Cf9gl0Y5.svg +1 -0
  327. package/dist/assets/qoder-gHCinseV.svg +1 -0
  328. package/dist/assets/qrcode-DV-_rjU8.js +3 -0
  329. package/dist/assets/qsharp-BcaKFfP-.js +1 -0
  330. package/dist/assets/quadrantDiagram-ABIIQ3AL-a5qoWz-X.js +7 -0
  331. package/dist/assets/r-rFNes0iK.js +1 -0
  332. package/dist/assets/radar-I7S5WNFK-B7OaD8vp.js +1 -0
  333. package/dist/assets/railroad-3IZDKUUU-BXW2nuCA.js +1 -0
  334. package/dist/assets/railroad-abnf-AHOZXSZD-Buw9QG2r.js +1 -0
  335. package/dist/assets/railroad-ebnf-EBAXGLYW-BjSi4sWJ.js +1 -0
  336. package/dist/assets/railroad-peg-LSFZ7HO6-DN1wIVhS.js +1 -0
  337. package/dist/assets/railroadDiagram-RFXS5EU6-D2TOuXW9.js +1 -0
  338. package/dist/assets/razor-CBifNGXO.js +1 -0
  339. package/dist/assets/react-dom-gtU6X-Hj.js +1 -0
  340. package/dist/assets/redis-DkpQ3K6G.js +1 -0
  341. package/dist/assets/redshift-Di9N5TI3.js +1 -0
  342. package/dist/assets/requirementDiagram-TGXJPOKE-LQBWpUvd.js +84 -0
  343. package/dist/assets/restructuredtext-By5ZzLt6.js +1 -0
  344. package/dist/assets/review-BcnwQtJv.css +1 -0
  345. package/dist/assets/rough.esm-c4PR5shF.js +1 -0
  346. package/dist/assets/ruby-fF5zGiLp.js +1 -0
  347. package/dist/assets/rust-C5b3AYY6.js +1 -0
  348. package/dist/assets/sankeyDiagram-HTMAVEWB-B1ohYoyK.js +40 -0
  349. package/dist/assets/sb-CD07UNRs.js +1 -0
  350. package/dist/assets/scala-jtn1ip9G.js +1 -0
  351. package/dist/assets/scheme-C4vqMkn_.js +1 -0
  352. package/dist/assets/scss-I4Glxjbz.js +3 -0
  353. package/dist/assets/sequenceDiagram-DBY2YBRQ-C3vM-hMq.js +162 -0
  354. package/dist/assets/shell-DubUcuns.js +1 -0
  355. package/dist/assets/sizeCapture-X5ZJPWSS-daTB6OcF.js +1 -0
  356. package/dist/assets/solidity-CNy2KSl2.js +1 -0
  357. package/dist/assets/sophia-D9_URt84.js +1 -0
  358. package/dist/assets/sparql-DviXpm4C.js +1 -0
  359. package/dist/assets/sql-vDRbAZgK.js +1 -0
  360. package/dist/assets/src-WBW1QxJt.js +1 -0
  361. package/dist/assets/st-CnpnRqA7.js +1 -0
  362. package/dist/assets/stateDiagram-2N3HPSRC-BvFhOryI.js +1 -0
  363. package/dist/assets/stateDiagram-v2-6OUMAXLB-WMrz4PNo.js +1 -0
  364. package/dist/assets/swift-D_g8Ganz.js +1 -0
  365. package/dist/assets/swimlanes-5IMT3BWC-DKF_b-2s.js +2 -0
  366. package/dist/assets/swimlanesDiagram-G3AALYLV-BGswC9oC.js +8 -0
  367. package/dist/assets/systemverilog-OuZa5h1M.js +1 -0
  368. package/dist/assets/tcl-DBm9ucsV.js +1 -0
  369. package/dist/assets/timeline-definition-FHXFAJF6-vAs11ux_.js +120 -0
  370. package/dist/assets/tokens-BwenNn5Q.css +1 -0
  371. package/dist/assets/treeView-QDETBFTQ-BVfICaZQ.js +1 -0
  372. package/dist/assets/treemap-6X3UGDF4-oWHPqo17.js +1 -0
  373. package/dist/assets/ts.worker-BwM5Ha3L.js +67719 -0
  374. package/dist/assets/tsMode-DGZTlTj8.js +11 -0
  375. package/dist/assets/twig-BRzEqX5Y.js +1 -0
  376. package/dist/assets/typescript-KJKfYO7K.js +1 -0
  377. package/dist/assets/typespec-D4ZgGeX8.js +1 -0
  378. package/dist/assets/useWorkspaceMenuKeyboard-CneKAZUJ.js +1 -0
  379. package/dist/assets/vb-DcP4s6WS.js +1 -0
  380. package/dist/assets/vennDiagram-L72KCM5P-BybTX_9B.js +34 -0
  381. package/dist/assets/wardley-OPB4EBWU-Dg8t5y3s.js +1 -0
  382. package/dist/assets/wardleyDiagram-EHGQE667-CicUmMBR.js +78 -0
  383. package/dist/assets/wgsl-CnRJ_QUf.js +298 -0
  384. package/dist/assets/workers-BSQRIkqa.js +1 -0
  385. package/dist/assets/workspace-view-state-CTyDzk2D.js +1 -0
  386. package/dist/assets/xml-C8FAcN6j.js +1 -0
  387. package/dist/assets/xychartDiagram-FW5EYKEG-BhWDyCX-.js +7 -0
  388. package/dist/assets/yaml-BQ4NrRhG.js +1 -0
  389. package/dist/assets/zsh-CLpveKlF.svg +1 -0
  390. package/dist/assets/zsh-FxSpMPbz.svg +1 -0
  391. package/dist/farming-2/app-icon-v2-180.png +0 -0
  392. package/dist/farming-2/app-icon-v2-192.png +0 -0
  393. package/dist/farming-2/app-icon-v2-512.png +0 -0
  394. package/dist/farming-2/app-icon-v2-maskable-512.png +0 -0
  395. package/dist/farming-2/app-icon-v2-master.png +0 -0
  396. package/dist/farming-2/favicon-v2-16.png +0 -0
  397. package/dist/farming-2/favicon-v2-32.png +0 -0
  398. package/dist/farming-2/favicon-v2-48.png +0 -0
  399. package/dist/farming-2/favicon-v2-64.png +0 -0
  400. package/dist/farming-2/favicon-v2.ico +0 -0
  401. package/dist/farming-2/favicon.ico +0 -0
  402. package/dist/farming-2/github-social-preview.png +0 -0
  403. package/dist/farming-2/images/avatar-watercolor-v1-bee-garden.png +0 -0
  404. package/dist/farming-2/images/avatar-watercolor-v2.png +0 -0
  405. package/dist/farming-2/images/avatar-watercolor-v3.png +0 -0
  406. package/dist/farming-2/legacy-icons-v1/favicon.ico +1 -0
  407. package/dist/farming-2/legacy-icons-v1/logo-16.png +0 -0
  408. package/dist/farming-2/legacy-icons-v1/logo-180.png +0 -0
  409. package/dist/farming-2/legacy-icons-v1/logo-192.png +0 -0
  410. package/dist/farming-2/legacy-icons-v1/logo-32.png +0 -0
  411. package/dist/farming-2/legacy-icons-v1/logo-48.png +0 -0
  412. package/dist/farming-2/legacy-icons-v1/logo-512.png +0 -0
  413. package/dist/farming-2/legacy-icons-v1/logo-64.png +0 -0
  414. package/dist/farming-2/logo-16.png +0 -0
  415. package/dist/farming-2/logo-180.png +0 -0
  416. package/dist/farming-2/logo-192.png +0 -0
  417. package/dist/farming-2/logo-32.png +0 -0
  418. package/dist/farming-2/logo-48.png +0 -0
  419. package/dist/farming-2/logo-512.png +0 -0
  420. package/dist/farming-2/logo-64.png +0 -0
  421. package/dist/farming-2/site.webmanifest +27 -0
  422. package/dist/farming-2/style_capsules/watercolor-pastoral.json +94 -0
  423. package/dist/index.html +27 -0
  424. package/frontend/ghostty-loader.js +24 -0
  425. package/frontend/runtime-paths.js +28 -0
  426. package/frontend/session-bridge.js +82 -0
  427. package/frontend/session-modal-bridge.js +331 -0
  428. package/frontend/skin-bridge.js +25 -0
  429. package/frontend/skins/crt/app.js +6940 -0
  430. package/frontend/skins/crt/assets/branding/farming-crt-icon.svg +11 -0
  431. package/frontend/skins/crt/assets/branding/farming-crt-logo-v1.png +0 -0
  432. package/frontend/skins/crt/assets/effects/phosphor-noise.svg +11 -0
  433. package/frontend/skins/crt/assets/fonts/departure-mono/DepartureMonoNerdFontMono-Regular.otf +0 -0
  434. package/frontend/skins/crt/assets/fonts/departure-mono/LICENSE +93 -0
  435. package/frontend/skins/crt/effects.js +8 -0
  436. package/frontend/skins/crt/index.html +1712 -0
  437. package/frontend/skins/crt/styles/billing.css +1003 -0
  438. package/frontend/skins/crt/styles/effects.css +171 -0
  439. package/frontend/skins/crt/styles/history.css +215 -0
  440. package/frontend/skins/crt/styles/monochrome-green.css +509 -0
  441. package/frontend/skins/crt/styles/search.css +271 -0
  442. package/frontend/terminal-bridge.js +192 -0
  443. package/frontend/theme-loader.js +84 -0
  444. package/frontend/themes/terminal/theme.json +24 -0
  445. package/frontend/vendor/ghostty-web/LICENSE +21 -0
  446. package/frontend/vendor/ghostty-web/__vite-browser-external-2447137e.js +4 -0
  447. package/frontend/vendor/ghostty-web/ghostty-vt.wasm +0 -0
  448. package/frontend/vendor/ghostty-web/ghostty-web.js +2963 -0
  449. package/frontend/vendor/ghostty-web/ghostty-web.umd.cjs +13 -0
  450. package/frontend/vendor/ghostty-web/index.d.ts +1842 -0
  451. package/index.html +23 -0
  452. package/package.json +144 -0
  453. package/scripts/sync-ghostty-vendor.js +66 -0
@@ -0,0 +1,1144 @@
1
+ const EventEmitter = require('events');
2
+ const fs = require('fs');
3
+ const path = require('path');
4
+ const { spawn } = require('child_process');
5
+ const { Readable, Writable } = require('stream');
6
+ const { createRequire } = require('module');
7
+ const packageJson = require('../package.json');
8
+ const { AcpSessionState } = require('./acp-session-state');
9
+ const { AcpClientFileSystem, AcpClientTerminalManager } = require('./acp/client-services');
10
+ const { permissionSecurityWarnings } = require('./acp/permission-security');
11
+ const { rejectPatch } = require('./acp/patch-decisions');
12
+
13
+ const ADAPTER_VERSIONS = Object.freeze({
14
+ codex: '1.1.2',
15
+ claude: '0.58.1',
16
+ opencode: 'native',
17
+ qoder: 'native',
18
+ });
19
+ const DEFAULT_INITIALIZE_TIMEOUT_MS = 15_000;
20
+ const DEFAULT_SESSION_SETUP_TIMEOUT_MS = 120_000;
21
+ const DEFAULT_REQUEST_TIMEOUT_MS = 30_000;
22
+ const DEFAULT_CANCEL_TIMEOUT_MS = 15_000;
23
+ const DEFAULT_HISTORY_REPLAY_MIN_WAIT_MS = 350;
24
+ const DEFAULT_HISTORY_REPLAY_QUIET_MS = 150;
25
+ const DEFAULT_HISTORY_REPLAY_MAX_WAIT_MS = 5_000;
26
+
27
+ let sdkPromise;
28
+ const runtimeRequire = createRequire(__filename);
29
+ function loadAcpSdk() {
30
+ if (!sdkPromise) sdkPromise = import('@agentclientprotocol/sdk');
31
+ return sdkPromise;
32
+ }
33
+
34
+ function adapterEntry(packageName) {
35
+ let sdkDirectory;
36
+ try {
37
+ sdkDirectory = path.dirname(runtimeRequire.resolve('@agentclientprotocol/sdk'));
38
+ } catch {
39
+ throw new Error('ACP runtime packages are unavailable in this installation. Use the npm or app-bundle distribution.');
40
+ }
41
+ const entry = path.resolve(sdkDirectory, '..', '..', packageName.split('/').pop(), 'dist', 'index.js');
42
+ if (!fs.existsSync(entry)) throw new Error(`ACP adapter is not installed: ${packageName}`);
43
+ return entry;
44
+ }
45
+
46
+ function resolveAcpLaunch(provider, options = {}) {
47
+ const normalized = String(provider || '').trim().toLowerCase();
48
+ if (normalized === 'codex') {
49
+ return {
50
+ command: process.execPath,
51
+ args: [adapterEntry('@agentclientprotocol/codex-acp')],
52
+ version: ADAPTER_VERSIONS.codex,
53
+ };
54
+ }
55
+ if (normalized === 'claude') {
56
+ return {
57
+ command: process.execPath,
58
+ args: [adapterEntry('@agentclientprotocol/claude-agent-acp')],
59
+ version: ADAPTER_VERSIONS.claude,
60
+ };
61
+ }
62
+ if (normalized === 'opencode') {
63
+ return {
64
+ command: options.executable || 'opencode',
65
+ args: ['acp', '--cwd', path.resolve(options.cwd || process.cwd())],
66
+ version: ADAPTER_VERSIONS.opencode,
67
+ };
68
+ }
69
+ if (normalized === 'qoder') {
70
+ return {
71
+ command: options.executable || 'qodercli',
72
+ args: ['--acp'],
73
+ version: ADAPTER_VERSIONS.qoder,
74
+ };
75
+ }
76
+ throw new Error(`Unsupported ACP provider: ${provider}`);
77
+ }
78
+
79
+ function codexAcpEnvironment(options = {}) {
80
+ const env = { ...(options.env || process.env) };
81
+ let config = {};
82
+ if (env.CODEX_CONFIG) {
83
+ try {
84
+ const parsed = JSON.parse(env.CODEX_CONFIG);
85
+ if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) config = parsed;
86
+ } catch {
87
+ // A selected Farming profile below replaces an invalid adapter config.
88
+ }
89
+ }
90
+
91
+ if (options.model && options.model !== 'config') config.model = options.model;
92
+ if (options.reasoningEffort && options.reasoningEffort !== 'config') {
93
+ config.model_reasoning_effort = options.reasoningEffort;
94
+ }
95
+ if (options.serviceTier && !['config', 'default'].includes(options.serviceTier)) {
96
+ config.service_tier = options.serviceTier;
97
+ }
98
+ if (Object.keys(config).length > 0) env.CODEX_CONFIG = JSON.stringify(config);
99
+
100
+ const initialMode = {
101
+ ask: 'read-only',
102
+ approve: 'agent',
103
+ full: 'agent-full-access',
104
+ }[options.approvalMode];
105
+ if (initialMode) env.INITIAL_AGENT_MODE = initialMode;
106
+ return env;
107
+ }
108
+
109
+ function selectedPermission(option) {
110
+ return { outcome: { outcome: 'selected', optionId: option.optionId } };
111
+ }
112
+
113
+ function withTimeout(promise, timeoutMs, label) {
114
+ let timer;
115
+ const timeout = new Promise((_, reject) => {
116
+ timer = setTimeout(() => reject(new Error(`${label} timed out after ${timeoutMs}ms`)), timeoutMs);
117
+ timer.unref?.();
118
+ });
119
+ return Promise.race([promise, timeout]).finally(() => clearTimeout(timer));
120
+ }
121
+
122
+ function acpErrorMessage(error) {
123
+ const message = error && error.message ? error.message : String(error || 'ACP request failed');
124
+ const details = error?.data?.details;
125
+ return typeof details === 'string' && details && details !== message
126
+ ? `${message}: ${details}`
127
+ : message;
128
+ }
129
+
130
+ function acpErrorKind(error) {
131
+ const message = (error && error.message ? error.message : String(error || '')).toLowerCase();
132
+ const code = String(error?.code || error?.data?.code || '').toLowerCase();
133
+ const text = `${code} ${message}`;
134
+ if (/\b(401|unauthorized|authentication|authenticate|login|sign[ -]?in|invalid api key)\b/.test(text)) return 'authentication';
135
+ if (/\b(402|payment|billing|credit|insufficient quota)\b/.test(text)) return 'payment';
136
+ if (/context (?:window|length|limit)|too many tokens|max(?:imum)? tokens/.test(text)) return 'context';
137
+ if (/\b(model_not_found|unknown model|model unavailable|unsupported model)\b/.test(text)) return 'model';
138
+ if (/\b(429|rate[ -]?limit|too many requests)\b/.test(text)) return 'rate-limit';
139
+ if (/\b(econn|enet|ehost|socket|network|connection|timed? out|timeout|dns)\b/.test(text)) return 'network';
140
+ if (/\b(protocol|json-rpc|parse error|invalid request|method not found)\b/.test(text)) return 'protocol';
141
+ return 'unknown';
142
+ }
143
+
144
+ function autoPermissionResponse(request, approvalMode) {
145
+ const options = Array.isArray(request?.options) ? request.options : [];
146
+ if (approvalMode === 'full') {
147
+ const option = options.find(item => item.kind === 'allow_always')
148
+ || options.find(item => item.kind === 'allow_once');
149
+ return option ? selectedPermission(option) : { outcome: { outcome: 'cancelled' } };
150
+ }
151
+ if (approvalMode === 'ask') {
152
+ const option = options.find(item => item.kind === 'reject_once')
153
+ || options.find(item => item.kind === 'reject_always');
154
+ return option ? selectedPermission(option) : { outcome: { outcome: 'cancelled' } };
155
+ }
156
+ return null;
157
+ }
158
+
159
+ function validateElicitationContent(request, content) {
160
+ if (request?.mode !== 'form') return undefined;
161
+ const value = content && typeof content === 'object' && !Array.isArray(content) ? content : {};
162
+ const schema = request.requestedSchema && typeof request.requestedSchema === 'object'
163
+ ? request.requestedSchema
164
+ : {};
165
+ const properties = schema.properties && typeof schema.properties === 'object' ? schema.properties : {};
166
+ for (const name of Array.isArray(schema.required) ? schema.required : []) {
167
+ if (!Object.prototype.hasOwnProperty.call(value, name)) throw new Error(`ACP input is required: ${name}`);
168
+ }
169
+ for (const [name, fieldValue] of Object.entries(value)) {
170
+ const property = properties[name];
171
+ if (!property || typeof property !== 'object') throw new Error(`Unknown ACP input field: ${name}`);
172
+ if (property.type === 'string' && typeof fieldValue !== 'string') throw new Error(`ACP input must be text: ${name}`);
173
+ if (property.type === 'number' && (typeof fieldValue !== 'number' || !Number.isFinite(fieldValue))) {
174
+ throw new Error(`ACP input must be a number: ${name}`);
175
+ }
176
+ if (property.type === 'integer' && !Number.isInteger(fieldValue)) throw new Error(`ACP input must be an integer: ${name}`);
177
+ if (property.type === 'boolean' && typeof fieldValue !== 'boolean') throw new Error(`ACP input must be true or false: ${name}`);
178
+ if (property.type === 'array' && (!Array.isArray(fieldValue) || fieldValue.some(item => typeof item !== 'string'))) {
179
+ throw new Error(`ACP input must be a text selection: ${name}`);
180
+ }
181
+ if (typeof fieldValue === 'string') {
182
+ if (Number.isFinite(property.minLength) && fieldValue.length < property.minLength) throw new Error(`ACP input is too short: ${name}`);
183
+ if (Number.isFinite(property.maxLength) && fieldValue.length > property.maxLength) throw new Error(`ACP input is too long: ${name}`);
184
+ if (typeof property.pattern === 'string') {
185
+ let pattern;
186
+ try {
187
+ pattern = new RegExp(property.pattern);
188
+ } catch {
189
+ throw new Error(`ACP input schema has an invalid pattern: ${name}`);
190
+ }
191
+ if (!pattern.test(fieldValue)) throw new Error(`ACP input has an invalid format: ${name}`);
192
+ }
193
+ const allowed = Array.isArray(property.enum)
194
+ ? property.enum
195
+ : Array.isArray(property.oneOf)
196
+ ? property.oneOf.map(option => option?.const)
197
+ : null;
198
+ if (allowed && !allowed.includes(fieldValue)) throw new Error(`ACP input is not an allowed choice: ${name}`);
199
+ }
200
+ if (typeof fieldValue === 'number') {
201
+ if (Number.isFinite(property.minimum) && fieldValue < property.minimum) throw new Error(`ACP input is below the minimum: ${name}`);
202
+ if (Number.isFinite(property.maximum) && fieldValue > property.maximum) throw new Error(`ACP input is above the maximum: ${name}`);
203
+ }
204
+ if (Array.isArray(fieldValue)) {
205
+ if (Number.isFinite(property.minItems) && fieldValue.length < property.minItems) throw new Error(`ACP input needs more selections: ${name}`);
206
+ if (Number.isFinite(property.maxItems) && fieldValue.length > property.maxItems) throw new Error(`ACP input has too many selections: ${name}`);
207
+ const allowedItems = Array.isArray(property.items?.enum)
208
+ ? property.items.enum
209
+ : Array.isArray(property.items?.anyOf)
210
+ ? property.items.anyOf.map(option => option?.const)
211
+ : null;
212
+ if (allowedItems && fieldValue.some(item => !allowedItems.includes(item))) {
213
+ throw new Error(`ACP input contains an unknown selection: ${name}`);
214
+ }
215
+ }
216
+ }
217
+ return JSON.parse(JSON.stringify(value));
218
+ }
219
+
220
+ function interactiveRuntimeState(binding, fallback = '') {
221
+ if (binding.pendingPermissions.size > 0) return 'waiting-for-permission';
222
+ if (binding.pendingElicitations.size > 0) return 'waiting-for-input';
223
+ if (binding.promptActive) return 'working';
224
+ if (['connecting', 'idle', 'error'].includes(String(fallback || ''))) return fallback;
225
+ return binding.sessionId ? 'idle' : 'connecting';
226
+ }
227
+
228
+ class AcpRuntime extends EventEmitter {
229
+ constructor(options = {}) {
230
+ super();
231
+ this.spawn = options.spawn || spawn;
232
+ this.createConnection = options.createConnection || null;
233
+ this.resolveLaunch = options.resolveLaunch || resolveAcpLaunch;
234
+ this.maxUpdates = options.maxUpdates;
235
+ this.initializeTimeoutMs = options.initializeTimeoutMs || DEFAULT_INITIALIZE_TIMEOUT_MS;
236
+ this.sessionSetupTimeoutMs = options.sessionSetupTimeoutMs || DEFAULT_SESSION_SETUP_TIMEOUT_MS;
237
+ this.requestTimeoutMs = options.requestTimeoutMs || DEFAULT_REQUEST_TIMEOUT_MS;
238
+ this.cancelTimeoutMs = options.cancelTimeoutMs || DEFAULT_CANCEL_TIMEOUT_MS;
239
+ this.historyReplayMinWaitMs = options.historyReplayMinWaitMs ?? DEFAULT_HISTORY_REPLAY_MIN_WAIT_MS;
240
+ this.historyReplayQuietMs = options.historyReplayQuietMs ?? DEFAULT_HISTORY_REPLAY_QUIET_MS;
241
+ this.historyReplayMaxWaitMs = options.historyReplayMaxWaitMs ?? DEFAULT_HISTORY_REPLAY_MAX_WAIT_MS;
242
+ this.bindings = new Map();
243
+ this.permissionSequence = 0;
244
+ this.elicitationSequence = 0;
245
+ this.clientFileSystem = options.clientFileSystem || new AcpClientFileSystem();
246
+ this.clientTerminals = options.clientTerminals || new AcpClientTerminalManager({ spawn: options.terminalSpawn });
247
+ }
248
+
249
+ async prepareAgent(options = {}) {
250
+ const agentId = String(options.agentId || '');
251
+ if (!agentId) throw new Error('ACP Agent id is required');
252
+ if (this.bindings.has(agentId)) throw new Error('ACP Agent is already registered');
253
+ const provider = String(options.provider || '').trim().toLowerCase();
254
+ const launch = this.resolveLaunch(provider, options);
255
+ const binding = {
256
+ agentId,
257
+ provider,
258
+ cwd: path.resolve(options.cwd || process.cwd()),
259
+ env: provider === 'codex' ? codexAcpEnvironment(options) : (options.env || process.env),
260
+ launch,
261
+ restartOptions: { ...options, agentId, provider },
262
+ approvalMode: options.approvalMode || 'approve',
263
+ child: null,
264
+ connection: null,
265
+ initializeResponse: null,
266
+ sessionId: '',
267
+ state: 'connecting',
268
+ error: '',
269
+ stopReason: '',
270
+ modes: null,
271
+ configOptions: [],
272
+ pendingPermissions: new Map(),
273
+ permissionResolvers: new Map(),
274
+ pendingElicitations: new Map(),
275
+ elicitationResolvers: new Map(),
276
+ activeElicitations: new Map(),
277
+ subagentStates: new Map(),
278
+ interactionOrigins: new Map(),
279
+ promptActive: false,
280
+ sessionState: null,
281
+ authTerminal: null,
282
+ patchDecisions: new Map(),
283
+ stderr: '',
284
+ updatedAt: new Date().toISOString(),
285
+ };
286
+ this.bindings.set(agentId, binding);
287
+ this.emitRuntime(binding);
288
+
289
+ try {
290
+ const child = this.spawn(launch.command, launch.args, {
291
+ cwd: binding.cwd,
292
+ env: binding.env,
293
+ stdio: ['pipe', 'pipe', 'pipe'],
294
+ });
295
+ binding.child = child;
296
+ child.stderr.on('data', chunk => {
297
+ binding.stderr = `${binding.stderr}${chunk.toString('utf8')}`.slice(-16_000);
298
+ });
299
+ child.on('error', error => this.handleExit(binding, error));
300
+ child.on('close', (code, signal) => {
301
+ if (!binding.connection?.signal?.aborted) {
302
+ const detail = binding.stderr.trim() || `ACP adapter exited with code ${code}${signal ? ` (${signal})` : ''}`;
303
+ this.handleExit(binding, code === 0 ? null : new Error(detail));
304
+ }
305
+ });
306
+
307
+ const connection = this.createConnection
308
+ ? await this.createConnection(this.clientHandlers(binding), child, binding)
309
+ : await this.officialConnection(this.clientHandlers(binding), child);
310
+ binding.connection = connection;
311
+ connection.closed.catch(error => this.handleExit(binding, error));
312
+ const sdk = await loadAcpSdk();
313
+ binding.initializeResponse = await withTimeout(connection.initialize({
314
+ protocolVersion: sdk.PROTOCOL_VERSION,
315
+ clientCapabilities: {
316
+ fs: { readTextFile: true, writeTextFile: true },
317
+ terminal: true,
318
+ auth: { terminal: true },
319
+ session: { configOptions: { boolean: {} } },
320
+ plan: {},
321
+ elicitation: { form: {}, url: {} },
322
+ _meta: { terminal_output: true },
323
+ },
324
+ clientInfo: { name: 'farming', title: 'Farming', version: packageJson.version || '0.0.0' },
325
+ }), this.initializeTimeoutMs, 'ACP initialize');
326
+ if (binding.initializeResponse.protocolVersion !== sdk.PROTOCOL_VERSION) {
327
+ throw new Error(`ACP protocol version mismatch: Agent selected ${binding.initializeResponse.protocolVersion}, Farming supports ${sdk.PROTOCOL_VERSION}`);
328
+ }
329
+
330
+ const requestedSessionId = String(options.sessionId || '').trim();
331
+ const revisionBase = Number.isFinite(Number(options.revisionBase))
332
+ ? Math.max(0, Math.floor(Number(options.revisionBase)))
333
+ : 0;
334
+ const sessionRequest = { sessionId: requestedSessionId, cwd: binding.cwd, mcpServers: [] };
335
+ let sessionResponse;
336
+ let historyMode = 'new';
337
+ if (requestedSessionId) {
338
+ const capabilities = binding.initializeResponse.agentCapabilities || {};
339
+ if (options.historyMode !== 'resume' && capabilities.loadSession) {
340
+ binding.sessionId = requestedSessionId;
341
+ binding.sessionState = new AcpSessionState({
342
+ provider,
343
+ sessionId: requestedSessionId,
344
+ cwd: binding.cwd,
345
+ maxUpdates: this.maxUpdates,
346
+ revisionBase,
347
+ resetBeforeRevision: revisionBase,
348
+ });
349
+ sessionResponse = await withTimeout(
350
+ connection.loadSession(sessionRequest),
351
+ this.sessionSetupTimeoutMs,
352
+ 'ACP session/load'
353
+ );
354
+ if (provider === 'qoder') await this.waitForHistoryReplay(binding);
355
+ binding.sessionState.finishHistoryReplay();
356
+ historyMode = 'load';
357
+ } else if (capabilities.sessionCapabilities?.resume) {
358
+ binding.sessionId = requestedSessionId;
359
+ binding.sessionState = new AcpSessionState({
360
+ provider,
361
+ sessionId: requestedSessionId,
362
+ cwd: binding.cwd,
363
+ maxUpdates: this.maxUpdates,
364
+ revisionBase,
365
+ });
366
+ sessionResponse = await withTimeout(
367
+ connection.resumeSession(sessionRequest),
368
+ this.sessionSetupTimeoutMs,
369
+ 'ACP session/resume'
370
+ );
371
+ historyMode = 'resume';
372
+ } else {
373
+ throw new Error(`${provider} ACP Agent cannot load or resume session ${requestedSessionId}`);
374
+ }
375
+ } else {
376
+ sessionResponse = await withTimeout(
377
+ connection.newSession({ cwd: binding.cwd, mcpServers: [] }),
378
+ this.sessionSetupTimeoutMs,
379
+ 'ACP session/new'
380
+ );
381
+ binding.sessionId = String(sessionResponse.sessionId || '');
382
+ if (!binding.sessionId) throw new Error('ACP session/new did not return a session id');
383
+ binding.sessionState = new AcpSessionState({ provider, sessionId: binding.sessionId, cwd: binding.cwd, maxUpdates: this.maxUpdates });
384
+ }
385
+ binding.modes = sessionResponse?.modes || null;
386
+ binding.configOptions = sessionResponse?.configOptions || [];
387
+ binding.sessionState.currentModeId = String(binding.modes?.currentModeId || '');
388
+ binding.sessionState.configOptions = JSON.parse(JSON.stringify(binding.configOptions));
389
+ binding.state = 'idle';
390
+ binding.updatedAt = new Date().toISOString();
391
+ this.emitRuntime(binding);
392
+ this.emitSession(binding);
393
+ return {
394
+ sessionId: binding.sessionId,
395
+ historyMode,
396
+ protocolVersion: binding.initializeResponse.protocolVersion,
397
+ agentInfo: binding.initializeResponse.agentInfo || null,
398
+ capabilities: binding.initializeResponse.agentCapabilities || {},
399
+ adapter: launch,
400
+ };
401
+ } catch (error) {
402
+ const runtimeError = new Error(acpErrorMessage(error), { cause: error });
403
+ this.handleExit(binding, runtimeError);
404
+ this.unregisterAgent(agentId);
405
+ throw runtimeError;
406
+ }
407
+ }
408
+
409
+ async officialConnection(handlers, child) {
410
+ const sdk = await loadAcpSdk();
411
+ const stream = sdk.ndJsonStream(Writable.toWeb(child.stdin), Readable.toWeb(child.stdout));
412
+ return new sdk.ClientSideConnection(() => handlers, stream);
413
+ }
414
+
415
+ async waitForHistoryReplay(binding) {
416
+ const startedAt = Date.now();
417
+ let lastUpdateCount = binding.sessionState?.updates.length || 0;
418
+ let lastChangedAt = startedAt;
419
+ while (Date.now() - startedAt < this.historyReplayMaxWaitMs) {
420
+ await new Promise(resolve => setTimeout(resolve, 50));
421
+ const now = Date.now();
422
+ const updateCount = binding.sessionState?.updates.length || 0;
423
+ if (updateCount !== lastUpdateCount) {
424
+ lastUpdateCount = updateCount;
425
+ lastChangedAt = now;
426
+ }
427
+ if (
428
+ now - startedAt >= this.historyReplayMinWaitMs
429
+ && now - lastChangedAt >= this.historyReplayQuietMs
430
+ ) return;
431
+ }
432
+ }
433
+
434
+ clientHandlers(binding) {
435
+ return {
436
+ sessionUpdate: notification => {
437
+ const notificationSessionId = String(notification?.sessionId || '');
438
+ const isPrimarySession = !binding.sessionId || !notificationSessionId || notificationSessionId === binding.sessionId;
439
+ let targetState = isPrimarySession ? binding.sessionState : binding.subagentStates.get(notificationSessionId);
440
+ if (!targetState && notificationSessionId && binding.subagentStates.size < 32) {
441
+ targetState = new AcpSessionState({
442
+ provider: binding.provider,
443
+ sessionId: notificationSessionId,
444
+ cwd: binding.cwd,
445
+ maxUpdates: this.maxUpdates,
446
+ });
447
+ binding.subagentStates.set(notificationSessionId, targetState);
448
+ }
449
+ if (targetState?.apply(notification)) {
450
+ const update = notification?.update;
451
+ if (isPrimarySession && update?.sessionUpdate === 'current_mode_update' && binding.modes) {
452
+ binding.modes = { ...binding.modes, currentModeId: String(update.currentModeId || '') };
453
+ }
454
+ if (isPrimarySession && update?.sessionUpdate === 'config_option_update') {
455
+ binding.configOptions = JSON.parse(JSON.stringify(update.configOptions || []));
456
+ }
457
+ if (!isPrimarySession && binding.sessionState) {
458
+ const parentTool = binding.sessionState.entries.find(entry => (
459
+ entry?.type === 'tool'
460
+ && String(entry?._meta?.subagent_session_info?.session_id || '') === notificationSessionId
461
+ ));
462
+ if (parentTool) binding.sessionState.touchEntry(parentTool);
463
+ }
464
+ binding.updatedAt = new Date().toISOString();
465
+ this.emitSession(binding);
466
+ }
467
+ },
468
+ requestPermission: request => this.requestPermission(binding, request),
469
+ readTextFile: request => this.clientFileSystem.readTextFile(binding, request),
470
+ writeTextFile: request => this.clientFileSystem.writeTextFile(binding, request),
471
+ createTerminal: request => this.clientTerminals.create(binding, request),
472
+ terminalOutput: request => this.clientTerminals.output(binding, request),
473
+ waitForTerminalExit: request => this.clientTerminals.waitForExit(binding, request),
474
+ killTerminal: request => this.clientTerminals.kill(binding, request),
475
+ releaseTerminal: request => this.clientTerminals.release(binding, request),
476
+ unstable_createElicitation: request => this.requestElicitation(binding, request),
477
+ unstable_completeElicitation: notification => this.completeElicitation(binding, notification),
478
+ };
479
+ }
480
+
481
+ requestPermission(binding, request) {
482
+ const automatic = autoPermissionResponse(request, binding.approvalMode);
483
+ if (automatic) return automatic;
484
+ const requestId = `acp-permission-${++this.permissionSequence}`;
485
+ binding.interactionOrigins.set(requestId, binding.state);
486
+ binding.state = 'waiting-for-permission';
487
+ const pending = { ...JSON.parse(JSON.stringify(request)), requestId };
488
+ pending.origin = String(request?.sessionId || '') && String(request.sessionId) !== binding.sessionId
489
+ ? 'subagent'
490
+ : 'agent';
491
+ pending.securityWarnings = permissionSecurityWarnings(pending);
492
+ binding.pendingPermissions.set(requestId, pending);
493
+ this.emitRuntime(binding);
494
+ return new Promise(resolve => binding.permissionResolvers.set(requestId, resolve));
495
+ }
496
+
497
+ respondPermission(agentId, requestId, optionId, cancelled = false) {
498
+ const binding = this.requireBinding(agentId);
499
+ const pending = binding.pendingPermissions.get(String(requestId || ''));
500
+ const resolve = binding.permissionResolvers.get(String(requestId || ''));
501
+ if (!pending || pending.requestId !== requestId || !resolve) throw new Error('ACP permission request is no longer pending');
502
+ let response = { outcome: { outcome: 'cancelled' } };
503
+ if (!cancelled) {
504
+ const option = pending.options.find(item => item.optionId === optionId);
505
+ if (!option) throw new Error('Unknown ACP permission option');
506
+ response = selectedPermission(option);
507
+ }
508
+ binding.permissionResolvers.delete(requestId);
509
+ binding.pendingPermissions.delete(requestId);
510
+ const origin = binding.interactionOrigins.get(requestId);
511
+ binding.interactionOrigins.delete(requestId);
512
+ binding.state = interactiveRuntimeState(binding, origin);
513
+ resolve(response);
514
+ this.emitRuntime(binding);
515
+ return response;
516
+ }
517
+
518
+ requestElicitation(binding, request) {
519
+ const requestSessionId = String(request?.sessionId || binding.sessionId);
520
+ const isPrimarySession = requestSessionId === binding.sessionId;
521
+ if (!isPrimarySession && !binding.subagentStates.has(requestSessionId)) {
522
+ throw new Error('ACP elicitation does not match an active session');
523
+ }
524
+ if (!['form', 'url'].includes(String(request?.mode || ''))) {
525
+ return { action: 'cancel' };
526
+ }
527
+ const requestId = `acp-elicitation-${++this.elicitationSequence}`;
528
+ const cloned = JSON.parse(JSON.stringify(request));
529
+ const protocolRequestId = Object.prototype.hasOwnProperty.call(cloned, 'requestId')
530
+ ? cloned.requestId
531
+ : undefined;
532
+ delete cloned.requestId;
533
+ const pending = {
534
+ ...cloned,
535
+ ...(protocolRequestId !== undefined ? { protocolRequestId } : {}),
536
+ requestId,
537
+ origin: isPrimarySession ? 'agent' : 'subagent',
538
+ };
539
+ binding.interactionOrigins.set(requestId, binding.state);
540
+ binding.pendingElicitations.set(requestId, pending);
541
+ binding.state = 'waiting-for-input';
542
+ this.emitRuntime(binding);
543
+ return new Promise(resolve => binding.elicitationResolvers.set(requestId, resolve));
544
+ }
545
+
546
+ respondElicitation(agentId, requestId, action, content) {
547
+ const binding = this.requireBinding(agentId);
548
+ const id = String(requestId || '');
549
+ const pending = binding.pendingElicitations.get(id);
550
+ const resolve = binding.elicitationResolvers.get(id);
551
+ if (!pending || !resolve) throw new Error('ACP input request is no longer pending');
552
+ const normalizedAction = String(action || 'cancel');
553
+ if (!['accept', 'decline', 'cancel'].includes(normalizedAction)) throw new Error('Unknown ACP input action');
554
+ const response = normalizedAction === 'accept'
555
+ ? { action: 'accept', ...(pending.mode === 'form' ? { content: validateElicitationContent(pending, content) } : {}) }
556
+ : { action: normalizedAction };
557
+ binding.elicitationResolvers.delete(id);
558
+ binding.pendingElicitations.delete(id);
559
+ if (pending.mode === 'url' && normalizedAction === 'accept') {
560
+ binding.activeElicitations.set(String(pending.elicitationId || id), { ...pending, status: 'accepted' });
561
+ }
562
+ const origin = binding.interactionOrigins.get(id);
563
+ binding.interactionOrigins.delete(id);
564
+ binding.state = interactiveRuntimeState(binding, origin);
565
+ resolve(response);
566
+ this.emitRuntime(binding);
567
+ return response;
568
+ }
569
+
570
+ completeElicitation(binding, notification) {
571
+ const elicitationId = String(notification?.elicitationId || '');
572
+ if (elicitationId) binding.activeElicitations.delete(elicitationId);
573
+ this.emitRuntime(binding);
574
+ }
575
+
576
+ async prompt(agentId, prompt) {
577
+ const binding = this.requireBinding(agentId);
578
+ if (!['idle', 'error'].includes(binding.state)) throw new Error(`ACP Agent is not ready (${binding.state})`);
579
+ const content = Array.isArray(prompt) ? prompt : [{ type: 'text', text: String(prompt || '') }];
580
+ binding.sessionState.beginPrompt(content);
581
+ binding.promptActive = true;
582
+ binding.state = 'working';
583
+ binding.error = '';
584
+ binding.stopReason = '';
585
+ this.emitRuntime(binding);
586
+ this.emitSession(binding);
587
+ try {
588
+ const response = await binding.connection.prompt({ sessionId: binding.sessionId, prompt: content });
589
+ binding.stopReason = String(response?.stopReason || '');
590
+ binding.sessionState.completePrompt(binding.stopReason);
591
+ binding.promptActive = false;
592
+ binding.state = 'idle';
593
+ binding.updatedAt = new Date().toISOString();
594
+ this.emitSession(binding);
595
+ this.emitRuntime(binding);
596
+ return { sessionId: binding.sessionId, stopReason: binding.stopReason };
597
+ } catch (error) {
598
+ const runtimeError = new Error(acpErrorMessage(error), { cause: error });
599
+ binding.stopReason = 'error';
600
+ // JSON-RPC implementations commonly move the actionable provider text
601
+ // into error.data.details. Classify the normalized message so the
602
+ // ordered transcript and runtime snapshot cannot disagree.
603
+ binding.sessionState.recordError(runtimeError.message, acpErrorKind(runtimeError));
604
+ binding.sessionState.completePrompt('error');
605
+ binding.promptActive = false;
606
+ binding.state = 'error';
607
+ binding.error = runtimeError.message;
608
+ this.emitSession(binding);
609
+ this.emitRuntime(binding);
610
+ throw runtimeError;
611
+ }
612
+ }
613
+
614
+ async cancel(agentId) {
615
+ const binding = this.requireBinding(agentId);
616
+ if (!binding.sessionId) return false;
617
+ binding.state = 'interrupting';
618
+ for (const resolve of binding.permissionResolvers.values()) {
619
+ resolve({ outcome: { outcome: 'cancelled' } });
620
+ }
621
+ for (const resolve of binding.elicitationResolvers.values()) resolve({ action: 'cancel' });
622
+ binding.permissionResolvers.clear();
623
+ binding.pendingPermissions.clear();
624
+ binding.elicitationResolvers.clear();
625
+ binding.pendingElicitations.clear();
626
+ binding.activeElicitations.clear();
627
+ binding.interactionOrigins.clear();
628
+ this.emitRuntime(binding);
629
+ try {
630
+ await withTimeout(
631
+ binding.connection.cancel({ sessionId: binding.sessionId }),
632
+ this.cancelTimeoutMs,
633
+ 'ACP session/cancel'
634
+ );
635
+ return true;
636
+ } catch (error) {
637
+ const runtimeError = new Error(acpErrorMessage(error), { cause: error });
638
+ binding.state = 'error';
639
+ binding.error = runtimeError.message;
640
+ binding.stopReason = 'cancel_error';
641
+ binding.updatedAt = new Date().toISOString();
642
+ this.emitSession(binding);
643
+ this.emitRuntime(binding);
644
+ throw runtimeError;
645
+ }
646
+ }
647
+
648
+ async cancelSubagent(agentId, sessionId) {
649
+ const binding = this.requireBinding(agentId);
650
+ const targetSessionId = String(sessionId || '');
651
+ if (!targetSessionId || targetSessionId === binding.sessionId || !binding.subagentStates.has(targetSessionId)) {
652
+ throw new Error('ACP subagent session not found');
653
+ }
654
+ for (const [requestId, pending] of binding.pendingPermissions.entries()) {
655
+ if (String(pending?.sessionId || '') !== targetSessionId) continue;
656
+ binding.permissionResolvers.get(requestId)?.({ outcome: { outcome: 'cancelled' } });
657
+ binding.permissionResolvers.delete(requestId);
658
+ binding.pendingPermissions.delete(requestId);
659
+ binding.interactionOrigins.delete(requestId);
660
+ }
661
+ for (const [requestId, pending] of binding.pendingElicitations.entries()) {
662
+ if (String(pending?.sessionId || '') !== targetSessionId) continue;
663
+ binding.elicitationResolvers.get(requestId)?.({ action: 'cancel' });
664
+ binding.elicitationResolvers.delete(requestId);
665
+ binding.pendingElicitations.delete(requestId);
666
+ binding.interactionOrigins.delete(requestId);
667
+ }
668
+ for (const [elicitationId, active] of binding.activeElicitations.entries()) {
669
+ if (String(active?.sessionId || '') === targetSessionId) binding.activeElicitations.delete(elicitationId);
670
+ }
671
+ binding.state = interactiveRuntimeState(binding, binding.state);
672
+ this.emitRuntime(binding);
673
+ await withTimeout(
674
+ binding.connection.cancel({ sessionId: targetSessionId }),
675
+ this.cancelTimeoutMs,
676
+ 'ACP subagent session/cancel'
677
+ );
678
+ binding.updatedAt = new Date().toISOString();
679
+ this.emitSession(binding);
680
+ this.emitRuntime(binding);
681
+ return { cancelled: true, sessionId: targetSessionId };
682
+ }
683
+
684
+ async listSessions(agentId, options = {}) {
685
+ const binding = this.requireBinding(agentId);
686
+ const capabilities = binding.initializeResponse?.agentCapabilities?.sessionCapabilities;
687
+ if (!capabilities?.list) throw new Error(`${binding.provider} ACP Agent does not support session/list`);
688
+ return withTimeout(binding.connection.listSessions({
689
+ ...(options.cwd ? { cwd: path.resolve(options.cwd) } : {}),
690
+ ...(options.cursor ? { cursor: String(options.cursor) } : {}),
691
+ }), this.requestTimeoutMs, 'ACP session/list');
692
+ }
693
+
694
+ async authenticate(agentId, methodId) {
695
+ const binding = this.requireBinding(agentId);
696
+ const method = binding.initializeResponse?.authMethods?.find(item => item.id === methodId);
697
+ if (!method) throw new Error('Unknown ACP authentication method');
698
+ if (method.type === 'terminal' || method?._meta?.['terminal-auth']) {
699
+ return this.startTerminalAuthentication(binding, method);
700
+ }
701
+ await withTimeout(
702
+ binding.connection.authenticate({ methodId }),
703
+ this.requestTimeoutMs,
704
+ 'ACP authenticate'
705
+ );
706
+ binding.error = '';
707
+ binding.stopReason = '';
708
+ binding.state = interactiveRuntimeState(binding, 'idle');
709
+ binding.updatedAt = new Date().toISOString();
710
+ this.emitRuntime(binding);
711
+ this.emitSession(binding);
712
+ return { authenticated: true, methodId };
713
+ }
714
+
715
+ terminalAuthenticationLaunch(binding, method) {
716
+ const legacy = method?._meta?.['terminal-auth'];
717
+ if (legacy && typeof legacy === 'object' && legacy.command) {
718
+ return {
719
+ command: String(legacy.command),
720
+ args: Array.isArray(legacy.args) ? legacy.args.map(String) : [],
721
+ env: legacy.env && typeof legacy.env === 'object' ? legacy.env : {},
722
+ };
723
+ }
724
+ if (method.type !== 'terminal') throw new Error('ACP authentication method is not terminal based');
725
+ return {
726
+ command: binding.launch.command,
727
+ args: [...binding.launch.args, ...(Array.isArray(method.args) ? method.args.map(String) : [])],
728
+ env: method.env && typeof method.env === 'object' ? method.env : {},
729
+ };
730
+ }
731
+
732
+ async startTerminalAuthentication(binding, method) {
733
+ if (binding.authTerminal?.state === 'running') throw new Error('ACP terminal authentication is already running');
734
+ const launch = this.terminalAuthenticationLaunch(binding, method);
735
+ const created = await this.clientTerminals.create(binding, {
736
+ sessionId: binding.sessionId,
737
+ command: launch.command,
738
+ args: launch.args,
739
+ cwd: binding.cwd,
740
+ env: Object.entries(launch.env).map(([name, value]) => ({ name, value: String(value) })),
741
+ outputByteLimit: 2 * 1024 * 1024,
742
+ });
743
+ binding.authTerminal = {
744
+ terminalId: created.terminalId,
745
+ methodId: String(method.id || ''),
746
+ name: String(method.name || 'Sign in'),
747
+ state: 'running',
748
+ error: '',
749
+ };
750
+ binding.updatedAt = new Date().toISOString();
751
+ this.emitRuntime(binding);
752
+ this.emitSession(binding);
753
+ void Promise.resolve(this.clientTerminals.waitForExit(binding, {
754
+ sessionId: binding.sessionId,
755
+ terminalId: created.terminalId,
756
+ })).then(async exit => {
757
+ if (this.bindings.get(binding.agentId) !== binding) return;
758
+ if (exit.exitCode !== 0) {
759
+ binding.authTerminal.state = 'failed';
760
+ binding.authTerminal.error = `Sign-in command exited ${exit.exitCode ?? exit.signal ?? ''}`.trim();
761
+ binding.updatedAt = new Date().toISOString();
762
+ this.emitRuntime(binding);
763
+ this.emitSession(binding);
764
+ return;
765
+ }
766
+ binding.authTerminal.state = 'completed';
767
+ binding.updatedAt = new Date().toISOString();
768
+ this.emitSession(binding);
769
+ await this.restartAgentConnection(binding.agentId).catch(error => {
770
+ const current = this.bindings.get(binding.agentId);
771
+ if (!current) return;
772
+ current.state = 'error';
773
+ current.error = acpErrorMessage(error);
774
+ current.updatedAt = new Date().toISOString();
775
+ this.emitRuntime(current);
776
+ });
777
+ }).catch(error => {
778
+ if (this.bindings.get(binding.agentId) !== binding) return;
779
+ binding.authTerminal.state = 'failed';
780
+ binding.authTerminal.error = acpErrorMessage(error);
781
+ binding.updatedAt = new Date().toISOString();
782
+ this.emitRuntime(binding);
783
+ this.emitSession(binding);
784
+ });
785
+ return { authenticated: false, methodId: method.id, terminalId: created.terminalId };
786
+ }
787
+
788
+ async restartAgentConnection(agentId) {
789
+ const binding = this.requireBinding(agentId);
790
+ const revisionBase = Number(binding.sessionState?.revision || 0);
791
+ const options = {
792
+ ...binding.restartOptions,
793
+ agentId: binding.agentId,
794
+ provider: binding.provider,
795
+ cwd: binding.cwd,
796
+ env: binding.env,
797
+ approvalMode: binding.approvalMode,
798
+ ...(binding.sessionId ? { sessionId: binding.sessionId } : {}),
799
+ ...(revisionBase > 0 ? { revisionBase } : {}),
800
+ };
801
+ this.unregisterAgent(agentId);
802
+ return this.prepareAgent(options);
803
+ }
804
+
805
+ async forkSession(agentId, options = {}) {
806
+ const binding = this.requireBinding(agentId);
807
+ const capabilities = binding.initializeResponse?.agentCapabilities?.sessionCapabilities;
808
+ if (!capabilities?.fork) throw new Error(`${binding.provider} ACP Agent does not support session/fork`);
809
+ return withTimeout(binding.connection.unstable_forkSession({
810
+ sessionId: options.sessionId || binding.sessionId,
811
+ cwd: path.resolve(options.cwd || binding.cwd),
812
+ additionalDirectories: Array.isArray(options.additionalDirectories)
813
+ ? options.additionalDirectories.map(directory => path.resolve(directory))
814
+ : [],
815
+ mcpServers: [],
816
+ }), this.sessionSetupTimeoutMs, 'ACP session/fork');
817
+ }
818
+
819
+ async deleteSession(agentId, sessionId) {
820
+ const binding = this.requireBinding(agentId);
821
+ const capabilities = binding.initializeResponse?.agentCapabilities?.sessionCapabilities;
822
+ if (!capabilities?.delete) throw new Error(`${binding.provider} ACP Agent does not support session/delete`);
823
+ await withTimeout(
824
+ binding.connection.deleteSession({ sessionId: String(sessionId || '') }),
825
+ this.requestTimeoutMs,
826
+ 'ACP session/delete'
827
+ );
828
+ return { deleted: true, sessionId: String(sessionId || '') };
829
+ }
830
+
831
+ async closeSession(agentId) {
832
+ const binding = this.requireBinding(agentId);
833
+ const capabilities = binding.initializeResponse?.agentCapabilities?.sessionCapabilities;
834
+ if (!capabilities?.close) throw new Error(`${binding.provider} ACP Agent does not support session/close`);
835
+ await withTimeout(
836
+ binding.connection.closeSession({ sessionId: binding.sessionId }),
837
+ this.requestTimeoutMs,
838
+ 'ACP session/close'
839
+ );
840
+ binding.state = 'closed';
841
+ this.emitRuntime(binding);
842
+ return { closed: true, sessionId: binding.sessionId };
843
+ }
844
+
845
+ async setSessionMode(agentId, modeId) {
846
+ const binding = this.requireBinding(agentId);
847
+ await withTimeout(
848
+ binding.connection.setSessionMode({ sessionId: binding.sessionId, modeId: String(modeId || '') }),
849
+ this.requestTimeoutMs,
850
+ 'ACP session/set_mode'
851
+ );
852
+ binding.sessionState.currentModeId = String(modeId || '');
853
+ if (binding.modes) binding.modes = { ...binding.modes, currentModeId: binding.sessionState.currentModeId };
854
+ this.emitSession(binding);
855
+ return { sessionId: binding.sessionId, modeId: binding.sessionState.currentModeId };
856
+ }
857
+
858
+ async setSessionConfigOption(agentId, configId, value) {
859
+ const binding = this.requireBinding(agentId);
860
+ const request = typeof value === 'boolean'
861
+ ? { sessionId: binding.sessionId, configId: String(configId || ''), type: 'boolean', value }
862
+ : { sessionId: binding.sessionId, configId: String(configId || ''), value: String(value ?? '') };
863
+ const response = await withTimeout(
864
+ binding.connection.setSessionConfigOption(request),
865
+ this.requestTimeoutMs,
866
+ 'ACP session/set_config_option'
867
+ );
868
+ binding.configOptions = response?.configOptions || binding.configOptions;
869
+ binding.sessionState.configOptions = JSON.parse(JSON.stringify(binding.configOptions));
870
+ this.emitSession(binding);
871
+ return { sessionId: binding.sessionId, configOptions: binding.configOptions };
872
+ }
873
+
874
+ getSession(agentId, options = {}) {
875
+ const binding = this.requireBinding(agentId);
876
+ const runtimeState = {
877
+ state: binding.state,
878
+ error: binding.error,
879
+ errorKind: binding.error ? acpErrorKind(binding.error) : '',
880
+ stopReason: binding.stopReason,
881
+ protocolVersion: binding.initializeResponse?.protocolVersion || null,
882
+ agentInfo: binding.initializeResponse?.agentInfo || null,
883
+ capabilities: binding.initializeResponse?.agentCapabilities || {},
884
+ authMethods: binding.initializeResponse?.authMethods || [],
885
+ modes: binding.modes,
886
+ configOptions: binding.configOptions,
887
+ pendingPermission: binding.pendingPermissions.values().next().value || null,
888
+ pendingPermissions: [...binding.pendingPermissions.values()],
889
+ pendingElicitation: binding.pendingElicitations.values().next().value || null,
890
+ pendingElicitations: [...binding.pendingElicitations.values()],
891
+ activeElicitations: [...binding.activeElicitations.values()],
892
+ adapter: binding.launch,
893
+ authTerminal: binding.authTerminal
894
+ ? { ...binding.authTerminal, terminal: this.clientTerminals.display(binding.authTerminal.terminalId) }
895
+ : null,
896
+ updatedAt: binding.updatedAt,
897
+ };
898
+ if (!binding.sessionState) {
899
+ return {
900
+ version: 1,
901
+ protocol: 'acp',
902
+ provider: binding.provider,
903
+ sessionId: binding.sessionId,
904
+ cwd: binding.cwd,
905
+ title: '',
906
+ updatedAt: binding.updatedAt,
907
+ truncated: false,
908
+ entries: [],
909
+ usage: null,
910
+ availableCommands: [],
911
+ currentModeId: '',
912
+ configOptions: [],
913
+ ...runtimeState,
914
+ };
915
+ }
916
+ return binding.sessionState.snapshot(runtimeState, options);
917
+ }
918
+
919
+ getTranscriptSession(agentId, options = {}) {
920
+ const binding = this.requireBinding(agentId);
921
+ const slice = binding.sessionState
922
+ ? binding.sessionState.transcriptSlice(options)
923
+ : { entries: [], revision: 0, delta: false, hasMoreBefore: false };
924
+ return {
925
+ version: 2,
926
+ protocol: 'acp',
927
+ provider: binding.provider,
928
+ sessionId: binding.sessionId,
929
+ cwd: binding.cwd,
930
+ title: binding.sessionState?.title || '',
931
+ updatedAt: binding.updatedAt,
932
+ truncated: binding.sessionState?.truncated === true,
933
+ state: binding.state,
934
+ error: binding.error,
935
+ errorKind: binding.error ? acpErrorKind(binding.error) : '',
936
+ stopReason: binding.stopReason,
937
+ ...slice,
938
+ };
939
+ }
940
+
941
+ getToolEntry(agentId, toolCallId) {
942
+ const binding = this.requireBinding(agentId);
943
+ const entry = binding.sessionState?.toolEntries.get(String(toolCallId || ''));
944
+ if (!entry || binding.sessionState.isInternalEntry(entry)) return null;
945
+ const visible = JSON.parse(JSON.stringify(entry));
946
+ visible.content = (Array.isArray(visible.content) ? visible.content : []).map(block => {
947
+ if (block?.type !== 'terminal') return block;
948
+ const terminal = this.clientTerminals.display(block.terminalId);
949
+ return terminal ? { ...block, terminal } : block;
950
+ });
951
+ return visible;
952
+ }
953
+
954
+ getPatchDecision(agentId, toolCallId, requestedPath) {
955
+ const binding = this.requireBinding(agentId);
956
+ return binding.patchDecisions.get(`${String(toolCallId || '')}\n${String(requestedPath || '')}`) || '';
957
+ }
958
+
959
+ async decidePatch(agentId, toolCallId, requestedPath, decision) {
960
+ const binding = this.requireBinding(agentId);
961
+ if (binding.promptActive) throw new Error('Wait for the Agent to finish before deciding a patch');
962
+ const entry = binding.sessionState?.toolEntries.get(String(toolCallId || ''));
963
+ if (entry && binding.sessionState.isInternalEntry(entry)) throw new Error('ACP tool call not found');
964
+ if (!entry) throw new Error('ACP tool call not found');
965
+ const normalizedDecision = String(decision || '').trim().toLowerCase();
966
+ if (!['keep', 'revert'].includes(normalizedDecision)) throw new Error('ACP patch decision is invalid');
967
+ const key = `${String(toolCallId || '')}\n${String(requestedPath || '')}`;
968
+ if (binding.patchDecisions.has(key)) throw new Error('ACP patch file already has a decision');
969
+ let result = { action: 'kept', path: String(requestedPath || '') };
970
+ if (normalizedDecision === 'revert') {
971
+ result = await rejectPatch({ entry, root: binding.cwd, requestedPath });
972
+ }
973
+ binding.patchDecisions.set(key, result.action);
974
+ entry._meta = { ...(entry._meta || {}) };
975
+ entry._meta.farming_patch_decisions = {
976
+ ...(entry._meta.farming_patch_decisions || {}),
977
+ [String(requestedPath || '')]: result.action,
978
+ };
979
+ binding.sessionState?.touchEntry(entry);
980
+ binding.updatedAt = new Date().toISOString();
981
+ this.emitSession(binding);
982
+ return { ...result, toolCallId: String(toolCallId || '') };
983
+ }
984
+
985
+ killTerminal(agentId, terminalId) {
986
+ const binding = this.requireBinding(agentId);
987
+ this.clientTerminals.kill(binding, {
988
+ sessionId: binding.sessionId,
989
+ terminalId: String(terminalId || ''),
990
+ });
991
+ return { killed: true, terminalId: String(terminalId || '') };
992
+ }
993
+
994
+ inputTerminal(agentId, terminalId, input) {
995
+ const binding = this.requireBinding(agentId);
996
+ return this.clientTerminals.input(binding, {
997
+ sessionId: binding.sessionId,
998
+ terminalId,
999
+ input,
1000
+ });
1001
+ }
1002
+
1003
+ resizeTerminal(agentId, terminalId, cols, rows) {
1004
+ const binding = this.requireBinding(agentId);
1005
+ return this.clientTerminals.resize(binding, {
1006
+ sessionId: binding.sessionId,
1007
+ terminalId,
1008
+ cols,
1009
+ rows,
1010
+ });
1011
+ }
1012
+
1013
+ getSubagentTranscriptSession(agentId, sessionId, options = {}) {
1014
+ const binding = this.requireBinding(agentId);
1015
+ const id = String(sessionId || '');
1016
+ const state = binding.subagentStates.get(id);
1017
+ if (!state) return null;
1018
+ const parentTool = binding.sessionState?.entries.find(entry => (
1019
+ entry?.type === 'tool'
1020
+ && String(entry?._meta?.subagent_session_info?.session_id || '') === id
1021
+ ));
1022
+ const status = String(parentTool?.status || '').toLowerCase();
1023
+ const pendingPermission = [...binding.pendingPermissions.values()]
1024
+ .some(request => String(request?.sessionId || '') === id);
1025
+ const pendingElicitation = [...binding.pendingElicitations.values()]
1026
+ .some(request => String(request?.sessionId || '') === id);
1027
+ const active = ['pending', 'in_progress', 'in-progress', 'running'].includes(status);
1028
+ const failed = ['failed', 'error'].includes(status);
1029
+ const stateName = pendingPermission
1030
+ ? 'waiting-for-permission'
1031
+ : pendingElicitation
1032
+ ? 'waiting-for-input'
1033
+ : active
1034
+ ? 'working'
1035
+ : failed
1036
+ ? 'error'
1037
+ : 'idle';
1038
+ return {
1039
+ version: 2,
1040
+ protocol: 'acp',
1041
+ provider: binding.provider,
1042
+ sessionId: id,
1043
+ cwd: binding.cwd,
1044
+ title: state.title || '',
1045
+ updatedAt: binding.updatedAt,
1046
+ truncated: state.truncated === true,
1047
+ state: stateName,
1048
+ error: failed ? String(parentTool?.title || 'Subagent failed') : '',
1049
+ errorKind: failed ? 'agent' : '',
1050
+ stopReason: failed ? 'error' : active ? '' : 'end_turn',
1051
+ ...state.transcriptSlice(options),
1052
+ };
1053
+ }
1054
+
1055
+ requireBinding(agentId) {
1056
+ const binding = this.bindings.get(agentId);
1057
+ if (!binding) throw new Error('ACP Agent is not registered');
1058
+ return binding;
1059
+ }
1060
+
1061
+ emitRuntime(binding) {
1062
+ this.emit('agent-runtime', {
1063
+ agentId: binding.agentId,
1064
+ provider: binding.provider,
1065
+ sessionId: binding.sessionId,
1066
+ state: binding.state,
1067
+ error: binding.error,
1068
+ errorKind: binding.error ? acpErrorKind(binding.error) : '',
1069
+ stopReason: binding.stopReason,
1070
+ pendingPermission: binding.pendingPermissions.values().next().value || null,
1071
+ pendingPermissions: [...binding.pendingPermissions.values()],
1072
+ pendingElicitation: binding.pendingElicitations.values().next().value || null,
1073
+ pendingElicitations: [...binding.pendingElicitations.values()],
1074
+ activeElicitations: [...binding.activeElicitations.values()],
1075
+ updatedAt: binding.updatedAt,
1076
+ });
1077
+ }
1078
+
1079
+ emitSession(binding) {
1080
+ this.emit('session', {
1081
+ agentId: binding.agentId,
1082
+ updatedAt: binding.updatedAt,
1083
+ revision: binding.sessionState?.revision || 0,
1084
+ });
1085
+ }
1086
+
1087
+ handleExit(binding, error) {
1088
+ if (this.bindings.get(binding.agentId) !== binding) return;
1089
+ if (error) {
1090
+ binding.state = 'error';
1091
+ binding.error = acpErrorMessage(error);
1092
+ } else if (binding.state !== 'error') {
1093
+ binding.state = 'stopped';
1094
+ }
1095
+ for (const resolve of binding.permissionResolvers.values()) {
1096
+ resolve({ outcome: { outcome: 'cancelled' } });
1097
+ }
1098
+ for (const resolve of binding.elicitationResolvers.values()) resolve({ action: 'cancel' });
1099
+ binding.permissionResolvers.clear();
1100
+ binding.pendingPermissions.clear();
1101
+ binding.elicitationResolvers.clear();
1102
+ binding.pendingElicitations.clear();
1103
+ binding.activeElicitations.clear();
1104
+ binding.interactionOrigins.clear();
1105
+ binding.subagentStates.clear();
1106
+ this.clientTerminals.cleanupAgent(binding.agentId);
1107
+ this.emitRuntime(binding);
1108
+ }
1109
+
1110
+ unregisterAgent(agentId) {
1111
+ const binding = this.bindings.get(agentId);
1112
+ if (!binding) return;
1113
+ this.bindings.delete(agentId);
1114
+ for (const resolve of binding.permissionResolvers.values()) resolve({ outcome: { outcome: 'cancelled' } });
1115
+ for (const resolve of binding.elicitationResolvers.values()) resolve({ action: 'cancel' });
1116
+ binding.permissionResolvers.clear();
1117
+ binding.pendingPermissions.clear();
1118
+ binding.elicitationResolvers.clear();
1119
+ binding.pendingElicitations.clear();
1120
+ binding.activeElicitations.clear();
1121
+ binding.interactionOrigins.clear();
1122
+ binding.subagentStates.clear();
1123
+ this.clientTerminals.cleanupAgent(binding.agentId);
1124
+ try {
1125
+ binding.connection?.close();
1126
+ } catch {
1127
+ // The adapter process is terminated below even if transport cleanup raced its exit.
1128
+ }
1129
+ if (binding.child && !binding.child.killed) binding.child.kill('SIGTERM');
1130
+ }
1131
+
1132
+ dispose() {
1133
+ for (const agentId of [...this.bindings.keys()]) this.unregisterAgent(agentId);
1134
+ }
1135
+ }
1136
+
1137
+ module.exports = {
1138
+ AcpRuntime,
1139
+ ADAPTER_VERSIONS,
1140
+ acpErrorKind,
1141
+ autoPermissionResponse,
1142
+ codexAcpEnvironment,
1143
+ resolveAcpLaunch,
1144
+ };