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

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 (328) 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-CzTtVMnI.js} +1 -1
  7. package/dist/assets/AssistantView-Cg1qQvVe.js +3 -0
  8. package/dist/assets/{DiagramView-D1LGK_s2.js → DiagramView-hOi72Wdx.js} +1 -1
  9. package/dist/assets/{FadeInOnReveal-B0kJo9Ii.js → FadeInOnReveal-DCqrQkkz.js} +1 -1
  10. package/dist/assets/Icon-B8fSTLdP.js +1 -0
  11. package/dist/assets/MarkdownRenderer-Cw74oVxV.js +2 -0
  12. package/dist/assets/{MarkdownRendererImpl-DfoCJ1Mq.js → MarkdownRendererImpl-YM7iRmzK.js} +3 -3
  13. package/dist/assets/MarkstreamRendererImpl-BDy_oGJQ.js +3 -0
  14. package/dist/assets/MobileDetailNavigation-iX_hb_Xt.js +1 -0
  15. package/dist/assets/{MobileShareBridge-2Yjn6wXg.js → MobileShareBridge-CAMUupwR.js} +2 -2
  16. package/dist/assets/MobileSurface-CHbdPcLH.js +2 -0
  17. package/dist/assets/{MultiRunWindow-tTbMFowS.js → MultiRunWindow-Ci1ODFFX.js} +1 -1
  18. package/dist/assets/{OnboardingScreen-CD2KtlF3.js → OnboardingScreen-O4cGdz5p.js} +1 -1
  19. package/dist/assets/OverlayScrollbar-CRfGtcmG.js +2 -0
  20. package/dist/assets/SettingsView-Dg8BtTV1.js +25 -0
  21. package/dist/assets/{SettingsWindow-zIa76s4t.js → SettingsWindow-B8d8qIQ7.js} +1 -1
  22. package/dist/assets/TerminalView-CMiw3s_c.js +8 -0
  23. package/dist/assets/{ToolOutputDialog-DNqA4uBl.js → ToolOutputDialog-ByaQYRTp.js} +2 -2
  24. package/dist/assets/appThemeRegistry-BGGHio8T.js +1 -0
  25. package/dist/assets/{assistant-attachment-cache-C0aRnFkj.js → assistant-attachment-cache-DphJleHr.js} +1 -1
  26. package/dist/assets/{button-CI7Q-Zw_.js → button-DIIrsMjW.js} +1 -1
  27. package/dist/assets/{collapsible-D5Phaykh.js → collapsible-CnvUeYst.js} +1 -1
  28. package/dist/assets/{context-menu-CubSDW2g.js → context-menu-BGBUpZEQ.js} +1 -1
  29. package/dist/assets/{desktopHosts-B1ixEylr.js → desktopHosts-JGFqhlaM.js} +1 -1
  30. package/dist/assets/{desktopRelayRestore-4JmFUw8r.js → desktopRelayRestore-CHEwrI93.js} +1 -1
  31. package/dist/assets/{diagram-DdLO8HZ9.js → diagram-rvn7_qBM.js} +1 -1
  32. package/dist/assets/{dialog-B6pnIYmp.js → dialog-CQ63sc9E.js} +1 -1
  33. package/dist/assets/es-Dsmc0A_3.js +15 -0
  34. package/dist/assets/fileMentionTouchSelection-BzzcWJCi.js +3 -0
  35. package/dist/assets/{fileQueries-DQc8EJUf.js → fileQueries-DN6gbLhM.js} +1 -1
  36. package/dist/assets/fr-C_ZlZDI2.js +13 -0
  37. package/dist/assets/gitApi-BF8vuoZc.js +10 -0
  38. package/dist/assets/{imageSource-BlAJJUEI.js → imageSource-BMztRu0c.js} +1 -1
  39. package/dist/assets/{input-D-__fwRi.js → input-BVu-iykl.js} +1 -1
  40. package/dist/assets/insertionBoundaries-EcAfGT2D.js +1 -0
  41. package/dist/assets/ja-BqqTaWTi.js +15 -0
  42. package/dist/assets/ko-DP2XPM37.js +15 -0
  43. package/dist/assets/main-B7t9lavz.js +2 -0
  44. package/dist/assets/main-D2BzpnUt.css +1 -0
  45. package/dist/assets/main-DWP-prNB.js +169 -0
  46. package/dist/assets/markdown-worker-bS5OgGO3.js +2 -0
  47. package/dist/assets/{markdownHeightCache-BhxqJOvf.js → markdownHeightCache-D9-B1wdY.js} +1 -1
  48. package/dist/assets/markdownTheme-DM5h1i18.js +1 -0
  49. package/dist/assets/mcpQueries-CfXrAkuU.js +1 -0
  50. package/dist/assets/miniChat-DgOrEQbN.js +2 -0
  51. package/dist/assets/mobile-Co_ZOHkZ.js +2 -0
  52. package/dist/assets/multirun-DelyUO6k.js +4 -0
  53. package/dist/assets/persistence-D0y7226-.js +3 -0
  54. package/dist/assets/pl-BCf5uLlK.js +15 -0
  55. package/dist/assets/platform-CurqO28D.js +6 -0
  56. package/dist/assets/{projectMeta-S1hJ7oBp.js → projectMeta-CPElzJ0n.js} +2 -2
  57. package/dist/assets/pt-BR-W09Q2pvn.js +15 -0
  58. package/dist/assets/{relay-image-stream-DM7atqJs.js → relay-image-stream-DrdVj99c.js} +1 -1
  59. package/dist/assets/renderElectronMiniChatApp-pOE93w86.js +2 -0
  60. package/dist/assets/renderMobileApp-F7ZSLcw8.js +28 -0
  61. package/dist/assets/runtime-fetch-Fs9ZV2V8.js +1 -0
  62. package/dist/assets/{runtimeConfig-CMkwoQ_X.js → runtimeConfig-DSS0KxG3.js} +1 -1
  63. package/dist/assets/runtimeEndpointReset-x2Wgx57R.js +149 -0
  64. package/dist/assets/sessionChangeSummary-BUGSYcHp.js +2 -0
  65. package/dist/assets/{sessionLookup-BrU3rFHJ.js → sessionLookup-Bss8M20i.js} +1 -1
  66. package/dist/assets/{sortable-tabs-strip-Fnfhf3K9.js → sortable-tabs-strip-CE7W7lJ9.js} +1 -1
  67. package/dist/assets/src-lS1keDVV.css +2 -0
  68. package/dist/assets/store-jaP79mqI.js +15 -0
  69. package/dist/assets/{terminalApi-xjqssKke.js → terminalApi-mDR2jXTo.js} +1 -1
  70. package/dist/assets/textarea-XD2oX_Ee.js +1 -0
  71. package/dist/assets/{tooltip-0T5jv0cu.js → tooltip-DYeQ_Hvy.js} +1 -1
  72. package/dist/assets/uk-BgFzz8m2.js +15 -0
  73. package/dist/assets/{url-_z7evzdK.js → url-CGbJbiJq.js} +1 -1
  74. package/dist/assets/{useAppFontEffects-C02YFyAv.js → useAppFontEffects-BAWHZ-t0.js} +64 -45
  75. package/dist/assets/{useAssistantUIStore-CMutVh9B.js → useAssistantUIStore-Bz4tRflF.js} +1 -1
  76. package/dist/assets/useDesktopWindowControlsLayout-hN_nLb7T.js +1 -0
  77. package/dist/assets/useEffectiveDirectory-Do9DQ453.js +1 -0
  78. package/dist/assets/{useFontPreferences-B1JYyzpK.js → useFontPreferences-Bjx8zkGM.js} +1 -1
  79. package/dist/assets/useMobileNavigationStore-BsY_gi_K.js +414 -0
  80. package/dist/assets/useSessionAutoCleanup-C4g5KmDf.js +2 -0
  81. package/dist/assets/useUIStore-syVmbuDA.js +1 -0
  82. package/dist/assets/{useWorkerHighlightedLines-C9z0ML1z.js → useWorkerHighlightedLines-B5QXxBok.js} +2 -2
  83. package/dist/assets/vendor-opencode-client-C12gfjag.js +12 -0
  84. package/dist/assets/{vendor-reactuses-core-BnW6GxJt.js → vendor-reactuses-core-Dq5ocTF8.js} +1 -1
  85. package/dist/assets/{vendor-yaml-DIcAK3EC.js → vendor-yaml-C9KlJAlj.js} +37 -37
  86. package/dist/assets/zh-CN-BhqSLgUY.js +15 -0
  87. package/dist/assets/zh-TW-BsaBgY-p.js +15 -0
  88. package/dist/index.html +12 -12
  89. package/dist/mini-chat.html +12 -12
  90. package/dist/mobile.html +12 -12
  91. package/package.json +3 -2
  92. package/server/index.js +423 -20
  93. package/server/lib/assistants/DOCUMENTATION.md +26 -21
  94. package/server/lib/assistants/assign.js +2 -1
  95. package/server/lib/assistants/contact-store.js +37 -19
  96. package/server/lib/assistants/contracts.js +11 -1
  97. package/server/lib/assistants/projection-history.test.js +454 -0
  98. package/server/lib/assistants/routes.js +11 -5
  99. package/server/lib/assistants/service.js +850 -294
  100. package/server/lib/assistants/service.test.js +588 -274
  101. package/server/lib/browser-provider/DOCUMENTATION.md +141 -0
  102. package/server/lib/browser-provider/contract.js +312 -0
  103. package/server/lib/browser-provider/host.js +421 -0
  104. package/server/lib/browser-provider/host.test.js +304 -0
  105. package/server/lib/browser-provider/routes.js +122 -0
  106. package/server/lib/browser-provider/routes.test.js +120 -0
  107. package/server/lib/browser-provider/selection-store.js +43 -0
  108. package/server/lib/browser-provider/selection-store.test.js +35 -0
  109. package/server/lib/conversations/DOCUMENTATION.md +5 -5
  110. package/server/lib/conversations/service.js +87 -57
  111. package/server/lib/conversations/service.test.js +132 -100
  112. package/server/lib/conversations/validation.js +11 -1
  113. package/server/lib/event-stream/DOCUMENTATION.md +3 -1
  114. package/server/lib/event-stream/directory-ws-bridge.js +13 -2
  115. package/server/lib/event-stream/global-hub.js +51 -5
  116. package/server/lib/event-stream/reasoning-projection.js +16 -1
  117. package/server/lib/event-stream/reasoning-projection.test.js +15 -0
  118. package/server/lib/event-stream/runtime.js +57 -20
  119. package/server/lib/event-stream/runtime.test.js +112 -0
  120. package/server/lib/llm/DOCUMENTATION.md +95 -84
  121. package/server/lib/llm/catalog.js +111 -45
  122. package/server/lib/llm/catalog.test.js +121 -59
  123. package/server/lib/llm/completions.js +41 -12
  124. package/server/lib/llm/completions.test.js +45 -4
  125. package/server/lib/llm/generate.js +516 -453
  126. package/server/lib/llm/generate.test.js +705 -484
  127. package/server/lib/llm/routes.js +8 -4
  128. package/server/lib/message-queue/DOCUMENTATION.md +5 -5
  129. package/server/lib/message-queue/opencode-adapter.js +191 -104
  130. package/server/lib/message-queue/opencode-adapter.test.js +328 -58
  131. package/server/lib/message-queue/runtime.js +15 -2
  132. package/server/lib/message-queue/runtime.test.js +6 -6
  133. package/server/lib/message-queue/worker.js +7 -1
  134. package/server/lib/notifications/DOCUMENTATION.md +3 -1
  135. package/server/lib/notifications/runtime.js +133 -31
  136. package/server/lib/notifications/runtime.test.js +103 -0
  137. package/server/lib/notifications/template-runtime.js +130 -38
  138. package/server/lib/notifications/template-runtime.test.js +96 -0
  139. package/server/lib/opencode/DOCUMENTATION.md +111 -16
  140. package/server/lib/opencode/agent-document.d.ts +28 -0
  141. package/server/lib/opencode/agent-document.js +280 -0
  142. package/server/lib/opencode/agent-document.test.js +137 -0
  143. package/server/lib/opencode/agents.js +96 -0
  144. package/server/lib/opencode/auth-state-runtime.js +5 -0
  145. package/server/lib/opencode/bootstrap-runtime.js +2 -0
  146. package/server/lib/opencode/config-entity-routes.js +117 -103
  147. package/server/lib/opencode/config-entity-routes.test.js +148 -10
  148. package/server/lib/opencode/config-hot-reload.integration.test.js +105 -0
  149. package/server/lib/opencode/ensure-cli.d.ts +8 -0
  150. package/server/lib/opencode/ensure-cli.js +238 -0
  151. package/server/lib/opencode/ensure-cli.test.js +119 -0
  152. package/server/lib/opencode/env-runtime.js +208 -111
  153. package/server/lib/opencode/env-runtime.test.js +211 -14
  154. package/server/lib/opencode/feature-routes-runtime.js +90 -5
  155. package/server/lib/opencode/feature-routes-runtime.test.js +25 -0
  156. package/server/lib/opencode/http-server-connections.js +98 -0
  157. package/server/lib/opencode/http-server-connections.test.js +321 -0
  158. package/server/lib/opencode/lifecycle.js +539 -95
  159. package/server/lib/opencode/lifecycle.test.js +698 -6
  160. package/server/lib/opencode/managed-capabilities-runtime.js +15 -2
  161. package/server/lib/opencode/managed-capabilities-runtime.test.js +1 -0
  162. package/server/lib/opencode/network-runtime.js +28 -16
  163. package/server/lib/opencode/network-runtime.test.js +68 -4
  164. package/server/lib/opencode/openchamber-routes.js +7 -1
  165. package/server/lib/opencode/opencode2-pin.js +139 -0
  166. package/server/lib/opencode/opencode2-pin.test.js +88 -0
  167. package/server/lib/opencode/opencode2-sidecar.e2e.test.js +326 -0
  168. package/server/lib/opencode/owned-runtime-upgrade.js +431 -0
  169. package/server/lib/opencode/owned-runtime-upgrade.test.js +160 -0
  170. package/server/lib/opencode/plugin-compatibility.js +15 -0
  171. package/server/lib/opencode/plugin-compatibility.test.js +21 -0
  172. package/server/lib/opencode/plugin-routes.js +3 -18
  173. package/server/lib/opencode/plugin-routes.test.js +9 -9
  174. package/server/lib/opencode/plugins.js +4 -3
  175. package/server/lib/opencode/plugins.test.js +12 -0
  176. package/server/lib/opencode/provider-catalog.js +58 -12
  177. package/server/lib/opencode/provider-catalog.test.js +48 -0
  178. package/server/lib/opencode/proxy.js +373 -28
  179. package/server/lib/opencode/proxy.test.js +353 -0
  180. package/server/lib/opencode/routes.js +333 -76
  181. package/server/lib/opencode/routes.test.js +315 -50
  182. package/server/lib/opencode/runtime-contract.js +401 -0
  183. package/server/lib/opencode/runtime-contract.test.js +214 -0
  184. package/server/lib/opencode/server-opencode-fetch.js +242 -0
  185. package/server/lib/opencode/server-opencode-fetch.test.js +130 -0
  186. package/server/lib/opencode/server-utils-runtime.js +35 -0
  187. package/server/lib/opencode/shared-service.integration.test.js +66 -0
  188. package/server/lib/opencode/shared-service.js +147 -0
  189. package/server/lib/opencode/shared-service.test.js +93 -0
  190. package/server/lib/opencode/shared.js +3 -3
  191. package/server/lib/opencode/shutdown-runtime.js +15 -6
  192. package/server/lib/opencode/shutdown-runtime.test.js +49 -1
  193. package/server/lib/opencode/skill-routes.js +26 -40
  194. package/server/lib/opencode/skill-routes.test.js +71 -7
  195. package/server/lib/opencode/skills.test.js +15 -0
  196. package/server/lib/opencode/startup-pipeline-runtime.js +4 -0
  197. package/server/lib/opencode/static-routes-runtime.js +3 -1
  198. package/server/lib/opencode/static-routes-runtime.test.js +33 -0
  199. package/server/lib/opencode/upgrade-screen.d.ts +30 -0
  200. package/server/lib/opencode/upgrade-screen.js +299 -0
  201. package/server/lib/opencode/upgrade-screen.test.js +196 -0
  202. package/server/lib/opencode/v1-migration-gate.js +157 -0
  203. package/server/lib/opencode/v1-migration-gate.test.js +111 -0
  204. package/server/lib/opencode/v2-client.js +60 -0
  205. package/server/lib/opencode/v2-plugin-host-shim/index.mjs +155 -0
  206. package/server/lib/opencode/v2-plugin-host-shim/package.json +4 -0
  207. package/server/lib/opencode/watcher.js +2 -1
  208. package/server/lib/opencode/watcher.test.js +1 -1
  209. package/server/lib/permission-auto-accept/DOCUMENTATION.md +5 -5
  210. package/server/lib/permission-auto-accept/runtime.js +14 -7
  211. package/server/lib/permission-auto-accept/runtime.test.js +27 -12
  212. package/server/lib/relay/reasoning-projection.e2e.test.ts +22 -7
  213. package/server/lib/scheduled-tasks/DOCUMENTATION.md +79 -39
  214. package/server/lib/scheduled-tasks/managed-tool-route.js +31 -1
  215. package/server/lib/scheduled-tasks/managed-tool-route.test.js +23 -0
  216. package/server/lib/scheduled-tasks/routes.js +27 -0
  217. package/server/lib/scheduled-tasks/routes.test.js +94 -0
  218. package/server/lib/scheduled-tasks/runtime.js +381 -310
  219. package/server/lib/scheduled-tasks/runtime.test.js +600 -309
  220. package/server/lib/session-goal/DOCUMENTATION.md +173 -22
  221. package/server/lib/session-goal/capability.js +57 -0
  222. package/server/lib/session-goal/capability.test.js +33 -0
  223. package/server/lib/session-goal/routes.js +240 -2
  224. package/server/lib/session-goal/routes.test.js +211 -0
  225. package/server/lib/session-goal/runtime.js +1617 -113
  226. package/server/lib/session-goal/runtime.test.js +1500 -70
  227. package/server/lib/session-index/DOCUMENTATION.md +18 -15
  228. package/server/lib/session-index/event-ingest.js +89 -16
  229. package/server/lib/session-index/event-ingest.test.js +73 -0
  230. package/server/lib/session-index/host-authority-snapshot.js +92 -0
  231. package/server/lib/session-index/routes.host-archive.test.js +201 -0
  232. package/server/lib/session-index/routes.js +63 -10
  233. package/server/lib/session-index/routes.test.js +8 -8
  234. package/server/lib/session-index/service.js +7 -1
  235. package/server/lib/session-index/sync-runtime.js +171 -41
  236. package/server/lib/session-index/sync-runtime.test.js +164 -9
  237. package/server/lib/session-metadata/DOCUMENTATION.md +96 -0
  238. package/server/lib/session-metadata/opencode-session-record.d.ts +57 -0
  239. package/server/lib/session-metadata/opencode-session-record.js +316 -0
  240. package/server/lib/session-metadata/opencode-session-record.test.js +242 -0
  241. package/server/lib/session-metadata/routes.js +197 -0
  242. package/server/lib/session-metadata/routes.test.js +156 -0
  243. package/server/lib/session-metadata/session-archive.d.ts +73 -0
  244. package/server/lib/session-metadata/session-archive.js +654 -0
  245. package/server/lib/session-metadata/session-archive.test.js +359 -0
  246. package/server/lib/session-metadata/session-metadata-store.d.ts +38 -0
  247. package/server/lib/session-metadata/session-metadata-store.js +422 -0
  248. package/server/lib/session-metadata/session-metadata-store.test.js +379 -0
  249. package/server/lib/session-metadata/session-projection.d.ts +41 -0
  250. package/server/lib/session-metadata/session-projection.js +279 -0
  251. package/server/lib/session-metadata/session-projection.test.js +64 -0
  252. package/server/lib/session-metadata/system-session.js +55 -0
  253. package/server/lib/session-metadata/system-session.test.js +55 -0
  254. package/server/lib/session-title/DOCUMENTATION.md +40 -7
  255. package/server/lib/session-title/native-runtime.test.js +167 -0
  256. package/server/lib/session-title/runtime.js +69 -44
  257. package/server/lib/session-title/session-access.js +46 -0
  258. package/server/lib/session-title/session-access.test.js +91 -0
  259. package/server/lib/session-turn-pages/DOCUMENTATION.md +12 -11
  260. package/server/lib/session-turn-pages/reconcile.service.js +1 -1
  261. package/server/lib/session-turn-pages/reconcile.service.test.js +16 -0
  262. package/server/lib/session-turn-pages/routes.js +123 -75
  263. package/server/lib/session-turn-pages/routes.test.js +566 -0
  264. package/server/lib/session-turn-pages/service.js +6 -2
  265. package/server/lib/session-turn-pages/service.test.js +34 -0
  266. package/server/lib/session-turn-pages/session-message-projection.d.ts +13 -0
  267. package/server/lib/session-turn-pages/session-message-projection.js +281 -0
  268. package/server/lib/small-model/DOCUMENTATION.md +101 -146
  269. package/server/lib/small-model/catalog.js +84 -54
  270. package/server/lib/small-model/catalog.test.js +111 -9
  271. package/server/lib/small-model/custom-api.js +68 -0
  272. package/server/lib/small-model/custom-api.test.js +64 -0
  273. package/server/lib/small-model/index.js +125 -241
  274. package/server/lib/small-model/resolve.js +35 -120
  275. package/server/lib/small-model/resolve.test.js +20 -110
  276. package/server/lib/small-model/routes.js +1 -3
  277. package/server/lib/small-model/summary-settings.test.js +157 -253
  278. package/server/opencode-proxy.test.js +63 -12
  279. package/dist/assets/AssistantView-BUveAgc4.js +0 -3
  280. package/dist/assets/Icon-BgdfPhNs.js +0 -1
  281. package/dist/assets/MarkdownRenderer-BbrbDH6-.js +0 -2
  282. package/dist/assets/MarkstreamRendererImpl-D2dsW5Lg.js +0 -3
  283. package/dist/assets/MobileDetailNavigation-B0aqKXd7.js +0 -1
  284. package/dist/assets/MobileSurface-DW7WfPsy.js +0 -2
  285. package/dist/assets/OverlayScrollbar-DrA0Vsg5.js +0 -2
  286. package/dist/assets/SettingsView-PkXIavr7.js +0 -25
  287. package/dist/assets/TerminalView-CDdzF4yi.js +0 -8
  288. package/dist/assets/appThemeRegistry-D5Yxg4YF.js +0 -1
  289. package/dist/assets/es-DA3MqOo0.js +0 -15
  290. package/dist/assets/fileMentionTouchSelection-DNeiqns-.js +0 -4
  291. package/dist/assets/fr-Dbz0CdIA.js +0 -13
  292. package/dist/assets/gitApi-DXyC4MiV.js +0 -7
  293. package/dist/assets/insertionBoundaries-KlOVrHHt.js +0 -1
  294. package/dist/assets/ja-DumanVQA.js +0 -15
  295. package/dist/assets/ko-C0IkiUjQ.js +0 -15
  296. package/dist/assets/main-BPS5ylib.js +0 -2
  297. package/dist/assets/main-Cpd9VWYs.js +0 -169
  298. package/dist/assets/main-qSeeJRW1.css +0 -1
  299. package/dist/assets/markdown-worker-CptoICmq.js +0 -2
  300. package/dist/assets/markdownTheme-DOfGg-A7.js +0 -1
  301. package/dist/assets/miniChat-B8_KLWiY.js +0 -2
  302. package/dist/assets/mobile-CtktKrXn.js +0 -2
  303. package/dist/assets/multirun-CYfP9Cfm.js +0 -4
  304. package/dist/assets/persistence-Ce3AnpEr.js +0 -3
  305. package/dist/assets/pl-B9No6MQp.js +0 -15
  306. package/dist/assets/platform-0w_Q9_ji.js +0 -2
  307. package/dist/assets/pt-BR-DyQ0WTDh.js +0 -15
  308. package/dist/assets/renderElectronMiniChatApp-iK14LWm8.js +0 -2
  309. package/dist/assets/renderMobileApp-DORLPWnb.js +0 -28
  310. package/dist/assets/runtime-fetch-zIalUnbb.js +0 -1
  311. package/dist/assets/runtimeEndpointReset-ChOxt-J7.js +0 -147
  312. package/dist/assets/sessionChangeSummary-NnxcqCTI.js +0 -2
  313. package/dist/assets/src-rS3zCcsC.css +0 -2
  314. package/dist/assets/store-5B_LDnLM.js +0 -15
  315. package/dist/assets/textarea-bHYxZtfH.js +0 -1
  316. package/dist/assets/uk-C_1KkqyP.js +0 -15
  317. package/dist/assets/useDesktopWindowControlsLayout-Dnr-jX0C.js +0 -1
  318. package/dist/assets/useEffectiveDirectory-CZLLyarc.js +0 -1
  319. package/dist/assets/useMobileNavigationStore-CoCiqNgm.js +0 -413
  320. package/dist/assets/useSessionAutoCleanup-xQyETQkG.js +0 -2
  321. package/dist/assets/useUIStore-Bxv3f23A.js +0 -1
  322. package/dist/assets/vendor-opencode-sdk-C07HQypa.js +0 -7
  323. package/dist/assets/zh-CN-uWILgBdt.js +0 -15
  324. package/dist/assets/zh-TW-MYfdvhbD.js +0 -15
  325. package/server/lib/small-model/call.js +0 -427
  326. package/server/lib/small-model/call.test.js +0 -382
  327. package/server/lib/small-model/opencode-session.js +0 -339
  328. package/server/lib/small-model/opencode-session.test.js +0 -177
