farming-code 2.2.29 → 2.2.32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (385) hide show
  1. package/README.md +25 -66
  2. package/README.zh_cn.md +15 -66
  3. package/THIRD_PARTY_NOTICES.md +7 -0
  4. package/backend/acp/{client-services.js → client-services.cjs} +132 -134
  5. package/backend/acp/packaged-claude-acp.cjs +37 -0
  6. package/backend/acp/packaged-codex-acp.cjs +37 -0
  7. package/backend/acp/patch-decisions.cjs +182 -0
  8. package/backend/acp/permission-security.cjs +131 -0
  9. package/backend/{acp-checkpoint-store.js → acp-checkpoint-store.cjs} +90 -59
  10. package/backend/{acp-runtime.js → acp-runtime.cjs} +1219 -1125
  11. package/backend/acp-session-state.cjs +622 -0
  12. package/backend/acp-transcript.cjs +525 -0
  13. package/backend/agent-env.cjs +257 -0
  14. package/backend/agent-extension-discovery.cjs +452 -0
  15. package/backend/agent-extension-inventory.cjs +116 -0
  16. package/backend/agent-json-stream.cjs +283 -0
  17. package/backend/agent-lifecycle-journal.cjs +185 -0
  18. package/backend/{agent-manager.js → agent-manager.cjs} +3116 -4275
  19. package/backend/agent-order-transaction.cjs +85 -0
  20. package/backend/agent-order.cjs +159 -0
  21. package/backend/agent-provider-session.cjs +98 -0
  22. package/backend/agent-runtime-binding.cjs +179 -0
  23. package/backend/agent-session-history.cjs +1022 -0
  24. package/backend/agent-session-inventory.cjs +291 -0
  25. package/backend/async-cache.cjs +104 -0
  26. package/backend/atomic-json-store.cjs +67 -0
  27. package/backend/auth.cjs +261 -0
  28. package/backend/authoritative-inventory-cache.cjs +383 -0
  29. package/backend/business-health.cjs +68 -0
  30. package/backend/chat-runtime.cjs +50 -0
  31. package/backend/claude-settings.cjs +132 -0
  32. package/backend/cli-agents.cjs +435 -0
  33. package/backend/codex-context-window.cjs +196 -0
  34. package/backend/codex-models.cjs +228 -0
  35. package/backend/codex-session-archive.cjs +82 -0
  36. package/backend/codex-session-history.cjs +590 -0
  37. package/backend/{codex-terminal-profile.js → codex-terminal-profile.cjs} +149 -155
  38. package/backend/codex-transcript-sanitizer.cjs +130 -0
  39. package/backend/codex-transcript.cjs +2107 -0
  40. package/backend/command-runner-child.cjs +105 -0
  41. package/backend/config-manager.cjs +1263 -0
  42. package/backend/control-api.cjs +439 -0
  43. package/backend/data/LICENSE.cua +21 -0
  44. package/backend/executable-discovery.cjs +204 -0
  45. package/backend/farming-agent-bootstrap.cjs +81 -0
  46. package/backend/farming-agent-bootstrap.zh_cn.md +6 -2
  47. package/backend/{farming-app-cli.js → farming-app-cli.cjs} +441 -493
  48. package/backend/farming-cli.cjs +414 -0
  49. package/backend/farming-net-pass.cjs +319 -0
  50. package/backend/farming-net-registry.cjs +123 -0
  51. package/backend/farming-net-server.cjs +309 -0
  52. package/backend/farming-session-store.cjs +651 -0
  53. package/backend/git-worktree-info.cjs +181 -0
  54. package/backend/haiku-token.cjs +828 -0
  55. package/backend/index-html.cjs +96 -0
  56. package/backend/input-parts.cjs +68 -0
  57. package/backend/input-routing.cjs +40 -0
  58. package/backend/{local-session-engine.js → local-session-engine.cjs} +276 -282
  59. package/backend/main-agent-skills.cjs +309 -0
  60. package/backend/main-page-session.cjs +132 -0
  61. package/backend/native-pty-controller-generation.cjs +139 -0
  62. package/backend/{native-pty-host-client.js → native-pty-host-client.cjs} +338 -346
  63. package/backend/native-pty-host-identity.cjs +125 -0
  64. package/backend/native-pty-host-path.cjs +68 -0
  65. package/backend/{native-pty-host.js → native-pty-host.cjs} +480 -569
  66. package/backend/native-session-engine.cjs +296 -0
  67. package/backend/network.cjs +57 -0
  68. package/backend/npm-update-helper.cjs +399 -0
  69. package/backend/packaged-node-pty.cjs +120 -0
  70. package/backend/preview-session-manager.cjs +89 -0
  71. package/backend/provider-adapters.cjs +492 -0
  72. package/backend/provider-home-configuration.cjs +211 -0
  73. package/backend/provider-session-id.cjs +79 -0
  74. package/backend/provider-session-service.cjs +333 -0
  75. package/backend/qr-share-tickets.cjs +119 -0
  76. package/backend/resource-broadcast-protocol.cjs +50 -0
  77. package/backend/review-diff-router.cjs +202 -0
  78. package/backend/review-diff-service.cjs +1267 -0
  79. package/backend/review-session-router.cjs +93 -0
  80. package/backend/review-session-service.cjs +470 -0
  81. package/backend/review-session-store.cjs +161 -0
  82. package/backend/review-state-router.cjs +132 -0
  83. package/backend/review-state-store.cjs +299 -0
  84. package/backend/run-history-store.cjs +97 -0
  85. package/backend/runtime-dependency-manager.cjs +893 -0
  86. package/backend/runtime-executable-invocation.cjs +37 -0
  87. package/backend/runtime-observation.cjs +96 -0
  88. package/backend/server-process-identity.cjs +70 -0
  89. package/backend/server.cjs +3356 -0
  90. package/backend/session-engine-bridge.cjs +144 -0
  91. package/backend/session-engine-router.cjs +105 -0
  92. package/backend/session-engine.cjs +64 -0
  93. package/backend/session-stream-protocol.cjs +181 -0
  94. package/backend/shell-busy-integration.cjs +442 -0
  95. package/backend/slash-command-discovery.cjs +423 -0
  96. package/backend/storage-layout.cjs +263 -0
  97. package/backend/system-monitor.cjs +96 -0
  98. package/backend/terminal-attach-checkpoint.cjs +73 -0
  99. package/backend/terminal-exit-quiescence.cjs +57 -0
  100. package/backend/{terminal-reducer-flow-control.js → terminal-reducer-flow-control.cjs} +45 -22
  101. package/backend/terminal-runtime-cleanup.cjs +99 -0
  102. package/backend/{terminal-screen-state.js → terminal-screen-state.cjs} +100 -108
  103. package/backend/{terminal-screen-worker-pool.js → terminal-screen-worker-pool.cjs} +89 -79
  104. package/backend/{terminal-screen-worker-thread.js → terminal-screen-worker-thread.cjs} +64 -65
  105. package/backend/terminal-screen-worker.cjs +352 -0
  106. package/backend/terminal-state-serialization.cjs +147 -0
  107. package/backend/{terminal-status.js → terminal-status.cjs} +137 -187
  108. package/backend/theme-manager.cjs +185 -0
  109. package/backend/update-service.cjs +675 -0
  110. package/backend/usage-forecast.cjs +95 -0
  111. package/backend/{usage-history-client.js → usage-history-client.cjs} +144 -94
  112. package/backend/usage-history-smoke.cjs +85 -0
  113. package/backend/usage-history-worker.cjs +60 -0
  114. package/backend/{usage-monitor.js → usage-monitor.cjs} +363 -390
  115. package/backend/workspace-directory.cjs +239 -0
  116. package/backend/workspace-discovery.cjs +271 -0
  117. package/backend/{workspace-file-router.js → workspace-file-router.cjs} +213 -282
  118. package/backend/{workspace-file-service.js → workspace-file-service.cjs} +1139 -1318
  119. package/backend/workspace-root-registry.cjs +192 -0
  120. package/bin/farming +1 -1
  121. package/config/farming.deploy.env.example +1 -1
  122. package/config/farming.install.env.example +1 -1
  123. package/dist/acp/codex-acp-1.1.4.mjs +405 -8
  124. package/dist/assets/App-BCkdpWB6.css +1 -0
  125. package/dist/assets/App-CRjKpaGp.js +697 -0
  126. package/dist/assets/CodeSelect-Ci8sKZPU.js +1 -0
  127. package/dist/assets/CodeSelect-T2eNzrOc.css +1 -0
  128. package/dist/assets/{FileEditorMarkdownPreview-Cs9cLv8a.js → FileEditorMarkdownPreview-V06QHlAg.js} +55 -55
  129. package/dist/assets/FileEditorPane-BX-Wjg9U.js +2 -0
  130. package/dist/assets/IconGlyphs-YhV7tqd4.js +1 -0
  131. package/dist/assets/ProjectFilesSection-BDT2_r0o.js +12 -0
  132. package/dist/assets/ReviewPage-BqAs5sG1.js +3 -0
  133. package/dist/assets/code-dark-B7eM_9G9.css +1 -0
  134. package/dist/assets/{cssMode-Bdg5RpcJ.js → cssMode-ADdAlxTO.js} +1 -1
  135. package/dist/assets/{editor.api2-D74ufkbr.js → editor.api2-BFLAr0YF.js} +162 -162
  136. package/dist/assets/file-icons-BpeqWkIB.js +1 -0
  137. package/dist/assets/{freemarker2-DfMDJfnE.js → freemarker2-C90q5jrp.js} +1 -1
  138. package/dist/assets/{handlebars-BEdPtzMT.js → handlebars-BwUQcEDq.js} +1 -1
  139. package/dist/assets/{html-B1fPb1QD.js → html-1s3uPKfh.js} +1 -1
  140. package/dist/assets/{htmlMode-CY2ZjHxH.js → htmlMode-xtKD99de.js} +1 -1
  141. package/dist/assets/{index-CQNaZc3l.js → index-C8MchCem.js} +3 -3
  142. package/dist/assets/{javascript-BV2OsWA0.js → javascript-Ph7ijyny.js} +1 -1
  143. package/dist/assets/{jsonMode-zd07aeuX.js → jsonMode-D-jGAnFH.js} +1 -1
  144. package/dist/assets/linkComputer-CbGyI_qx.js +1 -0
  145. package/dist/assets/{liquid-BParI24X.js → liquid-0IamE_cI.js} +1 -1
  146. package/dist/assets/{lspLanguageFeatures-BzredkoV.js → lspLanguageFeatures-BxnQunWF.js} +1 -1
  147. package/dist/assets/main-CLKXa6hf.css +1 -0
  148. package/dist/assets/{mdx-vGzUJN98.js → mdx-DdNmfivn.js} +1 -1
  149. package/dist/assets/{monaco.contribution-BmaeBQEc.js → monaco.contribution-B43jsUs1.js} +2 -2
  150. package/dist/assets/{python-CUwdLgsI.js → python-IAt4X5TS.js} +1 -1
  151. package/dist/assets/{razor-BScebUxU.js → razor-DRkFYW0S.js} +1 -1
  152. package/dist/assets/review-C8-oPKXk.css +1 -0
  153. package/dist/assets/{tsMode-DzUAibXK.js → tsMode-_d_4U2gf.js} +1 -1
  154. package/dist/assets/{typescript-D9EOeM7N.js → typescript-C7zcMq9l.js} +1 -1
  155. package/dist/assets/{workers-DAiomYZM.js → workers-D0nOyJzf.js} +1 -1
  156. package/dist/assets/workspace-editor-model-DND_Y-Bj.js +1 -0
  157. package/dist/assets/workspace-editor-monaco-BPWhgcIB.js +4 -0
  158. package/dist/assets/workspace-editor-monaco-N641qPjX.js +1 -0
  159. package/dist/assets/workspace-view-state-k1bTlJ_Z.js +13 -0
  160. package/dist/assets/{xml-wWl1dhkQ.js → xml-BR-HvxT8.js} +1 -1
  161. package/dist/assets/{yaml-Dc2WDaOZ.js → yaml-YtFQwh4q.js} +1 -1
  162. package/dist/index.html +2 -1
  163. package/extensions/browser/backend/agent-browser-runtime.cjs +1040 -0
  164. package/extensions/browser/backend/agent-capability.cjs +85 -0
  165. package/extensions/browser/backend/browser-mcp-server.cjs +655 -0
  166. package/extensions/browser/backend/browser-resource-manager.cjs +1486 -0
  167. package/extensions/browser/backend/browser-resource-store.cjs +228 -0
  168. package/extensions/browser/backend/browser-router.cjs +203 -0
  169. package/extensions/browser/backend/executable-discovery.cjs +248 -0
  170. package/extensions/browser/backend/farming-browser-client.cjs +116 -0
  171. package/extensions/browser/backend/index.cjs +32 -0
  172. package/extensions/browser/backend/managed-chromium-installer.cjs +833 -0
  173. package/extensions/browser/bin/farming-browser +16 -7
  174. package/extensions/computer/backend/agent-capability.cjs +72 -0
  175. package/extensions/computer/backend/computer-constants.cjs +59 -0
  176. package/extensions/computer/backend/computer-mcp-server.cjs +182 -0
  177. package/extensions/computer/backend/computer-resource-manager.cjs +1219 -0
  178. package/extensions/computer/backend/computer-resource-store.cjs +190 -0
  179. package/extensions/computer/backend/computer-router.cjs +257 -0
  180. package/extensions/computer/backend/cua-tools.json +3067 -0
  181. package/extensions/computer/backend/farming-computer-client.cjs +84 -0
  182. package/extensions/computer/backend/index.cjs +35 -0
  183. package/extensions/computer/backend/isolated-browser-provider.cjs +157 -0
  184. package/extensions/computer/bin/farming-computer +131 -0
  185. package/frontend/farming-net/app.js +255 -262
  186. package/frontend/ghostty-loader.js +24 -20
  187. package/frontend/reading-anchor.js +210 -193
  188. package/frontend/runtime-paths.js +24 -26
  189. package/frontend/session-bridge.js +90 -111
  190. package/frontend/session-modal-bridge.js +184 -302
  191. package/frontend/skin-bridge.js +20 -23
  192. package/frontend/skins/crt/app.js +7686 -7901
  193. package/frontend/skins/crt/effects.js +7 -6
  194. package/frontend/skins/crt/index.html +6 -6
  195. package/frontend/skins/crt/styles/markdown.css +4 -4
  196. package/frontend/terminal-bridge.js +174 -181
  197. package/frontend/terminal-replay.js +320 -345
  198. package/frontend/theme-loader.js +55 -71
  199. package/index.html +1 -0
  200. package/package.json +64 -37
  201. package/shared/backend-connection-status.js +38 -69
  202. package/shared/browser-protocol.js +235 -110
  203. package/shared/websocket-liveness.js +40 -0
  204. package/backend/acp/packaged-claude-acp.js +0 -13
  205. package/backend/acp/packaged-codex-acp.js +0 -13
  206. package/backend/acp/patch-decisions.js +0 -153
  207. package/backend/acp/permission-security.js +0 -108
  208. package/backend/acp-session-state.js +0 -644
  209. package/backend/acp-transcript.js +0 -512
  210. package/backend/agent-env.js +0 -250
  211. package/backend/agent-json-stream.js +0 -268
  212. package/backend/agent-lifecycle-journal.js +0 -133
  213. package/backend/agent-memory-report.js +0 -583
  214. package/backend/agent-order.js +0 -166
  215. package/backend/agent-provider-session.js +0 -76
  216. package/backend/agent-runtime-binding.js +0 -174
  217. package/backend/agent-session-history.js +0 -953
  218. package/backend/async-cache.js +0 -94
  219. package/backend/atomic-json-store.js +0 -37
  220. package/backend/auth.js +0 -251
  221. package/backend/bundle-update-helper.js +0 -113
  222. package/backend/chat-runtime.js +0 -28
  223. package/backend/claude-settings.js +0 -121
  224. package/backend/cli-agents.js +0 -486
  225. package/backend/codex-context-window.js +0 -189
  226. package/backend/codex-models.js +0 -226
  227. package/backend/codex-rollout-follower.js +0 -326
  228. package/backend/codex-session-archive.js +0 -65
  229. package/backend/codex-session-history.js +0 -647
  230. package/backend/codex-transcript-sanitizer.js +0 -115
  231. package/backend/codex-transcript.js +0 -2564
  232. package/backend/command-runner-child.js +0 -67
  233. package/backend/config-manager.js +0 -1133
  234. package/backend/control-api.js +0 -455
  235. package/backend/executable-discovery.js +0 -216
  236. package/backend/farming-agent-bootstrap.js +0 -49
  237. package/backend/farming-cli.js +0 -457
  238. package/backend/farming-net-pass.js +0 -285
  239. package/backend/farming-net-registry.js +0 -112
  240. package/backend/farming-net-server.js +0 -298
  241. package/backend/farming-session-store.js +0 -677
  242. package/backend/git-worktree-info.js +0 -181
  243. package/backend/haiku-token.js +0 -772
  244. package/backend/index-html.js +0 -62
  245. package/backend/input-parts.js +0 -54
  246. package/backend/input-routing.js +0 -24
  247. package/backend/json-cli-runtime.js +0 -275
  248. package/backend/main-agent-skills.js +0 -305
  249. package/backend/main-page-session.js +0 -122
  250. package/backend/native-pty-controller-generation.js +0 -106
  251. package/backend/native-pty-host-identity.js +0 -86
  252. package/backend/native-pty-host-path.js +0 -42
  253. package/backend/native-session-engine.js +0 -268
  254. package/backend/network.js +0 -23
  255. package/backend/npm-update-helper.js +0 -374
  256. package/backend/packaged-node-pty.js +0 -99
  257. package/backend/preview-session-manager.js +0 -68
  258. package/backend/provider-adapters.js +0 -399
  259. package/backend/provider-session-id.js +0 -47
  260. package/backend/provider-session-service.js +0 -345
  261. package/backend/qr-share-tickets.js +0 -100
  262. package/backend/review-diff-router.js +0 -180
  263. package/backend/review-diff-service.js +0 -1270
  264. package/backend/review-session-router.js +0 -74
  265. package/backend/review-session-service.js +0 -446
  266. package/backend/review-session-store.js +0 -151
  267. package/backend/review-state-router.js +0 -116
  268. package/backend/review-state-store.js +0 -286
  269. package/backend/run-history-store.js +0 -67
  270. package/backend/runtime-dependency-manager.js +0 -759
  271. package/backend/runtime-executable-invocation.js +0 -20
  272. package/backend/runtime-observation.js +0 -81
  273. package/backend/server-process-identity.js +0 -47
  274. package/backend/server.js +0 -3267
  275. package/backend/session-engine-bridge.js +0 -129
  276. package/backend/session-engine-router.js +0 -66
  277. package/backend/session-engine.js +0 -39
  278. package/backend/session-stream-protocol.js +0 -185
  279. package/backend/shell-busy-integration.js +0 -472
  280. package/backend/slash-command-discovery.js +0 -433
  281. package/backend/storage-layout.js +0 -190
  282. package/backend/system-monitor.js +0 -77
  283. package/backend/terminal-attach-checkpoint.js +0 -74
  284. package/backend/terminal-exit-quiescence.js +0 -39
  285. package/backend/terminal-runtime-cleanup.js +0 -80
  286. package/backend/terminal-screen-worker.js +0 -337
  287. package/backend/terminal-state-serialization.js +0 -127
  288. package/backend/theme-manager.js +0 -163
  289. package/backend/update-service.js +0 -1545
  290. package/backend/usage-forecast.js +0 -86
  291. package/backend/usage-history-scanner.ts +0 -2718
  292. package/backend/usage-history-smoke.js +0 -64
  293. package/backend/usage-history-worker.js +0 -34
  294. package/backend/workspace-directory.js +0 -232
  295. package/backend/workspace-discovery.js +0 -311
  296. package/backend/workspace-root-registry.js +0 -166
  297. package/dist/assets/App-BKNUILuL.css +0 -1
  298. package/dist/assets/App-Ex2oVVXO.js +0 -693
  299. package/dist/assets/FileEditorPane-CNtuGjQ3.js +0 -2
  300. package/dist/assets/IconGlyphs-BClgag6E.js +0 -1
  301. package/dist/assets/ProjectFilesSection-Cf2BEcAN.js +0 -12
  302. package/dist/assets/ReviewPage-BRuxFaH5.js +0 -3
  303. package/dist/assets/code-dark-BEZTQwYZ.css +0 -1
  304. package/dist/assets/file-icons-BOlcJ0ZF.js +0 -1
  305. package/dist/assets/main-DK9Sy7-c.css +0 -1
  306. package/dist/assets/review-BcnwQtJv.css +0 -1
  307. package/dist/assets/workspace-editor-model-CSGCl21x.js +0 -1
  308. package/dist/assets/workspace-editor-monaco-CJNwbpCy.js +0 -4
  309. package/dist/assets/workspace-editor-monaco-CO9w8YV0.js +0 -1
  310. package/dist/assets/workspace-view-state-BjKPYp0E.js +0 -7
  311. package/extensions/browser/backend/agent-browser-runtime.js +0 -1041
  312. package/extensions/browser/backend/agent-capability.js +0 -55
  313. package/extensions/browser/backend/browser-mcp-server.js +0 -621
  314. package/extensions/browser/backend/browser-resource-manager.js +0 -1309
  315. package/extensions/browser/backend/browser-resource-store.js +0 -190
  316. package/extensions/browser/backend/browser-router.js +0 -133
  317. package/extensions/browser/backend/executable-discovery.js +0 -224
  318. package/extensions/browser/backend/farming-browser-client.js +0 -83
  319. package/extensions/browser/backend/index.js +0 -7
  320. package/extensions/browser/frontend/BrowserSidebarPortals.tsx +0 -360
  321. package/extensions/browser/frontend/BrowserViewer.tsx +0 -500
  322. package/extensions/browser/frontend/browser-resource-state.ts +0 -60
  323. package/extensions/browser/frontend/browser.css +0 -567
  324. package/extensions/browser/frontend/types.ts +0 -31
  325. package/extensions/browser/frontend/useBrowserResources.ts +0 -158
  326. package/frontend/vendor/ghostty-web/index.d.ts +0 -1842
  327. package/shared/backend-connection-status.d.ts +0 -37
  328. package/shared/browser-protocol.d.ts +0 -5
  329. /package/dist/assets/{api-w2x5QfxB.js → api-ByCV9MEr.js} +0 -0
  330. /package/dist/assets/{core-vPkV8P_m.js → core-Czwdx12k.js} +0 -0
  331. /package/dist/assets/{fsharp-DNZNZyEE.js → fsharp-BjPpkoJe.js} +0 -0
  332. /package/dist/assets/{go-_kPJjNc_.js → go-BkIDmSUP.js} +0 -0
  333. /package/dist/assets/{graphql-4HCNVt5g.js → graphql-CCJTpqi1.js} +0 -0
  334. /package/dist/assets/{hcl-C87ymY0o.js → hcl-s4z8lUFg.js} +0 -0
  335. /package/dist/assets/{ini-BYrrGNTc.js → ini-CyUW1I7Z.js} +0 -0
  336. /package/dist/assets/{java-B211rJbk.js → java-CFCcGRiM.js} +0 -0
  337. /package/dist/assets/{julia-DVPsRtBm.js → julia-DQyH_EYb.js} +0 -0
  338. /package/dist/assets/{kotlin-BYyNerjp.js → kotlin-B8hA5O51.js} +0 -0
  339. /package/dist/assets/{less-EC9UblGE.js → less-DaVdaas0.js} +0 -0
  340. /package/dist/assets/{lexon-B1yuUdqW.js → lexon-DLBhPd3y.js} +0 -0
  341. /package/dist/assets/{lua-Bo6GFzxT.js → lua-gB1c_cif.js} +0 -0
  342. /package/dist/assets/{m3-C-Y9cBVI.js → m3-C2T3z3gB.js} +0 -0
  343. /package/dist/assets/{markdown-BHu-wLbe.js → markdown-CKq-Euj1.js} +0 -0
  344. /package/dist/assets/{mips-Dkds26rX.js → mips-Bcs5qIDX.js} +0 -0
  345. /package/dist/assets/{msdax-PU4g-8_g.js → msdax-CBP48FHF.js} +0 -0
  346. /package/dist/assets/{mysql-fPpuGpS7.js → mysql-c7tiPcFj.js} +0 -0
  347. /package/dist/assets/{objective-c-Wy1e5JUf.js → objective-c-D4BZkeEV.js} +0 -0
  348. /package/dist/assets/{pascal-BIXwvrqU.js → pascal-CJDZg0MN.js} +0 -0
  349. /package/dist/assets/{pascaligo-DwR0AtL_.js → pascaligo-Bygi8Ynl.js} +0 -0
  350. /package/dist/assets/{perl-CGHAl8my.js → perl-aS8bkf7E.js} +0 -0
  351. /package/dist/assets/{pgsql-B91M73E2.js → pgsql-CzNAYFYl.js} +0 -0
  352. /package/dist/assets/{php-C70ttrCx.js → php-DOtoEFTT.js} +0 -0
  353. /package/dist/assets/{pla-BNrXaRh0.js → pla-DugRnao1.js} +0 -0
  354. /package/dist/assets/{postiats-Dv2IAZR9.js → postiats-DsXwusKh.js} +0 -0
  355. /package/dist/assets/{powerquery-CkY5Wq7J.js → powerquery-DL3o72oD.js} +0 -0
  356. /package/dist/assets/{powershell-uGIYLU3C.js → powershell-C1qHekys.js} +0 -0
  357. /package/dist/assets/{protobuf-CavfmJu-.js → protobuf-DHgsc-cH.js} +0 -0
  358. /package/dist/assets/{pug-Drqnyitx.js → pug-FZXlE_Iu.js} +0 -0
  359. /package/dist/assets/{qrcode-7uGsTEmi.js → qrcode-DJDTdjL7.js} +0 -0
  360. /package/dist/assets/{qsharp-C7yF-Dti.js → qsharp-bLyhI3DT.js} +0 -0
  361. /package/dist/assets/{r-HXLGP0_-.js → r-DjFGXbAa.js} +0 -0
  362. /package/dist/assets/{redis-C4y6mf2p.js → redis-DSoZ_V6u.js} +0 -0
  363. /package/dist/assets/{redshift-DdQrFPrv.js → redshift-CzkdCsrg.js} +0 -0
  364. /package/dist/assets/{restructuredtext-CBrf2PNj.js → restructuredtext-B_XPrGCQ.js} +0 -0
  365. /package/dist/assets/{ruby-CVtMoX2B.js → ruby-Cry2RneJ.js} +0 -0
  366. /package/dist/assets/{rust-Ckq4sbMP.js → rust-CWzdbFq1.js} +0 -0
  367. /package/dist/assets/{sb-er66OpE3.js → sb-DmofU59h.js} +0 -0
  368. /package/dist/assets/{scala-BdWZz-hH.js → scala-BUC2l-vN.js} +0 -0
  369. /package/dist/assets/{scheme-kTV_s2kP.js → scheme-CNIB_72f.js} +0 -0
  370. /package/dist/assets/{scss-Bymy2VJQ.js → scss-DcL4-NbX.js} +0 -0
  371. /package/dist/assets/{shell-bwEP1LQ1.js → shell-CgAGvSSA.js} +0 -0
  372. /package/dist/assets/{solidity-DCddeBgL.js → solidity-DSkt6O_P.js} +0 -0
  373. /package/dist/assets/{sophia-BMN3AQig.js → sophia--5ytcjDN.js} +0 -0
  374. /package/dist/assets/{sparql-CSXOLjzi.js → sparql-C1Srr7cD.js} +0 -0
  375. /package/dist/assets/{sql-COg8ewmK.js → sql-2k_O5fj7.js} +0 -0
  376. /package/dist/assets/{st-CIaZtBS_.js → st-DCCCPqfz.js} +0 -0
  377. /package/dist/assets/{swift-DXiOhUHu.js → swift-Dd8mAfOt.js} +0 -0
  378. /package/dist/assets/{systemverilog-BTDE6Dr2.js → systemverilog-B0-aZYXu.js} +0 -0
  379. /package/dist/assets/{tcl-B0zy9NvV.js → tcl-CZAZwPVX.js} +0 -0
  380. /package/dist/assets/{twig-D594l4Z1.js → twig-D-Ag0tdS.js} +0 -0
  381. /package/dist/assets/{typespec-XKVjaPqo.js → typespec-B7kTFp0Z.js} +0 -0
  382. /package/dist/assets/{useWorkspaceMenuKeyboard-Bpy_s69Z.js → useWorkspaceMenuKeyboard-uYZpDxhU.js} +0 -0
  383. /package/dist/assets/{vb-B2wYoKRV.js → vb-20w1JPum.js} +0 -0
  384. /package/dist/assets/{wgsl-D0Yf2LbK.js → wgsl-CLJfqFt0.js} +0 -0
  385. /package/dist/assets/{workspace-working-copy-C3FSR3N6.js → workspace-working-copy-Cfz07cOd.js} +0 -0
