@aiworker/web 1.22.0 → 1.23.0

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 (415) hide show
  1. package/README.md +4 -4
  2. package/bin/cli.js +14 -4
  3. package/bin/cli.test.js +165 -73
  4. package/bin/lib/cli-api-target.js +2 -2
  5. package/bin/lib/cli-args.js +119 -119
  6. package/bin/lib/cli-lifecycle.js +9 -9
  7. package/bin/lib/cli-network.js +3 -3
  8. package/bin/lib/cli-paths.js +3 -3
  9. package/bin/lib/cli-ports.js +2 -2
  10. package/bin/lib/cli-process.js +4 -4
  11. package/bin/lib/cli-startup.js +64 -15
  12. package/bin/lib/cli-tunnel-profiles.js +1 -1
  13. package/bin/lib/cli-tunnel-utils.js +2 -2
  14. package/bin/lib/commands-connect-url.js +19 -19
  15. package/bin/lib/commands-lifecycle.js +27 -27
  16. package/bin/lib/commands-logs.js +5 -5
  17. package/bin/lib/commands-models.js +1 -1
  18. package/bin/lib/commands-projects.js +1 -1
  19. package/bin/lib/commands-schedule.js +1 -1
  20. package/bin/lib/commands-serve.js +29 -29
  21. package/bin/lib/commands-session.js +1 -1
  22. package/bin/lib/commands-startup.js +45 -10
  23. package/bin/lib/commands-status.js +1 -1
  24. package/bin/lib/commands-tunnel.js +40 -40
  25. package/bin/lib/commands-update.js +1 -1
  26. package/bin/lib/commands-update.test.js +6 -6
  27. package/dist/assets/{CodeMirrorCommentWidgets-B3u7UIcE.js → CodeMirrorCommentWidgets-B1Gizi3a.js} +1 -1
  28. package/dist/assets/DiffView-DXjVsEOw.js +4 -0
  29. package/dist/assets/DirtyBranchSwitchDialog-aig6zJjg.js +1 -0
  30. package/dist/assets/FilesView-CJdG0-wk.js +17 -0
  31. package/dist/assets/GitView-BXsS3jG6.js +6 -0
  32. package/dist/assets/{JsonTreeView-B0BdZEHG.js → JsonTreeView-D_hpXBnF.js} +1 -1
  33. package/dist/assets/LinearIssuesView-BoUiLQG1.js +1 -0
  34. package/dist/assets/MarkdownImageGallery-B_B_a9KS.js +1 -0
  35. package/dist/assets/MarkdownRendererImpl-xgyyLiBM.js +12 -0
  36. package/dist/assets/OnboardingScreen-DIp84OWt.js +1 -0
  37. package/dist/assets/{PatchDiff-DvfqDql7.js → PatchDiff-8p6nLJaU.js} +1 -1
  38. package/dist/assets/PierreDiffViewer-DjC8nTk9.js +161 -0
  39. package/dist/assets/PlanView-CJTmOF6I.js +5 -0
  40. package/dist/assets/SettingsView-DD-JqIJx.js +28 -0
  41. package/dist/assets/{SettingsWindow-i45U1Cy1.js → SettingsWindow-Cpb4hM6q.js} +1 -1
  42. package/dist/assets/SymbolsNerdFontMono-Regular-aK5vsLov.woff2 +0 -0
  43. package/dist/assets/{ToolOutputDialog-BTwyh5OF.js → ToolOutputDialog-DfaKZXNU.js} +6 -6
  44. package/dist/assets/ToolPartDiffPreview-DtZLzrSK.js +8 -0
  45. package/dist/assets/{VirtualizedFileDiff-DD8lDVej.js → VirtualizedFileDiff-C8i9HpTB.js} +1 -1
  46. package/dist/assets/WalkthroughView-DeKMvZlL.js +4 -0
  47. package/dist/assets/{computeVirtualFileMetrics-BMIRgMHv.js → computeVirtualFileMetrics-BBI7ZwrC.js} +2 -2
  48. package/dist/assets/contextPanelEmbeddedChat-BdA6bdky.js +1 -0
  49. package/dist/assets/de-DCrsQ5zS.js +10 -0
  50. package/dist/assets/es-Dwv4YfH6.js +16 -0
  51. package/dist/assets/fr-BCWg4Cnx.js +14 -0
  52. package/dist/assets/ghostty-vt-DdA0Zryv.wasm +0 -0
  53. package/dist/assets/index-B68h5S5B.css +1 -0
  54. package/dist/assets/index-DVwMarHI.js +39 -0
  55. package/dist/assets/{index-CODwDsU_.js → index-jXSK5RrB.js} +1 -1
  56. package/dist/assets/{isDiffPlainText-BqayB8d3.js → isDiffPlainText-B_x_rVon.js} +1 -1
  57. package/dist/assets/{isFilePlainText-QfoiKJNY.js → isFilePlainText-DW_xMer1.js} +1 -1
  58. package/dist/assets/ja-Bruw3RYC.js +16 -0
  59. package/dist/assets/ko-idhXGF8j.js +16 -0
  60. package/dist/assets/main-BkyaQhJX.js +930 -0
  61. package/dist/assets/{main-C5DVOxc6.js → main-CzdPuL0s.js} +2 -2
  62. package/dist/assets/markdownCore-Cn4Tyejo.js +5 -0
  63. package/dist/assets/{miniChat-DUJ4irZc.js → miniChat-CCkqKOLp.js} +2 -2
  64. package/dist/assets/{mobile-CcaxB0n4.js → mobile-mvxTo055.js} +2 -2
  65. package/dist/assets/patchFileDiff-Csk4iNUi.js +13 -0
  66. package/dist/assets/pl-CGZWUqZJ.js +16 -0
  67. package/dist/assets/pt-BR-B8V7u7tA.js +16 -0
  68. package/dist/assets/renderElectronMiniChatApp-BxoyujA5.js +2 -0
  69. package/dist/assets/renderMobileApp-B48CsP4r.js +25 -0
  70. package/dist/assets/runtimeEndpointReset-Bg5gx18H.js +17 -0
  71. package/dist/assets/scheduledTasksApi-BW0Etzwz.js +1 -0
  72. package/dist/assets/{shikiHighlight-DYOWuY65.js → shikiHighlight-CRl0SUC9.js} +1 -1
  73. package/dist/assets/surface-oMvhsog3.js +1 -0
  74. package/dist/assets/tr-BK_KiO2V.js +7 -0
  75. package/dist/assets/uk-CG74xV8N.js +16 -0
  76. package/dist/assets/useAppFontEffects-CPKeOpUo.js +7284 -0
  77. package/dist/assets/useGitComparison-DH220WmO.js +1 -0
  78. package/dist/assets/useInlineCommentController-BiWz4AVb.js +2 -0
  79. package/dist/assets/useRootScrollLock-BS4-kr7P.js +1 -0
  80. package/dist/assets/{vendor-base-ui-BeMD8X_w.js → vendor-base-ui-DdV8vhHn.js} +1 -1
  81. package/dist/assets/{vendor-codemirror-language-data-DFZik9R2.js → vendor-codemirror-language-data-Bg4AEeCr.js} +2 -2
  82. package/dist/assets/vendor-zod-BPAANqvH.js +39 -0
  83. package/dist/assets/{web-components-Bhg83tjf.js → web-components-z7hfnS8n.js} +1 -1
  84. package/dist/assets/zh-CN-T4AXofZI.js +16 -0
  85. package/dist/assets/zh-TW-Dte40sxp.js +16 -0
  86. package/dist/index.html +21 -59
  87. package/dist/mini-chat.html +7 -7
  88. package/dist/mobile.html +5 -5
  89. package/package.json +3 -4
  90. package/server/index.d.ts +15 -0
  91. package/server/index.js +158 -89
  92. package/server/lib/agent-memory/actions.js +2 -2
  93. package/server/lib/agent-memory/feature-flag.js +1 -1
  94. package/server/lib/agent-memory/feature-flag.test.js +8 -8
  95. package/server/lib/agent-memory/routes.js +2 -2
  96. package/server/lib/agent-tool/runtime.js +37 -54
  97. package/server/lib/agent-tool/runtime.test.js +59 -59
  98. package/server/lib/browser-control/broker.js +3 -3
  99. package/server/lib/browser-control/broker.test.js +1 -1
  100. package/server/lib/changelog/update-notes.js +82 -0
  101. package/server/lib/changelog/update-notes.test.js +59 -0
  102. package/server/lib/client-auth/remote-clients.test.js +5 -5
  103. package/server/lib/context-obligatory/runtime.js +7 -7
  104. package/server/lib/context-obligatory/runtime.test.js +5 -5
  105. package/server/lib/data-dir-migration.js +40 -0
  106. package/server/lib/data-dir-migration.test.js +59 -0
  107. package/server/lib/dev-servers/parse.js +2 -2
  108. package/server/lib/dev-servers/parse.test.js +2 -2
  109. package/server/lib/dev-servers/routes.js +1 -1
  110. package/server/lib/dev-tunnel/DOCUMENTATION.md +13 -4
  111. package/server/lib/dev-tunnel/client.js +1 -1
  112. package/server/lib/dev-tunnel/runtime.js +5 -5
  113. package/server/lib/dev-tunnel/tunnel.test.js +21 -1
  114. package/server/lib/dictation/local/model-catalog.js +1 -1
  115. package/server/lib/dictation/local/worker-process.js +2 -2
  116. package/server/lib/event-stream/directory-ws-bridge.js +1 -1
  117. package/server/lib/event-stream/global-ws-bridge.js +1 -1
  118. package/server/lib/event-stream/protocol.test.js +4 -4
  119. package/server/lib/event-stream/runtime.test.js +18 -18
  120. package/server/lib/fs/DOCUMENTATION.md +5 -0
  121. package/server/lib/fs/routes.js +68 -26
  122. package/server/lib/fs/routes.test.js +208 -22
  123. package/server/lib/git/DOCUMENTATION.md +32 -6
  124. package/server/lib/git/identity-storage.js +1 -1
  125. package/server/lib/git/issue-2746-longpaths.test.js +4 -4
  126. package/server/lib/git/routes.js +41 -5
  127. package/server/lib/git/service.js +300 -224
  128. package/server/lib/git/service.test.js +596 -28
  129. package/server/lib/github/auth.js +8 -8
  130. package/server/lib/github/pr-status.js +1 -1
  131. package/server/lib/github/routes.js +2 -2
  132. package/server/lib/inherited-env.js +17 -10
  133. package/server/lib/inherited-env.test.js +10 -11
  134. package/server/lib/linear/auth.js +8 -8
  135. package/server/lib/linear/auth.test.js +24 -24
  136. package/server/lib/linear/issues.test.js +6 -6
  137. package/server/lib/linear/mapping.test.js +4 -4
  138. package/server/lib/linear/oauth.js +2 -2
  139. package/server/lib/linear/oauth.test.js +14 -14
  140. package/server/lib/linear/routes.js +5 -5
  141. package/server/lib/linear/routes.test.js +16 -16
  142. package/server/lib/linear/status-runtime.test.js +9 -9
  143. package/server/lib/linear/status.js +1 -1
  144. package/server/lib/linear/status.test.js +15 -15
  145. package/server/lib/linear/teams.test.js +4 -4
  146. package/server/lib/magic-prompts/routes.js +2 -2
  147. package/server/lib/mcp-reconnect/DOCUMENTATION.md +55 -0
  148. package/server/lib/mcp-reconnect/runtime.js +122 -0
  149. package/server/lib/mcp-reconnect/runtime.test.js +140 -0
  150. package/server/lib/message-queue/DOCUMENTATION.md +161 -0
  151. package/server/lib/message-queue/runtime.js +870 -0
  152. package/server/lib/message-queue/runtime.test.js +482 -0
  153. package/server/lib/network-defaults.js +23 -0
  154. package/server/lib/network-defaults.test.js +52 -0
  155. package/server/lib/notifications/apns-runtime.js +17 -17
  156. package/server/lib/notifications/apns-runtime.test.js +11 -11
  157. package/server/lib/notifications/emitter-runtime.js +1 -1
  158. package/server/lib/notifications/emitter-runtime.test.js +1 -1
  159. package/server/lib/notifications/push-runtime.js +7 -7
  160. package/server/lib/notifications/routes.js +1 -1
  161. package/server/lib/notifications/runtime.js +1 -1
  162. package/server/lib/openchamber-control/action-resolution.test.js +10 -10
  163. package/server/lib/openchamber-control/actions.js +24 -24
  164. package/server/lib/openchamber-control/error.js +4 -4
  165. package/server/lib/openchamber-control/routes.js +2 -2
  166. package/server/lib/openchamber-control/routes.test.js +5 -5
  167. package/server/lib/openchamber-control/screenshots.js +1 -1
  168. package/server/lib/openchamber-control/screenshots.test.js +1 -1
  169. package/server/lib/openchamber-control/service.js +50 -50
  170. package/server/lib/openchamber-control/service.test.js +5 -5
  171. package/server/lib/openchamber-sessions/routes.js +124 -22
  172. package/server/lib/openchamber-sessions/routes.test.js +99 -11
  173. package/server/lib/opencode/DOCUMENTATION.md +30 -6
  174. package/server/lib/opencode/auth.js +1 -1
  175. package/server/lib/opencode/bootstrap-runtime.js +8 -6
  176. package/server/lib/opencode/cli-options.js +8 -8
  177. package/server/lib/opencode/config-paths.test.js +116 -0
  178. package/server/lib/opencode/core-routes.js +8 -7
  179. package/server/lib/opencode/core-routes.test.js +11 -11
  180. package/server/lib/opencode/env-config.js +5 -5
  181. package/server/lib/opencode/env-config.test.js +7 -7
  182. package/server/lib/opencode/env-runtime.js +10 -10
  183. package/server/lib/opencode/env-runtime.test.js +18 -18
  184. package/server/lib/opencode/feature-routes-runtime.js +19 -12
  185. package/server/lib/opencode/hmr-state-runtime.js +2 -2
  186. package/server/lib/opencode/hmr-state-runtime.test.js +1 -1
  187. package/server/lib/opencode/input-history-scope.js +14 -0
  188. package/server/lib/opencode/lifecycle.js +8 -8
  189. package/server/lib/opencode/lifecycle.test.js +4 -4
  190. package/server/lib/opencode/managed-plugin-config.js +35 -0
  191. package/server/lib/opencode/managed-process-registry.js +3 -3
  192. package/server/lib/opencode/mcp-oauth-callback.test.js +2 -2
  193. package/server/lib/opencode/openchamber-routes.js +79 -19
  194. package/server/lib/opencode/openchamber-routes.test.js +139 -12
  195. package/server/lib/opencode/plugins.js +2 -2
  196. package/server/lib/opencode/plugins.test.js +1 -1
  197. package/server/lib/opencode/project-icon-routes.js +2 -2
  198. package/server/lib/opencode/project-icon-routes.test.js +1 -1
  199. package/server/lib/opencode/providers.js +44 -2
  200. package/server/lib/opencode/providers.test.js +131 -4
  201. package/server/lib/opencode/proxy.js +2 -2
  202. package/server/lib/opencode/pwa-manifest-routes.js +1 -1
  203. package/server/lib/opencode/restart-session-recovery.test.js +1 -1
  204. package/server/lib/opencode/routes-upgrade.test.js +4 -4
  205. package/server/lib/opencode/routes.js +19 -17
  206. package/server/lib/opencode/server-startup-runtime.js +6 -6
  207. package/server/lib/opencode/session-runtime.js +3 -3
  208. package/server/lib/opencode/session-runtime.test.js +6 -6
  209. package/server/lib/opencode/settings-files.js +214 -0
  210. package/server/lib/opencode/settings-helpers.js +116 -44
  211. package/server/lib/opencode/settings-helpers.test.js +354 -12
  212. package/server/lib/opencode/settings-registry.json +747 -0
  213. package/server/lib/opencode/settings-runtime.js +141 -20
  214. package/server/lib/opencode/settings-runtime.test.js +191 -4
  215. package/server/lib/opencode/shared.js +5 -2
  216. package/server/lib/opencode/shared.test.js +7 -7
  217. package/server/lib/opencode/shutdown-runtime.js +2 -0
  218. package/server/lib/opencode/skill-routes.js +2 -1
  219. package/server/lib/opencode/snippets.js +1 -2
  220. package/server/lib/opencode/startup-performance.js +1 -1
  221. package/server/lib/opencode/startup-performance.test.js +6 -6
  222. package/server/lib/opencode/static-routes-runtime.js +8 -8
  223. package/server/lib/opencode/static-routes-runtime.test.js +7 -7
  224. package/server/lib/opencode/upgrade-capability.js +2 -2
  225. package/server/lib/opencode/upgrade-capability.test.js +3 -3
  226. package/server/lib/package-manager.js +17 -42
  227. package/server/lib/package-manager.test.js +19 -19
  228. package/server/lib/permission-auto-accept/runtime.js +1 -1
  229. package/server/lib/project-context/DOCUMENTATION.md +25 -1
  230. package/server/lib/project-context/routes.http.test.js +18 -0
  231. package/server/lib/project-context/routes.js +16 -1
  232. package/server/lib/project-context/runtime.js +248 -23
  233. package/server/lib/project-context/runtime.test.js +106 -3
  234. package/server/lib/projects/DOCUMENTATION.md +76 -0
  235. package/server/lib/projects/project-config.js +144 -11
  236. package/server/lib/projects/project-id.js +17 -0
  237. package/server/lib/projects/project-setup.js +409 -0
  238. package/server/lib/projects/project-setup.test.js +557 -0
  239. package/server/lib/projects/routes.js +59 -0
  240. package/server/lib/quota/DOCUMENTATION.md +40 -2
  241. package/server/lib/quota/credentials/providers.js +4 -0
  242. package/server/lib/quota/credentials/store.js +4 -4
  243. package/server/lib/quota/credentials/store.test.js +8 -8
  244. package/server/lib/quota/index.js +1 -0
  245. package/server/lib/quota/providers/cline-pass.js +125 -0
  246. package/server/lib/quota/providers/cline-pass.test.js +103 -0
  247. package/server/lib/quota/providers/exe-dev.js +75 -0
  248. package/server/lib/quota/providers/exe-dev.test.js +47 -0
  249. package/server/lib/quota/providers/hyper.js +118 -0
  250. package/server/lib/quota/providers/hyper.test.js +126 -0
  251. package/server/lib/quota/providers/index.js +22 -0
  252. package/server/lib/quota/providers/neuralwatt.js +3 -6
  253. package/server/lib/quota/providers/neuralwatt.test.js +7 -7
  254. package/server/lib/quota/providers/ollama-cloud.js +32 -1
  255. package/server/lib/quota/providers/ollama-cloud.test.js +33 -1
  256. package/server/lib/quota/providers/opencode-go.js +3 -2
  257. package/server/lib/quota/providers/opencode-go.test.js +35 -6
  258. package/server/lib/quota/providers/openrouter.js +119 -24
  259. package/server/lib/quota/providers/openrouter.test.js +330 -0
  260. package/server/lib/quota/providers/xai.js +1 -1
  261. package/server/lib/quota/routes.js +2 -0
  262. package/server/lib/quota/utils/auth.js +1 -1
  263. package/server/lib/relay/DOCUMENTATION.md +56 -1
  264. package/server/lib/relay/cross-compat.test.js +26 -0
  265. package/server/lib/relay/downstream-scheduler.js +154 -0
  266. package/server/lib/relay/downstream-scheduler.test.js +162 -0
  267. package/server/lib/relay/e2ee.js +10 -8
  268. package/server/lib/relay/flow-control.test.js +177 -0
  269. package/server/lib/relay/host-client.js +38 -9
  270. package/server/lib/relay/host-client.test.js +9 -1
  271. package/server/lib/relay/host-lock.js +1 -1
  272. package/server/lib/relay/service.js +5 -5
  273. package/server/lib/relay/tunnel-codec.js +9 -0
  274. package/server/lib/relay/tunnel-host.js +69 -18
  275. package/server/lib/relay/tunnel-host.test.js +9 -1
  276. package/server/lib/scheduled-tasks/issue-2710-double-execution.test.js +2 -2
  277. package/server/lib/scheduled-tasks/routes.js +5 -5
  278. package/server/lib/scheduled-tasks/service.js +25 -25
  279. package/server/lib/scheduled-tasks/service.test.js +1 -1
  280. package/server/lib/security/bind-host.js +4 -4
  281. package/server/lib/security/bind-host.test.js +1 -1
  282. package/server/lib/security/request-security.js +11 -5
  283. package/server/lib/security/request-security.test.js +14 -14
  284. package/server/lib/session-assist/runtime.js +15 -18
  285. package/server/lib/session-folders/routes.js +2 -2
  286. package/server/lib/session-folders/routes.test.js +8 -8
  287. package/server/lib/session-goal/DOCUMENTATION.md +20 -7
  288. package/server/lib/session-goal/create.js +4 -2
  289. package/server/lib/session-goal/create.test.js +1 -1
  290. package/server/lib/session-goal/objectives.js +4 -4
  291. package/server/lib/session-goal/routes.js +1 -1
  292. package/server/lib/session-goal/runtime.js +91 -30
  293. package/server/lib/session-goal/runtime.test.js +428 -2
  294. package/server/lib/session-knowledge/runtime.js +11 -11
  295. package/server/lib/session-knowledge/runtime.test.js +5 -5
  296. package/server/lib/skills-catalog/DOCUMENTATION.md +1 -1
  297. package/server/lib/skills-catalog/cache.test.js +2 -2
  298. package/server/lib/skills-catalog/disk-cache.js +5 -5
  299. package/server/lib/skills-catalog/github-meta.test.js +2 -2
  300. package/server/lib/skills-catalog/install.js +3 -2
  301. package/server/lib/small-model/DOCUMENTATION.md +7 -2
  302. package/server/lib/small-model/call.js +18 -7
  303. package/server/lib/small-model/call.test.js +58 -6
  304. package/server/lib/small-model/index.js +12 -16
  305. package/server/lib/small-model/index.test.js +1 -1
  306. package/server/lib/small-model/resolve.js +1 -1
  307. package/server/lib/small-model/resolve.test.js +1 -1
  308. package/server/lib/small-model/routes.js +2 -1
  309. package/server/lib/small-model/runtime-providers.js +15 -7
  310. package/server/lib/small-model/runtime-providers.test.js +7 -1
  311. package/server/lib/system-prompt/runtime.js +3 -22
  312. package/server/lib/system-prompt/runtime.test.js +3 -3
  313. package/server/lib/terminal/DOCUMENTATION.md +14 -10
  314. package/server/lib/terminal/runtime.js +190 -31
  315. package/server/lib/terminal/runtime.test.js +572 -38
  316. package/server/lib/terminal/shells.js +20 -3
  317. package/server/lib/terminal/shells.test.js +33 -2
  318. package/server/lib/tts/base-url.js +3 -3
  319. package/server/lib/tts/routes.test.js +20 -20
  320. package/server/lib/ui-auth/ui-auth.js +12 -9
  321. package/server/lib/ui-auth/ui-auth.test.js +19 -3
  322. package/server/lib/ui-auth/ui-passkeys.js +7 -7
  323. package/server/lib/walkthrough/DOCUMENTATION.md +33 -14
  324. package/server/lib/walkthrough/index.js +1 -0
  325. package/server/lib/walkthrough/jobs.test.js +1 -1
  326. package/server/lib/walkthrough/language.test.js +1 -1
  327. package/server/lib/walkthrough/model-settings.js +11 -15
  328. package/server/lib/walkthrough/model-settings.test.js +1 -1
  329. package/server/lib/walkthrough/prompt.js +3 -1
  330. package/server/lib/walkthrough/pull-request.test.js +5 -5
  331. package/server/lib/walkthrough/reproduce-2607.test.js +2 -2
  332. package/server/lib/walkthrough/routes.test.js +26 -5
  333. package/server/lib/walkthrough/sources.js +23 -2
  334. package/server/lib/walkthrough/store.js +3 -3
  335. package/server/lib/walkthrough/store.test.js +1 -1
  336. package/server/opencode-proxy.test.js +2 -2
  337. package/server/proxy-headers.js +1 -1
  338. package/server/sse-routes.test.js +4 -4
  339. package/README.md.bak +0 -250
  340. package/bin/lib/DOCUMENTATION.md.bak +0 -163
  341. package/dist/assets/CommitSection-SGBCAW0w.js +0 -1
  342. package/dist/assets/DiffView-DluiRSla.js +0 -4
  343. package/dist/assets/FilesView-Bdq8ppva.js +0 -17
  344. package/dist/assets/GitView-Znn7VJcP.js +0 -6
  345. package/dist/assets/LinearIssuesView-jP-hXyac.js +0 -1
  346. package/dist/assets/MarkdownImageGallery-ClOvuZxC.js +0 -1
  347. package/dist/assets/MarkdownRendererImpl-Dt9mjTRN.js +0 -12
  348. package/dist/assets/OnboardingScreen-D2DG7K6b.js +0 -1
  349. package/dist/assets/PierreDiffViewer-B7m3f0d7.js +0 -157
  350. package/dist/assets/PlanView-B0F3BtPZ.js +0 -5
  351. package/dist/assets/SettingsView-BBW-S-gh.js +0 -28
  352. package/dist/assets/ToolPartDiffPreview-CUNe28U1.js +0 -8
  353. package/dist/assets/WalkthroughView-egQgxel3.js +0 -4
  354. package/dist/assets/contextPanelEmbeddedChat-C3fjDbdg.js +0 -1
  355. package/dist/assets/de-C1P6X5Rs.js +0 -9
  356. package/dist/assets/es-CDgaF1_t.js +0 -15
  357. package/dist/assets/fr-Bgxqvvz_.js +0 -13
  358. package/dist/assets/index-C57jT0Sa.css +0 -1
  359. package/dist/assets/index-DW_E3rnL.js +0 -7
  360. package/dist/assets/ja-BDRrEcNP.js +0 -15
  361. package/dist/assets/ko-Ba8sQV79.js +0 -15
  362. package/dist/assets/main-Bv5Pkngh.js +0 -928
  363. package/dist/assets/markdownCore-CZwzz5gw.js +0 -3
  364. package/dist/assets/patchFileDiff-yNfWXN4E.js +0 -10
  365. package/dist/assets/pl-Dioz29BI.js +0 -15
  366. package/dist/assets/pt-BR-DIs6ElwV.js +0 -15
  367. package/dist/assets/renderElectronMiniChatApp-BcKLHXKe.js +0 -2
  368. package/dist/assets/renderMobileApp-D9yQJy0L.js +0 -25
  369. package/dist/assets/runtimeEndpointReset-CjhvxLH0.js +0 -16
  370. package/dist/assets/scheduledTasksApi-DLW0mkQK.js +0 -1
  371. package/dist/assets/tr-DFnMlPxY.js +0 -6
  372. package/dist/assets/uk-ZrCNjM7Y.js +0 -15
  373. package/dist/assets/useAppFontEffects-CjrntdL0.js +0 -7310
  374. package/dist/assets/useInlineCommentController-CDEuSlPH.js +0 -2
  375. package/dist/assets/useRootScrollLock-BHYPkrLv.js +0 -1
  376. package/dist/assets/vendor-ghostty-web-CrxinGGH.js +0 -13
  377. package/dist/assets/vendor-zod-BLfBSgBh.js +0 -39
  378. package/dist/assets/zh-CN-iv_AV4EC.js +0 -15
  379. package/dist/assets/zh-TW-CzEUk38j.js +0 -15
  380. package/server/index.d.ts.bak +0 -39
  381. package/server/lib/agent-tool/DOCUMENTATION.md.bak +0 -119
  382. package/server/lib/browser-control/DOCUMENTATION.md.bak +0 -50
  383. package/server/lib/context-obligatory/DOCUMENTATION.md.bak +0 -19
  384. package/server/lib/dev-tunnel/DOCUMENTATION.md.bak +0 -57
  385. package/server/lib/dictation/DOCUMENTATION.md.bak +0 -100
  386. package/server/lib/event-stream/DOCUMENTATION.md.bak +0 -62
  387. package/server/lib/fs/DOCUMENTATION.md.bak +0 -47
  388. package/server/lib/git/DOCUMENTATION.md.bak +0 -193
  389. package/server/lib/github/DOCUMENTATION.md.bak +0 -183
  390. package/server/lib/linear/DOCUMENTATION.md.bak +0 -97
  391. package/server/lib/linear/OAUTH-BROKER-HANDOFF.md.bak +0 -91
  392. package/server/lib/markdown-image-grants/DOCUMENTATION.md.bak +0 -40
  393. package/server/lib/notifications/APNS.md.bak +0 -135
  394. package/server/lib/notifications/DOCUMENTATION.md.bak +0 -138
  395. package/server/lib/openchamber-control/DOCUMENTATION.md.bak +0 -64
  396. package/server/lib/opencode/DOCUMENTATION.md.bak +0 -431
  397. package/server/lib/opencode/managed-process-registry.d.ts.bak +0 -13
  398. package/server/lib/opencode/provider-env-aliases.d.ts.bak +0 -3
  399. package/server/lib/permission-auto-accept/DOCUMENTATION.md.bak +0 -34
  400. package/server/lib/project-context/DOCUMENTATION.md.bak +0 -160
  401. package/server/lib/quota/DOCUMENTATION.md.bak +0 -123
  402. package/server/lib/relay/DOCUMENTATION.md.bak +0 -100
  403. package/server/lib/scheduled-tasks/DOCUMENTATION.md.bak +0 -182
  404. package/server/lib/session-assist/DOCUMENTATION.md.bak +0 -66
  405. package/server/lib/session-goal/DOCUMENTATION.md.bak +0 -213
  406. package/server/lib/session-knowledge/DOCUMENTATION.md.bak +0 -89
  407. package/server/lib/skills-catalog/DOCUMENTATION.md.bak +0 -151
  408. package/server/lib/small-model/DOCUMENTATION.md.bak +0 -212
  409. package/server/lib/system-prompt/DOCUMENTATION.md.bak +0 -36
  410. package/server/lib/terminal/DOCUMENTATION.md.bak +0 -50
  411. package/server/lib/text/DOCUMENTATION.md.bak +0 -35
  412. package/server/lib/tts/DOCUMENTATION.md.bak +0 -147
  413. package/server/lib/tunnels/DOCUMENTATION.md.bak +0 -21
  414. package/server/lib/ui-auth/DOCUMENTATION.md.bak +0 -44
  415. package/server/lib/walkthrough/DOCUMENTATION.md.bak +0 -411