@@ -4,7 +4,7 @@
4
4
 
5
5
  OpenChamber-owned turn-window pagination and anchor reconcile for session messages.
6
6
 
7
- 1. **Turn pages** — clients request the last N **authored user turns** (default 3) rather than a raw message count. The host loops official OpenCode `session.messages` pages (`limit` / `before` / `x-next-cursor`) until the turn budget is met or history is exhausted.
7
+ 1. **Turn pages** — clients request the last N **authored user turns** (default 3) rather than a raw message count. The host loops OpenCode v2 `message.list` pages (first page `limit` + `order=desc`; continuation `limit` + `cursor` from the previous body `cursor.next` — upstream rejects `order` together with `cursor` with 400) until the turn budget is met or history is exhausted.
8
8
  2. **Anchor reconcile** — after SSE reconnect, clients request the gap from a stable turn-boundary anchor to the current head. The host scans newest→older, returns chronological gap records (including the anchor's overlap turn), and paginates via host-owned continuation tokens with page/byte/total budgets.
9
9
 
10
10
  Exposed as:
@@ -16,7 +16,7 @@ Exposed as:
16
16
 
17
17
  ## Why this exists
18
18
 
19
- Official OpenCode pagination is message-count based and uses **opaque** cursors (not message ids). Each upstream page is chronological **old→new** (including the latest slice). UI prepend/load-more needs a stable turn boundary so synthetic loop messages, subtasks, and compaction rows do not consume the turn budget. This module aggregates upstream pages on the host, returns chronological records, and exposes a continuous **host-owned opaque cursor** that points just before the earliest returned authored user.
19
+ Official OpenCode pagination is message-count based and uses **opaque** cursors (not message ids). v2 pages arrive **newest-first**; `fetchPage` reverses each page to chronological **old→new** before aggregation. UI prepend/load-more needs a stable turn boundary so synthetic loop messages, subtasks, and compaction rows do not consume the turn budget. This module aggregates upstream pages on the host, returns chronological records, and exposes a continuous **host-owned opaque cursor** that points just before the earliest returned authored user.
20
20
 
21
21
  ## Scope
22
22
 
@@ -34,7 +34,7 @@ Official OpenCode pagination is message-count based and uses **opaque** cursors
34
34
  | `scanLimit` | **omitted** | `10..200` when present | Optional override only. Host→OpenCode local page size; when omitted it resolves to `_inner_scanLimit`. Clients should omit this. |
35
35
  | `before` | omitted | opaque string ≤ 8192 chars | Resume older history: host cursor (`oc1.…`) or raw OpenCode SDK cursor on first page |
36
36
  | `directory` | omitted | path string | Forwarded to directory-scoped OpenCode client |
37
- | `includeReasoning` | omitted (include) | strict `'false'` only disables | OpenChamber projection control. When `'false'`, drop `type=reasoning` parts from turn-page / reconcile / exact responses after other projections. Missing or any other value keeps existing behavior (slim reasoning identity on turn pages; full parts on reconcile/exact). **Never forwarded to OpenCode.** Same contract on transcript-cache reads, official `session.messages` list proxy, and event SSE/WS send boundaries — see `event-stream/reasoning-projection.js`. |
37
+ | `includeReasoning` | omitted (include) | strict `'false'` only disables | OpenChamber projection control. When `'false'`, drop `type=reasoning` parts from turn-page / reconcile / exact responses. Host order is **normalize native SessionMessageInfo → slim/L1 bounds → reasoning strip** so `content`/`text` carriers cannot rebuild filtered parts or leak through `info` expansion. Missing or any other value keeps existing behavior (slim reasoning identity on turn pages; full parts on reconcile/exact). **Never forwarded to OpenCode.** Same contract on transcript-cache reads, official `session.messages` list proxy, and event SSE/WS send boundaries — see `event-stream/reasoning-projection.js`. |
38
38
 
39
39
  Invalid `turns`, explicit invalid `scanLimit`, or `before` (malformed/stale host token, oversize) → HTTP 400.
40
40
 
@@ -66,7 +66,7 @@ Success `200`:
66
66
  }
