@vantran-se/openchamber-web 1.24.4 → 2.0.2

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 (367) hide show
  1. package/README.md +2 -8
  2. package/bin/lib/commands-connect-url.js +3 -8
  3. package/bin/lib/commands-tunnel.js +3 -0
  4. package/dist/assets/{CodeMirrorCommentWidgets-B4a0JkUz.js → CodeMirrorCommentWidgets-B82Oye3V.js} +1 -1
  5. package/dist/assets/DiffView-DJLmWn6Z.js +4 -0
  6. package/dist/assets/FilesView-DjksmZRo.js +17 -0
  7. package/dist/assets/{GitView-Byu2DY7s.js → GitView-DiUsUIgr.js} +4 -4
  8. package/dist/assets/{GuestAttachDialog-B808E0-d.js → GuestAttachDialog-CYwWYLr3.js} +1 -1
  9. package/dist/assets/GuestSurfacePane-CmctyRI4.js +1 -0
  10. package/dist/assets/{JsonTreeView-VW_7czo3.js → JsonTreeView-DOHHcbgJ.js} +1 -1
  11. package/dist/assets/LinearIssuesView-DaqnylcV.js +1 -0
  12. package/dist/assets/MarkdownImageGallery-HJNvWt7L.js +1 -0
  13. package/dist/assets/MarkdownRendererImpl-nDZRGg2I.js +12 -0
  14. package/dist/assets/OnboardingScreen-C2B5Bsof.js +1 -0
  15. package/dist/assets/{PatchDiff-DCjGhlYV.js → PatchDiff-C4FLBjpt.js} +1 -1
  16. package/dist/assets/{PierreDiffViewer-CEDZNkU2.js → PierreDiffViewer-C_1S9c9n.js} +1 -1
  17. package/dist/assets/PlanView-_eZvb-G5.js +5 -0
  18. package/dist/assets/PluginPane-sM8fcZIV.js +3 -0
  19. package/dist/assets/SettingsView-BRfr46hb.js +29 -0
  20. package/dist/assets/SettingsWindow-CcUDwQHg.js +1 -0
  21. package/dist/assets/SubmoduleDiffSummary-CrN1ZGwl.js +1 -0
  22. package/dist/assets/ToolOutputDialog-Cij4jI09.js +15 -0
  23. package/dist/assets/ToolPartDiffPreview-BXzwQ-JY.js +8 -0
  24. package/dist/assets/{VirtualizedFileDiff-NU0FHFhT.js → VirtualizedFileDiff-DSWTLJNH.js} +4 -4
  25. package/dist/assets/{WalkthroughView-BZyjhqHv.js → WalkthroughView-CpUUgY8H.js} +2 -2
  26. package/dist/assets/appThemeRegistry-B5tHTkR8.js +1 -0
  27. package/dist/assets/{commitAndPush-BDftrjr6.js → commitAndPush-D7D8qV8p.js} +1 -1
  28. package/dist/assets/{computeVirtualFileMetrics-DlWlEM3x.js → computeVirtualFileMetrics-cq9uQ30i.js} +1 -1
  29. package/dist/assets/de-CUO1-KqF.js +10 -0
  30. package/dist/assets/es-BlbC6sGJ.js +16 -0
  31. package/dist/assets/fr-D2nwe_0W.js +14 -0
  32. package/dist/assets/index-BQOoYIOu.js +1 -0
  33. package/dist/assets/index-CLcM8HKx.css +1 -0
  34. package/dist/assets/index-CvLgnuNI.js +41 -0
  35. package/dist/assets/{isDiffPlainText-CO4e5J_S.js → isDiffPlainText-D0obJ2p8.js} +1 -1
  36. package/dist/assets/{isFilePlainText-ue6kCou7.js → isFilePlainText-DB3_rjyW.js} +1 -1
  37. package/dist/assets/ja-CA7vnDsZ.js +16 -0
  38. package/dist/assets/ko-Chy6a9l-.js +16 -0
  39. package/dist/assets/main-B9JFK3vF.js +896 -0
  40. package/dist/assets/main-Bhiqnrhd.js +2 -0
  41. package/dist/assets/{markdownCore-CK48LG0z.js → markdownCore-tfqOFaBd.js} +4 -4
  42. package/dist/assets/miniChat-B0UOL5zA.js +2 -0
  43. package/dist/assets/mobile-BJppFIEo.js +2 -0
  44. package/dist/assets/{patchFileDiff-D7QVlovD.js → patchFileDiff-Db6KuwCP.js} +3 -3
  45. package/dist/assets/pl-CTR5A_1P.js +16 -0
  46. package/dist/assets/pt-BR-BE1O1KEx.js +16 -0
  47. package/dist/assets/renderElectronMiniChatApp-Yz5sfoFh.js +2 -0
  48. package/dist/assets/renderMobileApp-QQ-yB8aY.js +25 -0
  49. package/dist/assets/runtimeEndpointReset-DKDdzRzm.js +27 -0
  50. package/dist/assets/scheduledTasksApi-C5sCuNqf.js +1 -0
  51. package/dist/assets/{shikiHighlight-BPu3JiQs.js → shikiHighlight-xURoo5t_.js} +1 -1
  52. package/dist/assets/{textMateThemeFromAppTheme-DWqRPo9A.js → textMateThemeFromAppTheme-BZLGuYTU.js} +1 -1
  53. package/dist/assets/tr-Bh5vK6cW.js +7 -0
  54. package/dist/assets/uk-CLDkZFy_.js +16 -0
  55. package/dist/assets/useAppFontEffects-BxrtCnfR.js +7337 -0
  56. package/dist/assets/{useInlineCommentController-CgS0fnw_.js → useInlineCommentController-CeXuR-SR.js} +2 -2
  57. package/dist/assets/useRootScrollLock-DgWxezc1.js +2 -0
  58. package/dist/assets/{vendor-codemirror-language-data-Bx2EHc1O.js → vendor-codemirror-language-data-DOuaQUZ0.js} +2 -2
  59. package/dist/assets/vendor-cron-parser-CjvYVigk.js +2 -0
  60. package/dist/assets/vendor-opencode-client-_BAHQo4R.js +12 -0
  61. package/dist/assets/{vendor-remixicon-react-BDmGmFCA.js → vendor-remixicon-react-D6dZ0UwF.js} +1 -1
  62. package/dist/assets/{vendor-zod-CgAdXWZO.js → vendor-zod-Gea9uBlY.js} +1 -1
  63. package/dist/assets/{web-components-DxX8bZ1o.js → web-components-63H1h21j.js} +2 -2
  64. package/dist/assets/zh-CN-DODgcmYj.js +16 -0
  65. package/dist/assets/zh-TW-ByNfIwLA.js +16 -0
  66. package/dist/index.html +13 -7
  67. package/dist/mini-chat.html +5 -5
  68. package/dist/mobile.html +5 -5
  69. package/package.json +6 -5
  70. package/server/index.d.ts +1 -0
  71. package/server/index.js +246 -91
  72. package/server/lib/agent-tool/DOCUMENTATION.md +32 -15
  73. package/server/lib/agent-tool/runtime.js +135 -54
  74. package/server/lib/agent-tool/runtime.test.js +155 -87
  75. package/server/lib/browser-control/DOCUMENTATION.md +37 -2
  76. package/server/lib/browser-control/provider.js +217 -0
  77. package/server/lib/browser-control/provider.test.js +251 -0
  78. package/server/lib/context-obligatory/runtime.js +61 -50
  79. package/server/lib/context-obligatory/runtime.test.js +62 -162
  80. package/server/lib/event-stream/DOCUMENTATION.md +12 -1
  81. package/server/lib/event-stream/delta-coalescer.js +0 -0
  82. package/server/lib/event-stream/delta-coalescer.test.js +68 -57
  83. package/server/lib/event-stream/directory-ws-bridge.js +1 -1
  84. package/server/lib/event-stream/global-hub.js +10 -1
  85. package/server/lib/event-stream/global-hub.test.js +27 -26
  86. package/server/lib/event-stream/index.js +6 -0
  87. package/server/lib/event-stream/protocol.js +26 -19
  88. package/server/lib/event-stream/protocol.test.js +18 -7
  89. package/server/lib/event-stream/rebind.test.js +5 -5
  90. package/server/lib/event-stream/runtime.test.js +17 -15
  91. package/server/lib/event-stream/translate-v2.js +313 -0
  92. package/server/lib/event-stream/translate-v2.test.js +209 -0
  93. package/server/lib/event-stream/upstream-reader.test.js +16 -16
  94. package/server/lib/fs/DOCUMENTATION.md +4 -2
  95. package/server/lib/fs/byte-range.js +35 -0
  96. package/server/lib/fs/byte-range.test.js +31 -0
  97. package/server/lib/fs/routes.js +47 -42
  98. package/server/lib/fs/routes.test.js +123 -18
  99. package/server/lib/git/service.js +12 -0
  100. package/server/lib/github/DOCUMENTATION.md +1 -1
  101. package/server/lib/github/pr-status.js +22 -2
  102. package/server/lib/github/pr-status.test.js +27 -14
  103. package/server/lib/guests/DOCUMENTATION.md +13 -0
  104. package/server/lib/guests/catalog.js +12 -0
  105. package/server/lib/guests/catalog.test.js +33 -0
  106. package/server/lib/guests/routes.js +10 -1
  107. package/server/lib/guests/service.js +192 -14
  108. package/server/lib/guests/service.test.js +201 -1
  109. package/server/lib/guests/surface.js +630 -0
  110. package/server/lib/guests/surface.test.js +486 -0
  111. package/server/lib/markdown-image-grants/routes.js +18 -5
  112. package/server/lib/markdown-image-grants/routes.test.js +9 -6
  113. package/server/lib/message-queue/DOCUMENTATION.md +33 -22
  114. package/server/lib/message-queue/runtime.js +155 -100
  115. package/server/lib/message-queue/runtime.test.js +168 -82
  116. package/server/lib/notifications/DOCUMENTATION.md +28 -0
  117. package/server/lib/notifications/control-stream.http.test.js +68 -0
  118. package/server/lib/notifications/emitter-runtime.js +15 -0
  119. package/server/lib/notifications/emitter-runtime.test.js +32 -0
  120. package/server/lib/notifications/push-runtime.js +5 -1
  121. package/server/lib/notifications/push-runtime.test.js +2 -2
  122. package/server/lib/notifications/routes.js +1 -1
  123. package/server/lib/notifications/runtime.js +115 -26
  124. package/server/lib/notifications/template-runtime.js +13 -15
  125. package/server/lib/notifications/template-runtime.test.js +16 -9
  126. package/server/lib/notifications/trigger-runtime.test.js +187 -0
  127. package/server/lib/openchamber-control/DOCUMENTATION.md +10 -0
  128. package/server/lib/openchamber-control/actions.js +3 -2
  129. package/server/lib/openchamber-control/error.js +1 -1
  130. package/server/lib/openchamber-control/file-open.js +59 -0
  131. package/server/lib/openchamber-control/file-open.test.js +57 -0
  132. package/server/lib/openchamber-control/routes.test.js +12 -0
  133. package/server/lib/openchamber-control/service.js +102 -49
  134. package/server/lib/openchamber-control/service.test.js +84 -44
  135. package/server/lib/openchamber-sessions/archive-store.js +224 -0
  136. package/server/lib/openchamber-sessions/archive-store.test.js +187 -0
  137. package/server/lib/openchamber-sessions/opencode-client.js +22 -0
  138. package/server/lib/openchamber-sessions/routes.js +398 -328
  139. package/server/lib/openchamber-sessions/routes.test.js +777 -740
  140. package/server/lib/openchamber-sessions/session-metadata-store.js +285 -0
  141. package/server/lib/openchamber-sessions/session-metadata-store.test.js +259 -0
  142. package/server/lib/opencode/DOCUMENTATION.md +449 -29
  143. package/server/lib/opencode/agents.js +233 -327
  144. package/server/lib/opencode/auth.js +41 -44
  145. package/server/lib/opencode/auth.test.js +92 -0
  146. package/server/lib/opencode/cli-upgrade.d.ts +4 -0
  147. package/server/lib/opencode/cli-upgrade.js +18 -0
  148. package/server/lib/opencode/cli-upgrade.test.js +36 -0
  149. package/server/lib/opencode/commands.js +164 -145
  150. package/server/lib/opencode/compatibility.d.ts +16 -0
  151. package/server/lib/opencode/compatibility.js +88 -0
  152. package/server/lib/opencode/compatibility.test.js +62 -0
  153. package/server/lib/opencode/config-entity-routes.js +58 -29
  154. package/server/lib/opencode/config-entity-v2.test.js +290 -0
  155. package/server/lib/opencode/config-mutation-response.js +17 -7
  156. package/server/lib/opencode/config-mutation-response.test.js +4 -7
  157. package/server/lib/opencode/config-paths.test.js +2 -2
  158. package/server/lib/opencode/config-v2.d.ts +183 -0
  159. package/server/lib/opencode/config-v2.js +801 -0
  160. package/server/lib/opencode/config-v2.test.js +466 -0
  161. package/server/lib/opencode/credential-db.js +144 -0
  162. package/server/lib/opencode/credential-db.test.js +104 -0
  163. package/server/lib/opencode/env-runtime.js +48 -9
  164. package/server/lib/opencode/env-runtime.test.js +45 -0
  165. package/server/lib/opencode/feature-routes-runtime.js +15 -5
  166. package/server/lib/opencode/lifecycle-process.test.js +7 -4
  167. package/server/lib/opencode/lifecycle.js +85 -30
  168. package/server/lib/opencode/lifecycle.test.js +80 -8
  169. package/server/lib/opencode/login-shell-env.js +27 -0
  170. package/server/lib/opencode/managed-config-file.js +119 -0
  171. package/server/lib/opencode/managed-config-file.test.js +152 -0
  172. package/server/lib/opencode/managed-plugin-config.js +33 -13
  173. package/server/lib/opencode/mcp.js +43 -149
  174. package/server/lib/opencode/network-runtime.js +6 -7
  175. package/server/lib/opencode/openchamber-routes.test.js +2 -5
  176. package/server/lib/opencode/plugin-routes.js +3 -3
  177. package/server/lib/opencode/plugin-routes.test.js +6 -24
  178. package/server/lib/opencode/plugins.js +91 -40
  179. package/server/lib/opencode/plugins.test.js +28 -15
  180. package/server/lib/opencode/providers.js +63 -83
  181. package/server/lib/opencode/providers.test.js +128 -59
  182. package/server/lib/opencode/proxy-agent-wiring.test.js +4 -2
  183. package/server/lib/opencode/proxy.js +179 -129
  184. package/server/lib/opencode/pwa-manifest-routes.js +4 -2
  185. package/server/lib/opencode/pwa-manifest-routes.test.js +3 -2
  186. package/server/lib/opencode/response-envelope.js +14 -0
  187. package/server/lib/opencode/response-envelope.test.js +23 -0
  188. package/server/lib/opencode/routes-upgrade.test.js +73 -116
  189. package/server/lib/opencode/routes.js +121 -428
  190. package/server/lib/opencode/server-utils-runtime.js +10 -4
  191. package/server/lib/opencode/session-activity.js +78 -0
  192. package/server/lib/opencode/session-runtime.js +21 -10
  193. package/server/lib/opencode/session-runtime.test.js +9 -9
  194. package/server/lib/opencode/settings-helpers.js +9 -9
  195. package/server/lib/opencode/settings-helpers.test.js +7 -12
  196. package/server/lib/opencode/settings-registry.json +6 -8
  197. package/server/lib/opencode/settings-runtime.js +18 -1
  198. package/server/lib/opencode/settings-runtime.test.js +31 -2
  199. package/server/lib/opencode/shared.js +54 -22
  200. package/server/lib/opencode/shared.test.js +8 -4
  201. package/server/lib/opencode/shutdown-runtime.js +54 -7
  202. package/server/lib/opencode/shutdown-runtime.test.js +224 -30
  203. package/server/lib/opencode/skill-routes.js +23 -24
  204. package/server/lib/opencode/skills.js +7 -5
  205. package/server/lib/opencode/tunnel-wiring-runtime.js +2 -1
  206. package/server/lib/opencode/upgrade-capability.test.js +1 -1
  207. package/server/lib/opencode/v1-migration-topup.js +288 -0
  208. package/server/lib/opencode/v1-migration-topup.test.js +266 -0
  209. package/server/lib/opencode/v2-install.d.ts +8 -0
  210. package/server/lib/opencode/v2-install.js +158 -0
  211. package/server/lib/opencode/v2-install.test.js +124 -0
  212. package/server/lib/opencode/watcher.js +10 -3
  213. package/server/lib/opencode/watcher.test.js +17 -21
  214. package/server/lib/opencode/websearch-config.js +66 -0
  215. package/server/lib/package-manager.js +55 -68
  216. package/server/lib/package-manager.test.js +241 -75
  217. package/server/lib/permission-auto-accept/runtime.js +38 -22
  218. package/server/lib/permission-auto-accept/runtime.test.js +39 -17
  219. package/server/lib/quota/DOCUMENTATION.md +4 -0
  220. package/server/lib/quota/providers/xai.js +4 -4
  221. package/server/lib/relay/tunnel-host.js +4 -1
  222. package/server/lib/relay/tunnel-host.test.js +7 -0
  223. package/server/lib/routing/DOCUMENTATION.md +52 -21
  224. package/server/lib/routing/defaults.js +23 -6
  225. package/server/lib/routing/jev.js +17 -7
  226. package/server/lib/routing/routes.http.test.js +68 -33
  227. package/server/lib/routing/routes.js +65 -22
  228. package/server/lib/routing/runtime.js +145 -48
  229. package/server/lib/routing/runtime.test.js +79 -55
  230. package/server/lib/scheduled-tasks/DOCUMENTATION.md +4 -0
  231. package/server/lib/scheduled-tasks/issue-2710-double-execution.test.js +51 -46
  232. package/server/lib/scheduled-tasks/runtime.js +81 -74
  233. package/server/lib/scheduled-tasks/runtime.test.js +53 -1
  234. package/server/lib/session-assist/DOCUMENTATION.md +8 -3
  235. package/server/lib/session-assist/context.js +122 -46
  236. package/server/lib/session-assist/context.test.js +117 -49
  237. package/server/lib/session-assist/runtime.js +52 -22
  238. package/server/lib/session-assist/runtime.test.js +145 -173
  239. package/server/lib/session-goal/DOCUMENTATION.md +23 -15
  240. package/server/lib/session-goal/create.js +9 -12
  241. package/server/lib/session-goal/create.test.js +27 -28
  242. package/server/lib/session-goal/runtime.js +164 -77
  243. package/server/lib/session-goal/runtime.test.js +271 -519
  244. package/server/lib/session-knowledge/DOCUMENTATION.md +5 -0
  245. package/server/lib/session-knowledge/runtime.js +59 -41
  246. package/server/lib/session-knowledge/runtime.test.js +90 -20
  247. package/server/lib/small-model/DOCUMENTATION.md +154 -208
  248. package/server/lib/small-model/client.js +113 -0
  249. package/server/lib/small-model/index.js +296 -158
  250. package/server/lib/small-model/index.test.js +423 -256
  251. package/server/lib/small-model/routes.js +1 -1
  252. package/server/lib/spaces/DOCUMENTATION.md +807 -0
  253. package/server/lib/spaces/code-in-bait.js +185 -0
  254. package/server/lib/spaces/code-in.js +641 -0
  255. package/server/lib/spaces/code-in.test.js +1048 -0
  256. package/server/lib/spaces/errors.js +8 -0
  257. package/server/lib/spaces/exec-http.js +94 -0
  258. package/server/lib/spaces/gatekeeper-channel.js +202 -0
  259. package/server/lib/spaces/gatekeeper-channel.test.js +215 -0
  260. package/server/lib/spaces/gatekeeper-program.cjs +914 -0
  261. package/server/lib/spaces/gatekeeper-program.test.js +1391 -0
  262. package/server/lib/spaces/hardening.js +457 -0
  263. package/server/lib/spaces/hardening.test.js +368 -0
  264. package/server/lib/spaces/host-git.js +127 -0
  265. package/server/lib/spaces/host-git.test.js +137 -0
  266. package/server/lib/spaces/labels.js +234 -0
  267. package/server/lib/spaces/labels.test.js +219 -0
  268. package/server/lib/spaces/layout.js +184 -0
  269. package/server/lib/spaces/layout.test.js +30 -0
  270. package/server/lib/spaces/manager.js +82 -0
  271. package/server/lib/spaces/manager.test.js +114 -0
  272. package/server/lib/spaces/places/code-in.docker.live.test.js +216 -0
  273. package/server/lib/spaces/places/contract-suite.js +156 -0
  274. package/server/lib/spaces/places/contract.docker.live.test.js +7 -0
  275. package/server/lib/spaces/places/contract.test.js +6 -0
  276. package/server/lib/spaces/places/docker-engine.js +126 -0
  277. package/server/lib/spaces/places/docker-live-support.js +202 -0
  278. package/server/lib/spaces/places/docker-tools.js +186 -0
  279. package/server/lib/spaces/places/docker.js +587 -0
  280. package/server/lib/spaces/places/docker.test.js +1610 -0
  281. package/server/lib/spaces/places/escape-suite.js +1018 -0
  282. package/server/lib/spaces/places/escape.docker.live.test.js +7 -0
  283. package/server/lib/spaces/places/fake-docker.js +375 -0
  284. package/server/lib/spaces/places/memory-place.js +103 -0
  285. package/server/lib/spaces/places/packed.docker.live.test.js +77 -0
  286. package/server/lib/spaces/places/registry.js +43 -0
  287. package/server/lib/spaces/places/registry.test.js +50 -0
  288. package/server/lib/spaces/places/server.docker.live.test.js +225 -0
  289. package/server/lib/spaces/run-command.js +261 -0
  290. package/server/lib/spaces/run-command.test.js +396 -0
  291. package/server/lib/spaces/space-server.js +190 -0
  292. package/server/lib/spaces/space-server.test.js +335 -0
  293. package/server/lib/spaces/tools-filler.js +82 -0
  294. package/server/lib/spaces/tools-filler.test.js +148 -0
  295. package/server/lib/spaces/tools-pack.js +49 -0
  296. package/server/lib/spaces/tools-pack.test.js +57 -0
  297. package/server/lib/spaces/tools.js +138 -0
  298. package/server/lib/spaces/tools.test.js +153 -0
  299. package/server/lib/tts/openai-sdk.js +11 -0
  300. package/server/lib/tts/service.js +7 -5
  301. package/server/lib/tts/stt.js +3 -2
  302. package/server/lib/tunnels/DOCUMENTATION.md +2 -0
  303. package/server/lib/tunnels/routes.js +7 -0
  304. package/server/lib/tunnels/routes.test.js +55 -0
  305. package/server/lib/ui-auth/ui-auth.js +38 -2
  306. package/server/lib/ui-auth/ui-auth.test.js +42 -0
  307. package/server/lib/ui-auth/ui-passkeys.js +12 -10
  308. package/server/lib/walkthrough/DOCUMENTATION.md +10 -1
  309. package/server/lib/walkthrough/pull-request.js +61 -8
  310. package/server/lib/walkthrough/pull-request.test.js +54 -1
  311. package/server/lib/walkthrough/routes.js +20 -0
  312. package/server/lib/walkthrough/routes.test.js +19 -0
  313. package/server/opencode-proxy.test.js +190 -166
  314. package/server/opencode-worktree-readiness.test.js +5 -4
  315. package/dist/assets/DiffView-B2CD9wC8.js +0 -4
  316. package/dist/assets/FilesView-C4vMFclc.js +0 -13
  317. package/dist/assets/LinearIssuesView-EzGNCGrd.js +0 -1
  318. package/dist/assets/MarkdownImageGallery-B2YUDFaB.js +0 -1
  319. package/dist/assets/MarkdownRendererImpl-B4ikIzR3.js +0 -12
  320. package/dist/assets/OnboardingScreen-CQHBSTEi.js +0 -1
  321. package/dist/assets/PlanView-D3x-pTkZ.js +0 -5
  322. package/dist/assets/PluginPane-DEcgDQ9h.js +0 -3
  323. package/dist/assets/SettingsView-CSinuH-1.js +0 -29
  324. package/dist/assets/SettingsWindow-B5CHoJFZ.js +0 -1
  325. package/dist/assets/SubmoduleDiffSummary-oxeACCWs.js +0 -1
  326. package/dist/assets/ToolOutputDialog-tLNWmfcW.js +0 -15
  327. package/dist/assets/ToolPartDiffPreview-CflzmK-x.js +0 -8
  328. package/dist/assets/appThemeRegistry-DLicw4dH.js +0 -1
  329. package/dist/assets/de-Bro6_zyl.js +0 -10
  330. package/dist/assets/es-Bb9XQZvd.js +0 -16
  331. package/dist/assets/fr-Dgfzn-IL.js +0 -14
  332. package/dist/assets/index-CtQNmqUL.js +0 -1
  333. package/dist/assets/index-DqaMWNrC.css +0 -1
  334. package/dist/assets/index-Dqc6USl3.js +0 -40
  335. package/dist/assets/ja-BYsakGFh.js +0 -16
  336. package/dist/assets/ko-BSaQz9aN.js +0 -16
  337. package/dist/assets/main-B62_pjL5.js +0 -2
  338. package/dist/assets/main-BEdcOI2P.js +0 -896
  339. package/dist/assets/miniChat-LT8Rqofr.js +0 -2
  340. package/dist/assets/mobile-CqarEynh.js +0 -2
  341. package/dist/assets/pl-OIviPdoj.js +0 -16
  342. package/dist/assets/pt-BR-Cltg-E6s.js +0 -16
  343. package/dist/assets/renderElectronMiniChatApp-COyLTtZx.js +0 -2
  344. package/dist/assets/renderMobileApp-BC1MHg6g.js +0 -25
  345. package/dist/assets/runtimeEndpointReset-DdrazOVH.js +0 -25
  346. package/dist/assets/scheduledTasksApi-B2SWMGEU.js +0 -1
  347. package/dist/assets/tr-CQI-KPre.js +0 -7
  348. package/dist/assets/uk-CqW7R4DA.js +0 -16
  349. package/dist/assets/useAppFontEffects-ne84JBVL.js +0 -7333
  350. package/dist/assets/useRootScrollLock-Cpf1qoGp.js +0 -2
  351. package/dist/assets/vendor-cron-parser-CUcP7OfX.js +0 -2
  352. package/dist/assets/vendor-opencode-sdk-BJ73u-bH.js +0 -7
  353. package/dist/assets/zh-CN-CVZG_o_J.js +0 -16
  354. package/dist/assets/zh-TW-D5ZxzCZp.js +0 -16
  355. package/server/lib/opencode/mcp-oauth-callback.test.js +0 -111
  356. package/server/lib/routing/feature-flag.js +0 -11
  357. package/server/lib/small-model/call.js +0 -851
  358. package/server/lib/small-model/call.test.js +0 -1292
  359. package/server/lib/small-model/catalog.js +0 -13
  360. package/server/lib/small-model/resolve.js +0 -131
  361. package/server/lib/small-model/resolve.test.js +0 -197
  362. package/server/lib/small-model/runtime-providers.js +0 -164
  363. package/server/lib/small-model/runtime-providers.test.js +0 -118
  364. package/server/lib/system-prompt/DOCUMENTATION.md +0 -36
  365. package/server/lib/system-prompt/runtime.js +0 -49
  366. package/server/lib/system-prompt/runtime.test.js +0 -76
  367. package/server/lib/walkthrough/reproduce-2607.test.js +0 -148
