@openchambery/web 2.0.0-beta.1 → 2.0.0-beta.4

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 (287) hide show
  1. package/README.md +15 -11
  2. package/bin/cli-opencode.test.js +4 -4
  3. package/bin/cli.js +1 -1
  4. package/bin/lib/cli-startup.js +1 -1
  5. package/bin/lib/cli-startup.test.js +18 -4
  6. package/dist/assets/{AgentAvatar-CLWWNu_Q.js → AgentAvatar-Q1SmFRx2.js} +1 -1
  7. package/dist/assets/AssistantView-CwXkiCAd.js +3 -0
  8. package/dist/assets/{DiagramView-xuSJTnv7.js → DiagramView-BW92schO.js} +1 -1
  9. package/dist/assets/{FadeInOnReveal-Ov0D96d0.js → FadeInOnReveal-BZmwl-zE.js} +1 -1
  10. package/dist/assets/{Icon-DD-BuMfZ.js → Icon-AQPvxMiU.js} +1 -1
  11. package/dist/assets/{MarkdownRenderer-bc_p1H7l.js → MarkdownRenderer-Bv05iDZZ.js} +2 -2
  12. package/dist/assets/{MarkdownRendererImpl-SKi4oG0c.js → MarkdownRendererImpl-C81xqYib.js} +3 -3
  13. package/dist/assets/MarkstreamRendererImpl-CXUCQuKJ.js +3 -0
  14. package/dist/assets/{MobileDetailNavigation-Bnx59U_J.js → MobileDetailNavigation-BMgIu7zc.js} +1 -1
  15. package/dist/assets/{MobileShareBridge-BQlP4J-F.js → MobileShareBridge-BOXGNHmC.js} +2 -2
  16. package/dist/assets/MobileSurface-xuc_z0_G.js +2 -0
  17. package/dist/assets/{MultiRunWindow-ChZGob0v.js → MultiRunWindow-B5At2Z95.js} +1 -1
  18. package/dist/assets/{OnboardingScreen-DtQJxZG-.js → OnboardingScreen-bsPLgz4r.js} +1 -1
  19. package/dist/assets/OverlayScrollbar-4wp4migh.js +2 -0
  20. package/dist/assets/SettingsView-CJ6tcH5_.js +25 -0
  21. package/dist/assets/{SettingsWindow-DfnsFa8y.js → SettingsWindow-CMtX2bMl.js} +1 -1
  22. package/dist/assets/{TerminalView-Dh9MWCpS.js → TerminalView-Dvr-ysTS.js} +1 -1
  23. package/dist/assets/{ToolOutputDialog-BejmjrRk.js → ToolOutputDialog-uH1ZZDSS.js} +2 -2
  24. package/dist/assets/appThemeRegistry-DK7q2juD.js +1 -0
  25. package/dist/assets/{assistant-attachment-cache-C0aRnFkj.js → assistant-attachment-cache-DphJleHr.js} +1 -1
  26. package/dist/assets/{button-D-rATN7w.js → button-CcNJUxUd.js} +1 -1
  27. package/dist/assets/collapsible-ZmZ6rLWs.js +3 -0
  28. package/dist/assets/context-menu-B2LXJgXL.js +1 -0
  29. package/dist/assets/{desktopHosts-B1ixEylr.js → desktopHosts-C6HrP0jq.js} +1 -1
  30. package/dist/assets/{desktopRelayRestore-4JmFUw8r.js → desktopRelayRestore-DdVBp45U.js} +1 -1
  31. package/dist/assets/{diagram-DQf7Y_uR.js → diagram-CxTs9hCQ.js} +1 -1
  32. package/dist/assets/{dialog-bfbvR4VH.js → dialog-KMVL9QKF.js} +1 -1
  33. package/dist/assets/es-CN3E_zXj.js +15 -0
  34. package/dist/assets/{fileMentionTouchSelection-BvrNCroG.js → fileMentionTouchSelection-7RIwlpkB.js} +1 -1
  35. package/dist/assets/{fileQueries-DQc8EJUf.js → fileQueries-CjdBRy8y.js} +1 -1
  36. package/dist/assets/fr-9zZvQZD2.js +13 -0
  37. package/dist/assets/gitApi-yuWg9BN4.js +10 -0
  38. package/dist/assets/{imageSource-BlAJJUEI.js → imageSource-9vQBq3zp.js} +1 -1
  39. package/dist/assets/{input-8r6F_Fza.js → input-CrIvk3HZ.js} +1 -1
  40. package/dist/assets/insertionBoundaries-BiUCSkx-.js +1 -0
  41. package/dist/assets/ja-Dnjk_82N.js +15 -0
  42. package/dist/assets/ko-BxCEE99C.js +15 -0
  43. package/dist/assets/{main-CCE_4y5F.js → main-C9mIBo77.js} +2 -2
  44. package/dist/assets/main-DiL6w6KF.js +169 -0
  45. package/dist/assets/markdown-worker-bS5OgGO3.js +2 -0
  46. package/dist/assets/{markdownHeightCache-1Qo6y6IJ.js → markdownHeightCache-BmXVY_gc.js} +1 -1
  47. package/dist/assets/{markdownTheme-DOfGg-A7.js → markdownTheme-CKac3wKC.js} +1 -1
  48. package/dist/assets/mcpQueries-C7jknHV8.js +1 -0
  49. package/dist/assets/miniChat-CwtUaElU.js +2 -0
  50. package/dist/assets/mobile-DB-6--ws.js +2 -0
  51. package/dist/assets/multirun-TAsxFI8e.js +4 -0
  52. package/dist/assets/{persistence-Ce3AnpEr.js → persistence-56kq8Z4V.js} +2 -2
  53. package/dist/assets/pl-CNNsWnPM.js +15 -0
  54. package/dist/assets/platform-C13fMVEs.js +6 -0
  55. package/dist/assets/pt-BR-BZB2DJMp.js +15 -0
  56. package/dist/assets/{relay-image-stream-DM7atqJs.js → relay-image-stream-DrdVj99c.js} +1 -1
  57. package/dist/assets/renderElectronMiniChatApp-CGj0MNRU.js +2 -0
  58. package/dist/assets/renderMobileApp-CGMMqbsO.js +28 -0
  59. package/dist/assets/runtime-fetch-Fs9ZV2V8.js +1 -0
  60. package/dist/assets/{runtimeConfig-BMvqa5we.js → runtimeConfig-BEANkvOd.js} +1 -1
  61. package/dist/assets/runtimeEndpointReset-A_ZBUJFS.js +147 -0
  62. package/dist/assets/sessionChangeSummary-qOLrP9fw.js +2 -0
  63. package/dist/assets/{sessionLookup-gh-hVRIc.js → sessionLookup-34XFmP9C.js} +1 -1
  64. package/dist/assets/{sortable-tabs-strip-CudoSgV4.js → sortable-tabs-strip-DyEV0Heg.js} +1 -1
  65. package/dist/assets/src-ubCWXZhR.css +2 -0
  66. package/dist/assets/store-DLCiZaA6.js +15 -0
  67. package/dist/assets/{terminalApi-DiF6JmBP.js → terminalApi-CXaBEoa_.js} +1 -1
  68. package/dist/assets/textarea-BBOlRI9O.js +1 -0
  69. package/dist/assets/{tooltip-CQvpK-UV.js → tooltip-CPI4fr1a.js} +1 -1
  70. package/dist/assets/uk-rwQrxeYM.js +15 -0
  71. package/dist/assets/{url-_z7evzdK.js → url-DRDQXjyX.js} +1 -1
  72. package/dist/assets/{useAppFontEffects-Cpmpo7Wb.js → useAppFontEffects-CiDe42I5.js} +57 -43
  73. package/dist/assets/{useAssistantUIStore-CMutVh9B.js → useAssistantUIStore-asNG6K7n.js} +1 -1
  74. package/dist/assets/useDesktopWindowControlsLayout-BlizAlOt.js +1 -0
  75. package/dist/assets/useEffectiveDirectory-C_CouL5v.js +1 -0
  76. package/dist/assets/{useFontPreferences-DcbXQOkc.js → useFontPreferences-B0oUZAiB.js} +1 -1
  77. package/dist/assets/useMobileNavigationStore-DfPClaW-.js +414 -0
  78. package/dist/assets/{useRuntimeAPIs-DSLXJybW.js → useRuntimeAPIs-DwLtO0c_.js} +1 -1
  79. package/dist/assets/useSessionAutoCleanup-DtrfbxAp.js +2 -0
  80. package/dist/assets/{useThemeSystem-DMzWVJaW.js → useThemeSystem-BlNUWH47.js} +1 -1
  81. package/dist/assets/useUIStore-CQYKJnrg.js +1 -0
  82. package/dist/assets/{useWorkerHighlightedLines-Bqn4AiBL.js → useWorkerHighlightedLines-CPNYz6Wf.js} +2 -2
  83. package/dist/assets/{vendor-base-ui-CnHfUmDN.js → vendor-base-ui-CDjyofeD.js} +1 -1
  84. package/dist/assets/{vendor-cmdk-CeAGfrLo.js → vendor-cmdk-BFH0UpXX.js} +1 -1
  85. package/dist/assets/{vendor-dnd-kit-accessibility-BY5LhhUB.js → vendor-dnd-kit-accessibility-BrR3AKvM.js} +1 -1
  86. package/dist/assets/{vendor-dnd-kit-core-CvWxUPdb.js → vendor-dnd-kit-core-CNfdvk6H.js} +1 -1
  87. package/dist/assets/{vendor-dnd-kit-sortable-hHdP9lLb.js → vendor-dnd-kit-sortable-DQUaTeGj.js} +1 -1
  88. package/dist/assets/{vendor-framer-motion-DFy_BSVd.js → vendor-framer-motion-4dogsUk3.js} +1 -1
  89. package/dist/assets/{vendor-legendapp-list-WE32lwNk.js → vendor-legendapp-list-BU7MdPZR.js} +1 -1
  90. package/dist/assets/{vendor-markstream-react-DX55XLGO.js → vendor-markstream-react-qHC2FykU.js} +3 -3
  91. package/dist/assets/{vendor-pierre-diffs-DX7XTPG3.js → vendor-pierre-diffs-CUfqC003.js} +1 -1
  92. package/dist/assets/{vendor-react-DLPElpoQ.js → vendor-react-C94A0AHl.js} +1 -1
  93. package/dist/assets/{vendor-react-drawio-ECtEwvbc.js → vendor-react-drawio-FVlWqKVE.js} +1 -1
  94. package/dist/assets/{vendor-reactuses-core-BnW6GxJt.js → vendor-reactuses-core-C2A3EjUi.js} +1 -1
  95. package/dist/assets/{vendor-sonner-DVzioF3d.js → vendor-sonner-DNqHmUQs.js} +1 -1
  96. package/dist/assets/{vendor-stream-diffs-DWy3xSmp.js → vendor-stream-diffs-CwbKsmlv.js} +2 -2
  97. package/dist/assets/{vendor-tanstack-react-query-D7pys_c9.js → vendor-tanstack-react-query-D0iFK57C.js} +1 -1
  98. package/dist/assets/{vendor-tanstack-react-virtual-tlDgH6PG.js → vendor-tanstack-react-virtual-B2kV6cnT.js} +1 -1
  99. package/dist/assets/{vendor-zustand-B4dlGEM2.js → vendor-zustand-BqLKf6ZY.js} +1 -1
  100. package/dist/assets/zh-CN-BvmBDnkD.js +15 -0
  101. package/dist/assets/zh-TW-9wcHTX0y.js +15 -0
  102. package/dist/index.html +13 -13
  103. package/dist/mini-chat.html +13 -13
  104. package/dist/mobile.html +13 -13
  105. package/package.json +1 -1
  106. package/server/index.js +327 -62
  107. package/server/lib/assistants/DOCUMENTATION.md +14 -9
  108. package/server/lib/assistants/contracts.js +11 -1
  109. package/server/lib/assistants/projection-history.test.js +265 -18
  110. package/server/lib/assistants/routes.js +6 -0
  111. package/server/lib/assistants/service.js +417 -231
  112. package/server/lib/assistants/service.test.js +392 -148
  113. package/server/lib/conversations/DOCUMENTATION.md +1 -1
  114. package/server/lib/conversations/service.js +1 -0
  115. package/server/lib/conversations/service.test.js +16 -0
  116. package/server/lib/conversations/validation.js +11 -1
  117. package/server/lib/event-stream/DOCUMENTATION.md +3 -1
  118. package/server/lib/event-stream/directory-ws-bridge.js +13 -2
  119. package/server/lib/event-stream/global-hub.js +49 -4
  120. package/server/lib/event-stream/reasoning-projection.js +16 -1
  121. package/server/lib/event-stream/reasoning-projection.test.js +15 -0
  122. package/server/lib/event-stream/runtime.js +57 -20
  123. package/server/lib/event-stream/runtime.test.js +112 -0
  124. package/server/lib/llm/DOCUMENTATION.md +33 -9
  125. package/server/lib/llm/catalog.js +1 -1
  126. package/server/lib/llm/completions.js +5 -1
  127. package/server/lib/llm/completions.test.js +3 -3
  128. package/server/lib/llm/generate.js +237 -145
  129. package/server/lib/llm/generate.test.js +201 -46
  130. package/server/lib/llm/routes.js +2 -0
  131. package/server/lib/message-queue/runtime.js +15 -2
  132. package/server/lib/notifications/runtime.js +23 -5
  133. package/server/lib/notifications/runtime.test.js +11 -0
  134. package/server/lib/opencode/DOCUMENTATION.md +85 -17
  135. package/server/lib/opencode/agent-document.d.ts +28 -0
  136. package/server/lib/opencode/agent-document.js +280 -0
  137. package/server/lib/opencode/agent-document.test.js +137 -0
  138. package/server/lib/opencode/agents.js +96 -0
  139. package/server/lib/opencode/auth-state-runtime.js +5 -0
  140. package/server/lib/opencode/bootstrap-runtime.js +2 -0
  141. package/server/lib/opencode/config-entity-routes.js +42 -99
  142. package/server/lib/opencode/config-entity-routes.test.js +107 -0
  143. package/server/lib/opencode/config-hot-reload.integration.test.js +105 -0
  144. package/server/lib/opencode/ensure-cli.js +1 -2
  145. package/server/lib/opencode/env-runtime.js +40 -26
  146. package/server/lib/opencode/env-runtime.test.js +10 -17
  147. package/server/lib/opencode/feature-routes-runtime.js +43 -4
  148. package/server/lib/opencode/feature-routes-runtime.test.js +18 -0
  149. package/server/lib/opencode/http-server-connections.js +98 -0
  150. package/server/lib/opencode/http-server-connections.test.js +321 -0
  151. package/server/lib/opencode/lifecycle.js +349 -39
  152. package/server/lib/opencode/lifecycle.test.js +322 -0
  153. package/server/lib/opencode/managed-capabilities-runtime.js +15 -2
  154. package/server/lib/opencode/managed-capabilities-runtime.test.js +1 -0
  155. package/server/lib/opencode/openchamber-routes.js +7 -1
  156. package/server/lib/opencode/opencode2-pin.js +4 -4
  157. package/server/lib/opencode/opencode2-pin.test.js +1 -0
  158. package/server/lib/opencode/opencode2-sidecar.e2e.test.js +127 -4
  159. package/server/lib/opencode/owned-runtime-upgrade.js +431 -0
  160. package/server/lib/opencode/owned-runtime-upgrade.test.js +160 -0
  161. package/server/lib/opencode/plugin-routes.js +3 -18
  162. package/server/lib/opencode/plugin-routes.test.js +9 -9
  163. package/server/lib/opencode/provider-catalog.js +58 -12
  164. package/server/lib/opencode/provider-catalog.test.js +48 -0
  165. package/server/lib/opencode/proxy.js +252 -46
  166. package/server/lib/opencode/proxy.test.js +235 -2
  167. package/server/lib/opencode/routes.js +296 -62
  168. package/server/lib/opencode/routes.test.js +278 -12
  169. package/server/lib/opencode/runtime-contract.js +401 -0
  170. package/server/lib/opencode/runtime-contract.test.js +214 -0
  171. package/server/lib/opencode/server-opencode-fetch.js +242 -0
  172. package/server/lib/opencode/server-opencode-fetch.test.js +130 -0
  173. package/server/lib/opencode/server-utils-runtime.js +33 -0
  174. package/server/lib/opencode/shared-service.integration.test.js +66 -0
  175. package/server/lib/opencode/shared-service.js +147 -0
  176. package/server/lib/opencode/shared-service.test.js +93 -0
  177. package/server/lib/opencode/shared.js +3 -3
  178. package/server/lib/opencode/shutdown-runtime.js +15 -6
  179. package/server/lib/opencode/shutdown-runtime.test.js +49 -1
  180. package/server/lib/opencode/skill-routes.js +20 -33
  181. package/server/lib/opencode/skill-routes.test.js +35 -2
  182. package/server/lib/opencode/skills.test.js +15 -0
  183. package/server/lib/opencode/startup-pipeline-runtime.js +4 -0
  184. package/server/lib/opencode/static-routes-runtime.js +3 -1
  185. package/server/lib/opencode/static-routes-runtime.test.js +33 -0
  186. package/server/lib/opencode/v2-client.js +14 -3
  187. package/server/lib/opencode/v2-plugin-host-shim/index.mjs +155 -0
  188. package/server/lib/opencode/v2-plugin-host-shim/package.json +4 -0
  189. package/server/lib/permission-auto-accept/DOCUMENTATION.md +5 -5
  190. package/server/lib/permission-auto-accept/runtime.js +14 -7
  191. package/server/lib/permission-auto-accept/runtime.test.js +27 -12
  192. package/server/lib/relay/reasoning-projection.e2e.test.ts +22 -7
  193. package/server/lib/scheduled-tasks/DOCUMENTATION.md +8 -0
  194. package/server/lib/scheduled-tasks/runtime.js +52 -4
  195. package/server/lib/scheduled-tasks/runtime.test.js +80 -1
  196. package/server/lib/session-goal/DOCUMENTATION.md +113 -13
  197. package/server/lib/session-goal/routes.js +9 -0
  198. package/server/lib/session-goal/runtime.js +1579 -117
  199. package/server/lib/session-goal/runtime.test.js +1453 -84
  200. package/server/lib/session-index/DOCUMENTATION.md +18 -15
  201. package/server/lib/session-index/event-ingest.js +89 -16
  202. package/server/lib/session-index/event-ingest.test.js +73 -0
  203. package/server/lib/session-index/host-authority-snapshot.js +92 -0
  204. package/server/lib/session-index/routes.host-archive.test.js +201 -0
  205. package/server/lib/session-index/routes.js +63 -10
  206. package/server/lib/session-index/routes.test.js +8 -8
  207. package/server/lib/session-index/service.js +7 -1
  208. package/server/lib/session-index/sync-runtime.js +161 -34
  209. package/server/lib/session-index/sync-runtime.test.js +155 -1
  210. package/server/lib/session-metadata/DOCUMENTATION.md +61 -16
  211. package/server/lib/session-metadata/routes.js +108 -9
  212. package/server/lib/session-metadata/routes.test.js +47 -2
  213. package/server/lib/session-metadata/session-archive.d.ts +73 -0
  214. package/server/lib/session-metadata/session-archive.js +654 -0
  215. package/server/lib/session-metadata/session-archive.test.js +359 -0
  216. package/server/lib/session-metadata/session-metadata-store.d.ts +36 -0
  217. package/server/lib/session-metadata/session-metadata-store.js +212 -72
  218. package/server/lib/session-metadata/session-metadata-store.test.js +205 -10
  219. package/server/lib/session-metadata/session-projection.d.ts +41 -0
  220. package/server/lib/session-metadata/session-projection.js +255 -0
  221. package/server/lib/session-metadata/session-projection.test.js +64 -0
  222. package/server/lib/session-metadata/system-session.js +55 -0
  223. package/server/lib/session-metadata/system-session.test.js +55 -0
  224. package/server/lib/session-title/DOCUMENTATION.md +40 -7
  225. package/server/lib/session-title/native-runtime.test.js +167 -0
  226. package/server/lib/session-title/runtime.js +67 -42
  227. package/server/lib/session-title/session-access.js +46 -0
  228. package/server/lib/session-title/session-access.test.js +91 -0
  229. package/server/lib/session-turn-pages/DOCUMENTATION.md +9 -8
  230. package/server/lib/session-turn-pages/routes.js +17 -102
  231. package/server/lib/session-turn-pages/routes.test.js +281 -0
  232. package/server/lib/session-turn-pages/session-message-projection.d.ts +13 -0
  233. package/server/lib/session-turn-pages/session-message-projection.js +281 -0
  234. package/server/lib/small-model/DOCUMENTATION.md +101 -147
  235. package/server/lib/small-model/catalog.js +31 -51
  236. package/server/lib/small-model/catalog.test.js +65 -10
  237. package/server/lib/small-model/custom-api.js +68 -0
  238. package/server/lib/small-model/custom-api.test.js +64 -0
  239. package/server/lib/small-model/index.js +124 -240
  240. package/server/lib/small-model/resolve.js +35 -120
  241. package/server/lib/small-model/resolve.test.js +20 -110
  242. package/server/lib/small-model/routes.js +1 -3
  243. package/server/lib/small-model/summary-settings.test.js +157 -253
  244. package/server/opencode-proxy.test.js +2 -0
  245. package/dist/assets/AssistantView-BTCkm8yk.js +0 -3
  246. package/dist/assets/MarkstreamRendererImpl-CnkodGJW.js +0 -3
  247. package/dist/assets/MobileSurface-BXLeiZ4d.js +0 -2
  248. package/dist/assets/OverlayScrollbar-pMwxaCca.js +0 -2
  249. package/dist/assets/SettingsView-B5I0KDTZ.js +0 -25
  250. package/dist/assets/appThemeRegistry-COGfxTFV.js +0 -1
  251. package/dist/assets/collapsible-BXRd5_Hr.js +0 -1
  252. package/dist/assets/context-menu-jGtH6fR9.js +0 -1
  253. package/dist/assets/es-c52LTHHe.js +0 -15
  254. package/dist/assets/fr-PisFbUE8.js +0 -13
  255. package/dist/assets/gitApi-ButwzQ0N.js +0 -9
  256. package/dist/assets/insertionBoundaries-DGs7S5zv.js +0 -1
  257. package/dist/assets/ja-C8Ikun3Z.js +0 -15
  258. package/dist/assets/ko-ClFys5o2.js +0 -15
  259. package/dist/assets/main-Dn9rpsgl.js +0 -169
  260. package/dist/assets/markdown-worker-CptoICmq.js +0 -2
  261. package/dist/assets/miniChat-BpjxVqgg.js +0 -2
  262. package/dist/assets/mobile-nMJCaNq1.js +0 -2
  263. package/dist/assets/multirun-BvSG94mW.js +0 -4
  264. package/dist/assets/pl-CnaKWN5F.js +0 -15
  265. package/dist/assets/platform-CWTIp02q.js +0 -4
  266. package/dist/assets/projectMeta-DOe49dM_.js +0 -3
  267. package/dist/assets/pt-BR-s8OpfRI6.js +0 -15
  268. package/dist/assets/renderElectronMiniChatApp-R6dKh79f.js +0 -2
  269. package/dist/assets/renderMobileApp-y-S8y8v_.js +0 -28
  270. package/dist/assets/runtime-fetch-zIalUnbb.js +0 -1
  271. package/dist/assets/runtimeEndpointReset-B6BxhvR0.js +0 -147
  272. package/dist/assets/sessionChangeSummary-VVBlEXuh.js +0 -2
  273. package/dist/assets/src-BzX5UI-c.css +0 -2
  274. package/dist/assets/store-DJczmczv.js +0 -15
  275. package/dist/assets/textarea-3XR8sUfy.js +0 -1
  276. package/dist/assets/uk-CaX0iqkB.js +0 -15
  277. package/dist/assets/useDesktopWindowControlsLayout-B1GX59-L.js +0 -1
  278. package/dist/assets/useEffectiveDirectory-CN6JoCFw.js +0 -1
  279. package/dist/assets/useMobileNavigationStore-BVjFtXSy.js +0 -414
  280. package/dist/assets/useSessionAutoCleanup-BLjymaFt.js +0 -2
  281. package/dist/assets/useUIStore-Bxv3f23A.js +0 -1
  282. package/dist/assets/zh-CN-20MYEjha.js +0 -15
  283. package/dist/assets/zh-TW-DiN04qTw.js +0 -15
  284. package/server/lib/small-model/call.js +0 -427
  285. package/server/lib/small-model/call.test.js +0 -382
  286. package/server/lib/small-model/opencode-session.js +0 -183
  287. package/server/lib/small-model/opencode-session.test.js +0 -147