67
67
  ```
68
68
 
69
- - `records`: chronological oldest → newest, including intermediate non-boundary rows (synthetic, tools, subtask, compaction) inside the window.
69
+ - `records`: chronological oldest → newest, including intermediate non-boundary rows (synthetic, tools, subtask, compaction) inside the window. Host projection drops v2 **control** SessionMessage rows (`idle`, `model-switched`, `agent-selected`, `agent-switched`, `location-switched`) so they never reach ChatMessage as empty Assistant headers. Native assistants project `content[]` once (never also top-level `text` when `content` is present). Wire `model.id` becomes domain `modelID`; `finish`/`error` on failed assistants (e.g. `provider.auth` 401) are preserved.
70
70
  - `turnCount`: count of authored user boundaries in `records`.
71
71
  - `cursor`: host-owned opaque token for the next `before=` request; `null` when `complete` is true. The token represents the position **just before** the earliest returned authored user (so the client can load older history without overlap).
72
72
  - `complete`: `upstreamComplete && selected.length === accumulated.length`. True when upstream history is exhausted **and** `selectTurnRecords` did not trim any older scanned rows (nothing left for the client). When the scan window held more than N turns and older rows were trimmed, or when upstream stopped on a full page with a missing `nextCursor` (suspicious truncation), `complete` stays `false` with a `cursor` so the client can fetch / retry.
@@ -82,7 +82,7 @@ Every turn-page response summarizes `tool`, `reasoning`, and `file` parts plus m
82
82
  |---|---|---|
83
83
  | **L1** | Turn pages, reconcile response pages, message/session SSE fan-out, exact `GET /api/session/:sessionID/message/:messageID` | Any `info.summary.diffs` **array** is projected to a **slim file list** `[{ file, status?, additions, deletions }]` plus additive markers `summary.diffCount: number` and `summary.hasDiffs: boolean`. Patch/before/after/from/to and other heavy bodies are stripped. Other lightweight summary fields are preserved. `diffCount` equals the slim array length; `hasDiffs === diffCount > 0`. Consumers may use either the slim array or the markers. |
84
84
  | **L2** | `GET /api/openchamber/sessions/:sessionID/changes?messageID=` (no `file`) | Host reads official `session.message` locally, returns only `{ files: [{ file, status?, additions, deletions }] }`. Never returns patch/before/after/from/to or the full message envelope. |
85
- | **L3** | Same route with `file=` | Host calls official `session.diff({ sessionID, messageID, directory })`, returns `{ diff: SnapshotFileDiff }` for the exact `file` string match (includes that file's patch). No match → HTTP 404 `{ error: 'change file not found', code: 'change_not_found' }`. Older hosts may ignore `file=` and answer 200 `{ files }` (L2 only). Current clients treat a 200 without `{ diff }` as missing L3 and fall back to official `session.diff`. |
85
+ | **L3** | Same route with `file=` | Host currently still reads `info.summary.diffs` from the exact message payload when present. **v2 `SessionMessageInfo` has no `summary.diffs`**, so L3 fails closed with upstream 502 until a parent follow-up lands an authoritative per-message diff source. Do **not** substitute working-tree / `vcs.diff` here. When diffs are available, returns `{ diff: SnapshotFileDiff }` for the exact `file` string match. No match → HTTP 404 `{ error: 'change file not found', code: 'change_not_found' }`. Older hosts may ignore `file=` and answer 200 `{ files }` (L2 only). |
86
86
 
87
87
  Exact message GET keeps the official `{ info, parts }` response shape and only applies L1 summary projection so materialize/recovery never relays raw multi-megabyte patch bodies through the generic proxy. Abort/45s timeout semantics match the turn-page routes. Global `/api` auth still applies.
88
88
 
@@ -126,7 +126,7 @@ On resume:
126
126
  1. Strict decode / shape / length validation of the host token.
127
127
  2. First `fetchPage` uses the token’s raw upstream `before`.
128
128
  3. Locate `boundaryID` in that page; keep only records strictly older than it (`slice(0, index)` in old→new order). Boundary and newer rows are excluded.
129
- 4. If fewer than N turns remain, continue with that raw page’s `x-next-cursor` for older pages.
129
+ 4. If fewer than N turns remain, continue with that raw page’s `cursor.next` for older pages.
130
130
  5. Each accumulated record records its upstream page’s request-before origin; when `complete=false`, the next host cursor encodes the earliest selected authored user’s origin + id.
131
131
 
132
132
  A raw OpenCode SDK cursor (no `oc1.` prefix) is still accepted as the first `before` and is passed through unchanged to `fetchPage`.
@@ -146,14 +146,15 @@ A message is an authored user turn boundary when:
146
146
 
147
147
  ## Aggregation rules
148
148
 
149
- - Upstream pages are chronological **old→new** within each page; **do not reverse**. Older pages are **prepended** while deduping by `info.id` (global order remains old→new).
150
- - Continues paging with raw upstream `before` until `turns` authored boundaries are collected or upstream reports no next cursor.
151
- - **Suspicious truncation:** an upstream page whose `records.length >= scanLimit` (full page) but `nextCursor == null` is treated as a non-authoritative cut (observed OpenCode `x-next-cursor` race), not as end-of-history. The host stops scanning that round with `upstreamComplete=false`, so the response stays `complete=false` and still emits a boundary host cursor the client can retry. A short page (`records.length < scanLimit`) with no cursor, an empty page with no cursor, or an explicit `page.complete === true` remain authoritative exhaustion. Client no-growth handling still bounds retries if the upstream view stays truncated.
149
+ - `fetchPage` returns each page chronological **old→new** (v2 `data` is newest-first and is reversed there). Older pages are **prepended** while deduping by `info.id` (global order remains old→new).
150
+ - Continues paging with the raw upstream `cursor.next` (sent as `cursor`, never with `order`) until `turns` authored boundaries are collected or upstream reports no next cursor.
151
+ - **Suspicious truncation:** an upstream page whose `records.length >= scanLimit` (full page) but `nextCursor == null` is treated as a non-authoritative cut (observed OpenCode next-cursor race), not as end-of-history. The host stops scanning that round with `upstreamComplete=false`, so the response stays `complete=false` and still emits a boundary host cursor the client can retry. A short page (`records.length < scanLimit`) with no cursor, an empty page with no cursor, or an explicit `page.complete === true` remain authoritative exhaustion. Client no-growth handling still bounds retries if the upstream view stays truncated.
152
152
  - Every upstream record must have a non-empty `info.id`; otherwise structured error, no partial `records`, HTTP 502.
153
153
  - Hard scan caps (no partial success): **50 pages** / **5000 messages** → structured error, HTTP 413.
154
154
  - Repeated / stalled cursor, or empty page that still carries a next cursor → structured error, no partial `records`, HTTP 502.
155
155
  - Malformed upstream payloads and SDK/transport failures → HTTP 502 (`error: "upstream"`).
156
- - `AbortSignal` is forwarded to each `session.messages` call; route also applies a 45s linked timeout.
156
+ - Native v2 `SessionMessageInfo` rows are projected to the external `{ info, parts }` record contract by `session-message-projection.js` (control rows such as `idle` / `model-switched` are dropped). The VS Code turn-page bridge imports the same module so both runtimes emit identical records.
157
+ - `AbortSignal` is forwarded to each `message.list` call; route also applies a 45s linked timeout.
157
158
  - Request abort only when the client disconnects mid-flight: `req` aborted, or `res` `close` while `!res.writableEnded`. A normal GET request `close` after the response has ended must not abort.
158
159
 
159
160
  ## Anchor reconcile API
@@ -263,7 +264,7 @@ When the per-page budget fills before the gap is complete, the host returns `con
263
264
  - `service.js` — `isUserAuthoredTurnBoundary`, `selectTurnRecords`, `encodeHostCursor`, `decodeHostCursor`, `projectMessageDiffSummaries` (L1), `projectSlimParts`, `createSessionTurnPageService({ fetchPage, maxScanPages?, maxScanMessages? })`