@@ -0,0 +1,224 @@
1
+ /**
2
+ * OpenChamber-owned session archive state.
3
+ *
4
+ * OpenCode 2.x still carries `SessionInfo.time.archived` on the wire (sessions
5
+ * migrated from v1 keep the flag) but has no route that sets or clears it, so
6
+ * archiving is an OpenChamber capability now. The state lives beside the
7
+ * OpenCode instance it describes — one JSON file per data dir,
8
+ * `{ [sessionID]: archivedAt | null }` — and the proxy folds it back onto the
9
+ * session records it serves: a number archives, `null` is an explicit
10
+ * unarchive that overrides what OpenCode still carries, and a session the file
11
+ * does not mention keeps whatever OpenCode says.
12
+ */
13
+
14
+ import fsDefault from 'node:fs';
15
+ import pathDefault from 'node:path';
16
+
17
+ const ARCHIVE_FILE_NAME = 'sessions-archive.json';
18
+
19
+ const asNonEmptyString = (value) => {
20
+ if (typeof value !== 'string') return null;
21
+ const trimmed = value.trim();
22
+ return trimmed.length > 0 ? trimmed : null;
23
+ };
24
+
25
+ const asTimestamp = (value) => (Number.isSafeInteger(value) && value > 0 ? value : null);
26
+
27
+ const asIdList = (ids) => {
28
+ if (!Array.isArray(ids)) return [];
29
+ const seen = new Set();
30
+ const result = [];
31
+ for (const value of ids) {
32
+ const id = asNonEmptyString(value);
33
+ if (!id || seen.has(id)) continue;
34
+ seen.add(id);
35
+ result.push(id);
36
+ }
37
+ return result;
38
+ };
39
+
40
+ /**
41
+ * @param {object} options
42
+ * @param {string} options.dataDir OpenChamber data directory for this instance.
43
+ * @param {typeof fsDefault.promises} [options.fsPromises]
44
+ * @param {typeof pathDefault} [options.path]
45
+ * @param {() => number} [options.now]
46
+ */
47
+ export const createArchiveStore = ({
48
+ dataDir,
49
+ fsPromises = fsDefault.promises,
50
+ path = pathDefault,
51
+ now = Date.now,
52
+ }) => {
53
+ const filePath = path.join(dataDir, ARCHIVE_FILE_NAME);
54
+
55
+ /** sessionID → archivedAt (ms). Authoritative once `loaded` is true. */
56
+ const entries = new Map();
57
+ let loaded = false;
58
+ let loadPromise = null;
59
+ /**
60
+ * Writes stay disabled until one load has told us what is already on disk.
61
+ * A failed read is not evidence that nothing is archived, and writing over
62
+ * a file we could not read would drop exactly the state we are protecting.
63
+ */
64
+ let writable = false;
65
+ /**
66
+ * One batch at a time: mutate memory, persist, roll back on failure.
67
+ * Serializing only the file write is not enough — a rollback that runs after
68
+ * a later batch has already committed would erase that batch's memory while
69
+ * its bytes stay on disk, and the next write would then erase the bytes too.
70
+ */
71
+ let transactionChain = Promise.resolve();
72
+ const runExclusive = (work) => {
73
+ const next = transactionChain.then(work, work);
74
+ transactionChain = next.then(() => undefined, () => undefined);
75
+ return next;
76
+ };
77
+
78
+ const snapshot = () => Object.fromEntries(entries);
79
+
80
+ const parseStored = (raw) => {
81
+ const parsed = JSON.parse(raw);
82
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
83
+ throw new Error('archive file is not a JSON object');
84
+ }
85
+ const result = new Map();
86
+ for (const [sessionID, value] of Object.entries(parsed)) {
87
+ const id = asNonEmptyString(sessionID);
88
+ if (!id) continue;
89
+ if (value === null) {
90
+ result.set(id, null);
91
+ continue;
92
+ }
93
+ const archivedAt = asTimestamp(value);
94
+ if (archivedAt) result.set(id, archivedAt);
95
+ }
96
+ return result;
97
+ };
98
+
99
+ const readFile = async () => {
100
+ let raw;
101
+ try {
102
+ raw = await fsPromises.readFile(filePath, 'utf8');
103
+ } catch (error) {
104
+ // No file yet is the normal first run: nothing is archived, and writing
105
+ // is safe because there is no state to lose.
106
+ if (error?.code === 'ENOENT') return { ok: true, stored: new Map() };
107
+ console.warn('[openchamber-sessions] could not read the archive file:', error?.message ?? error);
108
+ return { ok: false, stored: null };
109
+ }
110
+
111
+ try {
112
+ return { ok: true, stored: parseStored(raw) };
113
+ } catch (error) {
114
+ // Malformed bytes are kept for the user instead of being overwritten on
115
+ // the next archive, and whatever this process already knows stays in
116
+ // memory rather than collapsing to "nothing archived".
117
+ const backup = `${filePath}.corrupt-${now()}`;
118
+ await fsPromises.rename(filePath, backup).catch(() => undefined);
119
+ console.warn(
120
+ `[openchamber-sessions] archive file was unreadable and was moved to ${backup}: ${error?.message ?? error}`,
121
+ );
122
+ return { ok: true, stored: new Map() };
123
+ }
124
+ };
125
+
126
+ const load = () => {
127
+ if (!loadPromise) {
128
+ loadPromise = readFile().then((result) => {
129
+ if (result.ok) {
130
+ // Merge rather than replace: an archive that happened while the first
131
+ // load was still running must survive it.
132
+ for (const [id, archivedAt] of result.stored) {
133
+ if (!entries.has(id)) entries.set(id, archivedAt);
134
+ }
135
+ loaded = true;
136
+ writable = true;
137
+ } else {
138
+ // Let a later call retry; until one succeeds the store answers reads
139
+ // from memory and refuses writes.
140
+ loadPromise = null;
141
+ }
142
+ return { ok: result.ok, entries: snapshot() };
143
+ });
144
+ }
145
+ return loadPromise;
146
+ };
147
+
148
+ /** Only ever called inside `runExclusive`, so two batches cannot interleave their renames. */
149
+ const persist = async () => {
150
+ const payload = JSON.stringify(snapshot());
151
+ await fsPromises.mkdir(dataDir, { recursive: true });
152
+ // Temp file in the same directory so the rename is atomic on one device:
153
+ // a reader sees either the previous file or the complete new one.
154
+ const tmpPath = `${filePath}.${process.pid}.tmp`;
155
+ await fsPromises.writeFile(tmpPath, payload, 'utf8');
156
+ await fsPromises.rename(tmpPath, filePath);
157
+ };
158
+
159
+ /** Applies a batch and rolls the memory back when the file write fails. */
160
+ const applyBatch = async (ids, archivedAt) => {
161
+ const targets = asIdList(ids);
162
+ if (targets.length === 0) return { applied: [], failedIds: [] };
163
+ return runExclusive(() => applyBatchExclusive(targets, archivedAt));
164
+ };
165
+
166
+ const applyBatchExclusive = async (targets, archivedAt) => {
167
+ const loadResult = await load();
168
+ if (!loadResult.ok || !writable) {
169
+ return { applied: [], failedIds: targets };
170
+ }
171
+
172
+ const previous = targets.map((id) => [id, entries.has(id) ? { value: entries.get(id) } : undefined]);
173
+ // `null` is kept as an entry on purpose: it tells the overlay to drop an
174
+ // archived stamp OpenCode still carries (migrated v1 sessions).
175
+ for (const id of targets) entries.set(id, archivedAt);
176
+
177
+ try {
178
+ await persist();
179
+ } catch (error) {
180
+ for (const [id, restore] of previous) {
181
+ if (restore === undefined) entries.delete(id);
182
+ else entries.set(id, restore.value);
183
+ }
184
+ console.warn('[openchamber-sessions] failed to persist archive state:', error?.message ?? error);
185
+ return { applied: [], failedIds: targets };
186
+ }
187
+
188
+ return { applied: targets, failedIds: [] };
189
+ };
190
+
191
+ const getAll = async () => {
192
+ await load();
193
+ return snapshot();
194
+ };
195
+
196
+ return {
197
+ load,
198
+ getAll,
199
+ list: getAll,
200
+ isLoaded: () => loaded,
201
+ isArchived: async (id) => {
202
+ const sessionID = asNonEmptyString(id);
203
+ if (!sessionID) return false;
204
+ await load();
205
+ return typeof entries.get(sessionID) === 'number';
206
+ },
207
+ archivedAt: async (id) => {
208
+ const sessionID = asNonEmptyString(id);
209
+ if (!sessionID) return null;
210
+ await load();
211
+ return entries.get(sessionID) ?? null;
212
+ },
213
+ archive: async (ids, archivedAt) => {
214
+ const stamp = asTimestamp(archivedAt) ?? now();
215
+ const { applied, failedIds } = await applyBatch(ids, stamp);
216
+ return { archived: applied.map((id) => ({ id, archivedAt: stamp })), failedIds };
217
+ },
218
+ unarchive: async (ids) => {
219
+ const { applied, failedIds } = await applyBatch(ids, null);
220
+ return { restored: applied.map((id) => ({ id, archivedAt: null })), failedIds };
221
+ },
222
+ filePath,
223
+ };
224
+ };
@@ -0,0 +1,187 @@
1
+ import fs from 'node:fs';
2
+ import os from 'node:os';
3
+ import path from 'node:path';
4
+ import { afterEach, describe, expect, it, vi } from 'vitest';
5
+
6
+ import { createArchiveStore } from './archive-store.js';
7
+
8
+ const tempDirs = [];
9
+
10
+ const makeDataDir = () => {
11
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'openchamber-archive-'));
12
+ tempDirs.push(dir);
13
+ return dir;
14
+ };
15
+
16
+ const readFile = (dataDir) => fs.readFileSync(path.join(dataDir, 'sessions-archive.json'), 'utf8');
17
+
18
+ afterEach(() => {
19
+ while (tempDirs.length > 0) {
20
+ fs.rmSync(tempDirs.pop(), { recursive: true, force: true });
21
+ }
22
+ vi.restoreAllMocks();
23
+ });
24
+
25
+ describe('createArchiveStore', () => {
26
+ it('starts empty when the file does not exist', async () => {
27
+ const store = createArchiveStore({ dataDir: makeDataDir() });
28
+ await expect(store.getAll()).resolves.toEqual({});
29
+ await expect(store.isArchived('ses_1')).resolves.toBe(false);
30
+ await expect(store.archivedAt('ses_1')).resolves.toBeNull();
31
+ });
32
+
33
+ it('archives a batch, persists it, and reads it back in a fresh store', async () => {
34
+ const dataDir = makeDataDir();
35
+ const store = createArchiveStore({ dataDir, now: () => 1_700_000 });
36
+
37
+ const result = await store.archive(['ses_1', 'ses_2']);
38
+ expect(result).toEqual({
39
+ archived: [
40
+ { id: 'ses_1', archivedAt: 1_700_000 },
41
+ { id: 'ses_2', archivedAt: 1_700_000 },
42
+ ],
43
+ failedIds: [],
44
+ });
45
+ expect(JSON.parse(readFile(dataDir))).toEqual({ ses_1: 1_700_000, ses_2: 1_700_000 });
46
+
47
+ const reopened = createArchiveStore({ dataDir });
48
+ await expect(reopened.getAll()).resolves.toEqual({ ses_1: 1_700_000, ses_2: 1_700_000 });
49
+ await expect(reopened.isArchived('ses_2')).resolves.toBe(true);
50
+ });
51
+
52
+ it('honours an explicit archivedAt and ignores a nonsense one', async () => {
53
+ const store = createArchiveStore({ dataDir: makeDataDir(), now: () => 42 });
54
+ await expect(store.archive(['ses_1'], 999)).resolves.toMatchObject({
55
+ archived: [{ id: 'ses_1', archivedAt: 999 }],
56
+ });
57
+ await expect(store.archive(['ses_2'], -5)).resolves.toMatchObject({
58
+ archived: [{ id: 'ses_2', archivedAt: 42 }],
59
+ });
60
+ });
61
+
62
+ it('unarchives a batch and records the explicit unarchive in the file', async () => {
63
+ const dataDir = makeDataDir();
64
+ const store = createArchiveStore({ dataDir, now: () => 1 });
65
+ await store.archive(['ses_1', 'ses_2']);
66
+
67
+ await expect(store.unarchive(['ses_1'])).resolves.toEqual({
68
+ restored: [{ id: 'ses_1', archivedAt: null }],
69
+ failedIds: [],
70
+ });
71
+ expect(JSON.parse(readFile(dataDir))).toEqual({ ses_1: null, ses_2: 1 });
72
+ await expect(store.isArchived('ses_1')).resolves.toBe(false);
73
+ });
74
+
75
+ it('ignores blank ids and collapses duplicates', async () => {
76
+ const store = createArchiveStore({ dataDir: makeDataDir(), now: () => 7 });
77
+ const result = await store.archive(['ses_1', ' ses_1 ', '', ' ', null, 5]);
78
+ expect(result.archived).toEqual([{ id: 'ses_1', archivedAt: 7 }]);
79
+ await expect(store.archive([])).resolves.toEqual({ archived: [], failedIds: [] });
80
+ });
81
+
82
+ it('writes atomically: the final file never contains a partial payload', async () => {
83
+ const dataDir = makeDataDir();
84
+ const seen = [];
85
+ const realRename = fs.promises.rename.bind(fs.promises);
86
+ const fsPromises = {
87
+ ...fs.promises,
88
+ writeFile: async (target, payload, encoding) => {
89
+ // The visible file must still be the previous one at this point.
90
+ seen.push(fs.existsSync(path.join(dataDir, 'sessions-archive.json'))
91
+ ? readFile(dataDir)
92
+ : null);
93
+ return fs.promises.writeFile(target, payload, encoding);
94
+ },
95
+ rename: realRename,
96
+ };
97
+ const store = createArchiveStore({ dataDir, fsPromises, now: () => 3 });
98
+
99
+ await store.archive(['ses_1']);
100
+ await store.archive(['ses_2']);
101
+
102
+ expect(seen).toEqual([null, JSON.stringify({ ses_1: 3 })]);
103
+ expect(JSON.parse(readFile(dataDir))).toEqual({ ses_1: 3, ses_2: 3 });
104
+ });
105
+
106
+ it('treats a malformed file as empty, keeps a backup, and still accepts writes', async () => {
107
+ const dataDir = makeDataDir();
108
+ fs.writeFileSync(path.join(dataDir, 'sessions-archive.json'), '{ not json', 'utf8');
109
+ vi.spyOn(console, 'warn').mockImplementation(() => {});
110
+
111
+ const store = createArchiveStore({ dataDir, now: () => 11 });
112
+ await expect(store.getAll()).resolves.toEqual({});
113
+ await expect(store.archive(['ses_1'])).resolves.toMatchObject({ failedIds: [] });
114
+ expect(JSON.parse(readFile(dataDir))).toEqual({ ses_1: 11 });
115
+ expect(fs.readdirSync(dataDir).some((name) => name.includes('sessions-archive.json.'))).toBe(true);
116
+ });
117
+
118
+ it('drops entries that are not positive timestamps', async () => {
119
+ const dataDir = makeDataDir();
120
+ fs.writeFileSync(
121
+ path.join(dataDir, 'sessions-archive.json'),
122
+ JSON.stringify({ ses_ok: 5, ses_zero: 0, ses_text: 'yesterday', ses_float: 1.5 }),
123
+ 'utf8',
124
+ );
125
+ const store = createArchiveStore({ dataDir });
126
+ await expect(store.getAll()).resolves.toEqual({ ses_ok: 5 });
127
+ });
128
+
129
+ it('refuses to write when the file could not be read, so unknown state is never overwritten', async () => {
130
+ const dataDir = makeDataDir();
131
+ fs.writeFileSync(path.join(dataDir, 'sessions-archive.json'), '{}', 'utf8');
132
+ const unreadable = Object.assign(new Error('EACCES'), { code: 'EACCES' });
133
+ const fsPromises = {
134
+ ...fs.promises,
135
+ readFile: async () => { throw unreadable; },
136
+ writeFile: async () => { throw new Error('must not write'); },
137
+ };
138
+ vi.spyOn(console, 'warn').mockImplementation(() => {});
139
+
140
+ const store = createArchiveStore({ dataDir, fsPromises });
141
+ await expect(store.archive(['ses_1'])).resolves.toEqual({ archived: [], failedIds: ['ses_1'] });
142
+ expect(readFile(dataDir)).toBe('{}');
143
+ });
144
+
145
+ it('rolls memory back when persisting fails', async () => {
146
+ const dataDir = makeDataDir();
147
+ const fsPromises = {
148
+ ...fs.promises,
149
+ writeFile: async () => { throw new Error('disk full'); },
150
+ };
151
+ vi.spyOn(console, 'warn').mockImplementation(() => {});
152
+
153
+ const store = createArchiveStore({ dataDir, fsPromises, now: () => 1 });
154
+ await expect(store.archive(['ses_1'])).resolves.toEqual({ archived: [], failedIds: ['ses_1'] });
155
+ await expect(store.isArchived('ses_1')).resolves.toBe(false);
156
+ });
157
+
158
+ it('keeps a later successful batch when an earlier one fails and rolls back', async () => {
159
+ const dataDir = makeDataDir();
160
+ let failNext = true;
161
+ let releaseFailure;
162
+ const failureReleased = new Promise((resolve) => { releaseFailure = resolve; });
163
+ const fsPromises = {
164
+ ...fs.promises,
165
+ writeFile: async (...args) => {
166
+ if (failNext) {
167
+ failNext = false;
168
+ await failureReleased;
169
+ throw new Error('disk full');
170
+ }
171
+ return fs.promises.writeFile(...args);
172
+ },
173
+ };
174
+ vi.spyOn(console, 'warn').mockImplementation(() => {});
175
+ const store = createArchiveStore({ dataDir, fsPromises, now: () => 1 });
176
+
177
+ const failing = store.archive(['ses_1'], 10);
178
+ const succeeding = store.archive(['ses_1'], 20);
179
+ releaseFailure();
180
+
181
+ await expect(failing).resolves.toEqual({ archived: [], failedIds: ['ses_1'] });
182
+ await expect(succeeding).resolves.toEqual({ archived: [{ id: 'ses_1', archivedAt: 20 }], failedIds: [] });
183
+ // The failed batch's rollback must not undo what the later batch committed.
184
+ await expect(store.archivedAt('ses_1')).resolves.toBe(20);
185
+ expect(JSON.parse(readFile(dataDir))).toEqual({ ses_1: 20 });
186
+ });
187
+ });
@@ -0,0 +1,22 @@
1
+ import { OpenCode } from '@opencode/client';
2
+
3
+ /**
4
+ * OpenCode rejects non-ASCII header values; the official client sends this
5
+ * header percent-encoded, so match that wire format (non-ASCII checkout paths
6
+ * such as "Masaüstü" otherwise fail every dispatched prompt). No directory
7
+ * means no header: OpenCode then answers from its default instance, which is
8
+ * enough for lookups keyed by session id.
9
+ */
10
+ const buildDirectoryHeaders = (directory) => {
11
+ const headers = {};
12
+ if (directory) headers['x-opencode-directory'] = encodeURIComponent(directory);
13
+ return headers;
14
+ };
15
+
16
+ /** One directory-scoped `@opencode/client` for a session-route call. */
17
+ export const createOpenCodeClient = ({ baseUrl, headers, directory }) => OpenCode.make({
18
+ baseUrl,
19
+ headers: { ...headers, ...buildDirectoryHeaders(directory) },
20
+ // Resolved per call so a test (or a runtime that swaps the global) is honoured.
21
+ fetch: (...args) => globalThis.fetch(...args),
22
+ });