@openchambery/web 1.19.22-beta.1 → 2.0.0-beta.3

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 (311) hide show
  1. package/README.md +13 -7
  2. package/bin/cli-opencode.test.js +208 -0
  3. package/bin/cli.js +62 -9
  4. package/bin/lib/cli-startup.js +105 -2
  5. package/bin/lib/cli-startup.test.js +179 -0
  6. package/dist/assets/{AgentAvatar-CRaBJXKs.js → AgentAvatar-B_JQoreg.js} +1 -1
  7. package/dist/assets/AssistantView-ClQAfGOT.js +3 -0
  8. package/dist/assets/{DiagramView-D1LGK_s2.js → DiagramView-BNnda8qO.js} +1 -1
  9. package/dist/assets/{FadeInOnReveal-B0kJo9Ii.js → FadeInOnReveal-VYQTSAkr.js} +1 -1
  10. package/dist/assets/{Icon-BgdfPhNs.js → Icon-BiEQEcih.js} +1 -1
  11. package/dist/assets/MarkdownRenderer-CJHSsWin.js +2 -0
  12. package/dist/assets/{MarkdownRendererImpl-DfoCJ1Mq.js → MarkdownRendererImpl-DwrHFGUp.js} +3 -3
  13. package/dist/assets/{MarkstreamRendererImpl-D2dsW5Lg.js → MarkstreamRendererImpl-CSnbQMu3.js} +1 -1
  14. package/dist/assets/MobileDetailNavigation-BXr6AGLT.js +1 -0
  15. package/dist/assets/{MobileShareBridge-2Yjn6wXg.js → MobileShareBridge-D7OE9OP_.js} +2 -2
  16. package/dist/assets/MobileSurface-D2jUkX9l.js +2 -0
  17. package/dist/assets/{MultiRunWindow-tTbMFowS.js → MultiRunWindow-DF3iIFCd.js} +1 -1
  18. package/dist/assets/{OnboardingScreen-CD2KtlF3.js → OnboardingScreen-BjHmlwlw.js} +1 -1
  19. package/dist/assets/OverlayScrollbar-DLtWO_Gp.js +2 -0
  20. package/dist/assets/SettingsView-BRxELjow.js +25 -0
  21. package/dist/assets/{SettingsWindow-zIa76s4t.js → SettingsWindow-BI5swspb.js} +1 -1
  22. package/dist/assets/{TerminalView-CDdzF4yi.js → TerminalView-BU3x8-fr.js} +1 -1
  23. package/dist/assets/{ToolOutputDialog-DNqA4uBl.js → ToolOutputDialog-BEWhBcRd.js} +2 -2
  24. package/dist/assets/appThemeRegistry-4MR0F7Jz.js +1 -0
  25. package/dist/assets/{button-CI7Q-Zw_.js → button-DKuz3L8l.js} +1 -1
  26. package/dist/assets/collapsible-DXbDynN4.js +3 -0
  27. package/dist/assets/context-menu-CqolsLCm.js +1 -0
  28. package/dist/assets/{desktopHosts-B1ixEylr.js → desktopHosts-CoDkOnyS.js} +1 -1
  29. package/dist/assets/{desktopRelayRestore-4JmFUw8r.js → desktopRelayRestore-DDlVKyOE.js} +1 -1
  30. package/dist/assets/{diagram-DdLO8HZ9.js → diagram-D7T8HQgj.js} +1 -1
  31. package/dist/assets/{dialog-B6pnIYmp.js → dialog-DQYl3W_o.js} +1 -1
  32. package/dist/assets/es-CN3E_zXj.js +15 -0
  33. package/dist/assets/{fileMentionTouchSelection-DNeiqns-.js → fileMentionTouchSelection-BZ6ovBYH.js} +1 -1
  34. package/dist/assets/{fileQueries-DQc8EJUf.js → fileQueries-JrpcsOCs.js} +1 -1
  35. package/dist/assets/fr-9zZvQZD2.js +13 -0
  36. package/dist/assets/gitApi-CM42qUed.js +10 -0
  37. package/dist/assets/{imageSource-BlAJJUEI.js → imageSource-CIl0FL10.js} +1 -1
  38. package/dist/assets/{input-D-__fwRi.js → input-CzQy8UhV.js} +1 -1
  39. package/dist/assets/insertionBoundaries-CtkZ9720.js +1 -0
  40. package/dist/assets/ja-Dnjk_82N.js +15 -0
  41. package/dist/assets/ko-BxCEE99C.js +15 -0
  42. package/dist/assets/main-BILgBI3B.js +169 -0
  43. package/dist/assets/main-BeveiW6F.js +2 -0
  44. package/dist/assets/markdown-worker-bS5OgGO3.js +2 -0
  45. package/dist/assets/{markdownHeightCache-BhxqJOvf.js → markdownHeightCache-Cj1I5NDw.js} +1 -1
  46. package/dist/assets/{markdownTheme-DOfGg-A7.js → markdownTheme-CKac3wKC.js} +1 -1
  47. package/dist/assets/mcpQueries-D5mQymgM.js +1 -0
  48. package/dist/assets/miniChat-BgrzM3u7.js +2 -0
  49. package/dist/assets/mobile-DtYoQE8C.js +2 -0
  50. package/dist/assets/multirun-jy8dEI96.js +4 -0
  51. package/dist/assets/{persistence-Ce3AnpEr.js → persistence-C9LQpiOS.js} +2 -2
  52. package/dist/assets/pl-CNNsWnPM.js +15 -0
  53. package/dist/assets/platform-BQghHRfp.js +6 -0
  54. package/dist/assets/pt-BR-BZB2DJMp.js +15 -0
  55. package/dist/assets/renderElectronMiniChatApp-BGrrmeME.js +2 -0
  56. package/dist/assets/renderMobileApp-u3c4GtFC.js +28 -0
  57. package/dist/assets/{runtimeConfig-CMkwoQ_X.js → runtimeConfig-BIGBRGQI.js} +1 -1
  58. package/dist/assets/{runtimeEndpointReset-ChOxt-J7.js → runtimeEndpointReset-D2savkuw.js} +12 -12
  59. package/dist/assets/{sessionChangeSummary-NnxcqCTI.js → sessionChangeSummary-eIbpSfrO.js} +1 -1
  60. package/dist/assets/{sessionLookup-BrU3rFHJ.js → sessionLookup-myHe5ltu.js} +1 -1
  61. package/dist/assets/{sortable-tabs-strip-Fnfhf3K9.js → sortable-tabs-strip-BQtmHx9r.js} +1 -1
  62. package/dist/assets/src-CLN7Z-zx.css +2 -0
  63. package/dist/assets/store-DLCiZaA6.js +15 -0
  64. package/dist/assets/{terminalApi-xjqssKke.js → terminalApi-BxcdiduX.js} +1 -1
  65. package/dist/assets/{textarea-bHYxZtfH.js → textarea-CakKvqFw.js} +1 -1
  66. package/dist/assets/{tooltip-0T5jv0cu.js → tooltip-Cx5gTbdY.js} +1 -1
  67. package/dist/assets/uk-rwQrxeYM.js +15 -0
  68. package/dist/assets/{url-_z7evzdK.js → url-CDHnjlsm.js} +1 -1
  69. package/dist/assets/{useAppFontEffects-C02YFyAv.js → useAppFontEffects-CNeo0aZs.js} +59 -45
  70. package/dist/assets/{useAssistantUIStore-CMutVh9B.js → useAssistantUIStore-asNG6K7n.js} +1 -1
  71. package/dist/assets/useDesktopWindowControlsLayout-DBJ56qIo.js +1 -0
  72. package/dist/assets/useEffectiveDirectory-B4LvR8Gt.js +1 -0
  73. package/dist/assets/{useFontPreferences-B1JYyzpK.js → useFontPreferences-CUnQ4TVm.js} +1 -1
  74. package/dist/assets/useMobileNavigationStore-ZHJPxlHt.js +414 -0
  75. package/dist/assets/{useRuntimeAPIs-DSLXJybW.js → useRuntimeAPIs-DwLtO0c_.js} +1 -1
  76. package/dist/assets/useSessionAutoCleanup-BIVmrnYU.js +2 -0
  77. package/dist/assets/{useThemeSystem-DMzWVJaW.js → useThemeSystem-BlNUWH47.js} +1 -1
  78. package/dist/assets/useUIStore-CQYKJnrg.js +1 -0
  79. package/dist/assets/{useWorkerHighlightedLines-C9z0ML1z.js → useWorkerHighlightedLines-CJZbJDW3.js} +2 -2
  80. package/dist/assets/{vendor-base-ui-CnHfUmDN.js → vendor-base-ui-CDjyofeD.js} +1 -1
  81. package/dist/assets/{vendor-cmdk-CeAGfrLo.js → vendor-cmdk-BFH0UpXX.js} +1 -1
  82. package/dist/assets/{vendor-dnd-kit-accessibility-BY5LhhUB.js → vendor-dnd-kit-accessibility-BrR3AKvM.js} +1 -1
  83. package/dist/assets/{vendor-dnd-kit-core-CvWxUPdb.js → vendor-dnd-kit-core-CNfdvk6H.js} +1 -1
  84. package/dist/assets/{vendor-dnd-kit-sortable-hHdP9lLb.js → vendor-dnd-kit-sortable-DQUaTeGj.js} +1 -1
  85. package/dist/assets/{vendor-framer-motion-DFy_BSVd.js → vendor-framer-motion-4dogsUk3.js} +1 -1
  86. package/dist/assets/{vendor-legendapp-list-WE32lwNk.js → vendor-legendapp-list-BU7MdPZR.js} +1 -1
  87. package/dist/assets/{vendor-markstream-react-DX55XLGO.js → vendor-markstream-react-qHC2FykU.js} +3 -3
  88. package/dist/assets/vendor-opencode-client-BOe2qpQb.js +12 -0
  89. package/dist/assets/{vendor-pierre-diffs-DX7XTPG3.js → vendor-pierre-diffs-CUfqC003.js} +1 -1
  90. package/dist/assets/{vendor-react-DLPElpoQ.js → vendor-react-C94A0AHl.js} +1 -1
  91. package/dist/assets/{vendor-react-drawio-ECtEwvbc.js → vendor-react-drawio-FVlWqKVE.js} +1 -1
  92. package/dist/assets/{vendor-reactuses-core-BnW6GxJt.js → vendor-reactuses-core-C2A3EjUi.js} +1 -1
  93. package/dist/assets/{vendor-sonner-DVzioF3d.js → vendor-sonner-DNqHmUQs.js} +1 -1
  94. package/dist/assets/{vendor-stream-diffs-DWy3xSmp.js → vendor-stream-diffs-CwbKsmlv.js} +2 -2
  95. package/dist/assets/{vendor-tanstack-react-query-D7pys_c9.js → vendor-tanstack-react-query-D0iFK57C.js} +1 -1
  96. package/dist/assets/{vendor-tanstack-react-virtual-tlDgH6PG.js → vendor-tanstack-react-virtual-B2kV6cnT.js} +1 -1
  97. package/dist/assets/{vendor-yaml-DIcAK3EC.js → vendor-yaml-C9KlJAlj.js} +37 -37
  98. package/dist/assets/{vendor-zustand-B4dlGEM2.js → vendor-zustand-BqLKf6ZY.js} +1 -1
  99. package/dist/assets/zh-CN-BvmBDnkD.js +15 -0
  100. package/dist/assets/zh-TW-9wcHTX0y.js +15 -0
  101. package/dist/index.html +13 -13
  102. package/dist/mini-chat.html +13 -13
  103. package/dist/mobile.html +13 -13
  104. package/package.json +2 -2
  105. package/server/index.js +392 -20
  106. package/server/lib/assistants/DOCUMENTATION.md +26 -21
  107. package/server/lib/assistants/assign.js +2 -1
  108. package/server/lib/assistants/contact-store.js +37 -19
  109. package/server/lib/assistants/contracts.js +11 -1
  110. package/server/lib/assistants/projection-history.test.js +454 -0
  111. package/server/lib/assistants/routes.js +11 -5
  112. package/server/lib/assistants/service.js +850 -294
  113. package/server/lib/assistants/service.test.js +588 -274
  114. package/server/lib/conversations/DOCUMENTATION.md +5 -5
  115. package/server/lib/conversations/service.js +87 -57
  116. package/server/lib/conversations/service.test.js +132 -100
  117. package/server/lib/conversations/validation.js +11 -1
  118. package/server/lib/event-stream/directory-ws-bridge.js +13 -2
  119. package/server/lib/event-stream/global-hub.js +51 -5
  120. package/server/lib/event-stream/runtime.js +57 -20
  121. package/server/lib/event-stream/runtime.test.js +112 -0
  122. package/server/lib/llm/DOCUMENTATION.md +95 -84
  123. package/server/lib/llm/catalog.js +111 -45
  124. package/server/lib/llm/catalog.test.js +121 -59
  125. package/server/lib/llm/completions.js +41 -12
  126. package/server/lib/llm/completions.test.js +45 -4
  127. package/server/lib/llm/generate.js +516 -453
  128. package/server/lib/llm/generate.test.js +705 -484
  129. package/server/lib/llm/routes.js +8 -4
  130. package/server/lib/message-queue/DOCUMENTATION.md +5 -5
  131. package/server/lib/message-queue/opencode-adapter.js +191 -104
  132. package/server/lib/message-queue/opencode-adapter.test.js +328 -58
  133. package/server/lib/message-queue/runtime.js +15 -2
  134. package/server/lib/message-queue/runtime.test.js +6 -6
  135. package/server/lib/message-queue/worker.js +7 -1
  136. package/server/lib/notifications/runtime.js +5 -0
  137. package/server/lib/opencode/DOCUMENTATION.md +103 -16
  138. package/server/lib/opencode/agent-document.d.ts +28 -0
  139. package/server/lib/opencode/agent-document.js +280 -0
  140. package/server/lib/opencode/agent-document.test.js +137 -0
  141. package/server/lib/opencode/agents.js +96 -0
  142. package/server/lib/opencode/auth-state-runtime.js +5 -0
  143. package/server/lib/opencode/bootstrap-runtime.js +2 -0
  144. package/server/lib/opencode/config-entity-routes.js +111 -102
  145. package/server/lib/opencode/config-entity-routes.test.js +123 -10
  146. package/server/lib/opencode/config-hot-reload.integration.test.js +105 -0
  147. package/server/lib/opencode/ensure-cli.js +238 -0
  148. package/server/lib/opencode/ensure-cli.test.js +119 -0
  149. package/server/lib/opencode/env-runtime.js +208 -111
  150. package/server/lib/opencode/env-runtime.test.js +211 -14
  151. package/server/lib/opencode/feature-routes-runtime.js +80 -5
  152. package/server/lib/opencode/feature-routes-runtime.test.js +18 -0
  153. package/server/lib/opencode/http-server-connections.js +98 -0
  154. package/server/lib/opencode/http-server-connections.test.js +321 -0
  155. package/server/lib/opencode/lifecycle.js +539 -95
  156. package/server/lib/opencode/lifecycle.test.js +698 -6
  157. package/server/lib/opencode/managed-capabilities-runtime.js +15 -2
  158. package/server/lib/opencode/managed-capabilities-runtime.test.js +1 -0
  159. package/server/lib/opencode/network-runtime.js +28 -16
  160. package/server/lib/opencode/network-runtime.test.js +68 -4
  161. package/server/lib/opencode/openchamber-routes.js +7 -1
  162. package/server/lib/opencode/opencode2-pin.js +139 -0
  163. package/server/lib/opencode/opencode2-pin.test.js +88 -0
  164. package/server/lib/opencode/opencode2-sidecar.e2e.test.js +326 -0
  165. package/server/lib/opencode/owned-runtime-upgrade.js +431 -0
  166. package/server/lib/opencode/owned-runtime-upgrade.test.js +160 -0
  167. package/server/lib/opencode/plugin-compatibility.js +15 -0
  168. package/server/lib/opencode/plugin-compatibility.test.js +21 -0
  169. package/server/lib/opencode/plugin-routes.js +3 -18
  170. package/server/lib/opencode/plugin-routes.test.js +9 -9
  171. package/server/lib/opencode/plugins.js +4 -3
  172. package/server/lib/opencode/plugins.test.js +12 -0
  173. package/server/lib/opencode/provider-catalog.js +58 -12
  174. package/server/lib/opencode/provider-catalog.test.js +48 -0
  175. package/server/lib/opencode/proxy.js +368 -28
  176. package/server/lib/opencode/proxy.test.js +338 -0
  177. package/server/lib/opencode/routes.js +303 -76
  178. package/server/lib/opencode/routes.test.js +315 -50
  179. package/server/lib/opencode/runtime-contract.js +401 -0
  180. package/server/lib/opencode/runtime-contract.test.js +214 -0
  181. package/server/lib/opencode/server-opencode-fetch.js +242 -0
  182. package/server/lib/opencode/server-opencode-fetch.test.js +130 -0
  183. package/server/lib/opencode/server-utils-runtime.js +35 -0
  184. package/server/lib/opencode/shared-service.integration.test.js +66 -0
  185. package/server/lib/opencode/shared-service.js +147 -0
  186. package/server/lib/opencode/shared-service.test.js +93 -0
  187. package/server/lib/opencode/shared.js +3 -3
  188. package/server/lib/opencode/shutdown-runtime.js +15 -6
  189. package/server/lib/opencode/shutdown-runtime.test.js +49 -1
  190. package/server/lib/opencode/skill-routes.js +26 -40
  191. package/server/lib/opencode/skill-routes.test.js +71 -7
  192. package/server/lib/opencode/skills.test.js +15 -0
  193. package/server/lib/opencode/startup-pipeline-runtime.js +4 -0
  194. package/server/lib/opencode/static-routes-runtime.js +3 -1
  195. package/server/lib/opencode/static-routes-runtime.test.js +33 -0
  196. package/server/lib/opencode/v1-migration-gate.js +157 -0
  197. package/server/lib/opencode/v1-migration-gate.test.js +111 -0
  198. package/server/lib/opencode/v2-client.js +60 -0
  199. package/server/lib/opencode/v2-plugin-host-shim/index.mjs +155 -0
  200. package/server/lib/opencode/v2-plugin-host-shim/package.json +4 -0
  201. package/server/lib/opencode/watcher.js +2 -1
  202. package/server/lib/opencode/watcher.test.js +1 -1
  203. package/server/lib/permission-auto-accept/DOCUMENTATION.md +5 -5
  204. package/server/lib/permission-auto-accept/runtime.js +14 -7
  205. package/server/lib/permission-auto-accept/runtime.test.js +27 -12
  206. package/server/lib/relay/reasoning-projection.e2e.test.ts +22 -7
  207. package/server/lib/scheduled-tasks/DOCUMENTATION.md +79 -39
  208. package/server/lib/scheduled-tasks/managed-tool-route.js +31 -1
  209. package/server/lib/scheduled-tasks/managed-tool-route.test.js +23 -0
  210. package/server/lib/scheduled-tasks/routes.js +27 -0
  211. package/server/lib/scheduled-tasks/routes.test.js +94 -0
  212. package/server/lib/scheduled-tasks/runtime.js +381 -310
  213. package/server/lib/scheduled-tasks/runtime.test.js +600 -309
  214. package/server/lib/session-goal/DOCUMENTATION.md +173 -22
  215. package/server/lib/session-goal/capability.js +57 -0
  216. package/server/lib/session-goal/capability.test.js +33 -0
  217. package/server/lib/session-goal/routes.js +240 -2
  218. package/server/lib/session-goal/routes.test.js +211 -0
  219. package/server/lib/session-goal/runtime.js +1617 -113
  220. package/server/lib/session-goal/runtime.test.js +1500 -70
  221. package/server/lib/session-index/DOCUMENTATION.md +18 -15
  222. package/server/lib/session-index/event-ingest.js +89 -16
  223. package/server/lib/session-index/event-ingest.test.js +73 -0
  224. package/server/lib/session-index/host-authority-snapshot.js +92 -0
  225. package/server/lib/session-index/routes.host-archive.test.js +201 -0
  226. package/server/lib/session-index/routes.js +63 -10
  227. package/server/lib/session-index/routes.test.js +8 -8
  228. package/server/lib/session-index/service.js +7 -1
  229. package/server/lib/session-index/sync-runtime.js +171 -41
  230. package/server/lib/session-index/sync-runtime.test.js +164 -9
  231. package/server/lib/session-metadata/DOCUMENTATION.md +86 -0
  232. package/server/lib/session-metadata/routes.js +193 -0
  233. package/server/lib/session-metadata/routes.test.js +156 -0
  234. package/server/lib/session-metadata/session-archive.d.ts +73 -0
  235. package/server/lib/session-metadata/session-archive.js +654 -0
  236. package/server/lib/session-metadata/session-archive.test.js +359 -0
  237. package/server/lib/session-metadata/session-metadata-store.d.ts +36 -0
  238. package/server/lib/session-metadata/session-metadata-store.js +375 -0
  239. package/server/lib/session-metadata/session-metadata-store.test.js +379 -0
  240. package/server/lib/session-metadata/session-projection.d.ts +41 -0
  241. package/server/lib/session-metadata/session-projection.js +255 -0
  242. package/server/lib/session-metadata/session-projection.test.js +64 -0
  243. package/server/lib/session-metadata/system-session.js +55 -0
  244. package/server/lib/session-metadata/system-session.test.js +55 -0
  245. package/server/lib/session-title/DOCUMENTATION.md +40 -7
  246. package/server/lib/session-title/native-runtime.test.js +167 -0
  247. package/server/lib/session-title/runtime.js +67 -42
  248. package/server/lib/session-title/session-access.js +46 -0
  249. package/server/lib/session-title/session-access.test.js +91 -0
  250. package/server/lib/session-turn-pages/DOCUMENTATION.md +12 -11
  251. package/server/lib/session-turn-pages/reconcile.service.js +1 -1
  252. package/server/lib/session-turn-pages/reconcile.service.test.js +16 -0
  253. package/server/lib/session-turn-pages/routes.js +123 -75
  254. package/server/lib/session-turn-pages/routes.test.js +566 -0
  255. package/server/lib/session-turn-pages/service.js +6 -2
  256. package/server/lib/session-turn-pages/service.test.js +34 -0
  257. package/server/lib/session-turn-pages/session-message-projection.d.ts +13 -0
  258. package/server/lib/session-turn-pages/session-message-projection.js +281 -0
  259. package/server/lib/small-model/DOCUMENTATION.md +101 -146
  260. package/server/lib/small-model/catalog.js +84 -54
  261. package/server/lib/small-model/catalog.test.js +111 -9
  262. package/server/lib/small-model/custom-api.js +68 -0
  263. package/server/lib/small-model/custom-api.test.js +64 -0
  264. package/server/lib/small-model/index.js +125 -241
  265. package/server/lib/small-model/resolve.js +35 -120
  266. package/server/lib/small-model/resolve.test.js +20 -110
  267. package/server/lib/small-model/routes.js +1 -3
  268. package/server/lib/small-model/summary-settings.test.js +157 -253
  269. package/server/opencode-proxy.test.js +63 -12
  270. package/dist/assets/AssistantView-BUveAgc4.js +0 -3
  271. package/dist/assets/MarkdownRenderer-BbrbDH6-.js +0 -2
  272. package/dist/assets/MobileDetailNavigation-B0aqKXd7.js +0 -1
  273. package/dist/assets/MobileSurface-DW7WfPsy.js +0 -2
  274. package/dist/assets/OverlayScrollbar-DrA0Vsg5.js +0 -2
  275. package/dist/assets/SettingsView-PkXIavr7.js +0 -25
  276. package/dist/assets/appThemeRegistry-D5Yxg4YF.js +0 -1
  277. package/dist/assets/collapsible-D5Phaykh.js +0 -1
  278. package/dist/assets/context-menu-CubSDW2g.js +0 -1
  279. package/dist/assets/es-DA3MqOo0.js +0 -15
  280. package/dist/assets/fr-Dbz0CdIA.js +0 -13
  281. package/dist/assets/gitApi-DXyC4MiV.js +0 -7
  282. package/dist/assets/insertionBoundaries-KlOVrHHt.js +0 -1
  283. package/dist/assets/ja-DumanVQA.js +0 -15
  284. package/dist/assets/ko-C0IkiUjQ.js +0 -15
  285. package/dist/assets/main-BPS5ylib.js +0 -2
  286. package/dist/assets/main-Cpd9VWYs.js +0 -169
  287. package/dist/assets/markdown-worker-CptoICmq.js +0 -2
  288. package/dist/assets/miniChat-B8_KLWiY.js +0 -2
  289. package/dist/assets/mobile-CtktKrXn.js +0 -2
  290. package/dist/assets/multirun-CYfP9Cfm.js +0 -4
  291. package/dist/assets/pl-B9No6MQp.js +0 -15
  292. package/dist/assets/platform-0w_Q9_ji.js +0 -2
  293. package/dist/assets/projectMeta-S1hJ7oBp.js +0 -3
  294. package/dist/assets/pt-BR-DyQ0WTDh.js +0 -15
  295. package/dist/assets/renderElectronMiniChatApp-iK14LWm8.js +0 -2
  296. package/dist/assets/renderMobileApp-DORLPWnb.js +0 -28
  297. package/dist/assets/src-rS3zCcsC.css +0 -2
  298. package/dist/assets/store-5B_LDnLM.js +0 -15
  299. package/dist/assets/uk-C_1KkqyP.js +0 -15
  300. package/dist/assets/useDesktopWindowControlsLayout-Dnr-jX0C.js +0 -1
  301. package/dist/assets/useEffectiveDirectory-CZLLyarc.js +0 -1
  302. package/dist/assets/useMobileNavigationStore-CoCiqNgm.js +0 -413
  303. package/dist/assets/useSessionAutoCleanup-xQyETQkG.js +0 -2
  304. package/dist/assets/useUIStore-Bxv3f23A.js +0 -1
  305. package/dist/assets/vendor-opencode-sdk-C07HQypa.js +0 -7
  306. package/dist/assets/zh-CN-uWILgBdt.js +0 -15
  307. package/dist/assets/zh-TW-MYfdvhbD.js +0 -15
  308. package/server/lib/small-model/call.js +0 -427
  309. package/server/lib/small-model/call.test.js +0 -382
  310. package/server/lib/small-model/opencode-session.js +0 -339
  311. package/server/lib/small-model/opencode-session.test.js +0 -177