264
265
  - `reconcile.service.js` — `encodeReconcileContinuation`, `decodeReconcileContinuation`, `createSessionReconcileService({ fetchPage, runtimeKey?, page/total budgets?, continuationSecret?, clock?, continuationTtlMs? })`
265
266
  - `changes.service.js` — `createSessionChangesService({ fetchMessage, fetchDiff })` for L2/L3; injectable for unit tests
266
- - `routes.js` — Express registration for turn-page, reconcile, Changes, and exact message GET; optional injected `sessionTurnPageService` / `sessionReconcileService` / `sessionChangesService` / `fetchExactMessage` for tests; default wires SDK `session.messages` / `session.message` / `session.diff` via `buildOpenCodeUrl` + `getOpenCodeAuthHeaders`
267
+ - `routes.js` — Express registration for turn-page, reconcile, Changes, and exact message GET; optional injected `sessionTurnPageService` / `sessionReconcileService` / `sessionChangesService` / `fetchExactMessage` for tests; default wires `@opencode/client` `OpenCode.make` (`message.list` / `session.message`) via `buildOpenCodeUrl` + `getOpenCodeAuthHeaders`. Exact/not-found classification honors real client `_tag` (`MessageNotFoundError` / `SessionNotFoundError`) and preserves abort → HTTP 499 (never folds abort into 502).
267
268
  - `service.test.js` / `routes.test.js` / `reconcile.service.test.js` / `reconcile.routes.test.js` / `changes.service.test.js` — unit contracts
