@opengeni/react 3.3.2 → 3.4.0

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 (855) hide show
  1. package/README.md +769 -22
  2. package/dist/accounts.js +2 -0
  3. package/dist/accounts.js.map +1 -1
  4. package/dist/approvals.d.ts +2 -1
  5. package/dist/artifacts-document.js +5 -3
  6. package/dist/artifacts-presentation.js +5 -3
  7. package/dist/artifacts-spreadsheet.d.ts +1 -1
  8. package/dist/artifacts-spreadsheet.js +5 -3
  9. package/dist/artifacts.d.ts +5 -1
  10. package/dist/artifacts.js +536 -42
  11. package/dist/artifacts.js.map +1 -1
  12. package/dist/browser-viewer-ZWR6MWJ7.js +19 -0
  13. package/dist/capability-catalog-row.d.ts +20 -0
  14. package/dist/capability-logo-fallback.d.ts +3 -0
  15. package/dist/chat-interactive-block-4EUKJXUO.js +14 -0
  16. package/dist/chunk-2DDJ6YH6.js +41 -0
  17. package/dist/chunk-2DDJ6YH6.js.map +1 -0
  18. package/dist/chunk-2HVLQ26H.js +183 -0
  19. package/dist/chunk-2HVLQ26H.js.map +1 -0
  20. package/dist/{chunk-XYRDDSXF.js → chunk-2I6R3GCZ.js} +4285 -3980
  21. package/dist/chunk-2I6R3GCZ.js.map +1 -0
  22. package/dist/{chunk-JAQ5K7W6.js → chunk-2SFFYIH3.js} +1178 -166
  23. package/dist/chunk-2SFFYIH3.js.map +1 -0
  24. package/dist/chunk-3PVDYTTB.js +24 -0
  25. package/dist/chunk-3PVDYTTB.js.map +1 -0
  26. package/dist/chunk-3RJKUVCA.js +14068 -0
  27. package/dist/chunk-3RJKUVCA.js.map +1 -0
  28. package/dist/chunk-3UZCWK46.js +298 -0
  29. package/dist/chunk-3UZCWK46.js.map +1 -0
  30. package/dist/chunk-47CKUXTW.js +293 -0
  31. package/dist/chunk-47CKUXTW.js.map +1 -0
  32. package/dist/chunk-4QYP6QUI.js +109 -0
  33. package/dist/chunk-4QYP6QUI.js.map +1 -0
  34. package/dist/chunk-5EY26VZ7.js +1053 -0
  35. package/dist/chunk-5EY26VZ7.js.map +1 -0
  36. package/dist/chunk-5I4VRWBB.js +56 -0
  37. package/dist/chunk-5I4VRWBB.js.map +1 -0
  38. package/dist/chunk-5WOR7UCL.js +191 -0
  39. package/dist/chunk-5WOR7UCL.js.map +1 -0
  40. package/dist/chunk-5YJLGOM5.js +51 -0
  41. package/dist/chunk-5YJLGOM5.js.map +1 -0
  42. package/dist/chunk-6JFN3OWG.js +386 -0
  43. package/dist/chunk-6JFN3OWG.js.map +1 -0
  44. package/dist/chunk-7QJT5O4S.js +35 -0
  45. package/dist/chunk-7QJT5O4S.js.map +1 -0
  46. package/dist/chunk-A55KV72M.js +402 -0
  47. package/dist/chunk-A55KV72M.js.map +1 -0
  48. package/dist/chunk-ADWCLI4G.js +2028 -0
  49. package/dist/chunk-ADWCLI4G.js.map +1 -0
  50. package/dist/chunk-B27E3V37.js +29 -0
  51. package/dist/chunk-B27E3V37.js.map +1 -0
  52. package/dist/chunk-B7TC2A33.js +83 -0
  53. package/dist/chunk-B7TC2A33.js.map +1 -0
  54. package/dist/chunk-BLL56F7R.js +385 -0
  55. package/dist/chunk-BLL56F7R.js.map +1 -0
  56. package/dist/chunk-BOFEQC6K.js +162 -0
  57. package/dist/chunk-BOFEQC6K.js.map +1 -0
  58. package/dist/chunk-CM7LVUKC.js +35 -0
  59. package/dist/chunk-CM7LVUKC.js.map +1 -0
  60. package/dist/chunk-CORUP6JB.js +122 -0
  61. package/dist/chunk-CORUP6JB.js.map +1 -0
  62. package/dist/chunk-CSX5FFKZ.js +35 -0
  63. package/dist/chunk-CSX5FFKZ.js.map +1 -0
  64. package/dist/chunk-EGVHJKGJ.js +3125 -0
  65. package/dist/chunk-EGVHJKGJ.js.map +1 -0
  66. package/dist/{chunk-NEXLLTP4.js → chunk-G76LSA76.js} +6 -5
  67. package/dist/chunk-G76LSA76.js.map +1 -0
  68. package/dist/{chunk-PEI6RANI.js → chunk-GQMRL52F.js} +311 -75
  69. package/dist/chunk-GQMRL52F.js.map +1 -0
  70. package/dist/{chunk-ELIYWBZR.js → chunk-GUUXOISA.js} +1113 -457
  71. package/dist/chunk-GUUXOISA.js.map +1 -0
  72. package/dist/{chunk-HUIKIDM5.js → chunk-I7X2P4FU.js} +225 -116
  73. package/dist/chunk-I7X2P4FU.js.map +1 -0
  74. package/dist/chunk-IGFZFLI2.js +1390 -0
  75. package/dist/chunk-IGFZFLI2.js.map +1 -0
  76. package/dist/{chunk-4MDLEHXL.js → chunk-IMSUVYQB.js} +26 -13
  77. package/dist/chunk-IMSUVYQB.js.map +1 -0
  78. package/dist/chunk-IOYHDEP3.js +38 -0
  79. package/dist/chunk-IOYHDEP3.js.map +1 -0
  80. package/dist/chunk-IZV5FTJC.js +55 -0
  81. package/dist/chunk-IZV5FTJC.js.map +1 -0
  82. package/dist/chunk-KF33GTWN.js +343 -0
  83. package/dist/chunk-KF33GTWN.js.map +1 -0
  84. package/dist/chunk-KSGQ5ODA.js +457 -0
  85. package/dist/chunk-KSGQ5ODA.js.map +1 -0
  86. package/dist/{chunk-ZPBTI3TE.js → chunk-KZC6QRNR.js} +1513 -697
  87. package/dist/chunk-KZC6QRNR.js.map +1 -0
  88. package/dist/chunk-LJUMYUBO.js +218 -0
  89. package/dist/chunk-LJUMYUBO.js.map +1 -0
  90. package/dist/chunk-LSW2OJVV.js +380 -0
  91. package/dist/chunk-LSW2OJVV.js.map +1 -0
  92. package/dist/chunk-M357MHH3.js +2 -0
  93. package/dist/chunk-M357MHH3.js.map +1 -0
  94. package/dist/chunk-MK2TQO4Y.js +24 -0
  95. package/dist/chunk-MK2TQO4Y.js.map +1 -0
  96. package/dist/chunk-N7S72EXK.js +25 -0
  97. package/dist/chunk-N7S72EXK.js.map +1 -0
  98. package/dist/{chunk-YVCHAQZY.js → chunk-NKMYZFHA.js} +616 -621
  99. package/dist/chunk-NKMYZFHA.js.map +1 -0
  100. package/dist/chunk-NX4ADH73.js +14 -0
  101. package/dist/chunk-NX4ADH73.js.map +1 -0
  102. package/dist/chunk-O7F6UJO4.js +71 -0
  103. package/dist/chunk-O7F6UJO4.js.map +1 -0
  104. package/dist/chunk-OYQQJL3J.js +16 -0
  105. package/dist/chunk-OYQQJL3J.js.map +1 -0
  106. package/dist/{chunk-H5YGYNXS.js → chunk-P4BNUO57.js} +60 -17
  107. package/dist/chunk-P4BNUO57.js.map +1 -0
  108. package/dist/{chunk-JVYQRRC4.js → chunk-PEHTX3V7.js} +21 -1
  109. package/dist/chunk-PEHTX3V7.js.map +1 -0
  110. package/dist/chunk-PMV53IMM.js +26 -0
  111. package/dist/chunk-PMV53IMM.js.map +1 -0
  112. package/dist/{chunk-KCGXPP5V.js → chunk-PYEOPA4M.js} +13 -7
  113. package/dist/chunk-PYEOPA4M.js.map +1 -0
  114. package/dist/{chunk-KZ73RL76.js → chunk-QHUE7E4N.js} +8 -3
  115. package/dist/chunk-QHUE7E4N.js.map +1 -0
  116. package/dist/chunk-QV3DJVRZ.js +11 -0
  117. package/dist/chunk-QV3DJVRZ.js.map +1 -0
  118. package/dist/chunk-R5SBNG7S.js +810 -0
  119. package/dist/chunk-R5SBNG7S.js.map +1 -0
  120. package/dist/chunk-RI42H5IU.js +326 -0
  121. package/dist/chunk-RI42H5IU.js.map +1 -0
  122. package/dist/chunk-S2QH6EZ5.js +211 -0
  123. package/dist/chunk-S2QH6EZ5.js.map +1 -0
  124. package/dist/chunk-SHLZMAJN.js +304 -0
  125. package/dist/chunk-SHLZMAJN.js.map +1 -0
  126. package/dist/chunk-TGXK4BAZ.js +293 -0
  127. package/dist/chunk-TGXK4BAZ.js.map +1 -0
  128. package/dist/{chunk-5YK2ZDIG.js → chunk-TNEZNMSN.js} +4 -10
  129. package/dist/chunk-TNEZNMSN.js.map +1 -0
  130. package/dist/{chunk-22KP6LR5.js → chunk-TXS65BB2.js} +3 -1
  131. package/dist/{chunk-22KP6LR5.js.map → chunk-TXS65BB2.js.map} +1 -1
  132. package/dist/chunk-UNJPZGNK.js +63 -0
  133. package/dist/chunk-UNJPZGNK.js.map +1 -0
  134. package/dist/{chunk-IOLVN74D.js → chunk-V5ZUJT2T.js} +175 -42
  135. package/dist/chunk-V5ZUJT2T.js.map +1 -0
  136. package/dist/chunk-WBJY57A3.js +402 -0
  137. package/dist/chunk-WBJY57A3.js.map +1 -0
  138. package/dist/chunk-WIUKB3LD.js +1911 -0
  139. package/dist/chunk-WIUKB3LD.js.map +1 -0
  140. package/dist/{chunk-ICA6R6TW.js → chunk-WNN7NO2N.js} +1451 -355
  141. package/dist/chunk-WNN7NO2N.js.map +1 -0
  142. package/dist/{chunk-3AGBGTRT.js → chunk-X5UVUIMF.js} +1712 -1174
  143. package/dist/chunk-X5UVUIMF.js.map +1 -0
  144. package/dist/chunk-XDA5QJE7.js +402 -0
  145. package/dist/chunk-XDA5QJE7.js.map +1 -0
  146. package/dist/{chunk-VZTQ73XT.js → chunk-XEA5BCMA.js} +3 -1
  147. package/dist/{chunk-VZTQ73XT.js.map → chunk-XEA5BCMA.js.map} +1 -1
  148. package/dist/chunk-XHF7OYAN.js +1150 -0
  149. package/dist/chunk-XHF7OYAN.js.map +1 -0
  150. package/dist/chunk-YSRBFJMQ.js +43 -0
  151. package/dist/chunk-YSRBFJMQ.js.map +1 -0
  152. package/dist/chunk-ZOZAPAUO.js +174 -0
  153. package/dist/chunk-ZOZAPAUO.js.map +1 -0
  154. package/dist/chunk-ZQV5W7YC.js +809 -0
  155. package/dist/chunk-ZQV5W7YC.js.map +1 -0
  156. package/dist/chunk-ZUWF4NF7.js +13 -0
  157. package/dist/chunk-ZUWF4NF7.js.map +1 -0
  158. package/dist/client.d.ts +16 -7
  159. package/dist/clipboard.js +3 -1
  160. package/dist/commands/types.d.ts +2 -0
  161. package/dist/components/anthropic-mark.d.ts +3 -0
  162. package/dist/components/approval-surface.d.ts +19 -6
  163. package/dist/components/artifacts/artifact-chrome.d.ts +120 -0
  164. package/dist/components/artifacts/artifact-sandbox.d.ts +30 -0
  165. package/dist/components/artifacts/artifact-surface.d.ts +4 -2
  166. package/dist/components/artifacts/chat-interactive-block.d.ts +29 -0
  167. package/dist/components/artifacts/deferred-chat-media.d.ts +15 -0
  168. package/dist/components/artifacts/document-editor.d.ts +4 -2
  169. package/dist/components/artifacts/editable-artifact-ui.d.ts +2 -1
  170. package/dist/components/artifacts/editable-artifact-view.d.ts +48 -0
  171. package/dist/components/artifacts/editable-document.d.ts +1 -1
  172. package/dist/components/artifacts/editable-presentation.d.ts +2 -2
  173. package/dist/components/artifacts/editable-spreadsheet.d.ts +8 -3
  174. package/dist/components/artifacts/index.d.ts +9 -2
  175. package/dist/components/artifacts/inline-html-document.d.ts +2 -0
  176. package/dist/components/artifacts/latest-task-runner.d.ts +15 -0
  177. package/dist/components/artifacts/presentation-editor.d.ts +6 -2
  178. package/dist/components/artifacts/published-html-artifact-frame.d.ts +23 -3
  179. package/dist/components/artifacts/session-artifact-viewer.d.ts +39 -0
  180. package/dist/components/artifacts/site-view.d.ts +26 -0
  181. package/dist/components/artifacts/spreadsheet-command-queue.d.ts +20 -0
  182. package/dist/components/artifacts/spreadsheet-download.d.ts +7 -0
  183. package/dist/components/artifacts/spreadsheet-general-display.d.ts +24 -0
  184. package/dist/components/artifacts/spreadsheet-grid.d.ts +10 -3
  185. package/dist/components/artifacts/spreadsheet-resize.d.ts +50 -0
  186. package/dist/components/artifacts/spreadsheet-submissions.d.ts +9 -0
  187. package/dist/components/artifacts/visualization-assets.gen.d.ts +2 -0
  188. package/dist/components/browser-select-control.d.ts +16 -0
  189. package/dist/components/browser-viewer.d.ts +11 -2
  190. package/dist/components/chat-composer.d.ts +21 -3
  191. package/dist/components/child-session-link.d.ts +9 -0
  192. package/dist/components/claude-mark.d.ts +3 -0
  193. package/dist/components/code-editor.d.ts +1 -4
  194. package/dist/components/composer-transcription-control.d.ts +7 -0
  195. package/dist/components/composer.d.ts +34 -67
  196. package/dist/components/computer-viewer.d.ts +1 -1
  197. package/dist/components/copy-button.d.ts +3 -1
  198. package/dist/components/desktop-viewer.d.ts +1 -1
  199. package/dist/components/grok-mark.d.ts +26 -0
  200. package/dist/components/human-input-form.d.ts +28 -32
  201. package/dist/components/human-input-surface.d.ts +7 -2
  202. package/dist/components/linkified-text.d.ts +18 -0
  203. package/dist/components/machine-input-display.d.ts +22 -1
  204. package/dist/components/machines/agent-status-icon.d.ts +6 -0
  205. package/dist/components/machines/machine-detail.d.ts +7 -1
  206. package/dist/components/machines/machine-status.d.ts +48 -0
  207. package/dist/components/markdown-table-layout.d.ts +18 -0
  208. package/dist/components/markdown.d.ts +37 -4
  209. package/dist/components/message-timeline.d.ts +86 -7
  210. package/dist/components/model-mark.d.ts +29 -0
  211. package/dist/components/model-policy-picker-menu.d.ts +9 -0
  212. package/dist/components/model-policy-picker.d.ts +35 -28
  213. package/dist/components/new-conversation.d.ts +32 -0
  214. package/dist/components/open-geni-chat.d.ts +73 -0
  215. package/dist/components/open-geni-links.d.ts +68 -0
  216. package/dist/components/openrouter-mark.d.ts +6 -0
  217. package/dist/components/opper-mark.d.ts +7 -0
  218. package/dist/components/preview-loading.d.ts +2 -0
  219. package/dist/components/provider-recovery-notice.d.ts +16 -0
  220. package/dist/components/queue-item-content.d.ts +9 -0
  221. package/dist/components/rehype-secret-refs.d.ts +23 -0
  222. package/dist/components/remark-soft-line-breaks.d.ts +38 -0
  223. package/dist/components/sandbox-files.d.ts +3 -1
  224. package/dist/components/sandbox-workspace.d.ts +18 -0
  225. package/dist/components/secret-question-field.d.ts +31 -0
  226. package/dist/components/secret-ref-chip.d.ts +35 -0
  227. package/dist/components/session-capability-frame.d.ts +37 -0
  228. package/dist/components/session-chrome.d.ts +57 -8
  229. package/dist/components/session-commands-panel.d.ts +6 -0
  230. package/dist/components/session-commands.d.ts +6 -0
  231. package/dist/components/session-connection-request.d.ts +8 -0
  232. package/dist/components/session-conversation.d.ts +157 -0
  233. package/dist/components/session-list.d.ts +39 -0
  234. package/dist/components/session-mcp-capability-card.d.ts +41 -0
  235. package/dist/components/session-proxy-scope.d.ts +35 -0
  236. package/dist/components/session-status.d.ts +4 -1
  237. package/dist/components/session-usage-limit-notice.d.ts +39 -0
  238. package/dist/components/timeline-anchor.d.ts +24 -0
  239. package/dist/components/timeline-annotation-chrome.d.ts +25 -0
  240. package/dist/components/timeline-annotation-layout.d.ts +42 -0
  241. package/dist/components/timeline-annotation-markers.d.ts +8 -0
  242. package/dist/components/timeline-annotation-reveal-context.d.ts +4 -0
  243. package/dist/components/timeline-annotation-shared.d.ts +31 -0
  244. package/dist/components/timeline-annotations-dialog.d.ts +3 -2
  245. package/dist/components/timeline-annotations.d.ts +13 -3
  246. package/dist/components/timeline-reading-progress.d.ts +11 -0
  247. package/dist/components/timeline-search.d.ts +34 -0
  248. package/dist/components/timeline-settlement.d.ts +16 -0
  249. package/dist/components/tool-action-review.d.ts +47 -0
  250. package/dist/components/tool-review-history.d.ts +25 -0
  251. package/dist/components/usage-limit-notice.d.ts +49 -0
  252. package/dist/components/usage-member-list.d.ts +91 -0
  253. package/dist/components/usage-meter.d.ts +73 -0
  254. package/dist/components/use-viewer-menu-dismiss.d.ts +2 -0
  255. package/dist/components/user-message-body.d.ts +10 -1
  256. package/dist/composer-messages.d.ts +74 -0
  257. package/dist/composer-messages.js +9 -0
  258. package/dist/composer-messages.js.map +1 -0
  259. package/dist/composer-voice-events.d.ts +8 -0
  260. package/dist/composer.d.ts +5 -4
  261. package/dist/composer.js +26 -13
  262. package/dist/computer-viewer-GMLDXPON.js +20 -0
  263. package/dist/computer-viewer-GMLDXPON.js.map +1 -0
  264. package/dist/connect-account-identity.d.ts +8 -0
  265. package/dist/connect-accounts.d.ts +15 -0
  266. package/dist/connect-chooser.d.ts +18 -0
  267. package/dist/connect-panel.d.ts +18 -0
  268. package/dist/connect-setup.d.ts +18 -0
  269. package/dist/connect.d.ts +21 -0
  270. package/dist/connect.js +2149 -0
  271. package/dist/connect.js.map +1 -0
  272. package/dist/connection-catalog.d.ts +38 -0
  273. package/dist/connection-discovery.d.ts +12 -0
  274. package/dist/connection-installed.d.ts +17 -0
  275. package/dist/connection-logo.d.ts +9 -0
  276. package/dist/connection-service-logo.d.ts +8 -0
  277. package/dist/connection-service-presentation.d.ts +13 -0
  278. package/dist/connection-skeleton.d.ts +5 -0
  279. package/dist/connection-type-picker.d.ts +6 -0
  280. package/dist/conversation-timeline.d.ts +5 -0
  281. package/dist/desktop.d.ts +7 -0
  282. package/dist/desktop.js +27 -0
  283. package/dist/desktop.js.map +1 -0
  284. package/dist/device-authorization.d.ts +23 -0
  285. package/dist/diffs.d.ts +8 -0
  286. package/dist/diffs.js +15 -0
  287. package/dist/diffs.js.map +1 -0
  288. package/dist/discovery-cache.d.ts +5 -0
  289. package/dist/editor.d.ts +7 -0
  290. package/dist/editor.js +22 -0
  291. package/dist/editor.js.map +1 -0
  292. package/dist/embedded-new-voice-OCRNT6U5.js +18 -0
  293. package/dist/embedded-new-voice-OCRNT6U5.js.map +1 -0
  294. package/dist/embedded-voice-RPHPYVBT.js +18 -0
  295. package/dist/embedded-voice-RPHPYVBT.js.map +1 -0
  296. package/dist/{fleet-decision-row-GGCAT4E4.js → fleet-decision-row-SI63O5V4.js} +4 -1
  297. package/dist/fleet-decision-row-SI63O5V4.js.map +1 -0
  298. package/dist/hooks/latest-question.d.ts +25 -0
  299. package/dist/hooks/shared-machines-list.d.ts +4 -0
  300. package/dist/hooks/shared-query.d.ts +113 -0
  301. package/dist/hooks/use-attached-browsers.d.ts +1 -1
  302. package/dist/hooks/use-auth-runs.d.ts +1 -4
  303. package/dist/hooks/use-available-models.d.ts +1 -1
  304. package/dist/hooks/use-browser-frame-stream.d.ts +1 -1
  305. package/dist/hooks/use-browser-session.d.ts +12 -1
  306. package/dist/hooks/use-client-config-flags.d.ts +49 -0
  307. package/dist/hooks/use-codex-accounts.d.ts +59 -10
  308. package/dist/hooks/use-composer.d.ts +24 -2
  309. package/dist/hooks/use-computer-frame-stream.d.ts +1 -1
  310. package/dist/hooks/use-computer-input-posture.d.ts +13 -0
  311. package/dist/hooks/use-computer-session.d.ts +4 -0
  312. package/dist/hooks/use-connect.d.ts +23 -0
  313. package/dist/hooks/use-desktop-stream.d.ts +1 -1
  314. package/dist/hooks/use-file-attachments.d.ts +12 -0
  315. package/dist/hooks/use-goal.d.ts +2 -1
  316. package/dist/hooks/use-last-started-turn-policy.d.ts +5 -1
  317. package/dist/hooks/use-new-conversation.d.ts +56 -0
  318. package/dist/hooks/use-new-session-draft.d.ts +74 -0
  319. package/dist/hooks/use-realtime-voice-models.d.ts +7 -0
  320. package/dist/hooks/use-sandbox-files.d.ts +4 -1
  321. package/dist/hooks/use-session-conversation.d.ts +49 -0
  322. package/dist/hooks/use-session-events.d.ts +26 -5
  323. package/dist/hooks/use-session-mcp-approval-policy.d.ts +2 -2
  324. package/dist/hooks/use-session-model-account.d.ts +48 -0
  325. package/dist/hooks/use-supergrok-accounts.d.ts +7 -1
  326. package/dist/hooks/use-terminal-stream.d.ts +11 -5
  327. package/dist/hooks/use-usage.d.ts +40 -0
  328. package/dist/hooks/use-vaults.d.ts +53 -0
  329. package/dist/hooks/use-voice-input.d.ts +8 -1
  330. package/dist/hooks/use-workspace-edit.d.ts +4 -0
  331. package/dist/hooks/use-workspace-sessions.d.ts +24 -5
  332. package/dist/human-input-model.d.ts +122 -0
  333. package/dist/identity-link-accounts.d.ts +11 -0
  334. package/dist/identity-link-consent.d.ts +18 -0
  335. package/dist/index.d.ts +82 -20
  336. package/dist/index.js +2340 -6603
  337. package/dist/index.js.map +1 -1
  338. package/dist/interaction.d.ts +1 -1
  339. package/dist/interaction.js +14 -24
  340. package/dist/interaction.js.map +1 -1
  341. package/dist/latest-question-R77KWSUP.js +118 -0
  342. package/dist/latest-question-R77KWSUP.js.map +1 -0
  343. package/dist/lib/clipboard.d.ts +7 -0
  344. package/dist/lib/error-message.d.ts +7 -0
  345. package/dist/lib/format.d.ts +58 -4
  346. package/dist/lib/host-theme.d.ts +77 -0
  347. package/dist/lib/interaction-errors.d.ts +3 -0
  348. package/dist/lib/menu-styles.d.ts +22 -0
  349. package/dist/lib/notify-observer.d.ts +2 -0
  350. package/dist/lib/pierre-diffs-loader.d.ts +20 -0
  351. package/dist/lib/provider-recovery.d.ts +55 -0
  352. package/dist/lib/scrollbars.d.ts +11 -0
  353. package/dist/lib/terminal-input-readiness.d.ts +11 -0
  354. package/dist/lib/transient-retry.d.ts +25 -0
  355. package/dist/lib/workbench-peers.d.ts +36 -0
  356. package/dist/lib/xterm-theme.d.ts +7 -5
  357. package/dist/machines.js +9 -5
  358. package/dist/markdown-table-layout-3PV2UAAH.js +113 -0
  359. package/dist/markdown-table-layout-3PV2UAAH.js.map +1 -0
  360. package/dist/mcp-connection-status.d.ts +14 -0
  361. package/dist/model-mark-paths.d.ts +34 -0
  362. package/dist/model-mark-paths.js +16 -0
  363. package/dist/model-mark-paths.js.map +1 -0
  364. package/dist/model-picker-model.d.ts +111 -0
  365. package/dist/model-picker-model.js +25 -0
  366. package/dist/model-picker-model.js.map +1 -0
  367. package/dist/model-policy-picker-menu-7GJ5URPR.js +17 -0
  368. package/dist/model-policy-picker-menu-7GJ5URPR.js.map +1 -0
  369. package/dist/model-policy.d.ts +30 -3
  370. package/dist/model-policy.js +14 -1
  371. package/dist/native-previews.d.ts +28 -0
  372. package/dist/native-previews.js +82 -0
  373. package/dist/native-previews.js.map +1 -0
  374. package/dist/new-session-draft.d.ts +7 -0
  375. package/dist/new-session-draft.js +639 -0
  376. package/dist/new-session-draft.js.map +1 -0
  377. package/dist/notification-text.d.ts +27 -0
  378. package/dist/{noto-sans-arabic-loader-IA2T4X2A.js → noto-sans-arabic-loader-XSH2KYWJ.js} +3 -1
  379. package/dist/{noto-sans-arabic-loader-IA2T4X2A.js.map → noto-sans-arabic-loader-XSH2KYWJ.js.map} +1 -1
  380. package/dist/{noto-sans-jp-loader.generated-HFGLYBR3.js → noto-sans-jp-loader.generated-MR5L6V7K.js} +3 -1
  381. package/dist/{noto-sans-jp-loader.generated-HFGLYBR3.js.map → noto-sans-jp-loader.generated-MR5L6V7K.js.map} +1 -1
  382. package/dist/older-history.d.ts +3 -2
  383. package/dist/organization-model.d.ts +32 -0
  384. package/dist/organization-model.js +65 -0
  385. package/dist/organization-model.js.map +1 -0
  386. package/dist/{platform-activity-row-BJHUHXAT.js → platform-activity-row-5OCKOQWZ.js} +16 -78
  387. package/dist/platform-activity-row-5OCKOQWZ.js.map +1 -0
  388. package/dist/plugin-details.d.ts +14 -0
  389. package/dist/plugin-discovery.d.ts +25 -0
  390. package/dist/preview-loading-paint.d.ts +20 -0
  391. package/dist/provider.d.ts +25 -2
  392. package/dist/queue-presentation-model.d.ts +72 -0
  393. package/dist/queue-presentation-model.js +38 -0
  394. package/dist/queue-presentation-model.js.map +1 -0
  395. package/dist/{queue-surface-implementation-BSKUWHRI.js → queue-surface-implementation-GJBH7NKO.js} +65 -265
  396. package/dist/queue-surface-implementation-GJBH7NKO.js.map +1 -0
  397. package/dist/realtime/embedded-new-voice.d.ts +1 -0
  398. package/dist/realtime/embedded-voice.d.ts +1 -0
  399. package/dist/realtime/realtime-control.d.ts +4 -65
  400. package/dist/realtime/session-realtime.d.ts +78 -0
  401. package/dist/realtime.js +25 -1333
  402. package/dist/realtime.js.map +1 -1
  403. package/dist/sandbox-files-O27IPOZW.js +21 -0
  404. package/dist/sandbox-files-O27IPOZW.js.map +1 -0
  405. package/dist/sandbox-label-model.d.ts +30 -0
  406. package/dist/sandbox-label-model.js +20 -0
  407. package/dist/sandbox-label-model.js.map +1 -0
  408. package/dist/scrollbars.d.ts +1 -0
  409. package/dist/scrollbars.js +10 -0
  410. package/dist/scrollbars.js.map +1 -0
  411. package/dist/secret-reference-label.d.ts +14 -0
  412. package/dist/secrets-ui.d.ts +57 -0
  413. package/dist/session-agents-model.d.ts +10 -0
  414. package/dist/session-agents-model.js +25 -0
  415. package/dist/session-agents-model.js.map +1 -0
  416. package/dist/session-attention-model.d.ts +8 -0
  417. package/dist/session-attention-model.js +47 -0
  418. package/dist/session-attention-model.js.map +1 -0
  419. package/dist/session-auth-recommendation.d.ts +5 -0
  420. package/dist/session-capability-policy.d.ts +31 -0
  421. package/dist/session-feedback-model.d.ts +20 -0
  422. package/dist/session-feedback-model.js +56 -0
  423. package/dist/session-feedback-model.js.map +1 -0
  424. package/dist/session-list-model.d.ts +138 -0
  425. package/dist/session-list-model.js +274 -0
  426. package/dist/session-list-model.js.map +1 -0
  427. package/dist/session-realtime-7DJOIHQW.js +21 -0
  428. package/dist/session-realtime-7DJOIHQW.js.map +1 -0
  429. package/dist/session-realtime.d.ts +7 -0
  430. package/dist/session-realtime.js +19 -0
  431. package/dist/session-realtime.js.map +1 -0
  432. package/dist/session-status-model.d.ts +36 -0
  433. package/dist/session-ui.d.ts +39 -1
  434. package/dist/session-ui.js +126 -12
  435. package/dist/session-ui.js.map +1 -1
  436. package/dist/session.d.ts +8 -2
  437. package/dist/session.js +35 -20
  438. package/dist/site-snapshot.d.ts +14 -0
  439. package/dist/sites-ui.d.ts +24 -0
  440. package/dist/sites.d.ts +3 -0
  441. package/dist/sites.js +14 -0
  442. package/dist/sites.js.map +1 -0
  443. package/dist/skill-discovery.d.ts +47 -0
  444. package/dist/terminal.d.ts +8 -0
  445. package/dist/terminal.js +32 -0
  446. package/dist/terminal.js.map +1 -0
  447. package/dist/testing/session-scenarios.d.ts +10 -0
  448. package/dist/testing/session-scenarios.js +367 -0
  449. package/dist/testing/session-scenarios.js.map +1 -0
  450. package/dist/timeline/activity-rail.d.ts +11 -2
  451. package/dist/timeline/agent-identity.d.ts +30 -0
  452. package/dist/timeline/agent-row.d.ts +31 -0
  453. package/dist/timeline/allowance-exhausted-row.d.ts +24 -0
  454. package/dist/timeline/compaction-copy.d.ts +46 -0
  455. package/dist/timeline/focus-sequence.d.ts +9 -0
  456. package/dist/timeline/genie-copy.d.ts +3 -0
  457. package/dist/timeline/genie-loading.d.ts +47 -0
  458. package/dist/timeline/index.d.ts +10 -5
  459. package/dist/timeline/knowledge-receipt.d.ts +17 -0
  460. package/dist/timeline/message-sender.d.ts +19 -0
  461. package/dist/timeline/notice-presentation.d.ts +13 -0
  462. package/dist/timeline/parsers.d.ts +11 -0
  463. package/dist/timeline/patch-apply-command.d.ts +18 -0
  464. package/dist/timeline/platform-activity-presentation.d.ts +21 -0
  465. package/dist/timeline/platform-activity-row.d.ts +1 -2
  466. package/dist/timeline/presented-image.d.ts +11 -0
  467. package/dist/timeline/projection.d.ts +31 -2
  468. package/dist/timeline/provider-logo.d.ts +15 -0
  469. package/dist/timeline/question-nav-model.d.ts +23 -0
  470. package/dist/timeline/registry.d.ts +12 -1
  471. package/dist/timeline/retained-image.d.ts +18 -0
  472. package/dist/timeline/retained-loaders.d.ts +27 -0
  473. package/dist/timeline/rolling-activity.d.ts +11 -0
  474. package/dist/timeline/screenshot-lightbox.d.ts +23 -1
  475. package/dist/timeline/session-usage-limit-row.d.ts +11 -0
  476. package/dist/timeline/shared.d.ts +4 -1
  477. package/dist/timeline/startup-dispatch-details.d.ts +5 -0
  478. package/dist/timeline/startup-preference.d.ts +3 -0
  479. package/dist/timeline/startup-timings.d.ts +4 -0
  480. package/dist/timeline/tool-display-name.d.ts +5 -2
  481. package/dist/timeline/tool-presentation.d.ts +172 -0
  482. package/dist/timeline/turn-summary-model.d.ts +95 -0
  483. package/dist/timeline/turn-summary.d.ts +20 -54
  484. package/dist/timeline/types.d.ts +179 -7
  485. package/dist/timeline/work-presentation.d.ts +60 -0
  486. package/dist/timeline-annotation-markers-U3G66KUD.js +166 -0
  487. package/dist/timeline-annotation-markers-U3G66KUD.js.map +1 -0
  488. package/dist/timeline-annotation-selection-BI7QTPTZ.js +242 -0
  489. package/dist/timeline-annotation-selection-BI7QTPTZ.js.map +1 -0
  490. package/dist/timeline-annotations-dialog-FOMHSDTE.js +274 -0
  491. package/dist/timeline-annotations-dialog-FOMHSDTE.js.map +1 -0
  492. package/dist/timeline-model.d.ts +29 -0
  493. package/dist/timeline-model.js +254 -0
  494. package/dist/timeline-model.js.map +1 -0
  495. package/dist/usage/allowance-copy.d.ts +25 -0
  496. package/dist/usage/session-usage-limit.d.ts +88 -0
  497. package/dist/usage/summary.d.ts +35 -0
  498. package/dist/usage.d.ts +17 -0
  499. package/dist/usage.js +1187 -0
  500. package/dist/usage.js.map +1 -0
  501. package/dist/voice-recording-owner.d.ts +7 -0
  502. package/package.json +106 -7
  503. package/src/approvals.ts +8 -1
  504. package/src/artifacts-spreadsheet.ts +1 -0
  505. package/src/artifacts.ts +44 -0
  506. package/src/assets/README.md +8 -0
  507. package/src/assets/gmail.ico +0 -0
  508. package/src/assets/outlook.ico +0 -0
  509. package/src/capability-catalog-row.tsx +94 -0
  510. package/src/capability-logo-fallback.ts +22 -0
  511. package/src/client.ts +20 -31
  512. package/src/commands/registry.ts +18 -15
  513. package/src/commands/types.ts +2 -0
  514. package/src/components/anthropic-mark.tsx +10 -0
  515. package/src/components/approval-surface.tsx +401 -138
  516. package/src/components/artifacts/artifact-chrome.tsx +413 -0
  517. package/src/components/artifacts/artifact-sandbox.tsx +193 -0
  518. package/src/components/artifacts/artifact-surface.tsx +40 -20
  519. package/src/components/artifacts/chat-interactive-block.tsx +260 -0
  520. package/src/components/artifacts/deferred-chat-media.tsx +67 -0
  521. package/src/components/artifacts/document-editor.tsx +141 -16
  522. package/src/components/artifacts/editable-artifact-ui.tsx +4 -2
  523. package/src/components/artifacts/editable-artifact-view.tsx +197 -0
  524. package/src/components/artifacts/editable-artifact-workbench.tsx +5 -2
  525. package/src/components/artifacts/editable-document.tsx +37 -36
  526. package/src/components/artifacts/editable-presentation.tsx +36 -18
  527. package/src/components/artifacts/editable-spreadsheet.tsx +543 -103
  528. package/src/components/artifacts/index.ts +45 -0
  529. package/src/components/artifacts/inline-html-document.ts +24 -0
  530. package/src/components/artifacts/latest-task-runner.ts +49 -0
  531. package/src/components/artifacts/presentation-editor.tsx +323 -67
  532. package/src/components/artifacts/published-html-artifact-frame.tsx +122 -5
  533. package/src/components/artifacts/session-artifact-viewer.tsx +378 -0
  534. package/src/components/artifacts/site-view.tsx +121 -0
  535. package/src/components/artifacts/spreadsheet-canvas.ts +5 -10
  536. package/src/components/artifacts/spreadsheet-command-queue.ts +184 -0
  537. package/src/components/artifacts/spreadsheet-download.tsx +109 -0
  538. package/src/components/artifacts/spreadsheet-general-display.ts +43 -0
  539. package/src/components/artifacts/spreadsheet-grid.tsx +344 -144
  540. package/src/components/artifacts/spreadsheet-resize.tsx +421 -0
  541. package/src/components/artifacts/spreadsheet-submissions.tsx +46 -0
  542. package/src/components/artifacts/visualization-assets/base.css +1015 -0
  543. package/src/components/artifacts/visualization-assets/helpers.html +349 -0
  544. package/src/components/artifacts/visualization-assets.gen.ts +3 -0
  545. package/src/components/browser-select-control.tsx +312 -0
  546. package/src/components/browser-viewer.tsx +778 -318
  547. package/src/components/chat-composer.tsx +47 -5
  548. package/src/components/child-session-link.tsx +36 -0
  549. package/src/components/claude-mark.tsx +11 -0
  550. package/src/components/code-editor.tsx +13 -16
  551. package/src/components/composer-transcription-control.tsx +128 -30
  552. package/src/components/composer.tsx +404 -195
  553. package/src/components/computer-viewer.tsx +880 -253
  554. package/src/components/copy-button.tsx +13 -5
  555. package/src/components/desktop-viewer.tsx +1 -1
  556. package/src/components/enrollment-consent.tsx +1 -1
  557. package/src/components/enrollment-device-flow.tsx +1 -1
  558. package/src/components/file-browser.tsx +10 -14
  559. package/src/components/fleet-tile.tsx +5 -3
  560. package/src/components/generated-video-player.tsx +4 -2
  561. package/src/components/grok-mark.tsx +34 -0
  562. package/src/components/human-input-form.tsx +590 -283
  563. package/src/components/human-input-surface.tsx +11 -0
  564. package/src/components/interaction-intervention-banner.tsx +2 -2
  565. package/src/components/linkified-text.tsx +79 -0
  566. package/src/components/machine-card.tsx +35 -78
  567. package/src/components/machine-dock-bar.tsx +1 -1
  568. package/src/components/machine-input-display.ts +197 -3
  569. package/src/components/machines/agent-status-icon.tsx +31 -0
  570. package/src/components/machines/machine-detail.tsx +200 -31
  571. package/src/components/machines/machine-operation-policy.tsx +1 -1
  572. package/src/components/machines/machine-status.ts +241 -0
  573. package/src/components/machines-dashboard.tsx +2 -17
  574. package/src/components/markdown-table-layout.ts +143 -0
  575. package/src/components/markdown.tsx +466 -129
  576. package/src/components/message-timeline.tsx +2370 -667
  577. package/src/components/model-mark.tsx +102 -0
  578. package/src/components/model-picker.tsx +15 -4
  579. package/src/components/model-policy-picker-menu.tsx +371 -0
  580. package/src/components/model-policy-picker.tsx +252 -429
  581. package/src/components/new-conversation.tsx +221 -0
  582. package/src/components/open-geni-chat.tsx +309 -0
  583. package/src/components/open-geni-links.tsx +179 -0
  584. package/src/components/openrouter-mark.tsx +13 -0
  585. package/src/components/opper-mark.tsx +18 -0
  586. package/src/components/pierre-diff.tsx +41 -16
  587. package/src/components/pierre-file.tsx +35 -10
  588. package/src/components/preview-loading.tsx +109 -0
  589. package/src/components/provider-recovery-notice.tsx +49 -0
  590. package/src/components/queue-item-content.ts +14 -0
  591. package/src/components/queue-surface-implementation.tsx +51 -356
  592. package/src/components/queue-surface-state.tsx +3 -1
  593. package/src/components/rehype-secret-refs.ts +98 -0
  594. package/src/components/remark-soft-line-breaks.ts +166 -0
  595. package/src/components/sandbox-files.tsx +91 -15
  596. package/src/components/sandbox-terminal.tsx +84 -13
  597. package/src/components/sandbox-workspace.tsx +147 -51
  598. package/src/components/secret-question-field.tsx +324 -0
  599. package/src/components/secret-ref-chip.tsx +185 -0
  600. package/src/components/session-capability-frame.tsx +197 -0
  601. package/src/components/session-chrome.tsx +842 -251
  602. package/src/components/session-commands-panel.tsx +126 -0
  603. package/src/components/session-commands.tsx +18 -0
  604. package/src/components/session-connection-request.tsx +97 -0
  605. package/src/components/session-conversation.tsx +876 -0
  606. package/src/components/session-list.tsx +257 -0
  607. package/src/components/session-mcp-capability-card.tsx +680 -0
  608. package/src/components/session-proxy-scope.tsx +153 -0
  609. package/src/components/session-status.tsx +26 -17
  610. package/src/components/session-usage-limit-notice.tsx +174 -0
  611. package/src/components/timeline-anchor.tsx +119 -0
  612. package/src/components/timeline-annotation-chrome.tsx +196 -0
  613. package/src/components/timeline-annotation-layout.ts +268 -0
  614. package/src/components/timeline-annotation-markers.tsx +185 -0
  615. package/src/components/timeline-annotation-reveal-context.ts +9 -0
  616. package/src/components/timeline-annotation-selection.tsx +106 -54
  617. package/src/components/timeline-annotation-shared.ts +245 -0
  618. package/src/components/timeline-annotations-dialog.tsx +185 -128
  619. package/src/components/timeline-annotations.tsx +189 -45
  620. package/src/components/timeline-reading-progress.ts +80 -0
  621. package/src/components/timeline-search.tsx +168 -0
  622. package/src/components/timeline-settlement.ts +60 -0
  623. package/src/components/tool-action-review.tsx +650 -0
  624. package/src/components/tool-review-history.tsx +88 -0
  625. package/src/components/usage-limit-notice.tsx +179 -0
  626. package/src/components/usage-member-list.tsx +792 -0
  627. package/src/components/usage-meter.tsx +382 -0
  628. package/src/components/use-viewer-menu-dismiss.ts +33 -0
  629. package/src/components/user-message-body.tsx +27 -2
  630. package/src/components/workspace-dock.tsx +18 -1
  631. package/src/composer-messages.ts +150 -0
  632. package/src/composer-voice-events.ts +12 -0
  633. package/src/composer.ts +6 -1
  634. package/src/connect-account-identity.tsx +34 -0
  635. package/src/connect-accounts.tsx +233 -0
  636. package/src/connect-chooser.tsx +336 -0
  637. package/src/connect-panel.tsx +99 -0
  638. package/src/connect-setup.tsx +355 -0
  639. package/src/connect.ts +60 -0
  640. package/src/connection-assets.d.ts +4 -0
  641. package/src/connection-catalog.tsx +162 -0
  642. package/src/connection-discovery.tsx +238 -0
  643. package/src/connection-installed.tsx +51 -0
  644. package/src/connection-logo.tsx +37 -0
  645. package/src/connection-service-logo.tsx +55 -0
  646. package/src/connection-service-presentation.ts +46 -0
  647. package/src/connection-skeleton.tsx +16 -0
  648. package/src/connection-type-picker.tsx +36 -0
  649. package/src/conversation-timeline.ts +92 -0
  650. package/src/desktop.ts +24 -0
  651. package/src/device-authorization.tsx +98 -0
  652. package/src/diffs.ts +16 -0
  653. package/src/discovery-cache.ts +42 -0
  654. package/src/editor.ts +13 -0
  655. package/src/hooks/internal.ts +3 -2
  656. package/src/hooks/latest-question.ts +164 -0
  657. package/src/hooks/shared-machines-list.ts +34 -1
  658. package/src/hooks/shared-query.ts +721 -0
  659. package/src/hooks/use-attached-browsers.ts +1 -1
  660. package/src/hooks/use-auth-runs.ts +0 -27
  661. package/src/hooks/use-available-models.ts +2 -2
  662. package/src/hooks/use-browser-frame-stream.ts +19 -2
  663. package/src/hooks/use-browser-session.ts +680 -205
  664. package/src/hooks/use-browser-sessions.ts +32 -1
  665. package/src/hooks/use-client-config-flags.ts +118 -0
  666. package/src/hooks/use-codex-accounts.ts +247 -47
  667. package/src/hooks/use-composer.ts +227 -44
  668. package/src/hooks/use-computer-frame-stream.ts +25 -7
  669. package/src/hooks/use-computer-input-posture.ts +94 -0
  670. package/src/hooks/use-computer-session.ts +409 -95
  671. package/src/hooks/use-connect.ts +23 -0
  672. package/src/hooks/use-desktop-stream.ts +19 -28
  673. package/src/hooks/use-file-attachments.ts +80 -4
  674. package/src/hooks/use-goal.ts +79 -9
  675. package/src/hooks/use-last-started-turn-policy.ts +37 -11
  676. package/src/hooks/use-machines.ts +4 -0
  677. package/src/hooks/use-new-conversation.ts +374 -0
  678. package/src/hooks/use-new-session-draft.ts +813 -0
  679. package/src/hooks/use-realtime-voice-models.ts +46 -0
  680. package/src/hooks/use-sandbox-files.ts +68 -20
  681. package/src/hooks/use-session-background-commands.ts +22 -11
  682. package/src/hooks/use-session-capabilities.ts +3 -2
  683. package/src/hooks/use-session-conversation.ts +150 -0
  684. package/src/hooks/use-session-events.ts +531 -335
  685. package/src/hooks/use-session-mcp-approval-policy.ts +13 -5
  686. package/src/hooks/use-session-model-account.ts +174 -0
  687. package/src/hooks/use-session.ts +30 -2
  688. package/src/hooks/use-slash-commands.ts +6 -11
  689. package/src/hooks/use-supergrok-accounts.ts +22 -8
  690. package/src/hooks/use-terminal-stream.ts +153 -27
  691. package/src/hooks/use-transcription.ts +6 -0
  692. package/src/hooks/use-turn-queue.ts +2 -1
  693. package/src/hooks/use-usage.ts +100 -0
  694. package/src/hooks/use-vaults.ts +153 -0
  695. package/src/hooks/use-video-artifact-playback.ts +1 -1
  696. package/src/hooks/use-voice-input.ts +127 -19
  697. package/src/hooks/use-workspace-capture.ts +3 -2
  698. package/src/hooks/use-workspace-edit.ts +23 -5
  699. package/src/hooks/use-workspace-sessions.ts +74 -7
  700. package/src/human-input-model.ts +359 -0
  701. package/src/identity-link-accounts.tsx +148 -0
  702. package/src/identity-link-consent.tsx +192 -0
  703. package/src/index.ts +262 -16
  704. package/src/interaction.ts +1 -0
  705. package/src/lib/clipboard.ts +29 -0
  706. package/src/lib/error-message.ts +37 -0
  707. package/src/lib/format.ts +205 -10
  708. package/src/lib/host-theme.ts +529 -0
  709. package/src/lib/interaction-errors.ts +15 -0
  710. package/src/lib/menu-styles.ts +44 -0
  711. package/src/lib/notify-observer.ts +15 -0
  712. package/src/lib/pierre-diffs-loader.ts +53 -0
  713. package/src/lib/provider-recovery.ts +243 -0
  714. package/src/lib/scrollbars.ts +30 -0
  715. package/src/lib/terminal-input-readiness.ts +51 -0
  716. package/src/lib/transient-retry.ts +99 -0
  717. package/src/lib/workbench-peers.ts +62 -0
  718. package/src/lib/xterm-theme.ts +23 -21
  719. package/src/mcp-connection-status.ts +35 -0
  720. package/src/model-mark-paths.ts +53 -0
  721. package/src/model-picker-model.ts +330 -0
  722. package/src/model-policy.ts +299 -74
  723. package/src/native-previews.ts +100 -0
  724. package/src/new-session-draft.ts +22 -0
  725. package/src/notification-text.ts +81 -0
  726. package/src/older-history.ts +22 -4
  727. package/src/organization-model.ts +118 -0
  728. package/src/plugin-details.tsx +185 -0
  729. package/src/plugin-discovery.tsx +196 -0
  730. package/src/preview-loading-paint.ts +68 -0
  731. package/src/provider.tsx +74 -15
  732. package/src/queue-presentation-model.ts +464 -0
  733. package/src/realtime/dropdown-menu.tsx +19 -12
  734. package/src/realtime/embedded-new-voice.tsx +1 -0
  735. package/src/realtime/embedded-voice.tsx +3 -0
  736. package/src/realtime/realtime-control.tsx +138 -550
  737. package/src/realtime/session-realtime.ts +566 -0
  738. package/src/sandbox-label-model.ts +53 -0
  739. package/src/scrollbars.ts +3 -0
  740. package/src/secret-reference-label.ts +43 -0
  741. package/src/secrets-ui.tsx +59 -0
  742. package/src/session-agents-model.ts +33 -0
  743. package/src/session-attention-model.ts +62 -0
  744. package/src/session-auth-recommendation.ts +48 -0
  745. package/src/session-capability-policy.ts +196 -0
  746. package/src/session-context.ts +3 -4
  747. package/src/session-feedback-model.ts +95 -0
  748. package/src/session-list-model.ts +434 -0
  749. package/src/session-realtime.ts +18 -0
  750. package/src/session-status-model.ts +99 -0
  751. package/src/session-ui.ts +98 -1
  752. package/src/session.ts +17 -1
  753. package/src/site-snapshot.ts +74 -0
  754. package/src/sites-ui.tsx +348 -0
  755. package/src/sites.ts +3 -0
  756. package/src/skill-discovery.tsx +208 -0
  757. package/src/terminal.ts +26 -0
  758. package/src/testing/session-scenarios.ts +404 -0
  759. package/src/timeline/activity-rail.tsx +242 -101
  760. package/src/timeline/agent-identity.tsx +84 -0
  761. package/src/timeline/agent-row.tsx +231 -0
  762. package/src/timeline/allowance-exhausted-row.tsx +86 -0
  763. package/src/timeline/compaction-copy.ts +115 -0
  764. package/src/timeline/fleet-decision-projection.ts +2 -1
  765. package/src/timeline/fleet-decision-row.tsx +1 -0
  766. package/src/timeline/focus-sequence.ts +44 -0
  767. package/src/timeline/genie-copy.ts +19 -0
  768. package/src/timeline/genie-loading.tsx +143 -0
  769. package/src/timeline/index.ts +30 -2
  770. package/src/timeline/knowledge-receipt.tsx +100 -0
  771. package/src/timeline/message-sender.tsx +71 -0
  772. package/src/timeline/notice-presentation.ts +48 -0
  773. package/src/timeline/parsers.ts +42 -2
  774. package/src/timeline/patch-apply-command.tsx +135 -0
  775. package/src/timeline/platform-activity-presentation.ts +166 -0
  776. package/src/timeline/platform-activity-row.tsx +15 -90
  777. package/src/timeline/presented-image.ts +63 -0
  778. package/src/timeline/projection.ts +1742 -76
  779. package/src/timeline/provider-logo.tsx +53 -0
  780. package/src/timeline/question-nav-model.ts +28 -0
  781. package/src/timeline/registry.ts +26 -4
  782. package/src/timeline/retained-image.ts +97 -0
  783. package/src/timeline/retained-loaders.ts +91 -0
  784. package/src/timeline/rolling-activity.tsx +80 -0
  785. package/src/timeline/screenshot-lightbox.tsx +228 -5
  786. package/src/timeline/session-usage-limit-row.tsx +42 -0
  787. package/src/timeline/shared.tsx +42 -15
  788. package/src/timeline/startup-dispatch-details.tsx +36 -0
  789. package/src/timeline/startup-preference.ts +33 -0
  790. package/src/timeline/startup-timings.tsx +145 -0
  791. package/src/timeline/tool-display-name.ts +19 -7
  792. package/src/timeline/tool-presentation.ts +1316 -0
  793. package/src/timeline/tool-renderers.tsx +835 -1138
  794. package/src/timeline/turn-summary-model.ts +343 -0
  795. package/src/timeline/turn-summary.tsx +272 -365
  796. package/src/timeline/types.ts +189 -6
  797. package/src/timeline/work-presentation.ts +189 -0
  798. package/src/timeline-model.ts +143 -0
  799. package/src/usage/allowance-copy.ts +64 -0
  800. package/src/usage/session-usage-limit.ts +266 -0
  801. package/src/usage/summary.ts +93 -0
  802. package/src/usage.ts +70 -0
  803. package/src/voice-recording-owner.ts +22 -0
  804. package/styles/compiled.css +2319 -748
  805. package/styles/connect.css +290 -0
  806. package/styles/connect.d.ts +1 -0
  807. package/styles/effective-tokens.css +1 -0
  808. package/styles/index.css +309 -9
  809. package/styles/responsive.css +99 -7
  810. package/styles/session-capability.css +44 -0
  811. package/styles/tokens.css +134 -66
  812. package/dist/browser-viewer-MKKFRRRV.js +0 -14
  813. package/dist/chunk-2DZVYHVO.js +0 -470
  814. package/dist/chunk-2DZVYHVO.js.map +0 -1
  815. package/dist/chunk-3AGBGTRT.js.map +0 -1
  816. package/dist/chunk-4MDLEHXL.js.map +0 -1
  817. package/dist/chunk-5YK2ZDIG.js.map +0 -1
  818. package/dist/chunk-7EAFGICR.js +0 -302
  819. package/dist/chunk-7EAFGICR.js.map +0 -1
  820. package/dist/chunk-ELIYWBZR.js.map +0 -1
  821. package/dist/chunk-H5YGYNXS.js.map +0 -1
  822. package/dist/chunk-HUIKIDM5.js.map +0 -1
  823. package/dist/chunk-ICA6R6TW.js.map +0 -1
  824. package/dist/chunk-IOLVN74D.js.map +0 -1
  825. package/dist/chunk-JAQ5K7W6.js.map +0 -1
  826. package/dist/chunk-JVYQRRC4.js.map +0 -1
  827. package/dist/chunk-KCGXPP5V.js.map +0 -1
  828. package/dist/chunk-KZ73RL76.js.map +0 -1
  829. package/dist/chunk-NEXLLTP4.js.map +0 -1
  830. package/dist/chunk-PEI6RANI.js.map +0 -1
  831. package/dist/chunk-VTOREEXF.js +0 -142
  832. package/dist/chunk-VTOREEXF.js.map +0 -1
  833. package/dist/chunk-WSK7G5LE.js +0 -170
  834. package/dist/chunk-WSK7G5LE.js.map +0 -1
  835. package/dist/chunk-X4A66EWZ.js +0 -223
  836. package/dist/chunk-X4A66EWZ.js.map +0 -1
  837. package/dist/chunk-XYRDDSXF.js.map +0 -1
  838. package/dist/chunk-YG4QEQF5.js +0 -9557
  839. package/dist/chunk-YG4QEQF5.js.map +0 -1
  840. package/dist/chunk-YVCHAQZY.js.map +0 -1
  841. package/dist/chunk-ZPBTI3TE.js.map +0 -1
  842. package/dist/computer-viewer-FWEZISW4.js +0 -16
  843. package/dist/fleet-decision-row-GGCAT4E4.js.map +0 -1
  844. package/dist/hooks/use-environments.d.ts +0 -41
  845. package/dist/hooks/use-packs.d.ts +0 -32
  846. package/dist/platform-activity-row-BJHUHXAT.js.map +0 -1
  847. package/dist/queue-surface-implementation-BSKUWHRI.js.map +0 -1
  848. package/dist/timeline-annotation-selection-3L7LHGG2.js +0 -187
  849. package/dist/timeline-annotation-selection-3L7LHGG2.js.map +0 -1
  850. package/dist/timeline-annotations-dialog-U7EVLR75.js +0 -196
  851. package/dist/timeline-annotations-dialog-U7EVLR75.js.map +0 -1
  852. package/src/hooks/use-environments.ts +0 -182
  853. package/src/hooks/use-packs.ts +0 -157
  854. /package/dist/{browser-viewer-MKKFRRRV.js.map → browser-viewer-ZWR6MWJ7.js.map} +0 -0
  855. /package/dist/{computer-viewer-FWEZISW4.js.map → chat-interactive-block-4EUKJXUO.js.map} +0 -0