@@ -9,15 +9,77 @@ import {
9
9
  } from './terminal-ws-protocol.js';
10
10
  import { sanitizeTerminalHistoryChunk } from './history.js';
11
11
  import { consumeTerminalThemeQueries, terminalThemeModeReport } from './theme-response.js';
12
- import { createTerminalShellResolver, getTerminalShellLoginArgs, normalizeTerminalShell } from './shells.js';
13
- import { stripAppImageArgv0Leak, resolveLinuxPtyLaunch } from '../inherited-env.js';
12
+ import { buildTerminalShellLaunch, createTerminalShellResolver, normalizeTerminalShell } from './shells.js';
13
+ import { stripAppImageArgv0Leak, resolvePosixPtyLaunch } from '../inherited-env.js';
14
14
 
15
15
  const MAX_SESSIONS = 20;
16
16
  const MAX_HISTORY_BYTES = 512 * 1024;
17
17
  const MAX_INPUT_CHARS = 65_536;
18
18
  const IDLE_TIMEOUT_MS = 30 * 60 * 1000;
19
19
  const TERMINATION_GRACE_MS = 1000;
20
+ const INTERACTIVE_TERMINAL_MODE = 'interactive';
21
+ const COMMAND_TERMINAL_MODE = 'command';
22
+ // Error code the create/restart routes attach when the requested cwd no longer
23
+ // exists. Mirrored by `TERMINAL_CWD_MISSING_CODE` in packages/ui/src/lib/terminalApi.ts.
24
+ const TERMINAL_CWD_MISSING_CODE = 'TERMINAL_CWD_MISSING';
25
+ const TERMINAL_PURPOSE = Object.freeze({ type: 'terminal' });
26
+ const MAX_PURPOSE_ID_CHARS = 128;
27
+ const OBJECT_TAG = '[object Object]';
20
28
  const validateSize = (value, max) => Number.isInteger(value) && value >= 1 && value <= max;