268
269
 
269
270
  ## Registration
@@ -47,7 +47,7 @@ const PROCESS_CONTINUATION_SECRET = randomBytes(32);
47
47
  const fail = (error) => ({ ok: false, error });
48
48
 
49
49
  const recordId = (record) => {
50
- const id = record?.info?.id;
50
+ const id = record?.info?.id ?? record?.id;
51
51
  return typeof id === 'string' && id.length > 0 ? id : null;
52
52
  };
53
53
 
@@ -686,4 +686,20 @@ describe('createSessionReconcileService', () => {
686
686
  expect(result.ok).toBe(true);
687
687
  expect(result.resetRequired).toBe(true);
688
688
  });
689
+
690
+ it('locates a SessionMessageInfo anchor by top-level id', async () => {
691
+ const fetchPage = vi.fn(async () => pageResult([
692
+ { id: 'msg_u1', type: 'user', time: { created: 1 }, text: 'hi' },
693
+ { id: 'msg_a1', type: 'assistant', time: { created: 2, completed: 3 }, content: [{ type: 'text', text: 'ok' }] },
694
+ ], null));
695
+ const service = createSessionReconcileService(serviceOptions(fetchPage));
696
+ const result = await service.reconcile({
697
+ sessionID: 'ses_1',
698
+ directory: '/repo',
699
+ anchor: 'msg_u1',
700
+ });
701
+ expect(result.ok).toBe(true);
702
+ expect(result.anchorFound).toBe(true);
703
+ expect(result.records.map((entry) => entry.id)).toEqual(['msg_u1', 'msg_a1']);
704
+ });
689
705
  });
