@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
@@ -91,6 +91,10 @@ other runtime API.
91
91
  a blocker instead of a raw 500 message.
92
92
  - `call.js` — wire formats and per-provider auth, replicating OpenCode's
93
93
  plugin auth loaders:
94
+ - OpenCode-hosted providers receive `x-opencode-session`. Session-backed
95
+ features reuse the real OpenCode session id, walkthrough retries reuse the
96
+ walkthrough cache key, and standalone one-shot actions receive a fresh
97
+ opaque id for that generation.
94
98
  - **GitHub Copilot**: fetches the requested model's authenticated `/models`
95
99
  metadata from `https://api.githubcopilot.com` (or
96
100
  `copilot-api.<enterprise>`) and honors its advertised endpoint, preferring
@@ -113,8 +117,9 @@ other runtime API.
113
117
  - Everything else: OpenAI-compatible `/chat/completions` against the
114
118
  provider's base URL, resolved from (1) `provider.<id>.options.baseURL`
115
119
  in the OpenCode config, (2) the hardcoded `https://api.openai.com/v1`
116
- endpoint, (3) the endpoint OpenCode resolved at runtime, or (4) the
117
- provider's `api` field from the models.dev catalog. The credential follows
120
+ endpoint, (3) the selected model's endpoint OpenCode resolved at runtime,
121
+ (4) the provider-level runtime endpoint, or (5) the provider's `api` field
122
+ from the models.dev catalog. The credential follows
118
123
  the same shape: config `options.apiKey`, then the runtime credential, then
119
124
  the auth.json entry. `provider.<id>.options.headers` is sent with the
120
125
  request and overrides the bearer default, so gateways that authenticate on
@@ -1,6 +1,7 @@
1
1
  import fs from 'fs';
2
2
  import os from 'os';
3
3
  import path from 'path';
4
+ import { randomUUID } from 'node:crypto';
4
5
  import { readAuthFile, writeAuthFile } from '../opencode/auth.js';
5
6
  import { readConfig, readConfigLayers, isPlainObject } from '../opencode/shared.js';
6
7
  import { getCatalogProvider } from './catalog.js';
@@ -17,7 +18,7 @@ const COPILOT_MODELS_TIMEOUT_MS = 5_000;
17
18
  // Qwen, …) spend part of this budget on reasoning before the actual answer.
18
19
  const DEFAULT_MAX_OUTPUT_TOKENS = 4_000;
19
20
 
20
- const USER_AGENT = 'opencode/1.0 openchamber';
21
+ const USER_AGENT = 'opencode/1.0 aiworker';
21
22
 
22
23
  const mergeHeadersCaseInsensitive = (base, overrides) => {
23
24
  const merged = { ...base };
@@ -605,6 +606,8 @@ const readProviderConfig = (workingDirectory, providerID) => {
605
606
  }
606
607
  }
607
608
 
609
+ const getRuntimeModel = (runtimeProvider, modelID) => runtimeProvider?.models?.get(modelID) ?? null;
610
+
608
611
  // ---------------------------------------------------------------------------
609
612
  // Dispatch
610
613
  // ---------------------------------------------------------------------------
