@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,157 @@
1
+ export const OPENCODE_V1_MIGRATION_PATH = '/api/experimental/migration/v1';
2
+
3
+ // Backfill notes must stay readable by UI and docs. Do not drop them when the
4
+ // gate blocks or later admits a migrated library.
5
+ export const V1_MIGRATION_USER_NOTICE = [
6
+ 'V1 history is backfilled in the opencode2 process.',
7
+ 'Message ids are reused.',
8
+ 'In-progress tools become interrupted.',
9
+ 'V1 subtasks do not appear in v2.',
10
+ ].join(' ');
11
+
12
+ const MIGRATION_PHASES = new Set(['required', 'running', 'completed', 'error']);
13
+
14
+ const stringifyError = (error) => {
15
+ if (error instanceof Error && error.message) {
16
+ return error.message;
17
+ }
18
+ if (typeof error === 'string' && error.trim()) {
19
+ return error;
20
+ }
21
+ return 'V1 migration status request failed';
22
+ };
23
+
24
+ const blocked = (phase, extra = {}) => ({
25
+ admitTranscript: false,
26
+ phase,
27
+ userNotice: V1_MIGRATION_USER_NOTICE,
28
+ ...extra,
29
+ });
30
+
31
+ const admitted = (phase, extra = {}) => ({
32
+ admitTranscript: true,
33
+ phase,
34
+ ...extra,
35
+ });
36
+
37
+ const normalizeProgress = (progress) => {
38
+ if (!progress || typeof progress !== 'object' || Array.isArray(progress)) {
39
+ return undefined;
40
+ }
41
+ const result = {
42
+ label: typeof progress.label === 'string' ? progress.label : '',
43
+ };
44
+ if (Number.isFinite(progress.numerator) && progress.numerator >= 0) {
45
+ result.numerator = progress.numerator;
46
+ }
47
+ if (Number.isFinite(progress.denominator) && progress.denominator >= 0) {
48
+ result.denominator = progress.denominator;
49
+ }
50
+ return result;
51
+ };
52
+
53
+ const resolveHttpStatus = (input) => {
54
+ if (typeof input.httpStatus === 'number') {
55
+ return input.httpStatus;
56
+ }
57
+ if (typeof input.status === 'number') {
58
+ return input.status;
59
+ }
60
+ return undefined;
61
+ };
62
+
63
+ const resolveBody = (input) => {
64
+ if (input.body !== undefined) {
65
+ return input.body;
66
+ }
67
+ if (typeof input.status === 'string') {
68
+ return input;
69
+ }
70
+ return null;
71
+ };
72
+
73
+ const resolveTransportError = (input) => {
74
+ if (input instanceof Error || typeof input === 'string') {
75
+ return input;
76
+ }
77
+ if (input?.error instanceof Error || typeof input?.error === 'string') {
78
+ if (input.body === undefined && typeof input.status !== 'string') {
79
+ return input.error;
80
+ }
81
+ }
82
+ return null;
83
+ };
84
+
85
+ // Pure admission decision for GET /api/experimental/migration/v1.
86
+ // required/running/error never become an empty-list success.
87
+ export const evaluateV1MigrationGate = (input = {}) => {
88
+ if (input == null) {
89
+ return blocked('error', { error: 'V1 migration status is missing or invalid' });
90
+ }
91
+
92
+ const transportError = resolveTransportError(input);
93
+ if (transportError) {
94
+ return blocked('error', { error: stringifyError(transportError) });
95
+ }
96
+
97
+ const httpStatus = resolveHttpStatus(input);
98
+ if (httpStatus === 404) {
99
+ return admitted('absent');
100
+ }
101
+ if (Number.isInteger(httpStatus) && httpStatus >= 400) {
102
+ return blocked('error', { error: `V1 migration status HTTP ${httpStatus}` });
103
+ }
104
+
105
+ const body = resolveBody(input);
106
+ const phase = body && typeof body === 'object' ? body.status : undefined;
107
+
108
+ if (phase === 'required') {
109
+ return blocked('required');
110
+ }
111
+ if (phase === 'running') {
112
+ return blocked('running', { progress: normalizeProgress(body.progress) });
113
+ }
114
+ if (phase === 'completed') {
115
+ return admitted('completed', { userNotice: V1_MIGRATION_USER_NOTICE });
116
+ }
117
+ if (phase === 'error') {
118
+ return blocked('error', {
119
+ error: typeof body.error === 'string' && body.error.trim()
120
+ ? body.error
121
+ : 'V1 migration failed',
122
+ });
123
+ }
124
+
125
+ if (MIGRATION_PHASES.has(phase) === false) {
126
+ return blocked('error', { error: 'V1 migration status is missing or invalid' });
127
+ }
128
+
129
+ return blocked('error', { error: 'V1 migration status is missing or invalid' });
130
+ };
131
+
132
+ // Official protocol only publishes GET status. Backfill is owned by opencode2;
133
+ // clients poll this helper and do not POST unless a later API requires it.
134
+ export const fetchV1MigrationGate = async ({
135
+ url,
136
+ headers = {},
137
+ signal,
138
+ fetchImpl = globalThis.fetch,
139
+ } = {}) => {
140
+ try {
141
+ const response = await fetchImpl(url, {
142
+ method: 'GET',
143
+ headers: {
144
+ Accept: 'application/json',
145
+ ...headers,
146
+ },
147
+ signal,
148
+ });
149
+ const body = await response.json().catch(() => null);
150
+ return evaluateV1MigrationGate({
151
+ httpStatus: response.status,
152
+ body,
153
+ });
154
+ } catch (error) {
155
+ return evaluateV1MigrationGate({ error });
156
+ }
157
+ };
@@ -0,0 +1,111 @@
1
+ import { describe, expect, it, vi } from 'vitest';
2
+
3
+ import {
4
+ OPENCODE_V1_MIGRATION_PATH,
5
+ V1_MIGRATION_USER_NOTICE,
6
+ evaluateV1MigrationGate,
7
+ fetchV1MigrationGate,
8
+ } from './v1-migration-gate.js';
9
+
10
+ describe('V1 migration gate', () => {
11
+ it('denies transcript while V1 migration is required', () => {
12
+ expect(evaluateV1MigrationGate({ status: 'required' })).toMatchObject({
13
+ admitTranscript: false,
14
+ phase: 'required',
15
+ userNotice: V1_MIGRATION_USER_NOTICE,
16
+ });
17
+ });
18
+
19
+ it('denies transcript while V1 migration is running and keeps progress', () => {
20
+ expect(evaluateV1MigrationGate({
21
+ status: 'running',
22
+ progress: { label: 'Sessions', numerator: 3, denominator: 10 },
23
+ })).toEqual({
24
+ admitTranscript: false,
25
+ phase: 'running',
26
+ progress: { label: 'Sessions', numerator: 3, denominator: 10 },
27
+ userNotice: V1_MIGRATION_USER_NOTICE,
28
+ });
29
+ });
30
+
31
+ it('admits transcript when V1 migration is completed', () => {
32
+ expect(evaluateV1MigrationGate({ status: 'completed' })).toMatchObject({
33
+ admitTranscript: true,
34
+ phase: 'completed',
35
+ });
36
+ });
37
+
38
+ it('admits transcript when V1 library is absent (HTTP 404)', () => {
39
+ expect(evaluateV1MigrationGate({ httpStatus: 404 })).toEqual({
40
+ admitTranscript: true,
41
+ phase: 'absent',
42
+ });
43
+ expect(evaluateV1MigrationGate({ status: 404 })).toEqual({
44
+ admitTranscript: true,
45
+ phase: 'absent',
46
+ });
47
+ });
48
+
49
+ it('admits transcript when completed with no remaining work', () => {
50
+ expect(evaluateV1MigrationGate({
51
+ body: { status: 'completed' },
52
+ httpStatus: 200,
53
+ })).toMatchObject({
54
+ admitTranscript: true,
55
+ phase: 'completed',
56
+ });
57
+ });
58
+
59
+ it('denies transcript on V1 migration error instead of treating an empty list as success', () => {
60
+ expect(evaluateV1MigrationGate({
61
+ status: 'error',
62
+ error: 'disk full',
63
+ })).toEqual({
64
+ admitTranscript: false,
65
+ phase: 'error',
66
+ error: 'disk full',
67
+ userNotice: V1_MIGRATION_USER_NOTICE,
68
+ });
69
+ expect(evaluateV1MigrationGate({})).toMatchObject({
70
+ admitTranscript: false,
71
+ phase: 'error',
72
+ });
73
+ });
74
+
75
+ it('denies transcript on network failure instead of treating an empty list as success', () => {
76
+ expect(evaluateV1MigrationGate({ error: new Error('offline') })).toMatchObject({
77
+ admitTranscript: false,
78
+ phase: 'error',
79
+ error: 'offline',
80
+ });
81
+ });
82
+
83
+ it('exposes backfill userNotice for UI and docs', () => {
84
+ expect(V1_MIGRATION_USER_NOTICE).toContain('Message ids are reused');
85
+ expect(V1_MIGRATION_USER_NOTICE).toContain('interrupted');
86
+ expect(V1_MIGRATION_USER_NOTICE).toContain('V1 subtasks do not appear in v2');
87
+ expect(evaluateV1MigrationGate({ status: 'required' }).userNotice).toBe(V1_MIGRATION_USER_NOTICE);
88
+ expect(evaluateV1MigrationGate({ status: 'completed' }).userNotice).toBe(V1_MIGRATION_USER_NOTICE);
89
+ });
90
+
91
+ it('polls GET migration status and does not POST', async () => {
92
+ const fetchImpl = vi.fn(async () => new Response(JSON.stringify({ status: 'completed' }), {
93
+ status: 200,
94
+ headers: { 'content-type': 'application/json' },
95
+ }));
96
+
97
+ await expect(fetchV1MigrationGate({
98
+ url: `http://127.0.0.1:4096${OPENCODE_V1_MIGRATION_PATH}`,
99
+ headers: { Authorization: 'Basic test' },
100
+ fetchImpl,
101
+ })).resolves.toMatchObject({
102
+ admitTranscript: true,
103
+ phase: 'completed',
104
+ });
105
+
106
+ expect(fetchImpl).toHaveBeenCalledTimes(1);
107
+ expect(fetchImpl.mock.calls[0][0]).toContain(OPENCODE_V1_MIGRATION_PATH);
108
+ expect(fetchImpl.mock.calls[0][1].method).toBe('GET');
109
+ expect(fetchImpl.mock.calls[0][1].headers.Authorization).toBe('Basic test');
110
+ });
111
+ });
@@ -0,0 +1,60 @@
1
+ import { OpenCode } from '@opencode/client';
2
+ import { wrapFetchWithRuntimeContractGate } from './server-opencode-fetch.js';
3
+
4
+ /**
5
+ * @typedef {import('@opencode/client').OpenCodeClient} OpenCodeV2ClientRaw
6
+ */
7
+
8
+ /**
9
+ * Compatibility client: official 2.x nests exact message lookup under
10
+ * `session.message.get`, while OpenChamber callers still use
11
+ * `session.message(input, requestOptions)`.
12
+ *
13
+ * @typedef {Omit<OpenCodeV2ClientRaw, 'session'> & {
14
+ * session: Omit<OpenCodeV2ClientRaw['session'], 'message'> & {
15
+ * message: ((input: Parameters<OpenCodeV2ClientRaw['session']['message']['get']>[0], requestOptions?: Parameters<OpenCodeV2ClientRaw['session']['message']['get']>[1]) => ReturnType<OpenCodeV2ClientRaw['session']['message']['get']>)
16
+ * & OpenCodeV2ClientRaw['session']['message']
17
+ * }
18
+ * }} OpenCodeV2Client
19
+ */
20
+
21
+ /**
22
+ * Create the single server-side client for official OpenCode v2 APIs.
23
+ * Fetch is always wrapped with runtime-contract write admission so Host
24
+ * background clients (queue / scheduled / assistants) cannot bypass the proxy gate.
25
+ *
26
+ * @param {{
27
+ * baseUrl: string,
28
+ * authHeaders?: Record<string, string>,
29
+ * fetchImpl?: typeof fetch,
30
+ * getRuntimeContract?: () => (object | null | undefined),
31
+ * }} input
32
+ * @returns {OpenCodeV2Client}
33
+ */
34
+ export function makeOpenCodeV2Client({ baseUrl, authHeaders, fetchImpl, getRuntimeContract }) {
35
+ const gatedFetch = wrapFetchWithRuntimeContractGate(fetchImpl || fetch, {
36
+ ...(typeof getRuntimeContract === 'function' ? { getRuntimeContract } : {}),
37
+ });
38
+ const client = OpenCode.make({
39
+ baseUrl: baseUrl.replace(/\/$/, ''),
40
+ ...(authHeaders ? { headers: authHeaders } : {}),
41
+ fetch: gatedFetch,
42
+ });
43
+
44
+ const messageGet = client.session.message?.get?.bind(client.session.message);
45
+ if (typeof messageGet === 'function') {
46
+ const messageCompat = Object.assign(
47
+ (input, requestOptions) => messageGet(input, requestOptions),
48
+ client.session.message,
49
+ );
50
+ return {
51
+ ...client,
52
+ session: {
53
+ ...client.session,
54
+ message: messageCompat,
55
+ },
56
+ };
57
+ }
58
+
59
+ return client;
60
+ }
@@ -0,0 +1,155 @@
1
+ import { readdirSync, readFileSync, statSync } from "node:fs"
2
+ import { homedir } from "node:os"
3
+ import path from "node:path"
4
+ import { pathToFileURL } from "node:url"
5
+
6
+ const stripJsonc = (text) => text
7
+ .replace(/\/\*[\s\S]*?\*\//g, "")
8
+ .replace(/(^|[^:\\])\/\/.*$/gm, "$1")
9
+
10
+ const readConfigPlugins = () => {
11
+ const specs = []
12
+ const files = []
13
+ if (process.env.OPENCODE_CONFIG) files.push(process.env.OPENCODE_CONFIG)
14
+ const dirs = [process.env.OPENCODE_CONFIG_DIR, path.join(homedir(), ".config", "opencode")].filter(Boolean)
15
+ for (const dir of dirs) {
16
+ files.push(path.join(dir, "opencode.jsonc"), path.join(dir, "opencode.json"), path.join(dir, "config.json"))
17
+ }
18
+ for (const file of files) {
19
+ try {
20
+ const parsed = JSON.parse(stripJsonc(readFileSync(file, "utf8")))
21
+ const plugin = parsed?.plugin
22
+ const list = Array.isArray(plugin) ? plugin : plugin ? [plugin] : []
23
+ for (const entry of list) {
24
+ if (typeof entry === "string") specs.push(entry)
25
+ else if (Array.isArray(entry) && typeof entry[0] === "string") specs.push(entry[0])
26
+ }
27
+ } catch {
28
+ // Missing or invalid config files are skipped; OpenCode still loads the ones it can parse.
29
+ }
30
+ }
31
+ return [...new Set(specs)]
32
+ }
33
+
34
+ const packageName = (spec) => {
35
+ const slash = spec.startsWith("@") ? spec.indexOf("/", 1) : -1
36
+ const versionAt = spec.lastIndexOf("@")
37
+ if (versionAt > 0 && versionAt > slash) return spec.slice(0, versionAt)
38
+ return spec
39
+ }
40
+
41
+ const resolvePackageEntry = (spec) => {
42
+ const name = packageName(spec)
43
+ const roots = [
44
+ path.join(homedir(), ".cache", "opencode", "npm"),
45
+ path.join(homedir(), ".cache", "opencode", "packages"),
46
+ ]
47
+ const candidates = []
48
+ for (const root of roots) {
49
+ let entries = []
50
+ try {
51
+ entries = readdirSync(root)
52
+ } catch {
53
+ continue
54
+ }
55
+ for (const entry of entries) {
56
+ if (entry !== name && !entry.startsWith(`${name}@`)) continue
57
+ const base = path.join(root, entry)
58
+ const packageDirs = [path.join(base, "node_modules", name)]
59
+ try {
60
+ for (const child of readdirSync(base)) {
61
+ if (/^\d+$/.test(child)) packageDirs.push(path.join(base, child, "node_modules", name))
62
+ }
63
+ } catch {
64
+ // The cache entry may be the package directory itself.
65
+ }
66
+ for (const pkgDir of packageDirs) {
67
+ try {
68
+ const pkg = JSON.parse(readFileSync(path.join(pkgDir, "package.json"), "utf8"))
69
+ if (pkg.name !== name) continue
70
+ const exported = pkg.exports?.["."]
71
+ const rel = typeof exported === "string" ? exported : pkg.module || pkg.main || "index.mjs"
72
+ candidates.push({
73
+ dir: pkgDir,
74
+ entry: path.resolve(pkgDir, rel),
75
+ mtime: statSync(path.join(pkgDir, "package.json")).mtimeMs,
76
+ })
77
+ } catch {
78
+ // Not a readable package directory.
79
+ }
80
+ }
81
+ }
82
+ }
83
+ candidates.sort((left, right) => right.mtime - left.mtime)
84
+ return candidates[0] || null
85
+ }
86
+
87
+ const usesCatalog = (dir) => {
88
+ const stack = [dir]
89
+ let seen = 0
90
+ while (stack.length > 0 && seen < 80) {
91
+ const current = stack.pop()
92
+ let names = []
93
+ try {
94
+ names = readdirSync(current)
95
+ } catch {
96
+ continue
97
+ }
98
+ for (const name of names) {
99
+ if (name === "node_modules" || name === ".git") continue
100
+ const full = path.join(current, name)
101
+ let info
102
+ try {
103
+ info = statSync(full)
104
+ } catch {
105
+ continue
106
+ }
107
+ if (info.isDirectory()) {
108
+ stack.push(full)
109
+ continue
110
+ }
111
+ if (!/\.(?:mjs|cjs|js)$/.test(name) || info.size > 2_000_000) continue
112
+ seen += 1
113
+ try {
114
+ if (readFileSync(full, "utf8").includes(".catalog")) return true
115
+ } catch {
116
+ // Unreadable files are not catalog callers.
117
+ }
118
+ }
119
+ }
120
+ return false
121
+ }
122
+
123
+ const catalogFor = (ctx) => ({
124
+ // OpenCode 2.0.12 removed ctx.catalog. Model plugins still call
125
+ // draft.provider.update / draft.model.update inside catalog.transform.
126
+ transform: (fn) => ctx.provider?.transform?.((draft) => fn({
127
+ provider: {
128
+ update: (id, updater) => draft.update(id, updater),
129
+ add: (...args) => draft.add?.(...args),
130
+ },
131
+ model: {
132
+ update: (providerID, modelID, updater) => draft.models?.update?.(providerID, modelID, updater),
133
+ set: (providerID, modelID, value) => draft.models?.set?.(providerID, modelID, value),
134
+ },
135
+ })),
136
+ reload: () => ctx.provider?.reload?.() ?? ctx.model?.reload?.(),
137
+ })
138
+
139
+ export default {
140
+ id: "openchamber-v2-plugin-host-shim",
141
+ setup: async (ctx) => {
142
+ if (typeof ctx?.catalog?.transform === "function") return
143
+ const patched = { ...ctx, catalog: catalogFor(ctx) }
144
+ for (const spec of readConfigPlugins()) {
145
+ const resolved = resolvePackageEntry(spec)
146
+ if (!resolved || !usesCatalog(resolved.dir)) continue
147
+ try {
148
+ const mod = await import(pathToFileURL(resolved.entry).href)
149
+ if (typeof mod.default?.setup === "function") await mod.default.setup(patched)
150
+ } catch (error) {
151
+ console.error(`[openchamber] v2 plugin host shim skipped ${spec}: ${error instanceof Error ? error.message : String(error)}`)
152
+ }
153
+ }
154
+ },
155
+ }
@@ -0,0 +1,4 @@
1
+ {
2
+ "type": "module",
3
+ "name": "openchamber-v2-plugin-host-shim"
4
+ }
@@ -65,7 +65,8 @@ export const createOpenCodeWatcherRuntime = (deps) => {
65
65
 
66
66
  reader = createUpstreamSseReader({
67
67
  signal,
68
- buildUrl: () => buildOpenCodeUrl('/global/event', ''),
68
+ // v2 serves the event stream at `/api/event`; `/api/global/event` is gone.
69
+ buildUrl: () => buildOpenCodeUrl('/event', ''),
69
70
  getHeaders: getOpenCodeAuthHeaders,
70
71
  fetchImpl,
71
72
  stallTimeoutMs: upstreamStallTimeoutMs,
@@ -74,7 +74,7 @@ describe('createOpenCodeWatcherRuntime', () => {
74
74
 
75
75
  expect(fetchCalls).toEqual([
76
76
  {
77
- url: 'http://127.0.0.1:4096/global/event',
77
+ url: 'http://127.0.0.1:4096/event',
78
78
  headers: {
79
79
  Accept: 'text/event-stream',
80
80
  'Cache-Control': 'no-cache',
@@ -8,13 +8,13 @@ This module owns the authoritative permission auto-accept policy for web, deskto
8
8
 
9
9
  `permissionAutoAccept.sessions` contains explicit per-session boolean policies.
10
10
 
11
- Policy inheritance uses the nearest explicit session value. A child `false` therefore overrides a parent `true`; descendants without an explicit value inherit from their nearest configured ancestor.
11
+ Policy inheritance uses the nearest explicit session value. A child `false` therefore overrides a parent `true`. Sessions with no explicit value, including a missing ancestor lookup, default to allow. A failed settings load still fails closed and does not approve.
12
12
 
13
13
  ## Runtime
14
14
 
15
- `createPermissionAutoAcceptRuntime` loads and serializes policy writes, subscribes to the global OpenCode event hub, caches session lineage, retries transient replies, and reconciles pending permissions after startup, reconnect, and policy enablement. Enabling Auto-Accept for a session immediately accepts matching pending requests and keeps handling future requests without requiring a connected UI.
15
+ `createPermissionAutoAcceptRuntime` loads and serializes policy writes, subscribes to the global OpenCode event hub, caches session lineage, retries transient replies, and reconciles pending permissions after startup, reconnect, and policy enablement. Pending requests are listed at `GET /permission/request` and approved with `POST /session/:sessionID/permission/:requestID/reply` body `{ decision: "always" }`. OpenCode 2.0.12 rejects the legacy `{ reply }` body and the unscoped `/permission/:id/reply` path.
16
16
 
17
- Unknown lineage and failed policy loads fail closed. A failed pending-permission fetch is distinct from an empty successful response and never clears policy state.
17
+ A failed settings load fails closed and does not approve. A failed pending-permission fetch is distinct from an empty successful response and never clears policy state.
18
18
 
19
19
  ## Routes
20
20
 
@@ -27,9 +27,9 @@ These are normal authenticated OpenChamber runtime routes. They must not be adde
27
27
 
28
28
  `packages/ui/src/stores/permissionStore.ts` is a projection of server policy and does not persist an independent policy. The server is the sole responder and the UI renders pending requests until the authoritative `permission.replied` event arrives.
29
29
 
30
- `ChatInput` derives control visibility from the persisted, directory-scoped `useConfigStore` agent snapshot. A selected agent with a final global `allow` or `deny` rule has no remaining prompt path and hides the control. Unknown snapshots, missing selected agents, and rules that can ask keep the control visible. This path issues no additional request.
30
+ `ChatInput` derives control visibility from the persisted, directory-scoped `useConfigStore` agent snapshot via `shouldShowPermissionAutoAcceptControl`. It reads the V2 wire `permissions` field when present (OpenCode `AgentInfo.permissions`), otherwise the legacy singular `permission` document. Rule normalization reuses `toPermissionRuleset` so both V2 `{ action, resource, effect }` and V1 `{ permission, pattern, action }` / tool-map shapes are recognized. A selected agent with a final global `allow` or `deny` rule has no remaining prompt path and hides the control. Unknown snapshots, missing selected agents, unparseable rules, and rules that can still ask keep the control visible. This path issues no additional request and does not change server auto-accept policy.
31
31
 
32
- VS Code retains its foreground-only implementation because it does not run the web server runtime.
32
+ VS Code retains its foreground-only implementation because it does not run the web server runtime (`isVSCodeRuntime()` always shows the control).
33
33
 
34
34
  ## Tests
35
35
 
@@ -123,6 +123,7 @@ export function createPermissionAutoAcceptRuntime({
123
123
 
124
124
  const isSessionAutoAccepting = async (sessionId, directory) => {
125
125
  await load();
126
+ if (!sessionId) return false;
126
127
  const seen = new Set();
127
128
  let current = sessionId;
128
129
  let currentDirectory = directory;
@@ -133,22 +134,23 @@ export function createPermissionAutoAcceptRuntime({
133
134
  try {
134
135
  info = await getSession(current, currentDirectory);
135
136
  } catch {
136
- return false;
137
+ // Lineage lookup failed. No explicit deny was found, so keep the default allow.
138
+ return true;
137
139
  }
138
140
  current = info?.parentID ?? null;
139
141
  currentDirectory = info?.directory ?? currentDirectory;
140
142
  }
141
- return false;
143
+ return true;
142
144
  };
143
145
 
144
146
  const replyOnce = async (permission, directory) => {
145
147
  if (!permission?.id || !permission?.sessionID) return false;
146
148
  await load();
147
149
  if (!(await isSessionAutoAccepting(permission.sessionID, directory))) return false;
148
- await request(`/permission/${encodeURIComponent(permission.id)}/reply`, {
150
+ await request(`/session/${encodeURIComponent(permission.sessionID)}/permission/${encodeURIComponent(permission.id)}/reply`, {
149
151
  directory,
150
152
  method: 'POST',
151
- body: { reply: 'once' },
153
+ body: { decision: 'always' },
152
154
  });
153
155
  return true;
154
156
  };
@@ -187,7 +189,7 @@ export function createPermissionAutoAcceptRuntime({
187
189
  for (const directory of scopes) {
188
190
  let payload;
189
191
  try {
190
- payload = await request('/permission', { directory });
192
+ payload = await request('/permission/request', { directory });
191
193
  } catch {
192
194
  continue;
193
195
  }
@@ -209,12 +211,17 @@ export function createPermissionAutoAcceptRuntime({
209
211
  const raw = event?.payload;
210
212
  const payload = raw?.payload && typeof raw.payload === 'object' ? raw.payload : raw;
211
213
  const directory = typeof event?.directory === 'string' && event.directory !== 'global' ? event.directory : undefined;
214
+ const body = payload?.properties && typeof payload.properties === 'object'
215
+ ? payload.properties
216
+ : payload?.data && typeof payload.data === 'object'
217
+ ? payload.data
218
+ : payload;
212
219
  if (payload?.type === 'session.created' || payload?.type === 'session.updated') {
213
- rememberSession(payload.properties?.info, directory);
220
+ rememberSession(body?.info ?? payload.properties?.info, directory);
214
221
  return;
215
222
  }
216
223
  if (payload?.type === 'permission.asked') {
217
- void processPermission(payload.properties, directory);
224
+ void processPermission(body, directory);
218
225
  }
219
226
  };
220
227