@@ -0,0 +1,281 @@
1
+ /**
2
+ * OpenCode v2 SessionMessageInfo → Host `{ info, parts }` projection.
3
+ *
4
+ * Shared by the web session-turn-pages routes and the VS Code turn-page
5
+ * bridge so both runtimes expose the same external record contract.
6
+ * Pure: no I/O, no logging.
7
+ */
8
+
9
+ /**
10
+ * Native SessionMessageInfo carrier fields that become `parts`.
11
+ * Must not remain on `info` after normalize — otherwise
12
+ * includeReasoning=false can strip parts while leaking content/text.
13
+ */
14
+ const MESSAGE_PART_CARRIER_KEYS = new Set(['content', 'text', 'files', 'parts', 'agents', 'skills']);
15
+
16
+ /**
17
+ * v2 session-lifecycle / control rows. OpenCode lists them alongside chat
18
+ * turns; projecting `type`→`role` made cold reload paint empty Assistant
19
+ * headers (FINAL-GATE hard reload). Align with UI session-projection-api.
20
+ */
21
+ const CONTROL_MESSAGE_TYPES = new Set([
22
+ 'idle',
23
+ 'model-switched',
24
+ 'agent-selected',
25
+ 'agent-switched',
26
+ 'location-switched',
27
+ ]);
28
+
29
+ const CHAT_MESSAGE_TYPES = new Set([
30
+ 'user',
31
+ 'assistant',
32
+ 'system',
33
+ 'synthetic',
34
+ 'compaction',
35
+ ]);
36
+
37
+ const asNonEmptyString = (value) => (
38
+ typeof value === 'string' && value.trim().length > 0 ? value.trim() : undefined
39
+ );
40
+
41
+ const stripMessagePartCarriers = (value) => {
42
+ if (!value || typeof value !== 'object' || Array.isArray(value)) return value;
43
+ let changed = false;
44
+ const next = {};
45
+ for (const [key, field] of Object.entries(value)) {
46
+ if (MESSAGE_PART_CARRIER_KEYS.has(key)) {
47
+ changed = true;
48
+ continue;
49
+ }
50
+ next[key] = field;
51
+ }
52
+ return changed ? next : value;
53
+ };
54
+
55
+ /**
56
+ * True for v2 control rows that must never reach ChatMessage props.
57
+ * Accepts raw SessionMessageInfo or already-projected `{ info, parts }`.
58
+ */
59
+ const isControlSessionMessage = (entry) => {
60
+ if (!entry || typeof entry !== 'object') return false;
61
+ const info = entry.info && typeof entry.info === 'object' ? entry.info : entry;
62
+ const type = asNonEmptyString(info.type);
63
+ const role = asNonEmptyString(info.role) ?? asNonEmptyString(info.clientRole);
64
+ if (type && CONTROL_MESSAGE_TYPES.has(type)) return true;
65
+ if (role && CONTROL_MESSAGE_TYPES.has(role)) return true;
66
+ return false;
67
+ };
68
+
69
+ /**
70
+ * Project SessionMessageInfo body fields into host `parts`.
71
+ *
72
+ * v2 assistants put body in `content[]`. Users put body in top-level `text`.
73
+ * Never expand both: the same string in `text` and `content[].text` produced
74
+ * duplicate text parts (FINAL-GATE follow-up cold props).
75
+ */
76
+ const projectV2Parts = (entry) => {
77
+ if (Array.isArray(entry?.parts)) return entry.parts;
78
+ const messageID = typeof entry?.id === 'string' ? entry.id : '';
79
+ const sessionID = typeof entry?.sessionID === 'string' ? entry.sessionID : '';
80
+ const parts = [];
81
+ const content = Array.isArray(entry?.content) ? entry.content : null;
82
+ const hasContent = Boolean(content && content.length > 0);
83
+
84
+ if (hasContent) {
85
+ let textOrdinal = 0;
86
+ let reasoningOrdinal = 0;
87
+ content.forEach((item, index) => {
88
+ if (!item || typeof item !== 'object') return;
89
+ if (item.type === 'text') {
90
+ const partID = asNonEmptyString(item.id) || `${messageID}:text:${textOrdinal}`;
91
+ textOrdinal += 1;
92
+ parts.push({
93
+ id: partID,
94
+ sessionID,
95
+ messageID,
96
+ type: 'text',
97
+ text: typeof item.text === 'string' ? item.text : '',
98
+ });
99
+ return;
100
+ }
101
+ if (item.type === 'reasoning') {
102
+ const partID = asNonEmptyString(item.id) || `${messageID}:reasoning:${reasoningOrdinal}`;
103
+ reasoningOrdinal += 1;
104
+ parts.push({
105
+ id: partID,
106
+ sessionID,
107
+ messageID,
108
+ type: 'reasoning',
109
+ text: typeof item.text === 'string' ? item.text : '',
110
+ });
111
+ return;
112
+ }
113
+ if (item.type === 'tool') {
114
+ const partID = asNonEmptyString(item.id) || `${messageID}:tool:${index}`;
115
+ parts.push({
116
+ id: partID,
117
+ sessionID,
118
+ messageID,
119
+ type: 'tool',
120
+ tool: item.name,
121
+ callID: item.id,
122
+ state: item.state ?? {},
123
+ });
124
+ }
125
+ });
126
+ } else if (typeof entry?.text === 'string' && entry.text) {
127
+ // User / synthetic body when content[] is absent.
128
+ parts.push({
129
+ id: `${messageID}:text:0`,
130
+ sessionID,
131
+ messageID,
132
+ type: 'text',
133
+ text: entry.text,
134
+ });
135
+ }
136
+
137
+ if (Array.isArray(entry?.files)) {
138
+ entry.files.forEach((file, index) => {
139
+ parts.push({
140
+ id: `${messageID}:file:${index}`,
141
+ sessionID,
142
+ messageID,
143
+ type: 'file',
144
+ mime: file?.mime || 'application/octet-stream',
145
+ url: file?.uri ?? file?.url,
146
+ ...(file?.name ? { filename: file.name } : {}),
147
+ });
148
+ });
149
+ }
150
+ return parts;
151
+ };
152
+
153
+ /**
154
+ * Build clean chat `info` from a native SessionMessageInfo row.
155
+ * Does not spread wire carriers (`type`, `rawFinish`, `outcome`, `content`).
156
+ */
157
+ const projectNativeSessionInfo = (entry, role) => {
158
+ const info = {
159
+ id: entry.id,
160
+ ...(asNonEmptyString(entry.sessionID) ? { sessionID: entry.sessionID } : {}),
161
+ role,
162
+ time: entry.time && typeof entry.time === 'object' ? entry.time : { created: 0 },
163
+ };
164
+
165
+ const agent = asNonEmptyString(entry.agent) ?? asNonEmptyString(entry.mode);
166
+ if (agent) info.agent = agent;
167
+ if (asNonEmptyString(entry.mode) && entry.mode !== agent) info.mode = entry.mode;
168
+
169
+ const model = entry.model && typeof entry.model === 'object' && !Array.isArray(entry.model)
170
+ ? entry.model
171
+ : null;
172
+ const modelID = asNonEmptyString(model?.id)
173
+ ?? asNonEmptyString(model?.modelID)
174
+ ?? asNonEmptyString(entry.modelID);
175
+ const providerID = asNonEmptyString(model?.providerID) ?? asNonEmptyString(entry.providerID);
176
+ const variant = asNonEmptyString(model?.variant) ?? asNonEmptyString(entry.variant);
177
+ if (modelID) info.modelID = modelID;
178
+ if (providerID) info.providerID = providerID;
179
+ if (variant) info.variant = variant;
180
+ if (modelID || providerID || variant) {
181
+ info.model = {
182
+ ...(providerID ? { providerID } : {}),
183
+ ...(modelID ? { modelID } : {}),
184
+ ...(variant ? { variant } : {}),
185
+ };
186
+ }
187
+
188
+ const finish = asNonEmptyString(entry.finish) ?? asNonEmptyString(entry.rawFinish);
189
+ if (finish) info.finish = finish;
190
+
191
+ if (entry.error && typeof entry.error === 'object' && !Array.isArray(entry.error)) {
192
+ info.error = {
193
+ type: asNonEmptyString(entry.error.type) ?? 'error',
194
+ message: typeof entry.error.message === 'string' ? entry.error.message : '',
195
+ ...(typeof entry.error.status === 'number' ? { status: entry.error.status } : {}),
196
+ };
197
+ }
198
+
199
+ if (typeof entry.cost === 'number' && Number.isFinite(entry.cost)) info.cost = entry.cost;
200
+ if (entry.tokens && typeof entry.tokens === 'object' && !Array.isArray(entry.tokens)) {
201
+ info.tokens = entry.tokens;
202
+ }
203
+ if (asNonEmptyString(entry.parentID)) info.parentID = entry.parentID;
204
+ if (entry.summary != null) info.summary = entry.summary;
205
+ if (role === 'compaction') info.clientRole = 'compaction';
206
+ if (role === 'system') info.clientRole = 'system';
207
+ if (role === 'synthetic') {
208
+ // Synthetic rows render as user-authored system injections in the UI.
209
+ info.role = 'user';
210
+ info.clientRole = 'system';
211
+ }
212
+
213
+ return info;
214
+ };
215
+
216
+ /**
217
+ * Project SessionMessageInfo (or an already-projected {info, parts} row)
218
+ * into the host HTTP DTO. External route contract stays {info, parts}.
219
+ * Returns `null` for v2 control rows (idle / model-switched / …).
220
+ */
221
+ export const projectSessionMessage = (entry) => {
222
+ if (isControlSessionMessage(entry)) return null;
223
+
224
+ if (entry && typeof entry === 'object' && entry.info && typeof entry.info === 'object') {
225
+ if (isControlSessionMessage(entry.info)) return null;
226
+ const stripped = stripMessagePartCarriers(entry.info);
227
+ // Already-projected chat rows: keep shape, drop leftover wire carriers.
228
+ const info = { ...stripped };
229
+ delete info.type;
230
+ delete info.rawFinish;
231
+ delete info.outcome;
232
+ // Normalize nested model.id → modelID when a prior path left wire shape.
233
+ if (info.model && typeof info.model === 'object' && !Array.isArray(info.model)) {
234
+ const modelID = asNonEmptyString(info.model.modelID)
235
+ ?? asNonEmptyString(info.model.id)
236
+ ?? asNonEmptyString(info.modelID);
237
+ const providerID = asNonEmptyString(info.model.providerID) ?? asNonEmptyString(info.providerID);
238
+ const variant = asNonEmptyString(info.model.variant) ?? asNonEmptyString(info.variant);
239
+ if (modelID) info.modelID = modelID;
240
+ if (providerID) info.providerID = providerID;
241
+ info.model = {
242
+ ...(providerID ? { providerID } : {}),
243
+ ...(modelID ? { modelID } : {}),
244
+ ...(variant ? { variant } : {}),
245
+ };
246
+ }
247
+ const role = asNonEmptyString(info.role) ?? asNonEmptyString(info.clientRole);
248
+ if (!role || CONTROL_MESSAGE_TYPES.has(role)) return null;
249
+ return {
250
+ info,
251
+ parts: Array.isArray(entry.parts) ? entry.parts : [],
252
+ };
253
+ }
254
+
255
+ if (!entry || typeof entry !== 'object' || typeof entry.id !== 'string' || entry.id.length === 0) {
256
+ return entry == null ? null : entry;
257
+ }
258
+
259
+ const type = asNonEmptyString(entry.type);
260
+ if (type && CONTROL_MESSAGE_TYPES.has(type)) return null;
261
+ if (type && !CHAT_MESSAGE_TYPES.has(type)) return null;
262
+
263
+ let role;
264
+ if (type === 'user' || type === 'assistant' || type === 'system' || type === 'compaction' || type === 'synthetic') {
265
+ role = type;
266
+ } else if (asNonEmptyString(entry.role) === 'user' || asNonEmptyString(entry.role) === 'assistant') {
267
+ role = entry.role;
268
+ } else {
269
+ return null;
270
+ }
271
+
272
+ return {
273
+ info: projectNativeSessionInfo(entry, role),
274
+ parts: projectV2Parts(entry),
275
+ };
276
+ };
277
+
278
+ export const projectSessionMessageRecords = (records) =>
279
+ (Array.isArray(records) ? records : [])
280
+ .map(projectSessionMessage)
281
+ .filter((record) => record != null && typeof record === 'object' && record.info);
@@ -1,142 +1,103 @@
1
1
  # Small Model