@@ -645,10 +648,11 @@ export async function resolveProviderLogin({ auth, workingDirectory, providerID
645
648
  || null;
646
649
  }
647
650
 
648
- export async function callSmallModel({ auth, catalog, workingDirectory, providerID, modelID, prompt, system, maxOutputTokens, responseSchema, timeoutMs, signal }) {
651
+ export async function callSmallModel({ auth, catalog, workingDirectory, sessionID, providerID, modelID, prompt, system, maxOutputTokens, responseSchema, timeoutMs, signal }) {
649
652
  const tokens = Number(maxOutputTokens) > 0 ? Number(maxOutputTokens) : DEFAULT_MAX_OUTPUT_TOKENS;
650
653
  const providerConfig = readProviderConfig(workingDirectory, providerID);
651
654
  const runtimeProvider = await getRuntimeProvider(providerID);
655
+ const runtimeModel = getRuntimeModel(runtimeProvider, modelID);
652
656
  // Match OpenCode's resolveSDK precedence: config `provider.<id>.options`
653
657
  // wins, then what OpenCode itself resolved at runtime (the only place a
654
658
  // plugin's credential exists), and the auth.json entry last.
@@ -758,9 +762,10 @@ export async function callSmallModel({ auth, catalog, workingDirectory, provider
758
762
  // base URL for that provider (openai itself included). When a custom provider
759
763
  // is not in the catalog (e.g. a user-configured OpenAI-compatible proxy),
760
764
  // fall back to its baseURL from the OpenCode provider config, then to the
761
- // endpoint OpenCode resolved at runtime which for a plugin provider is the
762
- // only place it exists, and for several of them is a local proxy the plugin
763
- // itself runs. The openai provider also respects
765
+ // selected model's endpoint OpenCode resolved at runtime, then to the
766
+ // provider-level runtime endpoint. For a plugin provider, the runtime listing
767
+ // is the only place those endpoints exist, and several are local proxies the
768
+ // plugin itself runs. The openai provider also respects
764
769
  // provider.openai.options.baseURL — OpenCode itself uses the same config for
765
770
  // all providers including openai.
766
771
  const provider = getCatalogProvider(catalog, providerID);
@@ -770,7 +775,8 @@ export async function callSmallModel({ auth, catalog, workingDirectory, provider
770
775
  ? providerConfigUrl
771
776
  : providerID === 'openai'
772
777
  ? defaultOpenaiUrl
773
- : runtimeProvider?.baseURL
778
+ : runtimeModel?.api?.url
779
+ ?? runtimeProvider?.baseURL
774
780
  ?? (typeof provider?.api === 'string' && provider.api
775
781
  ? provider.api
776
782
  : null);
@@ -795,7 +801,12 @@ export async function callSmallModel({ auth, catalog, workingDirectory, provider
795
801
  baseURL,
796
802
  // Configured headers last: a gateway that authenticates on its own header
797
803
  // must be able to override the bearer default rather than sit beside it.
798
- headers: mergeHeadersCaseInsensitive({ Authorization: `Bearer ${apiKey}` }, providerConfig?.headers),
804
+ headers: mergeHeadersCaseInsensitive(
805
+ mergeHeadersCaseInsensitive({ Authorization: `Bearer ${apiKey}` }, providerConfig?.headers),
806
+ providerID.startsWith('opencode')
807
+ ? { 'x-opencode-session': typeof sessionID === 'string' && sessionID.trim() ? sessionID.trim() : randomUUID() }
808
+ : null,
809
+ ),
799
810
  modelID,
800
811
  prompt,
801
812
  system,
@@ -69,8 +69,8 @@ describe('callSmallModel — custom provider config', () => {
69
69
  afterEach(() => {
70
70
  globalThis.fetch = originalFetch;
71
71
  vi.restoreAllMocks();
72
- delete process.env.OPENCHAMBER_TEST_PROVIDER_KEY;
73
- delete process.env.OPENCHAMBER_TEST_GATEWAY_KEY;
72
+ delete process.env.AIWORKER_TEST_PROVIDER_KEY;
73
+ delete process.env.AIWORKER_TEST_GATEWAY_KEY;
74
74
  });
75
75
 
76
76
  describe('config-supplied credentials (no auth.json entry)', () => {
@@ -104,11 +104,11 @@ describe('callSmallModel — custom provider config', () => {
104
104
  });
105
105
 
106
106
  it('resolves an OpenCode environment variable before sending the API key', async () => {
107
- process.env.OPENCHAMBER_TEST_PROVIDER_KEY = 'sk-env-key';
107
+ process.env.AIWORKER_TEST_PROVIDER_KEY = 'sk-env-key';
108
108
  readConfig.mockReturnValue({
109
109
  provider: {
110
110
  custom: {
111
- options: { apiKey: '{env:OPENCHAMBER_TEST_PROVIDER_KEY}', baseURL: 'https://proxy.example.test/v1' },
111
+ options: { apiKey: '{env:AIWORKER_TEST_PROVIDER_KEY}', baseURL: 'https://proxy.example.test/v1' },
112
112
  },
113
113
  },
114
114
  });
@@ -127,7 +127,7 @@ describe('callSmallModel — custom provider config', () => {
127
127
  });
128
128
 
129
129
  it('sends configured provider headers alongside the bearer token', async () => {
130
- process.env.OPENCHAMBER_TEST_GATEWAY_KEY = 'sub-key';
130
+ process.env.AIWORKER_TEST_GATEWAY_KEY = 'sub-key';
131
131
  readConfig.mockReturnValue({
132
132
  provider: {
133
133
  custom: {
@@ -135,7 +135,7 @@ describe('callSmallModel — custom provider config', () => {
135
135
  apiKey: 'sk-config',
136
136
  baseURL: 'https://proxy.example.test/v1',
137
137
  headers: {
138
- 'Ocp-Apim-Subscription-Key': '{env:OPENCHAMBER_TEST_GATEWAY_KEY}',
138
+ 'Ocp-Apim-Subscription-Key': '{env:AIWORKER_TEST_GATEWAY_KEY}',
139
139
  'x-tenant': 'team',
140
140
  },
141
141
  },
@@ -483,6 +483,29 @@ describe('callSmallModel — custom provider config', () => {
483
483
  });
484
484
 
485
485
  describe('catalog-based base URL (no config override)', () => {
486
+ it('identifies OpenCode Go requests with the owning conversation', async () => {
487
+ readConfig.mockReturnValue({});
488
+ fetchMock.mockResolvedValue(ok('ok'));
489
+
490
+ await callSmallModel({
491
+ auth: { 'opencode-go': { type: 'api', key: 'go-key' } },
492
+ catalog: {
493
+ 'opencode-go': {
494
+ id: 'opencode-go',
495
+ api: 'https://opencode.ai/zen/go/v1',
496
+ models: { utility: { id: 'utility' } },
497
+ },
498
+ },
499
+ workingDirectory: '/proj',
500
+ sessionID: 'ses_conversation',
501
+ providerID: 'opencode-go',
502
+ modelID: 'utility',
503
+ prompt: 'hi',
504
+ });
505
+
506
+ expect(lastCall(fetchMock).init.headers['x-opencode-session']).toBe('ses_conversation');
507
+ });
508
+
486
509
  it('uses the catalog api field when no config baseURL is set', async () => {
487
510
  readConfig.mockReturnValue({});
488
511
  fetchMock.mockResolvedValue(ok('ok'));
@@ -542,6 +565,35 @@ describe('callSmallModel — custom provider config', () => {
542
565
  expect(init.headers.Authorization).toBe('Bearer plugin-key');
543
566
  });
544
567
 
568
+ it('uses the selected runtime model endpoint', async () => {
569
+ readConfig.mockReturnValue({});
570
+ getRuntimeProvider.mockResolvedValue({
571
+ id: 'runtime-provider',
572
+ apiKey: 'plugin-key',
573
+ baseURL: 'https://runtime-provider/v1beta',
574
+ models: new Map([
575
+ ['first-model', { api: { url: 'https://runtime-provider/v1beta', npm: '@ai-sdk/google' } }],
576
+ ['selected-model', { api: { url: 'https://runtime-provider/v1', npm: '@ai-sdk/openai' } }],
577
+ ]),
578
+ anonymousZen: false,
579
+ });
580
+ fetchMock.mockResolvedValue(ok('done'));
581
+
582
+ await callSmallModel({
583
+ auth: {},
584
+ catalog: {},
585
+ workingDirectory: '/proj',
586
+ providerID: 'runtime-provider',
587
+ modelID: 'selected-model',
588
+ prompt: 'hi',
589
+ });
590
+
591
+ const { url, init } = lastCall(fetchMock);
592
+ expect(url).toBe('https://runtime-provider/v1/chat/completions');
593
+ expect(url).not.toContain('/v1beta');
594
+ expect(JSON.parse(init.body).model).toBe('selected-model');
595
+ });
596
+
545
597
  it('keeps the ChatGPT-plan login on its own transport instead of the runtime key', async () => {
546
598
  readConfig.mockReturnValue({});
547
599
  // OpenCode reports an OAuth access token as `options.apiKey` for openai;
@@ -6,6 +6,7 @@ import { readConfigLayers } from '../opencode/shared.js';
6
6
  import { getModelCatalog } from './catalog.js';
7
7
  import { resolveSmallModel, parseModelRef, isUsableAuthEntry, getAuthEntryForProvider } from './resolve.js';
8
8
  import { DEDICATED_WIRE_FORMAT_PROVIDERS, callSmallModel, resolveProviderLogin } from './call.js';
9
+ import { readMergedSettingsSync } from '../opencode/settings-files.js';
9
10
  import { getRuntimeProviderSnapshot } from './runtime-providers.js';
10
11
 
11
12
  // Never a small model, whatever the transport looks like. A plugin can publish
@@ -16,26 +17,20 @@ import { getRuntimeProviderSnapshot } from './runtime-providers.js';
16
17
  // conditional on an endpoint existing.
17
18
  const CLAUDE_CODE_PROVIDER = 'claude-code';
18
19
 
19
- const OPENCHAMBER_SETTINGS_FILE = path.join(
20
- process.env.OPENCHAMBER_DATA_DIR
21
- ? path.resolve(process.env.OPENCHAMBER_DATA_DIR)
22
- : path.join(os.homedir(), '.config', 'openchamber'),
20
+ const AIWORKER_SETTINGS_FILE = path.join(
21
+ process.env.AIWORKER_DATA_DIR
22
+ ? path.resolve(process.env.AIWORKER_DATA_DIR)
23
+ : path.join(os.homedir(), '.config', 'aiworker'),
23
24
  'settings.json',
24
25
  );
25
26
 
26
- // OpenChamber's own settings: when the user unchecks "use default small model"
27
+ // AiWorker's own settings: when the user unchecks "use default small model"
27
28
  // their explicit override outranks every other resolution step.
28
29
  const readSmallModelSettingsOverride = () => {
29
- try {
30
- const raw = fs.readFileSync(OPENCHAMBER_SETTINGS_FILE, 'utf8');
31
- const settings = JSON.parse(raw);
32
- if (!settings || typeof settings !== 'object') return null;
33
- if (settings.smallModelUseDefault !== false) return null;
34
- const override = typeof settings.smallModelOverride === 'string' ? settings.smallModelOverride.trim() : '';
35
- return override || null;
36
- } catch {
37
- return null;
38
- }
30
+ const settings = readMergedSettingsSync({ fs, path, settingsFilePath: AIWORKER_SETTINGS_FILE });
31
+ if (settings.smallModelUseDefault !== false) return null;
32
+ const override = typeof settings.smallModelOverride === 'string' ? settings.smallModelOverride.trim() : '';
33
+ return override || null;
39
34
  };
40
35
 
41
36
  // Rough safety clamp so a huge input never blows the model's context window.
@@ -102,7 +97,7 @@ const readConfiguredSmallModel = (workingDirectory) => {
102
97
  * Generates text with the user's small model, resolved and authenticated
103
98
  * entirely server-side from the OpenCode config and auth store.
104
99
  */
105
- export async function generateSmallModelText({ prompt, system, maxOutputTokens, model, directory, preferredProviderID, preferredModelID, restrictToPreferredProvider = false, responseSchema, timeoutMs, signal, onOverflow = 'truncate' }) {
100
+ export async function generateSmallModelText({ prompt, system, maxOutputTokens, model, directory, sessionID, preferredProviderID, preferredModelID, restrictToPreferredProvider = false, responseSchema, timeoutMs, signal, onOverflow = 'truncate' }) {
106
101
  if (typeof prompt !== 'string' || !prompt.trim()) {
107
102
  throw Object.assign(new Error('prompt is required'), { statusCode: 400 });
108
103
  }
@@ -169,6 +164,7 @@ export async function generateSmallModelText({ prompt, system, maxOutputTokens,
169
164
  auth,
170
165
  catalog,
171
166
  workingDirectory: directory,
167
+ sessionID,
172
168
  providerID: resolved.providerID,
173
169
  modelID: resolved.modelID,
174
170
  prompt: clamped.prompt,
@@ -7,7 +7,7 @@ import { afterAll, afterEach, beforeEach, describe, expect, it, vi } from 'vites
7
7
  // this the suite would resolve whatever small model the developer running it
8
8
  // happens to have configured.
9
9
  const TEMP_DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), 'small-model-settings-'));
10
- process.env.OPENCHAMBER_DATA_DIR = TEMP_DATA_DIR;
10
+ process.env.AIWORKER_DATA_DIR = TEMP_DATA_DIR;
11
11
 
12
12
  vi.mock('../opencode/auth.js', () => ({ readAuthFile: vi.fn() }));
13
13
  vi.mock('../opencode/shared.js', () => ({
@@ -76,7 +76,7 @@ const pickWithinProvider = (providerID, auth, catalog, family) => {
76
76
  };
77
77
 
78
78
  export function resolveSmallModel({ auth, catalog, settingsSmallModel, configSmallModel, preferredProviderID, preferredModelID }) {
79
- // OpenChamber's own setting (Settings → Sessions → Small Model override)
79
+ // AiWorker's own setting (Settings → Sessions → Small Model override)
80
80
  // outranks everything, including the OpenCode config.
81
81
  const fromSettings = parseModelRef(settingsSmallModel);
82
82
  if (fromSettings) {
@@ -57,7 +57,7 @@ describe('isUsableAuthEntry', () => {
57
57
  });
58
58
 
59
59
  describe('resolveSmallModel', () => {
60
- it('gives the OpenChamber settings override top priority', () => {
60
+ it('gives the AiWorker settings override top priority', () => {
61
61
  const result = resolveSmallModel({
62
62
  auth: { anthropic: { type: 'api', key: 'sk-x' } },
63
63
  catalog,
@@ -21,13 +21,14 @@ export function registerSmallModelRoutes(app, { getSmallModelService }) {
21
21
  app.post('/api/small-model/generate', async (req, res) => {
22
22
  try {
23
23
  const { generateSmallModelText } = await getSmallModelService();
24
- const { prompt, system, maxOutputTokens, model, directory, preferredProviderID, preferredModelID, restrictToPreferredProvider } = req.body || {};
24
+ const { prompt, system, maxOutputTokens, model, directory, sessionID, preferredProviderID, preferredModelID, restrictToPreferredProvider } = req.body || {};
25
25
  const result = await generateSmallModelText({
26
26
  prompt,
27
27
  system,
28
28
  maxOutputTokens,
29
29
  model,
30
30
  directory,
31
+ sessionID,
31
32
  preferredProviderID,
32
33
  preferredModelID,
33
34
  restrictToPreferredProvider: restrictToPreferredProvider === true,
@@ -8,7 +8,8 @@
8
8
  //
9
9
  // `GET /provider` is where that state becomes visible. It reports, per
10
10
  // provider, the resolved `options.baseURL` and `options.apiKey`, and per model
11
- // the wire adapter (`api.npm`) and endpoint (`api.url`).
11
+ // the wire adapter (`api.npm`) and endpoint (`api.url`). The provider-level
12
+ // endpoint remains only as a fallback when the selected model has no endpoint.
12
13
  //
13
14
  // What it does NOT report is `options.fetch`. OpenCode strips functions from
14
15
  // the response, and a plugin is free to put its whole protocol in there:
@@ -61,8 +62,8 @@ export function resetOpenCodeRuntimeProviders() {
61
62
  /**
62
63
  * The boundary. Everything the `/provider` payload claims is checked here, so
63
64
  * the rest of this module and its callers work with settled values:
64
- * a credential we may use, an endpoint, and whether the provider is the
65
- * anonymous zen case.
65
+ * a credential we may use, provider and model endpoints, and whether the
66
+ * provider is the anonymous zen case.
66
67
  *
67
68
  * The credential deliberately prefers `options.apiKey` over the `key` field:
68
69
  * for a plugin provider the former is what its auth loader produced and what
@@ -82,13 +83,22 @@ function parseProviderListing(payload) {
82
83
  const id = text(record(raw).id);
83
84
  if (!id) continue;
84
85
  const options = record(record(raw).options);
85
- const firstModel = record(Object.values(record(record(raw).models))[0]);
86
+ const models = new Map();
87
+ for (const [modelID, rawModel] of Object.entries(record(record(raw).models))) {
88
+ const model = record(rawModel);
89
+ const api = record(model.api);
90
+ const modelURL = endpoint(api.url);
91
+ const modelNpm = text(api.npm);
92
+ models.set(modelID, { api: { url: modelURL, npm: modelNpm } });
93
+ }
94
+ const firstModel = models.values().next().value;
86
95
  const declaredKey = text(options.apiKey);
87
96
  providers.set(id, {
88
97
  id,
89
98
  source: text(record(raw).source),
90
99
  apiKey: declaredKey === ZEN_ANONYMOUS_API_KEY ? null : (declaredKey ?? text(record(raw).key)),
91
- baseURL: endpoint(options.baseURL) ?? endpoint(record(firstModel.api).url),
100
+ baseURL: endpoint(options.baseURL) ?? firstModel?.api?.url ?? null,
101
+ models,
92
102
  // True only for the zen-without-login case: a provider that is present
93
103
  // and usable through OpenCode, but that we must not call ourselves.
94
104
  anonymousZen: declaredKey === ZEN_ANONYMOUS_API_KEY,
@@ -152,5 +162,3 @@ export async function getRuntimeProvider(providerID) {
152
162
  const current = await getRuntimeProviderSnapshot();
153
163
  return current?.providers.get(providerID) ?? null;
154
164
  }
155
-
156
-
@@ -14,7 +14,10 @@ const providerPayload = (overrides = {}) => ({
14
14
  id: 'llmapi',
15
15
  source: 'config',
16
16
  options: { apiKey: 'plugin-key', baseURL: 'https://api.llmapi.ai/v1/' },
17
- models: { 'claude-opus-4-8': { api: { id: 'claude-opus-4-8', url: '', npm: '@ai-sdk/anthropic' } } },
17
+ models: {
18
+ 'claude-opus-4-8': { api: { id: 'claude-opus-4-8', url: '', npm: '@ai-sdk/anthropic' } },
19
+ 'gpt-5.6-luna': { api: { id: 'gpt-5.6-luna', url: 'https://api.llmapi.ai/v1', npm: '@ai-sdk/openai' } },
20
+ },
18
21
  },
19
22
  {
20
23
  id: 'opencode',
@@ -59,6 +62,9 @@ describe('OpenCode runtime provider snapshot', () => {
59
62
  const provider = await getRuntimeProvider('llmapi');
60
63
 
61
64
  expect(provider).toMatchObject({ apiKey: 'plugin-key', baseURL: 'https://api.llmapi.ai/v1' });
65
+ expect(provider.models.get('gpt-5.6-luna')).toEqual({
66
+ api: { url: 'https://api.llmapi.ai/v1', npm: '@ai-sdk/openai' },
67
+ });
62
68
  expect(fetchMock.mock.calls[0][0]).toBe('http://127.0.0.1:4096/provider');
63
69
  expect(fetchMock.mock.calls[0][1].headers).toMatchObject({ Authorization: 'Basic test' });
64
70
  });
@@ -1,5 +1,5 @@
1
- import { parse as parseJsonc } from 'jsonc-parser';
2
1
  import { pathToFileURL } from 'node:url';
2
+ import { appendManagedPlugin } from '../opencode/managed-plugin-config.js';
3
3
 
4
4
  const PROVIDER_PROMPT_BOUNDARY = 'You are powered by the model named';
5
5
  const MINIMAL_IDENTITY = 'You are OpenCode, a coding agent.';
@@ -9,7 +9,7 @@ const PROVIDER_PROMPT_BOUNDARY = ${JSON.stringify(PROVIDER_PROMPT_BOUNDARY)}
9
9
  const MINIMAL_IDENTITY = ${JSON.stringify(MINIMAL_IDENTITY)}
10
10
  const optimizedSessions = new Map()
11
11
 
12
- export const OpenChamberSystemPromptPlugin = async () => ({
12
+ export const AiWorkerSystemPromptPlugin = async () => ({
13
13
  "chat.message": async (input, output) => {
14
14
  if (!input.sessionID) return
15
15
  const agent = output?.message?.agent ?? input.agent
@@ -33,25 +33,6 @@ export const OpenChamberSystemPromptPlugin = async () => ({
33
33
  })
34
34
  `;
35
35
 
36
- const mergePluginConfig = (rawConfig, pluginUrl) => {
37
- const errors = [];
38
- const parsed = typeof rawConfig === 'string' && rawConfig.trim()
39
- ? parseJsonc(rawConfig, errors, { allowTrailingComma: true })
40
- : {};
41
- if (errors.length > 0 || !parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
42
- throw new Error('OPENCODE_CONFIG_CONTENT must contain a valid JSON object before OpenChamber can inject its system prompt optimizer');
43
- }
44
- if (parsed.plugin !== undefined && !Array.isArray(parsed.plugin)) {
45
- throw new Error('OPENCODE_CONFIG_CONTENT plugin must be an array before OpenChamber can inject its system prompt optimizer');
46
- }
47
- const configured = Array.isArray(parsed.plugin) ? parsed.plugin : [];
48
- parsed.plugin = [
49
- ...configured.filter((value) => value !== pluginUrl && (!Array.isArray(value) || value[0] !== pluginUrl)),
50
- pluginUrl,
51
- ];
52
- return JSON.stringify(parsed);
53
- };
54
-
55
36
  export const createSystemPromptRuntime = ({ fsPromises, path, dataDir }) => {
56
37
  const pluginDirectory = path.join(dataDir, 'system-prompt');
57
38
  const pluginPath = path.join(pluginDirectory, 'openchamber-system-prompt-plugin.js');
@@ -60,7 +41,7 @@ export const createSystemPromptRuntime = ({ fsPromises, path, dataDir }) => {
60
41
  await fsPromises.mkdir(pluginDirectory, { recursive: true });
61
42
  await fsPromises.writeFile(pluginPath, createPluginSource(), { mode: 0o600 });
62
43
  return {
63
- OPENCODE_CONFIG_CONTENT: mergePluginConfig(rawConfig, pathToFileURL(pluginPath).href),
44
+ OPENCODE_CONFIG_CONTENT: appendManagedPlugin(rawConfig, pathToFileURL(pluginPath).href, 'system prompt optimizer'),
64
45
  };
65
46
  };
66
47
 
@@ -25,7 +25,7 @@ describe('managed system prompt runtime', () => {
25
25
  expect(config.model).toBe('test/model');
26
26
  expect(config.plugin).toEqual(['file:///existing.js', pathToFileURL(pluginPath).href]);
27
27
  const pluginModule = await import(`${pathToFileURL(pluginPath).href}?test=${Date.now()}`);
28
- const hooks = await pluginModule.OpenChamberSystemPromptPlugin();
28
+ const hooks = await pluginModule.AiWorkerSystemPromptPlugin();
29
29
  const output = {
30
30
  system: ['Behavioral prompt\nYou are powered by the model named GPT.\n<env>kept</env>'],
31
31
  };
@@ -46,7 +46,7 @@ describe('managed system prompt runtime', () => {
46
46
  await runtime.prepareManagedOpenCodeEnv('{}');
47
47
  const pluginPath = path.join(dataDir, 'system-prompt', 'openchamber-system-prompt-plugin.js');
48
48
  const pluginModule = await import(`${pathToFileURL(pluginPath).href}?test=${Date.now()}`);
49
- const hooks = await pluginModule.OpenChamberSystemPromptPlugin();
49
+ const hooks = await pluginModule.AiWorkerSystemPromptPlugin();
50
50
  const output = { system: ['Unrecognized prompt'] };
51
51
  await hooks['chat.message']({ sessionID: 'session-1', agent: 'plan' });
52
52
  await hooks['experimental.chat.system.transform']({ sessionID: 'session-1' }, output);
@@ -60,7 +60,7 @@ describe('managed system prompt runtime', () => {
60
60
  await runtime.prepareManagedOpenCodeEnv('{}');
61
61
  const pluginPath = path.join(dataDir, 'system-prompt', 'openchamber-system-prompt-plugin.js');
62
62
  const pluginModule = await import(`${pathToFileURL(pluginPath).href}?test=${Date.now()}`);
63
- const hooks = await pluginModule.OpenChamberSystemPromptPlugin();
63
+ const hooks = await pluginModule.AiWorkerSystemPromptPlugin();
64
64
  const output = {
65
65
  system: ['Custom agent prompt\nYou are powered by the model named GPT.\n<env>kept</env>'],
66
66
  };
@@ -2,14 +2,14 @@
2
2
 
3
3
  ## Ownership
4
4
 
5
- `runtime.js` owns terminal identity, PTY processes, status, ordered output, bounded scrollback, WebSocket attachments, and lifecycle routes. `shells.js` discovers executable shell families and resolves the persisted shell ID without accepting command strings or arguments. Clients own tab arrangement and choose stable terminal IDs. Electron uses this same runtime in-process; VS Code returns an explicit unsupported error.
5
+ `runtime.js` owns terminal identity, PTY processes, launch mode, session purpose, ordered output, bounded scrollback, WebSocket attachments, and lifecycle routes. `shells.js` discovers executable shell families, resolves the persisted shell ID, and builds the per-shell argv for interactive versus command launches. Clients own tab arrangement. Interactive terminals use stable IDs; project actions keep a stable UI tab and allocate a fresh terminal ID for each command execution. Electron uses this same runtime in-process; VS Code returns an explicit unsupported error.
6
6
 
7
7
  ## Protocol
8
8
 
9
9
  `/api/terminal/ws` is the only terminal data transport. It uses v3 binary JSON control frames and is opened through `openRuntimeWebSocket`, preserving direct, Electron proxy, URL-token authentication, and private-relay routing.
10
10
 
11
11
  - `attach` registers a connection for one terminal. One socket may attach to many terminals.
12
- - Every attach and reconnect begins with an authoritative `snapshot` containing bounded history and the current sequence.
12
+ - Every attach and reconnect begins with an authoritative `snapshot` containing bounded history, the current sequence, and the PTY `cols`/`rows` the history was drawn for. The client replays the history at that size before fitting its viewport; replaying shell output at another width leaves stray fragments that the shell's own SIGWINCH redraw never clears.
13
13
  - A current socket that closes or errors before its initial `open` invalidates its URL-scoped auth token before retrying, so retries mint a fresh token instead of backing off against a rejected upgrade. Hidden or offline clients wait 60 seconds and wake promptly on visibility/online recovery.
14
14
  - `output`, `exit`, and `restarted` carry monotonically increasing per-terminal sequences. Output carries raw live bytes plus replay-safe bytes with terminal query exchanges removed.
15
15
  - Attach registers before capturing the snapshot, buffers concurrent events, drops events represented by the snapshot sequence, then enters live delivery.
@@ -19,21 +19,25 @@
19
19
 
20
20
  HTTP remains the authenticated command plane for create, resize, appearance updates, restart, close, and force-kill. There is no SSE output or HTTP input compatibility path.
21
21
 
22
- `GET /api/terminal/sessions` enumerates live sessions (optionally filtered by resolved `cwd`) so clients can adopt terminals their local tab projection does not know about — another device, a new browser tab, or cleared storage. `POST /api/terminal/touch` refreshes `lastActivity` for the listed session ids; open clients call it periodically so background tabs, which hold no WebSocket attachment, are not idle-reaped while a client still shows them.
22
+ `GET /api/terminal/sessions` enumerates live sessions (optionally filtered by resolved `cwd`) so clients can adopt terminals their local tab projection does not know about — another device, a new browser tab, or cleared storage. Listings include the effective launch mode and the normalized purpose, but never the command text. `POST /api/terminal/touch` refreshes `lastActivity` for the listed session ids; open clients call it periodically so background tabs, which hold no WebSocket attachment, are not idle-reaped while a client still shows them.
23
23
 
24
24
  ## PTY Lifecycle
25
25
 
26
26
  - IDs are client-provided or generated with `randomUUID()`.
27
- - Concurrent creates for one ID are single-flight only when working directory and shell preference match. Existing IDs cannot be reused for another working directory.
27
+ - Create defaults to interactive mode. Command mode requires a non-empty trimmed command no longer than the terminal input limit and launches the shell so the PTY exits when that command exits. Each session also carries a normalized purpose. Omitted purpose means `{ type: 'terminal' }`. Project actions use `{ type: 'project-action', actionId, executionId }`, and the server validates both IDs as non-empty bounded strings. Create responses and attach snapshots echo the effective mode and purpose.
28
+ - Concurrent creates for one ID are single-flight only when working directory, shell preference, login mode, launch mode, and session purpose match. Command-mode creates must also match the command text, unless the purpose is a project action that is already running for the same resolved `(cwd, actionId)` pair. In that case the runtime returns the existing session and its existing execution identity, even when another client requested a different session ID. Existing IDs cannot be reused for another working directory or another purpose.
28
29
  - Dimensions are bounded to 1-1000 columns and 1-500 rows; input is capped at 64 KiB.
29
30
  - A client may create before its renderer has mounted. It derives an initial size from the container and font metrics (falling back to 80x24 when unavailable), then sends a resize once Ghostty reports its final dimensions. This allows shell startup and renderer initialization to overlap.
30
- - PTY children explicitly clear `NODE_CHANNEL_FD`; daemon IPC descriptors are host-private and invalid after PTY descriptor cleanup.
31
- - PTY children also strip AppImage `ARGV0` (and other host-private shell vars such as `ELECTRON_RUN_AS_NODE`, `BASH_ENV`, `ENV`, `BASH_XTRACEFD`). An exported `ARGV0` makes zsh rewrite argv[0] for every external command, which breaks Python venv detection and other argv[0]/$0 consumers while leaving `/proc/self/exe` correct. On Linux, PTY spawn is wrapped with `env -u ARGV0` because `bun-pty` merges the native OS environ and would otherwise reintroduce `ARGV0` after a JS-only delete.
32
- - `GET /api/terminal/shells` reports shell IDs available on the active server using the same augmented PATH provided to spawned PTYs, plus whether each executable has a supported login-mode argument. `auto` preserves environment/platform fallback order; an explicit unavailable shell fails creation instead of silently running a different shell. Login mode is opt-in and uses only built-in arguments for known shells. Preference changes affect new sessions and explicit restarts, not running PTYs.
33
- - PTY data and exit callbacks enter one FIFO queue. Stale callbacks from replaced processes are ignored.
31
+ - PTY children never inherit `NODE_CHANNEL_FD`; daemon IPC descriptors are host-private and invalid after PTY descriptor cleanup, and an inherited value (even empty) makes Node CLIs such as opencode print `Failed to parse IPC channel number` on exit.
32
+ - PTY children also strip AppImage `ARGV0` (and other host-private shell vars such as `ELECTRON_RUN_AS_NODE`, `BASH_ENV`, `ENV`, `BASH_XTRACEFD`). An exported `ARGV0` makes zsh rewrite argv[0] for every external command, which breaks Python venv detection and other argv[0]/$0 consumers while leaving `/proc/self/exe` correct. On POSIX, PTY spawn is wrapped with `env -u ARGV0 -u NODE_CHANNEL_FD` (`resolvePosixPtyLaunch`) because `bun-pty` merges the native OS environ and would otherwise reintroduce both after a JS-only delete.
33
+ - `GET /api/terminal/shells` reports shell IDs available on the active server using the same augmented PATH provided to spawned PTYs, plus whether each executable has a supported login-mode argument. `auto` preserves environment/platform fallback order; an explicit unavailable shell fails creation instead of silently running a different shell. Login mode is opt-in and uses only built-in arguments for known shells. Interactive shells still launch as before. Command-mode launches reuse the same environment and login support, but switch argv by shell family: POSIX and Fish use interactive `-c`, Nushell uses `-c`, PowerShell uses `-Command`, and cmd uses `/d /s /c`. Preference changes affect new sessions and explicit restarts, not running PTYs.
34
+ - PTY data and exit callbacks enter one FIFO queue. The runtime wires those listeners in the same synchronous turn that receives the PTY object. `node-pty` and `bun-pty` both expose the PTY before dispatching registered callbacks. If a backend emitted exit before listener registration, this layer could not recover it, so the wiring stays adjacent to PTY creation.
34
35
  - Scrollback is retained on the server and capped at 512 KiB with UTF-8-safe trimming. Device-status, device-attribute, cursor-position reply, and color-query exchanges are removed from replay history with incomplete control sequences carried across PTY chunks; live output remains byte-for-byte unchanged.
35
- - Exited sessions remain attachable until explicit close or idle cleanup.
36
- - Restarts are serialized per terminal. Each restart spawns and wires the replacement before terminating the old process, retaining the terminal ID.
36
+ - Exited sessions remain attachable until explicit close, idle cleanup, or a successful replacement of the same project action. Creating a replacement retires only exited records for the same resolved directory and action, after the new PTY starts. Failed creation preserves the old record and output. These replaced records do not exhaust the terminal capacity limit.
37
+ - Deduplicated create responses may describe another client's execution. Cancellation cleanup closes only the terminal ID allocated for the cancelled request; it never closes an adopted peer execution.
38
+ - Create and restart validate the working directory with a real `stat` and answer HTTP 400 `Invalid working directory` when it is not a directory. When the path does not exist at all (`ENOENT`/`ENOTDIR`, a worktree deleted outside AiWorker) the body also carries `code: "TERMINAL_CWD_MISSING"`. The client shows the failure without moving the session. The runtime never substitutes a parent directory on its own.
39
+ - Restarts are serialized per terminal. Each restart spawns and wires the replacement before terminating the old process, retaining the terminal ID. Command-mode sessions reject restart with HTTP 400 instead of silently turning into interactive shells with stale action metadata.
40
+ - A delete that arrives while create is still pending leaves a cancellation tombstone. When the PTY arrives, the runtime terminates it immediately, never inserts the session into the live map, and returns a create error while the delete still succeeds.
37
41
  - Close uses SIGTERM with bounded SIGKILL escalation. Force-kill, idle cleanup, and runtime shutdown terminate process groups immediately where supported. Removal explicitly sends a fatal scoped closure and evicts client projections even when a PTY backend fails to emit `onExit`; attached terminals are not considered idle.
38
42
 
39
43
  ## Security And Relay