@@ -1,172 +1,246 @@
1
1
  #!/usr/bin/env node
2
-
3
- const crypto = require('crypto');
4
- const fs = require('fs');
5
- const net = require('net');
6
- const os = require('os');
7
- const path = require('path');
8
-
9
- const TerminalScreenWorkerPool = require('./terminal-screen-worker-pool');
2
+ // Generated by scripts/build-backend-runtime.ts. Do not edit.
3
+ "use strict";
4
+ var __create = Object.create;
5
+ var __defProp = Object.defineProperty;
6
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
7
+ var __getOwnPropNames = Object.getOwnPropertyNames;
8
+ var __getProtoOf = Object.getPrototypeOf;
9
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
10
+ var __export = (target, all) => {
11
+ for (var name in all)
12
+ __defProp(target, name, { get: all[name], enumerable: true });
13
+ };
14
+ var __copyProps = (to, from, except, desc) => {
15
+ if (from && typeof from === "object" || typeof from === "function") {
16
+ for (let key of __getOwnPropNames(from))
17
+ if (!__hasOwnProp.call(to, key) && key !== except)
18
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
19
+ }
20
+ return to;
21
+ };
22
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
23
+ // If the importer is in node compatibility mode or this is not an ESM
24
+ // file that has been converted to a CommonJS file using a Babel-
25
+ // compatible transform (i.e. "__esModule" has not been set), then set
26
+ // "default" to the CommonJS "module.exports" for node compatibility.
27
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
28
+ mod
29
+ ));
30
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
31
+ var native_pty_host_exports = {};
32
+ __export(native_pty_host_exports, {
33
+ NativePtyHost: () => NativePtyHost,
34
+ startNativePtyHostProcess: () => startNativePtyHostProcess
35
+ });
36
+ module.exports = __toCommonJS(native_pty_host_exports);
37
+ var crypto = __toESM(require("crypto"));
38
+ var fs = __toESM(require("fs"));
39
+ var net = __toESM(require("net"));
40
+ var os = __toESM(require("os"));
41
+ var path = __toESM(require("path"));
42
+ var import_terminal_screen_worker_pool = require("./terminal-screen-worker-pool.cjs");
43
+ var nativePtyHostPathModule = __toESM(require("./native-pty-host-path.cjs"));
44
+ var inputPartsModule = __toESM(require("./input-parts.cjs"));
45
+ var agentEnvModule = __toESM(require("./agent-env.cjs"));
46
+ var shellBusyIntegrationModule = __toESM(require("./shell-busy-integration.cjs"));
47
+ var localSessionEngineModule = __toESM(require("./local-session-engine.cjs"));
48
+ var terminalStatusModule = __toESM(require("./terminal-status.cjs"));
49
+ var terminalRuntimeCleanupModule = __toESM(require("./terminal-runtime-cleanup.cjs"));
50
+ var nativePtyHostIdentityModule = __toESM(require("./native-pty-host-identity.cjs"));
51
+ var runtimeGenerationModule = __toESM(require("./native-pty-controller-generation.cjs"));
52
+ var reducerFlowControlModule = __toESM(require("./terminal-reducer-flow-control.cjs"));
53
+ var terminalStateSerializationModule = __toESM(require("./terminal-state-serialization.cjs"));
54
+ var terminalAttachCheckpointModule = __toESM(require("./terminal-attach-checkpoint.cjs"));
55
+ var terminalExitQuiescenceModule = __toESM(require("./terminal-exit-quiescence.cjs"));
10
56
  const {
11
57
  nativePtyHostPrivateSocketPath,
12
- nativePtyHostSocketPath,
13
- } = require('./native-pty-host-path');
14
- const { terminalInputToPtyString } = require('./input-parts');
15
- const { normalizeInteractiveTerminalEnv } = require('./agent-env');
58
+ nativePtyHostSocketPath
59
+ } = nativePtyHostPathModule;
60
+ const { terminalInputToPtyString } = inputPartsModule;
61
+ const { normalizeInteractiveTerminalEnv } = agentEnvModule;
16
62
  const {
17
63
  cleanupShellBusyIntegration,
18
- parseShellBusyMarkers,
19
- } = require('./shell-busy-integration');
64
+ parseShellBusyMarkers
65
+ } = shellBusyIntegrationModule;
20
66
  const {
21
67
  extractLatestTerminalTitle,
22
68
  normalizeShellSessionOptions,
23
- createPtyProcess,
24
- } = require('./local-session-engine');
25
- const { deriveTerminalStatus } = require('./terminal-status');
26
- const { probeUnixSocket } = require('./terminal-runtime-cleanup');
27
- const { nativePtyHostRuntimeIdentity } = require('./native-pty-host-identity');
69
+ createPtyProcess
70
+ } = localSessionEngineModule;
71
+ const { deriveTerminalStatus } = terminalStatusModule;
72
+ const { probeUnixSocket } = terminalRuntimeCleanupModule;
73
+ const { nativePtyHostRuntimeIdentity } = nativePtyHostIdentityModule;
28
74
  const {
29
75
  allocateNativePtyRuntimeGeneration,
30
- formatNativePtyRuntimeEpoch,
31
- } = require('./native-pty-controller-generation');
76
+ formatNativePtyRuntimeEpoch
77
+ } = runtimeGenerationModule;
32
78
  const {
33
79
  acknowledgeTerminalReducerData,
34
80
  createTerminalReducerFlowControl,
35
81
  ensureTerminalReducerFlowControl,
36
82
  enqueueTerminalReducerData,
37
83
  resetTerminalReducerFlowControl,
38
- setTerminalExternalFlowControlBlocked,
39
- } = require('./terminal-reducer-flow-control');
84
+ setTerminalExternalFlowControlBlocked
85
+ } = reducerFlowControlModule;
40
86
  const {
41
87
  normalizeTerminalStateEntry,
42
- serializeTerminalState,
43
- } = require('./terminal-state-serialization');
44
- const {
45
- captureTerminalAttachCheckpoint,
46
- } = require('./terminal-attach-checkpoint');
88
+ serializeTerminalState
89
+ } = terminalStateSerializationModule;
90
+ const { captureTerminalAttachCheckpoint } = terminalAttachCheckpointModule;
47
91
  const {
48
92
  acceptTerminalExitData,
49
- waitForTerminalExitDataQuiescence,
50
- } = require('./terminal-exit-quiescence');
51
-
52
- const OUTPUT_LIMIT = 10000;
93
+ waitForTerminalExitDataQuiescence
94
+ } = terminalExitQuiescenceModule;
95
+ const OUTPUT_LIMIT = 1e4;
53
96
  const DEFAULT_COLS = 80;