2
2
 
3
- Server-side LLM calls that reuse the user's existing OpenCode provider logins.
4
- Dedicated adapters still read `~/.local/share/opencode/auth.json`. Plugin and
5
- other non-dedicated providers are listed from OpenCode's connected catalog
6
- (`provider.list()` ∩ `config.providers`, same as `loadConnectedCatalog`).
7
- OpenCode uses a "small model" internally
3
+ Server-side utility LLM calls (commit messages, session titles, goal audits,
4
+ scheduled-task distill, TTS summaries, PR descriptions) routed through the
5
+ user's connected OpenCode providers. OpenCode uses a "small model" internally
8
6
  (titles, summaries) but does not expose it through the SDK or plugins — this
9
- module replicates that mechanism as an OpenChamber runtime API.
7
+ module replicates the selection as an OpenChamber runtime API and sends every
8
+ OpenCode-backed call through the Assistant LLM gateway
9
+ (`llm/completions.js` `createChatCompletion`).
10
10
 
11
11
  ## Security boundary
12
12
 
13
- Credentials never leave the server process. The client sends only a prompt;
14
- auth resolution, OAuth refresh, and provider dispatch all happen server-side
15
- (either in dedicated adapters or inside the OpenCode runtime for the session
16
- path). Routes live under `/api/*` and are gated by the ui-auth middleware like
17
- every other runtime API.
13
+ Credentials never leave OpenCode. This module does not read `auth.json`, does
14
+ not refresh OAuth tokens, and does not call provider APIs directly. The client
15
+ sends only a prompt; model resolution happens server-side and generation runs
16
+ inside the OpenCode runtime. The only direct HTTP call is the user-configured
17
+ custom Summary AI endpoint, with the token the user saved for it. Routes live
18
+ under `/api/*` and are gated by the ui-auth middleware like every other
19
+ runtime API.
18
20
 