@@ -1,4 +1,4 @@
1
- import { createOpencodeClient } from '@opencode-ai/sdk/v2';
1
+ import { makeOpenCodeV2Client } from '../opencode/v2-client.js';
2
2
  import { projectMessageSummaryDiffCounts } from '../event-stream/diff-summary.js';
3
3
  import {
4
4
  projectMessagesPayloadForReasoning,
@@ -18,6 +18,10 @@ import {
18
18
  projectSlimParts,
19
19
  SLIM_PARTS_PROJECTION,
20
20
  } from './service.js';
21
+ import {
22
+ projectSessionMessage,
23
+ projectSessionMessageRecords as projectRecords,
24
+ } from './session-message-projection.js';
21
25
 
22
26
  const TURNS_MIN = 1;
23
27
  const TURNS_MAX = 10;
@@ -210,10 +214,57 @@ const requestSignal = (req, res) => {
210
214
  return controller.signal;
211
215
  };
212
216
 
217
+ /**
218
+ * Host outbound order: normalize → slim/L1 bounds → reasoning strip.
219
+ * Slim/reasoning must run on `{info, parts}` so native `content` cannot
220
+ * rebuild filtered parts or leak through info expansion.
221
+ */
222
+ const projectTurnPageRecords = (records, includeReasoning) =>
223
+ projectMessagesPayloadForReasoning(projectSlimParts(projectRecords(records)), includeReasoning);
224
+
225
+ const projectReconcileRecords = (records, includeReasoning) =>
226
+ projectMessagesPayloadForReasoning(
227
+ projectMessageDiffSummaries(projectRecords(records)),
228
+ includeReasoning,
229
+ );
230
+
231
+ const createV2FetchPage = ({ buildOpenCodeUrl, getOpenCodeAuthHeaders, logger }) => {
232
+ return async ({ sessionID, before, limit, signal }) => {
233
+ const baseUrl = buildOpenCodeUrl('/', '').replace(/\/$/, '');
234
+ const headers = typeof getOpenCodeAuthHeaders === 'function' ? getOpenCodeAuthHeaders() : {};
235
+ const client = makeOpenCodeV2Client({ baseUrl, authHeaders: headers });
236
+
237
+ // Upstream rejects `order` together with `cursor` (400); the cursor
238
+ // already encodes the desc direction of the first page.
239
+ const result = await client.message.list({
240
+ sessionID,
241
+ ...(Number.isFinite(limit) ? { limit } : {}),
242
+ ...(typeof before === 'string' && before.length > 0 ? { cursor: before } : { order: 'desc' }),
243
+ }, { signal });
244
+
245
+ const items = Array.isArray(result?.data) ? result.data : null;
246
+ if (!items) {
247
+ logger?.warn?.('[session-turn-pages] message.list malformed payload');
248
+ const error = new Error('upstream');
249
+ error.code = 'upstream';
250
+ throw error;
251
+ }
252
+
253
+ const next = result?.cursor?.next;
254
+ const nextCursor = typeof next === 'string' && next.length > 0 ? next : null;
255
+
256
+ return {
257
+ records: items.slice().reverse(),
258
+ nextCursor,
259
+ complete: nextCursor == null,
260
+ };
261
+ };
262
+ };
263
+
213
264
  const createSdkClient = ({ buildOpenCodeUrl, getOpenCodeAuthHeaders }) => {
214
265
  const baseUrl = buildOpenCodeUrl('/', '').replace(/\/$/, '');
215
266
  const headers = typeof getOpenCodeAuthHeaders === 'function' ? getOpenCodeAuthHeaders() : {};
216
- return createOpencodeClient({ baseUrl, headers });
267
+ return makeOpenCodeV2Client({ baseUrl, authHeaders: headers });
217
268
  };
218
269
 
219
270
  const throwUpstream = (logger, label) => {
@@ -223,89 +274,86 @@ const throwUpstream = (logger, label) => {
223
274
  throw error;
224
275
  };
225
276
 
226
- const createSdkFetchPage = ({ buildOpenCodeUrl, getOpenCodeAuthHeaders, logger }) => {
227
- return async ({ sessionID, directory, before, limit, signal }) => {
228
- const client = createSdkClient({ buildOpenCodeUrl, getOpenCodeAuthHeaders });
229
-
230
- const result = await client.session.messages({
231
- sessionID,
232
- ...(typeof directory === 'string' && directory.length > 0 ? { directory } : {}),
233
- ...(Number.isFinite(limit) ? { limit } : {}),
234
- ...(typeof before === 'string' && before.length > 0 ? { before } : {}),
235
- }, { signal });
236
-
237
- if (result?.error) {
238
- throwUpstream(logger, '[session-turn-pages] session.messages SDK error');
239
- }
240
-
241
- const data = result?.data;
242
- const records = Array.isArray(data)
243
- ? data
244
- : Array.isArray(data?.items)
245
- ? data.items
246
- : null;
247
- if (!records) {
248
- throwUpstream(logger, '[session-turn-pages] session.messages malformed payload');
249
- }
250
-
251
- const headerCursor = result?.response?.headers?.get?.('x-next-cursor');
252
- const nextCursor = typeof headerCursor === 'string' && headerCursor.length > 0
253
- ? headerCursor
254
- : null;
277
+ const getHttpStatus = (error) => {
278
+ const candidates = [error?.cause?.status, error?.status, error?.response?.status, error?.statusCode];
279
+ for (const value of candidates) {
280
+ if (Number.isFinite(value)) return value;
281
+ }
282
+ return undefined;
283
+ };
255
284
 
256
- return {
257
- records,
258
- nextCursor,
259
- complete: nextCursor == null,
260
- };
261
- };
285
+ /**
286
+ * Real `@opencode/client` throws declared JSON bodies with `_tag`
287
+ * (e.g. MessageNotFoundError) — no HTTP status on the object. Also accept
288
+ * legacy status/code shapes from tests and older surfaces.
289
+ */
290
+ const isNotFoundError = (error) => {
291
+ if (!error || typeof error !== 'object') return false;
292
+ const tag = error._tag ?? error.name;
293
+ if (tag === 'MessageNotFoundError' || tag === 'SessionNotFoundError') return true;
294
+ if (getHttpStatus(error) === 404) return true;
295
+ const code = error.code ?? error.type ?? error.error?.code ?? error.error?.type ?? error.error?._tag;
296
+ return code === 'not_found'
297
+ || code === 'NotFound'
298
+ || code === 'MessageNotFoundError'
299
+ || code === 'SessionNotFoundError';
262
300
  };
263
301
 
302
+ const isAbortError = (error) => (
303
+ error?.name === 'AbortError'
304
+ || error?.code === 'aborted'
305
+ || error?.cause?.name === 'AbortError'
306
+ );
307
+
264
308
  const createSdkFetchMessage = ({ buildOpenCodeUrl, getOpenCodeAuthHeaders, logger }) => {
265
- return async ({ sessionID, messageID, directory, signal }) => {
309
+ return async ({ sessionID, messageID, signal }) => {
266
310
  const client = createSdkClient({ buildOpenCodeUrl, getOpenCodeAuthHeaders });
267
- const result = await client.session.message({
268
- sessionID,
269
- messageID,
270
- ...(typeof directory === 'string' && directory.length > 0 ? { directory } : {}),
271
- }, { signal });
272
-
273
- if (result?.error) {
274
- const status = result?.response?.status;
275
- if (status === 404) {
276
- const error = new Error('not_found');
277
- error.code = 'not_found';
278
- throw error;
311
+ let result;
312
+ try {
313
+ result = await client.session.message({ sessionID, messageID }, { signal });
314
+ } catch (error) {
315
+ // Preserve abort so routes map 499; never fold into upstream 502.
316
+ if (isAbortError(error) || signal?.aborted) throw error;
317
+ if (isNotFoundError(error)) {
318
+ const notFound = new Error('not_found');
319
+ notFound.code = 'not_found';
320
+ throw notFound;
279
321
  }
280
322
  throwUpstream(logger, '[session-turn-pages] session.message SDK error');
281
323
  }
282
324
 
283
- const data = result?.data;
325
+ const data = result?.data ?? result;
284
326
  if (!data || typeof data !== 'object' || Array.isArray(data)) {
285
327
  throwUpstream(logger, '[session-turn-pages] session.message malformed payload');
286
328
  }
287
- return data;
329
+ const projected = projectSessionMessage(data);
330
+ // Control rows (idle / model-switched) are not chat messages.
331
+ if (!projected || !projected.info) {
332
+ const notFound = new Error('not_found');
333
+ notFound.code = 'not_found';
334
+ throw notFound;
335
+ }
336
+ return projected;
288
337
  };
289
338
  };
290
339
 
340
+ /**
341
+ * L3 diff source. v2 SessionMessageInfo has no summary.diffs; keep failure
342
+ * explicit until the parent lands an authoritative per-message diff API.
343
+ * Do not substitute working-tree / vcs.diff here.
344
+ */
291
345
  const createSdkFetchDiff = ({ buildOpenCodeUrl, getOpenCodeAuthHeaders, logger }) => {
292
346
  return async ({ sessionID, messageID, directory, signal }) => {
293
- const client = createSdkClient({ buildOpenCodeUrl, getOpenCodeAuthHeaders });
294
- const result = await client.session.diff({
295
- sessionID,
296
- messageID,
297
- ...(typeof directory === 'string' && directory.length > 0 ? { directory } : {}),
298
- }, { signal });
299
-
300
- if (result?.error) {
301
- throwUpstream(logger, '[session-changes] session.diff SDK error');
302
- }
303
-
304
- const data = result?.data;
305
- if (!Array.isArray(data)) {
306
- throwUpstream(logger, '[session-changes] session.diff malformed payload');
347
+ const payload = await createSdkFetchMessage({
348
+ buildOpenCodeUrl,
349
+ getOpenCodeAuthHeaders,
350
+ logger,
351
+ })({ sessionID, messageID, directory, signal });
352
+ const diffs = payload?.info?.summary?.diffs;
353
+ if (!Array.isArray(diffs)) {
354
+ throwUpstream(logger, '[session-changes] session.message summary.diffs unavailable (L3 source pending)');
307
355
  }
308
- return data;
356
+ return diffs;
309
357
  };
310
358
  };
311
359
 
@@ -441,7 +489,7 @@ export const registerSessionTurnPageRoutes = (app, dependencies = {}) => {
441
489
 
442
490
  const needsDefaultFetch = !injectedService || !injectedReconcileService;
443
491
  const defaultFetchPage = needsDefaultFetch
444
- ? createSdkFetchPage({ buildOpenCodeUrl, getOpenCodeAuthHeaders, logger })
492
+ ? createV2FetchPage({ buildOpenCodeUrl, getOpenCodeAuthHeaders, logger })
445
493
  : null;
446
494
 
447
495
  const service = injectedService ?? createSessionTurnPageService({
@@ -514,8 +562,8 @@ export const registerSessionTurnPageRoutes = (app, dependencies = {}) => {
514
562
  }
515
563
 
516
564
  const includeReasoning = readIncludeReasoningQuery(req.query);
517
- const records = projectMessagesPayloadForReasoning(
518
- projectMessageDiffSummaries(Array.isArray(result.records) ? result.records : []),
565
+ const records = projectReconcileRecords(
566
+ Array.isArray(result.records) ? result.records : [],
519
567
  includeReasoning,
520
568
  );
521
569
 
@@ -609,10 +657,10 @@ export const registerSessionTurnPageRoutes = (app, dependencies = {}) => {
609
657
 
610
658
  // Turn-page responses (first packet and prepend) share slim-v1.
611
659
  // Reconcile stays on the other route and keeps full parts.
612
- // includeReasoning=false drops type=reasoning parts after slim projection.
660
+ // Normalize native SessionMessageInfo first, then slim, then reasoning strip.
613
661
  const includeReasoning = readIncludeReasoningQuery(req.query);
614
- const records = projectMessagesPayloadForReasoning(
615
- projectSlimParts(result.records),
662
+ const records = projectTurnPageRecords(
663
+ Array.isArray(result.records) ? result.records : [],
616
664
  includeReasoning,
617
665
  );
618
666
  return res.status(200).json({
@@ -673,13 +721,13 @@ export const registerSessionTurnPageRoutes = (app, dependencies = {}) => {
673
721
  projectMessagesPayloadForReasoning(projectExactMessagePayload(payload), includeReasoning),
674
722
  );
675
723
  } catch (error) {
676
- if (error?.name === 'AbortError' || error?.code === 'aborted') {
724
+ if (isAbortError(error) || error?.code === 'aborted') {
677
725
  if (!res.headersSent) {
678
726
  return res.status(499).json({ error: SAFE_ERRORS.aborted });
679
727
  }
680
728
  return undefined;
681
729
  }
682
- if (error?.code === 'not_found') {
730
+ if (error?.code === 'not_found' || isNotFoundError(error)) {
683
731
  if (!res.headersSent) {
684
732
  return res.status(404).json({ error: 'not found', code: 'not_found' });
685
733
  }