54
97
  const DEFAULT_ROWS = 30;
55
- const OWNER_CHECK_INTERVAL_MS = 1000;
98
+ const OWNER_CHECK_INTERVAL_MS = 1e3;
56
99
  const DISPOSE_PTY_GRACE_MS = 800;
57
- const DEFAULT_IDLE_EXIT_MS = 60000;
100
+ const DEFAULT_IDLE_EXIT_MS = 6e4;
58
101
  const DEFAULT_CLIENT_MAX_BUFFERED_BYTES = 16 * 1024 * 1024;
59
102
  const DEFAULT_CLIENT_MAX_REQUEST_BYTES = 16 * 1024 * 1024;
60
103
  const HOST_RUNTIME_IDENTITY = nativePtyHostRuntimeIdentity();
61
- const CONTROLLER_MUTATION_ADMISSION = Symbol('controllerMutationAdmission');
62
- const TERMINAL_HISTORY_RESTORED_MESSAGE = '\r\n\x1b[7m History restored \x1b[0m\r\n';
63
-
104
+ const CONTROLLER_MUTATION_ADMISSION = /* @__PURE__ */ Symbol("controllerMutationAdmission");
105
+ const TERMINAL_HISTORY_RESTORED_MESSAGE = "\r\n\x1B[7m History restored \x1B[0m\r\n";
106
+ function errorMessage(error) {
107
+ if (error instanceof Error) return error.message;
108
+ if (typeof error === "object" && error !== null && "message" in error) {
109
+ return String(error.message || "");
110
+ }
111
+ return "";
112
+ }
113
+ function errorCode(error) {
114
+ return typeof error === "object" && error !== null && "code" in error ? String(error.code || "") : "";
115
+ }
116
+ function isControllerAdmission(value) {
117
+ return CONTROLLER_MUTATION_ADMISSION in value && value[CONTROLLER_MUTATION_ADMISSION] === true;
118
+ }
64
119
  function trimOutput(output) {
65
- const text = typeof output === 'string' ? output : '';
120
+ const text = typeof output === "string" ? output : "";
66
121
  return text.length > OUTPUT_LIMIT ? text.slice(-OUTPUT_LIMIT) : text;
67
122
  }
68
-
69
123
  function normalizePositiveInteger(value, fallback, min, max) {
70
124
  const parsed = Math.floor(Number(value));
71
125
  if (!Number.isFinite(parsed)) return fallback;
72
126
  return Math.max(min, Math.min(max, parsed));
73
127
  }
74
-
75
128
  function sanitizeAgentEnv(env) {
76
- const next = { ...(env || process.env) };
129
+ const next = { ...env || process.env };
77
130
  return normalizeInteractiveTerminalEnv(next, {
78
131
  stripRuntimeShims: false,
79
- stripNodeOptions: false,
132
+ stripNodeOptions: false
80
133
  });
81
134
  }
82
-
83
135
  function socketIdentity(socketPath) {
84
136
  const stat = fs.statSync(socketPath, { bigint: true });
85
137
  return { dev: stat.dev, ino: stat.ino };
86
138
  }
87
-
88
139
  function sameSocketIdentity(left, right) {
89
140
  return Boolean(left && right && left.dev === right.dev && left.ino === right.ino);
90
141
  }