19
21
  ## Files
20
22
 
21
23
  - `index.js` — `createSmallModelService(dependencies)` factory. Production
22
24
  obtains a single lazy instance from `server/index.js` (`getSmallModelService`)
23
25
  and injects it into feature routes, session assist/title/goal, and scheduled
24
- tasks. Tests may call the factory with a mock `getModelCatalog`. Returns
25
- `stop()` to remove the lazy temp directory used by the session path (wired
26
- from `server/index.js` `stop()`).
27
- - `opencode-session.js` — temporary OpenCode session path for non-dedicated
28
- providers. Lazily creates `mkdtemp('openchamber-smallmodel-')` with a hidden
29
- deny-all agent, then create → archive → `prompt_async` → poll idle → read
30
- assistant text → best-effort `session.delete`.
31
- - `resolve.js` — model selection, mirroring OpenCode's `getSmallModel` chain:
26
+ tasks. Tests may inject `getModelCatalog`, `openCodeClientFactory`, and
27
+ `createChatCompletion`. The host also injects `persistSessionMetadata` /
28
+ `onSystemSessionPersisted` so gateway throwaway sessions stay out of the
29
+ sidebar index.
30
+ - `resolve.js` — model selection over the connected catalog only, mirroring
31
+ OpenCode's `getSmallModel` chain:
32
32
  0. OpenChamber's own settings override (Settings → Sessions → Small Model):