@@ -1,427 +0,0 @@
1
- import { readAuthFile, writeAuthFile } from '../opencode/auth.js';
2
- import { readConfig } from '../opencode/shared.js';
3
- import { getCatalogProvider } from './catalog.js';
4
- import { getAuthEntryForProvider } from './resolve.js';
5
-
6
- // Direct, non-streaming text generation against the provider APIs, replicating
7
- // how OpenCode authenticates each of them (see the plugin auth loaders in the
8
- // opencode repo). auth.json credentials never leave this process.
9
-
10
- const REQUEST_TIMEOUT_MS = 60_000;
11
- // Generous default: thinking models that can't be switched off (DeepSeek,
12
- // Qwen, …) spend part of this budget on reasoning before the actual answer.
13
- const DEFAULT_MAX_OUTPUT_TOKENS = 4_000;
14
-
15
- const USER_AGENT = 'opencode/1.0 openchamber';
16
-
17
- const CODEX_TOKEN_URL = 'https://auth.openai.com/oauth/token';
18
- const CODEX_CLIENT_ID = 'app_EMoamEEZ73f0CkXaXp7hrann';
19
- const CODEX_RESPONSES_URL = 'https://chatgpt.com/backend-api/codex/responses';
20
-
21
- const httpError = async (response, provider) => {
22
- const body = await response.text().catch(() => '');
23
- const snippet = body ? `: ${body.slice(0, 300)}` : '';
24
- return new Error(`${provider} request failed with ${response.status}${snippet}`);
25
- };
26
-
27
- // ---------------------------------------------------------------------------
28
- // OpenAI OAuth (ChatGPT plan / codex) token refresh — single-flight, with the
29
- // refreshed token written back to auth.json exactly like OpenCode does.
30
- // ---------------------------------------------------------------------------
31
-
32
- let openaiRefreshPromise = null;
33
-
34
- const decodeJwtClaims = (token) => {
35
- try {
36
- const payload = token.split('.')[1];
37
- return JSON.parse(Buffer.from(payload, 'base64url').toString('utf8'));
38
- } catch {
39
- return null;
40
- }
41
- };
42
-
43
- const extractChatgptAccountId = (accessToken) => {
44
- const claims = decodeJwtClaims(accessToken);
45
- const auth = claims?.['https://api.openai.com/auth'];
46
- const value = auth?.chatgpt_account_id;
47
- return typeof value === 'string' && value ? value : null;
48
- };
49
-
50
- const refreshOpenaiOauth = async (entry) => {
51
- if (!openaiRefreshPromise) {
52
- openaiRefreshPromise = (async () => {
53
- const response = await fetch(CODEX_TOKEN_URL, {
54
- method: 'POST',
55
- headers: { 'Content-Type': 'application/json' },
56
- body: JSON.stringify({
57
- grant_type: 'refresh_token',
58
- refresh_token: entry.refresh,
59
- client_id: CODEX_CLIENT_ID,
60
- }),
61
- signal: AbortSignal.timeout(30_000),
62
- });
63
- if (!response.ok) {
64
- throw await httpError(response, 'OpenAI token refresh');
65
- }
66
- const payload = await response.json();
67
- const access = typeof payload?.access_token === 'string' ? payload.access_token : '';
68
- if (!access) {
69
- throw new Error('OpenAI token refresh returned no access token');
70
- }
71
- const refreshed = {
72
- ...entry,
73
- type: 'oauth',
74
- access,
75
- refresh: typeof payload?.refresh_token === 'string' && payload.refresh_token
76
- ? payload.refresh_token
77
- : entry.refresh,
78
- expires: Date.now() + (Number(payload?.expires_in) > 0 ? Number(payload.expires_in) : 3600) * 1000,
79
- };
80
- const auth = readAuthFile();
81
- auth.openai = refreshed;
82
- writeAuthFile(auth);
83
- return refreshed;
84
- })().finally(() => {
85
- openaiRefreshPromise = null;
86
- });
87
- }
88
- return openaiRefreshPromise;
89
- };
90
-
91
- const ensureFreshOpenaiOauth = async (entry) => {
92
- if (entry.access && Number(entry.expires) > Date.now()) {
93
- return entry;
94
- }
95
- if (!entry.refresh) {
96
- throw new Error('OpenAI OAuth entry has no refresh token');
97
- }
98
- return refreshOpenaiOauth(entry);
99
- };
100
-
101
- // ---------------------------------------------------------------------------
102
- // Wire formats
103
- // ---------------------------------------------------------------------------
104
-
105
- const callOpenaiCompatible = async ({ baseURL, headers, modelID, prompt, system, maxOutputTokens, providerLabel, extraBody }) => {
106
- const trimmedBase = baseURL.replace(/\/+$/, '');
107
- const response = await fetch(`${trimmedBase}/chat/completions`, {
108
- method: 'POST',
109
- headers: {
110
- 'Content-Type': 'application/json',
111
- Accept: 'application/json',
112
- ...headers,
113
- },
114
- body: JSON.stringify({
115
- model: modelID,
116
- messages: [
117
- ...(system ? [{ role: 'system', content: system }] : []),
118
- { role: 'user', content: prompt },
119
- ],
120
- max_tokens: maxOutputTokens,
121
- stream: false,
122
- ...(extraBody || {}),
123
- }),
124
- signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
125
- });
126
- if (!response.ok) {
127
- throw await httpError(response, providerLabel);
128
- }
129
- const payload = await response.json();
130
- const message = payload?.choices?.[0]?.message;
131
-
132
- // Providers disagree on the content shape: plain string, an array of
133
- // typed parts, or (thinking models) an empty content with the budget spent
134
- // on reasoning_content.
135
- let text = '';
136
- if (typeof message?.content === 'string') {
137
- text = message.content;
138
- } else if (Array.isArray(message?.content)) {
139
- text = message.content
140
- .map((part) => (typeof part?.text === 'string' ? part.text : ''))
141
- .join('');
142
- }
143
- if (!text.trim() && typeof message?.reasoning_content === 'string' && message.reasoning_content.trim()) {
144
- const finishReason = payload?.choices?.[0]?.finish_reason;
145
- throw new Error(
146
- `${providerLabel} spent the output budget on reasoning and returned no answer`
147
- + (finishReason ? ` (finish_reason: ${finishReason})` : ''),
148
- );
149
- }
150
- if (!text.trim()) {
151
- throw new Error(`${providerLabel} returned no message content`);
152
- }
153
- return text;
154
- };
155
-
156
- const callAnthropic = async ({ apiKey, modelID, prompt, system, maxOutputTokens }) => {
157
- const response = await fetch('https://api.anthropic.com/v1/messages', {
158
- method: 'POST',
159
- headers: {
160
- 'Content-Type': 'application/json',
161
- Accept: 'application/json',
162
- 'x-api-key': apiKey,
163
- 'anthropic-version': '2023-06-01',
164
- },
165
- body: JSON.stringify({
166
- model: modelID,
167
- max_tokens: maxOutputTokens,
168
- ...(system ? { system } : {}),
169
- messages: [{ role: 'user', content: prompt }],
170
- }),
171
- signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
172
- });
173
- if (!response.ok) {
174
- throw await httpError(response, 'Anthropic');
175
- }
176
- const payload = await response.json();
177
- const text = (payload?.content || [])
178
- .filter((part) => part?.type === 'text' && typeof part.text === 'string')
179
- .map((part) => part.text)
180
- .join('');
181
- if (!text) {
182
- throw new Error('Anthropic returned no text content');
183
- }
184
- return text;
185
- };
186
-
187
- const callGoogle = async ({ apiKey, modelID, prompt, system, maxOutputTokens }) => {
188
- const url = `https://generativelanguage.googleapis.com/v1beta/models/${encodeURIComponent(modelID)}:generateContent`;
189
- const response = await fetch(url, {
190
- method: 'POST',
191
- headers: {
192
- 'Content-Type': 'application/json',
193
- Accept: 'application/json',
194
- 'x-goog-api-key': apiKey,
195
- },
196
- body: JSON.stringify({
197
- contents: [{ role: 'user', parts: [{ text: prompt }] }],
198
- ...(system ? { systemInstruction: { parts: [{ text: system }] } } : {}),
199
- // thinkingBudget 0 switches Gemini Flash thinking off; Flash is the only
200
- // family the small-model resolver picks for Google.
201
- generationConfig: { maxOutputTokens, thinkingConfig: { thinkingBudget: 0 } },
202
- }),
203
- signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
204
- });
205
- if (!response.ok) {
206
- throw await httpError(response, 'Google');
207
- }
208
- const payload = await response.json();
209
- const text = (payload?.candidates?.[0]?.content?.parts || [])
210
- .map((part) => (typeof part?.text === 'string' ? part.text : ''))
211
- .join('');
212
- if (!text) {
213
- throw new Error('Google returned no text content');
214
- }
215
- return text;
216
- };
217
-
218
- // ChatGPT-plan traffic goes to the codex backend, which only speaks the
219
- // streaming Responses API — collect the output_text deltas from the SSE body.
220
- const callCodexResponses = async ({ accessToken, accountId, modelID, prompt, system }) => {
221
- const response = await fetch(CODEX_RESPONSES_URL, {
222
- method: 'POST',
223
- headers: {
224
- 'Content-Type': 'application/json',
225
- Accept: 'text/event-stream',
226
- Authorization: `Bearer ${accessToken}`,
227
- ...(accountId ? { 'ChatGPT-Account-Id': accountId } : {}),
228
- originator: 'opencode',
229
- 'User-Agent': USER_AGENT,
230
- },
231
- body: JSON.stringify({
232
- model: modelID,
233
- ...(system ? { instructions: system } : {}),
234
- input: [
235
- {
236
- type: 'message',
237
- role: 'user',
238
- content: [{ type: 'input_text', text: prompt }],
239
- },
240
- ],
241
- // The codex backend rejects max_output_tokens (OpenCode forces it to
242
- // undefined for this provider too).
243
- stream: true,
244
- store: false,
245
- }),
246
- signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
247
- });
248
- if (!response.ok) {
249
- throw await httpError(response, 'OpenAI (ChatGPT plan)');
250
- }
251
-
252
- const raw = await response.text();
253
- let text = '';
254
- let completedText = '';
255
- for (const line of raw.split('\n')) {
256
- if (!line.startsWith('data:')) continue;
257
- const data = line.slice(5).trim();
258
- if (!data || data === '[DONE]') continue;
259
- let event;
260
- try {
261
- event = JSON.parse(data);
262
- } catch {
263
- continue;
264
- }
265
- if (event?.type === 'response.output_text.delta' && typeof event.delta === 'string') {
266
- text += event.delta;
267
- }
268
- if (event?.type === 'response.output_text.done' && typeof event.text === 'string') {
269
- completedText = event.text;
270
- }
271
- if (event?.type === 'response.failed' || event?.type === 'error') {
272
- const message = event?.response?.error?.message || event?.message || 'response failed';
273
- throw new Error(`OpenAI (ChatGPT plan) stream error: ${message}`);
274
- }
275
- }
276
- const result = completedText || text;
277
- if (!result) {
278
- throw new Error('OpenAI (ChatGPT plan) returned no text output');
279
- }
280
- return result;
281
- };
282
-
283
- // ---------------------------------------------------------------------------
284
- // Custom provider configuration support
285
- // ---------------------------------------------------------------------------
286
-
287
- const readProviderConfig = (workingDirectory, providerID) => {
288
- try {
289
- const config = readConfig(workingDirectory);
290
- const providerCfg = config?.provider?.[providerID];
291
- if (!providerCfg || typeof providerCfg !== 'object') return null;
292
- const baseURL = typeof providerCfg?.options?.baseURL === 'string' ? providerCfg.options.baseURL.trim() : null;
293
- const apiKey = typeof providerCfg?.options?.apiKey === 'string' ? providerCfg.options.apiKey.trim() : null;
294
- return {
295
- baseURL,
296
- // Shape the config-supplied key as a regular api-key auth entry so it
297
- // can win the precedence check below and flow through the dispatch's
298
- // `entry.type === 'api' ? entry.key : ...` branch unchanged.
299
- auth: apiKey ? { type: 'api', key: apiKey } : null,
300
- };
301
- } catch {
302
- // Provider config is non-essential — continue with catalog-only resolution.
303
- return null;
304
- }
305
- }
306
-
307
- // ---------------------------------------------------------------------------
308
- // Dispatch
309
- // ---------------------------------------------------------------------------
310
-
311
- export async function callSmallModel({ auth, catalog, workingDirectory, providerID, modelID, prompt, system, maxOutputTokens, custom }) {
312
- const tokens = Number(maxOutputTokens) > 0 ? Number(maxOutputTokens) : DEFAULT_MAX_OUTPUT_TOKENS;
313
- if (custom) {
314
- return callOpenaiCompatible({
315
- baseURL: custom.baseURL,
316
- headers: { Authorization: `Bearer ${custom.apiToken}` },
317
- modelID,
318
- prompt,
319
- system,
320
- maxOutputTokens: tokens,
321
- providerLabel: 'Custom summary API',
322
- });
323
- }
324
- const providerConfig = readProviderConfig(workingDirectory, providerID);
325
- // Match OpenCode's resolveSDK precedence:
326
- // config provider.<id>.options.apiKey (providerConfig.auth) wins; the
327
- // auth.json entry is only a fallback.
328
- const entry = providerConfig?.auth || getAuthEntryForProvider(auth, providerID);
329
- if (!entry) {
330
- throw new Error(`No OpenCode login found for provider "${providerID}"`);
331
- }
332
-
333
- if (providerID === 'github-copilot') {
334
- // OpenCode uses the stored device-OAuth token directly as the bearer —
335
- // access === refresh, no exchange, no expiry.
336
- const token = entry.refresh || entry.access || entry.key;
337
- if (!token) {
338
- throw new Error('GitHub Copilot login has no token');
339
- }
340
- const baseURL = entry.enterpriseUrl
341
- ? `https://copilot-api.${String(entry.enterpriseUrl).replace(/^https?:\/\//, '').replace(/\/+$/, '')}`
342
- : 'https://api.githubcopilot.com';
343
- return callOpenaiCompatible({
344
- baseURL,
345
- headers: {
346
- Authorization: `Bearer ${token}`,
347
- 'User-Agent': USER_AGENT,
348
- 'Openai-Intent': 'conversation-edits',
349
- 'x-initiator': 'agent',
350
- 'X-GitHub-Api-Version': '2026-06-01',
351
- },
352
- modelID,
353
- prompt,
354
- system,
355
- maxOutputTokens: tokens,
356
- providerLabel: 'GitHub Copilot',
357
- });
358
- }
359
-
360
- if (providerID === 'openai' && entry.type === 'oauth') {
361
- const fresh = await ensureFreshOpenaiOauth(entry);
362
- return callCodexResponses({
363
- accessToken: fresh.access,
364
- accountId: fresh.accountId || extractChatgptAccountId(fresh.access),
365
- modelID,
366
- prompt,
367
- system,
368
- });
369
- }
370
-
371
- const apiKey = entry.type === 'api' ? entry.key
372
- : entry.type === 'wellknown' ? entry.token
373
- : entry.access;
374
- if (!apiKey) {
375
- throw new Error(`OpenCode login for "${providerID}" has no usable credential`);
376
- }
377
-
378
- if (providerID === 'anthropic') {
379
- return callAnthropic({ apiKey, modelID, prompt, system, maxOutputTokens: tokens });
380
- }
381
- if (providerID === 'google') {
382
- return callGoogle({ apiKey, modelID, prompt, system, maxOutputTokens: tokens });
383
- }
384
-
385
- // Everything else: OpenAI-compatible chat completions. Base URL order:
386
- // (1) OpenCode merged config provider.<id>.options.baseURL
387
- // (2) target model api.url from the directory catalog (OpenCode SDK)
388
- // (3) known provider constants (openai only — other adapters are above)
389
- // Never uses provider-level models.dev `api` fields.
390
- const provider = getCatalogProvider(catalog, providerID);
391
- const providerConfigUrl = providerConfig?.baseURL;
392
- const modelApiUrl = typeof catalog?.[providerID]?.models?.[modelID]?.api?.url === 'string'
393
- ? catalog[providerID].models[modelID].api.url.trim()
394
- : '';
395
- const defaultOpenaiUrl = 'https://api.openai.com/v1';
396
- const knownProviderUrl = providerID === 'openai' ? defaultOpenaiUrl : null;
397
- const baseURL = typeof providerConfigUrl === 'string' && providerConfigUrl
398
- ? providerConfigUrl
399
- : modelApiUrl || knownProviderUrl;
400
- if (!baseURL) {
401
- throw new Error(`Provider "${providerID}" has no known API base URL`);
402
- }
403
-
404
- // Thinking models burn the output budget on reasoning and leave content
405
- // empty — disable thinking where a wire-format switch exists (mirrors
406
- // OpenCode's smallOptions/variants special cases). There is NO universal
407
- // parameter: unknown body fields 400 on some providers, so this stays an
408
- // explicit allowlist. Models without a switch (DeepSeek, Qwen, Kimi, …)
409
- // just get the generous output budget.
410
- const lowerModel = modelID.toLowerCase();
411
- const supportsThinkingToggle = providerID.includes('zai')
412
- || providerID.includes('zhipu')
413
- || lowerModel.includes('glm')
414
- || lowerModel.includes('minimax-m3');
415
- const extraBody = supportsThinkingToggle ? { thinking: { type: 'disabled' } } : undefined;
416
-
417
- return callOpenaiCompatible({
418
- baseURL,
419
- headers: { Authorization: `Bearer ${apiKey}` },
420
- modelID,
421
- prompt,
422
- system,
423
- maxOutputTokens: tokens,
424
- providerLabel: provider?.name || providerID,
425
- extraBody,
426
- });
427
- }