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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (287) hide show
  1. package/README.md +15 -11
  2. package/bin/cli-opencode.test.js +4 -4
  3. package/bin/cli.js +1 -1
  4. package/bin/lib/cli-startup.js +1 -1
  5. package/bin/lib/cli-startup.test.js +18 -4
  6. package/dist/assets/{AgentAvatar-CLWWNu_Q.js → AgentAvatar-Q1SmFRx2.js} +1 -1
  7. package/dist/assets/AssistantView-CwXkiCAd.js +3 -0
  8. package/dist/assets/{DiagramView-xuSJTnv7.js → DiagramView-BW92schO.js} +1 -1
  9. package/dist/assets/{FadeInOnReveal-Ov0D96d0.js → FadeInOnReveal-BZmwl-zE.js} +1 -1
  10. package/dist/assets/{Icon-DD-BuMfZ.js → Icon-AQPvxMiU.js} +1 -1
  11. package/dist/assets/{MarkdownRenderer-bc_p1H7l.js → MarkdownRenderer-Bv05iDZZ.js} +2 -2
  12. package/dist/assets/{MarkdownRendererImpl-SKi4oG0c.js → MarkdownRendererImpl-C81xqYib.js} +3 -3
  13. package/dist/assets/MarkstreamRendererImpl-CXUCQuKJ.js +3 -0
  14. package/dist/assets/{MobileDetailNavigation-Bnx59U_J.js → MobileDetailNavigation-BMgIu7zc.js} +1 -1
  15. package/dist/assets/{MobileShareBridge-BQlP4J-F.js → MobileShareBridge-BOXGNHmC.js} +2 -2
  16. package/dist/assets/MobileSurface-xuc_z0_G.js +2 -0
  17. package/dist/assets/{MultiRunWindow-ChZGob0v.js → MultiRunWindow-B5At2Z95.js} +1 -1
  18. package/dist/assets/{OnboardingScreen-DtQJxZG-.js → OnboardingScreen-bsPLgz4r.js} +1 -1
  19. package/dist/assets/OverlayScrollbar-4wp4migh.js +2 -0
  20. package/dist/assets/SettingsView-CJ6tcH5_.js +25 -0
  21. package/dist/assets/{SettingsWindow-DfnsFa8y.js → SettingsWindow-CMtX2bMl.js} +1 -1
  22. package/dist/assets/{TerminalView-Dh9MWCpS.js → TerminalView-Dvr-ysTS.js} +1 -1
  23. package/dist/assets/{ToolOutputDialog-BejmjrRk.js → ToolOutputDialog-uH1ZZDSS.js} +2 -2
  24. package/dist/assets/appThemeRegistry-DK7q2juD.js +1 -0
  25. package/dist/assets/{assistant-attachment-cache-C0aRnFkj.js → assistant-attachment-cache-DphJleHr.js} +1 -1
  26. package/dist/assets/{button-D-rATN7w.js → button-CcNJUxUd.js} +1 -1
  27. package/dist/assets/collapsible-ZmZ6rLWs.js +3 -0
  28. package/dist/assets/context-menu-B2LXJgXL.js +1 -0
  29. package/dist/assets/{desktopHosts-B1ixEylr.js → desktopHosts-C6HrP0jq.js} +1 -1
  30. package/dist/assets/{desktopRelayRestore-4JmFUw8r.js → desktopRelayRestore-DdVBp45U.js} +1 -1
  31. package/dist/assets/{diagram-DQf7Y_uR.js → diagram-CxTs9hCQ.js} +1 -1
  32. package/dist/assets/{dialog-bfbvR4VH.js → dialog-KMVL9QKF.js} +1 -1
  33. package/dist/assets/es-CN3E_zXj.js +15 -0
  34. package/dist/assets/{fileMentionTouchSelection-BvrNCroG.js → fileMentionTouchSelection-7RIwlpkB.js} +1 -1
  35. package/dist/assets/{fileQueries-DQc8EJUf.js → fileQueries-CjdBRy8y.js} +1 -1
  36. package/dist/assets/fr-9zZvQZD2.js +13 -0
  37. package/dist/assets/gitApi-yuWg9BN4.js +10 -0
  38. package/dist/assets/{imageSource-BlAJJUEI.js → imageSource-9vQBq3zp.js} +1 -1
  39. package/dist/assets/{input-8r6F_Fza.js → input-CrIvk3HZ.js} +1 -1
  40. package/dist/assets/insertionBoundaries-BiUCSkx-.js +1 -0
  41. package/dist/assets/ja-Dnjk_82N.js +15 -0
  42. package/dist/assets/ko-BxCEE99C.js +15 -0
  43. package/dist/assets/{main-CCE_4y5F.js → main-C9mIBo77.js} +2 -2
  44. package/dist/assets/main-DiL6w6KF.js +169 -0
  45. package/dist/assets/markdown-worker-bS5OgGO3.js +2 -0
  46. package/dist/assets/{markdownHeightCache-1Qo6y6IJ.js → markdownHeightCache-BmXVY_gc.js} +1 -1
  47. package/dist/assets/{markdownTheme-DOfGg-A7.js → markdownTheme-CKac3wKC.js} +1 -1
  48. package/dist/assets/mcpQueries-C7jknHV8.js +1 -0
  49. package/dist/assets/miniChat-CwtUaElU.js +2 -0
  50. package/dist/assets/mobile-DB-6--ws.js +2 -0
  51. package/dist/assets/multirun-TAsxFI8e.js +4 -0
  52. package/dist/assets/{persistence-Ce3AnpEr.js → persistence-56kq8Z4V.js} +2 -2
  53. package/dist/assets/pl-CNNsWnPM.js +15 -0
  54. package/dist/assets/platform-C13fMVEs.js +6 -0
  55. package/dist/assets/pt-BR-BZB2DJMp.js +15 -0
  56. package/dist/assets/{relay-image-stream-DM7atqJs.js → relay-image-stream-DrdVj99c.js} +1 -1
  57. package/dist/assets/renderElectronMiniChatApp-CGj0MNRU.js +2 -0
  58. package/dist/assets/renderMobileApp-CGMMqbsO.js +28 -0
  59. package/dist/assets/runtime-fetch-Fs9ZV2V8.js +1 -0
  60. package/dist/assets/{runtimeConfig-BMvqa5we.js → runtimeConfig-BEANkvOd.js} +1 -1
  61. package/dist/assets/runtimeEndpointReset-A_ZBUJFS.js +147 -0
  62. package/dist/assets/sessionChangeSummary-qOLrP9fw.js +2 -0
  63. package/dist/assets/{sessionLookup-gh-hVRIc.js → sessionLookup-34XFmP9C.js} +1 -1
  64. package/dist/assets/{sortable-tabs-strip-CudoSgV4.js → sortable-tabs-strip-DyEV0Heg.js} +1 -1
  65. package/dist/assets/src-ubCWXZhR.css +2 -0
  66. package/dist/assets/store-DLCiZaA6.js +15 -0
  67. package/dist/assets/{terminalApi-DiF6JmBP.js → terminalApi-CXaBEoa_.js} +1 -1
  68. package/dist/assets/textarea-BBOlRI9O.js +1 -0
  69. package/dist/assets/{tooltip-CQvpK-UV.js → tooltip-CPI4fr1a.js} +1 -1
  70. package/dist/assets/uk-rwQrxeYM.js +15 -0
  71. package/dist/assets/{url-_z7evzdK.js → url-DRDQXjyX.js} +1 -1
  72. package/dist/assets/{useAppFontEffects-Cpmpo7Wb.js → useAppFontEffects-CiDe42I5.js} +57 -43
  73. package/dist/assets/{useAssistantUIStore-CMutVh9B.js → useAssistantUIStore-asNG6K7n.js} +1 -1
  74. package/dist/assets/useDesktopWindowControlsLayout-BlizAlOt.js +1 -0
  75. package/dist/assets/useEffectiveDirectory-C_CouL5v.js +1 -0
  76. package/dist/assets/{useFontPreferences-DcbXQOkc.js → useFontPreferences-B0oUZAiB.js} +1 -1
  77. package/dist/assets/useMobileNavigationStore-DfPClaW-.js +414 -0
  78. package/dist/assets/{useRuntimeAPIs-DSLXJybW.js → useRuntimeAPIs-DwLtO0c_.js} +1 -1
  79. package/dist/assets/useSessionAutoCleanup-DtrfbxAp.js +2 -0
  80. package/dist/assets/{useThemeSystem-DMzWVJaW.js → useThemeSystem-BlNUWH47.js} +1 -1
  81. package/dist/assets/useUIStore-CQYKJnrg.js +1 -0
  82. package/dist/assets/{useWorkerHighlightedLines-Bqn4AiBL.js → useWorkerHighlightedLines-CPNYz6Wf.js} +2 -2
  83. package/dist/assets/{vendor-base-ui-CnHfUmDN.js → vendor-base-ui-CDjyofeD.js} +1 -1
  84. package/dist/assets/{vendor-cmdk-CeAGfrLo.js → vendor-cmdk-BFH0UpXX.js} +1 -1
  85. package/dist/assets/{vendor-dnd-kit-accessibility-BY5LhhUB.js → vendor-dnd-kit-accessibility-BrR3AKvM.js} +1 -1
  86. package/dist/assets/{vendor-dnd-kit-core-CvWxUPdb.js → vendor-dnd-kit-core-CNfdvk6H.js} +1 -1
  87. package/dist/assets/{vendor-dnd-kit-sortable-hHdP9lLb.js → vendor-dnd-kit-sortable-DQUaTeGj.js} +1 -1
  88. package/dist/assets/{vendor-framer-motion-DFy_BSVd.js → vendor-framer-motion-4dogsUk3.js} +1 -1
  89. package/dist/assets/{vendor-legendapp-list-WE32lwNk.js → vendor-legendapp-list-BU7MdPZR.js} +1 -1
  90. package/dist/assets/{vendor-markstream-react-DX55XLGO.js → vendor-markstream-react-qHC2FykU.js} +3 -3
  91. package/dist/assets/{vendor-pierre-diffs-DX7XTPG3.js → vendor-pierre-diffs-CUfqC003.js} +1 -1
  92. package/dist/assets/{vendor-react-DLPElpoQ.js → vendor-react-C94A0AHl.js} +1 -1
  93. package/dist/assets/{vendor-react-drawio-ECtEwvbc.js → vendor-react-drawio-FVlWqKVE.js} +1 -1
  94. package/dist/assets/{vendor-reactuses-core-BnW6GxJt.js → vendor-reactuses-core-C2A3EjUi.js} +1 -1
  95. package/dist/assets/{vendor-sonner-DVzioF3d.js → vendor-sonner-DNqHmUQs.js} +1 -1
  96. package/dist/assets/{vendor-stream-diffs-DWy3xSmp.js → vendor-stream-diffs-CwbKsmlv.js} +2 -2
  97. package/dist/assets/{vendor-tanstack-react-query-D7pys_c9.js → vendor-tanstack-react-query-D0iFK57C.js} +1 -1
  98. package/dist/assets/{vendor-tanstack-react-virtual-tlDgH6PG.js → vendor-tanstack-react-virtual-B2kV6cnT.js} +1 -1
  99. package/dist/assets/{vendor-zustand-B4dlGEM2.js → vendor-zustand-BqLKf6ZY.js} +1 -1
  100. package/dist/assets/zh-CN-BvmBDnkD.js +15 -0
  101. package/dist/assets/zh-TW-9wcHTX0y.js +15 -0
  102. package/dist/index.html +13 -13
  103. package/dist/mini-chat.html +13 -13
  104. package/dist/mobile.html +13 -13
  105. package/package.json +1 -1
  106. package/server/index.js +327 -62
  107. package/server/lib/assistants/DOCUMENTATION.md +14 -9
  108. package/server/lib/assistants/contracts.js +11 -1
  109. package/server/lib/assistants/projection-history.test.js +265 -18
  110. package/server/lib/assistants/routes.js +6 -0
  111. package/server/lib/assistants/service.js +417 -231
  112. package/server/lib/assistants/service.test.js +392 -148
  113. package/server/lib/conversations/DOCUMENTATION.md +1 -1
  114. package/server/lib/conversations/service.js +1 -0
  115. package/server/lib/conversations/service.test.js +16 -0
  116. package/server/lib/conversations/validation.js +11 -1
  117. package/server/lib/event-stream/DOCUMENTATION.md +3 -1
  118. package/server/lib/event-stream/directory-ws-bridge.js +13 -2
  119. package/server/lib/event-stream/global-hub.js +49 -4
  120. package/server/lib/event-stream/reasoning-projection.js +16 -1
  121. package/server/lib/event-stream/reasoning-projection.test.js +15 -0
  122. package/server/lib/event-stream/runtime.js +57 -20
  123. package/server/lib/event-stream/runtime.test.js +112 -0
  124. package/server/lib/llm/DOCUMENTATION.md +33 -9
  125. package/server/lib/llm/catalog.js +1 -1
  126. package/server/lib/llm/completions.js +5 -1
  127. package/server/lib/llm/completions.test.js +3 -3
  128. package/server/lib/llm/generate.js +237 -145
  129. package/server/lib/llm/generate.test.js +201 -46
  130. package/server/lib/llm/routes.js +2 -0
  131. package/server/lib/message-queue/runtime.js +15 -2
  132. package/server/lib/notifications/runtime.js +23 -5
  133. package/server/lib/notifications/runtime.test.js +11 -0
  134. package/server/lib/opencode/DOCUMENTATION.md +85 -17
  135. package/server/lib/opencode/agent-document.d.ts +28 -0
  136. package/server/lib/opencode/agent-document.js +280 -0
  137. package/server/lib/opencode/agent-document.test.js +137 -0
  138. package/server/lib/opencode/agents.js +96 -0
  139. package/server/lib/opencode/auth-state-runtime.js +5 -0
  140. package/server/lib/opencode/bootstrap-runtime.js +2 -0
  141. package/server/lib/opencode/config-entity-routes.js +42 -99
  142. package/server/lib/opencode/config-entity-routes.test.js +107 -0
  143. package/server/lib/opencode/config-hot-reload.integration.test.js +105 -0
  144. package/server/lib/opencode/ensure-cli.js +1 -2
  145. package/server/lib/opencode/env-runtime.js +40 -26
  146. package/server/lib/opencode/env-runtime.test.js +10 -17
  147. package/server/lib/opencode/feature-routes-runtime.js +43 -4
  148. package/server/lib/opencode/feature-routes-runtime.test.js +18 -0
  149. package/server/lib/opencode/http-server-connections.js +98 -0
  150. package/server/lib/opencode/http-server-connections.test.js +321 -0
  151. package/server/lib/opencode/lifecycle.js +349 -39
  152. package/server/lib/opencode/lifecycle.test.js +322 -0
  153. package/server/lib/opencode/managed-capabilities-runtime.js +15 -2
  154. package/server/lib/opencode/managed-capabilities-runtime.test.js +1 -0
  155. package/server/lib/opencode/openchamber-routes.js +7 -1
  156. package/server/lib/opencode/opencode2-pin.js +4 -4
  157. package/server/lib/opencode/opencode2-pin.test.js +1 -0
  158. package/server/lib/opencode/opencode2-sidecar.e2e.test.js +127 -4
  159. package/server/lib/opencode/owned-runtime-upgrade.js +431 -0
  160. package/server/lib/opencode/owned-runtime-upgrade.test.js +160 -0
  161. package/server/lib/opencode/plugin-routes.js +3 -18
  162. package/server/lib/opencode/plugin-routes.test.js +9 -9
  163. package/server/lib/opencode/provider-catalog.js +58 -12
  164. package/server/lib/opencode/provider-catalog.test.js +48 -0
  165. package/server/lib/opencode/proxy.js +252 -46
  166. package/server/lib/opencode/proxy.test.js +235 -2
  167. package/server/lib/opencode/routes.js +296 -62
  168. package/server/lib/opencode/routes.test.js +278 -12
  169. package/server/lib/opencode/runtime-contract.js +401 -0
  170. package/server/lib/opencode/runtime-contract.test.js +214 -0
  171. package/server/lib/opencode/server-opencode-fetch.js +242 -0
  172. package/server/lib/opencode/server-opencode-fetch.test.js +130 -0
  173. package/server/lib/opencode/server-utils-runtime.js +33 -0
  174. package/server/lib/opencode/shared-service.integration.test.js +66 -0
  175. package/server/lib/opencode/shared-service.js +147 -0
  176. package/server/lib/opencode/shared-service.test.js +93 -0
  177. package/server/lib/opencode/shared.js +3 -3
  178. package/server/lib/opencode/shutdown-runtime.js +15 -6
  179. package/server/lib/opencode/shutdown-runtime.test.js +49 -1
  180. package/server/lib/opencode/skill-routes.js +20 -33
  181. package/server/lib/opencode/skill-routes.test.js +35 -2
  182. package/server/lib/opencode/skills.test.js +15 -0
  183. package/server/lib/opencode/startup-pipeline-runtime.js +4 -0
  184. package/server/lib/opencode/static-routes-runtime.js +3 -1
  185. package/server/lib/opencode/static-routes-runtime.test.js +33 -0
  186. package/server/lib/opencode/v2-client.js +14 -3
  187. package/server/lib/opencode/v2-plugin-host-shim/index.mjs +155 -0
  188. package/server/lib/opencode/v2-plugin-host-shim/package.json +4 -0
  189. package/server/lib/permission-auto-accept/DOCUMENTATION.md +5 -5
  190. package/server/lib/permission-auto-accept/runtime.js +14 -7
  191. package/server/lib/permission-auto-accept/runtime.test.js +27 -12
  192. package/server/lib/relay/reasoning-projection.e2e.test.ts +22 -7
  193. package/server/lib/scheduled-tasks/DOCUMENTATION.md +8 -0
  194. package/server/lib/scheduled-tasks/runtime.js +52 -4
  195. package/server/lib/scheduled-tasks/runtime.test.js +80 -1
  196. package/server/lib/session-goal/DOCUMENTATION.md +113 -13
  197. package/server/lib/session-goal/routes.js +9 -0
  198. package/server/lib/session-goal/runtime.js +1579 -117
  199. package/server/lib/session-goal/runtime.test.js +1453 -84
  200. package/server/lib/session-index/DOCUMENTATION.md +18 -15
  201. package/server/lib/session-index/event-ingest.js +89 -16
  202. package/server/lib/session-index/event-ingest.test.js +73 -0
  203. package/server/lib/session-index/host-authority-snapshot.js +92 -0
  204. package/server/lib/session-index/routes.host-archive.test.js +201 -0
  205. package/server/lib/session-index/routes.js +63 -10
  206. package/server/lib/session-index/routes.test.js +8 -8
  207. package/server/lib/session-index/service.js +7 -1
  208. package/server/lib/session-index/sync-runtime.js +161 -34
  209. package/server/lib/session-index/sync-runtime.test.js +155 -1
  210. package/server/lib/session-metadata/DOCUMENTATION.md +61 -16
  211. package/server/lib/session-metadata/routes.js +108 -9
  212. package/server/lib/session-metadata/routes.test.js +47 -2
  213. package/server/lib/session-metadata/session-archive.d.ts +73 -0
  214. package/server/lib/session-metadata/session-archive.js +654 -0
  215. package/server/lib/session-metadata/session-archive.test.js +359 -0
  216. package/server/lib/session-metadata/session-metadata-store.d.ts +36 -0
  217. package/server/lib/session-metadata/session-metadata-store.js +212 -72
  218. package/server/lib/session-metadata/session-metadata-store.test.js +205 -10
  219. package/server/lib/session-metadata/session-projection.d.ts +41 -0
  220. package/server/lib/session-metadata/session-projection.js +255 -0
  221. package/server/lib/session-metadata/session-projection.test.js +64 -0
  222. package/server/lib/session-metadata/system-session.js +55 -0
  223. package/server/lib/session-metadata/system-session.test.js +55 -0
  224. package/server/lib/session-title/DOCUMENTATION.md +40 -7
  225. package/server/lib/session-title/native-runtime.test.js +167 -0
  226. package/server/lib/session-title/runtime.js +67 -42
  227. package/server/lib/session-title/session-access.js +46 -0
  228. package/server/lib/session-title/session-access.test.js +91 -0
  229. package/server/lib/session-turn-pages/DOCUMENTATION.md +9 -8
  230. package/server/lib/session-turn-pages/routes.js +17 -102
  231. package/server/lib/session-turn-pages/routes.test.js +281 -0
  232. package/server/lib/session-turn-pages/session-message-projection.d.ts +13 -0
  233. package/server/lib/session-turn-pages/session-message-projection.js +281 -0
  234. package/server/lib/small-model/DOCUMENTATION.md +101 -147
  235. package/server/lib/small-model/catalog.js +31 -51
  236. package/server/lib/small-model/catalog.test.js +65 -10
  237. package/server/lib/small-model/custom-api.js +68 -0
  238. package/server/lib/small-model/custom-api.test.js +64 -0
  239. package/server/lib/small-model/index.js +124 -240
  240. package/server/lib/small-model/resolve.js +35 -120
  241. package/server/lib/small-model/resolve.test.js +20 -110
  242. package/server/lib/small-model/routes.js +1 -3
  243. package/server/lib/small-model/summary-settings.test.js +157 -253
  244. package/server/opencode-proxy.test.js +2 -0
  245. package/dist/assets/AssistantView-BTCkm8yk.js +0 -3
  246. package/dist/assets/MarkstreamRendererImpl-CnkodGJW.js +0 -3
  247. package/dist/assets/MobileSurface-BXLeiZ4d.js +0 -2
  248. package/dist/assets/OverlayScrollbar-pMwxaCca.js +0 -2
  249. package/dist/assets/SettingsView-B5I0KDTZ.js +0 -25
  250. package/dist/assets/appThemeRegistry-COGfxTFV.js +0 -1
  251. package/dist/assets/collapsible-BXRd5_Hr.js +0 -1
  252. package/dist/assets/context-menu-jGtH6fR9.js +0 -1
  253. package/dist/assets/es-c52LTHHe.js +0 -15
  254. package/dist/assets/fr-PisFbUE8.js +0 -13
  255. package/dist/assets/gitApi-ButwzQ0N.js +0 -9
  256. package/dist/assets/insertionBoundaries-DGs7S5zv.js +0 -1
  257. package/dist/assets/ja-C8Ikun3Z.js +0 -15
  258. package/dist/assets/ko-ClFys5o2.js +0 -15
  259. package/dist/assets/main-Dn9rpsgl.js +0 -169
  260. package/dist/assets/markdown-worker-CptoICmq.js +0 -2
  261. package/dist/assets/miniChat-BpjxVqgg.js +0 -2
  262. package/dist/assets/mobile-nMJCaNq1.js +0 -2
  263. package/dist/assets/multirun-BvSG94mW.js +0 -4
  264. package/dist/assets/pl-CnaKWN5F.js +0 -15
  265. package/dist/assets/platform-CWTIp02q.js +0 -4
  266. package/dist/assets/projectMeta-DOe49dM_.js +0 -3
  267. package/dist/assets/pt-BR-s8OpfRI6.js +0 -15
  268. package/dist/assets/renderElectronMiniChatApp-R6dKh79f.js +0 -2
  269. package/dist/assets/renderMobileApp-y-S8y8v_.js +0 -28
  270. package/dist/assets/runtime-fetch-zIalUnbb.js +0 -1
  271. package/dist/assets/runtimeEndpointReset-B6BxhvR0.js +0 -147
  272. package/dist/assets/sessionChangeSummary-VVBlEXuh.js +0 -2
  273. package/dist/assets/src-BzX5UI-c.css +0 -2
  274. package/dist/assets/store-DJczmczv.js +0 -15
  275. package/dist/assets/textarea-3XR8sUfy.js +0 -1
  276. package/dist/assets/uk-CaX0iqkB.js +0 -15
  277. package/dist/assets/useDesktopWindowControlsLayout-B1GX59-L.js +0 -1
  278. package/dist/assets/useEffectiveDirectory-CN6JoCFw.js +0 -1
  279. package/dist/assets/useMobileNavigationStore-BVjFtXSy.js +0 -414
  280. package/dist/assets/useSessionAutoCleanup-BLjymaFt.js +0 -2
  281. package/dist/assets/useUIStore-Bxv3f23A.js +0 -1
  282. package/dist/assets/zh-CN-20MYEjha.js +0 -15
  283. package/dist/assets/zh-TW-DiN04qTw.js +0 -15
  284. package/server/lib/small-model/call.js +0 -427
  285. package/server/lib/small-model/call.test.js +0 -382
  286. package/server/lib/small-model/opencode-session.js +0 -183
  287. package/server/lib/small-model/opencode-session.test.js +0 -147