33
33
  when `smallModelUseDefault` is `false`, `smallModelOverride`
34
34
  (`provider/model`) outranks everything below. Sanitized in
35
35
  `settings-helpers.js` (server), `persistence.ts` (client), and
36
36
  `bridge-settings-runtime.ts` (VS Code).
37
37
  1. `small_model` from the merged OpenCode config layers (`provider/model`).
38
- 2. Family-priority scan (`gemini-flash` → `gpt-nano` → `claude-haiku`)
39
- **within the session's provider first** (`preferredProviderID`, like
40
- OpenCode resolves within the current provider), then keyword tiers on that
41
- provider's catalog, then the session model, then other authenticated
42
- providers' catalog families, then keyword+cost default across providers,
43
- then the four minimal hardcoded candidates.
44
- 3. GitHub Copilot hidden utility models (`gpt-*-nano/mini`) — these never
45
- appear in the catalog, so they participate as the `gpt-nano` family entry
46
- and as a final utility fallback.
47
- 4. Session model (`preferredModelID`) when the session provider has auth but
48
- no small family/keyword match.
49
- Auth-type constraints for the four dedicated providers: OpenAI OAuth →
50
- hardcoded codex-small; OpenAI API key → catalog families; Anthropic/Google
51
- require API keys; Copilot supports auth aliases (`copilot` / `github-copilot`).
52
-
53
- **Default ranking (no explicit config):** candidates are catalog models from
54
- usable auth entries. Keyword tiers (tokenized id / known family):
55
- `flash` → `nano` → `haiku` → `mini` → `lite` → `turbo` → `instant` →
56
- `small` → `chat`. Within a tier: `cost.input` ascending (missing cost last),
57
- then newer `release_date`.
38
+ 2. When the session's provider (`preferredProviderID`) is in the connected
39
+ catalog: family-priority scan (`gemini-flash` → `gpt-nano` →
40
+ `claude-haiku`), then keyword tiers on that provider, then the session
41
+ model itself.
42
+ 3. Otherwise the other connected providers: family scan, then keyword+cost
43
+ default across providers.
44
+ 4. Nothing connected → `null` (routes answer 404).
45
+
46
+ **Keyword ranking:** tokenized id / known family tiers `flash` → `nano` →
47
+ `haiku` → `mini` → `lite` → `turbo` → `instant` → `small` → `chat`. Within a
48
+ tier: `cost.input` ascending (missing cost last), then newer `release_date`.
58
49
  - Input clamp: the prompt is truncated to the resolved model's catalog