@@ -1,108 +1,642 @@
1
+ "use client";
1
2
  import {
2
3
  buildTimeline,
3
- createOlderHistoryLoadReceipt,
4
4
  groupTimeline,
5
- isActionableHumanInputRequest,
6
5
  sessionStatusFromEvents
7
- } from "./chunk-JAQ5K7W6.js";
6
+ } from "./chunk-2SFFYIH3.js";
7
+ import {
8
+ isActionableHumanInputRequest
9
+ } from "./chunk-IZV5FTJC.js";
10
+ import {
11
+ FILE_ONLY_MESSAGE_TEXT,
12
+ notifyObserver,
13
+ useComposer
14
+ } from "./chunk-V5ZUJT2T.js";
15
+ import {
16
+ coerceReasoningEffortForModel,
17
+ defaultEffortForModel,
18
+ projectPickerRows,
19
+ runnableLatencyModesForModel,
20
+ sortPickerRows
21
+ } from "./chunk-KF33GTWN.js";
8
22
  import {
9
23
  useDebouncedCallback,
10
24
  useMutationRunner,
11
25
  usePageLiveActivity,
12
26
  usePolledValue,
13
27
  useSessionEventTrigger
14
- } from "./chunk-KZ73RL76.js";
28
+ } from "./chunk-QHUE7E4N.js";
29
+ import {
30
+ normalizeError,
31
+ useErrorMessage
32
+ } from "./chunk-CM7LVUKC.js";
15
33
  import {
16
34
  useEmbeddedFileAttachments,
17
35
  useEmbeddedGoal,
18
36
  useEmbeddedHumanInputSession,
19
37
  useEmbeddedSession,
20
- useEmbeddedSessionLineage,
21
- useEmbeddedSessionMcpApprovalPolicy,
22
- useEmbeddedSessionRead
23
- } from "./chunk-NEXLLTP4.js";
38
+ useEmbeddedSessionRead,
39
+ useOpenGeni,
40
+ useOpenGeniClient
41
+ } from "./chunk-G76LSA76.js";
24
42
 