29
+ const isString = (value) => String(value) === value;
30
+ const isObjectRecord = (value) => value != null && !Array.isArray(value) && Object.prototype.toString.call(value) === OBJECT_TAG;
31
+ const normalizeCreateMode = ({ mode, command }) => {
32
+ const normalizedMode = mode == null ? INTERACTIVE_TERMINAL_MODE : mode;
33
+ if (normalizedMode !== INTERACTIVE_TERMINAL_MODE && normalizedMode !== COMMAND_TERMINAL_MODE) throw new Error('Invalid terminal mode');
34
+ if (normalizedMode === INTERACTIVE_TERMINAL_MODE) {
35
+ if (command != null) throw new Error('Interactive terminal create does not accept a command');
36
+ return { mode: INTERACTIVE_TERMINAL_MODE, command: null };
37
+ }
38
+ if (!isString(command) || !command.trim()) throw new Error('Terminal command is required');
39
+ const trimmedCommand = command.trim();
40
+ if (trimmedCommand.length > MAX_INPUT_CHARS) throw new Error('Terminal command exceeds the input limit');
41
+ return { mode: COMMAND_TERMINAL_MODE, command: trimmedCommand };
42
+ };
43
+ const normalizePurposeId = (value, errorMessage) => {
44
+ if (!isString(value) || !value.trim()) throw new Error(errorMessage);
45
+ const normalized = value.trim();
46
+ if (normalized.length > MAX_PURPOSE_ID_CHARS) throw new Error(errorMessage);
47
+ return normalized;
48
+ };
49
+ const normalizeTerminalPurpose = (value) => {
50
+ if (value == null) return TERMINAL_PURPOSE;
51
+ if (!isObjectRecord(value)) throw new Error('Invalid terminal purpose');
52
+ if (value.type === 'terminal') return TERMINAL_PURPOSE;
53
+ if (value.type !== 'project-action') throw new Error('Invalid terminal purpose');
54
+ return {
55
+ type: 'project-action',
56
+ actionId: normalizePurposeId(value.actionId, 'Terminal project action id is required'),
57
+ executionId: normalizePurposeId(value.executionId, 'Terminal execution id is required'),
58
+ };
59
+ };
60
+ const getSessionPurpose = (session) => session.purpose ?? TERMINAL_PURPOSE;
61
+ const isPurposeActionMatch = (left, right) => {
62
+ if (left.type !== right.type) return false;
63
+ return left.type !== 'project-action' || left.actionId === right.actionId;
64
+ };
65
+ const findRunningActionSession = (sessions, resolvedCwd, purpose, path) => {
66
+ if (purpose.type !== 'project-action') return null;
67
+ for (const session of sessions.values()) {
68
+ if (session.status !== 'running') continue;
69
+ if (path.resolve(session.cwd) !== resolvedCwd) continue;
70
+ const sessionPurpose = getSessionPurpose(session);
71
+ if (sessionPurpose.type === 'project-action' && sessionPurpose.actionId === purpose.actionId) return session;
72
+ }
73
+ return null;
74
+ };
75
+ const findPendingActionCreate = (pendingSessionCreates, resolvedCwd, purpose) => {
76
+ if (purpose.type !== 'project-action') return null;
77
+ for (const pending of pendingSessionCreates.values()) {
78
+ if (pending.cancelled || pending.cwd !== resolvedCwd) continue;
79
+ if (pending.purpose?.type === 'project-action' && pending.purpose.actionId === purpose.actionId) return pending;
80
+ }
81
+ return null;
82
+ };
21
83
  const trimHistory = (history) => {
22
84
  const bytes = Buffer.from(history);
23
85
  if (bytes.byteLength <= MAX_HISTORY_BYTES) return history;
@@ -54,25 +116,26 @@ export function createTerminalRuntime({
54
116
  return ptyProviderPromise;
55
117
  };
56
118
 
57
- const spawnPty = async ({ cwd, cols, rows, themeMode, shell, loginShell }) => {
119
+ const spawnPty = async ({ cwd, cols, rows, themeMode, shell, loginShell, mode, command }) => {
58
120
  const provider = await getPtyProvider();
59
121
  const resolvedShell = await shellResolver.resolve(shell);
60
122
  let lastError = null;
61
123
  for (const executable of resolvedShell.executables) {
62
- const args = loginShell ? getTerminalShellLoginArgs(executable) : [];
63
- if (!args) throw new Error(`Terminal shell "${resolvedShell.id}" does not support login mode`);
64
124
  try {
65
125
  const env = { ...process.env, PATH: buildAugmentedPath(), TERM: 'xterm-256color', COLORTERM: 'truecolor', COLORFGBG: themeMode === 'light' ? '0;15' : '15;0' };
66
- // The daemon's IPC fd is closed inside the PTY. An explicit override is
67
- // required because bun-pty also inherits Bun's native process environment.
68
- env.NODE_CHANNEL_FD = '';
126
+ // The daemon's IPC fd is closed inside the PTY; an inherited NODE_CHANNEL_FD
127
+ // (even an empty one) makes Node CLIs warn about an unparsable IPC channel.
128
+ delete env.NODE_CHANNEL_FD;
69
129
  delete env.BASH_XTRACEFD; delete env.BASH_ENV; delete env.ENV; delete env.ELECTRON_RUN_AS_NODE;
70
130
  // AppImage exports ARGV0; zsh would otherwise rewrite argv[0] for every command (#2588).
71
- // bun-pty also merges the native OS environ, so wrap with `env -u ARGV0` on Linux.
72
131
  stripAppImageArgv0Leak(env);
73
- const launch = resolveLinuxPtyLaunch(executable, args);
74
- const options = { name: 'xterm-256color', cwd, cols, rows, env, ...(process.platform === 'win32' ? { useConpty: true } : {}) };
75
- return { process: provider.spawn(launch.executable, launch.args, options), backend: provider.backend, shell: resolvedShell.id, loginShell };
132
+ const shellLaunch = buildTerminalShellLaunch(executable, { mode, command, loginShell });
133
+ // bun-pty merges the native OS environ back in, so the POSIX launch is
134
+ // wrapped with `env -u` for the variables deleted above.
135
+ const launch = resolvePosixPtyLaunch(shellLaunch.executable, shellLaunch.args);
136
+ const options = { name: 'xterm-256color', cwd, cols, rows, env };
137
+ if (process.platform === 'win32') options.useConpty = true;
138
+ return { process: await provider.spawn(launch.executable, launch.args, options), backend: provider.backend, shell: resolvedShell.id, loginShell };
76
139
  } catch (error) { lastError = error; }
77
140
  }
78
141
  throw lastError ?? new Error('No executable shell found');
@@ -122,7 +185,12 @@ export function createTerminalRuntime({
122
185
 
123
186
  const snapshot = (session) => ({
124
187
  t: 'snapshot', v: 3, s: session.id, q: session.sequence, history: session.history,
188
+ // The PTY size the history was drawn for: a client replays history at this
189
+ // size before fitting its own viewport, so shell output wrapped for one
190
+ // width never gets re-laid-out at another.
191
+ cols: session.cols, rows: session.rows,
125
192
  status: session.status, exitCode: session.exitCode, signal: session.signal,
193
+ mode: session.mode ?? INTERACTIVE_TERMINAL_MODE, purpose: getSessionPurpose(session),
126
194
  runtime, ptyBackend: session.backend,
127
195
  });
128
196
 
@@ -175,11 +243,18 @@ export function createTerminalRuntime({
175
243
  ptyProcess.onExit(({ exitCode, signal }) => { session.eventQueue.push({ type: 'exit', process: ptyProcess, exitCode, signal }); drainEvents(session); });
176
244
  };
177
245
 
246
+ // A working directory that no longer exists (a deleted worktree) is the one
247
+ // rejection the client can recover from by moving the session to its
248
+ // project, so the response names it. Every other rejection stays generic.
249
+ const invalidWorkingDirectory = (code) => Object.assign(new Error('Invalid working directory'), code ? { code } : {});
178
250
  const validateCwd = async (cwd) => {
179
251
  if (typeof cwd !== 'string' || !cwd.trim()) throw new Error('cwd is required');
180
- const stats = await fs.promises.stat(cwd).catch(() => null);
181
- if (!stats?.isDirectory()) throw new Error('Invalid working directory');
252
+ let stats;
253
+ try { stats = await fs.promises.stat(cwd); }
254
+ catch (error) { throw invalidWorkingDirectory(error?.code === 'ENOENT' || error?.code === 'ENOTDIR' ? TERMINAL_CWD_MISSING_CODE : undefined); }
255
+ if (!stats?.isDirectory()) throw invalidWorkingDirectory();
182
256
  };
257
+ const errorBody = (error, fallback) => ({ error: error?.message || fallback, ...(typeof error?.code === 'string' ? { code: error.code } : {}) });
183
258
 
184
259
  const applyAppearance = (session, { themeMode, terminalBackground, terminalForeground }) => {
185
260
  const previous = [session.themeMode, session.terminalBackground, session.terminalForeground];
@@ -192,48 +267,87 @@ export function createTerminalRuntime({
192
267
  }
193
268
  };
194
269
 
195
- const startSession = async (session, { cwd, cols, rows, themeMode = 'dark', terminalBackground, terminalForeground, shell, loginShell }, clear = true) => {
270
+ const startSession = async (session, { cwd, cols, rows, themeMode = 'dark', terminalBackground, terminalForeground, shell, loginShell, mode = INTERACTIVE_TERMINAL_MODE, command = null, purpose = TERMINAL_PURPOSE }, clear = true) => {
196
271
  await validateCwd(cwd);
197
- const spawned = await spawnPty({ cwd, cols, rows, themeMode, shell, loginShell });
272
+ const spawned = await spawnPty({ cwd, cols, rows, themeMode, shell, loginShell, mode, command });
198
273
  if (clear) { session.history = ''; session.pendingHistoryControlSequence = ''; session.pendingThemeControlSequence = ''; session.themeModeEnabled = false; }
199
274
  session.cwd = cwd; session.cols = cols; session.rows = rows; session.process = spawned.process;
200
275
  session.backend = spawned.backend; session.shell = spawned.shell; session.loginShell = spawned.loginShell; session.status = 'running'; session.exitCode = null; session.signal = null;
276
+ session.mode = mode; session.command = mode === COMMAND_TERMINAL_MODE ? command : null;
277
+ session.purpose = purpose;
201
278
  session.themeMode = themeMode === 'light' ? 'light' : 'dark'; session.terminalBackground = terminalBackground; session.terminalForeground = terminalForeground;
202
279
  session.lastActivity = Date.now(); session.eventQueue.length = 0;
203
280
  wire(session, spawned.process);
281
+ return spawned.process;
204
282
  };
205
283
 
206
- const createSession = async ({ sessionId, cwd, cols = 80, rows = 24, themeMode, terminalBackground, terminalForeground, shell = 'auto', loginShell = false }) => {
284
+ const createSession = async ({ sessionId, cwd, cols = 80, rows = 24, themeMode, terminalBackground, terminalForeground, shell = 'auto', loginShell = false, mode, command, purpose }) => {
207
285
  if (!validateSize(cols, 1000) || !validateSize(rows, 500)) throw new Error('Invalid terminal dimensions');
208
286
  if (typeof loginShell !== 'boolean') throw new Error('Invalid terminal login mode');
209
287
  const normalizedShell = normalizeTerminalShell(shell);
210
288
  if (!normalizedShell) throw new Error('Invalid terminal shell');
289
+ const launchMode = normalizeCreateMode({ mode, command });
290
+ const normalizedPurpose = normalizeTerminalPurpose(purpose);
211
291
  const id = typeof sessionId === 'string' && sessionId.trim() ? sessionId.trim() : randomUUID();
212
292
  if (id.length > 128) throw new Error('Invalid terminal session id');
213
293
  const existing = sessions.get(id);
214
294
  const resolvedCwd = path.resolve(cwd);
215
295
  if (existing?.status === 'running') {
216
296
  if (path.resolve(existing.cwd) !== resolvedCwd) throw new Error('Terminal session belongs to a different working directory');
297
+ if (!isPurposeActionMatch(getSessionPurpose(existing), normalizedPurpose)) throw new Error('Terminal session is already running with a different purpose');
298
+ if (normalizedPurpose.type === 'project-action') { applyAppearance(existing, { themeMode, terminalBackground, terminalForeground }); return existing; }
299
+ if ((existing.mode ?? INTERACTIVE_TERMINAL_MODE) !== launchMode.mode) throw new Error('Terminal session is already running with a different mode');
300
+ if (launchMode.mode === COMMAND_TERMINAL_MODE && existing.command !== launchMode.command) throw new Error('Terminal session is already running with a different command');
217
301
  applyAppearance(existing, { themeMode, terminalBackground, terminalForeground });
218
302
  return existing;
219
303
  }
304
+ const runningActionSession = findRunningActionSession(sessions, resolvedCwd, normalizedPurpose, path);
305
+ if (runningActionSession) {
306
+ applyAppearance(runningActionSession, { themeMode, terminalBackground, terminalForeground });
307
+ return runningActionSession;
308
+ }
220
309
  const pending = pendingSessionCreates.get(id);
221
310
  if (pending) {
222
311
  if (pending.cwd !== resolvedCwd) throw new Error('Terminal session belongs to a different working directory');
312
+ if (!isPurposeActionMatch(pending.purpose, normalizedPurpose)) throw new Error('Terminal session is already being created with a different purpose');
313
+ if (normalizedPurpose.type === 'project-action') return pending.promise;
223
314
  if (pending.shell !== normalizedShell) throw new Error('Terminal session is already being created with a different shell');
224
315
  if (pending.loginShell !== loginShell) throw new Error('Terminal session is already being created with a different login mode');
316
+ if (pending.mode !== launchMode.mode) throw new Error('Terminal session is already being created with a different mode');
317
+ if (launchMode.mode === COMMAND_TERMINAL_MODE && pending.command !== launchMode.command) throw new Error('Terminal session is already being created with a different command');
225
318
  const session = await pending.promise;
226
319
  applyAppearance(session, { themeMode, terminalBackground, terminalForeground });
227
320
  return session;
228
321
  }
229
- if (!existing && sessions.size + pendingSessionCreates.size >= MAX_SESSIONS) throw new Error('Maximum terminal sessions reached');
322
+ const pendingActionCreate = findPendingActionCreate(pendingSessionCreates, resolvedCwd, normalizedPurpose);
323
+ if (pendingActionCreate) {
324
+ const session = await pendingActionCreate.promise;
325
+ applyAppearance(session, { themeMode, terminalBackground, terminalForeground });
326
+ return session;
327
+ }
328
+ const superseded = normalizedPurpose.type === 'project-action'
329
+ ? [...sessions.values()].filter((session) => session.id !== id && session.status === 'exited'
330
+ && path.resolve(session.cwd) === resolvedCwd && isPurposeActionMatch(getSessionPurpose(session), normalizedPurpose))
331
+ : [];
332
+ if (!existing && sessions.size - superseded.length + pendingSessionCreates.size >= MAX_SESSIONS) throw new Error('Maximum terminal sessions reached');
333
+ const pendingEntry = { cwd: resolvedCwd, shell: normalizedShell, loginShell, mode: launchMode.mode, command: launchMode.command, purpose: normalizedPurpose, cancelled: false, promise: null };
230
334
  const creation = (async () => {
231
335
  const session = existing ?? { id, sequence: 0, history: '', pendingHistoryControlSequence: '', pendingThemeControlSequence: '', eventQueue: [], draining: false, createdAt: Date.now() };
232
- await startSession(session, { cwd, cols, rows, themeMode, terminalBackground, terminalForeground, shell: normalizedShell, loginShell });
336
+ const ptyProcess = await startSession(session, { cwd, cols, rows, themeMode, terminalBackground, terminalForeground, shell: normalizedShell, loginShell, mode: launchMode.mode, command: launchMode.command, purpose: normalizedPurpose });
337
+ if (pendingEntry.cancelled) {
338
+ session.process = null;
339
+ await terminateProcess(ptyProcess, true);
340
+ throw new Error('Terminal session was closed during creation');
341
+ }
342
+ for (const previous of superseded) {
343
+ if (sessions.get(previous.id) !== previous || previous.status !== 'exited') continue;
344
+ sessions.delete(previous.id);
345
+ closeAttachments(previous.id, 'SUPERSEDED', 'Terminal replaced by a new action run');
346
+ }
233
347
  sessions.set(id, session);
234
348
  return session;
235
349
  })();
236
- const pendingEntry = { cwd: resolvedCwd, shell: normalizedShell, loginShell, promise: creation };
350
+ pendingEntry.promise = creation;
237
351
  pendingSessionCreates.set(id, pendingEntry);
238
352
  try { return await creation; }
239
353
  finally { if (pendingSessionCreates.get(id) === pendingEntry) pendingSessionCreates.delete(id); }
@@ -276,16 +390,39 @@ export function createTerminalRuntime({
276
390
 
277
391
  const upgradeHandler = (req, socket, head) => {
278
392
  if (parseRequestPathname(req.url) !== TERMINAL_WS_PATH) return;
279
- void (async () => {
393
+ const accept = () => {
394
+ if (!wsServer) { rejectWebSocketUpgrade(socket, 500, 'Terminal WebSocket unavailable'); return; }
280
395
  try {
281
- if (uiAuthController?.enabled) {
282
- if (!await uiAuthController.ensureSessionToken(req, null)) { rejectWebSocketUpgrade(socket, 401, 'UI authentication required'); return; }
283
- if (!await isRequestOriginAllowed(req)) { rejectWebSocketUpgrade(socket, 403, 'Invalid origin'); return; }
284
- }
285
- if (!wsServer) { rejectWebSocketUpgrade(socket, 500, 'Terminal WebSocket unavailable'); return; }
286
396
  wsServer.handleUpgrade(req, socket, head, (ws) => wsServer.emit('connection', ws, req));
287
397
  } catch { rejectWebSocketUpgrade(socket, 500, 'Upgrade failed'); }
288
- })();
398
+ };
399
+ const checkOrigin = () => {
400
+ try {
401
+ const result = isRequestOriginAllowed(req);
402
+ if (!(result instanceof Promise)) {
403
+ if (result) accept();
404
+ else rejectWebSocketUpgrade(socket, 403, 'Invalid origin');
405
+ return;
406
+ }
407
+ void result.then((allowed) => {
408
+ if (allowed) accept();
409
+ else rejectWebSocketUpgrade(socket, 403, 'Invalid origin');
410
+ }).catch(() => rejectWebSocketUpgrade(socket, 500, 'Upgrade failed'));
411
+ } catch { rejectWebSocketUpgrade(socket, 500, 'Upgrade failed'); }
412
+ };
413
+ if (!uiAuthController?.enabled) { accept(); return; }
414
+ try {
415
+ const result = uiAuthController.ensureSessionToken(req, null);
416
+ if (!(result instanceof Promise)) {
417
+ if (result) checkOrigin();
418
+ else rejectWebSocketUpgrade(socket, 401, 'UI authentication required');
419
+ return;
420
+ }
421
+ void result.then((sessionToken) => {
422
+ if (sessionToken) checkOrigin();
423
+ else rejectWebSocketUpgrade(socket, 401, 'UI authentication required');
424
+ }).catch(() => rejectWebSocketUpgrade(socket, 500, 'Upgrade failed'));
425
+ } catch { rejectWebSocketUpgrade(socket, 500, 'Upgrade failed'); }
289
426
  };
290
427
  server.on('upgrade', upgradeHandler);
291
428
 
@@ -308,6 +445,8 @@ export function createTerminalRuntime({
308
445
  cwd: session.cwd,
309
446
  status: session.status,
310
447
  createdAt: Number.isInteger(session.createdAt) ? session.createdAt : null,
448
+ mode: session.mode ?? INTERACTIVE_TERMINAL_MODE,
449
+ purpose: getSessionPurpose(session),
311
450
  });
312
451
  }
313
452
  res.json({ sessions: list });
@@ -326,8 +465,18 @@ export function createTerminalRuntime({
326
465
  res.json({ touched });
327
466
  });
328
467
  app.post('/api/terminal/create', async (req, res) => {
329
- try { const session = await createSession(req.body ?? {}); res.json({ sessionId: session.id, cols: session.cols, rows: session.rows, status: session.status }); }
330
- catch (error) { res.status(error?.message === 'Maximum terminal sessions reached' ? 429 : 400).json({ error: error?.message || 'Failed to create terminal session' }); }
468
+ try {
469
+ const session = await createSession(req.body ?? {});
470
+ res.json({
471
+ sessionId: session.id,
472
+ cols: session.cols,
473
+ rows: session.rows,
474
+ status: session.status,
475
+ mode: session.mode ?? INTERACTIVE_TERMINAL_MODE,
476
+ purpose: getSessionPurpose(session),
477
+ });
478
+ }
479
+ catch (error) { res.status(error?.message === 'Maximum terminal sessions reached' ? 429 : 400).json(errorBody(error, 'Failed to create terminal session')); }
331
480
  });
332
481
  app.post('/api/terminal/:sessionId/resize', (req, res) => {
333
482
  const session = sessions.get(req.params.sessionId);
@@ -346,6 +495,7 @@ export function createTerminalRuntime({
346
495
  app.post('/api/terminal/:sessionId/restart', async (req, res) => {
347
496
  const session = sessions.get(req.params.sessionId);
348
497
  if (!session) return res.status(404).json({ error: 'Terminal session not found' });
498
+ if ((session.mode ?? INTERACTIVE_TERMINAL_MODE) === COMMAND_TERMINAL_MODE) return res.status(400).json({ error: 'Command-mode terminal sessions cannot be restarted' });
349
499
  const cwd = req.body?.cwd ?? session.cwd;
350
500
  const cols = req.body?.cols ?? session.cols;
351
501
  const rows = req.body?.rows ?? session.rows;
@@ -370,12 +520,21 @@ export function createTerminalRuntime({
370
520
  try {
371
521
  await restart;
372
522
  res.json({ sessionId: session.id, cols, rows, status: session.status });
373
- } catch (error) { res.status(400).json({ error: error?.message || 'Failed to restart terminal' }); }
523
+ } catch (error) { res.status(400).json(errorBody(error, 'Failed to restart terminal')); }
374
524
  finally { if (pendingSessionRestarts.get(session.id) === restart) pendingSessionRestarts.delete(session.id); }
375
525
  });
376
526
  app.delete('/api/terminal/:sessionId', async (req, res) => {
377
527
  const session = sessions.get(req.params.sessionId);
378
- if (!session) return res.status(404).json({ error: 'Terminal session not found' });
528
+ if (!session) {
529
+ const pending = pendingSessionCreates.get(req.params.sessionId);
530
+ if (!pending) return res.status(404).json({ error: 'Terminal session not found' });
531
+ pending.cancelled = true;
532
+ try { await pending.promise; }
533
+ catch (error) {
534
+ if (error?.message !== 'Terminal session was closed during creation') throw error;
535
+ }
536
+ return res.json({ success: true });
537
+ }
379
538
  sessions.delete(session.id);
380
539
  closeAttachments(session.id, 'CLOSED', 'Terminal closed');
381
540
  await terminateProcess(session.process);