59
50
  `limit.context` (minus an output reserve, ~4 chars/token estimate;
60
- conservative default when the model is not in the catalog). Truncation is
61
- reported as `inputTruncated: true` in the response.
62
- - `call.js` — dedicated wire formats and per-provider auth for the four
63
- adapters (replicating OpenCode plugin auth loaders):
64
- - **GitHub Copilot**: OpenAI-compatible `/chat/completions` on
65
- `https://api.githubcopilot.com` (or `copilot-api.<enterprise>`) with the
66
- stored device-OAuth token as the bearer — no token exchange, no expiry.
67
- - **OpenAI OAuth (ChatGPT plan)**: streaming Responses API on
68
- `https://chatgpt.com/backend-api/codex/responses` with
69
- `ChatGPT-Account-Id`; expired tokens are refreshed against
70
- `auth.openai.com` (single-flight) and written back to `auth.json`.
71
- - **Anthropic** (`type: api`): `/v1/messages` with `x-api-key`.
72
- - **Google** (`type: api`): `generateContent` with `x-goog-api-key`.
73
- - Custom Summary AI mode: OpenAI-compatible `/chat/completions` against the
74
- user-supplied base URL + token.
75
- Generic `api.url` chat-completions for arbitrary providers is **not** used
76
- for dispatch anymore (plugin providers need runtime rewrite); those providers
77
- take the OpenCode session path instead. The helper remains in `call.js` for
78
- the dedicated adapters and custom mode.
51
+ conservative default when the model has no limit or for the custom API).
52
+ Truncation is reported as `inputTruncated: true` in the response.
79
53
  - `catalog.js` — directory-scoped provider catalog via official