25
- // src/embedded-session-client.ts
26
- var requiredMethods = [
27
- "getSession",
28
- "listEvents",
29
- "streamEvents",
30
- "getComposerDraft",
31
- "saveComposerDraft",
32
- "submitComposerDraft",
33
- "sendMessage",
34
- "steerMessage",
35
- "getQueue",
36
- "moveQueueItem",
37
- "editQueueItem",
38
- "steerQueueItem",
39
- "deleteQueueItem",
40
- "pauseSession",
41
- "resumeSession",
42
- "sendApprovalDecision"
43
- ];
44
- function createEmbeddedSessionClient(base, options = {}) {
45
- const overrides = options.overrides ?? {};
46
- const client = {};
47
- for (const key of requiredMethods) {
48
- const override = overrides[key];
49
- const method = override ?? base[key];
50
- if (typeof method !== "function") {
51
- throw new TypeError(`Embedded session client method is required: ${key}`);
43
+ // src/approvals.ts
44
+ import {
45
+ parseToolDisplayMetadata
46
+ } from "@opengeni/sdk";
47
+ function approvalsFromRequiresAction(payload) {
48
+ const approvals = payload && typeof payload === "object" ? payload.approvals : void 0;
49
+ if (!Array.isArray(approvals)) {
50
+ return [];
51
+ }
52
+ return approvals.map((approval, index) => {
53
+ const raw = approval && typeof approval === "object" ? approval : {};
54
+ const rawItem = raw.rawItem && typeof raw.rawItem === "object" ? raw.rawItem : {};
55
+ const display = parseToolDisplayMetadata(raw.display);
56
+ return {
57
+ // Mirror the runtime's canonical interruption identity before retaining
58
+ // the legacy top-level callId fallback for already-normalized payloads.
59
+ id: String(rawItem.callId ?? rawItem.id ?? raw.id ?? raw.callId ?? raw.name ?? index),
60
+ name: String(raw.name ?? raw.toolName ?? rawItem.name ?? "approval"),
61
+ ...display ? { display } : {},
62
+ arguments: raw.arguments ?? rawItem.arguments,
63
+ raw: approval
64
+ };
65
+ });
66
+ }
67
+ function projectPendingApprovals(events) {
68
+ let pending = [];
69
+ let owningTurnId = null;
70
+ for (const event of events) {
71
+ switch (event.type) {
72
+ case "session.requiresAction": {
73
+ pending = approvalsFromRequiresAction(event.payload);
74
+ owningTurnId = event.turnId ?? null;
75
+ break;
76
+ }
77
+ case "user.approvalDecision": {
78
+ const payload = event.payload && typeof event.payload === "object" ? event.payload : {};
79
+ if (typeof payload.approvalId === "string") {
80
+ pending = pending.filter((approval) => approval.id !== payload.approvalId);
81
+ }
82
+ break;
83
+ }
84
+ case "turn.completed":
85
+ case "turn.failed":
86
+ case "turn.cancelled": {
87
+ if (owningTurnId === null || event.turnId == null || event.turnId === owningTurnId) {
88
+ pending = [];
89
+ owningTurnId = null;
90
+ }
91
+ break;
92
+ }
93
+ default:
94
+ break;
52
95
  }
53
- client[key] = method.bind(override === void 0 ? base : overrides);
54
96
  }
55
- const inferenceOverride = overrides.setWorkspaceInferenceState;
56
- const inferenceBase = base.setWorkspaceInferenceState;
57
- const inferenceMethod = inferenceOverride ?? inferenceBase;
58
- if (typeof inferenceMethod === "function") {
59
- client.setWorkspaceInferenceState = inferenceMethod.bind(
60
- inferenceOverride === void 0 ? base : overrides
97
+ return pending;
98
+ }
99
+
100
+ // src/older-history.ts
101
+ var activeReceiptCapture;
102
+ var preserveTailForAutomaticLoad = false;
103
+ function invokeOlderHistoryLoaderWithReceiptCapture(load, capture, preserveTail = false) {
104
+ const previousCapture = activeReceiptCapture;
105
+ const previousPreserveTail = preserveTailForAutomaticLoad;
106
+ activeReceiptCapture = capture;
107
+ preserveTailForAutomaticLoad = preserveTail;
108
+ try {
109
+ return load();
110
+ } finally {
111
+ activeReceiptCapture = previousCapture;
112
+ preserveTailForAutomaticLoad = previousPreserveTail;
113
+ }
114
+ }
115
+ function createOlderHistoryLoadReceipt(load) {
116
+ let resolveResult;
117
+ let rejectResult;
118
+ const result = new Promise((resolve, reject) => {
119
+ resolveResult = resolve;
120
+ rejectResult = reject;
121
+ });
122
+ result.committed = false;
123
+ result.tailPreserved = false;
124
+ activeReceiptCapture?.(result);
125
+ try {
126
+ const loaded = load(
127
+ () => {
128
+ result.committed = true;
129
+ },
130
+ preserveTailForAutomaticLoad,
131
+ () => {
132
+ result.tailPreserved = true;
133
+ }
61
134
  );
135
+ Promise.resolve(loaded).then(resolveResult, rejectResult);
136
+ } catch (error) {
137
+ rejectResult(error);
62
138
  }
63
- const mapDraft = options.mapComposerDraft;
64
- if (!mapDraft) return client;
65
- const getComposerDraft = client.getComposerDraft;
66
- client.getComposerDraft = async (workspaceId, sessionId) => mapDraft(await getComposerDraft(workspaceId, sessionId), {
67
- operation: "read",
68
- workspaceId,
69
- sessionId
139
+ return result;
140
+ }
141
+
142
+ // src/conversation-timeline.ts
143
+ function conversationTimeline(items, queue, composer) {
144
+ const queued = new Set(
145
+ queue.queue.filter((turn) => turn.metadata.delivery !== "steer").map((turn) => turn.triggerEventId)
146
+ );
147
+ const pending = composer.optimisticMessages ?? [];
148
+ const pendingQueue = new Set(
149
+ pending.filter(
150
+ (message) => message.destination === "queue" && !(message.turnId && message.appliedQueueVersion != null && queue.snapshot && queue.snapshot.version >= message.appliedQueueVersion && !queue.queue.some((turn) => turn.id === message.turnId))
151
+ ).map((message) => `user-message:${message.clientEventId}`)
152
+ );
153
+ const visible = items.filter(
154
+ (item) => item.kind !== "user-message" || !queued.has(item.id) && !pendingQueue.has(item.reconciliationKey ?? "")
155
+ );
156
+ const keys = new Set(
157
+ visible.flatMap((item) => item.kind === "user-message" ? [item.reconciliationKey] : [])
158
+ );
159
+ const optimistic = pending.filter(
160
+ (message) => (message.destination === "chat" || message.state === "failed") && !keys.has(`user-message:${message.clientEventId}`) && !queue.queue.some((turn) => turn.id === message.turnId)
161
+ ).map((message) => ({
162
+ kind: "user-message",
163
+ id: `optimistic:${message.clientEventId}`,
164
+ reconciliationKey: `user-message:${message.clientEventId}`,
165
+ text: message.text,
166
+ annotations: message.annotations.map((annotation, ordinal) => ({ ...annotation, ordinal })),
167
+ resources: message.resources,
168
+ tools: [],
169
+ occurredAt: message.occurredAt,
170
+ delivery: {
171
+ state: message.state,
172
+ ...message.error ? { error: message.error } : {},
173
+ ...message.state === "failed" ? {
174
+ ...message.retryable !== false && composer.retryOptimisticMessage ? { onRetry: () => composer.retryOptimisticMessage?.(message.clientEventId) } : {},
175
+ ...message.retryable === false && composer.restoreOptimisticMessage ? { onEdit: () => composer.restoreOptimisticMessage?.(message.clientEventId) } : {},
176
+ onRemove: () => composer.removeOptimisticMessage?.(message.clientEventId)
177
+ } : {}
178
+ }
179
+ }));
180
+ const ids = new Set(visible.map((item) => item.id));
181
+ const steers = (queue.acceptedSteers ?? []).filter((steer) => !ids.has(steer.triggerEventId)).map((steer) => ({
182
+ kind: "user-message",
183
+ id: steer.triggerEventId,
184
+ text: steer.text,
185
+ annotations: steer.annotations,
186
+ resources: steer.resources,
187
+ tools: steer.tools,
188
+ occurredAt: steer.occurredAt,
189
+ delivery: { state: steer.state }
190
+ }));
191
+ return [...visible, ...optimistic, ...steers];
192
+ }
193
+
194
+ // src/hooks/use-available-models.ts
195
+ import { useCallback } from "react";
196
+ function useAvailableModels(options = {}) {
197
+ const client = useOpenGeniClient(options);
198
+ const load = useCallback(
199
+ async (signal) => await client.getClientConfig({ signal }),
200
+ [client]
201
+ );
202
+ const state = usePolledValue(load, {
203
+ pollIntervalMs: options.pollIntervalMs,
204
+ enabled: options.enabled
70
205
  });
71
- const saveComposerDraft = client.saveComposerDraft;
72
- client.saveComposerDraft = async (workspaceId, sessionId, request) => mapDraft(await saveComposerDraft(workspaceId, sessionId, request), {
73
- operation: "save",
74
- workspaceId,
75
- sessionId
206
+ return {
207
+ models: state.data?.models ?? [],
208
+ defaultModel: state.data?.defaultModel ?? null,
209
+ loading: state.loading,
210
+ error: state.error,
211
+ refresh: state.refresh
212
+ };
213
+ }
214
+ function useWorkspaceModelCatalog(options) {
215
+ const client = useOpenGeniClient(options);
216
+ const load = useCallback(
217
+ async (signal) => {
218
+ if (!options.workspaceId) {
219
+ return { models: [], defaultModel: null };
220
+ }
221
+ const [catalog, config] = await Promise.all([
222
+ client.getWorkspaceModelCatalog(options.workspaceId, { signal }),
223
+ client.getClientConfig({ signal, workspaceId: options.workspaceId })
224
+ ]);
225
+ return {
226
+ models: catalog.models,
227
+ defaultModel: config.defaultModel
228
+ };
229
+ },
230
+ [client, options.workspaceId]
231
+ );
232
+ const state = usePolledValue(load, {
233
+ pollIntervalMs: options.pollIntervalMs,
234
+ enabled: options.enabled !== false && Boolean(options.workspaceId)
76
235
  });
77
- const submitComposerDraft = client.submitComposerDraft;
78
- client.submitComposerDraft = async (workspaceId, sessionId, request) => {
79
- const response = await submitComposerDraft(workspaceId, sessionId, request);
80
- return {
81
- ...response,
82
- draft: mapDraft(response.draft, {
83
- operation: "submit",
84
- workspaceId,
85
- sessionId
236
+ const rows = sortPickerRows(projectPickerRows(state.data?.models ?? []));
237
+ return {
238
+ models: state.data?.models ?? [],
239
+ rows,
240
+ defaultModel: state.data?.defaultModel ?? null,
241
+ loading: state.loading,
242
+ error: state.error,
243
+ refresh: state.refresh
244
+ };
245
+ }
246
+
247
+ // src/hooks/use-file-attachments.ts
248
+ import { useCallback as useCallback2, useEffect, useRef, useState } from "react";
249
+ var isImage = (file) => file.type.startsWith("image/");
250
+ var fallbackAttachmentId = 0;
251
+ function createAttachmentId() {
252
+ const cryptoSource = globalThis.crypto;
253
+ if (typeof cryptoSource?.randomUUID === "function") return cryptoSource.randomUUID();
254
+ fallbackAttachmentId += 1;
255
+ return `attachment:${Date.now().toString(36)}:${fallbackAttachmentId.toString(36)}`;
256
+ }
257
+ function secureContextRequiredErrorCode(error) {
258
+ return typeof error === "object" && error !== null && "code" in error && error.code === "secure_context_required" ? error.code : void 0;
259
+ }
260
+ function useFileAttachments(options = {}) {
261
+ const { client, workspaceId } = useEmbeddedFileAttachments(options);
262
+ const formatError = useErrorMessage();
263
+ const pasteFilter = options.pasteFilter ?? isImage;
264
+ const [attachments, setAttachments] = useState([]);
265
+ const attachmentsRef = useRef(attachments);
266
+ attachmentsRef.current = attachments;
267
+ const sources = useRef(/* @__PURE__ */ new Map());
268
+ const previewUrls = useRef(/* @__PURE__ */ new Map());
269
+ const previewRetainers = useRef(/* @__PURE__ */ new Map());
270
+ const pendingPreviewRevocations = useRef(/* @__PURE__ */ new Set());
271
+ const revokePreview = useCallback2((id) => {
272
+ const previewUrl = previewUrls.current.get(id);
273
+ if (!previewUrl) return;
274
+ if ((previewRetainers.current.get(id) ?? 0) > 0) {
275
+ pendingPreviewRevocations.current.add(id);
276
+ return;
277
+ }
278
+ previewUrls.current.delete(id);
279
+ pendingPreviewRevocations.current.delete(id);
280
+ URL.revokeObjectURL(previewUrl);
281
+ }, []);
282
+ const revokeAllPreviews = useCallback2(() => {
283
+ for (const id of previewUrls.current.keys()) revokePreview(id);
284
+ }, [revokePreview]);
285
+ const retainPreview = useCallback2(
286
+ (id) => {
287
+ if (!previewUrls.current.has(id)) return void 0;
288
+ previewRetainers.current.set(id, (previewRetainers.current.get(id) ?? 0) + 1);
289
+ let released = false;
290
+ return () => {
291
+ if (released) return;
292
+ released = true;
293
+ const remaining = (previewRetainers.current.get(id) ?? 1) - 1;
294
+ if (remaining > 0) {
295
+ previewRetainers.current.set(id, remaining);
296
+ return;
297
+ }
298
+ previewRetainers.current.delete(id);
299
+ if (pendingPreviewRevocations.current.has(id)) revokePreview(id);
300
+ };
301
+ },
302
+ [revokePreview]
303
+ );
304
+ const committedAttachmentIds = useRef(/* @__PURE__ */ new Set());
305
+ useEffect(() => {
306
+ const currentIds = new Set(attachments.map((attachment) => attachment.id));
307
+ for (const id of committedAttachmentIds.current) {
308
+ if (!currentIds.has(id)) revokePreview(id);
309
+ }
310
+ committedAttachmentIds.current = currentIds;
311
+ }, [attachments, revokePreview]);
312
+ const scopeGeneration = useRef(0);
313
+ const previousScope = useRef({ client, workspaceId });
314
+ useEffect(() => {
315
+ const previous = previousScope.current;
316
+ if (previous.client === client && previous.workspaceId === workspaceId) return;
317
+ previousScope.current = { client, workspaceId };
318
+ scopeGeneration.current += 1;
319
+ sources.current.clear();
320
+ revokeAllPreviews();
321
+ setAttachments([]);
322
+ }, [client, revokeAllPreviews, workspaceId]);
323
+ useEffect(
324
+ () => () => {
325
+ scopeGeneration.current += 1;
326
+ sources.current.clear();
327
+ revokeAllPreviews();
328
+ },
329
+ [revokeAllPreviews]
330
+ );
331
+ const startUpload = useCallback2(
332
+ (id, file) => {
333
+ const generation = scopeGeneration.current;
334
+ void client.uploadFile(workspaceId, {
335
+ filename: file.name || "file",
336
+ contentType: file.type || "application/octet-stream",
337
+ data: file,
338
+ ...options.scope ? { scope: options.scope } : {}
339
+ }).then((asset) => {
340
+ if (scopeGeneration.current !== generation) return;
341
+ sources.current.delete(id);
342
+ setAttachments(
343
+ (current) => current.map(
344
+ (attachment) => attachment.id === id ? {
345
+ ...attachment,
346
+ status: "ready",
347
+ file: asset,
348
+ name: asset.filename,
349
+ contentType: asset.contentType,
350
+ sizeBytes: asset.sizeBytes,
351
+ errorCode: void 0,
352
+ error: void 0,
353
+ errorCause: void 0
354
+ } : attachment
355
+ )
356
+ );
357
+ }).catch((error) => {
358
+ if (scopeGeneration.current !== generation) return;
359
+ setAttachments(
360
+ (current) => current.map(
361
+ (attachment) => attachment.id === id ? {
362
+ ...attachment,
363
+ status: "failed",
364
+ errorCode: secureContextRequiredErrorCode(error),
365
+ error: formatError(error),
366
+ errorCause: error
367
+ } : attachment
368
+ )
369
+ );
370
+ });
371
+ },
372
+ [client, workspaceId, options.scope, formatError]
373
+ );
374
+ const addFiles = useCallback2(
375
+ (files) => {
376
+ for (const file of files) {
377
+ const id = createAttachmentId();
378
+ sources.current.set(id, file);
379
+ const previewUrl = isImage(file) ? URL.createObjectURL(file) : void 0;
380
+ if (previewUrl) previewUrls.current.set(id, previewUrl);
381
+ setAttachments((current) => [
382
+ ...current,
383
+ {
384
+ id,
385
+ name: file.name || "image",
386
+ contentType: file.type || "application/octet-stream",
387
+ sizeBytes: file.size,
388
+ status: "uploading",
389
+ ...previewUrl ? { previewUrl } : {}
390
+ }
391
+ ]);
392
+ startUpload(id, file);
393
+ }
394
+ },
395
+ [startUpload]
396
+ );
397
+ const retry = useCallback2(
398
+ (id) => {
399
+ const file = sources.current.get(id);
400
+ if (!file) {
401
+ return;
402
+ }
403
+ setAttachments(
404
+ (current) => current.map(
405
+ (attachment) => attachment.id === id ? {
406
+ ...attachment,
407
+ status: "uploading",
408
+ errorCode: void 0,
409
+ error: void 0,
410
+ errorCause: void 0
411
+ } : attachment
412
+ )
413
+ );
414
+ startUpload(id, file);
415
+ },
416
+ [startUpload]
417
+ );
418
+ const addFromPaste = useCallback2(
419
+ (event) => {
420
+ const clipboardFiles = event.clipboardData?.files;
421
+ if (!clipboardFiles) {
422
+ return;
423
+ }
424
+ const files = [...clipboardFiles].filter(pasteFilter);
425
+ if (files.length > 0) {
426
+ addFiles(files);
427
+ }
428
+ },
429
+ [addFiles, pasteFilter]
430
+ );
431
+ const restoreReadyFiles = useCallback2(
432
+ (files) => {
433
+ const incoming = /* @__PURE__ */ new Map();
434
+ for (const file of files) {
435
+ if (file.status === "ready" && file.workspaceId === workspaceId) {
436
+ incoming.set(file.id, file);
437
+ }
438
+ }
439
+ setAttachments((current) => {
440
+ const unresolved = current.filter((attachment) => attachment.status !== "ready");
441
+ const existingReady = new Map(
442
+ current.flatMap(
443
+ (attachment) => attachment.status === "ready" && attachment.file ? [[attachment.file.id, attachment]] : []
444
+ )
445
+ );
446
+ const restored = [...incoming.values()].map((file) => {
447
+ const existing = existingReady.get(file.id);
448
+ return existing ? {
449
+ ...existing,
450
+ name: file.filename,
451
+ contentType: file.contentType,
452
+ sizeBytes: file.sizeBytes,
453
+ status: "ready",
454
+ file,
455
+ errorCode: void 0,
456
+ error: void 0,
457
+ errorCause: void 0
458
+ } : {
459
+ id: `restored:${file.id}`,
460
+ name: file.filename,
461
+ contentType: file.contentType,
462
+ sizeBytes: file.sizeBytes,
463
+ status: "ready",
464
+ file
465
+ // No source File and no object URL: server metadata is the
466
+ // only authority restored across page/device boundaries.
467
+ };
468
+ });
469
+ return [...unresolved, ...restored];
470
+ });
471
+ },
472
+ [workspaceId]
473
+ );
474
+ const loadPreview = useCallback2(
475
+ async (id, signal) => {
476
+ const generation = scopeGeneration.current;
477
+ const attachment = attachmentsRef.current.find((candidate) => candidate.id === id);
478
+ const createDownloadUrl = client.createFileDownloadUrl;
479
+ if (!attachment || attachment.status !== "ready" || !attachment.file || !attachment.contentType.startsWith("image/") || typeof createDownloadUrl !== "function" || signal?.aborted) {
480
+ return void 0;
481
+ }
482
+ const fileId = attachment.file.id;
483
+ const signed = await createDownloadUrl.call(client, workspaceId, fileId, { signal });
484
+ if (scopeGeneration.current !== generation || signal?.aborted) return void 0;
485
+ const current = attachmentsRef.current.find((candidate) => candidate.id === id);
486
+ if (current?.status !== "ready" || current.file?.id !== fileId) return void 0;
487
+ return signed.url;
488
+ },
489
+ [client, workspaceId]
490
+ );
491
+ const remove = useCallback2(
492
+ (id) => {
493
+ sources.current.delete(id);
494
+ revokePreview(id);
495
+ setAttachments((current) => current.filter((attachment) => attachment.id !== id));
496
+ },
497
+ [revokePreview]
498
+ );
499
+ const removeReadyFiles = useCallback2((fileIds) => {
500
+ const accepted = new Set(fileIds);
501
+ if (accepted.size === 0) return;
502
+ setAttachments(
503
+ (current) => current.filter((attachment) => {
504
+ return !(attachment.status === "ready" && attachment.file !== void 0 && accepted.has(attachment.file.id));
86
505
  })
87
- };
506
+ );
507
+ }, []);
508
+ const clear = useCallback2(() => {
509
+ sources.current.clear();
510
+ revokeAllPreviews();
511
+ setAttachments([]);
512
+ }, [revokeAllPreviews]);
513
+ return {
514
+ attachments,
515
+ readyResources: attachments.flatMap(
516
+ (attachment) => attachment.status === "ready" && attachment.file ? [{ kind: "file", fileId: attachment.file.id }] : []
517
+ ),
518
+ uploading: attachments.some((attachment) => attachment.status === "uploading"),
519
+ hasUnresolved: attachments.some((attachment) => attachment.status !== "ready"),
520
+ addFiles,
521
+ addFromPaste,
522
+ restoreReadyFiles,
523
+ ...typeof client.createFileDownloadUrl === "function" ? { loadPreview } : {},
524
+ retry,
525
+ retainPreview,
526
+ remove,
527
+ removeReadyFiles,
528
+ clear
529
+ };
530
+ }
531
+
532
+ // src/hooks/use-human-input.ts
533
+ import { useCallback as useCallback3, useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "react";
534
+ function isHumanInputEvent(event) {
535
+ return event.type === "session.humanInput.requested" || event.type === "user.humanInputResponse" || event.type === "turn.completed" || event.type === "turn.failed" || event.type === "turn.cancelled";
536
+ }
537
+ function useHumanInputRequests(sessionId, options = {}) {
538
+ const { client, workspaceId, registerSessionReconciler } = useEmbeddedHumanInputSession(options);
539
+ const enabled = (options.enabled ?? true) && Boolean(sessionId);
540
+ const load = useCallback3(
541
+ async () => sessionId ? await client.listHumanInputRequests(workspaceId, sessionId, { status: "pending" }) : [],
542
+ [client, sessionId, workspaceId]
543
+ );
544
+ const state = usePolledValue(load, {
545
+ enabled,
546
+ pollIntervalMs: options.pollIntervalMs
547
+ });
548
+ const loadedRequests = state.data;
549
+ const refreshRequests = state.refresh;
550
+ const mutation = useMutationRunner(`${workspaceId}\0${sessionId ?? ""}`);
551
+ const [respondingRequestId, setRespondingRequestId] = useState2(null);
552
+ const [deadlineVersion, setDeadlineVersion] = useState2(0);
553
+ const respondingRequestRef = useRef2(null);
554
+ useEffect2(() => {
555
+ respondingRequestRef.current = null;
556
+ setRespondingRequestId(null);
557
+ }, [sessionId, workspaceId]);
558
+ useEffect2(() => {
559
+ if (!sessionId || !enabled) return;
560
+ return registerSessionReconciler(sessionId, "human-input", state.refresh);
561
+ }, [enabled, registerSessionReconciler, sessionId, state.refresh]);
562
+ const scheduleRefresh = useDebouncedCallback(() => void state.refresh());
563
+ useSessionEventTrigger(client, workspaceId, sessionId, isHumanInputEvent, scheduleRefresh, {
564
+ enabled,
565
+ ...options.events !== void 0 ? { events: options.events } : {}
566
+ });
567
+ useEffect2(() => {
568
+ const now2 = Date.now();
569
+ const nextDeadline = (loadedRequests ?? []).reduce((next, request) => {
570
+ if (request.status !== "pending" || !request.expiresAt) return next;
571
+ const deadline = Date.parse(request.expiresAt);
572
+ if (!Number.isFinite(deadline) || deadline <= now2) return next;
573
+ return next === null ? deadline : Math.min(next, deadline);
574
+ }, null);
575
+ if (nextDeadline === null) return;
576
+ const timer = globalThis.setTimeout(
577
+ () => {
578
+ setDeadlineVersion((current) => current + 1);
579
+ void refreshRequests();
580
+ },
581
+ Math.min(2147483647, Math.max(0, nextDeadline - now2 + 1))
582
+ );
583
+ return () => globalThis.clearTimeout(timer);
584
+ }, [loadedRequests, refreshRequests]);
585
+ const respond = useCallback3(
586
+ async (requestId, response) => {
587
+ if (!sessionId || respondingRequestRef.current !== null) return null;
588
+ respondingRequestRef.current = requestId;
589
+ setRespondingRequestId(requestId);
590
+ try {
591
+ const event = await mutation.run(
592
+ () => client.submitHumanInputResponse(workspaceId, sessionId, requestId, response, {
593
+ clientEventId: globalThis.crypto.randomUUID()
594
+ })
595
+ );
596
+ if (event) await state.refresh();
597
+ return event;
598
+ } finally {
599
+ if (respondingRequestRef.current === requestId) {
600
+ respondingRequestRef.current = null;
601
+ setRespondingRequestId(null);
602
+ }
603
+ }
604
+ },
605
+ [client, mutation, sessionId, state, workspaceId]
606
+ );
607
+ const now = Date.now();
608
+ void deadlineVersion;
609
+ return {
610
+ requests: [...state.data ?? []].filter((request) => isActionableHumanInputRequest(request, now)).sort(
611
+ (left, right) => left.createdAt.localeCompare(right.createdAt) || left.id.localeCompare(right.id)
612
+ ),
613
+ loading: state.loading,
614
+ error: state.error,
615
+ refresh: state.refresh,
616
+ respond,
617
+ respondingRequestId,
618
+ mutationError: mutation.mutationError,
619
+ clearMutationError: mutation.clearMutationError
88
620
  };
89
- return client;
90
621
  }
91
622
 
92
623
  // src/hooks/use-session.ts
93
- import { useCallback, useEffect, useMemo, useRef, useState } from "react";
624
+ import { useCallback as useCallback4, useEffect as useEffect3, useMemo, useRef as useRef3, useState as useState3 } from "react";
94
625
  function isTitleEvent(event) {
95
626
  return event.type === "session.title_set";
96
627
  }
628
+ function isSessionDetailEvent(event) {
629
+ return isTitleEvent(event) || event.type.startsWith("session.command.") || event.type.startsWith("session.wait.") || event.type === "session.status.changed" || event.type === "system.update.pending";
630
+ }
97
631
  function useSession(sessionId, options = {}) {
98
632
  const { client, workspaceId, workspaceControlEvent, registerSessionReconciler } = useEmbeddedSessionRead(options);
99
633
  const enabled = (options.enabled ?? true) && Boolean(sessionId);
100
- const [override, setOverride] = useState(null);
101
- const nextReadRevision = useRef(0);
102
- const nextReadGeneration = useRef(0);
634
+ const [override, setOverride] = useState3(null);
635
+ const nextReadRevision = useRef3(0);
636
+ const nextReadGeneration = useRef3(0);
103
637
  const beginRead = options.beginRead;
104
638
  const { run, mutating, mutationError, clearMutationError } = useMutationRunner();
105
- const load = useCallback(
639
+ const load = useCallback4(
106
640
  async (signal) => {
107
641
  if (!sessionId) {
108
642
  return null;
@@ -128,10 +662,10 @@ function useSession(sessionId, options = {}) {
128
662
  pollIntervalMs: options.pollIntervalMs,
129
663
  enabled
130
664
  });
131
- useEffect(() => {
665
+ useEffect3(() => {
132
666
  if (enabled && workspaceControlEvent) void refresh();
133
667
  }, [enabled, refresh, workspaceControlEvent]);
134
- useEffect(() => {
668
+ useEffect3(() => {
135
669
  if (!sessionId || !enabled) return;
136
670
  return registerSessionReconciler(sessionId, "session", refresh);
137
671
  }, [enabled, refresh, registerSessionReconciler, sessionId]);
@@ -142,8 +676,17 @@ function useSession(sessionId, options = {}) {
142
676
  () => base && override && override.id === base.id ? { ...base, title: override.title, titleSource: override.titleSource } : base,
143
677
  [base, override]
144
678
  );
145
- const onTitleEvent = useCallback(
679
+ const sharedEvents = options.events;
680
+ const onTitleEvent = useCallback4(
146
681
  (event) => {
682
+ if (sharedEvents?.some(
683
+ (item) => item.sessionId === sessionId && !isTitleEvent(item) && isSessionDetailEvent(item) && item.sequence > (base?.lastSequence ?? -1)
684
+ )) {
685
+ void refresh();
686
+ } else if (!isTitleEvent(event)) {
687
+ if (!base || event.sequence > base.lastSequence) void refresh();
688
+ return;
689
+ }
147
690
  if (!base || event.sequence <= base.lastSequence) {
148
691
  return;
149
692
  }
@@ -158,13 +701,13 @@ function useSession(sessionId, options = {}) {
158
701
  return { ...next, title, titleSource: source };
159
702
  });
160
703
  },
161
- [base]
704
+ [base, refresh, sharedEvents, sessionId]
162
705
  );
163
- useSessionEventTrigger(client, workspaceId, sessionId, isTitleEvent, onTitleEvent, {
706
+ useSessionEventTrigger(client, workspaceId, sessionId, isSessionDetailEvent, onTitleEvent, {
164
707
  enabled,
165
708
  ...options.events !== void 0 ? { events: options.events } : {}
166
709
  });
167
- const updateTitle = useCallback(
710
+ const updateTitle = useCallback4(
168
711
  async (title) => {
169
712
  if (!sessionId) {
170
713
  return null;
@@ -191,12 +734,109 @@ function useSession(sessionId, options = {}) {
191
734
  };
192
735
  }
193
736
 
737
+ // src/hooks/use-session-control.ts
738
+ import { useCallback as useCallback5, useLayoutEffect, useRef as useRef4 } from "react";
739
+ function useSessionControl(sessionId, options = {}) {
740
+ const { client, workspaceId } = useEmbeddedSession(options);
741
+ const approvalTargetKey = `${workspaceId}\0${sessionId ?? ""}`;
742
+ const pendingApproval = useRef4(null);
743
+ useLayoutEffect(() => {
744
+ if (pendingApproval.current?.targetKey !== approvalTargetKey) {
745
+ pendingApproval.current = null;
746
+ }
747
+ }, [approvalTargetKey]);
748
+ const {
749
+ run: runControl,
750
+ mutating: controlling,
751
+ mutationError: controlError,
752
+ clearMutationError: clearControlError
753
+ } = useMutationRunner(approvalTargetKey);
754
+ const {
755
+ run: runApproval,
756
+ mutating: responding,
757
+ mutationError: approvalError,
758
+ clearMutationError: clearApprovalError
759
+ } = useMutationRunner(approvalTargetKey);
760
+ const pause = useCallback5(
761
+ async (reason) => {
762
+ if (!sessionId) {
763
+ return null;
764
+ }
765
+ return await runControl(
766
+ () => client.pauseSession(workspaceId, sessionId, reason !== void 0 ? { reason } : {})
767
+ );
768
+ },
769
+ [client, workspaceId, sessionId, runControl]
770
+ );
771
+ const resume = useCallback5(
772
+ async (reason) => {
773
+ if (!sessionId) return null;
774
+ return await runControl(
775
+ () => client.resumeSession(workspaceId, sessionId, reason !== void 0 ? { reason } : {})
776
+ );
777
+ },
778
+ [client, workspaceId, sessionId, runControl]
779
+ );
780
+ const decide = useCallback5(
781
+ async (approvalId, decision, message) => {
782
+ if (!sessionId) {
783
+ return null;
784
+ }
785
+ const decisionKey = JSON.stringify([approvalId, decision, message ?? null]);
786
+ if (pendingApproval.current?.decisionKey !== decisionKey) {
787
+ pendingApproval.current = {
788
+ targetKey: approvalTargetKey,
789
+ decisionKey,
790
+ clientEventId: crypto.randomUUID()
791
+ };
792
+ }
793
+ const clientEventId = pendingApproval.current.clientEventId;
794
+ const accepted = await runApproval(
795
+ () => client.sendApprovalDecision(workspaceId, sessionId, {
796
+ approvalId,
797
+ decision,
798
+ ...message !== void 0 ? { message } : {},
799
+ clientEventId
800
+ })
801
+ );
802
+ if (accepted !== null && pendingApproval.current?.targetKey === approvalTargetKey && pendingApproval.current.clientEventId === clientEventId) {
803
+ pendingApproval.current = null;
804
+ }
805
+ return accepted;
806
+ },
807
+ [approvalTargetKey, client, workspaceId, sessionId, runApproval]
808
+ );
809
+ const approve = useCallback5(
810
+ async (approvalId, message) => await decide(approvalId, "approve", message),
811
+ [decide]
812
+ );
813
+ const reject = useCallback5(
814
+ async (approvalId, message) => await decide(approvalId, "reject", message),
815
+ [decide]
816
+ );
817
+ const error = approvalError ?? controlError;
818
+ const clearError = useCallback5(() => {
819
+ clearControlError();
820
+ clearApprovalError();
821
+ }, [clearControlError, clearApprovalError]);
822
+ return {
823
+ pause,
824
+ resume,
825
+ controlling,
826
+ approve,
827
+ reject,
828
+ responding,
829
+ error,
830
+ clearError
831
+ };
832
+ }
833
+
194
834
  // src/hooks/use-session-events.ts
195
- import { startTransition, useCallback as useCallback2, useEffect as useEffect2, useMemo as useMemo2, useRef as useRef2, useState as useState2 } from "react";
196
- var INITIAL_TAIL_PAGE_SIZE = 1e3;
197
- var OLDER_PAGE_SIZE = 5e3;
198
- var NEWER_PAGE_SIZE = 5e3;
199
- var OLDEST_PAGE_SIZE = 1e3;
835
+ import {
836
+ sessionEventStreamCoveredThrough
837
+ } from "@opengeni/sdk";
838
+ import { startTransition, useCallback as useCallback6, useEffect as useEffect4, useMemo as useMemo2, useRef as useRef5, useState as useState4 } from "react";
839
+ var SESSION_HISTORY_PAGE_SIZE = 1e3;
200
840
  var INITIAL_FETCH_CAP = 1;
201
841
  var OLDER_GROUP_TARGET = 32;
202
842
  var OLDER_FETCH_CAP = 2;
@@ -204,35 +844,16 @@ var NEWER_GROUP_TARGET = 32;
204
844
  var NEWER_FETCH_CAP = 2;
205
845
  var OLDEST_GROUP_TARGET = 32;
206
846
  var OLDEST_FETCH_CAP = 2;
207
- var BOUNDARY_PAGE_CAP = 4;
847
+ var BOUNDARY_PAGE_CAP = 1;
848
+ var FOREGROUND_DIRECT_REPLAY_MAX_SEQUENCES = 16;
849
+ var FOREGROUND_COMPACT_PROBE_MAX_SEQUENCES = SESSION_HISTORY_PAGE_SIZE;
850
+ var FOREGROUND_COMPACT_CATCHUP_MAX_EVENTS = 128;
851
+ var FOREGROUND_COMPACT_CATCHUP_MAX_GROUPS = 16;
852
+ var FOREGROUND_COMPACT_CATCHUP_MAX_BYTES = 512 * 1024;
208
853
  var EMPTY_EVENTS = [];
209
854
  var encoder = new TextEncoder();
210
- var decoder = new TextDecoder();
211
- var BROWSER_EVENT_TYPE_MAX_BYTES = 256;
212
- var BROWSER_EVENT_ID_MAX_BYTES = 256;
213
- var BROWSER_EVENT_CLIENT_ID_MAX_BYTES = 4 * 1024;
214
- var BROWSER_EVENT_DUPLICATE_REASON_MAX_BYTES = 4 * 1024;
215
- var BROWSER_EVENT_PAYLOAD_PREVIEW_MAX_BYTES = 48 * 1024;
216
- var BROWSER_EVENT_PAYLOAD_IDENTITY_FIELDS = [
217
- "id",
218
- "callId",
219
- "call_id",
220
- "name",
221
- "toolName",
222
- "status",
223
- "code",
224
- "isError",
225
- "stream",
226
- "commandId",
227
- "sequence",
228
- "coalescedUntil",
229
- "coalescedCount",
230
- "firstSequence",
231
- "lastSequence"
232
- ];
233
- var SESSION_EVENT_BROWSER_MAX_BYTES = 8 * 1024 * 1024;
234
- var SESSION_EVENT_BROWSER_MAX_COUNT = 1e4;
235
- var SESSION_EVENT_BROWSER_SINGLE_EVENT_MAX_BYTES = 96 * 1024;
855
+ var SESSION_EVENT_BROWSER_MAX_BYTES = 16 * 1024 * 1024;
856
+ var SESSION_EVENT_BROWSER_MAX_COUNT = 2e4;
236
857
  var SESSION_EVENT_BROWSER_PENDING_MAX_BYTES = 1024 * 1024;
237
858
  var SESSION_EVENT_BROWSER_PENDING_MAX_COUNT = 256;
238
859
  var EMPTY_EVENT_WINDOW = {
@@ -249,43 +870,75 @@ function useSessionEvents(sessionId, options = {}) {
249
870
  const replay = options.replay ?? "windowed";
250
871
  const fullReplay = replay === "full" || after !== 0;
251
872
  const streamKey = `${workspaceId}\0${sessionId ?? ""}\0${after}\0${fullReplay ? "full" : "windowed"}`;
252
- const [eventWindow, setEventWindow] = useState2(EMPTY_EVENT_WINDOW);
253
- const [connectionState, setConnectionState] = useState2("idle");
254
- const [error, setError] = useState2(null);
255
- const [hasOlder, setHasOlder] = useState2(false);
256
- const [hasNewer, setHasNewer] = useState2(false);
257
- const [sessionStatusProjection, setSessionStatusProjection] = useState2(
873
+ const navigationIdentityRef = useRef5({ client, streamKey, enabled });
874
+ if (navigationIdentityRef.current.client !== client || navigationIdentityRef.current.streamKey !== streamKey || navigationIdentityRef.current.enabled !== enabled) {
875
+ navigationIdentityRef.current = { client, streamKey, enabled };
876
+ }
877
+ const navigationIdentity = navigationIdentityRef.current;
878
+ const ownsNavigation = useCallback6(
879
+ () => navigationIdentityRef.current === navigationIdentity,
880
+ [navigationIdentity]
881
+ );
882
+ const [eventWindow, setEventWindow] = useState4(EMPTY_EVENT_WINDOW);
883
+ const [questionEvidence, setQuestionEvidence] = useState4(null);
884
+ const [connectionState, setConnectionState] = useState4("idle");
885
+ const [error, setError] = useState4(null);
886
+ const [newerError, setNewerError] = useState4(null);
887
+ const [hasOlder, setHasOlder] = useState4(false);
888
+ const [hasNewer, setHasNewer] = useState4(false);
889
+ const [sessionStatusProjection, setSessionStatusProjection] = useState4(
258
890
  null
259
891
  );
260
- const [initialLoading, setInitialLoading] = useState2(true);
261
- const [loadingOlder, setLoadingOlder] = useState2(false);
262
- const [loadingNewer, setLoadingNewer] = useState2(false);
263
- const [loadingOldest, setLoadingOldest] = useState2(false);
264
- const [loadingLatest, setLoadingLatest] = useState2(false);
265
- const [streamEpoch, setStreamEpoch] = useState2(0);
266
- const [viewMode, setViewMode] = useState2("live");
267
- const lastSequenceRef = useRef2(after);
268
- const streamResumeSequenceRef = useRef2(after);
269
- const oldestSequenceRef = useRef2(null);
270
- const newestSequenceRef = useRef2(null);
271
- const hasOlderRef = useRef2(false);
272
- const hasNewerRef = useRef2(false);
273
- const loadingOlderRef = useRef2(false);
274
- const loadingNewerRef = useRef2(false);
275
- const loadingOldestRef = useRef2(false);
276
- const loadingLatestRef = useRef2(false);
277
- const viewModeRef = useRef2("live");
278
- const initialWindowLoadedRef = useRef2(false);
279
- const streamAbortRef = useRef2(null);
280
- const streamKeyRef = useRef2(null);
281
- const generationRef = useRef2(0);
282
- const eventWindowRef = useRef2(EMPTY_EVENT_WINDOW);
283
- const sessionStatusRef = useRef2({
892
+ const [initialLoading, setInitialLoading] = useState4(true);
893
+ const [initialHistoryReady, setInitialHistoryReady] = useState4(false);
894
+ const [loadingOlder, setLoadingOlder] = useState4(false);
895
+ const [loadingNewer, setLoadingNewer] = useState4(false);
896
+ const [loadingOldest, setLoadingOldest] = useState4(false);
897
+ const [loadingLatest, setLoadingLatest] = useState4(false);
898
+ const [loadingTarget, setLoadingTarget] = useState4(false);
899
+ const loadingTargetRef = useRef5(false);
900
+ const [streamEpoch, setStreamEpoch] = useState4(0);
901
+ const [viewMode, setViewMode] = useState4("live");
902
+ const lastSequenceRef = useRef5(after);
903
+ const streamResumeSequenceRef = useRef5(after);
904
+ const oldestSequenceRef = useRef5(null);
905
+ const newestSequenceRef = useRef5(null);
906
+ const hasOlderRef = useRef5(false);
907
+ const hasNewerRef = useRef5(false);
908
+ const loadingOlderRef = useRef5(false);
909
+ const loadingNewerRef = useRef5(false);
910
+ const loadingOldestRef = useRef5(false);
911
+ const loadingLatestRef = useRef5(false);
912
+ const viewModeRef = useRef5("live");
913
+ const initialWindowLoadedRef = useRef5(false);
914
+ const reconcileAfterPageResumeRef = useRef5(false);
915
+ const streamAbortRef = useRef5(null);
916
+ const streamKeyRef = useRef5(null);
917
+ const generationRef = useRef5(0);
918
+ const navigationGenerationRef = useRef5(0);
919
+ const eventWindowRef = useRef5(EMPTY_EVENT_WINDOW);
920
+ const sessionStatusRef = useRef5({
284
921
  sequence: after,
285
922
  status: null
286
923
  });
287
- const [stateStreamKey, setStateStreamKey] = useState2(streamKey);
288
- useEffect2(() => {
924
+ const [stateStreamKey, setStateStreamKey] = useState4(streamKey);
925
+ useEffect4(() => {
926
+ navigationGenerationRef.current += 1;
927
+ setNewerError(null);
928
+ loadingOlderRef.current = false;
929
+ loadingNewerRef.current = false;
930
+ loadingOldestRef.current = false;
931
+ loadingTargetRef.current = false;
932
+ setQuestionEvidence(null);
933
+ setLoadingTarget(false);
934
+ setLoadingOlder(false);
935
+ setLoadingNewer(false);
936
+ setLoadingOldest(false);
937
+ return () => {
938
+ navigationGenerationRef.current += 1;
939
+ };
940
+ }, [client, workspaceId, sessionId, after, enabled, fullReplay]);
941
+ useEffect4(() => {
289
942
  if (streamKeyRef.current !== streamKey) {
290
943
  streamKeyRef.current = streamKey;
291
944
  setStateStreamKey(streamKey);
@@ -301,6 +954,7 @@ function useSessionEvents(sessionId, options = {}) {
301
954
  setLoadingOldest(false);
302
955
  setLoadingLatest(false);
303
956
  setInitialLoading(true);
957
+ setInitialHistoryReady(false);
304
958
  lastSequenceRef.current = after;
305
959
  streamResumeSequenceRef.current = after;
306
960
  oldestSequenceRef.current = null;
@@ -314,24 +968,27 @@ function useSessionEvents(sessionId, options = {}) {
314
968
  viewModeRef.current = "live";
315
969
  setViewMode("live");
316
970
  initialWindowLoadedRef.current = false;
971
+ reconcileAfterPageResumeRef.current = false;
317
972
  }
318
973
  const generation = generationRef.current + 1;
319
974
  generationRef.current = generation;
320
- if (loadingOlderRef.current) {
321
- loadingOlderRef.current = false;
322
- setLoadingOlder(false);
323
- }
324
975
  if (!sessionId || !streamEnabled) {
976
+ if (sessionId && enabled && !pageLive && !fullReplay && viewModeRef.current === "live" && initialWindowLoadedRef.current) {
977
+ reconcileAfterPageResumeRef.current = true;
978
+ }
325
979
  setConnectionState("idle");
326
980
  return;
327
981
  }
328
982
  if (viewMode === "history") {
983
+ reconcileAfterPageResumeRef.current = false;
329
984
  setConnectionState("idle");
330
985
  setInitialLoading(false);
331
986
  return;
332
987
  }
988
+ const reconcileForegroundResume = reconcileAfterPageResumeRef.current && !fullReplay;
989
+ reconcileAfterPageResumeRef.current = false;
333
990
  const controller = new AbortController();
334
- const isCurrent = () => generationRef.current === generation && !controller.signal.aborted;
991
+ const isCurrent = () => generationRef.current === generation && !controller.signal.aborted && navigationIdentityRef.current.client === client && navigationIdentityRef.current.streamKey === streamKey && navigationIdentityRef.current.enabled === enabled;
335
992
  streamAbortRef.current = controller;
336
993
  let pending = [];
337
994
  let pendingBytes = 2;
@@ -366,7 +1023,7 @@ function useSessionEvents(sessionId, options = {}) {
366
1023
  setSessionStatusProjection(status);
367
1024
  }
368
1025
  const current = eventWindowRef.current;
369
- const next = boundBrowserSessionEventWindow([...current.events, ...batch]);
1026
+ const next = appendBrowserSessionEventWindow(current, batch);
370
1027
  const retained = {
371
1028
  ...next,
372
1029
  truncated: current.truncated || next.truncated
@@ -386,13 +1043,57 @@ function useSessionEvents(sessionId, options = {}) {
386
1043
  };
387
1044
  void (async () => {
388
1045
  try {
1046
+ if (reconcileForegroundResume) {
1047
+ setConnectionState("connecting");
1048
+ const cursor = streamResumeSequenceRef.current;
1049
+ let plan = { kind: "resume" };
1050
+ try {
1051
+ plan = await planForegroundCatchup(client, workspaceId, sessionId, cursor, {
1052
+ signal: controller.signal
1053
+ });
1054
+ } catch {
1055
+ if (controller.signal.aborted) return;
1056
+ plan = { kind: "resume" };
1057
+ }
1058
+ if (!isCurrent()) return;
1059
+ if (plan.kind === "append") {
1060
+ const current = eventWindowRef.current;
1061
+ assertAppendOrder(current.events, plan.events);
1062
+ const status = observeSessionStatus(plan.events, sessionStatusRef);
1063
+ const next = appendBrowserSessionEventWindow(current, plan.events);
1064
+ const retained = {
1065
+ ...next,
1066
+ truncated: current.truncated || next.truncated
1067
+ };
1068
+ eventWindowRef.current = retained;
1069
+ oldestSequenceRef.current = retained.events[0]?.sequence ?? null;
1070
+ newestSequenceRef.current = maxResumeSequenceOrNull(retained.events);
1071
+ lastSequenceRef.current = Math.max(lastSequenceRef.current, plan.resumeSequence);
1072
+ streamResumeSequenceRef.current = Math.max(
1073
+ streamResumeSequenceRef.current,
1074
+ plan.resumeSequence
1075
+ );
1076
+ if (status !== void 0) {
1077
+ setSessionStatusProjection(status);
1078
+ }
1079
+ setEventWindow(retained);
1080
+ if (retained.truncated) {
1081
+ hasOlderRef.current = true;
1082
+ setHasOlder(true);
1083
+ }
1084
+ } else if (plan.kind === "reload") {
1085
+ initialWindowLoadedRef.current = false;
1086
+ setError(null);
1087
+ }
1088
+ }
389
1089
  if (!fullReplay && !initialWindowLoadedRef.current) {
390
1090
  setConnectionState("connecting");
391
1091
  const window = await loadEventWindow(client, workspaceId, sessionId, {
392
1092
  before: Number.MAX_SAFE_INTEGER,
393
- pageSize: INITIAL_TAIL_PAGE_SIZE,
1093
+ pageSize: SESSION_HISTORY_PAGE_SIZE,
394
1094
  targetGroups: Number.POSITIVE_INFINITY,
395
1095
  maxFetches: INITIAL_FETCH_CAP,
1096
+ boundaryPageCap: BOUNDARY_PAGE_CAP,
396
1097
  signal: controller.signal
397
1098
  });
398
1099
  if (!isCurrent()) {
@@ -400,6 +1101,13 @@ function useSessionEvents(sessionId, options = {}) {
400
1101
  }
401
1102
  const status = observeSessionStatus(window.events, sessionStatusRef);
402
1103
  const retained = boundBrowserSessionEventWindow(window.events);
1104
+ navigationGenerationRef.current += 1;
1105
+ loadingOlderRef.current = false;
1106
+ loadingNewerRef.current = false;
1107
+ loadingOldestRef.current = false;
1108
+ setLoadingOlder(false);
1109
+ setLoadingNewer(false);
1110
+ setLoadingOldest(false);
403
1111
  eventWindowRef.current = retained;
404
1112
  oldestSequenceRef.current = retained.events[0]?.sequence ?? window.oldestSequence;
405
1113
  newestSequenceRef.current = maxResumeSequenceOrNull(retained.events) ?? window.newestSequence;
@@ -408,6 +1116,8 @@ function useSessionEvents(sessionId, options = {}) {
408
1116
  lastSequenceRef.current = window.newestSequence;
409
1117
  streamResumeSequenceRef.current = window.newestSequence;
410
1118
  initialWindowLoadedRef.current = true;
1119
+ setInitialHistoryReady(true);
1120
+ setError(null);
411
1121
  if (status !== void 0) {
412
1122
  setSessionStatusProjection(status);
413
1123
  }
@@ -425,7 +1135,7 @@ function useSessionEvents(sessionId, options = {}) {
425
1135
  onOpen: () => {
426
1136
  void Promise.resolve().then(() => reconcileSession(sessionId)).catch((cause) => {
427
1137
  if (isCurrent()) {
428
- setError(cause instanceof Error ? cause : new Error(String(cause)));
1138
+ setError(normalizeError(cause));
429
1139
  }
430
1140
  });
431
1141
  },
@@ -437,7 +1147,7 @@ function useSessionEvents(sessionId, options = {}) {
437
1147
  });
438
1148
  for await (const event of stream) {
439
1149
  if (!isCurrent()) break;
440
- const boundedEvent = boundBrowserLegacyEvent(event);
1150
+ const boundedEvent = event;
441
1151
  const boundedEventBytes = browserJsonBytes(boundedEvent);
442
1152
  const separatorBytes = pending.length === 0 ? 0 : 1;
443
1153
  if (pending.length > 0 && (pending.length >= SESSION_EVENT_BROWSER_PENDING_MAX_COUNT || pendingBytes + separatorBytes + boundedEventBytes > SESSION_EVENT_BROWSER_PENDING_MAX_BYTES)) {
@@ -458,7 +1168,7 @@ function useSessionEvents(sessionId, options = {}) {
458
1168
  } catch (cause) {
459
1169
  if (isCurrent()) {
460
1170
  flush();
461
- setError(cause instanceof Error ? cause : new Error(String(cause)));
1171
+ setError(normalizeError(cause));
462
1172
  setConnectionState("error");
463
1173
  setInitialLoading(false);
464
1174
  }
@@ -481,6 +1191,8 @@ function useSessionEvents(sessionId, options = {}) {
481
1191
  workspaceId,
482
1192
  sessionId,
483
1193
  after,
1194
+ enabled,
1195
+ pageLive,
484
1196
  streamEnabled,
485
1197
  fullReplay,
486
1198
  streamKey,
@@ -488,10 +1200,10 @@ function useSessionEvents(sessionId, options = {}) {
488
1200
  viewMode,
489
1201
  reconcileSession
490
1202
  ]);
491
- const navigationBusy = () => loadingOlderRef.current || loadingNewerRef.current || loadingOldestRef.current || loadingLatestRef.current;
492
- const loadOlder = useCallback2(
493
- () => createOlderHistoryLoadReceipt(async (markCommitted) => {
494
- if (!sessionId || navigationBusy() || !hasOlderRef.current) {
1203
+ const navigationBusy = () => loadingTargetRef.current || loadingOlderRef.current || loadingNewerRef.current || loadingOldestRef.current || loadingLatestRef.current;
1204
+ const loadOlder = useCallback6(
1205
+ () => createOlderHistoryLoadReceipt(async (markCommitted, preserveTail, markTailPreserved) => {
1206
+ if (!ownsNavigation() || !sessionId || navigationBusy() || !hasOlderRef.current) {
495
1207
  return false;
496
1208
  }
497
1209
  const before = oldestSequenceRef.current;
@@ -501,18 +1213,20 @@ function useSessionEvents(sessionId, options = {}) {
501
1213
  setHasOlder(false);
502
1214
  return false;
503
1215
  }
504
- const generation = generationRef.current;
1216
+ const generation = navigationGenerationRef.current;
1217
+ const isCurrent = () => ownsNavigation() && navigationGenerationRef.current === generation;
505
1218
  loadingOlderRef.current = true;
506
1219
  setLoadingOlder(true);
507
1220
  let published = false;
508
1221
  try {
509
1222
  const window = await loadEventWindow(client, workspaceId, sessionId, {
510
1223
  before,
511
- pageSize: OLDER_PAGE_SIZE,
1224
+ pageSize: SESSION_HISTORY_PAGE_SIZE,
512
1225
  targetGroups: OLDER_GROUP_TARGET,
513
- maxFetches: OLDER_FETCH_CAP
1226
+ maxFetches: OLDER_FETCH_CAP,
1227
+ isCurrent
514
1228
  });
515
- if (generationRef.current !== generation) {
1229
+ if (!isCurrent()) {
516
1230
  return false;
517
1231
  }
518
1232
  if (window.events.length === 0) {
@@ -523,11 +1237,14 @@ function useSessionEvents(sessionId, options = {}) {
523
1237
  }
524
1238
  const current = eventWindowRef.current;
525
1239
  assertPrependOrder(current.events, window.events);
526
- streamAbortRef.current?.abort();
527
- const status = observeSessionStatus(window.events, sessionStatusRef);
528
1240
  const next = boundBrowserSessionEventWindow([...window.events, ...current.events], {
529
1241
  direction: "oldest"
530
1242
  });
1243
+ if (preserveTail && maxResumeSequence(next.events) < maxResumeSequence(current.events)) {
1244
+ markTailPreserved();
1245
+ return false;
1246
+ }
1247
+ const status = observeSessionStatus(window.events, sessionStatusRef);
531
1248
  const retained = {
532
1249
  ...next,
533
1250
  truncated: current.truncated || next.truncated
@@ -538,17 +1255,21 @@ function useSessionEvents(sessionId, options = {}) {
538
1255
  }
539
1256
  const retainedNewest = maxResumeSequenceOrNull(retained.events);
540
1257
  const previousNewest = maxResumeSequenceOrNull(current.events);
1258
+ const evictedLiveTail = previousNewest !== null && retainedNewest !== null && retainedNewest < previousNewest;
1259
+ if (evictedLiveTail && viewModeRef.current === "live") {
1260
+ streamAbortRef.current?.abort();
1261
+ }
541
1262
  eventWindowRef.current = retained;
542
1263
  markCommitted();
543
1264
  oldestSequenceRef.current = retainedOldest;
544
1265
  newestSequenceRef.current = retainedNewest;
545
- streamResumeSequenceRef.current = maxResumeSequence(retained.events);
1266
+ if (evictedLiveTail) {
1267
+ streamResumeSequenceRef.current = maxResumeSequence(retained.events);
1268
+ }
546
1269
  const olderStillAvailable = window.hasOlder;
547
1270
  hasOlderRef.current = olderStillAvailable;
548
- const evictedLiveTail = previousNewest !== null && retainedNewest !== null && retainedNewest < previousNewest;
549
1271
  let newer = null;
550
1272
  let enterHistory = false;
551
- let reconnectLive = false;
552
1273
  if (viewModeRef.current === "history" || evictedLiveTail) {
553
1274
  const highWater = lastSequenceRef.current;
554
1275
  newer = retainedNewest !== null && (retainedNewest < highWater || retained.truncated);
@@ -557,8 +1278,6 @@ function useSessionEvents(sessionId, options = {}) {
557
1278
  viewModeRef.current = "history";
558
1279
  enterHistory = true;
559
1280
  }
560
- } else {
561
- reconnectLive = true;
562
1281
  }
563
1282
  loadingOlderRef.current = false;
564
1283
  startTransition(() => {
@@ -572,38 +1291,42 @@ function useSessionEvents(sessionId, options = {}) {
572
1291
  }
573
1292
  if (enterHistory) {
574
1293
  setViewMode("history");
575
- } else if (reconnectLive) {
576
- setStreamEpoch((epoch) => epoch + 1);
577
1294
  }
578
1295
  setLoadingOlder(false);
579
1296
  });
580
1297
  published = true;
581
1298
  return olderStillAvailable;
1299
+ } catch (reason) {
1300
+ if (!isCurrent()) return false;
1301
+ throw reason;
582
1302
  } finally {
583
- if (!published) {
1303
+ if (!published && isCurrent()) {
584
1304
  loadingOlderRef.current = false;
585
1305
  setLoadingOlder(false);
586
1306
  }
587
1307
  }
588
1308
  }),
589
- [client, workspaceId, sessionId]
1309
+ [client, workspaceId, sessionId, ownsNavigation]
590
1310
  );
591
- const loadOldest = useCallback2(async () => {
592
- if (!sessionId || navigationBusy() || !hasOlderRef.current) {
1311
+ const loadOldest = useCallback6(async () => {
1312
+ if (!ownsNavigation() || !sessionId || navigationBusy() || !hasOlderRef.current) {
593
1313
  return false;
594
1314
  }
595
- const generation = generationRef.current;
1315
+ const generation = ++navigationGenerationRef.current;
1316
+ const isCurrent = () => ownsNavigation() && navigationGenerationRef.current === generation;
1317
+ setQuestionEvidence(null);
596
1318
  loadingOldestRef.current = true;
597
1319
  setLoadingOldest(true);
598
1320
  let published = false;
599
1321
  try {
600
1322
  const window = await loadForwardEventWindow(client, workspaceId, sessionId, {
601
1323
  after: 0,
602
- pageSize: OLDEST_PAGE_SIZE,
1324
+ pageSize: SESSION_HISTORY_PAGE_SIZE,
603
1325
  targetGroups: OLDEST_GROUP_TARGET,
604
- maxFetches: OLDEST_FETCH_CAP
1326
+ maxFetches: OLDEST_FETCH_CAP,
1327
+ isCurrent
605
1328
  });
606
- if (generationRef.current !== generation) {
1329
+ if (!isCurrent()) {
607
1330
  return false;
608
1331
  }
609
1332
  if (window.events.length === 0) {
@@ -625,8 +1348,11 @@ function useSessionEvents(sessionId, options = {}) {
625
1348
  const newer = window.hasNewer || retained.truncated || retainedNewest !== null && retainedNewest < highWater;
626
1349
  hasNewerRef.current = newer;
627
1350
  initialWindowLoadedRef.current = true;
1351
+ setInitialHistoryReady(true);
1352
+ setError(null);
628
1353
  viewModeRef.current = "history";
629
1354
  loadingOldestRef.current = false;
1355
+ setNewerError(null);
630
1356
  startTransition(() => {
631
1357
  if (status !== void 0) {
632
1358
  setSessionStatusProjection(status);
@@ -639,15 +1365,18 @@ function useSessionEvents(sessionId, options = {}) {
639
1365
  });
640
1366
  published = true;
641
1367
  return newer;
1368
+ } catch (reason) {
1369
+ if (!isCurrent()) return false;
1370
+ throw reason;
642
1371
  } finally {
643
- if (!published) {
1372
+ if (!published && isCurrent()) {
644
1373
  loadingOldestRef.current = false;
645
1374
  setLoadingOldest(false);
646
1375
  }
647
1376
  }
648
- }, [client, workspaceId, sessionId]);
649
- const loadNewer = useCallback2(async () => {
650
- if (!sessionId || navigationBusy() || !hasNewerRef.current) {
1377
+ }, [client, workspaceId, sessionId, ownsNavigation]);
1378
+ const loadNewer = useCallback6(async () => {
1379
+ if (!ownsNavigation() || !sessionId || navigationBusy() || !hasNewerRef.current) {
651
1380
  return false;
652
1381
  }
653
1382
  const afterSequence = newestSequenceRef.current;
@@ -656,21 +1385,24 @@ function useSessionEvents(sessionId, options = {}) {
656
1385
  setHasNewer(false);
657
1386
  return false;
658
1387
  }
659
- const generation = generationRef.current;
1388
+ const generation = navigationGenerationRef.current;
1389
+ const isCurrent = () => ownsNavigation() && navigationGenerationRef.current === generation;
660
1390
  loadingNewerRef.current = true;
661
1391
  setLoadingNewer(true);
662
1392
  let published = false;
663
1393
  try {
664
1394
  const window = await loadForwardEventWindow(client, workspaceId, sessionId, {
665
1395
  after: afterSequence,
666
- pageSize: NEWER_PAGE_SIZE,
1396
+ pageSize: SESSION_HISTORY_PAGE_SIZE,
667
1397
  targetGroups: NEWER_GROUP_TARGET,
668
- maxFetches: NEWER_FETCH_CAP
1398
+ maxFetches: NEWER_FETCH_CAP,
1399
+ isCurrent
669
1400
  });
670
- if (generationRef.current !== generation) {
1401
+ if (!isCurrent()) {
671
1402
  return false;
672
1403
  }
673
1404
  if (window.events.length === 0) {
1405
+ setNewerError(null);
674
1406
  hasNewerRef.current = false;
675
1407
  setHasNewer(false);
676
1408
  streamResumeSequenceRef.current = afterSequence;
@@ -683,9 +1415,7 @@ function useSessionEvents(sessionId, options = {}) {
683
1415
  assertAppendOrder(current.events, window.events);
684
1416
  const status = observeSessionStatus(window.events, sessionStatusRef);
685
1417
  const previousOldest = current.events[0]?.sequence ?? null;
686
- const next = boundBrowserSessionEventWindow([...current.events, ...window.events], {
687
- direction: "newest"
688
- });
1418
+ const next = appendBrowserSessionEventWindow(current, window.events);
689
1419
  const retained = {
690
1420
  ...next,
691
1421
  truncated: current.truncated || next.truncated || window.hasNewer
@@ -713,6 +1443,7 @@ function useSessionEvents(sessionId, options = {}) {
713
1443
  resumeLive = true;
714
1444
  }
715
1445
  loadingNewerRef.current = false;
1446
+ setNewerError(null);
716
1447
  startTransition(() => {
717
1448
  if (status !== void 0) {
718
1449
  setSessionStatusProjection(status);
@@ -728,19 +1459,147 @@ function useSessionEvents(sessionId, options = {}) {
728
1459
  });
729
1460
  published = true;
730
1461
  return newer;
1462
+ } catch (reason) {
1463
+ if (!isCurrent()) {
1464
+ return false;
1465
+ }
1466
+ setNewerError(normalizeError(reason));
1467
+ throw reason;
731
1468
  } finally {
732
- if (!published) {
1469
+ if (!published && isCurrent()) {
733
1470
  loadingNewerRef.current = false;
734
1471
  setLoadingNewer(false);
735
1472
  }
736
1473
  }
737
- }, [client, workspaceId, sessionId]);
738
- const jumpToLatest = useCallback2(async () => {
1474
+ }, [client, workspaceId, sessionId, ownsNavigation]);
1475
+ const jumpToSequence = useCallback6(
1476
+ async (sequence, navigationOptions) => {
1477
+ const signal = navigationOptions?.signal;
1478
+ if (!ownsNavigation() || !sessionId || !Number.isSafeInteger(sequence) || sequence < 1)
1479
+ return false;
1480
+ if (signal?.aborted) return false;
1481
+ const generation = ++navigationGenerationRef.current;
1482
+ setQuestionEvidence(null);
1483
+ const current = () => generation === navigationGenerationRef.current && ownsNavigation();
1484
+ const previousMode = viewModeRef.current;
1485
+ streamAbortRef.current?.abort();
1486
+ generationRef.current += 1;
1487
+ viewModeRef.current = "history";
1488
+ setViewMode("history");
1489
+ loadingOlderRef.current = loadingNewerRef.current = loadingOldestRef.current = false;
1490
+ setLoadingOlder(false);
1491
+ setLoadingNewer(false);
1492
+ setLoadingOldest(false);
1493
+ loadingTargetRef.current = true;
1494
+ setLoadingTarget(true);
1495
+ let published = false;
1496
+ try {
1497
+ const [before, following] = await Promise.all([
1498
+ loadPreviousPage(client, workspaceId, sessionId, sequence + 1, { pageSize: 128 }),
1499
+ loadNextPage(client, workspaceId, sessionId, sequence, { pageSize: 128 })
1500
+ ]);
1501
+ if (signal?.aborted) return false;
1502
+ if (!current()) return false;
1503
+ if (!before.some((event) => event.sequence === sequence)) return false;
1504
+ const prefix = boundBrowserSessionEventWindow(before, {
1505
+ direction: "newest",
1506
+ maxBytes: SESSION_EVENT_BROWSER_MAX_BYTES / 2
1507
+ });
1508
+ const retained = boundBrowserSessionEventWindow([...prefix.events, ...following], {
1509
+ direction: "oldest"
1510
+ });
1511
+ if (!retained.events.some((event) => event.sequence === sequence)) return false;
1512
+ streamAbortRef.current?.abort();
1513
+ generationRef.current += 1;
1514
+ const status = observeSessionStatus(retained.events, sessionStatusRef);
1515
+ eventWindowRef.current = retained;
1516
+ oldestSequenceRef.current = retained.events[0].sequence;
1517
+ newestSequenceRef.current = maxResumeSequenceOrNull(retained.events);
1518
+ lastSequenceRef.current = Math.max(lastSequenceRef.current, newestSequenceRef.current ?? 0);
1519
+ hasOlderRef.current = !isLogStart(retained.events[0]);
1520
+ hasNewerRef.current = following.length > 0 || retained.truncated || (newestSequenceRef.current ?? 0) < lastSequenceRef.current;
1521
+ initialWindowLoadedRef.current = true;
1522
+ setInitialHistoryReady(true);
1523
+ setError(null);
1524
+ viewModeRef.current = "history";
1525
+ setEventWindow(retained);
1526
+ setHasOlder(hasOlderRef.current);
1527
+ setHasNewer(hasNewerRef.current);
1528
+ setInitialLoading(false);
1529
+ setViewMode("history");
1530
+ setNewerError(null);
1531
+ if (status !== void 0) setSessionStatusProjection(status);
1532
+ published = true;
1533
+ return true;
1534
+ } catch (reason) {
1535
+ if (!current() || signal?.aborted) return false;
1536
+ throw reason;
1537
+ } finally {
1538
+ if (current()) {
1539
+ loadingTargetRef.current = false;
1540
+ setLoadingTarget(false);
1541
+ if (!published && previousMode === "live") {
1542
+ viewModeRef.current = "live";
1543
+ setViewMode("live");
1544
+ setStreamEpoch((epoch) => epoch + 1);
1545
+ }
1546
+ }
1547
+ }
1548
+ },
1549
+ [client, workspaceId, sessionId, ownsNavigation]
1550
+ );
1551
+ const jumpToLatestQuestion = useCallback6(
1552
+ async (questionOptions) => {
1553
+ if (!sessionId || !ownsNavigation()) return null;
1554
+ let generation = navigationGenerationRef.current;
1555
+ const current = () => ownsNavigation() && generation === navigationGenerationRef.current;
1556
+ try {
1557
+ const { resolveLatestQuestion } = await import("./latest-question-R77KWSUP.js");
1558
+ if (!current()) return null;
1559
+ const destination = await resolveLatestQuestion({
1560
+ client,
1561
+ workspaceId,
1562
+ sessionId,
1563
+ isCurrent: current,
1564
+ resumeSequence: maxResumeSequence,
1565
+ options: questionOptions
1566
+ });
1567
+ if (!destination || !current()) return null;
1568
+ if (!eventWindowRef.current.events.some((event) => event.sequence === destination.anchor)) {
1569
+ const navigation = jumpToSequence(destination.anchor);
1570
+ generation = navigationGenerationRef.current;
1571
+ if (!await navigation || !current()) return null;
1572
+ }
1573
+ setQuestionEvidence({
1574
+ client,
1575
+ streamKey,
1576
+ anchor: destination.anchor,
1577
+ events: destination.events
1578
+ });
1579
+ return destination.questionSequence;
1580
+ } catch (reason) {
1581
+ if (!current()) return null;
1582
+ throw reason;
1583
+ }
1584
+ },
1585
+ [client, workspaceId, sessionId, streamKey, ownsNavigation, jumpToSequence]
1586
+ );
1587
+ const jumpToLatest = useCallback6(async () => {
1588
+ if (!ownsNavigation()) return;
1589
+ if (loadingTargetRef.current) {
1590
+ navigationGenerationRef.current += 1;
1591
+ loadingTargetRef.current = false;
1592
+ setLoadingTarget(false);
1593
+ }
739
1594
  if (!sessionId || navigationBusy()) {
740
1595
  return;
741
1596
  }
1597
+ navigationGenerationRef.current += 1;
1598
+ setQuestionEvidence(null);
742
1599
  loadingLatestRef.current = true;
743
1600
  setLoadingLatest(true);
1601
+ setNewerError(null);
1602
+ setError(null);
744
1603
  let published = false;
745
1604
  try {
746
1605
  streamAbortRef.current?.abort();
@@ -770,19 +1629,30 @@ function useSessionEvents(sessionId, options = {}) {
770
1629
  setLoadingLatest(false);
771
1630
  }
772
1631
  }
773
- }, [after, sessionId]);
1632
+ }, [after, sessionId, ownsNavigation]);
774
1633
  const identityMatches = stateStreamKey === streamKey;
775
1634
  const visibleEvents = identityMatches ? eventWindow.events : EMPTY_EVENTS;
776
- const timeline = useMemo2(() => buildTimeline(visibleEvents), [visibleEvents]);
1635
+ const timeline = useMemo2(() => {
1636
+ const witness = questionEvidence?.client === client && questionEvidence.streamKey === streamKey && visibleEvents.some((event) => event.sequence === questionEvidence.anchor) ? questionEvidence.events : EMPTY_EVENTS;
1637
+ const timelineOptions = { partialStart: hasOlder || eventWindow.truncated || after > 0 };
1638
+ if (witness.length === 0) return buildTimeline(visibleEvents, timelineOptions);
1639
+ const ids = new Set(visibleEvents.map((event) => event.id));
1640
+ return buildTimeline(
1641
+ [...visibleEvents, ...witness.filter((event) => !ids.has(event.id))],
1642
+ timelineOptions
1643
+ );
1644
+ }, [visibleEvents, hasOlder, eventWindow.truncated, after, questionEvidence, client, streamKey]);
777
1645
  return {
778
1646
  events: visibleEvents,
779
1647
  timeline,
780
1648
  sessionStatus: identityMatches ? sessionStatusProjection : null,
1649
+ sessionStatusSequence: identityMatches ? sessionStatusRef.current.sequence : 0,
781
1650
  connectionState: identityMatches ? connectionState : "idle",
782
1651
  lastSequence: identityMatches ? lastSequenceRef.current : after,
783
1652
  windowBytes: identityMatches ? eventWindow.bytes : 2,
784
1653
  windowTruncated: identityMatches ? eventWindow.truncated : false,
785
1654
  initialLoading: fullReplay ? false : identityMatches ? initialLoading : true,
1655
+ initialHistoryReady: identityMatches && initialHistoryReady,
786
1656
  hasOlder: !identityMatches ? false : hasOlder,
787
1657
  loadingOlder: !identityMatches ? false : loadingOlder,
788
1658
  loadOlder,
@@ -793,13 +1663,16 @@ function useSessionEvents(sessionId, options = {}) {
793
1663
  loadOldest,
794
1664
  loadingLatest: !identityMatches ? false : loadingLatest,
795
1665
  jumpToLatest,
796
- error: identityMatches ? error : null
1666
+ jumpToLatestQuestion,
1667
+ jumpToSequence,
1668
+ loadingTarget: identityMatches && loadingTarget,
1669
+ error: identityMatches ? newerError ?? error : null
797
1670
  };
798
1671
  }
799
1672
  function boundBrowserSessionEventWindow(events, options = {}) {
800
1673
  const maxBytes = Math.max(1024, options.maxBytes ?? SESSION_EVENT_BROWSER_MAX_BYTES);
801
1674
  const maxCount = Math.max(1, Math.floor(options.maxCount ?? SESSION_EVENT_BROWSER_MAX_COUNT));
802
- const safe = events.map(boundBrowserLegacyEvent);
1675
+ const safe = events;
803
1676
  const selected = [];
804
1677
  let bytes = 2;
805
1678
  const direction = options.direction ?? "newest";
@@ -810,7 +1683,7 @@ function boundBrowserSessionEventWindow(events, options = {}) {
810
1683
  const event = safe[index];
811
1684
  const eventBytes = browserJsonBytes(event);
812
1685
  const separator = selected.length === 0 ? 0 : 1;
813
- if (bytes + separator + eventBytes > maxBytes) break;
1686
+ if (selected.length > 0 && bytes + separator + eventBytes > maxBytes) break;
814
1687
  selected.push(event);
815
1688
  bytes += separator + eventBytes;
816
1689
  }
@@ -821,182 +1694,27 @@ function boundBrowserSessionEventWindow(events, options = {}) {
821
1694
  truncated: selected.length < safe.length
822
1695
  };
823
1696
  }
824
- function boundBrowserLegacyEvent(event) {
825
- const serialized = browserSerialize(event);
826
- const originalBytes = serialized.serializable ? encoder.encode(serialized.value).byteLength : null;
827
- const typeIsSafe = browserUtf8Bytes(event.type) <= BROWSER_EVENT_TYPE_MAX_BYTES && !event.type.includes("\n") && !event.type.includes("\r");
828
- const clientEventId = boundBrowserOptionalText(
829
- event.clientEventId,
830
- BROWSER_EVENT_CLIENT_ID_MAX_BYTES
831
- );
832
- const duplicateReason = boundBrowserOptionalText(
833
- event.duplicateReason,
834
- BROWSER_EVENT_DUPLICATE_REASON_MAX_BYTES
835
- );
836
- if (serialized.serializable && originalBytes !== null && originalBytes <= SESSION_EVENT_BROWSER_SINGLE_EVENT_MAX_BYTES && typeIsSafe && clientEventId === event.clientEventId && duplicateReason === event.duplicateReason) {
837
- return event;
838
- }
839
- const envelopeProjection = [
840
- !typeIsSafe ? browserEnvelopeFieldProjection("type", event.type, "session.event.envelope_omitted") : null,
841
- clientEventId !== event.clientEventId ? browserEnvelopeFieldProjection("clientEventId", event.clientEventId, clientEventId) : null,
842
- duplicateReason !== event.duplicateReason ? browserEnvelopeFieldProjection("duplicateReason", event.duplicateReason, duplicateReason) : null
843
- ].filter((field) => field !== null);
844
- const payloadSerialization = browserSerialize(event.payload);
845
- const payloadBytes = payloadSerialization.serializable ? encoder.encode(payloadSerialization.value).byteLength : null;
846
- const preview = truncateBrowserUtf8Middle(
847
- payloadSerialization.value,
848
- BROWSER_EVENT_PAYLOAD_PREVIEW_MAX_BYTES
849
- );
850
- const truncation = {
851
- truncated: true,
852
- surface: "browser_legacy_guard",
853
- reason: serialized.serializable ? "event_envelope_bytes_exceeded" : "event_not_serializable",
854
- originalBytes,
855
- deliveredBytes: 0,
856
- omittedBytes: originalBytes,
857
- estimatedOriginalTokens: originalBytes === null ? null : Math.ceil(originalBytes / 4),
858
- estimatedDeliveredTokens: 0,
859
- fullEvidence: { available: false, reason: "not_retained" },
860
- details: [
861
- {
862
- path: "$.payload",
863
- kind: "payload_preview",
864
- originalBytes: payloadBytes,
865
- deliveredBytes: browserUtf8Bytes(preview)
866
- }
867
- ]
868
- };
869
- const payload = {
870
- ...browserPayloadIdentity(event.payload),
871
- preview,
872
- ...envelopeProjection.length > 0 ? {
873
- originalType: boundBrowserText(event.type, BROWSER_EVENT_TYPE_MAX_BYTES),
874
- envelopeProjection: {
875
- truncated: true,
876
- surface: "browser_legacy_guard",
877
- fields: envelopeProjection
878
- }
879
- } : {},
880
- truncation
881
- };
882
- const bounded = {
883
- id: boundBrowserText(event.id, BROWSER_EVENT_ID_MAX_BYTES),
884
- workspaceId: boundBrowserText(event.workspaceId, BROWSER_EVENT_ID_MAX_BYTES),
885
- sessionId: boundBrowserText(event.sessionId, BROWSER_EVENT_ID_MAX_BYTES),
886
- sequence: event.sequence,
887
- type: typeIsSafe ? event.type : "session.event.envelope_omitted",
888
- payload,
889
- occurredAt: boundBrowserText(event.occurredAt, BROWSER_EVENT_ID_MAX_BYTES),
890
- clientEventId,
891
- turnId: boundBrowserOptionalText(event.turnId, BROWSER_EVENT_ID_MAX_BYTES),
892
- turnGeneration: event.turnGeneration,
893
- turnAttemptId: boundBrowserOptionalText(event.turnAttemptId, BROWSER_EVENT_ID_MAX_BYTES),
894
- turnAssociation: event.turnAssociation,
895
- duplicateOfEventId: boundBrowserOptionalText(
896
- event.duplicateOfEventId,
897
- BROWSER_EVENT_ID_MAX_BYTES
898
- ),
899
- duplicateReason
900
- };
901
- settleBrowserEventTruncation(bounded, truncation);
902
- if (browserJsonBytes(bounded) > SESSION_EVENT_BROWSER_SINGLE_EVENT_MAX_BYTES) {
903
- payload.preview = truncateBrowserUtf8Middle(String(payload.preview), 4 * 1024);
904
- truncation.details = truncation.details.slice(0, 1);
905
- settleBrowserEventTruncation(bounded, truncation);
906
- }
907
- if (browserJsonBytes(bounded) > SESSION_EVENT_BROWSER_SINGLE_EVENT_MAX_BYTES) {
908
- bounded.clientEventId = null;
909
- bounded.duplicateReason = null;
910
- payload.preview = "[legacy event omitted at the browser byte boundary]";
911
- settleBrowserEventTruncation(bounded, truncation);
912
- }
913
- return bounded;
914
- }
915
- function browserJsonBytes(value) {
916
- return encoder.encode(browserSerialize(value).value).byteLength;
917
- }
918
- function browserSerialize(value) {
919
- try {
920
- const serialized = JSON.stringify(value);
921
- return { value: serialized === void 0 ? "null" : serialized, serializable: true };
922
- } catch {
923
- return {
924
- value: '"[unserializable event payload omitted at browser boundary]"',
925
- serializable: false
926
- };
927
- }
928
- }
929
- function browserPayloadIdentity(payload) {
930
- if (!payload || typeof payload !== "object" || Array.isArray(payload)) return {};
931
- const record = payload;
932
- const identity = {};
933
- for (const field of BROWSER_EVENT_PAYLOAD_IDENTITY_FIELDS) {
934
- const value = record[field];
935
- if (typeof value === "string") {
936
- identity[field] = boundBrowserText(value, BROWSER_EVENT_ID_MAX_BYTES);
937
- } else if (typeof value === "number" || typeof value === "boolean" || value === null) {
938
- identity[field] = value;
939
- }
940
- }
941
- return identity;
942
- }
943
- function browserEnvelopeFieldProjection(field, original, delivered) {
944
- return {
945
- field,
946
- originalBytes: typeof original === "string" ? browserUtf8Bytes(original) : 0,
947
- deliveredBytes: typeof delivered === "string" ? browserUtf8Bytes(delivered) : 0
948
- };
949
- }
950
- function boundBrowserOptionalText(value, maxBytes) {
951
- return typeof value === "string" ? boundBrowserText(value, maxBytes) : value;
952
- }
953
- function boundBrowserText(value, maxBytes) {
954
- const bytes = encoder.encode(value);
955
- if (bytes.byteLength <= maxBytes) return value;
956
- const marker = "\u2026[truncated]";
957
- const prefixBudget = Math.max(0, maxBytes - browserUtf8Bytes(marker));
958
- let prefixEnd = Math.min(prefixBudget, bytes.byteLength);
959
- while (prefixEnd > 0 && prefixEnd < bytes.byteLength && isBrowserUtf8Continuation(bytes[prefixEnd])) {
960
- prefixEnd -= 1;
961
- }
962
- return `${decoder.decode(bytes.subarray(0, prefixEnd))}${marker}`;
963
- }
964
- function truncateBrowserUtf8Middle(value, maxBytes) {
965
- const bytes = encoder.encode(value);
966
- if (bytes.byteLength <= maxBytes) return value;
967
- const marker = `\u2026[${bytes.byteLength - maxBytes} bytes omitted]\u2026`;
968
- const contentBudget = Math.max(0, maxBytes - browserUtf8Bytes(marker));
969
- const leftBudget = Math.floor(contentBudget / 2);
970
- const rightBudget = contentBudget - leftBudget;
971
- let leftEnd = Math.min(leftBudget, bytes.byteLength);
972
- while (leftEnd > 0 && leftEnd < bytes.byteLength && isBrowserUtf8Continuation(bytes[leftEnd])) {
973
- leftEnd -= 1;
974
- }
975
- let rightStart = Math.max(0, bytes.byteLength - rightBudget);
976
- while (rightStart < bytes.byteLength && isBrowserUtf8Continuation(bytes[rightStart])) {
977
- rightStart += 1;
978
- }
979
- return `${decoder.decode(bytes.subarray(0, leftEnd))}${marker}${decoder.decode(bytes.subarray(rightStart))}`;
980
- }
981
- function settleBrowserEventTruncation(event, truncation) {
982
- for (let attempt = 0; attempt < 16; attempt += 1) {
983
- const deliveredBytes = browserJsonBytes(event);
984
- const omittedBytes = truncation.originalBytes === null ? null : Math.max(0, truncation.originalBytes - deliveredBytes);
985
- const estimatedDeliveredTokens = Math.ceil(deliveredBytes / 4);
986
- if (truncation.deliveredBytes === deliveredBytes && truncation.omittedBytes === omittedBytes && truncation.estimatedDeliveredTokens === estimatedDeliveredTokens) {
987
- return;
988
- }
989
- truncation.deliveredBytes = deliveredBytes;
990
- truncation.omittedBytes = omittedBytes;
991
- truncation.estimatedDeliveredTokens = estimatedDeliveredTokens;
1697
+ function appendBrowserSessionEventWindow(current, batch, options = {}) {
1698
+ const maxBytes = Math.max(1024, options.maxBytes ?? SESSION_EVENT_BROWSER_MAX_BYTES);
1699
+ const maxCount = Math.max(1, Math.floor(options.maxCount ?? SESSION_EVENT_BROWSER_MAX_COUNT));
1700
+ const events = [...current.events, ...batch];
1701
+ let bytes = current.bytes;
1702
+ for (let index = 0; index < batch.length; index += 1) {
1703
+ bytes += browserJsonBytes(batch[index]) + (current.events.length + index > 0 ? 1 : 0);
992
1704
  }
993
- throw new RangeError("Browser event byte accounting did not converge");
994
- }
995
- function browserUtf8Bytes(value) {
996
- return encoder.encode(value).byteLength;
1705
+ let start = 0;
1706
+ while (events.length - start > 1 && (bytes > maxBytes || events.length - start > maxCount)) {
1707
+ bytes -= browserJsonBytes(events[start]) + 1;
1708
+ start += 1;
1709
+ }
1710
+ return {
1711
+ events: start === 0 ? events : events.slice(start),
1712
+ bytes,
1713
+ truncated: current.truncated || start > 0
1714
+ };
997
1715
  }
998
- function isBrowserUtf8Continuation(value) {
999
- return (value & 192) === 128;
1716
+ function browserJsonBytes(value) {
1717
+ return encoder.encode(JSON.stringify(value)).byteLength;
1000
1718
  }
1001
1719
  function observeSessionStatus(events, ref) {
1002
1720
  let latest = null;
@@ -1010,6 +1728,39 @@ function observeSessionStatus(events, ref) {
1010
1728
  ref.current = latest;
1011
1729
  return latest.status;
1012
1730
  }
1731
+ async function planForegroundCatchup(client, workspaceId, sessionId, cursor, options = {}) {
1732
+ if (options.signal?.aborted) throw abortError();
1733
+ const session = await client.getSession(workspaceId, sessionId, {
1734
+ fresh: true,
1735
+ ...options.signal ? { signal: options.signal } : {}
1736
+ });
1737
+ if (options.signal?.aborted) throw abortError();
1738
+ const durableHead = Math.max(cursor, session.lastSequence);
1739
+ const rawGap = durableHead - cursor;
1740
+ if (rawGap <= FOREGROUND_DIRECT_REPLAY_MAX_SEQUENCES) {
1741
+ return { kind: "resume" };
1742
+ }
1743
+ if (rawGap > FOREGROUND_COMPACT_PROBE_MAX_SEQUENCES) {
1744
+ return { kind: "reload" };
1745
+ }
1746
+ const page = await loadNextPage(client, workspaceId, sessionId, cursor, {
1747
+ pageSize: rawGap,
1748
+ ...options.signal ? { signal: options.signal } : {}
1749
+ });
1750
+ const events = page.filter((event) => eventResumeSequence(event) > cursor);
1751
+ if (events.length === 0) {
1752
+ return { kind: "reload" };
1753
+ }
1754
+ assertAscending(events);
1755
+ const resumeSequence = maxResumeSequence(events);
1756
+ if (resumeSequence < durableHead) {
1757
+ return { kind: "reload" };
1758
+ }
1759
+ if (events.length > FOREGROUND_COMPACT_CATCHUP_MAX_EVENTS || groupCount(events) > FOREGROUND_COMPACT_CATCHUP_MAX_GROUPS || browserJsonBytes(events) > FOREGROUND_COMPACT_CATCHUP_MAX_BYTES) {
1760
+ return { kind: "reload" };
1761
+ }
1762
+ return { kind: "append", events, resumeSequence };
1763
+ }
1013
1764
  async function loadEventWindow(client, workspaceId, sessionId, options) {
1014
1765
  let cursor = options.before;
1015
1766
  let buffer = [];
@@ -1023,6 +1774,7 @@ async function loadEventWindow(client, workspaceId, sessionId, options) {
1023
1774
  pageSize: options.pageSize,
1024
1775
  ...options.signal ? { signal: options.signal } : {}
1025
1776
  });
1777
+ if (options.isCurrent?.() === false) throw abortError();
1026
1778
  fetches += 1;
1027
1779
  if (page.length === 0) {
1028
1780
  reachedStart = true;
@@ -1037,11 +1789,12 @@ async function loadEventWindow(client, workspaceId, sessionId, options) {
1037
1789
  }
1038
1790
  }
1039
1791
  let snapPages = 0;
1040
- while (!reachedStart && findBoundaryIndex(buffer) === -1 && snapPages < BOUNDARY_PAGE_CAP && fetches < options.maxFetches) {
1792
+ while (!reachedStart && findBoundaryIndex(buffer) === -1 && snapPages < (options.boundaryPageCap ?? 0)) {
1041
1793
  const page = await loadPreviousPage(client, workspaceId, sessionId, cursor, {
1042
1794
  pageSize: options.pageSize,
1043
1795
  ...options.signal ? { signal: options.signal } : {}
1044
1796
  });
1797
+ if (options.isCurrent?.() === false) throw abortError();
1045
1798
  fetches += 1;
1046
1799
  snapPages += 1;
1047
1800
  if (page.length === 0) {
@@ -1097,6 +1850,7 @@ async function loadForwardEventWindow(client, workspaceId, sessionId, options) {
1097
1850
  pageSize: options.pageSize,
1098
1851
  ...options.signal ? { signal: options.signal } : {}
1099
1852
  });
1853
+ if (options.isCurrent?.() === false) throw abortError();
1100
1854
  fetches += 1;
1101
1855
  if (page.length === 0) {
1102
1856
  reachedEnd = true;
@@ -1202,12 +1956,9 @@ function maxResumeSequenceOrNull(events) {
1202
1956
  return events.length > 0 ? maxResumeSequence(events) : null;
1203
1957
  }
1204
1958
  function eventResumeSequence(event) {
1205
- const payload = asRecord(event.payload);
1206
- const coalescedUntil = Number(payload.coalescedUntil);
1207
- return Math.max(event.sequence, Number.isFinite(coalescedUntil) ? Math.floor(coalescedUntil) : 0);
1208
- }
1209
- function asRecord(value) {
1210
- return value && typeof value === "object" ? value : {};
1959
+ const streamedCoverage = sessionEventStreamCoveredThrough(event);
1960
+ if (streamedCoverage !== null) return streamedCoverage;
1961
+ return typeof event.coveredThrough === "number" && Number.isSafeInteger(event.coveredThrough) && event.coveredThrough >= event.sequence ? event.coveredThrough : event.sequence;
1211
1962
  }
1212
1963
  function assertAscending(events) {
1213
1964
  for (let index = 1; index < events.length; index += 1) {
@@ -1216,275 +1967,34 @@ function assertAscending(events) {
1216
1967
  }
1217
1968
  }
1218
1969
  }
1219
- function assertPrependOrder(existing, older) {
1220
- if (!shouldAssertDevelopment() || existing.length === 0 || older.length === 0) {
1221
- return;
1222
- }
1223
- if (older[older.length - 1].sequence >= existing[0].sequence) {
1224
- throw new Error("@opengeni/react: loadOlder returned overlapping session events");
1225
- }
1226
- }
1227
- function assertAppendOrder(existing, newer) {
1228
- if (!shouldAssertDevelopment() || existing.length === 0 || newer.length === 0) {
1229
- return;
1230
- }
1231
- if (newer[0].sequence <= existing[existing.length - 1].sequence) {
1232
- throw new Error("@opengeni/react: loadNewer returned overlapping session events");
1233
- }
1234
- }
1235
- function shouldAssertDevelopment() {
1236
- const processEnv = globalThis.process?.env;
1237
- return processEnv !== void 0 && processEnv.NODE_ENV !== "production";
1238
- }
1239
- function abortError() {
1240
- const error = new Error("The operation was aborted.");
1241
- error.name = "AbortError";
1242
- return error;
1243
- }
1244
-
1245
- // src/hooks/use-file-attachments.ts
1246
- import { useCallback as useCallback3, useEffect as useEffect3, useRef as useRef3, useState as useState3 } from "react";
1247
- var isImage = (file) => file.type.startsWith("image/");
1248
- function useFileAttachments(options = {}) {
1249
- const { client, workspaceId } = useEmbeddedFileAttachments(options);
1250
- const pasteFilter = options.pasteFilter ?? isImage;
1251
- const [attachments, setAttachments] = useState3([]);
1252
- const sources = useRef3(/* @__PURE__ */ new Map());
1253
- const previewUrls = useRef3(/* @__PURE__ */ new Map());
1254
- const previewRetainers = useRef3(/* @__PURE__ */ new Map());
1255
- const pendingPreviewRevocations = useRef3(/* @__PURE__ */ new Set());
1256
- const revokePreview = useCallback3((id) => {
1257
- const previewUrl = previewUrls.current.get(id);
1258
- if (!previewUrl) return;
1259
- if ((previewRetainers.current.get(id) ?? 0) > 0) {
1260
- pendingPreviewRevocations.current.add(id);
1261
- return;
1262
- }
1263
- previewUrls.current.delete(id);
1264
- pendingPreviewRevocations.current.delete(id);
1265
- URL.revokeObjectURL(previewUrl);
1266
- }, []);
1267
- const revokeAllPreviews = useCallback3(() => {
1268
- for (const id of previewUrls.current.keys()) revokePreview(id);
1269
- }, [revokePreview]);
1270
- const retainPreview = useCallback3(
1271
- (id) => {
1272
- if (!previewUrls.current.has(id)) return void 0;
1273
- previewRetainers.current.set(id, (previewRetainers.current.get(id) ?? 0) + 1);
1274
- let released = false;
1275
- return () => {
1276
- if (released) return;
1277
- released = true;
1278
- const remaining = (previewRetainers.current.get(id) ?? 1) - 1;
1279
- if (remaining > 0) {
1280
- previewRetainers.current.set(id, remaining);
1281
- return;
1282
- }
1283
- previewRetainers.current.delete(id);
1284
- if (pendingPreviewRevocations.current.has(id)) revokePreview(id);
1285
- };
1286
- },
1287
- [revokePreview]
1288
- );
1289
- const committedAttachmentIds = useRef3(/* @__PURE__ */ new Set());
1290
- useEffect3(() => {
1291
- const currentIds = new Set(attachments.map((attachment) => attachment.id));
1292
- for (const id of committedAttachmentIds.current) {
1293
- if (!currentIds.has(id)) revokePreview(id);
1294
- }
1295
- committedAttachmentIds.current = currentIds;
1296
- }, [attachments, revokePreview]);
1297
- const scopeGeneration = useRef3(0);
1298
- const previousScope = useRef3({ client, workspaceId });
1299
- useEffect3(() => {
1300
- const previous = previousScope.current;
1301
- if (previous.client === client && previous.workspaceId === workspaceId) return;
1302
- previousScope.current = { client, workspaceId };
1303
- scopeGeneration.current += 1;
1304
- sources.current.clear();
1305
- revokeAllPreviews();
1306
- setAttachments([]);
1307
- }, [client, revokeAllPreviews, workspaceId]);
1308
- useEffect3(
1309
- () => () => {
1310
- scopeGeneration.current += 1;
1311
- sources.current.clear();
1312
- revokeAllPreviews();
1313
- },
1314
- [revokeAllPreviews]
1315
- );
1316
- const startUpload = useCallback3(
1317
- (id, file) => {
1318
- const generation = scopeGeneration.current;
1319
- void client.uploadFile(workspaceId, {
1320
- filename: file.name || "file",
1321
- contentType: file.type || "application/octet-stream",
1322
- data: file
1323
- }).then((asset) => {
1324
- if (scopeGeneration.current !== generation) return;
1325
- sources.current.delete(id);
1326
- setAttachments(
1327
- (current) => current.map(
1328
- (attachment) => attachment.id === id ? {
1329
- ...attachment,
1330
- status: "ready",
1331
- file: asset,
1332
- name: asset.filename,
1333
- contentType: asset.contentType,
1334
- sizeBytes: asset.sizeBytes,
1335
- error: void 0
1336
- } : attachment
1337
- )
1338
- );
1339
- }).catch((error) => {
1340
- if (scopeGeneration.current !== generation) return;
1341
- setAttachments(
1342
- (current) => current.map(
1343
- (attachment) => attachment.id === id ? {
1344
- ...attachment,
1345
- status: "failed",
1346
- error: error instanceof Error ? error.message : String(error)
1347
- } : attachment
1348
- )
1349
- );
1350
- });
1351
- },
1352
- [client, workspaceId]
1353
- );
1354
- const addFiles = useCallback3(
1355
- (files) => {
1356
- for (const file of files) {
1357
- const id = crypto.randomUUID();
1358
- sources.current.set(id, file);
1359
- const previewUrl = isImage(file) ? URL.createObjectURL(file) : void 0;
1360
- if (previewUrl) previewUrls.current.set(id, previewUrl);
1361
- setAttachments((current) => [
1362
- ...current,
1363
- {
1364
- id,
1365
- name: file.name || "image",
1366
- contentType: file.type || "application/octet-stream",
1367
- sizeBytes: file.size,
1368
- status: "uploading",
1369
- ...previewUrl ? { previewUrl } : {}
1370
- }
1371
- ]);
1372
- startUpload(id, file);
1373
- }
1374
- },
1375
- [startUpload]
1376
- );
1377
- const retry = useCallback3(
1378
- (id) => {
1379
- const file = sources.current.get(id);
1380
- if (!file) {
1381
- return;
1382
- }
1383
- setAttachments(
1384
- (current) => current.map(
1385
- (attachment) => attachment.id === id ? { ...attachment, status: "uploading", error: void 0 } : attachment
1386
- )
1387
- );
1388
- startUpload(id, file);
1389
- },
1390
- [startUpload]
1391
- );
1392
- const addFromPaste = useCallback3(
1393
- (event) => {
1394
- const clipboardFiles = event.clipboardData?.files;
1395
- if (!clipboardFiles) {
1396
- return;
1397
- }
1398
- const files = [...clipboardFiles].filter(pasteFilter);
1399
- if (files.length > 0) {
1400
- addFiles(files);
1401
- }
1402
- },
1403
- [addFiles, pasteFilter]
1404
- );
1405
- const restoreReadyFiles = useCallback3(
1406
- (files) => {
1407
- const incoming = /* @__PURE__ */ new Map();
1408
- for (const file of files) {
1409
- if (file.status === "ready" && file.workspaceId === workspaceId) {
1410
- incoming.set(file.id, file);
1411
- }
1412
- }
1413
- setAttachments((current) => {
1414
- const unresolved = current.filter((attachment) => attachment.status !== "ready");
1415
- const existingReady = new Map(
1416
- current.flatMap(
1417
- (attachment) => attachment.status === "ready" && attachment.file ? [[attachment.file.id, attachment]] : []
1418
- )
1419
- );
1420
- const restored = [...incoming.values()].map((file) => {
1421
- const existing = existingReady.get(file.id);
1422
- return existing ? {
1423
- ...existing,
1424
- name: file.filename,
1425
- contentType: file.contentType,
1426
- sizeBytes: file.sizeBytes,
1427
- status: "ready",
1428
- file,
1429
- error: void 0
1430
- } : {
1431
- id: `restored:${file.id}`,
1432
- name: file.filename,
1433
- contentType: file.contentType,
1434
- sizeBytes: file.sizeBytes,
1435
- status: "ready",
1436
- file
1437
- // No source File and no object URL: server metadata is the
1438
- // only authority restored across page/device boundaries.
1439
- };
1440
- });
1441
- return [...unresolved, ...restored];
1442
- });
1443
- },
1444
- [workspaceId]
1445
- );
1446
- const remove = useCallback3(
1447
- (id) => {
1448
- sources.current.delete(id);
1449
- revokePreview(id);
1450
- setAttachments((current) => current.filter((attachment) => attachment.id !== id));
1451
- },
1452
- [revokePreview]
1453
- );
1454
- const removeReadyFiles = useCallback3((fileIds) => {
1455
- const accepted = new Set(fileIds);
1456
- if (accepted.size === 0) return;
1457
- setAttachments(
1458
- (current) => current.filter((attachment) => {
1459
- return !(attachment.status === "ready" && attachment.file !== void 0 && accepted.has(attachment.file.id));
1460
- })
1461
- );
1462
- }, []);
1463
- const clear = useCallback3(() => {
1464
- sources.current.clear();
1465
- revokeAllPreviews();
1466
- setAttachments([]);
1467
- }, [revokeAllPreviews]);
1468
- return {
1469
- attachments,
1470
- readyResources: attachments.flatMap(
1471
- (attachment) => attachment.status === "ready" && attachment.file ? [{ kind: "file", fileId: attachment.file.id }] : []
1472
- ),
1473
- uploading: attachments.some((attachment) => attachment.status === "uploading"),
1474
- hasUnresolved: attachments.some((attachment) => attachment.status !== "ready"),
1475
- addFiles,
1476
- addFromPaste,
1477
- restoreReadyFiles,
1478
- retry,
1479
- retainPreview,
1480
- remove,
1481
- removeReadyFiles,
1482
- clear
1483
- };
1484
- }
1970
+ function assertPrependOrder(existing, older) {
1971
+ if (!shouldAssertDevelopment() || existing.length === 0 || older.length === 0) {
1972
+ return;
1973
+ }
1974
+ if (older[older.length - 1].sequence >= existing[0].sequence) {
1975
+ throw new Error("@opengeni/react: loadOlder returned overlapping session events");
1976
+ }
1977
+ }
1978
+ function assertAppendOrder(existing, newer) {
1979
+ if (!shouldAssertDevelopment() || existing.length === 0 || newer.length === 0) {
1980
+ return;
1981
+ }
1982
+ if (newer[0].sequence <= existing[existing.length - 1].sequence) {
1983
+ throw new Error("@opengeni/react: loadNewer returned overlapping session events");
1984
+ }
1985
+ }
1986
+ function shouldAssertDevelopment() {
1987
+ const processEnv = globalThis.process?.env;
1988
+ return processEnv !== void 0 && processEnv.NODE_ENV !== "production";
1989
+ }
1990
+ function abortError() {
1991
+ const error = new Error("The operation was aborted.");
1992
+ error.name = "AbortError";
1993
+ return error;
1994
+ }
1485
1995
 
1486
1996
  // src/hooks/use-turn-queue.ts
1487
- import { useCallback as useCallback4, useEffect as useEffect4, useLayoutEffect, useMemo as useMemo3, useRef as useRef4, useState as useState4 } from "react";
1997
+ import { useCallback as useCallback7, useEffect as useEffect5, useLayoutEffect as useLayoutEffect2, useMemo as useMemo3, useRef as useRef6, useState as useState5 } from "react";
1488
1998
  function isTurnQueueEvent(event) {
1489
1999
  return event.type.startsWith("turn.") || event.type.startsWith("session.queue.") || event.type.startsWith("session.control.") || event.type.startsWith("system.update.") || event.type.startsWith("workspace.inference.");
1490
2000
  }
@@ -1499,18 +2009,18 @@ function useTurnQueue(sessionId, options = {}) {
1499
2009
  const enabled = (options.enabled ?? true) && Boolean(sessionId);
1500
2010
  const targetKey = `${workspaceId}\0${sessionId ?? ""}`;
1501
2011
  const pageLive = usePageLiveActivity();
1502
- const [snapshot, setSnapshot] = useState4(null);
1503
- const [stateTargetKey, setStateTargetKey] = useState4(targetKey);
1504
- const [loading, setLoading] = useState4(enabled);
1505
- const [error, setError] = useState4(null);
1506
- const [mutationError, setMutationError] = useState4(null);
1507
- const [pendingByTurn, setPendingByTurn] = useState4({});
1508
- const [acceptedSteers, setAcceptedSteers] = useState4([]);
1509
- const pendingRef = useRef4({});
1510
- const readGeneration = useRef4(0);
1511
- const targetKeyRef = useRef4(targetKey);
1512
- const snapshotRef = useRef4(null);
1513
- useLayoutEffect(() => {
2012
+ const [snapshot, setSnapshot] = useState5(null);
2013
+ const [stateTargetKey, setStateTargetKey] = useState5(targetKey);
2014
+ const [loading, setLoading] = useState5(enabled);
2015
+ const [error, setError] = useState5(null);
2016
+ const [mutationError, setMutationError] = useState5(null);
2017
+ const [pendingByTurn, setPendingByTurn] = useState5({});
2018
+ const [acceptedSteers, setAcceptedSteers] = useState5([]);
2019
+ const pendingRef = useRef6({});
2020
+ const readGeneration = useRef6(0);
2021
+ const targetKeyRef = useRef6(targetKey);
2022
+ const snapshotRef = useRef6(null);
2023
+ useLayoutEffect2(() => {
1514
2024
  if (targetKeyRef.current === targetKey) return;
1515
2025
  targetKeyRef.current = targetKey;
1516
2026
  readGeneration.current += 1;
@@ -1524,7 +2034,7 @@ function useTurnQueue(sessionId, options = {}) {
1524
2034
  setPendingByTurn({});
1525
2035
  setAcceptedSteers([]);
1526
2036
  }, [enabled, targetKey]);
1527
- useEffect4(() => {
2037
+ useEffect5(() => {
1528
2038
  if (acceptedSteers.length === 0 || options.events === void 0) return;
1529
2039
  const durablyStartedTurnIds = new Set(
1530
2040
  options.events.flatMap(
@@ -1536,7 +2046,7 @@ function useTurnQueue(sessionId, options = {}) {
1536
2046
  (current) => current.filter((steer) => !durablyStartedTurnIds.has(steer.turnId))
1537
2047
  );
1538
2048
  }, [acceptedSteers.length, options.events]);
1539
- const acceptSnapshot = useCallback4(
2049
+ const acceptSnapshot = useCallback7(
1540
2050
  (ownedTargetKey, next) => {
1541
2051
  if (targetKeyRef.current !== ownedTargetKey) return false;
1542
2052
  const current = snapshotRef.current;
@@ -1550,7 +2060,7 @@ function useTurnQueue(sessionId, options = {}) {
1550
2060
  },
1551
2061
  []
1552
2062
  );
1553
- const load = useCallback4(
2063
+ const load = useCallback7(
1554
2064
  async (rejectOnFailure = false) => {
1555
2065
  if (!sessionId) return;
1556
2066
  const ownedTargetKey = `${workspaceId}\0${sessionId}`;
@@ -1581,7 +2091,7 @@ function useTurnQueue(sessionId, options = {}) {
1581
2091
  },
1582
2092
  [client, workspaceId, sessionId, acceptSnapshot]
1583
2093
  );
1584
- useEffect4(() => {
2094
+ useEffect5(() => {
1585
2095
  if (!enabled || !pageLive) {
1586
2096
  setLoading(false);
1587
2097
  return;
@@ -1610,10 +2120,10 @@ function useTurnQueue(sessionId, options = {}) {
1610
2120
  readGeneration.current += 1;
1611
2121
  };
1612
2122
  }, [load, enabled, pageLive, options.pollIntervalMs]);
1613
- useEffect4(() => {
2123
+ useEffect5(() => {
1614
2124
  if (enabled && workspaceControlEvent) void load();
1615
2125
  }, [enabled, load, workspaceControlEvent]);
1616
- useEffect4(() => {
2126
+ useEffect5(() => {
1617
2127
  if (!sessionId || !enabled) return;
1618
2128
  return registerSessionReconciler(sessionId, "queue", load);
1619
2129
  }, [enabled, load, registerSessionReconciler, sessionId]);
@@ -1630,7 +2140,7 @@ function useTurnQueue(sessionId, options = {}) {
1630
2140
  },
1631
2141
  async () => await load(true)
1632
2142
  );
1633
- const mutate = useCallback4(
2143
+ const mutate = useCallback7(
1634
2144
  async (turnId, kind, command) => {
1635
2145
  const ownedTargetKey = targetKey;
1636
2146
  if (!sessionId || targetKeyRef.current !== ownedTargetKey || pendingRef.current[turnId]) {
@@ -1672,7 +2182,7 @@ function useTurnQueue(sessionId, options = {}) {
1672
2182
  },
1673
2183
  [acceptSnapshot, load, sessionId, targetKey]
1674
2184
  );
1675
- const moveTurn = useCallback4(
2185
+ const moveTurn = useCallback7(
1676
2186
  async (turnId, beforeTurnId) => {
1677
2187
  const result = await mutate(
1678
2188
  turnId,
@@ -1687,7 +2197,7 @@ function useTurnQueue(sessionId, options = {}) {
1687
2197
  },
1688
2198
  [client, mutate, sessionId, workspaceId]
1689
2199
  );
1690
- const editTurn = useCallback4(
2200
+ const editTurn = useCallback7(
1691
2201
  async (turnId, edit) => {
1692
2202
  const result = await mutate(
1693
2203
  turnId,
@@ -1703,7 +2213,7 @@ function useTurnQueue(sessionId, options = {}) {
1703
2213
  },
1704
2214
  [client, mutate, sessionId, workspaceId]
1705
2215
  );
1706
- const steerTurn = useCallback4(
2216
+ const steerTurn = useCallback7(
1707
2217
  async (turnId) => {
1708
2218
  const ownedTargetKey = targetKey;
1709
2219
  const turn = snapshotRef.current?.items.find((candidate) => candidate.id === turnId);
@@ -1755,7 +2265,7 @@ function useTurnQueue(sessionId, options = {}) {
1755
2265
  },
1756
2266
  [client, load, mutate, sessionId, targetKey, workspaceId]
1757
2267
  );
1758
- const removeTurn = useCallback4(
2268
+ const removeTurn = useCallback7(
1759
2269
  async (turnId) => {
1760
2270
  const result = await mutate(
1761
2271
  turnId,
@@ -1777,7 +2287,7 @@ function useTurnQueue(sessionId, options = {}) {
1777
2287
  () => Object.keys(visiblePendingByTurn).length > 0,
1778
2288
  [visiblePendingByTurn]
1779
2289
  );
1780
- const mutationFor = useCallback4(
2290
+ const mutationFor = useCallback7(
1781
2291
  (turnId) => visiblePendingByTurn[turnId] ?? null,
1782
2292
  [visiblePendingByTurn]
1783
2293
  );
@@ -1800,7 +2310,7 @@ function useTurnQueue(sessionId, options = {}) {
1800
2310
  mutationFor,
1801
2311
  mutating,
1802
2312
  mutationError: identityMatches ? mutationError : null,
1803
- clearMutationError: useCallback4(() => {
2313
+ clearMutationError: useCallback7(() => {
1804
2314
  if (targetKeyRef.current === targetKey) setMutationError(null);
1805
2315
  }, [targetKey]),
1806
2316
  acceptedSteers: identityMatches ? acceptedSteers : []
@@ -1810,15 +2320,175 @@ function operationKey() {
1810
2320
  return globalThis.crypto.randomUUID();
1811
2321
  }
1812
2322
  function asError(cause) {
1813
- return cause instanceof Error ? cause : new Error(String(cause));
2323
+ return normalizeError(cause);
1814
2324
  }
1815
2325
  function isOutcomeUnknownError(cause) {
1816
2326
  return typeof cause === "object" && cause !== null && cause.outcomeUnknown === true;
1817
2327
  }
1818
2328
 
2329
+ // src/hooks/use-session-conversation.ts
2330
+ import { useLayoutEffect as useLayoutEffect3, useMemo as useMemo4, useRef as useRef7 } from "react";
2331
+
2332
+ // src/hooks/use-client-config-flags.ts
2333
+ import { useEffect as useEffect6, useState as useState6 } from "react";
2334
+ var INITIAL_FLAGS = {
2335
+ loaded: false,
2336
+ uploads: false,
2337
+ modelSelection: false,
2338
+ sandboxFiles: false,
2339
+ defaultReasoningEffort: null,
2340
+ artifacts: true,
2341
+ sessionCreation: true,
2342
+ archive: true,
2343
+ voiceInput: null,
2344
+ realtimeVoice: false,
2345
+ realtimeVoiceOffered: false
2346
+ };
2347
+ var configReads = /* @__PURE__ */ new WeakMap();
2348
+ function readClientConfig(client) {
2349
+ let read = configReads.get(client);
2350
+ if (!read) {
2351
+ read = client.getClientConfig();
2352
+ configReads.set(client, read);
2353
+ read.catch(() => {
2354
+ if (configReads.get(client) === read) configReads.delete(client);
2355
+ });
2356
+ }
2357
+ return read;
2358
+ }
2359
+ function useClientConfigFlags(client) {
2360
+ const [flags, setFlags] = useState6(INITIAL_FLAGS);
2361
+ useEffect6(() => {
2362
+ if (typeof client.getClientConfig !== "function") return;
2363
+ let live = true;
2364
+ readClientConfig(client).then(
2365
+ (config) => {
2366
+ if (live) {
2367
+ setFlags({
2368
+ loaded: true,
2369
+ uploads: config.fileUploads?.enabled === true,
2370
+ // Only an explicit offer shows end users the picker.
2371
+ modelSelection: config.modelSelection === true,
2372
+ sandboxFiles: config.sandboxFiles !== false,
2373
+ defaultReasoningEffort: config.defaultReasoningEffort ?? null,
2374
+ artifacts: config.artifacts !== false,
2375
+ sessionCreation: config.sessionCreation !== false,
2376
+ archive: config.archive !== false,
2377
+ voiceInput: config.voiceInput?.available === true ? config.voiceInput : null,
2378
+ realtimeVoice: config.realtimeVoice !== false,
2379
+ realtimeVoiceOffered: config.realtimeVoice === true
2380
+ });
2381
+ }
2382
+ },
2383
+ () => void 0
2384
+ );
2385
+ return () => {
2386
+ live = false;
2387
+ };
2388
+ }, [client]);
2389
+ return flags;
2390
+ }
2391
+
2392
+ // src/hooks/use-session-conversation.ts
2393
+ function useSessionConversation(sessionId, options = {}) {
2394
+ const context = useOpenGeni(options);
2395
+ const scope = { client: context.client, workspaceId: context.workspaceId };
2396
+ const config = useClientConfigFlags(context.client);
2397
+ const showModelPicker = options.modelPicker ?? config.modelSelection;
2398
+ const catalog = useWorkspaceModelCatalog({ ...scope, enabled: showModelPicker });
2399
+ const feed = useSessionEvents(sessionId, scope);
2400
+ const eventOptions = { ...scope, events: feed.events };
2401
+ const detail = useSession(sessionId, eventOptions);
2402
+ const queue = useTurnQueue(sessionId, eventOptions);
2403
+ const human = useHumanInputRequests(sessionId, eventOptions);
2404
+ const control = useSessionControl(sessionId, scope);
2405
+ const approvals = useMemo4(() => projectPendingApprovals(feed.events), [feed.events]);
2406
+ const files = useFileAttachments(scope);
2407
+ const clearFiles = files.clear;
2408
+ const attachmentSession = useRef7(sessionId);
2409
+ useLayoutEffect3(() => {
2410
+ if (attachmentSession.current === sessionId) return;
2411
+ attachmentSession.current = sessionId;
2412
+ clearFiles();
2413
+ }, [clearFiles, sessionId]);
2414
+ const uploadsEnabled = (options.attachments ?? true) && config.uploads;
2415
+ const status = feed.sessionStatus ?? detail.session?.status;
2416
+ const terminal = status === "cancelled";
2417
+ const importedArchive = detail.session?.importedArchive?.readOnly === true;
2418
+ const host = options.composerOptions;
2419
+ const resolveExtras = () => typeof host?.sendExtras === "function" ? host.sendExtras() : host?.sendExtras;
2420
+ const releaseSentFiles = (input) => files.removeReadyFiles(
2421
+ (input.resources ?? []).flatMap(
2422
+ (resource) => resource.kind === "file" ? [resource.fileId] : []
2423
+ )
2424
+ );
2425
+ const composer = useComposer(sessionId, {
2426
+ ...eventOptions,
2427
+ initialDraft: host?.initialDraft,
2428
+ effectiveControl: queue.effectiveControl ?? detail.session?.effectiveControl,
2429
+ sendDestination: () => queue.queue.length > 0 || status === "running" ? "queue" : "chat",
2430
+ sendExtras: () => {
2431
+ const extras = resolveExtras();
2432
+ return {
2433
+ ...extras,
2434
+ resources: [...extras?.resources ?? [], ...uploadsEnabled ? files.readyResources : []]
2435
+ };
2436
+ },
2437
+ sendBlocked: () => terminal || importedArchive || uploadsEnabled && files.hasUnresolved || host?.sendBlocked?.() === true,
2438
+ onSubmitted: (text, input) => {
2439
+ releaseSentFiles(input);
2440
+ return host?.onSubmitted?.(text, input);
2441
+ },
2442
+ onSent: (text, input) => {
2443
+ releaseSentFiles(input);
2444
+ return host?.onSent?.(text, input);
2445
+ },
2446
+ onDeliveryError: host?.onDeliveryError
2447
+ });
2448
+ const timeline = useMemo4(
2449
+ () => conversationTimeline(feed.timeline, queue, composer),
2450
+ [feed.timeline, queue, composer]
2451
+ );
2452
+ const error = detail.error ?? feed.error ?? human.error;
2453
+ const loadFailed = Boolean(feed.error) && !feed.initialHistoryReady;
2454
+ const retry = async () => {
2455
+ await Promise.all([
2456
+ ...detail.error ? [detail.refresh()] : [],
2457
+ ...human.error ? [human.refresh()] : [],
2458
+ ...feed.error ? [feed.jumpToLatest()] : []
2459
+ ]);
2460
+ };
2461
+ return {
2462
+ ...scope,
2463
+ sessionId,
2464
+ config,
2465
+ catalog,
2466
+ showModelPicker,
2467
+ feed,
2468
+ detail,
2469
+ queue,
2470
+ human,
2471
+ control,
2472
+ approvals,
2473
+ files,
2474
+ uploadsEnabled,
2475
+ composer,
2476
+ /** The same live application context for text and durable voice messages. */
2477
+ getModelContext: () => resolveExtras()?.modelContext,
2478
+ timeline,
2479
+ status,
2480
+ terminal,
2481
+ importedArchive,
2482
+ running: status === "running" || status === "recovering" || status === "waiting_capacity",
2483
+ error,
2484
+ loadFailed,
2485
+ retry
2486
+ };
2487
+ }
2488
+
1819
2489
  // src/hooks/use-goal.ts
1820
2490
  import { OpenGeniApiError } from "@opengeni/sdk";
1821
- import { useCallback as useCallback5, useEffect as useEffect5, useRef as useRef5, useState as useState5 } from "react";
2491
+ import { useCallback as useCallback8, useEffect as useEffect7, useLayoutEffect as useLayoutEffect4, useRef as useRef8, useState as useState7 } from "react";
1822
2492
  function isGoalEvent(event) {
1823
2493
  return event.type.startsWith("goal.");
1824
2494
  }
@@ -1832,31 +2502,38 @@ function useGoal(sessionId, options = {}) {
1832
2502
  const sharedEvents = options.events;
1833
2503
  const sharedFeed = sharedEvents !== void 0;
1834
2504
  const pageLive = usePageLiveActivity();
1835
- const [goal, setGoal] = useState5(null);
1836
- const [loading, setLoading] = useState5(enabled);
1837
- const [error, setError] = useState5(null);
2505
+ const [goal, setGoal] = useState7(null);
2506
+ const [loading, setLoading] = useState7(enabled);
2507
+ const [error, setError] = useState7(null);
1838
2508
  const { run, mutating, mutationError, clearMutationError } = useMutationRunner();
1839
- const generation = useRef5(0);
1840
- const targetKeyRef = useRef5(null);
1841
- const loadAbort = useRef5(null);
1842
- const retireLoads = useCallback5(() => {
2509
+ const generation = useRef8(0);
2510
+ const targetKeyRef = useRef8(null);
2511
+ const goalAbsentRef = useRef8(false);
2512
+ const pendingRefreshRef = useRef8("none");
2513
+ const sharedEventsRef = useRef8(sharedEvents);
2514
+ useLayoutEffect4(() => {
2515
+ sharedEventsRef.current = sharedEvents;
2516
+ }, [sharedEvents]);
2517
+ const loadAbort = useRef8(null);
2518
+ const scheduleRefreshRef = useRef8(() => void 0);
2519
+ const retireLoads = useCallback8(() => {
1843
2520
  generation.current += 1;
1844
2521
  loadAbort.current?.abort();
1845
2522
  loadAbort.current = null;
1846
2523
  }, []);
1847
- const load = useCallback5(async () => {
2524
+ const load = useCallback8(async () => {
1848
2525
  if (!sessionId) {
1849
2526
  return;
1850
2527
  }
1851
2528
  const ticket = ++generation.current;
2529
+ pendingRefreshRef.current = "none";
1852
2530
  loadAbort.current?.abort();
1853
2531
  const controller = new AbortController();
1854
2532
  loadAbort.current = controller;
1855
2533
  try {
1856
- const fetched = await client.getGoal(workspaceId, sessionId, {
1857
- signal: controller.signal
1858
- });
2534
+ const fetched = typeof client.findGoal === "function" ? await client.findGoal(workspaceId, sessionId, { signal: controller.signal }) : await client.getGoal(workspaceId, sessionId, { signal: controller.signal });
1859
2535
  if (ticket === generation.current) {
2536
+ goalAbsentRef.current = fetched === null;
1860
2537
  setGoal(fetched);
1861
2538
  setError(null);
1862
2539
  setLoading(false);
@@ -1866,20 +2543,40 @@ function useGoal(sessionId, options = {}) {
1866
2543
  return;
1867
2544
  }
1868
2545
  if (cause instanceof OpenGeniApiError && cause.status === 404) {
2546
+ goalAbsentRef.current = true;
1869
2547
  setGoal(null);
1870
2548
  setError(null);
1871
2549
  } else {
1872
- setError(cause instanceof Error ? cause : new Error(String(cause)));
2550
+ setError(normalizeError(cause));
1873
2551
  }
1874
2552
  setLoading(false);
1875
2553
  } finally {
1876
2554
  if (loadAbort.current === controller) loadAbort.current = null;
1877
2555
  }
2556
+ if (ticket !== generation.current || pendingRefreshRef.current === "none") {
2557
+ return;
2558
+ }
2559
+ if (goalAbsentRef.current && pendingRefreshRef.current === "refresh") {
2560
+ const events = sharedEventsRef.current ?? [];
2561
+ let latestGoalEvent;
2562
+ for (let index = events.length - 1; index >= 0 && !latestGoalEvent; index -= 1) {
2563
+ const event = events[index];
2564
+ if (event && isGoalEvent(event)) latestGoalEvent = event;
2565
+ }
2566
+ if (!latestGoalEvent || latestGoalEvent.type === "goal.cleared") {
2567
+ pendingRefreshRef.current = "none";
2568
+ return;
2569
+ }
2570
+ pendingRefreshRef.current = "goal";
2571
+ }
2572
+ scheduleRefreshRef.current();
1878
2573
  }, [client, workspaceId, sessionId]);
1879
- useEffect5(() => {
2574
+ useEffect7(() => {
1880
2575
  const targetKey = `${workspaceId} ${sessionId ?? ""}`;
1881
2576
  if (targetKeyRef.current !== targetKey) {
1882
2577
  targetKeyRef.current = targetKey;
2578
+ goalAbsentRef.current = false;
2579
+ pendingRefreshRef.current = "none";
1883
2580
  setGoal(null);
1884
2581
  setError(null);
1885
2582
  }
@@ -1889,7 +2586,8 @@ function useGoal(sessionId, options = {}) {
1889
2586
  return;
1890
2587
  }
1891
2588
  if (sharedFeed) {
1892
- setLoading(false);
2589
+ setLoading(true);
2590
+ void load();
1893
2591
  return () => {
1894
2592
  retireLoads();
1895
2593
  };
@@ -1927,542 +2625,382 @@ function useGoal(sessionId, options = {}) {
1927
2625
  retireLoads,
1928
2626
  sharedFeed
1929
2627
  ]);
1930
- const scheduleRefresh = useDebouncedCallback(() => void load());
1931
- useSessionEventTrigger(client, workspaceId, sessionId, isGoalRefreshEvent, scheduleRefresh, {
1932
- enabled,
1933
- ...sharedEvents !== void 0 ? { events: sharedEvents } : {}
1934
- });
1935
- const pause = useCallback5(
1936
- async (rationale) => {
1937
- if (!sessionId) {
1938
- return null;
1939
- }
1940
- const result = await run(
1941
- () => client.updateGoal(workspaceId, sessionId, {
1942
- status: "paused",
1943
- ...rationale !== void 0 ? { rationale } : {}
1944
- })
1945
- );
1946
- if (result) {
1947
- setGoal(result);
1948
- }
1949
- return result;
1950
- },
1951
- [client, workspaceId, sessionId, run]
1952
- );
1953
- const resume = useCallback5(async () => {
1954
- if (!sessionId) {
1955
- return null;
1956
- }
1957
- const result = await run(() => client.updateGoal(workspaceId, sessionId, { status: "active" }));
1958
- if (result) {
1959
- setGoal(result);
1960
- }
1961
- return result;
1962
- }, [client, workspaceId, sessionId, run]);
1963
- const clearGoal = useCallback5(async () => {
1964
- if (!sessionId) {
2628
+ const scheduleRefresh = useDebouncedCallback(() => {
2629
+ const pending = pendingRefreshRef.current;
2630
+ if (pending === "none" || loadAbort.current !== null) return;
2631
+ if (pending === "refresh" && goalAbsentRef.current) {
2632
+ pendingRefreshRef.current = "none";
1965
2633
  return;
1966
2634
  }
1967
- const ok = await run(async () => {
1968
- await client.deleteGoal(workspaceId, sessionId);
1969
- return true;
1970
- });
1971
- if (ok) {
1972
- retireLoads();
1973
- setGoal(null);
1974
- setError(null);
1975
- }
1976
- }, [client, workspaceId, sessionId, run, retireLoads]);
1977
- return {
1978
- goal,
1979
- isActive: goal?.status === "active",
1980
- isPaused: goal?.status === "paused",
1981
- isCompleted: goal?.status === "completed",
1982
- loading,
1983
- error,
1984
- refresh: load,
1985
- pause,
1986
- resume,
1987
- clearGoal,
1988
- deleteGoal: clearGoal,
1989
- updating: mutating,
1990
- mutationError,
1991
- clearMutationError
1992
- };
1993
- }
1994
-
1995
- // src/hooks/use-session-control.ts
1996
- import { useCallback as useCallback6, useLayoutEffect as useLayoutEffect2, useRef as useRef6 } from "react";
1997
- function useSessionControl(sessionId, options = {}) {
1998
- const { client, workspaceId } = useEmbeddedSession(options);
1999
- const approvalTargetKey = `${workspaceId}\0${sessionId ?? ""}`;
2000
- const pendingApproval = useRef6(null);
2001
- useLayoutEffect2(() => {
2002
- if (pendingApproval.current?.targetKey !== approvalTargetKey) {
2003
- pendingApproval.current = null;
2004
- }
2005
- }, [approvalTargetKey]);
2006
- const {
2007
- run: runControl,
2008
- mutating: controlling,
2009
- mutationError: controlError,
2010
- clearMutationError: clearControlError
2011
- } = useMutationRunner(approvalTargetKey);
2012
- const {
2013
- run: runApproval,
2014
- mutating: responding,
2015
- mutationError: approvalError,
2016
- clearMutationError: clearApprovalError
2017
- } = useMutationRunner(approvalTargetKey);
2018
- const pause = useCallback6(
2019
- async (reason) => {
2020
- if (!sessionId) {
2021
- return null;
2022
- }
2023
- return await runControl(
2024
- () => client.pauseSession(workspaceId, sessionId, reason !== void 0 ? { reason } : {})
2025
- );
2026
- },
2027
- [client, workspaceId, sessionId, runControl]
2028
- );
2029
- const resume = useCallback6(
2030
- async (reason) => {
2031
- if (!sessionId) return null;
2032
- return await runControl(
2033
- () => client.resumeSession(workspaceId, sessionId, reason !== void 0 ? { reason } : {})
2034
- );
2035
- },
2036
- [client, workspaceId, sessionId, runControl]
2037
- );
2038
- const decide = useCallback6(
2039
- async (approvalId, decision, message) => {
2040
- if (!sessionId) {
2041
- return null;
2042
- }
2043
- const decisionKey = JSON.stringify([approvalId, decision, message ?? null]);
2044
- if (pendingApproval.current?.decisionKey !== decisionKey) {
2045
- pendingApproval.current = {
2046
- targetKey: approvalTargetKey,
2047
- decisionKey,
2048
- clientEventId: crypto.randomUUID()
2049
- };
2050
- }
2051
- const clientEventId = pendingApproval.current.clientEventId;
2052
- const accepted = await runApproval(
2053
- () => client.sendApprovalDecision(workspaceId, sessionId, {
2054
- approvalId,
2055
- decision,
2056
- ...message !== void 0 ? { message } : {},
2057
- clientEventId
2058
- })
2059
- );
2060
- if (accepted !== null && pendingApproval.current?.targetKey === approvalTargetKey && pendingApproval.current.clientEventId === clientEventId) {
2061
- pendingApproval.current = null;
2062
- }
2063
- return accepted;
2064
- },
2065
- [approvalTargetKey, client, workspaceId, sessionId, runApproval]
2066
- );
2067
- const approve = useCallback6(
2068
- async (approvalId, message) => await decide(approvalId, "approve", message),
2069
- [decide]
2070
- );
2071
- const reject = useCallback6(
2072
- async (approvalId, message) => await decide(approvalId, "reject", message),
2073
- [decide]
2074
- );
2075
- const error = approvalError ?? controlError;
2076
- const clearError = useCallback6(() => {
2077
- clearControlError();
2078
- clearApprovalError();
2079
- }, [clearControlError, clearApprovalError]);
2080
- return {
2081
- pause,
2082
- resume,
2083
- controlling,
2084
- approve,
2085
- reject,
2086
- responding,
2087
- error,
2088
- clearError
2089
- };
2090
- }
2091
-
2092
- // src/hooks/use-session-mcp-approval-policy.ts
2093
- import { useCallback as useCallback7, useEffect as useEffect6, useLayoutEffect as useLayoutEffect3, useMemo as useMemo4, useRef as useRef7, useState as useState6 } from "react";
2094
- function isSessionMcpApprovalPolicyEvent(event, serverId) {
2095
- if (event.type !== "session.mcp.approval_policy.updated") {
2096
- return false;
2097
- }
2098
- if (serverId === void 0) {
2099
- return true;
2100
- }
2101
- const payload = event.payload;
2102
- return payload?.serverId === serverId;
2103
- }
2104
- function useSessionMcpApprovalPolicy(sessionId, serverId, options = {}) {
2105
- const { client, workspaceId } = useEmbeddedSessionMcpApprovalPolicy(options);
2106
- const enabled = (options.enabled ?? true) && Boolean(sessionId && serverId);
2107
- const [override, setOverride] = useState6(null);
2108
- const targetKey = `${workspaceId}\0${sessionId ?? ""}\0${serverId ?? ""}`;
2109
- const authoritativeGeneration = useRef7(0);
2110
- const mutationIdentity = useMemo4(() => ({ client, targetKey }), [client, targetKey]);
2111
- const { run, mutating, mutationError, clearMutationError } = useMutationRunner(mutationIdentity);
2112
- useLayoutEffect3(() => {
2113
- authoritativeGeneration.current += 1;
2114
- setOverride(null);
2115
- }, [client, targetKey]);
2116
- const load = useCallback7(async () => {
2117
- const generation = authoritativeGeneration.current;
2118
- if (!sessionId || !serverId) {
2119
- return { targetKey, generation, server: null };
2120
- }
2121
- const session = await client.getSession(workspaceId, sessionId);
2122
- return {
2123
- targetKey,
2124
- generation,
2125
- server: session.mcpServers.find((server2) => server2.id === serverId) ?? null
2126
- };
2127
- }, [client, serverId, sessionId, targetKey, workspaceId]);
2128
- const {
2129
- data,
2130
- loading,
2131
- error: loadError,
2132
- refresh
2133
- } = usePolledValue(load, {
2134
- enabled,
2135
- pollIntervalMs: options.pollIntervalMs
2635
+ void load();
2136
2636
  });
2137
- useEffect6(() => {
2138
- if (data?.targetKey === targetKey && data.generation === authoritativeGeneration.current) {
2139
- setOverride(null);
2140
- }
2141
- }, [data, targetKey]);
2142
- const scheduleRefresh = useDebouncedCallback(() => void refresh());
2143
- const matchesPolicyEvent = useCallback7(
2144
- (event) => isSessionMcpApprovalPolicyEvent(event, serverId ?? void 0),
2145
- [serverId]
2637
+ useLayoutEffect4(() => {
2638
+ scheduleRefreshRef.current = scheduleRefresh;
2639
+ }, [scheduleRefresh]);
2640
+ const isRefreshEvent = useCallback8(
2641
+ (event) => goalAbsentRef.current ? isGoalEvent(event) : isGoalRefreshEvent(event),
2642
+ []
2146
2643
  );
2147
- useSessionEventTrigger(
2148
- client,
2149
- workspaceId,
2150
- sessionId,
2151
- matchesPolicyEvent,
2152
- scheduleRefresh,
2153
- {
2154
- enabled,
2155
- ...options.events !== void 0 ? { events: options.events } : {}
2644
+ const onRefreshEvent = useCallback8(
2645
+ (event) => {
2646
+ if (isGoalEvent(event)) pendingRefreshRef.current = "goal";
2647
+ else if (pendingRefreshRef.current === "none") pendingRefreshRef.current = "refresh";
2648
+ scheduleRefresh();
2156
2649
  },
2157
- async () => await refresh()
2650
+ [scheduleRefresh]
2158
2651
  );
2159
- const update = useCallback7(
2160
- async (policy) => {
2161
- if (!sessionId || !serverId) {
2652
+ useSessionEventTrigger(client, workspaceId, sessionId, isRefreshEvent, onRefreshEvent, {
2653
+ enabled,
2654
+ ...sharedEvents !== void 0 ? { events: sharedEvents } : {}
2655
+ });
2656
+ const pause = useCallback8(
2657
+ async (rationale) => {
2658
+ if (!sessionId) {
2162
2659
  return null;
2163
2660
  }
2164
- const response = await run(
2165
- () => client.updateSessionMcpApprovalPolicy(workspaceId, sessionId, serverId, {
2166
- requireApproval: policy
2661
+ const result = await run(
2662
+ () => client.updateGoal(workspaceId, sessionId, {
2663
+ status: "paused",
2664
+ ...rationale !== void 0 ? { rationale } : {}
2167
2665
  })
2168
2666
  );
2169
- if (response) {
2170
- authoritativeGeneration.current += 1;
2171
- setOverride(response.server);
2172
- void refresh();
2667
+ if (result) {
2668
+ setGoal(result);
2173
2669
  }
2174
- return response;
2670
+ return result;
2175
2671
  },
2176
- [client, refresh, run, serverId, sessionId, workspaceId]
2672
+ [client, workspaceId, sessionId, run]
2177
2673
  );
2178
- const server = override ?? data?.server ?? null;
2674
+ const resume = useCallback8(async () => {
2675
+ if (!sessionId) {
2676
+ return null;
2677
+ }
2678
+ const result = await run(() => client.updateGoal(workspaceId, sessionId, { status: "active" }));
2679
+ if (result) {
2680
+ setGoal(result);
2681
+ }
2682
+ return result;
2683
+ }, [client, workspaceId, sessionId, run]);
2684
+ const clearGoal = useCallback8(async () => {
2685
+ if (!sessionId) {
2686
+ return;
2687
+ }
2688
+ const ok = await run(async () => {
2689
+ await client.deleteGoal(workspaceId, sessionId);
2690
+ return true;
2691
+ });
2692
+ if (ok) {
2693
+ retireLoads();
2694
+ setGoal(null);
2695
+ setError(null);
2696
+ }
2697
+ }, [client, workspaceId, sessionId, run, retireLoads]);
2179
2698
  return {
2180
- server,
2181
- policy: server?.requireApproval ?? null,
2699
+ goal,
2700
+ isActive: goal?.status === "active",
2701
+ isPaused: goal?.status === "paused",
2702
+ isCompleted: goal?.status === "completed",
2182
2703
  loading,
2183
- error: mutationError ?? loadError,
2184
- refresh: async () => {
2185
- await refresh();
2186
- },
2187
- update,
2704
+ error,
2705
+ refresh: load,
2706
+ pause,
2707
+ resume,
2708
+ clearGoal,
2709
+ deleteGoal: clearGoal,
2188
2710
  updating: mutating,
2189
- clearError: clearMutationError
2711
+ mutationError,
2712
+ clearMutationError
2190
2713
  };
2191
2714
  }
2192
2715
 
2193
- // src/hooks/use-session-lineage.ts
2194
- import { useCallback as useCallback8, useEffect as useEffect7, useRef as useRef8 } from "react";
2195
- function isLineageRefreshEvent(event) {
2196
- if (event.type === "session.status.changed" || event.type === "session.created") {
2197
- return true;
2198
- }
2199
- if (event.type === "agent.toolCall.output") {
2200
- const payload = event.payload;
2201
- const name = typeof payload?.name === "string" ? payload.name : typeof payload?.toolName === "string" ? payload.toolName : "";
2202
- return name === "session_create" || name.endsWith("__session_create");
2203
- }
2204
- return false;
2205
- }
2206
- function isSessionCreateToolCallCreated(event) {
2207
- if (event.type !== "agent.toolCall.created") {
2208
- return false;
2209
- }
2210
- const payload = event.payload;
2211
- const name = typeof payload?.name === "string" ? payload.name : typeof payload?.toolName === "string" ? payload.toolName : "";
2212
- return name === "session_create" || name.endsWith("__session_create");
2213
- }
2214
- function useSessionLineage(sessionId, options = {}) {
2215
- const { client, workspaceId, workspaceControlEvent, registerSessionReconciler } = useEmbeddedSessionLineage(options);
2216
- const enabled = (options.enabled ?? true) && Boolean(sessionId);
2217
- const nextReadGeneration = useRef8(0);
2218
- const beginRead = options.beginRead;
2219
- const load = useCallback8(
2220
- async (signal) => {
2221
- if (!sessionId) {
2222
- return {
2223
- lineage: { ancestors: [], children: [], truncated: false },
2224
- readGeneration: 0
2225
- };
2226
- }
2227
- let readGeneration = 0;
2228
- const lineage = await client.getSessionLineage(workspaceId, sessionId, {
2229
- signal,
2230
- onRequestStart: (sharedReadGeneration) => {
2231
- readGeneration = sharedReadGeneration ?? beginRead?.() ?? ++nextReadGeneration.current;
2232
- }
2233
- });
2234
- return {
2235
- lineage,
2236
- readGeneration
2237
- };
2238
- },
2239
- [beginRead, client, workspaceId, sessionId]
2716
+ // src/hooks/use-new-conversation.ts
2717
+ import {
2718
+ OpenGeniApiError as OpenGeniApiError2
2719
+ } from "@opengeni/sdk";
2720
+ import { useLayoutEffect as useLayoutEffect5, useMemo as useMemo5, useRef as useRef9, useState as useState8 } from "react";
2721
+ var NOOP_ASYNC = async () => {
2722
+ };
2723
+ function useNewConversation(options = {}) {
2724
+ const context = useOpenGeni(options);
2725
+ const scope = { client: context.client, workspaceId: context.workspaceId };
2726
+ const config = useClientConfigFlags(context.client);
2727
+ const showModelPicker = options.modelPicker ?? config.modelSelection;
2728
+ const catalog = useWorkspaceModelCatalog({ ...scope, enabled: showModelPicker });
2729
+ const files = useFileAttachments(scope);
2730
+ const uploadsEnabled = (options.attachments ?? true) && config.uploads;
2731
+ const available = options.enabled !== false && (options.createSession !== void 0 || config.sessionCreation);
2732
+ const [text, setText] = useState8("");
2733
+ const [choice, setChoice] = useState8({});
2734
+ const [sending, setSending] = useState8(false);
2735
+ const [error, setError] = useState8(null);
2736
+ const [pending, setPending] = useState8(null);
2737
+ const [completion, setCompletion] = useState8(null);
2738
+ const textRef = useRef9(text);
2739
+ const textRevision = useRef9(0);
2740
+ const choiceRef = useRef9(choice);
2741
+ const filesRef = useRef9(files);
2742
+ filesRef.current = files;
2743
+ const pendingRef = useRef9(null);
2744
+ const completionRef = useRef9(null);
2745
+ const inFlight = useRef9(false);
2746
+ const lifetime = useRef9({ mounted: true, generation: 0 }).current;
2747
+ const identity = useMemo5(
2748
+ () => ({
2749
+ client: context.client,
2750
+ workspaceId: context.workspaceId,
2751
+ scopeKey: options.scopeKey
2752
+ }),
2753
+ [context.client, context.workspaceId, options.scopeKey]
2240
2754
  );
2241
- const state = usePolledValue(load, { pollIntervalMs: options.pollIntervalMs, enabled });
2242
- const refresh = state.refresh;
2243
- useEffect7(() => {
2244
- if (enabled && workspaceControlEvent) void refresh();
2245
- }, [enabled, refresh, workspaceControlEvent]);
2246
- useEffect7(() => {
2247
- if (!sessionId || !enabled) return;
2248
- return registerSessionReconciler(sessionId, "lineage", refresh);
2249
- }, [enabled, refresh, registerSessionReconciler, sessionId]);
2250
- const refreshSoon = useDebouncedCallback(() => void refresh(), 150);
2251
- const delayedChildRefreshRef = useRef8(null);
2252
- useEffect7(() => {
2755
+ const committedIdentity = useRef9(identity);
2756
+ useLayoutEffect5(() => {
2757
+ lifetime.mounted = true;
2253
2758
  return () => {
2254
- if (delayedChildRefreshRef.current !== null) {
2255
- clearTimeout(delayedChildRefreshRef.current);
2256
- delayedChildRefreshRef.current = null;
2257
- }
2759
+ lifetime.mounted = false;
2760
+ lifetime.generation++;
2258
2761
  };
2259
- }, [sessionId, workspaceId]);
2260
- const refreshAfterChildCreate = useCallback8(() => {
2261
- void refresh();
2262
- if (delayedChildRefreshRef.current !== null) {
2263
- clearTimeout(delayedChildRefreshRef.current);
2762
+ }, [lifetime]);
2763
+ useLayoutEffect5(() => {
2764
+ if (committedIdentity.current === identity) return;
2765
+ committedIdentity.current = identity;
2766
+ lifetime.generation++;
2767
+ inFlight.current = false;
2768
+ pendingRef.current = null;
2769
+ completionRef.current = null;
2770
+ textRef.current = "";
2771
+ textRevision.current = 0;
2772
+ choiceRef.current = {};
2773
+ setText("");
2774
+ setChoice({});
2775
+ setSending(false);
2776
+ setError(null);
2777
+ setPending(null);
2778
+ setCompletion(null);
2779
+ filesRef.current.clear();
2780
+ }, [identity, lifetime]);
2781
+ const ownsScope = () => lifetime.mounted && committedIdentity.current === identity;
2782
+ const updateText = (value) => {
2783
+ if (!ownsScope()) return;
2784
+ if (value !== textRef.current) textRevision.current++;
2785
+ textRef.current = value;
2786
+ setText(value);
2787
+ };
2788
+ const updateChoice = (patch) => {
2789
+ if (!ownsScope()) return;
2790
+ const next = { ...choiceRef.current, ...patch };
2791
+ const model = catalog.models.find((row) => row.id === (next.model ?? catalog.defaultModel));
2792
+ if (model) {
2793
+ if (next.reasoningEffort)
2794
+ next.reasoningEffort = coerceReasoningEffortForModel(model, next.reasoningEffort);
2795
+ if (next.latencyMode && !runnableLatencyModesForModel(model).includes(next.latencyMode))
2796
+ next.latencyMode = "standard";
2264
2797
  }
2265
- delayedChildRefreshRef.current = setTimeout(() => {
2266
- delayedChildRefreshRef.current = null;
2267
- void refresh();
2268
- }, 2500);
2269
- }, [refresh]);
2270
- useSessionEventTrigger(
2271
- client,
2272
- workspaceId,
2273
- sessionId,
2274
- isLineageRefreshEvent,
2275
- refreshSoon,
2276
- // SHARED-FEED ONLY: without a caller-provided events log the trigger would
2277
- // open its OWN streamEvents tail — a second live SSE connection next to the
2278
- // session route's useSessionEvents. A caller with no feed opts into polling
2279
- // (pollIntervalMs), never a duplicate stream.
2280
- { events: options.events, enabled: enabled && options.events !== void 0 }
2281
- );
2282
- useSessionEventTrigger(
2283
- client,
2284
- workspaceId,
2285
- sessionId,
2286
- isSessionCreateToolCallCreated,
2287
- refreshAfterChildCreate,
2288
- { events: options.events, enabled: enabled && options.events !== void 0 }
2289
- );
2290
- return {
2291
- lineage: state.data?.lineage ?? null,
2292
- loading: state.loading,
2293
- error: state.error,
2294
- readGeneration: state.data?.readGeneration ?? 0,
2295
- refresh
2798
+ choiceRef.current = next;
2799
+ setChoice(choiceRef.current);
2296
2800
  };
2297
- }
2298
-
2299
- // src/approvals.ts
2300
- function approvalsFromRequiresAction(payload) {
2301
- const approvals = payload && typeof payload === "object" ? payload.approvals : void 0;
2302
- if (!Array.isArray(approvals)) {
2303
- return [];
2304
- }
2305
- return approvals.map((approval, index) => {
2306
- const raw = approval && typeof approval === "object" ? approval : {};
2307
- const rawItem = raw.rawItem && typeof raw.rawItem === "object" ? raw.rawItem : {};
2308
- return {
2309
- // Mirror the runtime's canonical interruption identity before retaining
2310
- // the legacy top-level callId fallback for already-normalized payloads.
2311
- id: String(rawItem.callId ?? rawItem.id ?? raw.id ?? raw.callId ?? raw.name ?? index),
2312
- name: String(raw.name ?? raw.toolName ?? rawItem.name ?? "approval"),
2313
- arguments: raw.arguments ?? rawItem.arguments,
2314
- raw: approval
2315
- };
2801
+ const effectivePolicy = () => {
2802
+ const model = choiceRef.current.model ?? catalog.defaultModel;
2803
+ const definition = catalog.models.find((row) => row.id === model);
2804
+ const effort = choiceRef.current.reasoningEffort ?? (definition ? defaultEffortForModel(definition) : config.defaultReasoningEffort ?? "medium");
2805
+ return showModelPicker && model ? {
2806
+ model,
2807
+ reasoningEffort: definition ? coerceReasoningEffortForModel(definition, effort) : effort,
2808
+ latencyMode: choiceRef.current.latencyMode ?? "standard"
2809
+ } : void 0;
2810
+ };
2811
+ const create = options.createSession ?? (async (initialMessage, idempotencyKey, input) => {
2812
+ const creator = context.client.createSession;
2813
+ if (typeof creator !== "function")
2814
+ throw new Error("New chats are not enabled for this product.");
2815
+ const created = await creator.call(context.client, context.workspaceId, {
2816
+ ...input.startMode === "realtime" ? {} : { initialMessage },
2817
+ idempotencyKey,
2818
+ ...input
2819
+ });
2820
+ return created.id;
2316
2821
  });
2317
- }
2318
- function projectPendingApprovals(events) {
2319
- let pending = [];
2320
- let owningTurnId = null;
2321
- for (const event of events) {
2322
- switch (event.type) {
2323
- case "session.requiresAction": {
2324
- pending = approvalsFromRequiresAction(event.payload);
2325
- owningTurnId = event.turnId ?? null;
2326
- break;
2327
- }
2328
- case "user.approvalDecision": {
2329
- const payload = event.payload && typeof event.payload === "object" ? event.payload : {};
2330
- if (typeof payload.approvalId === "string") {
2331
- pending = pending.filter((approval) => approval.id !== payload.approvalId);
2332
- }
2333
- break;
2822
+ const finishCreation = (accepted) => {
2823
+ if (!lifetime.mounted || lifetime.generation !== accepted.generation) return;
2824
+ const { attempt, sessionId } = accepted;
2825
+ const sentFiles = new Set(attempt.options.resources?.map((resource) => resource.fileId) ?? []);
2826
+ const policyChanged = ["model", "reasoningEffort", "latencyMode"].some(
2827
+ (field) => choiceRef.current[field] !== attempt.options[field]
2828
+ );
2829
+ const draft = {
2830
+ text: attempt.clearDraft && textRevision.current === attempt.textRevision ? "" : textRef.current,
2831
+ resources: filesRef.current.readyResources.filter(
2832
+ (resource) => !sentFiles.has(resource.fileId)
2833
+ ),
2834
+ // Unchanged choices are already part of the created session. Do not
2835
+ // replace server-owned defaults with a browser's catalog estimate.
2836
+ ...policyChanged ? { policy: effectivePolicy() } : {}
2837
+ };
2838
+ pendingRef.current = null;
2839
+ completionRef.current = null;
2840
+ setPending(null);
2841
+ setCompletion(null);
2842
+ updateText(draft.text);
2843
+ filesRef.current.removeReadyFiles(sentFiles);
2844
+ notifyObserver(options.onCreated, {
2845
+ sessionId,
2846
+ initialMessage: attempt.initialMessage,
2847
+ draft,
2848
+ ...attempt.realtimeModel ? { realtimeModel: attempt.realtimeModel } : {}
2849
+ });
2850
+ };
2851
+ const finishRef = useRef9(finishCreation);
2852
+ finishRef.current = finishCreation;
2853
+ useLayoutEffect5(() => {
2854
+ if (completion && !files.hasUnresolved) finishRef.current(completion);
2855
+ }, [completion, files.hasUnresolved]);
2856
+ const deliver = async (attempt) => {
2857
+ if (!ownsScope() || inFlight.current || completionRef.current || !available) return false;
2858
+ const ownedGeneration = lifetime.generation;
2859
+ inFlight.current = true;
2860
+ pendingRef.current = attempt;
2861
+ setSending(true);
2862
+ setError(null);
2863
+ setPending(null);
2864
+ try {
2865
+ const sessionId = await attempt.create(
2866
+ attempt.initialMessage,
2867
+ attempt.key,
2868
+ structuredClone(attempt.options)
2869
+ );
2870
+ if (!lifetime.mounted || lifetime.generation !== ownedGeneration) return true;
2871
+ const accepted = { sessionId, attempt, generation: ownedGeneration };
2872
+ completionRef.current = accepted;
2873
+ if (filesRef.current.hasUnresolved) setCompletion(accepted);
2874
+ else finishCreation(accepted);
2875
+ return true;
2876
+ } catch (cause) {
2877
+ if (!lifetime.mounted || lifetime.generation !== ownedGeneration) return false;
2878
+ setError({ cause });
2879
+ if (cause instanceof OpenGeniApiError2 && cause.outcomeUnknown === false) {
2880
+ pendingRef.current = null;
2881
+ } else {
2882
+ setPending(attempt);
2334
2883
  }
2335
- case "turn.completed":
2336
- case "turn.failed":
2337
- case "turn.cancelled": {
2338
- if (owningTurnId === null || event.turnId == null || event.turnId === owningTurnId) {
2339
- pending = [];
2340
- owningTurnId = null;
2341
- }
2342
- break;
2884
+ return false;
2885
+ } finally {
2886
+ if (lifetime.mounted && lifetime.generation === ownedGeneration) {
2887
+ inFlight.current = false;
2888
+ setSending(false);
2343
2889
  }
2344
- default:
2345
- break;
2346
2890
  }
2347
- }
2348
- return pending;
2349
- }
2350
-
2351
- // src/hooks/use-human-input.ts
2352
- import { useCallback as useCallback9, useEffect as useEffect8, useRef as useRef9, useState as useState7 } from "react";
2353
- function isHumanInputEvent(event) {
2354
- return event.type === "session.humanInput.requested" || event.type === "user.humanInputResponse" || event.type === "turn.completed" || event.type === "turn.failed" || event.type === "turn.cancelled";
2355
- }
2356
- function useHumanInputRequests(sessionId, options = {}) {
2357
- const { client, workspaceId, registerSessionReconciler } = useEmbeddedHumanInputSession(options);
2358
- const enabled = (options.enabled ?? true) && Boolean(sessionId);
2359
- const load = useCallback9(
2360
- async () => sessionId ? await client.listHumanInputRequests(workspaceId, sessionId, { status: "pending" }) : [],
2361
- [client, sessionId, workspaceId]
2362
- );
2363
- const state = usePolledValue(load, {
2364
- enabled,
2365
- pollIntervalMs: options.pollIntervalMs
2366
- });
2367
- const loadedRequests = state.data;
2368
- const refreshRequests = state.refresh;
2369
- const mutation = useMutationRunner(`${workspaceId}\0${sessionId ?? ""}`);
2370
- const [respondingRequestId, setRespondingRequestId] = useState7(null);
2371
- const [deadlineVersion, setDeadlineVersion] = useState7(0);
2372
- const respondingRequestRef = useRef9(null);
2373
- useEffect8(() => {
2374
- respondingRequestRef.current = null;
2375
- setRespondingRequestId(null);
2376
- }, [sessionId, workspaceId]);
2377
- useEffect8(() => {
2378
- if (!sessionId || !enabled) return;
2379
- return registerSessionReconciler(sessionId, "human-input", state.refresh);
2380
- }, [enabled, registerSessionReconciler, sessionId, state.refresh]);
2381
- const scheduleRefresh = useDebouncedCallback(() => void state.refresh());
2382
- useSessionEventTrigger(client, workspaceId, sessionId, isHumanInputEvent, scheduleRefresh, {
2383
- enabled,
2384
- ...options.events !== void 0 ? { events: options.events } : {}
2385
- });
2386
- useEffect8(() => {
2387
- const now2 = Date.now();
2388
- const nextDeadline = (loadedRequests ?? []).reduce((next, request) => {
2389
- if (request.status !== "pending" || !request.expiresAt) return next;
2390
- const deadline = Date.parse(request.expiresAt);
2391
- if (!Number.isFinite(deadline) || deadline <= now2) return next;
2392
- return next === null ? deadline : Math.min(next, deadline);
2393
- }, null);
2394
- if (nextDeadline === null) return;
2395
- const timer = globalThis.setTimeout(
2396
- () => {
2397
- setDeadlineVersion((current) => current + 1);
2398
- void refreshRequests();
2399
- },
2400
- Math.min(2147483647, Math.max(0, nextDeadline - now2 + 1))
2401
- );
2402
- return () => globalThis.clearTimeout(timer);
2403
- }, [loadedRequests, refreshRequests]);
2404
- const respond = useCallback9(
2405
- async (requestId, response) => {
2406
- if (!sessionId || respondingRequestRef.current !== null) return null;
2407
- respondingRequestRef.current = requestId;
2408
- setRespondingRequestId(requestId);
2409
- try {
2410
- const event = await mutation.run(
2411
- () => client.submitHumanInputResponse(workspaceId, sessionId, requestId, response, {
2412
- clientEventId: globalThis.crypto.randomUUID()
2413
- })
2414
- );
2415
- if (event) await state.refresh();
2416
- return event;
2417
- } finally {
2418
- if (respondingRequestRef.current === requestId) {
2419
- respondingRequestRef.current = null;
2420
- setRespondingRequestId(null);
2421
- }
2891
+ };
2892
+ const submit = async (explicit) => {
2893
+ if (!ownsScope() || !available || inFlight.current || pendingRef.current || uploadsEnabled && filesRef.current.hasUnresolved)
2894
+ return false;
2895
+ const resources = uploadsEnabled ? filesRef.current.readyResources : [];
2896
+ const draftText = explicit ?? textRef.current;
2897
+ const initialMessage = draftText.trim() || (resources.length > 0 ? FILE_ONLY_MESSAGE_TEXT : "");
2898
+ if (!initialMessage) return false;
2899
+ return deliver({
2900
+ key: crypto.randomUUID(),
2901
+ initialMessage,
2902
+ textRevision: textRevision.current,
2903
+ clearDraft: explicit === void 0,
2904
+ create,
2905
+ options: {
2906
+ ...resources.length ? { resources: structuredClone(resources) } : {},
2907
+ ...showModelPicker ? { ...choiceRef.current } : {}
2422
2908
  }
2909
+ });
2910
+ };
2911
+ const startRealtime = async (model) => {
2912
+ if (!ownsScope() || !available || inFlight.current || pendingRef.current || !config.realtimeVoice || !(options.realtimeVoice ?? config.realtimeVoiceOffered))
2913
+ return false;
2914
+ if (filesRef.current.hasUnresolved) return false;
2915
+ return deliver({
2916
+ key: crypto.randomUUID(),
2917
+ initialMessage: "",
2918
+ textRevision: textRevision.current,
2919
+ clearDraft: false,
2920
+ create,
2921
+ options: { ...showModelPicker ? { ...choiceRef.current } : {}, startMode: "realtime" },
2922
+ realtimeModel: model
2923
+ });
2924
+ };
2925
+ const composer = {
2926
+ value: text,
2927
+ setValue: updateText,
2928
+ hasDraftContent: () => textRef.current.length > 0 || filesRef.current.attachments.length > 0,
2929
+ send: submit,
2930
+ steer: submit,
2931
+ sending,
2932
+ canSend: available && !sending && !pending && !completion && (text.trim().length > 0 || uploadsEnabled && files.readyResources.length > 0) && !(uploadsEnabled && files.hasUnresolved),
2933
+ pause: NOOP_ASYNC,
2934
+ pausing: false,
2935
+ resume: NOOP_ASYNC,
2936
+ resumeScope: NOOP_ASYNC,
2937
+ resuming: false,
2938
+ draft: null,
2939
+ draftRevision: 0,
2940
+ draftLoading: false,
2941
+ draftSaving: false,
2942
+ draftConflict: null,
2943
+ policy: effectivePolicy() ?? null,
2944
+ setModel: (model) => updateChoice({ model }),
2945
+ setReasoningEffort: (reasoningEffort) => updateChoice({ reasoningEffort }),
2946
+ setLatencyMode: (latencyMode) => updateChoice({ latencyMode }),
2947
+ draftPersistence: "disabled",
2948
+ applyDraft: () => {
2423
2949
  },
2424
- [client, mutation, sessionId, state, workspaceId]
2425
- );
2426
- const now = Date.now();
2427
- void deadlineVersion;
2950
+ reloadDraft: NOOP_ASYNC,
2951
+ resolveDraftConflict: NOOP_ASYNC,
2952
+ restoredResources: [],
2953
+ removeRestoredResource: () => {
2954
+ },
2955
+ error: null,
2956
+ clearError: () => {
2957
+ if (ownsScope()) setError(null);
2958
+ }
2959
+ };
2428
2960
  return {
2429
- requests: [...state.data ?? []].filter((request) => isActionableHumanInputRequest(request, now)).sort(
2430
- (left, right) => left.createdAt.localeCompare(right.createdAt) || left.id.localeCompare(right.id)
2431
- ),
2432
- loading: state.loading,
2433
- error: state.error,
2434
- refresh: state.refresh,
2435
- respond,
2436
- respondingRequestId,
2437
- mutationError: mutation.mutationError,
2438
- clearMutationError: mutation.clearMutationError
2961
+ ...scope,
2962
+ config,
2963
+ files,
2964
+ uploadsEnabled,
2965
+ available,
2966
+ showModelPicker,
2967
+ catalog,
2968
+ composer,
2969
+ realtimeVoiceEnabled: config.realtimeVoice && (options.realtimeVoice ?? config.realtimeVoiceOffered),
2970
+ error,
2971
+ finishingUploads: completion !== null,
2972
+ pending: pending ? { idempotencyKey: pending.key, realtimeModel: pending.realtimeModel } : null,
2973
+ retry: () => pendingRef.current ? deliver(pendingRef.current) : Promise.resolve(false),
2974
+ startRealtime
2439
2975
  };
2440
2976
  }
2441
2977
 
2442
2978
  export {
2443
- createEmbeddedSessionClient,
2979
+ approvalsFromRequiresAction,
2980
+ projectPendingApprovals,
2981
+ invokeOlderHistoryLoaderWithReceiptCapture,
2982
+ createOlderHistoryLoadReceipt,
2983
+ conversationTimeline,
2984
+ useAvailableModels,
2985
+ useWorkspaceModelCatalog,
2986
+ useClientConfigFlags,
2987
+ useFileAttachments,
2988
+ isHumanInputEvent,
2989
+ useHumanInputRequests,
2444
2990
  isTitleEvent,
2445
2991
  useSession,
2992
+ useSessionControl,
2446
2993
  SESSION_EVENT_BROWSER_MAX_BYTES,
2447
2994
  SESSION_EVENT_BROWSER_MAX_COUNT,
2448
- SESSION_EVENT_BROWSER_SINGLE_EVENT_MAX_BYTES,
2449
2995
  SESSION_EVENT_BROWSER_PENDING_MAX_BYTES,
2450
2996
  SESSION_EVENT_BROWSER_PENDING_MAX_COUNT,
2451
2997
  useSessionEvents,
2452
2998
  boundBrowserSessionEventWindow,
2453
- useFileAttachments,
2454
2999
  isTurnQueueEvent,
2455
3000
  useTurnQueue,
3001
+ useSessionConversation,
2456
3002
  isGoalEvent,
2457
3003
  useGoal,
2458
- useSessionControl,
2459
- isSessionMcpApprovalPolicyEvent,
2460
- useSessionMcpApprovalPolicy,
2461
- isLineageRefreshEvent,
2462
- useSessionLineage,
2463
- approvalsFromRequiresAction,
2464
- projectPendingApprovals,
2465
- isHumanInputEvent,
2466
- useHumanInputRequests
3004
+ useNewConversation
2467
3005
  };
2468
- //# sourceMappingURL=chunk-3AGBGTRT.js.map
3006
+ //# sourceMappingURL=chunk-X5UVUIMF.js.map