91
-
92
142
  class NativePtyHost {
143
+ configDir;
144
+ socketPath;
145
+ ownerPid;
146
+ runtimeIdentity;
147
+ exitOnShutdown;
148
+ boundSocketPath;
149
+ boundSocketIdentity;
150
+ sessions;
151
+ clients;
152
+ ownerCheckTimer;
153
+ idleExitTimer;
154
+ hasAcceptedClient;
155
+ activeControllerClient;
156
+ activeControllerIdentity;
157
+ sessionMutationQueues;
158
+ activeControllerMutations;
159
+ controllerRegistrationQueue;
160
+ controllerHandoff;
161
+ rotationPreparation;
162
+ terminalExitDataFlushMs;
163
+ clientMaxBufferedBytes;
164
+ clientMaxRequestBytes;
165
+ idleExitMs;
166
+ disposed;
167
+ screenWorkerPool;
168
+ server;
93
169
  constructor(options = {}) {
94
- this.configDir = options.configDir || process.env.FARMING_CONFIG_DIR || path.join(os.homedir(), '.farming');
170
+ this.configDir = options.configDir || process.env.FARMING_CONFIG_DIR || path.join(os.homedir(), ".farming");
95
171
  this.socketPath = options.socketPath || process.env.FARMING_NATIVE_PTY_HOST_SOCKET || nativePtyHostSocketPath(this.configDir);
96
172
  this.ownerPid = Number(options.ownerPid || process.env.FARMING_NATIVE_PTY_HOST_OWNER_PID || 0);
97
173
  this.runtimeIdentity = options.runtimeIdentity || HOST_RUNTIME_IDENTITY;
98
174
  this.exitOnShutdown = options.exitOnShutdown !== false;
99
- this.boundSocketPath = process.platform === 'win32'
100
- ? this.socketPath
101
- : nativePtyHostPrivateSocketPath(this.socketPath);
175
+ this.boundSocketPath = process.platform === "win32" ? this.socketPath : nativePtyHostPrivateSocketPath(this.socketPath);
102
176
  this.boundSocketIdentity = null;
103
- this.sessions = new Map();
104
- this.clients = new Set();
177
+ this.sessions = /* @__PURE__ */ new Map();
178
+ this.clients = /* @__PURE__ */ new Set();
105
179
  this.ownerCheckTimer = null;
106
180
  this.idleExitTimer = null;
107
181
  this.hasAcceptedClient = false;
108
182
  this.activeControllerClient = null;
109
183
  this.activeControllerIdentity = null;
110
- this.sessionMutationQueues = new Map();
111
- this.activeControllerMutations = new Set();
184
+ this.sessionMutationQueues = /* @__PURE__ */ new Map();
185
+ this.activeControllerMutations = /* @__PURE__ */ new Set();
112
186
  this.controllerRegistrationQueue = Promise.resolve();
113
187
  this.controllerHandoff = null;
114
188
  this.rotationPreparation = null;
189
+ this.terminalExitDataFlushMs = options.terminalExitDataFlushMs;
115
190
  this.clientMaxBufferedBytes = normalizePositiveInteger(
116
191
  options.clientMaxBufferedBytes,
117
192
  DEFAULT_CLIENT_MAX_BUFFERED_BYTES,
118
193
  1024,
119
- 256 * 1024 * 1024,
194
+ 256 * 1024 * 1024
120
195
  );
121
196
  this.clientMaxRequestBytes = normalizePositiveInteger(
122
197
  options.clientMaxRequestBytes,
123
198
  DEFAULT_CLIENT_MAX_REQUEST_BYTES,
124
199
  1024,
125
- 256 * 1024 * 1024,
200
+ 256 * 1024 * 1024
126
201
  );
127
202
  this.idleExitMs = normalizePositiveInteger(
128
203
  options.idleExitMs ?? process.env.FARMING_NATIVE_PTY_HOST_IDLE_EXIT_MS,
129
204
  DEFAULT_IDLE_EXIT_MS,
130
205
  0,
131
- 3600000
206
+ 36e5
132
207
  );
133
208
  this.disposed = false;
134
- this.screenWorkerPool = new TerminalScreenWorkerPool({
209
+ this.screenWorkerPool = new import_terminal_screen_worker_pool.TerminalScreenWorkerPool({
135
210
  size: normalizePositiveInteger(process.env.FARMING_NATIVE_PTY_SCREEN_WORKERS, 3, 0, 12),
136
211
  workerOptions: {
137
212
  cols: DEFAULT_COLS,
138
213
  rows: DEFAULT_ROWS,
139
- previewSnapshot: true,
140
- },
214
+ previewSnapshot: true
215
+ }
141
216
  });
142
217
  }
143
-
144
218
  async start() {
145
219
  await this.prepareSocket();
146
- this.server = net.createServer(socket => this.handleConnection(socket));
220
+ const server = net.createServer((socket) => this.handleConnection(socket));
221
+ this.server = server;
147
222
  await new Promise((resolve, reject) => {
148
- this.server.once('error', reject);
149
- this.server.listen(this.boundSocketPath, () => {
150
- this.server.off('error', reject);
223
+ server.once("error", reject);
224
+ server.listen(this.boundSocketPath, () => {
225
+ server.off("error", reject);
151
226
  resolve();
152
227
  });
153
228
  });
154
- if (process.platform !== 'win32') {
229
+ if (process.platform !== "win32") {
155
230
  this.boundSocketIdentity = socketIdentity(this.boundSocketPath);
156
- fs.chmodSync(this.boundSocketPath, 0o600);
231
+ fs.chmodSync(this.boundSocketPath, 384);
157
232
  try {
158
233
  fs.linkSync(this.boundSocketPath, this.socketPath);
159
234
  } catch (error) {
160
- await new Promise(resolve => this.server.close(() => resolve()));
235
+ await new Promise((resolve) => this.server?.close(() => resolve()));
161
236
  this.server = null;
162
237
  this.boundSocketIdentity = null;
163
238
  throw error;
164
239
  }
165
240
  }
166
- console.error(`[${new Date().toISOString()}] Native PTY host listening on ${this.socketPath} ownerPid=${this.ownerPid || ''}`);
241
+ console.error(`[${(/* @__PURE__ */ new Date()).toISOString()}] Native PTY host listening on ${this.socketPath} ownerPid=${this.ownerPid || ""}`);
167
242
  this.startOwnerWatch();
168
243
  }
169
-
170
244
  startOwnerWatch() {
171
245
  if (!this.ownerPid || this.ownerPid === process.pid || this.ownerCheckTimer) return;
172
246
  this.ownerCheckTimer = setInterval(() => {
@@ -176,19 +250,18 @@ class NativePtyHost {
176
250
  this.dispose().finally(() => process.exit(0));
177
251
  }
178
252
  }, OWNER_CHECK_INTERVAL_MS);
179
- if (typeof this.ownerCheckTimer.unref === 'function') {
253
+ if (typeof this.ownerCheckTimer.unref === "function") {
180
254
  this.ownerCheckTimer.unref();
181
255
  }
182
256
  }
183
-
184
257
  async prepareSocket() {
185
- if (process.platform === 'win32') return;
258
+ if (process.platform === "win32") return;
186
259
  fs.mkdirSync(path.dirname(this.socketPath), { recursive: true });
187
260
  if (fs.existsSync(this.socketPath)) {
188
261
  const probe = await probeUnixSocket(this.socketPath);
189
262
  if (probe.active) {
190
263
  const error = new Error(`Native PTY host socket is already active: ${this.socketPath}`);
191
- error.code = 'EADDRINUSE';
264
+ error.code = "EADDRINUSE";
192
265
  error.socketPath = this.socketPath;
193
266
  throw error;
194
267
  }
@@ -196,57 +269,46 @@ class NativePtyHost {
196
269
  try {
197
270
  fs.unlinkSync(this.socketPath);
198
271
  } catch (error) {
199
- if (!error || error.code !== 'ENOENT') throw error;
272
+ if (errorCode(error) !== "ENOENT") throw error;
200
273
  }
201
274
  try {
202
275
  fs.unlinkSync(this.boundSocketPath);
203
276
  } catch (error) {
204
- if (!error || error.code !== 'ENOENT') throw error;
277
+ if (errorCode(error) !== "ENOENT") throw error;
205
278
  }
206
279
  }
207
-
208
280
  handleConnection(socket) {
209
- const client = { socket, buffer: '', disconnected: false };
281
+ const client = { socket, buffer: "", disconnected: false };
210
282
  this.hasAcceptedClient = true;
211
283
  this.cancelIdleExit();
212
284
  this.clients.add(client);
213
- socket.on('data', chunk => this.handleClientData(client, chunk));
214
- socket.on('close', () => this.removeClient(client));
215
- socket.on('error', () => this.removeClient(client));
285
+ socket.on("data", (chunk) => this.handleClientData(client, chunk));
286
+ socket.on("close", () => this.removeClient(client));
287
+ socket.on("error", () => this.removeClient(client));
216
288
  }
217
-
218
289
  removeClient(client) {
219
290
  if (!client || client.disconnected === true) return Promise.resolve();
220
291
  client.disconnected = true;
221
292
  this.clients.delete(client);
222
293
  let retirement = Promise.resolve();
223
- if (
224
- this.activeControllerClient === client
225
- || this.controllerHandoff?.fromClient === client
226
- || this.controllerHandoff?.toClient === client
227
- ) {
228
- retirement = this.controllerRegistrationQueue
229
- .catch(() => {})
230
- .then(() => this.retireControllerClient(client));
294
+ if (this.activeControllerClient === client || this.controllerHandoff?.fromClient === client || this.controllerHandoff?.toClient === client) {
295
+ retirement = this.controllerRegistrationQueue.catch(() => {
296
+ }).then(() => this.retireControllerClient(client));
231
297
  this.controllerRegistrationQueue = retirement;
232
298
  }
233
299
  this.scheduleIdleExitIfUnused();
234
300
  return retirement;
235
301
  }
236
-
237
302
  async retireControllerClient(client) {
238
303
  if (this.activeControllerClient !== client) return;
239
304
  const handoff = {
240
305
  fromClient: client,
241
306
  fromIdentity: this.activeControllerIdentity,
242
307
  toClient: null,
243
- toIdentity: null,
308
+ toIdentity: null
244
309
  };
245
310
  this.controllerHandoff = handoff;
246
311
  try {
247
- // A disconnect closes admission immediately, but an operation that was
248
- // already admitted owns its linearization point until it completes.
249
- // Retire the server client only after that exact set has drained.
250
312
  await Promise.allSettled([...this.activeControllerMutations]);
251
313
  if (this.activeControllerClient !== client) return;
252
314
  this.activeControllerClient = null;
@@ -254,56 +316,38 @@ class NativePtyHost {
254
316
  if (this.controllerHandoff === handoff) this.controllerHandoff = null;
255
317
  }
256
318
  }
257
-
258
319
  hasLiveSessions() {
259
320
  for (const session of this.sessions.values()) {
260
- if (session && session.status !== 'exited') return true;
321
+ if (session && session.status !== "exited") return true;
261
322
  }
262
323
  return false;
263
324
  }
264
-
265
325
  cancelIdleExit() {
266
326
  if (!this.idleExitTimer) return;
267
327
  clearTimeout(this.idleExitTimer);
268
328
  this.idleExitTimer = null;
269
329
  }
270
-
271
330
  scheduleIdleExitIfUnused() {
272
- if (
273
- this.disposed ||
274
- this.ownerPid ||
275
- this.idleExitMs <= 0 ||
276
- !this.hasAcceptedClient ||
277
- this.clients.size > 0 ||
278
- this.hasLiveSessions() ||
279
- this.idleExitTimer
280
- ) {
331
+ if (this.disposed || this.ownerPid || this.idleExitMs <= 0 || !this.hasAcceptedClient || this.clients.size > 0 || this.hasLiveSessions() || this.idleExitTimer) {
281
332
  return;
282
333
  }
283
-
284
334
  this.idleExitTimer = setTimeout(() => {
285
335
  this.idleExitTimer = null;
286
- if (
287
- this.disposed ||
288
- this.ownerPid ||
289
- this.clients.size > 0 ||
290
- this.hasLiveSessions()
291
- ) {
336
+ if (this.disposed || this.ownerPid || this.clients.size > 0 || this.hasLiveSessions()) {
292
337
  return;
293
338
  }
294
339
  this.dispose().finally(() => process.exit(0));
295
340
  }, this.idleExitMs);
296
- if (typeof this.idleExitTimer.unref === 'function') {
341
+ if (typeof this.idleExitTimer.unref === "function") {
297
342
  this.idleExitTimer.unref();
298
343
  }
299
344
  }
300
-
301
345
  handleClientData(client, chunk) {
302
- client.buffer += chunk.toString('utf8');
303
- let newline = client.buffer.indexOf('\n');
346
+ client.buffer += chunk.toString("utf8");
347
+ let newline = client.buffer.indexOf("\n");
304
348
  while (newline >= 0) {
305
349
  if (newline > this.clientMaxRequestBytes) {
306
- this.disconnectSlowClient(client, 'native pty request exceeded limit');
350
+ this.disconnectSlowClient(client, "native pty request exceeded limit");
307
351
  return;
308
352
  }
309
353
  const line = client.buffer.slice(0, newline);
@@ -311,13 +355,12 @@ class NativePtyHost {
311
355
  if (line.trim()) {
312
356
  this.handleClientMessage(client, line);
313
357
  }
314
- newline = client.buffer.indexOf('\n');
358
+ newline = client.buffer.indexOf("\n");
315
359
  }
316
360
  if (client.buffer.length > this.clientMaxRequestBytes) {
317
- this.disconnectSlowClient(client, 'native pty request exceeded limit');
361
+ this.disconnectSlowClient(client, "native pty request exceeded limit");
318
362
  }
319
363
  }
320
-
321
364
  async handleClientMessage(client, line) {
322
365
  let message;
323
366
  try {
@@ -325,10 +368,8 @@ class NativePtyHost {
325
368
  } catch {
326
369
  return;
327
370
  }
328
-
329
371
  const id = message && message.id;
330
372
  if (!id) return;
331
-
332
373
  try {
333
374
  const result = await this.dispatch(message.method, message.params || {}, client);
334
375
  this.send(client, { id, ok: true, result });
@@ -336,92 +377,88 @@ class NativePtyHost {
336
377
  this.send(client, {
337
378
  id,
338
379
  ok: false,
339
- error: error && error.message ? error.message : 'Native pty host request failed',
380
+ error: errorMessage(error) || "Native pty host request failed"
340
381
  });
341
382
  }
342
383
  }
343
-
344
384
  dispatch(method, params, client) {
385
+ const sessionId = typeof params.sessionId === "string" ? params.sessionId : "";
345
386
  switch (method) {
346
- case 'ping':
387
+ case "ping":
347
388
  return {
348
389
  ok: true,
349
390
  pid: process.pid,
350
- privateSocketPath: process.platform === 'win32' ? '' : this.boundSocketPath,
351
- runtimeIdentity: this.runtimeIdentity,
391
+ privateSocketPath: process.platform === "win32" ? "" : this.boundSocketPath,
392
+ runtimeIdentity: this.runtimeIdentity
352
393
  };
353
- case 'registerController':
394
+ case "registerController":
354
395
  return this.registerController(client, params.identity || {});
355
- case 'createSession':
396
+ case "createSession":
356
397
  return this.enqueueControllerMutation(
357
398
  params.options?.agentId,
358
399
  client,
359
- () => this.createSession(params.options || {}),
400
+ () => this.createSession(params.options || { agentId: "", command: "" })
360
401
  );
361
- case 'sendInput':
402
+ case "sendInput":
362
403
  return this.enqueueControllerMutation(
363
- params.sessionId,
404
+ sessionId,
364
405
  client,
365
- () => this.sendInput(params.sessionId, params.input, params.expectedRuntimeEpoch || ''),
406
+ () => this.sendInput(sessionId, params.input, params.expectedRuntimeEpoch || "")
366
407
  );
367
- case 'resizeSession':
408
+ case "resizeSession":
368
409
  return this.enqueueControllerMutation(
369
- params.sessionId,
410
+ sessionId,
370
411
  client,
371
- () => this.resizeSession(params.sessionId, params.cols, params.rows),
412
+ () => this.resizeSession(sessionId, params.cols, params.rows)
372
413
  );
373
- case 'clearBuffer':
414
+ case "clearBuffer":
374
415
  return this.enqueueControllerMutation(
375
- params.sessionId,
416
+ sessionId,
376
417
  client,
377
- () => this.clearBuffer(params.sessionId, params.expectedRuntimeEpoch || ''),
418
+ () => this.clearBuffer(sessionId, params.expectedRuntimeEpoch || "")
378
419
  );
379
- case 'killSession':
420
+ case "killSession":
380
421
  return this.enqueueControllerMutation(
381
- params.sessionId,
422
+ sessionId,
382
423
  client,
383
- () => this.killSession(params.sessionId),
424
+ () => this.killSession(sessionId)
384
425
  );
385
- case 'getSessionState':
386
- return this.getSessionState(params.sessionId);
387
- case 'getSessionAttachCheckpoint':
388
- return this.getSessionAttachCheckpoint(params.sessionId, client);
389
- case 'getSessionPreview':
390
- return this.getSessionPreview(params.sessionId);
391
- case 'recoverSessions':
426
+ case "getSessionState":
427
+ return this.getSessionState(sessionId);
428
+ case "getSessionAttachCheckpoint":
429
+ return this.getSessionAttachCheckpoint(sessionId, client);
430
+ case "getSessionPreview":
431
+ return this.getSessionPreview(sessionId);
432
+ case "recoverSessions":
392
433
  return this.recoverSessions();
393
- case 'serializeTerminalState':
434
+ case "serializeTerminalState":
394
435
  return this.serializeTerminalState(client);
395
- case 'resumeTerminalState':
396
- return this.resumeTerminalState(client, params.preparationToken || '');
397
- case 'updateSessionMetadata':
436
+ case "resumeTerminalState":
437
+ return this.resumeTerminalState(client, params.preparationToken || "");
438
+ case "updateSessionMetadata":
398
439
  return this.enqueueControllerMutation(
399
- params.sessionId,
440
+ sessionId,
400
441
  client,
401
- () => this.updateSessionMetadata(params.sessionId, params.patch || {}),
442
+ () => this.updateSessionMetadata(sessionId, params.patch || {})
402
443
  );
403
- case 'shutdownHost':
444
+ case "shutdownHost":
404
445
  return this.shutdownHost(
405
446
  client,
406
447
  params.controller || {},
407
- params.preparationToken || '',
448
+ params.preparationToken || ""
408
449
  );
409
450
  default:
410
451
  throw new Error(`Unknown native pty host method: ${method}`);
411
452
  }
412
453
  }
413
-
414
454
  enqueueControllerMutation(sessionId, client, operation) {
415
- // Admission is fenced before queueing. Once a newer controller starts its
416
- // handoff, no additional work from the retiring controller may enter the
417
- // mutation set; work admitted before that cut is allowed to finish.
418
455
  try {
419
456
  this.assertActiveController(client);
420
457
  if (this.controllerHandoff) {
421
- throw new Error('Native pty controller handoff is in progress');
458
+ throw new Error("Native pty controller handoff is in progress");
422
459
  }
423
460
  if (this.rotationPreparation) {
424
- throw new Error('Native pty host is frozen for runtime rotation');
461
+ throw new Error("Native pty host is frozen for runtime rotation");
425
462
  }
426
463
  } catch (error) {
427
464
  return Promise.reject(error);
@@ -430,18 +467,17 @@ class NativePtyHost {
430
467
  [CONTROLLER_MUTATION_ADMISSION]: true,
431
468
  client,
432
469
  controllerId: client.controllerId,
433
- controllerGeneration: client.controllerGeneration,
470
+ controllerGeneration: client.controllerGeneration
434
471
  });
435
- const key = typeof sessionId === 'string' && sessionId ? sessionId : '__host__';
472
+ const key = typeof sessionId === "string" && sessionId ? sessionId : "__host__";
436
473
  const previous = this.sessionMutationQueues.get(key) || Promise.resolve();
437
- const next = previous
438
- .catch(() => {})
439
- .then(() => {
440
- if (this.rotationPreparation) {
441
- throw new Error('Native pty host is frozen for runtime rotation');
442
- }
443
- return operation(admission);
444
- });
474
+ const next = previous.catch(() => {
475
+ }).then(() => {
476
+ if (this.rotationPreparation) {
477
+ throw new Error("Native pty host is frozen for runtime rotation");
478
+ }
479
+ return operation(admission);
480
+ });
445
481
  this.activeControllerMutations.add(next);
446
482
  this.sessionMutationQueues.set(key, next);
447
483
  const cleanup = () => {
@@ -453,195 +489,161 @@ class NativePtyHost {
453
489
  next.then(cleanup, cleanup);
454
490
  return next;
455
491
  }
456
-
457
492
  send(client, message) {
458
- this.writeClientMessage(client, `${JSON.stringify(message)}\n`);
493
+ this.writeClientMessage(client, `${JSON.stringify(message)}
494
+ `);
459
495
  }
460
-
461
496
  disconnectSlowClient(client, reason) {
462
497
  if (!client || !client.socket || client.socket.destroyed) return false;
463
498
  client.socket.destroy(new Error(reason));
464
499
  return true;
465
500
  }
466
-
467
501
  writeClientMessage(client, message) {
468
502
  if (!client || !client.socket || client.socket.destroyed) return false;
469
503
  if (client.socket.writableLength > this.clientMaxBufferedBytes) {
470
- this.disconnectSlowClient(client, 'native pty client backpressure');
504
+ this.disconnectSlowClient(client, "native pty client backpressure");
471
505
  return false;
472
506
  }
473
507
  const accepted = client.socket.write(message);
474
508
  if (!accepted && client.socket.writableLength > this.clientMaxBufferedBytes) {
475
- this.disconnectSlowClient(client, 'native pty client backpressure');
509
+ this.disconnectSlowClient(client, "native pty client backpressure");
476
510
  return false;
477
511
  }
478
512
  return true;
479
513
  }
480
-
481
514
  broadcast(event, payload) {
482
- const message = `${JSON.stringify({ event, payload })}\n`;
515
+ const message = `${JSON.stringify({ event, payload })}
516
+ `;
483
517
  for (const client of this.clients) {
484
518
  this.writeClientMessage(client, message);
485
519
  }
486
520
  }
487
-
488
521
  emitSessionEvent(event, payload) {
489
522
  this.broadcast(event, payload);
490
523
  }
491
-
492
524
  registerController(client, rawIdentity) {
493
525
  if (!client || client.disconnected === true) {
494
- throw new Error('Native pty controller client is disconnected');
526
+ throw new Error("Native pty controller client is disconnected");
495
527
  }
496
528
  const identity = {
497
- id: typeof rawIdentity.id === 'string' ? rawIdentity.id : '',
498
- generation: Math.floor(Number(rawIdentity.generation)),
529
+ id: typeof rawIdentity.id === "string" ? rawIdentity.id : "",
530
+ generation: Math.floor(Number(rawIdentity.generation))
499
531
  };
500
532
  if (!identity.id || !Number.isFinite(identity.generation) || identity.generation <= 0) {
501
- throw new Error('Invalid native pty controller identity');
502
- }
503
-
504
- const registration = this.controllerRegistrationQueue
505
- .catch(() => {})
506
- .then(async () => {
507
- const current = this.activeControllerIdentity;
508
- if (
509
- current &&
510
- (identity.generation < current.generation ||
511
- (identity.generation === current.generation && current.id !== identity.id))
512
- ) {
513
- throw new Error('Stale native pty controller');
514
- }
515
-
516
- const replacesController = !current
517
- || current.id !== identity.id
518
- || current.generation !== identity.generation
519
- || this.activeControllerClient !== client;
520
- if (replacesController && current) {
521
- const handoff = {
522
- fromClient: this.activeControllerClient,
523
- fromIdentity: current,
524
- toClient: client,
525
- toIdentity: identity,
526
- };
527
- this.controllerHandoff = handoff;
528
- try {
529
- // New admissions from the retiring controller are now closed.
530
- // Drain the exact set admitted before that cut before publishing
531
- // the new generation as active.
532
- await Promise.allSettled([...this.activeControllerMutations]);
533
- } finally {
534
- if (this.controllerHandoff === handoff) this.controllerHandoff = null;
535
- }
536
- }
537
-
538
- if (client.disconnected === true) {
539
- throw new Error('Native pty controller client is disconnected');
540
- }
541
-
542
- this.activeControllerIdentity = identity;
543
- this.activeControllerClient = client;
544
- client.controllerId = identity.id;
545
- client.controllerGeneration = identity.generation;
546
- return {
547
- registered: true,
548
- controllerId: identity.id,
549
- controllerGeneration: identity.generation,
533
+ throw new Error("Invalid native pty controller identity");
534
+ }
535
+ const registration = this.controllerRegistrationQueue.catch(() => {
536
+ }).then(async () => {
537
+ const current = this.activeControllerIdentity;
538
+ if (current && (identity.generation < current.generation || identity.generation === current.generation && current.id !== identity.id)) {
539
+ throw new Error("Stale native pty controller");
540
+ }
541
+ const replacesController = !current || current.id !== identity.id || current.generation !== identity.generation || this.activeControllerClient !== client;
542
+ if (replacesController && current) {
543
+ const handoff = {
544
+ fromClient: this.activeControllerClient,
545
+ fromIdentity: current,
546
+ toClient: client,
547
+ toIdentity: identity
550
548
  };
551
- });
549
+ this.controllerHandoff = handoff;
550
+ try {
551
+ await Promise.allSettled([...this.activeControllerMutations]);
552
+ } finally {
553
+ if (this.controllerHandoff === handoff) this.controllerHandoff = null;
554
+ }
555
+ }
556
+ if (client.disconnected === true) {
557
+ throw new Error("Native pty controller client is disconnected");
558
+ }
559
+ this.activeControllerIdentity = identity;
560
+ this.activeControllerClient = client;
561
+ client.controllerId = identity.id;
562
+ client.controllerGeneration = identity.generation;
563
+ return {
564
+ registered: true,
565
+ controllerId: identity.id,
566
+ controllerGeneration: identity.generation
567
+ };
568
+ });
552
569
  this.controllerRegistrationQueue = registration;
553
570
  return registration;
554
571
  }
555
-
556
572
  assertActiveController(client) {
557
- if (client?.[CONTROLLER_MUTATION_ADMISSION] === true) return;
558
- if (
559
- !client ||
560
- client.disconnected === true ||
561
- client !== this.activeControllerClient ||
562
- !this.activeControllerIdentity ||
563
- client.controllerId !== this.activeControllerIdentity.id ||
564
- client.controllerGeneration !== this.activeControllerIdentity.generation
565
- ) {
566
- throw new Error('Native pty mutation requires the active controller');
573
+ if (client && isControllerAdmission(client)) return;
574
+ if (!client || client.disconnected === true || client !== this.activeControllerClient || !this.activeControllerIdentity || client.controllerId !== this.activeControllerIdentity.id || client.controllerGeneration !== this.activeControllerIdentity.generation) {
575
+ throw new Error("Native pty mutation requires the active controller");
567
576
  }
568
577
  }
569
-
570
578
  async createSession(options) {
571
579
  if (this.rotationPreparation) {
572
- throw new Error('Native pty host is frozen for runtime rotation');
580
+ throw new Error("Native pty host is frozen for runtime rotation");
573
581
  }
574
582
  this.cancelIdleExit();
575
- const normalized = options.shellIntegrationPrepared === true
576
- ? {
577
- ...options,
578
- args: [...(options.args || [])],
579
- env: sanitizeAgentEnv(options.env),
580
- }
581
- : normalizeShellSessionOptions({
582
- ...options,
583
- env: sanitizeAgentEnv(options.env),
584
- });
583
+ const normalized = options.shellIntegrationPrepared === true ? {
584
+ ...options,
585
+ args: [...options.args || []],
586
+ env: sanitizeAgentEnv(options.env)
587
+ } : normalizeShellSessionOptions({
588
+ ...options,
589
+ env: sanitizeAgentEnv(options.env)
590
+ });
585
591
  const agentId = normalized.agentId;
586
592
  if (!agentId) {
587
- throw new Error('Missing native pty session id');
593
+ throw new Error("Missing native pty session id");
588
594
  }
589
-
590
595
  const existing = this.sessions.get(agentId);
591
- if (existing && existing.status !== 'exited') {
596
+ if (existing && existing.status !== "exited") {
592
597
  return { sessionId: agentId, status: existing.status };
593
598
  }
594
-
595
- const reviveState = options.reviveState
596
- ? normalizeTerminalStateEntry(options.reviveState)
597
- : null;
599
+ const reviveState = options.reviveState ? normalizeTerminalStateEntry(options.reviveState) : null;
598
600
  const reviveEvent = reviveState?.replayEvent?.events?.[0] || null;
599
601
  const cols = reviveEvent?.cols || normalized.cols || DEFAULT_COLS;
600
602
  const rows = reviveEvent?.rows || normalized.rows || DEFAULT_ROWS;
601
603
  const runtimeGeneration = await allocateNativePtyRuntimeGeneration(this.configDir);
602
604
  const runtimeEpoch = formatNativePtyRuntimeEpoch(runtimeGeneration);
603
605
  const screenWorker = await this.screenWorkerPool.acquire({ cols, rows, runtimeEpoch });
604
- const replayText = reviveEvent?.data || '';
605
- const restoredOutput = reviveState
606
- ? `${replayText}${TERMINAL_HISTORY_RESTORED_MESSAGE}`
607
- : '';
606
+ const replayText = reviveEvent?.data || "";
607
+ const restoredOutput = reviveState ? `${replayText}${TERMINAL_HISTORY_RESTORED_MESSAGE}` : "";
608
608
  let restoredScreenState = null;
609
609
  if (restoredOutput) {
610
610
  try {
611
611
  await screenWorker.append(restoredOutput, 1, 1);
612
612
  restoredScreenState = await screenWorker.getState();
613
613
  } catch (error) {
614
- await screenWorker.dispose().catch(() => {});
615
- throw new Error(`Failed to restore serialized terminal history: ${error.message}`, {
616
- cause: error,
614
+ await screenWorker.dispose().catch(() => {
615
+ });
616
+ throw new Error(`Failed to restore serialized terminal history: ${errorMessage(error)}`, {
617
+ cause: error
617
618
  });
618
619
  }
619
620
  }
621
+ const persistedCommand = normalized.metadata?.command;
622
+ const persistedStartedAt = normalized.metadata?.startedAt;
620
623
  const metadata = {
621
- ...(normalized.metadata || {}),
624
+ ...normalized.metadata || {},
622
625
  protocolVersion: 1,
623
- engineName: 'native',
626
+ engineName: "native",
624
627
  agentId,
625
- command: normalized.metadata?.command || normalized.command,
628
+ command: typeof persistedCommand === "string" && persistedCommand ? persistedCommand : normalized.command,
626
629
  cwd: normalized.cwd || process.cwd(),
627
- startedAt: normalized.metadata?.startedAt || Date.now(),
630
+ startedAt: typeof persistedStartedAt === "number" && Number.isFinite(persistedStartedAt) ? persistedStartedAt : Date.now()
628
631
  };
629
-
630
632
  let ptyProcess;
631
633
  try {
632
634
  ptyProcess = createPtyProcess(normalized.command, normalized.args || [], {
633
- name: 'xterm-256color',
635
+ name: "xterm-256color",
634
636
  cols,
635
637
  rows,
636
638
  cwd: normalized.cwd,
637
- env: normalized.env || process.env,
639
+ env: normalized.env || process.env
638
640
  });
639
641
  } catch (error) {
640
642
  cleanupShellBusyIntegration(normalized.shellBusyIntegration);
641
- await screenWorker.dispose().catch(() => {});
643
+ await screenWorker.dispose().catch(() => {
644
+ });
642
645
  throw error;
643
646
  }
644
-
645
647
  const session = {
646
648
  id: agentId,
647
649
  command: normalized.command,
@@ -662,39 +664,38 @@ class NativePtyHost {
662
664
  previewSnapshot: restoredScreenState?.previewSnapshot || null,
663
665
  previewCols: restoredScreenState?.cols || cols,
664
666
  previewRows: restoredScreenState?.rows || rows,
665
- title: restoredScreenState?.title || '',
666
- status: 'running',
667
+ title: restoredScreenState?.title || "",
668
+ status: "running",
667
669
  terminalBusy: null,
668
- shellCwd: '',
670
+ shellCwd: "",
669
671
  shellLastExitCode: null,
670
- shellLastEvent: '',
671
- shellCommand: '',
672
- shellLastCommand: '',
672
+ shellLastEvent: "",
673
+ shellCommand: "",
674
+ shellLastCommand: "",
673
675
  shellCommandStartedAt: null,
674
676
  shellLastCommandStartedAt: null,
675
677
  shellLastCommandFinishedAt: null,
676
678
  shellLastCommandDurationMs: null,
677
- shellBusyMarkerPending: '',
679
+ shellBusyMarkerPending: "",
678
680
  shellBusyIntegration: normalized.shellBusyIntegration || null,
679
681
  startedAt: metadata.startedAt || Date.now(),
680
682
  lastActivityAt: Date.now(),
681
683
  exitedAt: null,
682
- screenWorker,
684
+ screenWorker
683
685
  };
684
-
685
686
  this.sessions.set(agentId, session);
686
687
  this.bindScreenWorker(session);
687
688
  this.bindPty(session);
688
- this.emitSessionEvent('session-started', {
689
+ this.emitSessionEvent("session-started", {
689
690
  sessionId: agentId,
690
691
  status: session.status,
691
692
  startedAt: session.startedAt,
692
693
  runtimeEpoch: session.runtimeEpoch,
693
694
  outputSeq: session.outputSeq,
694
- stateRevision: session.stateRevision,
695
+ stateRevision: session.stateRevision
695
696
  });
696
697
  if (restoredOutput) {
697
- this.emitSessionEvent('session-sync', {
698
+ this.emitSessionEvent("session-sync", {
698
699
  sessionId: agentId,
699
700
  output: session.renderOutput,
700
701
  textOutput: session.output,
@@ -704,93 +705,80 @@ class NativePtyHost {
704
705
  stateRevision: session.stateRevision,
705
706
  cols: session.previewCols,
706
707
  rows: session.previewRows,
707
- revived: true,
708
+ revived: true
708
709
  });
709
710
  }
710
-
711
711
  return { sessionId: agentId, status: session.status };
712
712
  }
713
-
714
713
  bindScreenWorker(session) {
715
- session.screenWorker.on('preview', ({ previewText, title, cols, rows, previewSnapshot }) => {
714
+ const screenWorker = session.screenWorker;
715
+ if (!screenWorker) throw new TypeError("Terminal screen worker is unavailable");
716
+ screenWorker.on("preview", ({ previewText, title, cols, rows, previewSnapshot }) => {
716
717
  const current = this.sessions.get(session.id);
717
718
  if (current !== session) return;
718
-
719
- current.previewText = previewText || '';
719
+ current.previewText = previewText || "";
720
720
  current.previewSnapshot = previewSnapshot || null;
721
721
  current.previewCols = cols || current.previewCols;
722
722
  current.previewRows = rows || current.previewRows;
723
-
724
723
  if (title && title !== current.title) {
725
724
  current.title = title;
726
- this.emitSessionEvent('session-title', {
725
+ this.emitSessionEvent("session-title", {
727
726
  sessionId: current.id,
728
727
  title: current.title,
729
- runtimeEpoch: current.runtimeEpoch,
728
+ runtimeEpoch: current.runtimeEpoch
730
729
  });
731
730
  }
732
-
733
- this.emitSessionEvent('session-preview', {
731
+ this.emitSessionEvent("session-preview", {
734
732
  sessionId: current.id,
735
733
  previewText: current.previewText,
736
734
  cols: current.previewCols,
737
735
  rows: current.previewRows,
738
736
  previewSnapshot: current.previewSnapshot,
739
- runtimeEpoch: current.runtimeEpoch,
737
+ runtimeEpoch: current.runtimeEpoch
740
738
  });
741
739
  });
742
-
743
- session.screenWorker.on('error', error => {
740
+ screenWorker.on("error", (error) => {
744
741
  this.failSessionScreenState(session, error);
745
742
  });
746
743
  }
747
-
748
744
  failSessionScreenState(session, error) {
749
745
  const current = this.sessions.get(session.id);
750
746
  if (current !== session || current.stateProofAvailable === false) return;
751
747
  current.stateProofAvailable = false;
752
- const message = error instanceof Error ? error.message : String(error || 'unknown reducer failure');
753
- this.emitSessionEvent('session-error', {
748
+ const message = error instanceof Error ? error.message : String(error || "unknown reducer failure");
749
+ this.emitSessionEvent("session-error", {
754
750
  sessionId: current.id,
755
751
  error: `Terminal state reducer failed: ${message}`,
756
752
  fatal: true,
757
- runtimeEpoch: current.runtimeEpoch,
753
+ runtimeEpoch: current.runtimeEpoch
758
754
  });
759
755
  try {
760
756
  current.process?.kill();
761
757
  } catch {
762
- // The session is already unusable because its authoritative reducer failed.
763
758
  }
764
759
  }
765
-
766
760
  bindPty(session) {
767
- session.process.onData(data => this.handleSessionData(session.id, data, session));
761
+ session.process.onData((data) => this.handleSessionData(session.id, data, session));
768
762
  session.process.onExit(({ code }) => {
769
- this.handleSessionExit(session.id, code, session).catch(error => {
763
+ this.handleSessionExit(session.id, code, session).catch((error) => {
770
764
  if (this.sessions.get(session.id) !== session) return;
771
- this.emitSessionEvent('session-error', {
765
+ this.emitSessionEvent("session-error", {
772
766
  sessionId: session.id,
773
767
  error: error.message,
774
768
  fatal: false,
775
- runtimeEpoch: session.runtimeEpoch,
769
+ runtimeEpoch: session.runtimeEpoch
776
770
  });
777
771
  });
778
772
  });
779
773
  }
780
-
781
774
  handleSessionData(sessionId, rawData, expectedSession = null) {
782
775
  const current = this.sessions.get(sessionId);
783
- if (
784
- !current ||
785
- (expectedSession && current !== expectedSession) ||
786
- current.stateProofAvailable === false
787
- ) return;
776
+ if (!current || expectedSession && current !== expectedSession || current.stateProofAvailable === false) return;
788
777
  if (!acceptTerminalExitData(current)) {
789
778
  current.finalCheckpoint = null;
790
- this.failSessionScreenState(current, new Error('PTY emitted data after its final checkpoint cut'));
779
+ this.failSessionScreenState(current, new Error("PTY emitted data after its final checkpoint cut"));
791
780
  return;
792
781
  }
793
-
794
782
  const busyState = parseShellBusyMarkers(rawData, current.terminalBusy, current.shellBusyMarkerPending);
795
783
  current.shellBusyMarkerPending = busyState.pending;
796
784
  if (busyState.markerSeen) {
@@ -805,26 +793,26 @@ class NativePtyHost {
805
793
  if (busyState.shellEvent) {
806
794
  current.shellLastEvent = busyState.shellEvent;
807
795
  }
808
- if (busyState.shellEvent === 'start') {
796
+ if (busyState.shellEvent === "start") {
809
797
  current.shellCommandStartedAt = markerAt;
810
798
  }
811
799
  if (busyState.commandTextSeen) {
812
- current.shellCommand = busyState.shellCommand || '';
800
+ current.shellCommand = busyState.shellCommand || "";
813
801
  }
814
- if (busyState.shellEvent === 'finish') {
802
+ if (busyState.shellEvent === "finish") {
815
803
  const commandStartedAt = current.shellCommandStartedAt;
816
804
  if (current.shellCommand) {
817
805
  current.shellLastCommand = current.shellCommand;
818
806
  }
819
- if (typeof commandStartedAt === 'number') {
807
+ if (typeof commandStartedAt === "number") {
820
808
  current.shellLastCommandStartedAt = commandStartedAt;
821
809
  current.shellLastCommandFinishedAt = markerAt;
822
810
  current.shellLastCommandDurationMs = Math.max(0, markerAt - commandStartedAt);
823
811
  }
824
- current.shellCommand = '';
812
+ current.shellCommand = "";
825
813
  current.shellCommandStartedAt = null;
826
814
  }
827
- this.emitSessionEvent('session-busy-state', {
815
+ this.emitSessionEvent("session-busy-state", {
828
816
  sessionId,
829
817
  terminalBusy: current.terminalBusy,
830
818
  cwd: current.shellCwd || current.cwd,
@@ -838,17 +826,15 @@ class NativePtyHost {
838
826
  shellLastCommandDurationMs: current.shellLastCommandDurationMs,
839
827
  statusMarkerSeen: busyState.statusMarkerSeen,
840
828
  busyMarkerSeen: busyState.busyMarkerSeen,
841
- runtimeEpoch: current.runtimeEpoch,
829
+ runtimeEpoch: current.runtimeEpoch
842
830
  });
843
831
  }
844
-
845
832
  const data = busyState.data;
846
833
  if (!data) return;
847
-
848
834
  const reducerDelivery = enqueueTerminalReducerData(
849
835
  ensureTerminalReducerFlowControl(current),
850
836
  current.process,
851
- data,
837
+ data
852
838
  );
853
839
  if (reducerDelivery.error) {
854
840
  this.failSessionScreenState(current, reducerDelivery.error);
@@ -860,17 +846,15 @@ class NativePtyHost {
860
846
  const stateRevision = current.stateRevision;
861
847
  current.output = trimOutput(current.output + data);
862
848
  current.lastActivityAt = Date.now();
863
-
864
849
  const fallbackTitle = extractLatestTerminalTitle(data);
865
850
  if (fallbackTitle && fallbackTitle !== current.title) {
866
851
  current.title = fallbackTitle;
867
- this.emitSessionEvent('session-title', {
852
+ this.emitSessionEvent("session-title", {
868
853
  sessionId,
869
854
  title: current.title,
870
- runtimeEpoch: current.runtimeEpoch,
855
+ runtimeEpoch: current.runtimeEpoch
871
856
  });
872
857
  }
873
-
874
858
  if (current.screenWorker) {
875
859
  const commit = current.screenWorker.append(data, stateRevision, outputSeq);
876
860
  current.reducerCommitQueue = current.reducerCommitQueue.then(() => commit).then(() => {
@@ -879,49 +863,43 @@ class NativePtyHost {
879
863
  const flowError = acknowledgeTerminalReducerData(
880
864
  ensureTerminalReducerFlowControl(latest),
881
865
  latest.process,
882
- reducerDelivery.bytes,
866
+ reducerDelivery.bytes
883
867
  );
884
868
  if (flowError) {
885
869
  this.failSessionScreenState(latest, flowError);
886
870
  return;
887
871
  }
888
- this.emitSessionEvent('session-output', {
872
+ this.emitSessionEvent("session-output", {
889
873
  sessionId,
890
874
  data,
891
875
  runtimeEpoch: latest.runtimeEpoch,
892
876
  outputSeq,
893
- stateRevision,
877
+ stateRevision
894
878
  });
895
- this.emitSessionEvent('session-activity', {
879
+ this.emitSessionEvent("session-activity", {
896
880
  sessionId,
897
881
  lastActivityAt: latest.lastActivityAt,
898
- runtimeEpoch: latest.runtimeEpoch,
882
+ runtimeEpoch: latest.runtimeEpoch
899
883
  });
900
- }).catch(error => this.failSessionScreenState(current, error));
884
+ }).catch((error) => this.failSessionScreenState(current, error));
901
885
  }
902
886
  }
903
-
904
887
  handleSessionExit(sessionId, code, expectedSession = null) {
905
888
  const session = this.sessions.get(sessionId);
906
- if (
907
- !session ||
908
- (expectedSession && session !== expectedSession) ||
909
- session.status === 'exited'
910
- ) return Promise.resolve();
889
+ if (!session || expectedSession && session !== expectedSession || session.status === "exited") return Promise.resolve();
911
890
  if (session.exitFinalizationPromise) return session.exitFinalizationPromise;
912
891
  const finalization = this.finalizeSessionExit(sessionId, code, session);
913
892
  session.exitFinalizationPromise = finalization;
914
893
  return finalization;
915
894
  }
916
-
917
895
  async finalizeSessionExit(sessionId, code, session) {
918
896
  const quiesced = await waitForTerminalExitDataQuiescence(session, {
919
897
  flushMs: this.terminalExitDataFlushMs,
920
- isCurrent: () => this.sessions.get(sessionId) === session,
898
+ isCurrent: () => this.sessions.get(sessionId) === session
921
899
  });
922
900
  if (!quiesced) return;
923
-
924
- await Promise.resolve(session.reducerCommitQueue).catch(() => {});
901
+ await Promise.resolve(session.reducerCommitQueue).catch(() => {
902
+ });
925
903
  if (this.sessions.get(sessionId) !== session) return;
926
904
  const finalCheckpoint = await captureTerminalAttachCheckpoint(session, { requireCurrentCut: true });
927
905
  if (this.sessions.get(sessionId) !== session) return;
@@ -934,64 +912,61 @@ class NativePtyHost {
934
912
  session.previewRows = finalCheckpoint.rows;
935
913
  session.title = finalCheckpoint.title || session.title;
936
914
  } else {
937
- this.failSessionScreenState(session, new Error('Unable to capture the exact final Terminal checkpoint'));
915
+ this.failSessionScreenState(session, new Error("Unable to capture the exact final Terminal checkpoint"));
938
916
  }
939
-
940
- session.status = 'exited';
917
+ session.status = "exited";
941
918
  session.exitedAt = Date.now();
942
919
  resetTerminalReducerFlowControl(
943
920
  ensureTerminalReducerFlowControl(session),
944
- session.process,
921
+ session.process
945
922
  );
946
923
  cleanupShellBusyIntegration(session.shellBusyIntegration);
947
924
  if (session.screenWorker) {
948
- await session.screenWorker.dispose().catch(() => {});
925
+ await session.screenWorker.dispose().catch(() => {
926
+ });
949
927
  session.screenWorker = null;
950
928
  }
951
-
952
- this.emitSessionEvent('session-exited', {
929
+ this.emitSessionEvent("session-exited", {
953
930
  sessionId,
954
- code: code == null ? 'unknown' : code,
931
+ code: code == null ? "unknown" : code,
955
932
  exitedAt: session.exitedAt,
956
933
  runtimeEpoch: session.runtimeEpoch,
957
934
  outputSeq: session.outputSeq,
958
935
  stateRevision: session.stateRevision,
959
- stateProofAvailable: session.stateProofAvailable !== false,
936
+ stateProofAvailable: session.stateProofAvailable !== false
960
937
  });
961
938
  if (finalCheckpoint) {
962
- this.emitSessionEvent('session-preview', {
939
+ this.emitSessionEvent("session-preview", {
963
940
  sessionId,
964
941
  previewText: session.previewText,
965
942
  cols: session.previewCols,
966
943
  rows: session.previewRows,
967
944
  previewSnapshot: session.previewSnapshot,
968
- runtimeEpoch: session.runtimeEpoch,
945
+ runtimeEpoch: session.runtimeEpoch
969
946
  });
970
947
  }
971
948
  this.scheduleIdleExitIfUnused();
972
949
  }
973
-
974
- async sendInput(sessionId, input, expectedRuntimeEpoch = '') {
950
+ async sendInput(sessionId, input, expectedRuntimeEpoch = "") {
975
951
  const session = this.sessions.get(sessionId);
976
- if (!session || !session.process || session.status === 'exited' || session.exitFinalizing === true) {
977
- throw new Error('Session not available');
952
+ if (!session || !session.process || session.status === "exited" || session.exitFinalizing === true) {
953
+ throw new Error("Session not available");
978
954
  }
979
955
  if (session.rotationFrozen === true) {
980
- throw new Error('Terminal session is frozen for native PTY host rotation');
956
+ throw new Error("Terminal session is frozen for native PTY host rotation");
981
957
  }
982
958
  if (expectedRuntimeEpoch && expectedRuntimeEpoch !== session.runtimeEpoch) {
983
- return { status: 'input-rejected', reason: 'runtime-epoch-mismatch' };
959
+ return { status: "input-rejected", reason: "runtime-epoch-mismatch" };
984
960
  }
985
961
  session.process.write(terminalInputToPtyString(input));
986
962
  session.lastActivityAt = Date.now();
987
- this.emitSessionEvent('session-activity', {
963
+ this.emitSessionEvent("session-activity", {
988
964
  sessionId,
989
965
  lastActivityAt: session.lastActivityAt,
990
- runtimeEpoch: session.runtimeEpoch,
966
+ runtimeEpoch: session.runtimeEpoch
991
967
  });
992
968
  return { sent: true };
993
969
  }
994
-
995
970
  async getSessionAttachCheckpoint(sessionId, client) {
996
971
  this.assertActiveController(client);
997
972
  const session = this.sessions.get(sessionId);
@@ -999,34 +974,35 @@ class NativePtyHost {
999
974
  const checkpoint = await captureTerminalAttachCheckpoint(session);
1000
975
  return this.sessions.get(sessionId) === session ? checkpoint : null;
1001
976
  }
1002
-
1003
977
  async resizeSession(sessionId, cols, rows) {
1004
978
  const session = this.sessions.get(sessionId);
1005
- if (!session || !session.process || session.status === 'exited' || session.exitFinalizing === true) {
1006
- return { status: 'resize-rejected', reason: 'session-unavailable', resized: false };
979
+ if (!session || !session.process || session.status === "exited" || session.exitFinalizing === true) {
980
+ return { status: "resize-rejected", reason: "session-unavailable", resized: false };
1007
981
  }
1008
982
  if (session.rotationFrozen === true) {
1009
- return { status: 'resize-rejected', reason: 'runtime-rotation', resized: false };
983
+ return { status: "resize-rejected", reason: "runtime-rotation", resized: false };
1010
984
  }
1011
-
1012
- const nextCols = normalizePositiveInteger(cols, session.previewCols || DEFAULT_COLS, 1, 1000);
1013
- const nextRows = normalizePositiveInteger(rows, session.previewRows || DEFAULT_ROWS, 1, 1000);
985
+ const nextCols = normalizePositiveInteger(cols, session.previewCols || DEFAULT_COLS, 1, 1e3);
986
+ const nextRows = normalizePositiveInteger(rows, session.previewRows || DEFAULT_ROWS, 1, 1e3);
1014
987
  if (nextCols === session.previewCols && nextRows === session.previewRows) {
1015
988
  return {
1016
- status: 'resize-committed',
989
+ status: "resize-committed",
1017
990
  resized: true,
1018
- unchanged: true,
991
+ unchanged: true
1019
992
  };
1020
993
  }
1021
-
994
+ const resize = session.process.resize;
995
+ if (!resize) {
996
+ return { status: "resize-rejected", reason: "session-unavailable", resized: false };
997
+ }
1022
998
  try {
1023
- session.process.resize(nextCols, nextRows);
999
+ resize.call(session.process, nextCols, nextRows);
1024
1000
  } catch (error) {
1025
1001
  return {
1026
- status: 'resize-rejected',
1027
- reason: 'pty-resize-failed',
1002
+ status: "resize-rejected",
1003
+ reason: "pty-resize-failed",
1028
1004
  resized: false,
1029
- error: error instanceof Error ? error.message : String(error || 'unknown PTY resize failure'),
1005
+ error: error instanceof Error ? error.message : String(error || "unknown PTY resize failure")
1030
1006
  };
1031
1007
  }
1032
1008
  session.stateRevision += 1;
@@ -1035,135 +1011,120 @@ class NativePtyHost {
1035
1011
  const runtimeEpoch = session.runtimeEpoch;
1036
1012
  session.previewCols = nextCols;
1037
1013
  session.previewRows = nextRows;
1038
-
1039
1014
  if (session.screenWorker) {
1040
1015
  const reducerCommit = session.screenWorker.resize(nextCols, nextRows, stateRevision);
1041
1016
  const publishedCommit = session.reducerCommitQueue.then(() => reducerCommit).then((screenState) => {
1042
1017
  if (this.sessions.get(sessionId) !== session) {
1043
- throw new Error('Terminal session was replaced during resize');
1018
+ throw new Error("Terminal session was replaced during resize");
1044
1019
  }
1045
- if (
1046
- screenState.runtimeEpoch !== runtimeEpoch ||
1047
- screenState.outputSeq !== outputSeq ||
1048
- screenState.stateRevision !== stateRevision
1049
- ) {
1050
- throw new Error('Terminal screen resize returned a non-authoritative state cut');
1020
+ if (screenState.runtimeEpoch !== runtimeEpoch || screenState.outputSeq !== outputSeq || screenState.stateRevision !== stateRevision) {
1021
+ throw new Error("Terminal screen resize returned a non-authoritative state cut");
1051
1022
  }
1052
- session.previewText = screenState.previewText || '';
1023
+ session.previewText = screenState.previewText || "";
1053
1024
  session.previewSnapshot = screenState.previewSnapshot || session.previewSnapshot;
1054
- session.renderOutput = typeof screenState.renderOutput === 'string'
1055
- ? screenState.renderOutput
1056
- : session.renderOutput;
1025
+ session.renderOutput = typeof screenState.renderOutput === "string" ? screenState.renderOutput : session.renderOutput;
1057
1026
  session.previewCols = screenState.cols || nextCols;
1058
1027
  session.previewRows = screenState.rows || nextRows;
1059
1028
  session.title = screenState.title || session.title;
1060
- this.emitSessionEvent('session-transition', {
1029
+ this.emitSessionEvent("session-transition", {
1061
1030
  sessionId,
1062
- kind: 'resize',
1063
- data: '',
1031
+ kind: "resize",
1032
+ data: "",
1064
1033
  runtimeEpoch: screenState.runtimeEpoch,
1065
1034
  outputSeq: screenState.outputSeq,
1066
1035
  stateRevision: screenState.stateRevision,
1067
1036
  cols: screenState.cols,
1068
- rows: screenState.rows,
1037
+ rows: screenState.rows
1069
1038
  });
1070
1039
  return screenState;
1071
1040
  });
1072
- session.reducerCommitQueue = publishedCommit.catch(error => {
1041
+ session.reducerCommitQueue = publishedCommit.catch((error) => {
1073
1042
  this.failSessionScreenState(session, error);
1074
1043
  });
1075
1044
  try {
1076
1045
  await publishedCommit;
1077
1046
  } catch {
1078
- return { status: 'resize-rejected', reason: 'screen-reducer-failed', resized: false };
1047
+ return { status: "resize-rejected", reason: "screen-reducer-failed", resized: false };
1079
1048
  }
1080
1049
  }
1081
1050
  if (this.sessions.get(sessionId) !== session) {
1082
- return { status: 'resize-rejected', reason: 'session-replaced', resized: false };
1051
+ return { status: "resize-rejected", reason: "session-replaced", resized: false };
1083
1052
  }
1084
-
1085
1053
  return {
1086
- status: 'resize-committed',
1054
+ status: "resize-committed",
1087
1055
  resized: true,
1088
1056
  unchanged: false,
1089
1057
  runtimeEpoch,
1090
1058
  outputSeq,
1091
1059
  stateRevision,
1092
1060
  cols: nextCols,
1093
- rows: nextRows,
1061
+ rows: nextRows
1094
1062
  };
1095
1063
  }
1096
-
1097
- async clearBuffer(sessionId, expectedRuntimeEpoch = '') {
1064
+ async clearBuffer(sessionId, expectedRuntimeEpoch = "") {
1098
1065
  const session = this.sessions.get(sessionId);
1099
- if (!session || session.status === 'exited' || session.exitFinalizing === true) {
1066
+ if (!session || session.status === "exited" || session.exitFinalizing === true) {
1100
1067
  return { cleared: false };
1101
1068
  }
1102
1069
  if (session.rotationFrozen === true) {
1103
- return { cleared: false, reason: 'runtime-rotation' };
1070
+ return { cleared: false, reason: "runtime-rotation" };
1104
1071
  }
1105
1072
  if (expectedRuntimeEpoch && expectedRuntimeEpoch !== session.runtimeEpoch) {
1106
- return { cleared: false, reason: 'runtime-epoch-mismatch' };
1073
+ return { cleared: false, reason: "runtime-epoch-mismatch" };
1107
1074
  }
1108
-
1109
- session.output = '';
1075
+ session.output = "";
1110
1076
  session.stateRevision += 1;
1111
1077
  const stateRevision = session.stateRevision;
1112
1078
  const outputSeq = session.outputSeq;
1113
1079
  const runtimeEpoch = session.runtimeEpoch;
1114
- session.renderOutput = '';
1115
- session.previewText = '';
1080
+ session.renderOutput = "";
1081
+ session.previewText = "";
1116
1082
  session.previewSnapshot = null;
1117
1083
  session.lastActivityAt = Date.now();
1118
-
1119
1084
  let exactState = {
1120
- renderOutput: '',
1085
+ renderOutput: "",
1121
1086
  runtimeEpoch,
1122
1087
  outputSeq,
1123
1088
  stateRevision,
1124
1089
  cols: session.previewCols,
1125
- rows: session.previewRows,
1090
+ rows: session.previewRows
1126
1091
  };
1127
1092
  if (session.screenWorker) {
1128
1093
  const reducerCommit = session.screenWorker.clear(stateRevision, outputSeq);
1129
1094
  const publishedCommit = session.reducerCommitQueue.then(() => reducerCommit).then((screenState) => {
1130
1095
  if (this.sessions.get(sessionId) !== session) {
1131
- throw new Error('Terminal session was replaced during clear');
1096
+ throw new Error("Terminal session was replaced during clear");
1132
1097
  }
1133
- if (
1134
- screenState.runtimeEpoch !== runtimeEpoch ||
1135
- screenState.outputSeq !== outputSeq ||
1136
- screenState.stateRevision !== stateRevision
1137
- ) {
1138
- throw new Error('Terminal screen clear returned a non-authoritative state cut');
1098
+ if (screenState.runtimeEpoch !== runtimeEpoch || screenState.outputSeq !== outputSeq || screenState.stateRevision !== stateRevision) {
1099
+ throw new Error("Terminal screen clear returned a non-authoritative state cut");
1139
1100
  }
1140
- session.renderOutput = typeof screenState.renderOutput === 'string' ? screenState.renderOutput : '';
1141
- session.previewText = screenState.previewText || '';
1101
+ session.renderOutput = typeof screenState.renderOutput === "string" ? screenState.renderOutput : "";
1102
+ session.previewText = screenState.previewText || "";
1142
1103
  session.previewSnapshot = screenState.previewSnapshot || null;
1143
1104
  session.previewCols = screenState.cols || session.previewCols;
1144
1105
  session.previewRows = screenState.rows || session.previewRows;
1145
1106
  exactState = screenState;
1146
1107
  if (screenState.title && screenState.title !== session.title) {
1147
1108
  session.title = screenState.title;
1148
- this.emitSessionEvent('session-title', {
1109
+ this.emitSessionEvent("session-title", {
1149
1110
  sessionId,
1150
1111
  title: session.title,
1151
- runtimeEpoch: session.runtimeEpoch,
1112
+ runtimeEpoch: session.runtimeEpoch
1152
1113
  });
1153
1114
  }
1154
- this.emitSessionEvent('session-transition', {
1115
+ this.emitSessionEvent("session-transition", {
1155
1116
  sessionId,
1156
- kind: 'clear',
1157
- data: '\x1b[2J\x1b[3J\x1b[H',
1117
+ kind: "clear",
1118
+ data: "\x1B[2J\x1B[3J\x1B[H",
1158
1119
  runtimeEpoch: screenState.runtimeEpoch,
1159
1120
  outputSeq: screenState.outputSeq,
1160
1121
  stateRevision: screenState.stateRevision,
1161
1122
  cols: screenState.cols,
1162
- rows: screenState.rows,
1123
+ rows: screenState.rows
1163
1124
  });
1164
1125
  return screenState;
1165
1126
  });
1166
- session.reducerCommitQueue = publishedCommit.catch(error => {
1127
+ session.reducerCommitQueue = publishedCommit.catch((error) => {
1167
1128
  this.failSessionScreenState(session, error);
1168
1129
  });
1169
1130
  try {
@@ -1172,20 +1133,20 @@ class NativePtyHost {
1172
1133
  return { cleared: false };
1173
1134
  }
1174
1135
  }
1175
- if (this.sessions.get(sessionId) !== session) return { cleared: false, reason: 'session-replaced' };
1176
- this.emitSessionEvent('session-preview', {
1136
+ if (this.sessions.get(sessionId) !== session) return { cleared: false, reason: "session-replaced" };
1137
+ this.emitSessionEvent("session-preview", {
1177
1138
  sessionId,
1178
1139
  previewText: session.previewText,
1179
1140
  cols: session.previewCols,
1180
1141
  rows: session.previewRows,
1181
1142
  previewSnapshot: session.previewSnapshot,
1182
1143
  title: session.title,
1183
- runtimeEpoch: session.runtimeEpoch,
1144
+ runtimeEpoch: session.runtimeEpoch
1184
1145
  });
1185
- this.emitSessionEvent('session-activity', {
1146
+ this.emitSessionEvent("session-activity", {
1186
1147
  sessionId,
1187
1148
  lastActivityAt: session.lastActivityAt,
1188
- runtimeEpoch: session.runtimeEpoch,
1149
+ runtimeEpoch: session.runtimeEpoch
1189
1150
  });
1190
1151
  return {
1191
1152
  cleared: true,
@@ -1193,34 +1154,29 @@ class NativePtyHost {
1193
1154
  outputSeq: exactState.outputSeq,
1194
1155
  stateRevision: exactState.stateRevision,
1195
1156
  cols: exactState.cols,
1196
- rows: exactState.rows,
1157
+ rows: exactState.rows
1197
1158
  };
1198
1159
  }
1199
-
1200
1160
  async killSession(sessionId) {
1201
1161
  const session = this.sessions.get(sessionId);
1202
1162
  if (!session || !session.process) return { killed: false };
1203
- if (session.status === 'exited') return { killed: false };
1204
- session.status = 'stopping';
1163
+ if (session.status === "exited") return { killed: false };
1164
+ session.status = "stopping";
1205
1165
  session.killRequestedAt = Date.now();
1206
- session.process.kill('SIGTERM');
1166
+ session.process.kill("SIGTERM");
1207
1167
  const timer = setTimeout(() => {
1208
1168
  const latest = this.sessions.get(sessionId);
1209
- if (latest !== session || latest.status === 'exited' || !latest.process) return;
1210
- latest.process.kill('SIGKILL');
1169
+ if (latest !== session || latest.status === "exited" || !latest.process) return;
1170
+ latest.process.kill("SIGKILL");
1211
1171
  }, 1500);
1212
- if (typeof timer.unref === 'function') timer.unref();
1172
+ if (typeof timer.unref === "function") timer.unref();
1213
1173
  return { killed: true };
1214
1174
  }
1215
-
1216
1175
  async getSessionState(sessionId) {
1217
1176
  const session = this.sessions.get(sessionId);
1218
1177
  if (!session) return null;
1219
- // Cut the raw output before the async worker request. The screen worker
1220
- // receives every append with its output sequence and reports the exact
1221
- // sequence represented by its serialized terminal state.
1222
1178
  const snapshotOutput = session.output;
1223
- const fallbackPreviewText = session.previewText || snapshotOutput.slice(-2000);
1179
+ const fallbackPreviewText = session.previewText || snapshotOutput.slice(-2e3);
1224
1180
  const fallbackPreviewSnapshot = session.previewSnapshot;
1225
1181
  const fallbackPreviewCols = session.previewCols;
1226
1182
  const fallbackPreviewRows = session.previewRows;
@@ -1229,8 +1185,7 @@ class NativePtyHost {
1229
1185
  const checkpoint = stateProofAvailable ? await captureTerminalAttachCheckpoint(session) : null;
1230
1186
  if (this.sessions.get(sessionId) !== session) return null;
1231
1187
  const title = checkpoint ? checkpoint.title : fallbackTitle;
1232
- const previewText = checkpoint ? checkpoint.previewText : (stateProofAvailable ? fallbackPreviewText : '');
1233
-
1188
+ const previewText = checkpoint ? checkpoint.previewText : stateProofAvailable ? fallbackPreviewText : "";
1234
1189
  return {
1235
1190
  sessionId: session.id,
1236
1191
  status: session.status,
@@ -1239,9 +1194,9 @@ class NativePtyHost {
1239
1194
  outputSeq: checkpoint?.outputSeq ?? null,
1240
1195
  stateRevision: checkpoint?.stateRevision ?? null,
1241
1196
  stateProofAvailable,
1242
- renderOutput: checkpoint ? checkpoint.renderOutput : (stateProofAvailable ? snapshotOutput : ''),
1197
+ renderOutput: checkpoint ? checkpoint.renderOutput : stateProofAvailable ? snapshotOutput : "",
1243
1198
  previewText,
1244
- previewSnapshot: checkpoint ? checkpoint.previewSnapshot : (stateProofAvailable ? fallbackPreviewSnapshot : null),
1199
+ previewSnapshot: checkpoint ? checkpoint.previewSnapshot : stateProofAvailable ? fallbackPreviewSnapshot : null,
1245
1200
  previewCols: checkpoint ? checkpoint.cols : fallbackPreviewCols,
1246
1201
  previewRows: checkpoint ? checkpoint.rows : fallbackPreviewRows,
1247
1202
  title,
@@ -1249,8 +1204,8 @@ class NativePtyHost {
1249
1204
  startedAt: session.startedAt,
1250
1205
  exitedAt: session.exitedAt || null,
1251
1206
  terminalBusy: session.terminalBusy,
1252
- shellCommand: session.shellCommand || '',
1253
- shellLastCommand: session.shellLastCommand || '',
1207
+ shellCommand: session.shellCommand || "",
1208
+ shellLastCommand: session.shellLastCommand || "",
1254
1209
  shellCommandStartedAt: session.shellCommandStartedAt ?? null,
1255
1210
  shellLastCommandStartedAt: session.shellLastCommandStartedAt ?? null,
1256
1211
  shellLastCommandFinishedAt: session.shellLastCommandFinishedAt ?? null,
@@ -1269,138 +1224,115 @@ class NativePtyHost {
1269
1224
  shellCommandStartedAt: session.shellCommandStartedAt,
1270
1225
  shellLastCommandStartedAt: session.shellLastCommandStartedAt,
1271
1226
  shellLastCommandFinishedAt: session.shellLastCommandFinishedAt,
1272
- shellLastCommandDurationMs: session.shellLastCommandDurationMs,
1273
- }),
1227
+ shellLastCommandDurationMs: session.shellLastCommandDurationMs
1228
+ })
1274
1229
  };
1275
1230
  }
1276
-
1277
1231
  async getSessionPreview(sessionId) {
1278
1232
  const state = await this.getSessionState(sessionId);
1279
- return state ? state.previewText || '' : '';
1233
+ return state && typeof state.previewText === "string" ? state.previewText : "";
1280
1234
  }
1281
-
1282
1235
  async recoverSessions() {
1283
1236
  const recovered = [];
1284
1237
  for (const session of this.sessions.values()) {
1285
- if (session.status === 'exited') continue;
1238
+ if (session.status === "exited") continue;
1286
1239
  recovered.push({
1287
1240
  agentId: session.id,
1288
1241
  metadata: session.metadata || {},
1289
- state: await this.getSessionState(session.id),
1242
+ state: await this.getSessionState(session.id)
1290
1243
  });
1291
1244
  }
1292
1245
  return recovered;
1293
1246
  }
1294
-
1295
1247
  serializeTerminalState(client = null) {
1296
1248
  if (client) this.assertActiveController(client);
1297
1249
  if (this.rotationPreparation) {
1298
- if (
1299
- client &&
1300
- this.rotationPreparation.controllerClient &&
1301
- this.rotationPreparation.controllerClient !== client
1302
- ) {
1303
- throw new Error('Native pty host rotation is owned by another controller');
1250
+ if (client && this.rotationPreparation.controllerClient && this.rotationPreparation.controllerClient !== client) {
1251
+ throw new Error("Native pty host rotation is owned by another controller");
1304
1252
  }
1305
1253
  return this.rotationPreparation.promise;
1306
1254
  }
1307
-
1308
1255
  const preparation = {
1309
1256
  token: crypto.randomUUID(),
1310
1257
  controllerClient: client,
1311
- phase: 'preparing',
1258
+ phase: "preparing",
1312
1259
  promise: null,
1313
- serializedTerminalState: '',
1260
+ serializedTerminalState: ""
1314
1261
  };
1315
1262
  this.rotationPreparation = preparation;
1316
1263
  preparation.promise = this.prepareSerializedTerminalState(preparation);
1317
1264
  return preparation.promise;
1318
1265
  }
1319
-
1320
1266
  async prepareSerializedTerminalState(preparation) {
1321
1267
  const entries = [];
1322
1268
  try {
1323
1269
  await Promise.allSettled([...this.activeControllerMutations]);
1324
1270
  if (this.rotationPreparation !== preparation) {
1325
- throw new Error('Native pty host rotation preparation was cancelled');
1271
+ throw new Error("Native pty host rotation preparation was cancelled");
1326
1272
  }
1327
-
1328
- const liveSessions = [...this.sessions.values()]
1329
- .filter(session => session.status === 'running' && session.stateProofAvailable !== false);
1273
+ const liveSessions = [...this.sessions.values()].filter((session) => session.status === "running" && session.stateProofAvailable !== false);
1330
1274
  for (const session of liveSessions) {
1331
1275
  session.rotationFrozen = true;
1332
1276
  const flowError = setTerminalExternalFlowControlBlocked(
1333
1277
  session.reducerFlowControl,
1334
1278
  session.process,
1335
- true,
1279
+ true
1336
1280
  );
1337
1281
  if (flowError) throw flowError;
1338
1282
  }
1339
-
1340
1283
  for (const session of liveSessions) {
1341
1284
  await session.reducerCommitQueue;
1342
1285
  const state = await this.getSessionState(session.id);
1343
1286
  const current = this.sessions.get(session.id);
1344
- if (
1345
- current !== session ||
1346
- session.status !== 'running' ||
1347
- state?.status !== 'running'
1348
- ) {
1287
+ if (current !== session || session.status !== "running" || state?.status !== "running") {
1349
1288
  continue;
1350
1289
  }
1351
- if (
1352
- !state ||
1353
- typeof state.renderOutput !== 'string' ||
1354
- !Number.isFinite(state.outputSeq) ||
1355
- !Number.isFinite(state.stateRevision)
1356
- ) {
1290
+ if (!state || typeof state.renderOutput !== "string" || !Number.isFinite(state.outputSeq) || !Number.isFinite(state.stateRevision)) {
1357
1291
  throw new Error(`Cannot serialize terminal ${session.id} without an exact reducer checkpoint`);
1358
1292
  }
1359
1293
  entries.push({
1360
1294
  id: session.id,
1361
1295
  metadata: session.metadata || {},
1362
1296
  processDetails: {
1363
- cwd: session.shellCwd || session.cwd || '',
1364
- title: state.title || session.title || '',
1297
+ cwd: session.shellCwd || session.cwd || "",
1298
+ title: state.title || session.title || ""
1365
1299
  },
1366
1300
  processLaunchConfig: {
1367
- command: session.command || '',
1301
+ command: session.command || "",
1368
1302
  args: session.args || [],
1369
- category: session.metadata?.category || '',
1303
+ category: session.metadata?.category || ""
1370
1304
  },
1371
1305
  replayEvent: {
1372
1306
  events: [{
1373
1307
  data: state.renderOutput,
1374
1308
  cols: state.previewCols || session.previewCols || DEFAULT_COLS,
1375
- rows: state.previewRows || session.previewRows || DEFAULT_ROWS,
1376
- }],
1309
+ rows: state.previewRows || session.previewRows || DEFAULT_ROWS
1310
+ }]
1377
1311
  },
1378
- timestamp: Date.now(),
1312
+ timestamp: Date.now()
1379
1313
  });
1380
1314
  }
1381
1315
  const serializedTerminalState = serializeTerminalState(entries);
1382
- preparation.phase = 'prepared';
1316
+ preparation.phase = "prepared";
1383
1317
  preparation.serializedTerminalState = serializedTerminalState;
1384
1318
  return {
1385
1319
  preparationToken: preparation.token,
1386
- serializedTerminalState,
1320
+ serializedTerminalState
1387
1321
  };
1388
1322
  } catch (error) {
1389
1323
  this.resumePreparedTerminalState(preparation.token);
1390
1324
  throw error;
1391
1325
  }
1392
1326
  }
1393
-
1394
- resumeTerminalState(client = null, preparationToken = '') {
1327
+ resumeTerminalState(client = null, preparationToken = "") {
1395
1328
  if (client) this.assertActiveController(client);
1396
1329
  return this.resumePreparedTerminalState(preparationToken);
1397
1330
  }
1398
-
1399
- resumePreparedTerminalState(preparationToken = '') {
1331
+ resumePreparedTerminalState(preparationToken = "") {
1400
1332
  const preparation = this.rotationPreparation;
1401
1333
  if (!preparation) return { resumed: 0 };
1402
1334
  if (preparationToken && preparation.token !== preparationToken) {
1403
- throw new Error('Native pty rotation preparation token does not match');
1335
+ throw new Error("Native pty rotation preparation token does not match");
1404
1336
  }
1405
1337
  let resumed = 0;
1406
1338
  for (const session of this.sessions.values()) {
@@ -1409,7 +1341,7 @@ class NativePtyHost {
1409
1341
  const flowError = setTerminalExternalFlowControlBlocked(
1410
1342
  session.reducerFlowControl,
1411
1343
  session.process,
1412
- false,
1344
+ false
1413
1345
  );
1414
1346
  if (flowError) {
1415
1347
  this.failSessionScreenState(session, flowError);
@@ -1422,47 +1354,34 @@ class NativePtyHost {
1422
1354
  }
1423
1355
  return { resumed };
1424
1356
  }
1425
-
1426
1357
  async updateSessionMetadata(sessionId, patch) {
1427
1358
  const session = this.sessions.get(sessionId);
1428
1359
  if (!session) return null;
1429
1360
  session.metadata = {
1430
- ...(session.metadata || {}),
1431
- ...(patch || {}),
1361
+ ...session.metadata || {},
1362
+ ...patch || {},
1432
1363
  agentId: sessionId,
1433
- engineName: 'native',
1364
+ engineName: "native"
1434
1365
  };
1435
1366
  return session.metadata;
1436
1367
  }
1437
-
1438
- shutdownHost(client, rawIdentity = {}, preparationToken = '') {
1368
+ shutdownHost(client, rawIdentity = {}, preparationToken = "") {
1439
1369
  const requestedGeneration = Math.floor(Number(rawIdentity.generation));
1440
- const requestedId = typeof rawIdentity.id === 'string' ? rawIdentity.id : '';
1370
+ const requestedId = typeof rawIdentity.id === "string" ? rawIdentity.id : "";
1441
1371
  const active = this.activeControllerIdentity;
1442
- const activeClient = client
1443
- && client === this.activeControllerClient
1444
- && active
1445
- && client.controllerId === active.id
1446
- && client.controllerGeneration === active.generation;
1447
- const newerController = requestedId
1448
- && Number.isFinite(requestedGeneration)
1449
- && requestedGeneration > 0
1450
- && (!active || requestedGeneration > active.generation);
1372
+ const activeClient = client && client === this.activeControllerClient && active && client.controllerId === active.id && client.controllerGeneration === active.generation;
1373
+ const newerController = requestedId && Number.isFinite(requestedGeneration) && requestedGeneration > 0 && (!active || requestedGeneration > active.generation);
1451
1374
  if (!activeClient && !newerController) {
1452
- throw new Error('Native pty shutdown requires the active or a newer controller');
1375
+ throw new Error("Native pty shutdown requires the active or a newer controller");
1453
1376
  }
1454
1377
  const preparation = this.rotationPreparation;
1455
1378
  if (preparation) {
1456
- if (
1457
- preparation.phase !== 'prepared' ||
1458
- !preparationToken ||
1459
- preparationToken !== preparation.token
1460
- ) {
1461
- throw new Error('Native pty shutdown requires the prepared rotation token');
1379
+ if (preparation.phase !== "prepared" || !preparationToken || preparationToken !== preparation.token) {
1380
+ throw new Error("Native pty shutdown requires the prepared rotation token");
1462
1381
  }
1463
- preparation.phase = 'committing';
1382
+ preparation.phase = "committing";
1464
1383
  } else if (this.hasLiveSessions()) {
1465
- throw new Error('Native pty shutdown requires a prepared terminal checkpoint');
1384
+ throw new Error("Native pty shutdown requires a prepared terminal checkpoint");
1466
1385
  }
1467
1386
  setImmediate(() => {
1468
1387
  this.dispose().finally(() => {
@@ -1471,26 +1390,21 @@ class NativePtyHost {
1471
1390
  });
1472
1391
  return { shuttingDown: true };
1473
1392
  }
1474
-
1475
1393
  async disposeSessionProcess(session) {
1476
- if (!session || !session.process || session.status === 'exited') return;
1477
- session.status = 'stopping';
1394
+ if (!session || !session.process || session.status === "exited") return;
1395
+ session.status = "stopping";
1478
1396
  try {
1479
- session.process.kill('SIGTERM');
1397
+ session.process.kill("SIGTERM");
1480
1398
  } catch {
1481
1399
  return;
1482
1400
  }
1483
-
1484
- await new Promise(resolve => setTimeout(resolve, DISPOSE_PTY_GRACE_MS));
1485
- if (session.status === 'exited') return;
1486
-
1401
+ await new Promise((resolve) => setTimeout(resolve, DISPOSE_PTY_GRACE_MS));
1402
+ if (session.status === "exited") return;
1487
1403
  try {
1488
- session.process.kill('SIGKILL');
1404
+ session.process.kill("SIGKILL");
1489
1405
  } catch {
1490
- // ignore dispose races
1491
1406
  }
1492
1407
  }
1493
-
1494
1408
  async dispose() {
1495
1409
  if (this.disposed) return;
1496
1410
  this.disposed = true;
@@ -1499,54 +1413,51 @@ class NativePtyHost {
1499
1413
  this.ownerCheckTimer = null;
1500
1414
  }
1501
1415
  this.cancelIdleExit();
1502
- await Promise.allSettled([...this.sessions.values()].map(session => this.disposeSessionProcess(session)));
1416
+ await Promise.allSettled([...this.sessions.values()].map((session) => this.disposeSessionProcess(session)));
1503
1417
  for (const session of this.sessions.values()) {
1504
1418
  cleanupShellBusyIntegration(session.shellBusyIntegration);
1505
1419
  if (session.screenWorker) {
1506
- await session.screenWorker.dispose().catch(() => {});
1420
+ await session.screenWorker.dispose().catch(() => {
1421
+ });
1507
1422
  }
1508
1423
  }
1509
1424
  await this.screenWorkerPool.dispose();
1510
- if (this.server && process.platform !== 'win32') {
1425
+ if (this.server && process.platform !== "win32") {
1511
1426
  try {
1512
1427
  const currentSocketIdentity = socketIdentity(this.socketPath);
1513
1428
  if (sameSocketIdentity(currentSocketIdentity, this.boundSocketIdentity)) {
1514
- // Unlink while this listener is still active. A competing host cannot
1515
- // replace an active socket between the ownership check and unlink.
1516
1429
  fs.unlinkSync(this.socketPath);
1517
1430
  }
1518
1431
  } catch (error) {
1519
- if (!error || error.code !== 'ENOENT') throw error;
1432
+ if (errorCode(error) !== "ENOENT") throw error;
1520
1433
  }
1521
1434
  this.boundSocketIdentity = null;
1522
1435
  }
1523
1436
  if (this.server) {
1524
- await new Promise(resolve => this.server.close(() => resolve()));
1437
+ await new Promise((resolve) => this.server?.close(() => resolve()));
1525
1438
  this.server = null;
1526
1439
  }
1527
1440
  }
1528
1441
  }
1529
-
1530
1442
  function startNativePtyHostProcess() {
1531
1443
  const host = new NativePtyHost();
1532
- host.start().catch(error => {
1444
+ host.start().catch((error) => {
1533
1445
  console.error(error && error.stack ? error.stack : error);
1534
1446
  process.exit(1);
1535
1447
  });
1536
-
1537
- process.on('SIGTERM', () => {
1448
+ process.on("SIGTERM", () => {
1538
1449
  host.dispose().finally(() => process.exit(0));
1539
1450
  });
1540
- process.on('SIGINT', () => {
1451
+ process.on("SIGINT", () => {
1541
1452
  host.dispose().finally(() => process.exit(0));
1542
1453
  });
1543
-
1544
1454
  return host;
1545
1455
  }
1546
-
1547
1456
  if (require.main === module) {
1548
1457
  startNativePtyHostProcess();
1549
1458
  }
1550
-
1551
- module.exports = NativePtyHost;
1552
- module.exports.startNativePtyHostProcess = startNativePtyHostProcess;
1459
+ // Annotate the CommonJS export names for ESM import in node:
1460
+ 0 && (module.exports = {
1461
+ NativePtyHost,
1462
+ startNativePtyHostProcess
1463
+ });