80
- `@opencode-ai/sdk/v2` `client.config.providers()`. Base URL/auth come from
81
- the composition root (`buildOpenCodeUrl` / `getOpenCodeAuthHeaders`).
82
- Per-directory short TTL (~30s) with single-flight. OpenCode failure returns
83
- an **explicit** minimal fallback catalog (OpenAI OAuth / GitHub Copilot /
84
- Google API / Anthropic API candidates only) — never an authoritative empty
85
- map. Raw small-model fields kept: `family`, `release_date`, `limit`,
54
+ `@opencode/client` `provider.list` + `model.list`. Model ids are `ModelInfo.id`
55
+ (external generate/session id). `ModelInfo.modelID` is internal and is not
56
+ preferred. v2 `cost` arrays keep the base tier's `input` / `output`. Base
57
+ URL/auth come from the composition root (`buildOpenCodeUrl` /
58
+ `getOpenCodeAuthHeaders`); the client's custom `fetch` must forward the
59
+ `init` argument (headers, method, body) or OpenCode answers 401.
60
+ Per-directory short TTL (~30s) with single-flight. OpenCode failure rejects
61
+ with `statusCode: 502` and is not cached — never a substitute or empty
62
+ catalog. Raw small-model fields kept: `family`, `release_date`, `limit`,
86
63
  `cost.input` / `cost.output`, `model.api.url`, provider `name`. Client-safe
87
64
  provider catalog projection remains in `opencode/provider-catalog.js` for
88
65
  `/api/config/catalog/providers`.