@@ -45,6 +45,7 @@ const createRuntime = (updateScheduledTaskState, overrides = {}) => createSchedu
45
45
  waitForOpenCodeReady: vi.fn(async () => {
46
46
  throw new Error('OpenCode unavailable');
47
47
  }),
48
+ persistSessionMetadata: vi.fn(async () => ({})),
48
49
  logger: { info: vi.fn(), warn: vi.fn() },
49
50
  ...overrides,
50
51
  });
@@ -443,12 +444,16 @@ describe('scheduled-tasks run history and session lifecycle', () => {
443
444
  it('creates session with Scheduled title and location, attaches history, then prompts via session.prompt', async () => {
444
445
  const history = createHistoryStore();
445
446
  const client = createSuccessfulClient();
447
+ const persistSessionMetadata = vi.fn(async () => ({}));
448
+ const onSystemSessionPersisted = vi.fn();
446
449
 
447
450
  const updateScheduledTaskState = vi.fn(async (_projectID, _taskID, state) => ({
448
451
  task: { ...scheduledTask, state: { ...scheduledTask.state, ...state } },
449
452
  }));
450
453
  const runtime = createRuntime(updateScheduledTaskState, {
451
454
  runHistoryStore: history,
455
+ persistSessionMetadata,
456
+ onSystemSessionPersisted,
452
457
  waitForOpenCodeReady: vi.fn(async () => {}),
453
458
  });
454
459
  await runtime.syncProject('project-1');
@@ -474,9 +479,37 @@ describe('scheduled-tasks run history and session lifecycle', () => {
474
479
  location: { directory: '/tmp/project-1' },
475
480
  title: expect.stringMatching(/^\[Scheduled\] Task /),
476
481
  model: { id: 'gpt-4.1', providerID: 'openai' },
482
+ metadata: {
483
+ openchamber: {
484
+ scheduledTask: expect.objectContaining({
485
+ projectID: 'project-1',
486
+ taskID: 'task-1',
487
+ runID: started.id,
488
+ name: 'Task',
489
+ }),
490
+ },
491
+ },
477
492
  }), expect.objectContaining({ signal: expect.any(AbortSignal) }));
478
- expect(client.create.mock.calls[0][0].metadata).toBeUndefined();
479
493
  expect(client.create.mock.calls[0][0].directory).toBeUndefined();
494
+ expect(persistSessionMetadata).toHaveBeenCalledWith('ses_1', {
495
+ openchamber: {
496
+ scheduledTask: expect.objectContaining({
497
+ projectID: 'project-1',
498
+ taskID: 'task-1',
499
+ runID: started.id,
500
+ name: 'Task',
501
+ }),
502
+ },
503
+ });
504
+ expect(onSystemSessionPersisted).toHaveBeenCalledWith({
505
+ sessionID: 'ses_1',
506
+ directory: '/tmp/project-1',
507
+ metadata: expect.objectContaining({
508
+ openchamber: expect.objectContaining({
509
+ scheduledTask: expect.objectContaining({ taskID: 'task-1' }),
510
+ }),
511
+ }),
512
+ });
480
513
  expect(history.attachSession).toHaveBeenCalledWith(started.id, 'ses_1');
481
514
  expect(client.prompt).toHaveBeenCalledWith({
482
515
  sessionID: 'ses_1',
@@ -492,6 +525,48 @@ describe('scheduled-tasks run history and session lifecycle', () => {
492
525
  expect(client.active.mock.invocationCallOrder[0]).toBeGreaterThan(client.prompt.mock.invocationCallOrder[0]);
493
526
  });
494
527
 
528
+ it('refuses before session create when Host metadata persist is not configured', async () => {
529
+ const history = createHistoryStore();
530
+ const client = createSuccessfulClient();
531
+ const updateScheduledTaskState = vi.fn(async (_projectID, _taskID, state) => ({
532
+ task: { ...scheduledTask, state: { ...scheduledTask.state, ...state } },
533
+ }));
534
+ const runtime = createRuntime(updateScheduledTaskState, {
535
+ runHistoryStore: history,
536
+ persistSessionMetadata: null,
537
+ waitForOpenCodeReady: vi.fn(async () => {}),
538
+ });
539
+ await runtime.syncProject('project-1');
540
+ const result = await runtime.runNow('project-1', 'task-1');
541
+
542
+ expect(result.ok).toBe(false);
543
+ expect(result.error).toMatch(/session metadata persist is not configured/);
544
+ expect(client.create).not.toHaveBeenCalled();
545
+ expect(client.prompt).not.toHaveBeenCalled();
546
+ });
547
+
548
+ it('fails the run before prompt when Host metadata persist throws', async () => {
549
+ const history = createHistoryStore();
550
+ const client = createSuccessfulClient();
551
+ const updateScheduledTaskState = vi.fn(async (_projectID, _taskID, state) => ({
552
+ task: { ...scheduledTask, state: { ...scheduledTask.state, ...state } },
553
+ }));
554
+ const runtime = createRuntime(updateScheduledTaskState, {
555
+ runHistoryStore: history,
556
+ persistSessionMetadata: vi.fn(async () => {
557
+ throw new Error('store unavailable');
558
+ }),
559
+ waitForOpenCodeReady: vi.fn(async () => {}),
560
+ });
561
+ await runtime.syncProject('project-1');
562
+ const result = await runtime.runNow('project-1', 'task-1');
563
+
564
+ expect(result.ok).toBe(false);
565
+ expect(result.error).toMatch(/store unavailable/);
566
+ expect(client.create).toHaveBeenCalled();
567
+ expect(client.prompt).not.toHaveBeenCalled();
568
+ });
569
+
495
570
  it('records error outcome and real duration when the assistant turn fails after admission', async () => {
496
571
  const history = createHistoryStore();
497
572
  const client = createSuccessfulClient({ settlement: 'assistant-error' });
@@ -763,6 +838,7 @@ describe('scheduled-tasks run history and session lifecycle', () => {
763
838
  buildOpenCodeUrl: vi.fn(() => 'http://127.0.0.1:4096'),
764
839
  getOpenCodeAuthHeaders: vi.fn(() => ({})),
765
840
  waitForOpenCodeReady: vi.fn(async () => {}),
841
+ persistSessionMetadata: vi.fn(async () => ({})),
766
842
  logger: { info: vi.fn(), warn: vi.fn() },
767
843
  runHistoryStore: history,
768
844
  maxRunDurationMs: 500,
@@ -948,6 +1024,7 @@ describe('scheduled-tasks run history and session lifecycle', () => {
948
1024
  buildOpenCodeUrl: vi.fn(() => 'http://127.0.0.1:4096'),
949
1025
  getOpenCodeAuthHeaders: vi.fn(() => ({})),
950
1026
  waitForOpenCodeReady: vi.fn(async () => {}),
1027
+ persistSessionMetadata: vi.fn(async () => ({})),
951
1028
  logger: { info: vi.fn(), warn: vi.fn() },
952
1029
  runHistoryStore: history,
953
1030
  persistSessionGoal,
@@ -1016,6 +1093,7 @@ describe('scheduled-tasks run history and session lifecycle', () => {
1016
1093
  buildOpenCodeUrl: vi.fn(() => 'http://127.0.0.1:4096'),
1017
1094
  getOpenCodeAuthHeaders: vi.fn(() => ({})),
1018
1095
  waitForOpenCodeReady: vi.fn(async () => {}),
1096
+ persistSessionMetadata: vi.fn(async () => ({})),
1019
1097
  logger: { info: vi.fn(), warn: vi.fn() },
1020
1098
  runHistoryStore: history,
1021
1099
  persistSessionGoal,
@@ -1581,6 +1659,7 @@ describe('scheduled-tasks cross-process slot occupancy', () => {
1581
1659
  waitForOpenCodeReady: vi.fn(async () => {
1582
1660
  throw new Error('OpenCode unavailable');
1583
1661
  }),
1662
+ persistSessionMetadata: vi.fn(async () => ({})),
1584
1663
  logger: { info: vi.fn(), warn: vi.fn() },
1585
1664
  runHistoryStore: store,
1586
1665
  notifyTaskRun,
@@ -16,14 +16,23 @@ PATCH. Goal progress therefore belongs in the Host
16
16
  `packages/web/server/lib/session-metadata/` store (merge-patched under
17
17
  `openchamber.goal`), not an OpenCode `PATCH /session/:id`.
18
18
 
19
- `createSessionGoalRuntime` accepts optional seams `readSessionMetadata` and
20
- `persistSessionGoal`. When **both** are injected, `writeGoal` / goal reads use
21
- only the store and never PATCH OpenCode. When either is missing, the legacy
22
- OpenCode PATCH path remains (tests and unwired servers).
19
+ `createSessionGoalRuntime` accepts optional seams:
20
+
21
+ | Seam | Role |
22
+ |---|---|
23
+ | `readSessionMetadata` | Read Host metadata for the session |
24
+ | `persistSessionGoal` | Full goal replace (routes / scheduled create) |
25
+ | `mutateSessionMetadata` | Preferred conditional commit: `decide(committed) → { ok, patch }` on the store lock |
26
+
27
+ When `mutateSessionMetadata` is present, runtime goal commits use it exclusively
28
+ (no OpenCode PATCH). When only `readSessionMetadata` + `persistSessionGoal` are
29
+ present, the runtime falls back to read-then-persist. When neither store path is
30
+ wired, the legacy OpenCode PATCH path remains (tests / unwired servers).
23
31
 
24
32
  Those seams, the proxy overlay, and `PUT /api/openchamber/sessions/:id/metadata`
25
33
  are wired in `server/index.js`. A UI metadata write notifies the runtime with
26
- a synthetic `session.updated` so create/resume can arm the loop.
34
+ a synthetic `session.updated` so create/resume can arm the loop. Metadata PUT
35
+ normalizes `executionGeneration` for goal patches inside the store mutate lock.
27
36
 
28
37
  **Capability is open** (`supported === true`). Scheduled-task goal create and
29
38
  `POST /api/goals/:sessionId` persist an active goal into the same store as
@@ -42,14 +51,52 @@ synthetic `session.updated` so the loop can arm.
42
51
  tokensUsed, // tokensCommitted + current segment (snapshot - baseline)
43
52
  tokensBaseline, // segment start snapshot (pre-goal turn; 0 after compaction)
44
53
  tokensCommitted, // closed segments' total (one segment per compaction)
45
- turnsUsed, // auto-continuations sent (capped at MAX_AUTO_TURNS)
54
+ turnsUsed, // auto-continuation attempts committed (capped at MAX_AUTO_TURNS)
46
55
  blockedStreak, // consecutive blocked audit verdicts
47
56
  auditFailStreak, // consecutive failed/unavailable audit calls
48
57
  note, // latest audit progress note, <= 280 chars
49
58
  statusReason, // why settled; 'resumed' is a kickoff signal from UI
50
59
  lastAccountedMessageID, // incremental accounting cursor
60
+ executionGeneration, // execution permit generation (see pause boundary)
61
+ pendingContinuation, // durable admission (ticket 10); null when idle
51
62
  createdAt, updatedAt
52
63
  }
64
+
65
+ ### Durable continuation admission (`pendingContinuation`)
66
+
67
+ Minimal Host-metadata field (not a parallel store). Written only through the
68
+ same conditional goal owner as other goal commits:
69
+
70
+ ```
71
+ {
72
+ messageID, // msg_goalc_<goalId>_<generation>_<turnsUsed>
73
+ goalId, generation, turnsUsed,
74
+ phase, // reserved | transport | uncertain | accepted
75
+ text, // fixed prompt payload for legal same-id re-dispatch
76
+ providerID, modelID, variant?, agent?,
77
+ error?, at
78
+ }
79
+ ```
80
+
81
+ Rules:
82
+
83
+ 1. **Reserve before send** — persist fixed `messageID` + prompt/selection
84
+ identity with `phase: reserved` **without** bumping `turnsUsed`.
85
+ 2. **Accept commit** — `turnsUsed` increment and `pendingContinuation: null`
86
+ share one conditional write after upstream accept (or reconcile `found`).
87
+ 3. **Selection / pre-prompt drop** — pause or switchAgent/switchModel failure
88
+ clears reserved pending; **0 count**.
89
+ 4. **Uncertain transport** — keep durable identity; pause auto-continue with a
90
+ user-visible `statusReason` (reuses blocked/pause reason UI). Reconcile via
91
+ official `session.inbox.list` / `session.message.get` (or projected list);
92
+ fetch failure is **unavailable**, never authoritative empty.
93
+ 5. **Already received** — count once, clear pending; do not invent a new id.
94
+ 6. **pause → resume / goal replace** — reserved pending cleared on pause;
95
+ transport/uncertain/accepted identity retained (no claim of upstream revoke).
96
+ Stale `goalId` / superseded `generation` never overwrite newer user ops;
97
+ found-under-stale-gen still counts once.
98
+ 7. **Reboot** — restore pending from metadata, reconcile first, then same-id
99
+ fixed-payload re-dispatch only when still absent and generation matches.
53
100
  ```
54
101
 
55
102
  The UI writes goals (create/edit/pause/resume/clear) by patching this
@@ -62,8 +109,49 @@ content, since "Implement this plan: X" alone gives the audit nothing to
62
109
  judge against. The armed send also attaches a synthetic system-reminder
63
110
  part telling the agent goal mode is active and that each turn should end
64
111
  with a factual done/verified/remaining statement for the independent audit.
65
- Freshness/stale-write protection is by `id`: every runtime write re-reads the
66
- session and drops the write when the stored goal id no longer matches.
112
+
113
+ ### Execution generation and pause/commit boundary
114
+
115
+ `executionGeneration` is the durable execution permit. It starts at `0` on
116
+ create and advances on:
117
+
118
+ - pause (user, abort, question) while status was `active`
119
+ - resume (`paused`/`blocked`/… → `active`)
120
+ - execution-condition edits (`tokenBudget`, `objective`, `objectiveFile`)
121
+
122
+ Settle paths (`active` → `complete` / `blocked` / `budgetLimited`) keep the
123
+ same generation. Writers go through `withGoalExecutionGeneration` (metadata
124
+ PUT, `persistSessionGoal` Host wiring) or the runtime's `advanceGeneration`
125
+ flag so UI patches that omit the field still bump correctly.
126
+
127
+ Runtime commits (continue accounting, settle, pause) validate **goal id +
128
+ expected generation + applicable status** on one serial boundary when the
129
+ Host `mutateSessionMetadata` seam is wired. A late audit that returns
130
+ `continue` / `complete` / `blocked` after pause cannot overwrite the paused
131
+ snapshot or start a new `session.prompt` continuation.
132
+
133
+ Local (in-process) ordering:
134
+
135
+ 1. Pause / stop / `session.updated` paused → bump dispatch epoch + abort
136
+ in-flight audit controller + clear idle timer (**before** awaiting durable
137
+ write).
138
+ 2. Tick captures epoch at start; re-checks before continue commit and before
139
+ dispatch.
140
+ 3. `sendContinuation` re-validates **epoch + shutdown recovery + stopped** after
141
+ every await (`switchAgent` / `switchModel` / generation re-read) and again
142
+ immediately before `session.prompt`. It also re-reads durable goal
143
+ generation/status at the prompt boundary. A pause or shutdown that lands
144
+ during selection must drop with `uncertain: false` and **zero** prompt.
145
+ 4. Continuation identity `{ goalId, generation, turnsUsed }` and
146
+ `phase: transport` are recorded **only at the actual prompt boundary** —
147
+ not before selection. Selection failures (agent/model switch) must **not**
148
+ be reported as prompt-uncertain. Uncertain applies only after prompt left
149
+ the process and transport failed; the identity is kept for reconcile. The
150
+ runtime does **not** claim full revoke of a request already handed upstream.
151
+ Explicit stop of the upstream turn remains the existing abort/stop operation.
152
+
153
+ Public seams for tests / controlled async: `runTick`, `pauseGoal`,
154
+ `getDispatchEpoch`, `getDispatchedContinuation`.
67
155
 
68
156
  ## File-backed objectives
69
157
 
@@ -168,11 +256,23 @@ before touching the filesystem). Rationale: metadata rides every
168
256
  audit unavailable") — resumable, and settling resets the streak so
169
257
  Resume gets fresh tolerance. A dead small model can never drive the
170
258
  loop blind to the turn cap;
171
- - continue: persist accounting + `turnsUsed` first (a crash after the
172
- write just waits for the next idle tick; the reverse could double-send),
173
- re-check the tail, then `POST /session/:id/prompt_async` with the
174
- continuation prompt using the last assistant message's
175
- provider/model/agent — the goal spends the session's own subscription.
259
+ - continue (ticket 10 durable admission): persist accounting +
260
+ `pendingContinuation` (`phase: reserved`, fixed `msg_goalc_…` id +
261
+ prompt/selection payload) **without** bumping `turnsUsed`; re-check
262
+ tail; then `session.switchAgent` / `session.switchModel` +
263
+ `session.prompt` with that stable id. Accept → one conditional commit
264
+ of `turnsUsed + 1` and clear pending. Selection/pre-prompt pause or
265
+ failure clears reserved pending (0 count). Uncertain transport keeps
266
+ durable identity and pauses auto-continue with a readable
267
+ `statusReason`; next tick / reboot reconciles via inbox / exact
268
+ message (fail ≠ empty) before any new id. Already-received counts
269
+ once. Synthetic is metadata-only (`goalContinuation`).
270
+ - **Shutdown recovery (ticket 07):** `session.execution.interrupted` with
271
+ `reason: shutdown` clears the idle timer and sets a **local** recovery
272
+ pending marker (not a parallel durable store). Auto-continue / tick stay
273
+ gated until authoritative `session.status` idle or a non-shutdown
274
+ execution terminal clears the marker. Goal status itself is unchanged.
275
+
176
276
  4. Settling (`complete`/`blocked`/`budgetLimited`) fires the injected
177
277
  `emitGoalNotification` so the user hears about it even with the UI closed.
178
278
  The same settle also notifies a registered Assistant contact reporter
@@ -62,6 +62,8 @@ const buildActiveGoalRecord = ({
62
62
  note: '',
63
63
  statusReason: typeof statusReason === 'string' ? statusReason : '',
64
64
  lastAccountedMessageID: '',
65
+ // Fresh goal starts a new execution generation (0). Resume / pause bump it.
66
+ executionGeneration: 0,
65
67
  createdAt: now,
66
68
  updatedAt: now,
67
69
  };
@@ -204,10 +206,17 @@ export function registerSessionGoalRoutes(app, deps = {}) {
204
206
  }
205
207
 
206
208
  const now = Date.now();
209
+ const priorGeneration = Number.isFinite(currentGoal.executionGeneration)
210
+ && currentGoal.executionGeneration >= 0
211
+ ? Math.floor(currentGoal.executionGeneration)
212
+ : 0;
207
213
  const goal = {
208
214
  ...currentGoal,
209
215
  status: 'active',
210
216
  statusReason: 'resumed',
217
+ // Resume opens a new execution generation so late audits from the prior
218
+ // active period cannot commit continue/settle.
219
+ executionGeneration: priorGeneration + 1,
211
220
  updatedAt: now,
212
221
  };
213
222