89
- - `routes.js` — `GET /api/small-model` (resolution preview),
66
+ - `routes.js` — `GET /api/small-model` (resolution preview; query
67
+ `directory`, `providerID`, `modelID`),
90
68
  `POST /api/small-model/generate` (`{ prompt, system?, maxOutputTokens?,
91
- model?, directory? }` → `{ text, providerID, modelID, source }`),
92
- `POST /api/small-model/test` (custom OpenAI-compatible probe → `{ ok, code? }`),
93
- and `POST /api/small-model/custom-models` (best-effort `{baseURL}/models`
94
- suggestions → `{ models }`). The preview response includes `callableModels`,
95
- the Provider/model allowlist consumed by Settings → Summary AI. Callable
96
- lists receive the same `directory` as describe/generate so they share the
97
- directory catalog.
98
- - `custom-api.js` — custom Summary AI probe and model listing. Failure codes
99
- are `incomplete`, `token` (401/403), `model` (404 / `model_not_found`), or
100
- `baseURL` (network / non-OpenAI-compatible). Tokens are never logged.
69
+ model?, directory?, preferredProviderID?, preferredModelID?,
70
+ restrictToPreferredProvider?, purpose? }` → `{ text, providerID, modelID,
71
+ source }`), `POST /api/small-model/test` (custom OpenAI-compatible probe →
72
+ `{ ok, code? }`), and `POST /api/small-model/custom-models` (best-effort
73
+ `{baseURL}/models` suggestions → `{ models }`).
74
+ - `custom-api.js` — custom Summary AI generation
75
+ (`generateCustomSummaryText`: OpenAI-compatible `/chat/completions`), probe,
76
+ and model listing. Probe failure codes are `incomplete`, `token` (401/403),
77
+ `model` (404 / `model_not_found`), or `baseURL` (network /
78
+ non-OpenAI-compatible). Reasoning-only or empty replies are errors, never
79
+ blank success. Tokens are never logged.
101
80
 
102
81
  ## Dispatch
103
82
 
104
- | Mode / provider | Path |
83
+ | Mode / purpose | Path |
105
84
  |---|---|
106
- | Summary AI `custom` | Direct OpenAI-compatible call (`call.js`) |
107
- | `openai` / `anthropic` / `google` / `github-copilot` with usable dedicated auth | Direct dedicated adapter (`call.js`) |
108
- | Any other usable auth + catalog model (plugin providers, …) | Temporary OpenCode session (`opencode-session.js`) |
109
-
110
- Callable Settings lists:
111
- - Dedicated adapters (`openai`, `anthropic`, `google`, `github-copilot`) stay
112
- gated by usable `auth.json` entries and the existing small-model whitelist
113
- (OpenAI OAuth → `gpt-5.4-mini`; Copilot → `gpt-5.4-nano`).
114
- - Every other provider is taken from the connected catalog (plugin / env
115
- credentials that OpenCode reports as connected), intersected with catalog
116
- model ids. No `api.url` gate. If the connected catalog request fails, the
117
- list falls back to usable `auth.json` plugin entries rather than pretending
118
- none exist.
119
- Copilot auth aliases merge to `github-copilot`.
120
-
121
- ## OpenCode session lifecycle
122
-
123
- 1. Lazy singleton temp directory under `os.tmpdir()` /
124
- `openchamber-smallmodel-*` with `.opencode/agent/openchamber-smallmodel.md`
125
- (`hidden: true`, `permissions: [{ action: "*", resource: "*", effect: deny }]`).
126
- 2. `session.create` with title `[small-model] <purpose>`, agent name, and
127
- `metadata.openchamber.smallModel = { purpose }` (sidebar invisibility also
128
- depends on a parallel filter lane for that marker).
129
- 3. Immediate `session.update({ time: { archived } })` (same pattern as
130
- assistants / scheduled tasks) so ordinary lists never flash the session.
131
- 4. `session.promptAsync` with `{ model: { providerID, modelID }, agent, system?,
132
- parts: [{ type: 'text', text, synthetic: false }] }`.
133
- 5. Poll `session.status` + `session.messages` until idle assistant completed
134
- (budget `SESSION_SETTLE_TIMEOUT_MS` = 60s). Network / timeout / empty text
135
- all throw with context — never an empty success.
136
- 6. `finally`: best-effort `session.delete`. Delete failure is logged and does
137
- not erase a successful text result.
138
- 7. Server `stop()` calls `smallModelServiceInstance.stop()` to `rm` the temp
139
- directory.
85
+ | Summary AI `custom` | User's OpenAI-compatible endpoint (`custom-api.js`) |
86
+ | Summary AI provider mode (`commit` / `session-title`) | Saved model, else OpenCode `model.default` → LLM gateway |
87
+ | Every other purpose | `resolve.js` over the connected catalog → LLM gateway |
88
+
89
+ The LLM gateway checks the model against the connected catalog (`no_provider`
90
+ otherwise) and runs `session.generate` in a throwaway session owned by the
91
+ hidden deny-all `openchamber-text` agent in the shared LLM temp directory
92
+ (see `llm/DOCUMENTATION.md`). One model call with the session's provider
93
+ context, no tool loop, no messages written; the session is removed afterwards.
94
+ `restrictToPreferredProvider` forbids resolution from switching away from the
95
+ session's provider unless the model came from an explicit choice (settings
96
+ override, OpenCode config, or request `model`).
97
+
98
+ Summary AI provider mode never uses small-model resolution. A saved model that
99
+ is not in the connected catalog fails with the gateway's `no_provider` error
100
+ instead of silently switching models.
140
101
 
141
102
  ### Skills / clean context boundary
142
103
 
@@ -146,53 +107,47 @@ by `Permission.evaluate("skill", skill.id, agent.permissions)` — a deny-all
146
107
  `action: "*"` rule therefore excludes every skill from the available list, so
147
108
  the guidance renders as "No skills are currently available." Skill **body**
148
109
  content is only injected when the skill tool runs; deny-all also blocks that
149
- tool. There is no separate agent/directory flag to disable skill guidance
150
- beyond permissions. Global `~/.agents/skills` may still be *discovered* by the
151
- OpenCode instance, but they are not listed or loadable under this agent.
152
- Project-level skills are absent because the temp directory has none.
110
+ tool. Global `~/.agents/skills` may still be *discovered* by the OpenCode
111
+ instance, but they are not listed or loadable under this agent. Project-level
112
+ skills are absent because the temp directory has none.
153
113
 
154
114
  ## Registration
155
115
 
156
116
  `server/index.js` creates one lazy `getSmallModelService` and injects
157
- `{ buildOpenCodeUrl, getOpenCodeAuthHeaders }` into the factory (and into
158
- session assist/title/goal, scheduled tasks, and `createFeatureRoutesRuntime`).
159
- Feature routes no longer import `small-model/index.js` top-level exports
160
- directly.
117
+ `{ buildOpenCodeUrl, getOpenCodeAuthHeaders, persistSessionMetadata,
118
+ onSystemSessionPersisted }` into the factory (and the service into session
119
+ assist/title/goal, scheduled tasks, and `createFeatureRoutesRuntime`). Server
120
+ `stop()` calls `stopLlmTempDirectory()` to remove the shared LLM temp root.
161
121
 
162
122
  ## Summary AI settings
163
123
 
164
124
  Commit-message generation and session-title refresh pass `purpose: 'commit'`
165
125
  or `purpose: 'session-title'` to `generateSmallModelText`. Settings → Summary
166
- AI can select an authenticated OpenCode provider/model or a custom
167
- OpenAI-compatible `baseURL`, model ID, and API token. Custom mode stores the
168
- model id in `summaryCustomModelID` (legacy `summaryModelID` is still read when
169
- that field is absent). Custom mode is enabled only when base URL, model ID,
170
- and API token are all present. A custom token stays in the server settings
171
- file; settings read responses expose only `hasSummaryCustomAPIToken`. Settings
172
- can probe the custom API (`POST /api/small-model/test`) and load model
173
- suggestions from `{baseURL}/models` without blocking free-form entry.
126
+ AI selects an OpenCode provider/model with the shared `ModelSelector` and the
127
+ same connected provider list as the chat model picker and Settings → Defaults
128
+ (`useConfigStore` providers; no allowlist), or a custom OpenAI-compatible
129
+ `baseURL`, model ID, and API token. Custom mode stores the model id in
130
+ `summaryCustomModelID` (legacy `summaryModelID` is still read when that field
131
+ is absent). Custom mode is enabled only when base URL, model ID, and API token
132
+ are all present. A custom token stays in the server settings file; settings
133
+ read responses expose only `hasSummaryCustomAPIToken`. Settings can probe the
134
+ custom API (`POST /api/small-model/test`) and load model suggestions from
135
+ `{baseURL}/models` without blocking free-form entry.
174
136
 
175
137
  `summaryCommitPrompt` and `summarySessionTitlePrompt` replace the respective
176
- call's system prompt when non-empty. With no persisted provider choice, summary
177
- calls use the same effective default shown by Settings: authenticated OpenAI
178
- when available, otherwise the first callable provider/model. This effective
179
- default remains authoritative when the active session uses another provider.
138
+ call's system prompt when non-empty. With no persisted provider choice (the
139
+ picker shows "OpenCode default model"), summary calls use OpenCode's
140
+ `model.default`. No default model is an explicit 404, not a fallback guess.
141
+ The chosen or default model stays authoritative when the active session uses
142
+ another provider.
180
143
 
181
144
  ## Known limitations
182
145
 
183
- - OpenCode's free models (`opencode/big-pickle`, `*-free`) work without a
184
- token only through OpenCode's own server — direct calls are rejected, and
185
- piggybacking on their subsidized infra is out of bounds by design. Every
186
- resolution step therefore requires a usable auth entry for the provider:
187
- a session on an unauthenticated `opencode` provider falls through to the
188
- global scan (or a clean 404 on a vanilla setup with no logins).
189
-
190
- - Anthropic OAuth (Claude Pro/Max) entries are not supported on the dedicated
191
- adapter — OpenCode itself keeps those outside `auth.json` in this generation;
192
- only `type: api` keys work for Anthropic direct calls. Plugin / other auth
193
- shapes that OpenCode can already run may still succeed via the session path.
194
- - Responses from the codex backend are collected from the SSE stream; the
195
- endpoint itself is non-streaming by design (small utility calls).
196
- - The server never requests models.dev. When OpenCode is down, only the four
197
- minimal hardcoded candidates remain available for resolution; session-path
198
- generation still requires a live OpenCode runtime.
146
+ - Provider behavior is OpenCode's: whatever fails in a normal OpenCode session
147
+ fails here with the same upstream error (for example OpenCode's free
148
+ `opencode` tier rejecting non-session utility use, a plugin provider
149
+ returning an unexpected status, or an invalid provider key).
150
+ - The first call after server start (or after the temp root is recreated)
151
+ waits for OpenCode to load the fresh location's config; agent verification
152
+ polls up to 5s, so cold calls take noticeably longer than warm ones.
153
+ - `session.generate` returns a full reply; utility calls have no streaming.