@opengeni/react 3.1.0 → 3.1.3-canary.38890816741001

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 (863) hide show
  1. package/README.md +769 -22
  2. package/dist/accounts.js +89 -11
  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-BO2IWXVK.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-RGALCTWO.js → chunk-2I6R3GCZ.js} +4285 -3980
  21. package/dist/chunk-2I6R3GCZ.js.map +1 -0
  22. package/dist/{chunk-D3UIAJKY.js → chunk-2SFFYIH3.js} +1179 -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-3UZCWK46.js +298 -0
  27. package/dist/chunk-3UZCWK46.js.map +1 -0
  28. package/dist/chunk-47CKUXTW.js +293 -0
  29. package/dist/chunk-47CKUXTW.js.map +1 -0
  30. package/dist/chunk-4QYP6QUI.js +109 -0
  31. package/dist/chunk-4QYP6QUI.js.map +1 -0
  32. package/dist/chunk-5EY26VZ7.js +1053 -0
  33. package/dist/chunk-5EY26VZ7.js.map +1 -0
  34. package/dist/chunk-5I4VRWBB.js +56 -0
  35. package/dist/chunk-5I4VRWBB.js.map +1 -0
  36. package/dist/chunk-5WOR7UCL.js +191 -0
  37. package/dist/chunk-5WOR7UCL.js.map +1 -0
  38. package/dist/chunk-5YJLGOM5.js +51 -0
  39. package/dist/chunk-5YJLGOM5.js.map +1 -0
  40. package/dist/chunk-6JFN3OWG.js +386 -0
  41. package/dist/chunk-6JFN3OWG.js.map +1 -0
  42. package/dist/chunk-7QJT5O4S.js +35 -0
  43. package/dist/chunk-7QJT5O4S.js.map +1 -0
  44. package/dist/chunk-A55KV72M.js +402 -0
  45. package/dist/chunk-A55KV72M.js.map +1 -0
  46. package/dist/chunk-ADWCLI4G.js +2028 -0
  47. package/dist/chunk-ADWCLI4G.js.map +1 -0
  48. package/dist/chunk-B27E3V37.js +29 -0
  49. package/dist/chunk-B27E3V37.js.map +1 -0
  50. package/dist/chunk-B7TC2A33.js +83 -0
  51. package/dist/chunk-B7TC2A33.js.map +1 -0
  52. package/dist/chunk-BLL56F7R.js +385 -0
  53. package/dist/chunk-BLL56F7R.js.map +1 -0
  54. package/dist/chunk-BOFEQC6K.js +162 -0
  55. package/dist/chunk-BOFEQC6K.js.map +1 -0
  56. package/dist/chunk-CM7LVUKC.js +35 -0
  57. package/dist/chunk-CM7LVUKC.js.map +1 -0
  58. package/dist/chunk-CORUP6JB.js +122 -0
  59. package/dist/chunk-CORUP6JB.js.map +1 -0
  60. package/dist/chunk-CSX5FFKZ.js +35 -0
  61. package/dist/chunk-CSX5FFKZ.js.map +1 -0
  62. package/dist/chunk-EGVHJKGJ.js +3125 -0
  63. package/dist/chunk-EGVHJKGJ.js.map +1 -0
  64. package/dist/{chunk-NEXLLTP4.js → chunk-G76LSA76.js} +6 -5
  65. package/dist/chunk-G76LSA76.js.map +1 -0
  66. package/dist/{chunk-PEI6RANI.js → chunk-GQMRL52F.js} +311 -75
  67. package/dist/chunk-GQMRL52F.js.map +1 -0
  68. package/dist/{chunk-ELIYWBZR.js → chunk-GUUXOISA.js} +1113 -457
  69. package/dist/chunk-GUUXOISA.js.map +1 -0
  70. package/dist/{chunk-HUIKIDM5.js → chunk-I7X2P4FU.js} +225 -116
  71. package/dist/chunk-I7X2P4FU.js.map +1 -0
  72. package/dist/chunk-IGFZFLI2.js +1390 -0
  73. package/dist/chunk-IGFZFLI2.js.map +1 -0
  74. package/dist/{chunk-4MDLEHXL.js → chunk-IMSUVYQB.js} +26 -13
  75. package/dist/chunk-IMSUVYQB.js.map +1 -0
  76. package/dist/chunk-IOYHDEP3.js +38 -0
  77. package/dist/chunk-IOYHDEP3.js.map +1 -0
  78. package/dist/chunk-IZV5FTJC.js +55 -0
  79. package/dist/chunk-IZV5FTJC.js.map +1 -0
  80. package/dist/chunk-KF33GTWN.js +343 -0
  81. package/dist/chunk-KF33GTWN.js.map +1 -0
  82. package/dist/chunk-KSGQ5ODA.js +457 -0
  83. package/dist/chunk-KSGQ5ODA.js.map +1 -0
  84. package/dist/{chunk-OAM4WAAM.js → chunk-KZC6QRNR.js} +1513 -697
  85. package/dist/chunk-KZC6QRNR.js.map +1 -0
  86. package/dist/chunk-LJUMYUBO.js +218 -0
  87. package/dist/chunk-LJUMYUBO.js.map +1 -0
  88. package/dist/chunk-LSW2OJVV.js +380 -0
  89. package/dist/chunk-LSW2OJVV.js.map +1 -0
  90. package/dist/chunk-M357MHH3.js +2 -0
  91. package/dist/chunk-M357MHH3.js.map +1 -0
  92. package/dist/chunk-MK2TQO4Y.js +24 -0
  93. package/dist/chunk-MK2TQO4Y.js.map +1 -0
  94. package/dist/chunk-N7S72EXK.js +25 -0
  95. package/dist/chunk-N7S72EXK.js.map +1 -0
  96. package/dist/{chunk-XXWH2JLC.js → chunk-NKMYZFHA.js} +616 -621
  97. package/dist/chunk-NKMYZFHA.js.map +1 -0
  98. package/dist/chunk-NX4ADH73.js +14 -0
  99. package/dist/chunk-NX4ADH73.js.map +1 -0
  100. package/dist/chunk-O7F6UJO4.js +71 -0
  101. package/dist/chunk-O7F6UJO4.js.map +1 -0
  102. package/dist/chunk-OVB5WIG5.js +14064 -0
  103. package/dist/chunk-OVB5WIG5.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-MZ2G2XBA.js → chunk-V5ZUJT2T.js} +205 -45
  135. package/dist/chunk-V5ZUJT2T.js.map +1 -0
  136. package/dist/chunk-VIUEOHXY.js +394 -0
  137. package/dist/chunk-VIUEOHXY.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-YJFGAFBD.js → chunk-X5UVUIMF.js} +1892 -1223
  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/fleet-tile.d.ts +2 -2
  200. package/dist/components/grok-mark.d.ts +26 -0
  201. package/dist/components/human-input-form.d.ts +28 -32
  202. package/dist/components/human-input-surface.d.ts +7 -2
  203. package/dist/components/linkified-text.d.ts +18 -0
  204. package/dist/components/machine-input-display.d.ts +22 -1
  205. package/dist/components/machines/agent-status-icon.d.ts +6 -0
  206. package/dist/components/machines/machine-detail.d.ts +7 -1
  207. package/dist/components/machines/machine-status.d.ts +48 -0
  208. package/dist/components/markdown-table-layout.d.ts +18 -0
  209. package/dist/components/markdown.d.ts +37 -4
  210. package/dist/components/message-timeline.d.ts +86 -7
  211. package/dist/components/model-mark.d.ts +29 -0
  212. package/dist/components/model-policy-picker-menu.d.ts +9 -0
  213. package/dist/components/model-policy-picker.d.ts +41 -25
  214. package/dist/components/new-conversation.d.ts +32 -0
  215. package/dist/components/open-geni-chat.d.ts +73 -0
  216. package/dist/components/open-geni-links.d.ts +68 -0
  217. package/dist/components/openrouter-mark.d.ts +6 -0
  218. package/dist/components/opper-mark.d.ts +7 -0
  219. package/dist/components/preview-loading.d.ts +2 -0
  220. package/dist/components/provider-recovery-notice.d.ts +16 -0
  221. package/dist/components/queue-item-content.d.ts +9 -0
  222. package/dist/components/rehype-secret-refs.d.ts +23 -0
  223. package/dist/components/remark-soft-line-breaks.d.ts +38 -0
  224. package/dist/components/sandbox-files.d.ts +3 -1
  225. package/dist/components/sandbox-workspace.d.ts +18 -0
  226. package/dist/components/secret-question-field.d.ts +31 -0
  227. package/dist/components/secret-ref-chip.d.ts +35 -0
  228. package/dist/components/session-capability-frame.d.ts +37 -0
  229. package/dist/components/session-chrome.d.ts +57 -8
  230. package/dist/components/session-commands-panel.d.ts +6 -0
  231. package/dist/components/session-commands.d.ts +6 -0
  232. package/dist/components/session-connection-request.d.ts +8 -0
  233. package/dist/components/session-conversation.d.ts +157 -0
  234. package/dist/components/session-list.d.ts +39 -0
  235. package/dist/components/session-mcp-capability-card.d.ts +41 -0
  236. package/dist/components/session-proxy-scope.d.ts +35 -0
  237. package/dist/components/session-status.d.ts +4 -1
  238. package/dist/components/session-usage-limit-notice.d.ts +39 -0
  239. package/dist/components/timeline-anchor.d.ts +24 -0
  240. package/dist/components/timeline-annotation-chrome.d.ts +25 -0
  241. package/dist/components/timeline-annotation-layout.d.ts +42 -0
  242. package/dist/components/timeline-annotation-markers.d.ts +8 -0
  243. package/dist/components/timeline-annotation-reveal-context.d.ts +4 -0
  244. package/dist/components/timeline-annotation-shared.d.ts +31 -0
  245. package/dist/components/timeline-annotations-dialog.d.ts +3 -2
  246. package/dist/components/timeline-annotations.d.ts +13 -3
  247. package/dist/components/timeline-reading-progress.d.ts +11 -0
  248. package/dist/components/timeline-search.d.ts +34 -0
  249. package/dist/components/timeline-settlement.d.ts +16 -0
  250. package/dist/components/tip-follow.d.ts +16 -0
  251. package/dist/components/tool-action-review.d.ts +47 -0
  252. package/dist/components/tool-review-history.d.ts +25 -0
  253. package/dist/components/usage-limit-notice.d.ts +49 -0
  254. package/dist/components/usage-member-list.d.ts +91 -0
  255. package/dist/components/usage-meter.d.ts +73 -0
  256. package/dist/components/use-viewer-menu-dismiss.d.ts +2 -0
  257. package/dist/components/user-message-body.d.ts +10 -1
  258. package/dist/composer-messages.d.ts +74 -0
  259. package/dist/composer-messages.js +9 -0
  260. package/dist/composer-messages.js.map +1 -0
  261. package/dist/composer-voice-events.d.ts +8 -0
  262. package/dist/composer.d.ts +5 -4
  263. package/dist/composer.js +26 -13
  264. package/dist/computer-viewer-GMLDXPON.js +20 -0
  265. package/dist/computer-viewer-GMLDXPON.js.map +1 -0
  266. package/dist/connect-account-identity.d.ts +8 -0
  267. package/dist/connect-accounts.d.ts +15 -0
  268. package/dist/connect-chooser.d.ts +18 -0
  269. package/dist/connect-panel.d.ts +18 -0
  270. package/dist/connect-setup.d.ts +18 -0
  271. package/dist/connect.d.ts +21 -0
  272. package/dist/connect.js +2149 -0
  273. package/dist/connect.js.map +1 -0
  274. package/dist/connection-catalog.d.ts +38 -0
  275. package/dist/connection-discovery.d.ts +12 -0
  276. package/dist/connection-installed.d.ts +17 -0
  277. package/dist/connection-logo.d.ts +9 -0
  278. package/dist/connection-service-logo.d.ts +8 -0
  279. package/dist/connection-service-presentation.d.ts +13 -0
  280. package/dist/connection-skeleton.d.ts +5 -0
  281. package/dist/connection-type-picker.d.ts +6 -0
  282. package/dist/conversation-timeline.d.ts +5 -0
  283. package/dist/desktop.d.ts +7 -0
  284. package/dist/desktop.js +27 -0
  285. package/dist/desktop.js.map +1 -0
  286. package/dist/device-authorization.d.ts +23 -0
  287. package/dist/diffs.d.ts +8 -0
  288. package/dist/diffs.js +15 -0
  289. package/dist/diffs.js.map +1 -0
  290. package/dist/discovery-cache.d.ts +5 -0
  291. package/dist/editor.d.ts +7 -0
  292. package/dist/editor.js +22 -0
  293. package/dist/editor.js.map +1 -0
  294. package/dist/embedded-new-voice-OCRNT6U5.js +18 -0
  295. package/dist/embedded-new-voice-OCRNT6U5.js.map +1 -0
  296. package/dist/embedded-voice-RPHPYVBT.js +18 -0
  297. package/dist/embedded-voice-RPHPYVBT.js.map +1 -0
  298. package/dist/{fleet-decision-row-GGCAT4E4.js → fleet-decision-row-SI63O5V4.js} +4 -1
  299. package/dist/fleet-decision-row-SI63O5V4.js.map +1 -0
  300. package/dist/hooks/latest-question.d.ts +25 -0
  301. package/dist/hooks/shared-machines-list.d.ts +4 -0
  302. package/dist/hooks/shared-query.d.ts +113 -0
  303. package/dist/hooks/use-attached-browsers.d.ts +1 -1
  304. package/dist/hooks/use-auth-runs.d.ts +1 -4
  305. package/dist/hooks/use-available-models.d.ts +1 -1
  306. package/dist/hooks/use-browser-frame-stream.d.ts +1 -1
  307. package/dist/hooks/use-browser-session.d.ts +12 -1
  308. package/dist/hooks/use-client-config-flags.d.ts +49 -0
  309. package/dist/hooks/use-codex-accounts.d.ts +59 -10
  310. package/dist/hooks/use-composer.d.ts +24 -2
  311. package/dist/hooks/use-computer-frame-stream.d.ts +1 -1
  312. package/dist/hooks/use-computer-input-posture.d.ts +13 -0
  313. package/dist/hooks/use-computer-session.d.ts +4 -0
  314. package/dist/hooks/use-connect.d.ts +23 -0
  315. package/dist/hooks/use-desktop-stream.d.ts +1 -1
  316. package/dist/hooks/use-file-attachments.d.ts +12 -0
  317. package/dist/hooks/use-goal.d.ts +2 -1
  318. package/dist/hooks/use-last-started-turn-policy.d.ts +5 -1
  319. package/dist/hooks/use-new-conversation.d.ts +56 -0
  320. package/dist/hooks/use-new-session-draft.d.ts +74 -0
  321. package/dist/hooks/use-realtime-voice-models.d.ts +7 -0
  322. package/dist/hooks/use-sandbox-files.d.ts +4 -1
  323. package/dist/hooks/use-session-conversation.d.ts +49 -0
  324. package/dist/hooks/use-session-events.d.ts +28 -7
  325. package/dist/hooks/use-session-mcp-approval-policy.d.ts +2 -2
  326. package/dist/hooks/use-session-model-account.d.ts +48 -0
  327. package/dist/hooks/use-supergrok-accounts.d.ts +7 -1
  328. package/dist/hooks/use-terminal-stream.d.ts +11 -5
  329. package/dist/hooks/use-usage.d.ts +40 -0
  330. package/dist/hooks/use-vaults.d.ts +53 -0
  331. package/dist/hooks/use-voice-input.d.ts +8 -1
  332. package/dist/hooks/use-workspace-edit.d.ts +4 -0
  333. package/dist/hooks/use-workspace-sessions.d.ts +22 -5
  334. package/dist/human-input-model.d.ts +122 -0
  335. package/dist/identity-link-accounts.d.ts +11 -0
  336. package/dist/identity-link-consent.d.ts +18 -0
  337. package/dist/index.d.ts +82 -20
  338. package/dist/index.js +2350 -6605
  339. package/dist/index.js.map +1 -1
  340. package/dist/interaction.d.ts +1 -1
  341. package/dist/interaction.js +14 -24
  342. package/dist/interaction.js.map +1 -1
  343. package/dist/latest-question-R77KWSUP.js +118 -0
  344. package/dist/latest-question-R77KWSUP.js.map +1 -0
  345. package/dist/lib/clipboard.d.ts +7 -0
  346. package/dist/lib/error-message.d.ts +7 -0
  347. package/dist/lib/format.d.ts +58 -4
  348. package/dist/lib/host-theme.d.ts +77 -0
  349. package/dist/lib/interaction-errors.d.ts +3 -0
  350. package/dist/lib/menu-styles.d.ts +22 -0
  351. package/dist/lib/notify-observer.d.ts +2 -0
  352. package/dist/lib/pierre-diffs-loader.d.ts +20 -0
  353. package/dist/lib/provider-recovery.d.ts +55 -0
  354. package/dist/lib/scrollbars.d.ts +11 -0
  355. package/dist/lib/terminal-input-readiness.d.ts +11 -0
  356. package/dist/lib/transient-retry.d.ts +25 -0
  357. package/dist/lib/workbench-peers.d.ts +36 -0
  358. package/dist/lib/xterm-theme.d.ts +7 -5
  359. package/dist/machines.js +9 -5
  360. package/dist/markdown-table-layout-3PV2UAAH.js +113 -0
  361. package/dist/markdown-table-layout-3PV2UAAH.js.map +1 -0
  362. package/dist/mcp-connection-status.d.ts +14 -0
  363. package/dist/model-mark-paths.d.ts +34 -0
  364. package/dist/model-mark-paths.js +16 -0
  365. package/dist/model-mark-paths.js.map +1 -0
  366. package/dist/model-picker-model.d.ts +111 -0
  367. package/dist/model-picker-model.js +25 -0
  368. package/dist/model-picker-model.js.map +1 -0
  369. package/dist/model-policy-picker-menu-7GJ5URPR.js +17 -0
  370. package/dist/model-policy-picker-menu-7GJ5URPR.js.map +1 -0
  371. package/dist/model-policy.d.ts +30 -3
  372. package/dist/model-policy.js +14 -1
  373. package/dist/native-previews.d.ts +28 -0
  374. package/dist/native-previews.js +82 -0
  375. package/dist/native-previews.js.map +1 -0
  376. package/dist/new-session-draft.d.ts +7 -0
  377. package/dist/new-session-draft.js +639 -0
  378. package/dist/new-session-draft.js.map +1 -0
  379. package/dist/notification-text.d.ts +27 -0
  380. package/dist/{noto-sans-arabic-loader-IA2T4X2A.js → noto-sans-arabic-loader-XSH2KYWJ.js} +3 -1
  381. package/dist/{noto-sans-arabic-loader-IA2T4X2A.js.map → noto-sans-arabic-loader-XSH2KYWJ.js.map} +1 -1
  382. package/dist/{noto-sans-jp-loader.generated-HFGLYBR3.js → noto-sans-jp-loader.generated-MR5L6V7K.js} +3 -1
  383. package/dist/{noto-sans-jp-loader.generated-HFGLYBR3.js.map → noto-sans-jp-loader.generated-MR5L6V7K.js.map} +1 -1
  384. package/dist/older-history.d.ts +3 -2
  385. package/dist/organization-model.d.ts +32 -0
  386. package/dist/organization-model.js +65 -0
  387. package/dist/organization-model.js.map +1 -0
  388. package/dist/{platform-activity-row-BJHUHXAT.js → platform-activity-row-5OCKOQWZ.js} +16 -78
  389. package/dist/platform-activity-row-5OCKOQWZ.js.map +1 -0
  390. package/dist/plugin-details.d.ts +14 -0
  391. package/dist/plugin-discovery.d.ts +25 -0
  392. package/dist/preview-loading-paint.d.ts +20 -0
  393. package/dist/provider.d.ts +25 -2
  394. package/dist/queue-presentation-model.d.ts +72 -0
  395. package/dist/queue-presentation-model.js +38 -0
  396. package/dist/queue-presentation-model.js.map +1 -0
  397. package/dist/{queue-surface-implementation-BSKUWHRI.js → queue-surface-implementation-GJBH7NKO.js} +65 -265
  398. package/dist/queue-surface-implementation-GJBH7NKO.js.map +1 -0
  399. package/dist/realtime/embedded-new-voice.d.ts +1 -0
  400. package/dist/realtime/embedded-voice.d.ts +1 -0
  401. package/dist/realtime/realtime-control.d.ts +4 -65
  402. package/dist/realtime/session-realtime.d.ts +78 -0
  403. package/dist/realtime.js +25 -1294
  404. package/dist/realtime.js.map +1 -1
  405. package/dist/sandbox-files-O27IPOZW.js +21 -0
  406. package/dist/sandbox-files-O27IPOZW.js.map +1 -0
  407. package/dist/sandbox-label-model.d.ts +30 -0
  408. package/dist/sandbox-label-model.js +20 -0
  409. package/dist/sandbox-label-model.js.map +1 -0
  410. package/dist/scrollbars.d.ts +1 -0
  411. package/dist/scrollbars.js +10 -0
  412. package/dist/scrollbars.js.map +1 -0
  413. package/dist/secret-reference-label.d.ts +14 -0
  414. package/dist/secrets-ui.d.ts +57 -0
  415. package/dist/session-agents-model.d.ts +10 -0
  416. package/dist/session-agents-model.js +25 -0
  417. package/dist/session-agents-model.js.map +1 -0
  418. package/dist/session-attention-model.d.ts +8 -0
  419. package/dist/session-attention-model.js +47 -0
  420. package/dist/session-attention-model.js.map +1 -0
  421. package/dist/session-auth-recommendation.d.ts +5 -0
  422. package/dist/session-capability-policy.d.ts +31 -0
  423. package/dist/session-feedback-model.d.ts +20 -0
  424. package/dist/session-feedback-model.js +56 -0
  425. package/dist/session-feedback-model.js.map +1 -0
  426. package/dist/session-list-model.d.ts +138 -0
  427. package/dist/session-list-model.js +274 -0
  428. package/dist/session-list-model.js.map +1 -0
  429. package/dist/session-realtime-7DJOIHQW.js +21 -0
  430. package/dist/session-realtime-7DJOIHQW.js.map +1 -0
  431. package/dist/session-realtime.d.ts +7 -0
  432. package/dist/session-realtime.js +19 -0
  433. package/dist/session-realtime.js.map +1 -0
  434. package/dist/session-status-model.d.ts +36 -0
  435. package/dist/session-ui.d.ts +39 -1
  436. package/dist/session-ui.js +126 -12
  437. package/dist/session-ui.js.map +1 -1
  438. package/dist/session.d.ts +8 -2
  439. package/dist/session.js +35 -20
  440. package/dist/site-snapshot.d.ts +14 -0
  441. package/dist/sites-ui.d.ts +24 -0
  442. package/dist/sites.d.ts +3 -0
  443. package/dist/sites.js +14 -0
  444. package/dist/sites.js.map +1 -0
  445. package/dist/skill-discovery.d.ts +47 -0
  446. package/dist/terminal.d.ts +8 -0
  447. package/dist/terminal.js +32 -0
  448. package/dist/terminal.js.map +1 -0
  449. package/dist/testing/session-scenarios.d.ts +10 -0
  450. package/dist/testing/session-scenarios.js +367 -0
  451. package/dist/testing/session-scenarios.js.map +1 -0
  452. package/dist/timeline/activity-rail.d.ts +11 -2
  453. package/dist/timeline/agent-identity.d.ts +30 -0
  454. package/dist/timeline/agent-row.d.ts +31 -0
  455. package/dist/timeline/allowance-exhausted-row.d.ts +24 -0
  456. package/dist/timeline/compaction-copy.d.ts +46 -0
  457. package/dist/timeline/entrance.d.ts +13 -1
  458. package/dist/timeline/focus-sequence.d.ts +9 -0
  459. package/dist/timeline/genie-copy.d.ts +3 -0
  460. package/dist/timeline/genie-loading.d.ts +47 -0
  461. package/dist/timeline/index.d.ts +10 -5
  462. package/dist/timeline/knowledge-receipt.d.ts +17 -0
  463. package/dist/timeline/message-sender.d.ts +19 -0
  464. package/dist/timeline/notice-presentation.d.ts +13 -0
  465. package/dist/timeline/parsers.d.ts +11 -0
  466. package/dist/timeline/patch-apply-command.d.ts +18 -0
  467. package/dist/timeline/platform-activity-presentation.d.ts +21 -0
  468. package/dist/timeline/platform-activity-row.d.ts +1 -2
  469. package/dist/timeline/presented-image.d.ts +11 -0
  470. package/dist/timeline/projection.d.ts +31 -2
  471. package/dist/timeline/provider-logo.d.ts +15 -0
  472. package/dist/timeline/question-nav-model.d.ts +23 -0
  473. package/dist/timeline/registry.d.ts +12 -1
  474. package/dist/timeline/retained-image.d.ts +18 -0
  475. package/dist/timeline/retained-loaders.d.ts +27 -0
  476. package/dist/timeline/rolling-activity.d.ts +11 -0
  477. package/dist/timeline/screenshot-lightbox.d.ts +23 -1
  478. package/dist/timeline/session-usage-limit-row.d.ts +11 -0
  479. package/dist/timeline/shared.d.ts +4 -1
  480. package/dist/timeline/startup-dispatch-details.d.ts +5 -0
  481. package/dist/timeline/startup-preference.d.ts +3 -0
  482. package/dist/timeline/startup-timings.d.ts +4 -0
  483. package/dist/timeline/tool-display-name.d.ts +5 -2
  484. package/dist/timeline/tool-presentation.d.ts +172 -0
  485. package/dist/timeline/turn-summary-model.d.ts +95 -0
  486. package/dist/timeline/turn-summary.d.ts +20 -54
  487. package/dist/timeline/types.d.ts +179 -7
  488. package/dist/timeline/work-presentation.d.ts +60 -0
  489. package/dist/timeline-annotation-markers-U3G66KUD.js +166 -0
  490. package/dist/timeline-annotation-markers-U3G66KUD.js.map +1 -0
  491. package/dist/timeline-annotation-selection-BI7QTPTZ.js +242 -0
  492. package/dist/timeline-annotation-selection-BI7QTPTZ.js.map +1 -0
  493. package/dist/timeline-annotations-dialog-FOMHSDTE.js +274 -0
  494. package/dist/timeline-annotations-dialog-FOMHSDTE.js.map +1 -0
  495. package/dist/timeline-model.d.ts +29 -0
  496. package/dist/timeline-model.js +254 -0
  497. package/dist/timeline-model.js.map +1 -0
  498. package/dist/usage/allowance-copy.d.ts +25 -0
  499. package/dist/usage/session-usage-limit.d.ts +88 -0
  500. package/dist/usage/summary.d.ts +35 -0
  501. package/dist/usage.d.ts +17 -0
  502. package/dist/usage.js +1187 -0
  503. package/dist/usage.js.map +1 -0
  504. package/dist/voice-recording-owner.d.ts +7 -0
  505. package/package.json +107 -7
  506. package/src/accounts.tsx +116 -14
  507. package/src/approvals.ts +8 -1
  508. package/src/artifacts-spreadsheet.ts +1 -0
  509. package/src/artifacts.ts +44 -0
  510. package/src/assets/README.md +8 -0
  511. package/src/assets/gmail.ico +0 -0
  512. package/src/assets/outlook.ico +0 -0
  513. package/src/capability-catalog-row.tsx +94 -0
  514. package/src/capability-logo-fallback.ts +22 -0
  515. package/src/client.ts +20 -31
  516. package/src/commands/registry.ts +18 -15
  517. package/src/commands/types.ts +2 -0
  518. package/src/components/anthropic-mark.tsx +10 -0
  519. package/src/components/approval-surface.tsx +401 -138
  520. package/src/components/artifacts/artifact-chrome.tsx +413 -0
  521. package/src/components/artifacts/artifact-sandbox.tsx +189 -0
  522. package/src/components/artifacts/artifact-surface.tsx +40 -20
  523. package/src/components/artifacts/chat-interactive-block.tsx +260 -0
  524. package/src/components/artifacts/deferred-chat-media.tsx +67 -0
  525. package/src/components/artifacts/document-editor.tsx +141 -16
  526. package/src/components/artifacts/editable-artifact-ui.tsx +4 -2
  527. package/src/components/artifacts/editable-artifact-view.tsx +197 -0
  528. package/src/components/artifacts/editable-artifact-workbench.tsx +5 -2
  529. package/src/components/artifacts/editable-document.tsx +37 -36
  530. package/src/components/artifacts/editable-presentation.tsx +36 -18
  531. package/src/components/artifacts/editable-spreadsheet.tsx +543 -103
  532. package/src/components/artifacts/index.ts +45 -0
  533. package/src/components/artifacts/inline-html-document.ts +24 -0
  534. package/src/components/artifacts/latest-task-runner.ts +49 -0
  535. package/src/components/artifacts/presentation-editor.tsx +323 -67
  536. package/src/components/artifacts/published-html-artifact-frame.tsx +122 -5
  537. package/src/components/artifacts/session-artifact-viewer.tsx +378 -0
  538. package/src/components/artifacts/site-view.tsx +121 -0
  539. package/src/components/artifacts/spreadsheet-canvas.ts +5 -10
  540. package/src/components/artifacts/spreadsheet-command-queue.ts +184 -0
  541. package/src/components/artifacts/spreadsheet-download.tsx +109 -0
  542. package/src/components/artifacts/spreadsheet-general-display.ts +43 -0
  543. package/src/components/artifacts/spreadsheet-grid.tsx +344 -144
  544. package/src/components/artifacts/spreadsheet-resize.tsx +421 -0
  545. package/src/components/artifacts/spreadsheet-submissions.tsx +46 -0
  546. package/src/components/artifacts/visualization-assets/base.css +1015 -0
  547. package/src/components/artifacts/visualization-assets/helpers.html +349 -0
  548. package/src/components/artifacts/visualization-assets.gen.ts +3 -0
  549. package/src/components/browser-select-control.tsx +312 -0
  550. package/src/components/browser-viewer.tsx +778 -318
  551. package/src/components/chat-composer.tsx +47 -5
  552. package/src/components/child-session-link.tsx +36 -0
  553. package/src/components/claude-mark.tsx +11 -0
  554. package/src/components/code-editor.tsx +13 -16
  555. package/src/components/composer-transcription-control.tsx +128 -30
  556. package/src/components/composer.tsx +404 -195
  557. package/src/components/computer-viewer.tsx +880 -253
  558. package/src/components/copy-button.tsx +13 -5
  559. package/src/components/desktop-viewer.tsx +1 -1
  560. package/src/components/enrollment-consent.tsx +1 -1
  561. package/src/components/enrollment-device-flow.tsx +1 -1
  562. package/src/components/file-browser.tsx +10 -14
  563. package/src/components/fleet-tile.tsx +9 -18
  564. package/src/components/generated-video-player.tsx +4 -2
  565. package/src/components/grok-mark.tsx +34 -0
  566. package/src/components/human-input-form.tsx +590 -283
  567. package/src/components/human-input-surface.tsx +11 -0
  568. package/src/components/interaction-intervention-banner.tsx +2 -2
  569. package/src/components/linkified-text.tsx +79 -0
  570. package/src/components/machine-card.tsx +35 -78
  571. package/src/components/machine-dock-bar.tsx +1 -1
  572. package/src/components/machine-input-display.ts +197 -3
  573. package/src/components/machines/agent-status-icon.tsx +31 -0
  574. package/src/components/machines/machine-detail.tsx +200 -31
  575. package/src/components/machines/machine-operation-policy.tsx +1 -1
  576. package/src/components/machines/machine-status.ts +241 -0
  577. package/src/components/machines-dashboard.tsx +2 -17
  578. package/src/components/markdown-table-layout.ts +143 -0
  579. package/src/components/markdown.tsx +466 -129
  580. package/src/components/message-timeline.tsx +2567 -728
  581. package/src/components/model-mark.tsx +102 -0
  582. package/src/components/model-picker.tsx +15 -4
  583. package/src/components/model-policy-picker-menu.tsx +371 -0
  584. package/src/components/model-policy-picker.tsx +256 -412
  585. package/src/components/new-conversation.tsx +221 -0
  586. package/src/components/open-geni-chat.tsx +309 -0
  587. package/src/components/open-geni-links.tsx +179 -0
  588. package/src/components/openrouter-mark.tsx +13 -0
  589. package/src/components/opper-mark.tsx +18 -0
  590. package/src/components/pierre-diff.tsx +41 -16
  591. package/src/components/pierre-file.tsx +35 -10
  592. package/src/components/preview-loading.tsx +109 -0
  593. package/src/components/provider-recovery-notice.tsx +49 -0
  594. package/src/components/queue-item-content.ts +14 -0
  595. package/src/components/queue-surface-implementation.tsx +51 -356
  596. package/src/components/queue-surface-state.tsx +3 -1
  597. package/src/components/rehype-secret-refs.ts +98 -0
  598. package/src/components/remark-soft-line-breaks.ts +166 -0
  599. package/src/components/sandbox-files.tsx +91 -15
  600. package/src/components/sandbox-terminal.tsx +84 -13
  601. package/src/components/sandbox-workspace.tsx +147 -51
  602. package/src/components/secret-question-field.tsx +324 -0
  603. package/src/components/secret-ref-chip.tsx +185 -0
  604. package/src/components/session-capability-frame.tsx +197 -0
  605. package/src/components/session-chrome.tsx +842 -251
  606. package/src/components/session-commands-panel.tsx +126 -0
  607. package/src/components/session-commands.tsx +18 -0
  608. package/src/components/session-connection-request.tsx +97 -0
  609. package/src/components/session-conversation.tsx +876 -0
  610. package/src/components/session-list.tsx +257 -0
  611. package/src/components/session-mcp-capability-card.tsx +680 -0
  612. package/src/components/session-proxy-scope.tsx +153 -0
  613. package/src/components/session-status.tsx +26 -17
  614. package/src/components/session-usage-limit-notice.tsx +174 -0
  615. package/src/components/timeline-anchor.tsx +119 -0
  616. package/src/components/timeline-annotation-chrome.tsx +196 -0
  617. package/src/components/timeline-annotation-layout.ts +268 -0
  618. package/src/components/timeline-annotation-markers.tsx +185 -0
  619. package/src/components/timeline-annotation-reveal-context.ts +9 -0
  620. package/src/components/timeline-annotation-selection.tsx +106 -54
  621. package/src/components/timeline-annotation-shared.ts +245 -0
  622. package/src/components/timeline-annotations-dialog.tsx +185 -128
  623. package/src/components/timeline-annotations.tsx +189 -45
  624. package/src/components/timeline-reading-progress.ts +80 -0
  625. package/src/components/timeline-search.tsx +168 -0
  626. package/src/components/timeline-settlement.ts +60 -0
  627. package/src/components/tip-follow.ts +52 -0
  628. package/src/components/tool-action-review.tsx +650 -0
  629. package/src/components/tool-review-history.tsx +88 -0
  630. package/src/components/tooltip.tsx +38 -5
  631. package/src/components/usage-limit-notice.tsx +179 -0
  632. package/src/components/usage-member-list.tsx +792 -0
  633. package/src/components/usage-meter.tsx +382 -0
  634. package/src/components/use-viewer-menu-dismiss.ts +33 -0
  635. package/src/components/user-message-body.tsx +27 -2
  636. package/src/components/workspace-dock.tsx +18 -1
  637. package/src/composer-messages.ts +150 -0
  638. package/src/composer-voice-events.ts +12 -0
  639. package/src/composer.ts +6 -1
  640. package/src/connect-account-identity.tsx +34 -0
  641. package/src/connect-accounts.tsx +233 -0
  642. package/src/connect-chooser.tsx +336 -0
  643. package/src/connect-panel.tsx +99 -0
  644. package/src/connect-setup.tsx +355 -0
  645. package/src/connect.ts +60 -0
  646. package/src/connection-assets.d.ts +4 -0
  647. package/src/connection-catalog.tsx +162 -0
  648. package/src/connection-discovery.tsx +238 -0
  649. package/src/connection-installed.tsx +51 -0
  650. package/src/connection-logo.tsx +37 -0
  651. package/src/connection-service-logo.tsx +55 -0
  652. package/src/connection-service-presentation.ts +46 -0
  653. package/src/connection-skeleton.tsx +16 -0
  654. package/src/connection-type-picker.tsx +36 -0
  655. package/src/conversation-timeline.ts +92 -0
  656. package/src/desktop.ts +24 -0
  657. package/src/device-authorization.tsx +98 -0
  658. package/src/diffs.ts +16 -0
  659. package/src/discovery-cache.ts +42 -0
  660. package/src/editor.ts +13 -0
  661. package/src/hooks/internal.ts +3 -2
  662. package/src/hooks/latest-question.ts +164 -0
  663. package/src/hooks/shared-machines-list.ts +34 -1
  664. package/src/hooks/shared-query.ts +721 -0
  665. package/src/hooks/use-attached-browsers.ts +1 -1
  666. package/src/hooks/use-auth-runs.ts +0 -27
  667. package/src/hooks/use-available-models.ts +21 -15
  668. package/src/hooks/use-browser-frame-stream.ts +19 -2
  669. package/src/hooks/use-browser-session.ts +680 -205
  670. package/src/hooks/use-browser-sessions.ts +32 -1
  671. package/src/hooks/use-client-config-flags.ts +118 -0
  672. package/src/hooks/use-codex-accounts.ts +247 -47
  673. package/src/hooks/use-composer.ts +276 -47
  674. package/src/hooks/use-computer-frame-stream.ts +25 -7
  675. package/src/hooks/use-computer-input-posture.ts +94 -0
  676. package/src/hooks/use-computer-session.ts +409 -95
  677. package/src/hooks/use-connect.ts +23 -0
  678. package/src/hooks/use-desktop-stream.ts +19 -28
  679. package/src/hooks/use-file-attachments.ts +80 -4
  680. package/src/hooks/use-goal.ts +107 -13
  681. package/src/hooks/use-last-started-turn-policy.ts +46 -14
  682. package/src/hooks/use-machines.ts +4 -0
  683. package/src/hooks/use-new-conversation.ts +374 -0
  684. package/src/hooks/use-new-session-draft.ts +813 -0
  685. package/src/hooks/use-realtime-voice-models.ts +46 -0
  686. package/src/hooks/use-sandbox-files.ts +68 -20
  687. package/src/hooks/use-session-background-commands.ts +22 -11
  688. package/src/hooks/use-session-capabilities.ts +3 -2
  689. package/src/hooks/use-session-conversation.ts +150 -0
  690. package/src/hooks/use-session-events.ts +703 -382
  691. package/src/hooks/use-session-lineage.ts +20 -16
  692. package/src/hooks/use-session-mcp-approval-policy.ts +13 -5
  693. package/src/hooks/use-session-model-account.ts +174 -0
  694. package/src/hooks/use-session.ts +53 -21
  695. package/src/hooks/use-slash-commands.ts +6 -11
  696. package/src/hooks/use-supergrok-accounts.ts +22 -8
  697. package/src/hooks/use-terminal-stream.ts +153 -27
  698. package/src/hooks/use-transcription.ts +6 -0
  699. package/src/hooks/use-turn-queue.ts +2 -1
  700. package/src/hooks/use-usage.ts +100 -0
  701. package/src/hooks/use-vaults.ts +153 -0
  702. package/src/hooks/use-video-artifact-playback.ts +1 -1
  703. package/src/hooks/use-voice-input.ts +127 -19
  704. package/src/hooks/use-workspace-capture.ts +3 -2
  705. package/src/hooks/use-workspace-edit.ts +23 -5
  706. package/src/hooks/use-workspace-sessions.ts +97 -32
  707. package/src/human-input-model.ts +359 -0
  708. package/src/identity-link-accounts.tsx +148 -0
  709. package/src/identity-link-consent.tsx +192 -0
  710. package/src/index.ts +262 -16
  711. package/src/interaction.ts +1 -0
  712. package/src/lib/clipboard.ts +29 -0
  713. package/src/lib/error-message.ts +37 -0
  714. package/src/lib/format.ts +213 -16
  715. package/src/lib/host-theme.ts +529 -0
  716. package/src/lib/interaction-errors.ts +15 -0
  717. package/src/lib/menu-styles.ts +44 -0
  718. package/src/lib/notify-observer.ts +15 -0
  719. package/src/lib/pierre-diffs-loader.ts +53 -0
  720. package/src/lib/provider-recovery.ts +243 -0
  721. package/src/lib/scrollbars.ts +30 -0
  722. package/src/lib/terminal-input-readiness.ts +51 -0
  723. package/src/lib/transient-retry.ts +99 -0
  724. package/src/lib/workbench-peers.ts +62 -0
  725. package/src/lib/xterm-theme.ts +23 -21
  726. package/src/mcp-connection-status.ts +35 -0
  727. package/src/model-mark-paths.ts +53 -0
  728. package/src/model-picker-model.ts +330 -0
  729. package/src/model-policy.ts +299 -74
  730. package/src/native-previews.ts +100 -0
  731. package/src/new-session-draft.ts +22 -0
  732. package/src/notification-text.ts +81 -0
  733. package/src/older-history.ts +22 -4
  734. package/src/organization-model.ts +118 -0
  735. package/src/plugin-details.tsx +185 -0
  736. package/src/plugin-discovery.tsx +196 -0
  737. package/src/preview-loading-paint.ts +68 -0
  738. package/src/provider.tsx +74 -15
  739. package/src/queue-presentation-model.ts +464 -0
  740. package/src/realtime/dropdown-menu.tsx +19 -12
  741. package/src/realtime/embedded-new-voice.tsx +1 -0
  742. package/src/realtime/embedded-voice.tsx +3 -0
  743. package/src/realtime/realtime-control.tsx +138 -501
  744. package/src/realtime/session-realtime.ts +566 -0
  745. package/src/sandbox-label-model.ts +53 -0
  746. package/src/scrollbars.ts +3 -0
  747. package/src/secret-reference-label.ts +43 -0
  748. package/src/secrets-ui.tsx +59 -0
  749. package/src/session-agents-model.ts +33 -0
  750. package/src/session-attention-model.ts +62 -0
  751. package/src/session-auth-recommendation.ts +48 -0
  752. package/src/session-capability-policy.ts +196 -0
  753. package/src/session-context.ts +3 -4
  754. package/src/session-feedback-model.ts +95 -0
  755. package/src/session-list-model.ts +434 -0
  756. package/src/session-realtime.ts +18 -0
  757. package/src/session-status-model.ts +99 -0
  758. package/src/session-ui.ts +98 -1
  759. package/src/session.ts +17 -1
  760. package/src/site-snapshot.ts +74 -0
  761. package/src/sites-ui.tsx +348 -0
  762. package/src/sites.ts +3 -0
  763. package/src/skill-discovery.tsx +208 -0
  764. package/src/terminal.ts +26 -0
  765. package/src/testing/session-scenarios.ts +404 -0
  766. package/src/timeline/activity-rail.tsx +242 -101
  767. package/src/timeline/agent-identity.tsx +84 -0
  768. package/src/timeline/agent-row.tsx +231 -0
  769. package/src/timeline/allowance-exhausted-row.tsx +86 -0
  770. package/src/timeline/compaction-copy.ts +115 -0
  771. package/src/timeline/entrance.tsx +28 -3
  772. package/src/timeline/fleet-decision-projection.ts +2 -1
  773. package/src/timeline/fleet-decision-row.tsx +1 -0
  774. package/src/timeline/focus-sequence.ts +44 -0
  775. package/src/timeline/genie-copy.ts +19 -0
  776. package/src/timeline/genie-loading.tsx +143 -0
  777. package/src/timeline/index.ts +30 -2
  778. package/src/timeline/knowledge-receipt.tsx +100 -0
  779. package/src/timeline/message-sender.tsx +71 -0
  780. package/src/timeline/notice-presentation.ts +48 -0
  781. package/src/timeline/parsers.ts +42 -2
  782. package/src/timeline/patch-apply-command.tsx +135 -0
  783. package/src/timeline/platform-activity-presentation.ts +166 -0
  784. package/src/timeline/platform-activity-row.tsx +15 -90
  785. package/src/timeline/presented-image.ts +63 -0
  786. package/src/timeline/projection.ts +1746 -76
  787. package/src/timeline/provider-logo.tsx +53 -0
  788. package/src/timeline/question-nav-model.ts +28 -0
  789. package/src/timeline/registry.ts +26 -4
  790. package/src/timeline/retained-image.ts +97 -0
  791. package/src/timeline/retained-loaders.ts +91 -0
  792. package/src/timeline/rolling-activity.tsx +80 -0
  793. package/src/timeline/screenshot-lightbox.tsx +228 -5
  794. package/src/timeline/session-usage-limit-row.tsx +42 -0
  795. package/src/timeline/shared.tsx +42 -15
  796. package/src/timeline/startup-dispatch-details.tsx +36 -0
  797. package/src/timeline/startup-preference.ts +33 -0
  798. package/src/timeline/startup-timings.tsx +145 -0
  799. package/src/timeline/tool-display-name.ts +19 -7
  800. package/src/timeline/tool-presentation.ts +1316 -0
  801. package/src/timeline/tool-renderers.tsx +835 -1138
  802. package/src/timeline/turn-summary-model.ts +343 -0
  803. package/src/timeline/turn-summary.tsx +272 -365
  804. package/src/timeline/types.ts +189 -6
  805. package/src/timeline/work-presentation.ts +189 -0
  806. package/src/timeline-model.ts +143 -0
  807. package/src/usage/allowance-copy.ts +64 -0
  808. package/src/usage/session-usage-limit.ts +266 -0
  809. package/src/usage/summary.ts +93 -0
  810. package/src/usage.ts +70 -0
  811. package/src/voice-recording-owner.ts +22 -0
  812. package/styles/compiled.css +2319 -748
  813. package/styles/connect.css +290 -0
  814. package/styles/connect.d.ts +1 -0
  815. package/styles/effective-tokens.css +1 -0
  816. package/styles/index.css +309 -9
  817. package/styles/responsive.css +99 -7
  818. package/styles/session-capability.css +44 -0
  819. package/styles/tokens.css +134 -66
  820. package/dist/browser-viewer-AEFHUYZK.js +0 -14
  821. package/dist/chunk-4MDLEHXL.js.map +0 -1
  822. package/dist/chunk-5YK2ZDIG.js.map +0 -1
  823. package/dist/chunk-D3UIAJKY.js.map +0 -1
  824. package/dist/chunk-ELIYWBZR.js.map +0 -1
  825. package/dist/chunk-H5YGYNXS.js.map +0 -1
  826. package/dist/chunk-HQVCKCLG.js +0 -9458
  827. package/dist/chunk-HQVCKCLG.js.map +0 -1
  828. package/dist/chunk-HUIKIDM5.js.map +0 -1
  829. package/dist/chunk-ICA6R6TW.js.map +0 -1
  830. package/dist/chunk-JVYQRRC4.js.map +0 -1
  831. package/dist/chunk-KCGXPP5V.js.map +0 -1
  832. package/dist/chunk-KZ73RL76.js.map +0 -1
  833. package/dist/chunk-LAKLF4PA.js +0 -141
  834. package/dist/chunk-LAKLF4PA.js.map +0 -1
  835. package/dist/chunk-MZ2G2XBA.js.map +0 -1
  836. package/dist/chunk-NEXLLTP4.js.map +0 -1
  837. package/dist/chunk-OAM4WAAM.js.map +0 -1
  838. package/dist/chunk-PEI6RANI.js.map +0 -1
  839. package/dist/chunk-RGALCTWO.js.map +0 -1
  840. package/dist/chunk-W6NFJXSA.js +0 -454
  841. package/dist/chunk-W6NFJXSA.js.map +0 -1
  842. package/dist/chunk-WSK7G5LE.js +0 -170
  843. package/dist/chunk-WSK7G5LE.js.map +0 -1
  844. package/dist/chunk-X4A66EWZ.js +0 -223
  845. package/dist/chunk-X4A66EWZ.js.map +0 -1
  846. package/dist/chunk-XXWH2JLC.js.map +0 -1
  847. package/dist/chunk-YFKJ7VZH.js +0 -268
  848. package/dist/chunk-YFKJ7VZH.js.map +0 -1
  849. package/dist/chunk-YJFGAFBD.js.map +0 -1
  850. package/dist/computer-viewer-FWEZISW4.js +0 -16
  851. package/dist/fleet-decision-row-GGCAT4E4.js.map +0 -1
  852. package/dist/hooks/use-environments.d.ts +0 -41
  853. package/dist/hooks/use-packs.d.ts +0 -32
  854. package/dist/platform-activity-row-BJHUHXAT.js.map +0 -1
  855. package/dist/queue-surface-implementation-BSKUWHRI.js.map +0 -1
  856. package/dist/timeline-annotation-selection-3L7LHGG2.js +0 -187
  857. package/dist/timeline-annotation-selection-3L7LHGG2.js.map +0 -1
  858. package/dist/timeline-annotations-dialog-U7EVLR75.js +0 -196
  859. package/dist/timeline-annotations-dialog-U7EVLR75.js.map +0 -1
  860. package/src/hooks/use-environments.ts +0 -182
  861. package/src/hooks/use-packs.ts +0 -157
  862. /package/dist/{browser-viewer-AEFHUYZK.js.map → browser-viewer-ZWR6MWJ7.js.map} +0 -0
  863. /package/dist/{computer-viewer-FWEZISW4.js.map → chat-interactive-block-BO2IWXVK.js.map} +0 -0
@@ -1,133 +1,671 @@
1
+ "use client";
1
2
  import {
2
3
  buildTimeline,
3
- createOlderHistoryLoadReceipt,
4
4
  groupTimeline,
5
- isActionableHumanInputRequest,
6
5
  sessionStatusFromEvents
7
- } from "./chunk-D3UIAJKY.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(async () => {
106
- if (!sessionId) {
107
- return null;
108
- }
109
- let readGeneration2 = 0;
110
- const fetched = await client.getSession(workspaceId, sessionId, {
111
- fresh: true,
112
- onRequestStart: () => {
113
- readGeneration2 = beginRead?.() ?? ++nextReadGeneration.current;
639
+ const load = useCallback4(
640
+ async (signal) => {
641
+ if (!sessionId) {
642
+ return null;
114
643
  }
115
- });
116
- setOverride(null);
117
- return {
118
- session: fetched,
119
- revision: ++nextReadRevision.current,
120
- readGeneration: readGeneration2
121
- };
122
- }, [beginRead, client, workspaceId, sessionId]);
644
+ let readGeneration2 = 0;
645
+ const fetched = await client.getSession(workspaceId, sessionId, {
646
+ fresh: true,
647
+ signal,
648
+ onRequestStart: () => {
649
+ readGeneration2 = beginRead?.() ?? ++nextReadGeneration.current;
650
+ }
651
+ });
652
+ setOverride(null);
653
+ return {
654
+ session: fetched,
655
+ revision: ++nextReadRevision.current,
656
+ readGeneration: readGeneration2
657
+ };
658
+ },
659
+ [beginRead, client, workspaceId, sessionId]
660
+ );
123
661
  const { data, loading, error, refresh } = usePolledValue(load, {
124
662
  pollIntervalMs: options.pollIntervalMs,
125
663
  enabled
126
664
  });
127
- useEffect(() => {
665
+ useEffect3(() => {
128
666
  if (enabled && workspaceControlEvent) void refresh();
129
667
  }, [enabled, refresh, workspaceControlEvent]);
130
- useEffect(() => {
668
+ useEffect3(() => {
131
669
  if (!sessionId || !enabled) return;
132
670
  return registerSessionReconciler(sessionId, "session", refresh);
133
671
  }, [enabled, refresh, registerSessionReconciler, sessionId]);
@@ -138,8 +676,17 @@ function useSession(sessionId, options = {}) {
138
676
  () => base && override && override.id === base.id ? { ...base, title: override.title, titleSource: override.titleSource } : base,
139
677
  [base, override]
140
678
  );
141
- const onTitleEvent = useCallback(
679
+ const sharedEvents = options.events;
680
+ const onTitleEvent = useCallback4(
142
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
+ }
143
690
  if (!base || event.sequence <= base.lastSequence) {
144
691
  return;
145
692
  }
@@ -154,13 +701,13 @@ function useSession(sessionId, options = {}) {
154
701
  return { ...next, title, titleSource: source };
155
702
  });
156
703
  },
157
- [base]
704
+ [base, refresh, sharedEvents, sessionId]
158
705
  );
159
- useSessionEventTrigger(client, workspaceId, sessionId, isTitleEvent, onTitleEvent, {
706
+ useSessionEventTrigger(client, workspaceId, sessionId, isSessionDetailEvent, onTitleEvent, {
160
707
  enabled,
161
708
  ...options.events !== void 0 ? { events: options.events } : {}
162
709
  });
163
- const updateTitle = useCallback(
710
+ const updateTitle = useCallback4(
164
711
  async (title) => {
165
712
  if (!sessionId) {
166
713
  return null;
@@ -187,12 +734,109 @@ function useSession(sessionId, options = {}) {
187
734
  };
188
735
  }
189
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
+
190
834
  // src/hooks/use-session-events.ts
191
- import { useCallback as useCallback2, useEffect as useEffect2, useMemo as useMemo2, useRef as useRef2, useState as useState2 } from "react";
192
- var INITIAL_TAIL_PAGE_SIZE = 1e3;
193
- var OLDER_PAGE_SIZE = 5e3;
194
- var NEWER_PAGE_SIZE = 5e3;
195
- 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;
196
840
  var INITIAL_FETCH_CAP = 1;
197
841
  var OLDER_GROUP_TARGET = 32;
198
842
  var OLDER_FETCH_CAP = 2;
@@ -200,35 +844,16 @@ var NEWER_GROUP_TARGET = 32;
200
844
  var NEWER_FETCH_CAP = 2;
201
845
  var OLDEST_GROUP_TARGET = 32;
202
846
  var OLDEST_FETCH_CAP = 2;
203
- 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;
204
853
  var EMPTY_EVENTS = [];
205
854
  var encoder = new TextEncoder();
206
- var decoder = new TextDecoder();
207
- var BROWSER_EVENT_TYPE_MAX_BYTES = 256;
208
- var BROWSER_EVENT_ID_MAX_BYTES = 256;
209
- var BROWSER_EVENT_CLIENT_ID_MAX_BYTES = 4 * 1024;
210
- var BROWSER_EVENT_DUPLICATE_REASON_MAX_BYTES = 4 * 1024;
211
- var BROWSER_EVENT_PAYLOAD_PREVIEW_MAX_BYTES = 48 * 1024;
212
- var BROWSER_EVENT_PAYLOAD_IDENTITY_FIELDS = [
213
- "id",
214
- "callId",
215
- "call_id",
216
- "name",
217
- "toolName",
218
- "status",
219
- "code",
220
- "isError",
221
- "stream",
222
- "commandId",
223
- "sequence",
224
- "coalescedUntil",
225
- "coalescedCount",
226
- "firstSequence",
227
- "lastSequence"
228
- ];
229
- var SESSION_EVENT_BROWSER_MAX_BYTES = 8 * 1024 * 1024;
230
- var SESSION_EVENT_BROWSER_MAX_COUNT = 1e4;
231
- 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;
232
857
  var SESSION_EVENT_BROWSER_PENDING_MAX_BYTES = 1024 * 1024;
233
858
  var SESSION_EVENT_BROWSER_PENDING_MAX_COUNT = 256;
234
859
  var EMPTY_EVENT_WINDOW = {
@@ -245,43 +870,75 @@ function useSessionEvents(sessionId, options = {}) {
245
870
  const replay = options.replay ?? "windowed";
246
871
  const fullReplay = replay === "full" || after !== 0;
247
872
  const streamKey = `${workspaceId}\0${sessionId ?? ""}\0${after}\0${fullReplay ? "full" : "windowed"}`;
248
- const [eventWindow, setEventWindow] = useState2(EMPTY_EVENT_WINDOW);
249
- const [connectionState, setConnectionState] = useState2("idle");
250
- const [error, setError] = useState2(null);
251
- const [hasOlder, setHasOlder] = useState2(false);
252
- const [hasNewer, setHasNewer] = useState2(false);
253
- 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(
254
890
  null
255
891
  );
256
- const [initialLoading, setInitialLoading] = useState2(true);
257
- const [loadingOlder, setLoadingOlder] = useState2(false);
258
- const [loadingNewer, setLoadingNewer] = useState2(false);
259
- const [loadingOldest, setLoadingOldest] = useState2(false);
260
- const [loadingLatest, setLoadingLatest] = useState2(false);
261
- const [streamEpoch, setStreamEpoch] = useState2(0);
262
- const [viewMode, setViewMode] = useState2("live");
263
- const lastSequenceRef = useRef2(after);
264
- const streamResumeSequenceRef = useRef2(after);
265
- const oldestSequenceRef = useRef2(null);
266
- const newestSequenceRef = useRef2(null);
267
- const hasOlderRef = useRef2(false);
268
- const hasNewerRef = useRef2(false);
269
- const loadingOlderRef = useRef2(false);
270
- const loadingNewerRef = useRef2(false);
271
- const loadingOldestRef = useRef2(false);
272
- const loadingLatestRef = useRef2(false);
273
- const viewModeRef = useRef2("live");
274
- const initialWindowLoadedRef = useRef2(false);
275
- const streamAbortRef = useRef2(null);
276
- const streamKeyRef = useRef2(null);
277
- const generationRef = useRef2(0);
278
- const eventWindowRef = useRef2(EMPTY_EVENT_WINDOW);
279
- 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({
280
921
  sequence: after,
281
922
  status: null
282
923
  });
283
- const [stateStreamKey, setStateStreamKey] = useState2(streamKey);
284
- 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(() => {
285
942
  if (streamKeyRef.current !== streamKey) {
286
943
  streamKeyRef.current = streamKey;
287
944
  setStateStreamKey(streamKey);
@@ -297,6 +954,7 @@ function useSessionEvents(sessionId, options = {}) {
297
954
  setLoadingOldest(false);
298
955
  setLoadingLatest(false);
299
956
  setInitialLoading(true);
957
+ setInitialHistoryReady(false);
300
958
  lastSequenceRef.current = after;
301
959
  streamResumeSequenceRef.current = after;
302
960
  oldestSequenceRef.current = null;
@@ -310,24 +968,27 @@ function useSessionEvents(sessionId, options = {}) {
310
968
  viewModeRef.current = "live";
311
969
  setViewMode("live");
312
970
  initialWindowLoadedRef.current = false;
971
+ reconcileAfterPageResumeRef.current = false;
313
972
  }
314
973
  const generation = generationRef.current + 1;
315
974
  generationRef.current = generation;
316
- if (loadingOlderRef.current) {
317
- loadingOlderRef.current = false;
318
- setLoadingOlder(false);
319
- }
320
975
  if (!sessionId || !streamEnabled) {
976
+ if (sessionId && enabled && !pageLive && !fullReplay && viewModeRef.current === "live" && initialWindowLoadedRef.current) {
977
+ reconcileAfterPageResumeRef.current = true;
978
+ }
321
979
  setConnectionState("idle");
322
980
  return;
323
981
  }
324
982
  if (viewMode === "history") {
983
+ reconcileAfterPageResumeRef.current = false;
325
984
  setConnectionState("idle");
326
985
  setInitialLoading(false);
327
986
  return;
328
987
  }
988
+ const reconcileForegroundResume = reconcileAfterPageResumeRef.current && !fullReplay;
989
+ reconcileAfterPageResumeRef.current = false;
329
990
  const controller = new AbortController();
330
- 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;
331
992
  streamAbortRef.current = controller;
332
993
  let pending = [];
333
994
  let pendingBytes = 2;
@@ -357,9 +1018,12 @@ function useSessionEvents(sessionId, options = {}) {
357
1018
  batchResumeSequence
358
1019
  );
359
1020
  }
360
- observeSessionStatus(batch, sessionStatusRef, setSessionStatusProjection);
1021
+ const status = observeSessionStatus(batch, sessionStatusRef);
1022
+ if (status !== void 0) {
1023
+ setSessionStatusProjection(status);
1024
+ }
361
1025
  const current = eventWindowRef.current;
362
- const next = boundBrowserSessionEventWindow([...current.events, ...batch]);
1026
+ const next = appendBrowserSessionEventWindow(current, batch);
363
1027
  const retained = {
364
1028
  ...next,
365
1029
  truncated: current.truncated || next.truncated
@@ -367,7 +1031,7 @@ function useSessionEvents(sessionId, options = {}) {
367
1031
  eventWindowRef.current = retained;
368
1032
  setEventWindow(retained);
369
1033
  oldestSequenceRef.current = retained.events[0]?.sequence ?? null;
370
- newestSequenceRef.current = retained.events.at(-1)?.sequence ?? null;
1034
+ newestSequenceRef.current = maxResumeSequenceOrNull(retained.events);
371
1035
  if (retained.truncated) {
372
1036
  hasOlderRef.current = true;
373
1037
  setHasOlder(true);
@@ -379,29 +1043,85 @@ function useSessionEvents(sessionId, options = {}) {
379
1043
  };
380
1044
  void (async () => {
381
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
+ }
382
1089
  if (!fullReplay && !initialWindowLoadedRef.current) {
383
1090
  setConnectionState("connecting");
384
1091
  const window = await loadEventWindow(client, workspaceId, sessionId, {
385
1092
  before: Number.MAX_SAFE_INTEGER,
386
- pageSize: INITIAL_TAIL_PAGE_SIZE,
1093
+ pageSize: SESSION_HISTORY_PAGE_SIZE,
387
1094
  targetGroups: Number.POSITIVE_INFINITY,
388
1095
  maxFetches: INITIAL_FETCH_CAP,
1096
+ boundaryPageCap: BOUNDARY_PAGE_CAP,
389
1097
  signal: controller.signal
390
1098
  });
391
1099
  if (!isCurrent()) {
392
1100
  return;
393
1101
  }
394
- observeSessionStatus(window.events, sessionStatusRef, setSessionStatusProjection);
1102
+ const status = observeSessionStatus(window.events, sessionStatusRef);
395
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);
396
1111
  eventWindowRef.current = retained;
397
- setEventWindow(retained);
398
1112
  oldestSequenceRef.current = retained.events[0]?.sequence ?? window.oldestSequence;
399
- newestSequenceRef.current = retained.events.at(-1)?.sequence ?? window.newestSequence;
1113
+ newestSequenceRef.current = maxResumeSequenceOrNull(retained.events) ?? window.newestSequence;
400
1114
  hasOlderRef.current = window.hasOlder || retained.truncated;
401
1115
  hasNewerRef.current = false;
402
1116
  lastSequenceRef.current = window.newestSequence;
403
1117
  streamResumeSequenceRef.current = window.newestSequence;
404
1118
  initialWindowLoadedRef.current = true;
1119
+ setInitialHistoryReady(true);
1120
+ setError(null);
1121
+ if (status !== void 0) {
1122
+ setSessionStatusProjection(status);
1123
+ }
1124
+ setEventWindow(retained);
405
1125
  setHasOlder(window.hasOlder || retained.truncated);
406
1126
  setHasNewer(false);
407
1127
  setInitialLoading(false);
@@ -415,7 +1135,7 @@ function useSessionEvents(sessionId, options = {}) {
415
1135
  onOpen: () => {
416
1136
  void Promise.resolve().then(() => reconcileSession(sessionId)).catch((cause) => {
417
1137
  if (isCurrent()) {
418
- setError(cause instanceof Error ? cause : new Error(String(cause)));
1138
+ setError(normalizeError(cause));
419
1139
  }
420
1140
  });
421
1141
  },
@@ -427,7 +1147,7 @@ function useSessionEvents(sessionId, options = {}) {
427
1147
  });
428
1148
  for await (const event of stream) {
429
1149
  if (!isCurrent()) break;
430
- const boundedEvent = boundBrowserLegacyEvent(event);
1150
+ const boundedEvent = event;
431
1151
  const boundedEventBytes = browserJsonBytes(boundedEvent);
432
1152
  const separatorBytes = pending.length === 0 ? 0 : 1;
433
1153
  if (pending.length > 0 && (pending.length >= SESSION_EVENT_BROWSER_PENDING_MAX_COUNT || pendingBytes + separatorBytes + boundedEventBytes > SESSION_EVENT_BROWSER_PENDING_MAX_BYTES)) {
@@ -448,8 +1168,9 @@ function useSessionEvents(sessionId, options = {}) {
448
1168
  } catch (cause) {
449
1169
  if (isCurrent()) {
450
1170
  flush();
451
- setError(cause instanceof Error ? cause : new Error(String(cause)));
1171
+ setError(normalizeError(cause));
452
1172
  setConnectionState("error");
1173
+ setInitialLoading(false);
453
1174
  }
454
1175
  }
455
1176
  })();
@@ -470,6 +1191,8 @@ function useSessionEvents(sessionId, options = {}) {
470
1191
  workspaceId,
471
1192
  sessionId,
472
1193
  after,
1194
+ enabled,
1195
+ pageLive,
473
1196
  streamEnabled,
474
1197
  fullReplay,
475
1198
  streamKey,
@@ -477,10 +1200,10 @@ function useSessionEvents(sessionId, options = {}) {
477
1200
  viewMode,
478
1201
  reconcileSession
479
1202
  ]);
480
- const navigationBusy = () => loadingOlderRef.current || loadingNewerRef.current || loadingOldestRef.current || loadingLatestRef.current;
481
- const loadOlder = useCallback2(
482
- () => createOlderHistoryLoadReceipt(async (markCommitted) => {
483
- 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) {
484
1207
  return false;
485
1208
  }
486
1209
  const before = oldestSequenceRef.current;
@@ -490,17 +1213,20 @@ function useSessionEvents(sessionId, options = {}) {
490
1213
  setHasOlder(false);
491
1214
  return false;
492
1215
  }
493
- const generation = generationRef.current;
1216
+ const generation = navigationGenerationRef.current;
1217
+ const isCurrent = () => ownsNavigation() && navigationGenerationRef.current === generation;
494
1218
  loadingOlderRef.current = true;
495
1219
  setLoadingOlder(true);
1220
+ let published = false;
496
1221
  try {
497
1222
  const window = await loadEventWindow(client, workspaceId, sessionId, {
498
1223
  before,
499
- pageSize: OLDER_PAGE_SIZE,
1224
+ pageSize: SESSION_HISTORY_PAGE_SIZE,
500
1225
  targetGroups: OLDER_GROUP_TARGET,
501
- maxFetches: OLDER_FETCH_CAP
1226
+ maxFetches: OLDER_FETCH_CAP,
1227
+ isCurrent
502
1228
  });
503
- if (generationRef.current !== generation) {
1229
+ if (!isCurrent()) {
504
1230
  return false;
505
1231
  }
506
1232
  if (window.events.length === 0) {
@@ -511,11 +1237,14 @@ function useSessionEvents(sessionId, options = {}) {
511
1237
  }
512
1238
  const current = eventWindowRef.current;
513
1239
  assertPrependOrder(current.events, window.events);
514
- streamAbortRef.current?.abort();
515
- observeSessionStatus(window.events, sessionStatusRef, setSessionStatusProjection);
516
1240
  const next = boundBrowserSessionEventWindow([...window.events, ...current.events], {
517
1241
  direction: "oldest"
518
1242
  });
1243
+ if (preserveTail && maxResumeSequence(next.events) < maxResumeSequence(current.events)) {
1244
+ markTailPreserved();
1245
+ return false;
1246
+ }
1247
+ const status = observeSessionStatus(window.events, sessionStatusRef);
519
1248
  const retained = {
520
1249
  ...next,
521
1250
  truncated: current.truncated || next.truncated
@@ -524,47 +1253,80 @@ function useSessionEvents(sessionId, options = {}) {
524
1253
  if (retainedOldest === null || retainedOldest >= before) {
525
1254
  throw new Error("@opengeni/react: loadOlder made no durable sequence progress");
526
1255
  }
527
- const retainedNewest = retained.events.at(-1)?.sequence ?? null;
1256
+ const retainedNewest = maxResumeSequenceOrNull(retained.events);
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
+ }
528
1262
  eventWindowRef.current = retained;
529
1263
  markCommitted();
530
- setEventWindow(retained);
531
1264
  oldestSequenceRef.current = retainedOldest;
532
1265
  newestSequenceRef.current = retainedNewest;
533
- streamResumeSequenceRef.current = maxResumeSequence(retained.events);
1266
+ if (evictedLiveTail) {
1267
+ streamResumeSequenceRef.current = maxResumeSequence(retained.events);
1268
+ }
534
1269
  const olderStillAvailable = window.hasOlder;
535
1270
  hasOlderRef.current = olderStillAvailable;
536
- setHasOlder(olderStillAvailable);
537
- if (viewModeRef.current === "history") {
1271
+ let newer = null;
1272
+ let enterHistory = false;
1273
+ if (viewModeRef.current === "history" || evictedLiveTail) {
538
1274
  const highWater = lastSequenceRef.current;
539
- const newer = retainedNewest !== null && (retainedNewest < highWater || retained.truncated);
1275
+ newer = retainedNewest !== null && (retainedNewest < highWater || retained.truncated);
540
1276
  hasNewerRef.current = newer;
541
- setHasNewer(newer);
542
- } else {
543
- setStreamEpoch((epoch) => epoch + 1);
1277
+ if (evictedLiveTail) {
1278
+ viewModeRef.current = "history";
1279
+ enterHistory = true;
1280
+ }
544
1281
  }
1282
+ loadingOlderRef.current = false;
1283
+ startTransition(() => {
1284
+ if (status !== void 0) {
1285
+ setSessionStatusProjection(status);
1286
+ }
1287
+ setEventWindow(retained);
1288
+ setHasOlder(olderStillAvailable);
1289
+ if (newer !== null) {
1290
+ setHasNewer(newer);
1291
+ }
1292
+ if (enterHistory) {
1293
+ setViewMode("history");
1294
+ }
1295
+ setLoadingOlder(false);
1296
+ });
1297
+ published = true;
545
1298
  return olderStillAvailable;
1299
+ } catch (reason) {
1300
+ if (!isCurrent()) return false;
1301
+ throw reason;
546
1302
  } finally {
547
- loadingOlderRef.current = false;
548
- setLoadingOlder(false);
1303
+ if (!published && isCurrent()) {
1304
+ loadingOlderRef.current = false;
1305
+ setLoadingOlder(false);
1306
+ }
549
1307
  }
550
1308
  }),
551
- [client, workspaceId, sessionId]
1309
+ [client, workspaceId, sessionId, ownsNavigation]
552
1310
  );
553
- const loadOldest = useCallback2(async () => {
554
- if (!sessionId || navigationBusy() || !hasOlderRef.current) {
1311
+ const loadOldest = useCallback6(async () => {
1312
+ if (!ownsNavigation() || !sessionId || navigationBusy() || !hasOlderRef.current) {
555
1313
  return false;
556
1314
  }
557
- const generation = generationRef.current;
1315
+ const generation = ++navigationGenerationRef.current;
1316
+ const isCurrent = () => ownsNavigation() && navigationGenerationRef.current === generation;
1317
+ setQuestionEvidence(null);
558
1318
  loadingOldestRef.current = true;
559
1319
  setLoadingOldest(true);
1320
+ let published = false;
560
1321
  try {
561
1322
  const window = await loadForwardEventWindow(client, workspaceId, sessionId, {
562
1323
  after: 0,
563
- pageSize: OLDEST_PAGE_SIZE,
1324
+ pageSize: SESSION_HISTORY_PAGE_SIZE,
564
1325
  targetGroups: OLDEST_GROUP_TARGET,
565
- maxFetches: OLDEST_FETCH_CAP
1326
+ maxFetches: OLDEST_FETCH_CAP,
1327
+ isCurrent
566
1328
  });
567
- if (generationRef.current !== generation) {
1329
+ if (!isCurrent()) {
568
1330
  return false;
569
1331
  }
570
1332
  if (window.events.length === 0) {
@@ -573,32 +1335,48 @@ function useSessionEvents(sessionId, options = {}) {
573
1335
  return false;
574
1336
  }
575
1337
  streamAbortRef.current?.abort();
576
- observeSessionStatus(window.events, sessionStatusRef, setSessionStatusProjection);
1338
+ const status = observeSessionStatus(window.events, sessionStatusRef);
577
1339
  const retained = boundBrowserSessionEventWindow(window.events, { direction: "oldest" });
578
1340
  const retainedOldest = retained.events[0]?.sequence ?? null;
579
- const retainedNewest = retained.events.at(-1)?.sequence ?? null;
1341
+ const retainedNewest = maxResumeSequenceOrNull(retained.events);
580
1342
  eventWindowRef.current = retained;
581
- setEventWindow(retained);
582
1343
  oldestSequenceRef.current = retainedOldest;
583
1344
  newestSequenceRef.current = retainedNewest;
584
1345
  hasOlderRef.current = false;
585
- setHasOlder(false);
586
1346
  const highWater = Math.max(lastSequenceRef.current, retainedNewest ?? 0);
587
1347
  lastSequenceRef.current = highWater;
588
1348
  const newer = window.hasNewer || retained.truncated || retainedNewest !== null && retainedNewest < highWater;
589
1349
  hasNewerRef.current = newer;
590
- setHasNewer(newer);
591
1350
  initialWindowLoadedRef.current = true;
1351
+ setInitialHistoryReady(true);
1352
+ setError(null);
592
1353
  viewModeRef.current = "history";
593
- setViewMode("history");
1354
+ loadingOldestRef.current = false;
1355
+ setNewerError(null);
1356
+ startTransition(() => {
1357
+ if (status !== void 0) {
1358
+ setSessionStatusProjection(status);
1359
+ }
1360
+ setEventWindow(retained);
1361
+ setHasOlder(false);
1362
+ setHasNewer(newer);
1363
+ setViewMode("history");
1364
+ setLoadingOldest(false);
1365
+ });
1366
+ published = true;
594
1367
  return newer;
1368
+ } catch (reason) {
1369
+ if (!isCurrent()) return false;
1370
+ throw reason;
595
1371
  } finally {
596
- loadingOldestRef.current = false;
597
- setLoadingOldest(false);
1372
+ if (!published && isCurrent()) {
1373
+ loadingOldestRef.current = false;
1374
+ setLoadingOldest(false);
1375
+ }
598
1376
  }
599
- }, [client, workspaceId, sessionId]);
600
- const loadNewer = useCallback2(async () => {
601
- if (!sessionId || navigationBusy() || !hasNewerRef.current) {
1377
+ }, [client, workspaceId, sessionId, ownsNavigation]);
1378
+ const loadNewer = useCallback6(async () => {
1379
+ if (!ownsNavigation() || !sessionId || navigationBusy() || !hasNewerRef.current) {
602
1380
  return false;
603
1381
  }
604
1382
  const afterSequence = newestSequenceRef.current;
@@ -607,20 +1385,24 @@ function useSessionEvents(sessionId, options = {}) {
607
1385
  setHasNewer(false);
608
1386
  return false;
609
1387
  }
610
- const generation = generationRef.current;
1388
+ const generation = navigationGenerationRef.current;
1389
+ const isCurrent = () => ownsNavigation() && navigationGenerationRef.current === generation;
611
1390
  loadingNewerRef.current = true;
612
1391
  setLoadingNewer(true);
1392
+ let published = false;
613
1393
  try {
614
1394
  const window = await loadForwardEventWindow(client, workspaceId, sessionId, {
615
1395
  after: afterSequence,
616
- pageSize: NEWER_PAGE_SIZE,
1396
+ pageSize: SESSION_HISTORY_PAGE_SIZE,
617
1397
  targetGroups: NEWER_GROUP_TARGET,
618
- maxFetches: NEWER_FETCH_CAP
1398
+ maxFetches: NEWER_FETCH_CAP,
1399
+ isCurrent
619
1400
  });
620
- if (generationRef.current !== generation) {
1401
+ if (!isCurrent()) {
621
1402
  return false;
622
1403
  }
623
1404
  if (window.events.length === 0) {
1405
+ setNewerError(null);
624
1406
  hasNewerRef.current = false;
625
1407
  setHasNewer(false);
626
1408
  streamResumeSequenceRef.current = afterSequence;
@@ -631,85 +1413,246 @@ function useSessionEvents(sessionId, options = {}) {
631
1413
  }
632
1414
  const current = eventWindowRef.current;
633
1415
  assertAppendOrder(current.events, window.events);
634
- observeSessionStatus(window.events, sessionStatusRef, setSessionStatusProjection);
1416
+ const status = observeSessionStatus(window.events, sessionStatusRef);
635
1417
  const previousOldest = current.events[0]?.sequence ?? null;
636
- const next = boundBrowserSessionEventWindow([...current.events, ...window.events], {
637
- direction: "newest"
638
- });
1418
+ const next = appendBrowserSessionEventWindow(current, window.events);
639
1419
  const retained = {
640
1420
  ...next,
641
1421
  truncated: current.truncated || next.truncated || window.hasNewer
642
1422
  };
643
1423
  const retainedOldest = retained.events[0]?.sequence ?? null;
644
- const retainedNewest = retained.events.at(-1)?.sequence ?? null;
1424
+ const retainedNewest = maxResumeSequenceOrNull(retained.events);
645
1425
  if (retainedNewest === null || retainedNewest <= afterSequence) {
646
1426
  throw new Error("@opengeni/react: loadNewer made no durable sequence progress");
647
1427
  }
648
1428
  eventWindowRef.current = retained;
649
- setEventWindow(retained);
650
1429
  oldestSequenceRef.current = retainedOldest;
651
1430
  newestSequenceRef.current = retainedNewest;
652
1431
  if (retainedOldest !== null && (previousOldest === null || retainedOldest > previousOldest || retained.truncated && retained.events[0]?.type !== "session.created" && retainedOldest > 1)) {
653
1432
  hasOlderRef.current = true;
654
- setHasOlder(true);
655
1433
  }
1434
+ const rearmedOlder = hasOlderRef.current;
656
1435
  const highWater = Math.max(lastSequenceRef.current, retainedNewest);
657
1436
  lastSequenceRef.current = highWater;
658
1437
  const newer = window.hasNewer || retainedNewest < highWater;
659
1438
  hasNewerRef.current = newer;
660
- setHasNewer(newer);
1439
+ let resumeLive = false;
661
1440
  if (!newer) {
662
1441
  streamResumeSequenceRef.current = retainedNewest;
663
1442
  viewModeRef.current = "live";
664
- setViewMode("live");
665
- setStreamEpoch((epoch) => epoch + 1);
1443
+ resumeLive = true;
666
1444
  }
1445
+ loadingNewerRef.current = false;
1446
+ setNewerError(null);
1447
+ startTransition(() => {
1448
+ if (status !== void 0) {
1449
+ setSessionStatusProjection(status);
1450
+ }
1451
+ setEventWindow(retained);
1452
+ setHasOlder(rearmedOlder);
1453
+ setHasNewer(newer);
1454
+ if (resumeLive) {
1455
+ setViewMode("live");
1456
+ setStreamEpoch((epoch) => epoch + 1);
1457
+ }
1458
+ setLoadingNewer(false);
1459
+ });
1460
+ published = true;
667
1461
  return newer;
1462
+ } catch (reason) {
1463
+ if (!isCurrent()) {
1464
+ return false;
1465
+ }
1466
+ setNewerError(normalizeError(reason));
1467
+ throw reason;
668
1468
  } finally {
669
- loadingNewerRef.current = false;
1469
+ if (!published && isCurrent()) {
1470
+ loadingNewerRef.current = false;
1471
+ setLoadingNewer(false);
1472
+ }
1473
+ }
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);
670
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);
671
1593
  }
672
- }, [client, workspaceId, sessionId]);
673
- const jumpToLatest = useCallback2(async () => {
674
1594
  if (!sessionId || navigationBusy()) {
675
1595
  return;
676
1596
  }
1597
+ navigationGenerationRef.current += 1;
1598
+ setQuestionEvidence(null);
677
1599
  loadingLatestRef.current = true;
678
1600
  setLoadingLatest(true);
1601
+ setNewerError(null);
1602
+ setError(null);
1603
+ let published = false;
679
1604
  try {
680
1605
  streamAbortRef.current?.abort();
681
1606
  hasNewerRef.current = false;
682
- setHasNewer(false);
683
1607
  hasOlderRef.current = false;
684
- setHasOlder(false);
685
1608
  newestSequenceRef.current = null;
686
1609
  oldestSequenceRef.current = null;
687
1610
  eventWindowRef.current = EMPTY_EVENT_WINDOW;
688
- setEventWindow(EMPTY_EVENT_WINDOW);
689
1611
  initialWindowLoadedRef.current = false;
690
1612
  streamResumeSequenceRef.current = after;
691
- setInitialLoading(true);
692
1613
  viewModeRef.current = "live";
693
- setViewMode("live");
694
- setStreamEpoch((epoch) => epoch + 1);
1614
+ loadingLatestRef.current = false;
1615
+ startTransition(() => {
1616
+ setHasNewer(false);
1617
+ setHasOlder(false);
1618
+ setEventWindow(EMPTY_EVENT_WINDOW);
1619
+ setInitialLoading(true);
1620
+ setViewMode("live");
1621
+ setStreamEpoch((epoch) => epoch + 1);
1622
+ setLoadingLatest(false);
1623
+ });
1624
+ published = true;
695
1625
  await Promise.resolve();
696
1626
  } finally {
697
- loadingLatestRef.current = false;
698
- setLoadingLatest(false);
1627
+ if (!published) {
1628
+ loadingLatestRef.current = false;
1629
+ setLoadingLatest(false);
1630
+ }
699
1631
  }
700
- }, [after, sessionId]);
1632
+ }, [after, sessionId, ownsNavigation]);
701
1633
  const identityMatches = stateStreamKey === streamKey;
702
1634
  const visibleEvents = identityMatches ? eventWindow.events : EMPTY_EVENTS;
703
- 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]);
704
1645
  return {
705
1646
  events: visibleEvents,
706
1647
  timeline,
707
1648
  sessionStatus: identityMatches ? sessionStatusProjection : null,
1649
+ sessionStatusSequence: identityMatches ? sessionStatusRef.current.sequence : 0,
708
1650
  connectionState: identityMatches ? connectionState : "idle",
709
1651
  lastSequence: identityMatches ? lastSequenceRef.current : after,
710
1652
  windowBytes: identityMatches ? eventWindow.bytes : 2,
711
1653
  windowTruncated: identityMatches ? eventWindow.truncated : false,
712
1654
  initialLoading: fullReplay ? false : identityMatches ? initialLoading : true,
1655
+ initialHistoryReady: identityMatches && initialHistoryReady,
713
1656
  hasOlder: !identityMatches ? false : hasOlder,
714
1657
  loadingOlder: !identityMatches ? false : loadingOlder,
715
1658
  loadOlder,
@@ -720,13 +1663,16 @@ function useSessionEvents(sessionId, options = {}) {
720
1663
  loadOldest,
721
1664
  loadingLatest: !identityMatches ? false : loadingLatest,
722
1665
  jumpToLatest,
723
- error: identityMatches ? error : null
1666
+ jumpToLatestQuestion,
1667
+ jumpToSequence,
1668
+ loadingTarget: identityMatches && loadingTarget,
1669
+ error: identityMatches ? newerError ?? error : null
724
1670
  };
725
1671
  }
726
1672
  function boundBrowserSessionEventWindow(events, options = {}) {
727
1673
  const maxBytes = Math.max(1024, options.maxBytes ?? SESSION_EVENT_BROWSER_MAX_BYTES);
728
1674
  const maxCount = Math.max(1, Math.floor(options.maxCount ?? SESSION_EVENT_BROWSER_MAX_COUNT));
729
- const safe = events.map(boundBrowserLegacyEvent);
1675
+ const safe = events;
730
1676
  const selected = [];
731
1677
  let bytes = 2;
732
1678
  const direction = options.direction ?? "newest";
@@ -737,7 +1683,7 @@ function boundBrowserSessionEventWindow(events, options = {}) {
737
1683
  const event = safe[index];
738
1684
  const eventBytes = browserJsonBytes(event);
739
1685
  const separator = selected.length === 0 ? 0 : 1;
740
- if (bytes + separator + eventBytes > maxBytes) break;
1686
+ if (selected.length > 0 && bytes + separator + eventBytes > maxBytes) break;
741
1687
  selected.push(event);
742
1688
  bytes += separator + eventBytes;
743
1689
  }
@@ -748,184 +1694,29 @@ function boundBrowserSessionEventWindow(events, options = {}) {
748
1694
  truncated: selected.length < safe.length
749
1695
  };
750
1696
  }
751
- function boundBrowserLegacyEvent(event) {
752
- const serialized = browserSerialize(event);
753
- const originalBytes = serialized.serializable ? encoder.encode(serialized.value).byteLength : null;
754
- const typeIsSafe = browserUtf8Bytes(event.type) <= BROWSER_EVENT_TYPE_MAX_BYTES && !event.type.includes("\n") && !event.type.includes("\r");
755
- const clientEventId = boundBrowserOptionalText(
756
- event.clientEventId,
757
- BROWSER_EVENT_CLIENT_ID_MAX_BYTES
758
- );
759
- const duplicateReason = boundBrowserOptionalText(
760
- event.duplicateReason,
761
- BROWSER_EVENT_DUPLICATE_REASON_MAX_BYTES
762
- );
763
- if (serialized.serializable && originalBytes !== null && originalBytes <= SESSION_EVENT_BROWSER_SINGLE_EVENT_MAX_BYTES && typeIsSafe && clientEventId === event.clientEventId && duplicateReason === event.duplicateReason) {
764
- return event;
765
- }
766
- const envelopeProjection = [
767
- !typeIsSafe ? browserEnvelopeFieldProjection("type", event.type, "session.event.envelope_omitted") : null,
768
- clientEventId !== event.clientEventId ? browserEnvelopeFieldProjection("clientEventId", event.clientEventId, clientEventId) : null,
769
- duplicateReason !== event.duplicateReason ? browserEnvelopeFieldProjection("duplicateReason", event.duplicateReason, duplicateReason) : null
770
- ].filter((field) => field !== null);
771
- const payloadSerialization = browserSerialize(event.payload);
772
- const payloadBytes = payloadSerialization.serializable ? encoder.encode(payloadSerialization.value).byteLength : null;
773
- const preview = truncateBrowserUtf8Middle(
774
- payloadSerialization.value,
775
- BROWSER_EVENT_PAYLOAD_PREVIEW_MAX_BYTES
776
- );
777
- const truncation = {
778
- truncated: true,
779
- surface: "browser_legacy_guard",
780
- reason: serialized.serializable ? "event_envelope_bytes_exceeded" : "event_not_serializable",
781
- originalBytes,
782
- deliveredBytes: 0,
783
- omittedBytes: originalBytes,
784
- estimatedOriginalTokens: originalBytes === null ? null : Math.ceil(originalBytes / 4),
785
- estimatedDeliveredTokens: 0,
786
- fullEvidence: { available: false, reason: "not_retained" },
787
- details: [
788
- {
789
- path: "$.payload",
790
- kind: "payload_preview",
791
- originalBytes: payloadBytes,
792
- deliveredBytes: browserUtf8Bytes(preview)
793
- }
794
- ]
795
- };
796
- const payload = {
797
- ...browserPayloadIdentity(event.payload),
798
- preview,
799
- ...envelopeProjection.length > 0 ? {
800
- originalType: boundBrowserText(event.type, BROWSER_EVENT_TYPE_MAX_BYTES),
801
- envelopeProjection: {
802
- truncated: true,
803
- surface: "browser_legacy_guard",
804
- fields: envelopeProjection
805
- }
806
- } : {},
807
- truncation
808
- };
809
- const bounded = {
810
- id: boundBrowserText(event.id, BROWSER_EVENT_ID_MAX_BYTES),
811
- workspaceId: boundBrowserText(event.workspaceId, BROWSER_EVENT_ID_MAX_BYTES),
812
- sessionId: boundBrowserText(event.sessionId, BROWSER_EVENT_ID_MAX_BYTES),
813
- sequence: event.sequence,
814
- type: typeIsSafe ? event.type : "session.event.envelope_omitted",
815
- payload,
816
- occurredAt: boundBrowserText(event.occurredAt, BROWSER_EVENT_ID_MAX_BYTES),
817
- clientEventId,
818
- turnId: boundBrowserOptionalText(event.turnId, BROWSER_EVENT_ID_MAX_BYTES),
819
- turnGeneration: event.turnGeneration,
820
- turnAttemptId: boundBrowserOptionalText(event.turnAttemptId, BROWSER_EVENT_ID_MAX_BYTES),
821
- turnAssociation: event.turnAssociation,
822
- duplicateOfEventId: boundBrowserOptionalText(
823
- event.duplicateOfEventId,
824
- BROWSER_EVENT_ID_MAX_BYTES
825
- ),
826
- duplicateReason
827
- };
828
- settleBrowserEventTruncation(bounded, truncation);
829
- if (browserJsonBytes(bounded) > SESSION_EVENT_BROWSER_SINGLE_EVENT_MAX_BYTES) {
830
- payload.preview = truncateBrowserUtf8Middle(String(payload.preview), 4 * 1024);
831
- truncation.details = truncation.details.slice(0, 1);
832
- settleBrowserEventTruncation(bounded, truncation);
833
- }
834
- if (browserJsonBytes(bounded) > SESSION_EVENT_BROWSER_SINGLE_EVENT_MAX_BYTES) {
835
- bounded.clientEventId = null;
836
- bounded.duplicateReason = null;
837
- payload.preview = "[legacy event omitted at the browser byte boundary]";
838
- settleBrowserEventTruncation(bounded, truncation);
839
- }
840
- return bounded;
841
- }
842
- function browserJsonBytes(value) {
843
- return encoder.encode(browserSerialize(value).value).byteLength;
844
- }
845
- function browserSerialize(value) {
846
- try {
847
- const serialized = JSON.stringify(value);
848
- return { value: serialized === void 0 ? "null" : serialized, serializable: true };
849
- } catch {
850
- return {
851
- value: '"[unserializable event payload omitted at browser boundary]"',
852
- serializable: false
853
- };
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);
854
1704
  }
855
- }
856
- function browserPayloadIdentity(payload) {
857
- if (!payload || typeof payload !== "object" || Array.isArray(payload)) return {};
858
- const record = payload;
859
- const identity = {};
860
- for (const field of BROWSER_EVENT_PAYLOAD_IDENTITY_FIELDS) {
861
- const value = record[field];
862
- if (typeof value === "string") {
863
- identity[field] = boundBrowserText(value, BROWSER_EVENT_ID_MAX_BYTES);
864
- } else if (typeof value === "number" || typeof value === "boolean" || value === null) {
865
- identity[field] = value;
866
- }
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;
867
1709
  }
868
- return identity;
869
- }
870
- function browserEnvelopeFieldProjection(field, original, delivered) {
871
1710
  return {
872
- field,
873
- originalBytes: typeof original === "string" ? browserUtf8Bytes(original) : 0,
874
- deliveredBytes: typeof delivered === "string" ? browserUtf8Bytes(delivered) : 0
1711
+ events: start === 0 ? events : events.slice(start),
1712
+ bytes,
1713
+ truncated: current.truncated || start > 0
875
1714
  };
876
1715
  }
877
- function boundBrowserOptionalText(value, maxBytes) {
878
- return typeof value === "string" ? boundBrowserText(value, maxBytes) : value;
879
- }
880
- function boundBrowserText(value, maxBytes) {
881
- const bytes = encoder.encode(value);
882
- if (bytes.byteLength <= maxBytes) return value;
883
- const marker = "\u2026[truncated]";
884
- const prefixBudget = Math.max(0, maxBytes - browserUtf8Bytes(marker));
885
- let prefixEnd = Math.min(prefixBudget, bytes.byteLength);
886
- while (prefixEnd > 0 && prefixEnd < bytes.byteLength && isBrowserUtf8Continuation(bytes[prefixEnd])) {
887
- prefixEnd -= 1;
888
- }
889
- return `${decoder.decode(bytes.subarray(0, prefixEnd))}${marker}`;
890
- }
891
- function truncateBrowserUtf8Middle(value, maxBytes) {
892
- const bytes = encoder.encode(value);
893
- if (bytes.byteLength <= maxBytes) return value;
894
- const marker = `\u2026[${bytes.byteLength - maxBytes} bytes omitted]\u2026`;
895
- const contentBudget = Math.max(0, maxBytes - browserUtf8Bytes(marker));
896
- const leftBudget = Math.floor(contentBudget / 2);
897
- const rightBudget = contentBudget - leftBudget;
898
- let leftEnd = Math.min(leftBudget, bytes.byteLength);
899
- while (leftEnd > 0 && leftEnd < bytes.byteLength && isBrowserUtf8Continuation(bytes[leftEnd])) {
900
- leftEnd -= 1;
901
- }
902
- let rightStart = Math.max(0, bytes.byteLength - rightBudget);
903
- while (rightStart < bytes.byteLength && isBrowserUtf8Continuation(bytes[rightStart])) {
904
- rightStart += 1;
905
- }
906
- return `${decoder.decode(bytes.subarray(0, leftEnd))}${marker}${decoder.decode(bytes.subarray(rightStart))}`;
907
- }
908
- function settleBrowserEventTruncation(event, truncation) {
909
- for (let attempt = 0; attempt < 16; attempt += 1) {
910
- const deliveredBytes = browserJsonBytes(event);
911
- const omittedBytes = truncation.originalBytes === null ? null : Math.max(0, truncation.originalBytes - deliveredBytes);
912
- const estimatedDeliveredTokens = Math.ceil(deliveredBytes / 4);
913
- if (truncation.deliveredBytes === deliveredBytes && truncation.omittedBytes === omittedBytes && truncation.estimatedDeliveredTokens === estimatedDeliveredTokens) {
914
- return;
915
- }
916
- truncation.deliveredBytes = deliveredBytes;
917
- truncation.omittedBytes = omittedBytes;
918
- truncation.estimatedDeliveredTokens = estimatedDeliveredTokens;
919
- }
920
- throw new RangeError("Browser event byte accounting did not converge");
921
- }
922
- function browserUtf8Bytes(value) {
923
- return encoder.encode(value).byteLength;
924
- }
925
- function isBrowserUtf8Continuation(value) {
926
- return (value & 192) === 128;
1716
+ function browserJsonBytes(value) {
1717
+ return encoder.encode(JSON.stringify(value)).byteLength;
927
1718
  }
928
- function observeSessionStatus(events, ref, setStatus) {
1719
+ function observeSessionStatus(events, ref) {
929
1720
  let latest = null;
930
1721
  for (const event of events) {
931
1722
  const status = sessionStatusFromEvents([event]);
@@ -933,9 +1724,42 @@ function observeSessionStatus(events, ref, setStatus) {
933
1724
  latest = { sequence: event.sequence, status };
934
1725
  }
935
1726
  }
936
- if (!latest || latest.sequence < ref.current.sequence) return;
937
- ref.current = latest;
938
- setStatus(latest.status);
1727
+ if (!latest || latest.sequence < ref.current.sequence) return void 0;
1728
+ ref.current = latest;
1729
+ return latest.status;
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 };
939
1763
  }
940
1764
  async function loadEventWindow(client, workspaceId, sessionId, options) {
941
1765
  let cursor = options.before;
@@ -950,6 +1774,7 @@ async function loadEventWindow(client, workspaceId, sessionId, options) {
950
1774
  pageSize: options.pageSize,
951
1775
  ...options.signal ? { signal: options.signal } : {}
952
1776
  });
1777
+ if (options.isCurrent?.() === false) throw abortError();
953
1778
  fetches += 1;
954
1779
  if (page.length === 0) {
955
1780
  reachedStart = true;
@@ -964,11 +1789,12 @@ async function loadEventWindow(client, workspaceId, sessionId, options) {
964
1789
  }
965
1790
  }
966
1791
  let snapPages = 0;
967
- while (!reachedStart && findBoundaryIndex(buffer) === -1 && snapPages < BOUNDARY_PAGE_CAP && fetches < options.maxFetches) {
1792
+ while (!reachedStart && findBoundaryIndex(buffer) === -1 && snapPages < (options.boundaryPageCap ?? 0)) {
968
1793
  const page = await loadPreviousPage(client, workspaceId, sessionId, cursor, {
969
1794
  pageSize: options.pageSize,
970
1795
  ...options.signal ? { signal: options.signal } : {}
971
1796
  });
1797
+ if (options.isCurrent?.() === false) throw abortError();
972
1798
  fetches += 1;
973
1799
  snapPages += 1;
974
1800
  if (page.length === 0) {
@@ -989,24 +1815,28 @@ async function loadEventWindow(client, workspaceId, sessionId, options) {
989
1815
  buffer = buffer.slice(boundary);
990
1816
  }
991
1817
  }
1818
+ const densityWindow = trimBackwardToGroupTarget(buffer, options.targetGroups);
1819
+ buffer = densityWindow.events;
992
1820
  const oldest = buffer[0] ?? null;
993
1821
  const newest = buffer[buffer.length - 1] ?? null;
994
1822
  return {
995
1823
  events: buffer,
996
1824
  oldestSequence: oldest?.sequence ?? null,
997
1825
  newestSequence: newest ? maxResumeSequence(buffer) : 0,
998
- hasOlder: buffer.length > 0 && !reachedStart && oldest?.type !== "session.created"
1826
+ hasOlder: buffer.length > 0 && (densityWindow.trimmed || !reachedStart && oldest?.type !== "session.created")
999
1827
  };
1000
1828
  }
1001
1829
  function findBoundaryIndex(events) {
1002
1830
  for (let index = 0; index < events.length; index += 1) {
1003
- const type = events[index].type;
1004
- if (type === "session.created" || type === "user.message") {
1831
+ if (isTurnBoundary(events[index])) {
1005
1832
  return index;
1006
1833
  }
1007
1834
  }
1008
1835
  return -1;
1009
1836
  }
1837
+ function isTurnBoundary(event) {
1838
+ return event.type === "session.created" || event.type === "user.message";
1839
+ }
1010
1840
  async function loadForwardEventWindow(client, workspaceId, sessionId, options) {
1011
1841
  let cursor = options.after;
1012
1842
  let buffer = [];
@@ -1020,6 +1850,7 @@ async function loadForwardEventWindow(client, workspaceId, sessionId, options) {
1020
1850
  pageSize: options.pageSize,
1021
1851
  ...options.signal ? { signal: options.signal } : {}
1022
1852
  });
1853
+ if (options.isCurrent?.() === false) throw abortError();
1023
1854
  fetches += 1;
1024
1855
  if (page.length === 0) {
1025
1856
  reachedEnd = true;
@@ -1027,21 +1858,45 @@ async function loadForwardEventWindow(client, workspaceId, sessionId, options) {
1027
1858
  }
1028
1859
  assertAscending(page);
1029
1860
  buffer = [...buffer, ...page];
1030
- cursor = page[page.length - 1].sequence;
1031
- if (page.length < options.pageSize) {
1032
- reachedEnd = true;
1033
- break;
1034
- }
1861
+ cursor = maxResumeSequence(page);
1035
1862
  }
1863
+ const densityWindow = trimForwardToGroupTarget(buffer, options.targetGroups);
1864
+ buffer = densityWindow.events;
1036
1865
  const oldest = buffer[0] ?? null;
1037
1866
  const newest = buffer[buffer.length - 1] ?? null;
1038
1867
  return {
1039
1868
  events: buffer,
1040
1869
  oldestSequence: oldest?.sequence ?? null,
1041
1870
  newestSequence: newest ? maxResumeSequence(buffer) : 0,
1042
- hasNewer: buffer.length > 0 && !reachedEnd
1871
+ hasNewer: buffer.length > 0 && (densityWindow.trimmed || !reachedEnd)
1043
1872
  };
1044
1873
  }
1874
+ function trimBackwardToGroupTarget(events, targetGroups) {
1875
+ if (!Number.isFinite(targetGroups) || targetGroups <= 0 || events.length === 0) {
1876
+ return { events, trimmed: false };
1877
+ }
1878
+ for (let index = events.length - 1; index > 0; index -= 1) {
1879
+ if (!isTurnBoundary(events[index])) continue;
1880
+ const candidate = events.slice(index);
1881
+ if (groupCount(candidate) >= targetGroups) {
1882
+ return { events: candidate, trimmed: true };
1883
+ }
1884
+ }
1885
+ return { events, trimmed: false };
1886
+ }
1887
+ function trimForwardToGroupTarget(events, targetGroups) {
1888
+ if (!Number.isFinite(targetGroups) || targetGroups <= 0 || events.length === 0) {
1889
+ return { events, trimmed: false };
1890
+ }
1891
+ for (let index = 1; index < events.length; index += 1) {
1892
+ if (!isTurnBoundary(events[index])) continue;
1893
+ const candidate = events.slice(0, index);
1894
+ if (groupCount(candidate) >= targetGroups) {
1895
+ return { events: candidate, trimmed: true };
1896
+ }
1897
+ }
1898
+ return { events, trimmed: false };
1899
+ }
1045
1900
  async function loadPreviousPage(client, workspaceId, sessionId, before, options) {
1046
1901
  if (options.signal?.aborted) {
1047
1902
  throw abortError();
@@ -1097,13 +1952,13 @@ function isLogStart(event) {
1097
1952
  function maxResumeSequence(events) {
1098
1953
  return events.reduce((max, event) => Math.max(max, eventResumeSequence(event)), 0);
1099
1954
  }
1100
- function eventResumeSequence(event) {
1101
- const payload = asRecord(event.payload);
1102
- const coalescedUntil = Number(payload.coalescedUntil);
1103
- return Math.max(event.sequence, Number.isFinite(coalescedUntil) ? Math.floor(coalescedUntil) : 0);
1955
+ function maxResumeSequenceOrNull(events) {
1956
+ return events.length > 0 ? maxResumeSequence(events) : null;
1104
1957
  }
1105
- function asRecord(value) {
1106
- return value && typeof value === "object" ? value : {};
1958
+ function eventResumeSequence(event) {
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;
1107
1962
  }
1108
1963
  function assertAscending(events) {
1109
1964
  for (let index = 1; index < events.length; index += 1) {
@@ -1115,272 +1970,31 @@ function assertAscending(events) {
1115
1970
  function assertPrependOrder(existing, older) {
1116
1971
  if (!shouldAssertDevelopment() || existing.length === 0 || older.length === 0) {
1117
1972
  return;
1118
- }
1119
- if (older[older.length - 1].sequence >= existing[0].sequence) {
1120
- throw new Error("@opengeni/react: loadOlder returned overlapping session events");
1121
- }
1122
- }
1123
- function assertAppendOrder(existing, newer) {
1124
- if (!shouldAssertDevelopment() || existing.length === 0 || newer.length === 0) {
1125
- return;
1126
- }
1127
- if (newer[0].sequence <= existing[existing.length - 1].sequence) {
1128
- throw new Error("@opengeni/react: loadNewer returned overlapping session events");
1129
- }
1130
- }
1131
- function shouldAssertDevelopment() {
1132
- const processEnv = globalThis.process?.env;
1133
- return processEnv !== void 0 && processEnv.NODE_ENV !== "production";
1134
- }
1135
- function abortError() {
1136
- const error = new Error("The operation was aborted.");
1137
- error.name = "AbortError";
1138
- return error;
1139
- }
1140
-
1141
- // src/hooks/use-file-attachments.ts
1142
- import { useCallback as useCallback3, useEffect as useEffect3, useRef as useRef3, useState as useState3 } from "react";
1143
- var isImage = (file) => file.type.startsWith("image/");
1144
- function useFileAttachments(options = {}) {
1145
- const { client, workspaceId } = useEmbeddedFileAttachments(options);
1146
- const pasteFilter = options.pasteFilter ?? isImage;
1147
- const [attachments, setAttachments] = useState3([]);
1148
- const sources = useRef3(/* @__PURE__ */ new Map());
1149
- const previewUrls = useRef3(/* @__PURE__ */ new Map());
1150
- const previewRetainers = useRef3(/* @__PURE__ */ new Map());
1151
- const pendingPreviewRevocations = useRef3(/* @__PURE__ */ new Set());
1152
- const revokePreview = useCallback3((id) => {
1153
- const previewUrl = previewUrls.current.get(id);
1154
- if (!previewUrl) return;
1155
- if ((previewRetainers.current.get(id) ?? 0) > 0) {
1156
- pendingPreviewRevocations.current.add(id);
1157
- return;
1158
- }
1159
- previewUrls.current.delete(id);
1160
- pendingPreviewRevocations.current.delete(id);
1161
- URL.revokeObjectURL(previewUrl);
1162
- }, []);
1163
- const revokeAllPreviews = useCallback3(() => {
1164
- for (const id of previewUrls.current.keys()) revokePreview(id);
1165
- }, [revokePreview]);
1166
- const retainPreview = useCallback3(
1167
- (id) => {
1168
- if (!previewUrls.current.has(id)) return void 0;
1169
- previewRetainers.current.set(id, (previewRetainers.current.get(id) ?? 0) + 1);
1170
- let released = false;
1171
- return () => {
1172
- if (released) return;
1173
- released = true;
1174
- const remaining = (previewRetainers.current.get(id) ?? 1) - 1;
1175
- if (remaining > 0) {
1176
- previewRetainers.current.set(id, remaining);
1177
- return;
1178
- }
1179
- previewRetainers.current.delete(id);
1180
- if (pendingPreviewRevocations.current.has(id)) revokePreview(id);
1181
- };
1182
- },
1183
- [revokePreview]
1184
- );
1185
- const committedAttachmentIds = useRef3(/* @__PURE__ */ new Set());
1186
- useEffect3(() => {
1187
- const currentIds = new Set(attachments.map((attachment) => attachment.id));
1188
- for (const id of committedAttachmentIds.current) {
1189
- if (!currentIds.has(id)) revokePreview(id);
1190
- }
1191
- committedAttachmentIds.current = currentIds;
1192
- }, [attachments, revokePreview]);
1193
- const scopeGeneration = useRef3(0);
1194
- const previousScope = useRef3({ client, workspaceId });
1195
- useEffect3(() => {
1196
- const previous = previousScope.current;
1197
- if (previous.client === client && previous.workspaceId === workspaceId) return;
1198
- previousScope.current = { client, workspaceId };
1199
- scopeGeneration.current += 1;
1200
- sources.current.clear();
1201
- revokeAllPreviews();
1202
- setAttachments([]);
1203
- }, [client, revokeAllPreviews, workspaceId]);
1204
- useEffect3(
1205
- () => () => {
1206
- scopeGeneration.current += 1;
1207
- sources.current.clear();
1208
- revokeAllPreviews();
1209
- },
1210
- [revokeAllPreviews]
1211
- );
1212
- const startUpload = useCallback3(
1213
- (id, file) => {
1214
- const generation = scopeGeneration.current;
1215
- void client.uploadFile(workspaceId, {
1216
- filename: file.name || "file",
1217
- contentType: file.type || "application/octet-stream",
1218
- data: file
1219
- }).then((asset) => {
1220
- if (scopeGeneration.current !== generation) return;
1221
- sources.current.delete(id);
1222
- setAttachments(
1223
- (current) => current.map(
1224
- (attachment) => attachment.id === id ? {
1225
- ...attachment,
1226
- status: "ready",
1227
- file: asset,
1228
- name: asset.filename,
1229
- contentType: asset.contentType,
1230
- sizeBytes: asset.sizeBytes,
1231
- error: void 0
1232
- } : attachment
1233
- )
1234
- );
1235
- }).catch((error) => {
1236
- if (scopeGeneration.current !== generation) return;
1237
- setAttachments(
1238
- (current) => current.map(
1239
- (attachment) => attachment.id === id ? {
1240
- ...attachment,
1241
- status: "failed",
1242
- error: error instanceof Error ? error.message : String(error)
1243
- } : attachment
1244
- )
1245
- );
1246
- });
1247
- },
1248
- [client, workspaceId]
1249
- );
1250
- const addFiles = useCallback3(
1251
- (files) => {
1252
- for (const file of files) {
1253
- const id = crypto.randomUUID();
1254
- sources.current.set(id, file);
1255
- const previewUrl = isImage(file) ? URL.createObjectURL(file) : void 0;
1256
- if (previewUrl) previewUrls.current.set(id, previewUrl);
1257
- setAttachments((current) => [
1258
- ...current,
1259
- {
1260
- id,
1261
- name: file.name || "image",
1262
- contentType: file.type || "application/octet-stream",
1263
- sizeBytes: file.size,
1264
- status: "uploading",
1265
- ...previewUrl ? { previewUrl } : {}
1266
- }
1267
- ]);
1268
- startUpload(id, file);
1269
- }
1270
- },
1271
- [startUpload]
1272
- );
1273
- const retry = useCallback3(
1274
- (id) => {
1275
- const file = sources.current.get(id);
1276
- if (!file) {
1277
- return;
1278
- }
1279
- setAttachments(
1280
- (current) => current.map(
1281
- (attachment) => attachment.id === id ? { ...attachment, status: "uploading", error: void 0 } : attachment
1282
- )
1283
- );
1284
- startUpload(id, file);
1285
- },
1286
- [startUpload]
1287
- );
1288
- const addFromPaste = useCallback3(
1289
- (event) => {
1290
- const clipboardFiles = event.clipboardData?.files;
1291
- if (!clipboardFiles) {
1292
- return;
1293
- }
1294
- const files = [...clipboardFiles].filter(pasteFilter);
1295
- if (files.length > 0) {
1296
- addFiles(files);
1297
- }
1298
- },
1299
- [addFiles, pasteFilter]
1300
- );
1301
- const restoreReadyFiles = useCallback3(
1302
- (files) => {
1303
- const incoming = /* @__PURE__ */ new Map();
1304
- for (const file of files) {
1305
- if (file.status === "ready" && file.workspaceId === workspaceId) {
1306
- incoming.set(file.id, file);
1307
- }
1308
- }
1309
- setAttachments((current) => {
1310
- const unresolved = current.filter((attachment) => attachment.status !== "ready");
1311
- const existingReady = new Map(
1312
- current.flatMap(
1313
- (attachment) => attachment.status === "ready" && attachment.file ? [[attachment.file.id, attachment]] : []
1314
- )
1315
- );
1316
- const restored = [...incoming.values()].map((file) => {
1317
- const existing = existingReady.get(file.id);
1318
- return existing ? {
1319
- ...existing,
1320
- name: file.filename,
1321
- contentType: file.contentType,
1322
- sizeBytes: file.sizeBytes,
1323
- status: "ready",
1324
- file,
1325
- error: void 0
1326
- } : {
1327
- id: `restored:${file.id}`,
1328
- name: file.filename,
1329
- contentType: file.contentType,
1330
- sizeBytes: file.sizeBytes,
1331
- status: "ready",
1332
- file
1333
- // No source File and no object URL: server metadata is the
1334
- // only authority restored across page/device boundaries.
1335
- };
1336
- });
1337
- return [...unresolved, ...restored];
1338
- });
1339
- },
1340
- [workspaceId]
1341
- );
1342
- const remove = useCallback3(
1343
- (id) => {
1344
- sources.current.delete(id);
1345
- revokePreview(id);
1346
- setAttachments((current) => current.filter((attachment) => attachment.id !== id));
1347
- },
1348
- [revokePreview]
1349
- );
1350
- const removeReadyFiles = useCallback3((fileIds) => {
1351
- const accepted = new Set(fileIds);
1352
- if (accepted.size === 0) return;
1353
- setAttachments(
1354
- (current) => current.filter((attachment) => {
1355
- return !(attachment.status === "ready" && attachment.file !== void 0 && accepted.has(attachment.file.id));
1356
- })
1357
- );
1358
- }, []);
1359
- const clear = useCallback3(() => {
1360
- sources.current.clear();
1361
- revokeAllPreviews();
1362
- setAttachments([]);
1363
- }, [revokeAllPreviews]);
1364
- return {
1365
- attachments,
1366
- readyResources: attachments.flatMap(
1367
- (attachment) => attachment.status === "ready" && attachment.file ? [{ kind: "file", fileId: attachment.file.id }] : []
1368
- ),
1369
- uploading: attachments.some((attachment) => attachment.status === "uploading"),
1370
- hasUnresolved: attachments.some((attachment) => attachment.status !== "ready"),
1371
- addFiles,
1372
- addFromPaste,
1373
- restoreReadyFiles,
1374
- retry,
1375
- retainPreview,
1376
- remove,
1377
- removeReadyFiles,
1378
- clear
1379
- };
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;
1380
1994
  }
1381
1995
 
1382
1996
  // src/hooks/use-turn-queue.ts
1383
- 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";
1384
1998
  function isTurnQueueEvent(event) {
1385
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.");
1386
2000
  }
@@ -1395,18 +2009,18 @@ function useTurnQueue(sessionId, options = {}) {
1395
2009
  const enabled = (options.enabled ?? true) && Boolean(sessionId);
1396
2010
  const targetKey = `${workspaceId}\0${sessionId ?? ""}`;
1397
2011
  const pageLive = usePageLiveActivity();
1398
- const [snapshot, setSnapshot] = useState4(null);
1399
- const [stateTargetKey, setStateTargetKey] = useState4(targetKey);
1400
- const [loading, setLoading] = useState4(enabled);
1401
- const [error, setError] = useState4(null);
1402
- const [mutationError, setMutationError] = useState4(null);
1403
- const [pendingByTurn, setPendingByTurn] = useState4({});
1404
- const [acceptedSteers, setAcceptedSteers] = useState4([]);
1405
- const pendingRef = useRef4({});
1406
- const readGeneration = useRef4(0);
1407
- const targetKeyRef = useRef4(targetKey);
1408
- const snapshotRef = useRef4(null);
1409
- 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(() => {
1410
2024
  if (targetKeyRef.current === targetKey) return;
1411
2025
  targetKeyRef.current = targetKey;
1412
2026
  readGeneration.current += 1;
@@ -1420,7 +2034,7 @@ function useTurnQueue(sessionId, options = {}) {
1420
2034
  setPendingByTurn({});
1421
2035
  setAcceptedSteers([]);
1422
2036
  }, [enabled, targetKey]);
1423
- useEffect4(() => {
2037
+ useEffect5(() => {
1424
2038
  if (acceptedSteers.length === 0 || options.events === void 0) return;
1425
2039
  const durablyStartedTurnIds = new Set(
1426
2040
  options.events.flatMap(
@@ -1432,7 +2046,7 @@ function useTurnQueue(sessionId, options = {}) {
1432
2046
  (current) => current.filter((steer) => !durablyStartedTurnIds.has(steer.turnId))
1433
2047
  );
1434
2048
  }, [acceptedSteers.length, options.events]);
1435
- const acceptSnapshot = useCallback4(
2049
+ const acceptSnapshot = useCallback7(
1436
2050
  (ownedTargetKey, next) => {
1437
2051
  if (targetKeyRef.current !== ownedTargetKey) return false;
1438
2052
  const current = snapshotRef.current;
@@ -1446,7 +2060,7 @@ function useTurnQueue(sessionId, options = {}) {
1446
2060
  },
1447
2061
  []
1448
2062
  );
1449
- const load = useCallback4(
2063
+ const load = useCallback7(
1450
2064
  async (rejectOnFailure = false) => {
1451
2065
  if (!sessionId) return;
1452
2066
  const ownedTargetKey = `${workspaceId}\0${sessionId}`;
@@ -1477,7 +2091,7 @@ function useTurnQueue(sessionId, options = {}) {
1477
2091
  },
1478
2092
  [client, workspaceId, sessionId, acceptSnapshot]
1479
2093
  );
1480
- useEffect4(() => {
2094
+ useEffect5(() => {
1481
2095
  if (!enabled || !pageLive) {
1482
2096
  setLoading(false);
1483
2097
  return;
@@ -1506,10 +2120,10 @@ function useTurnQueue(sessionId, options = {}) {
1506
2120
  readGeneration.current += 1;
1507
2121
  };
1508
2122
  }, [load, enabled, pageLive, options.pollIntervalMs]);
1509
- useEffect4(() => {
2123
+ useEffect5(() => {
1510
2124
  if (enabled && workspaceControlEvent) void load();
1511
2125
  }, [enabled, load, workspaceControlEvent]);
1512
- useEffect4(() => {
2126
+ useEffect5(() => {
1513
2127
  if (!sessionId || !enabled) return;
1514
2128
  return registerSessionReconciler(sessionId, "queue", load);
1515
2129
  }, [enabled, load, registerSessionReconciler, sessionId]);
@@ -1526,7 +2140,7 @@ function useTurnQueue(sessionId, options = {}) {
1526
2140
  },
1527
2141
  async () => await load(true)
1528
2142
  );
1529
- const mutate = useCallback4(
2143
+ const mutate = useCallback7(
1530
2144
  async (turnId, kind, command) => {
1531
2145
  const ownedTargetKey = targetKey;
1532
2146
  if (!sessionId || targetKeyRef.current !== ownedTargetKey || pendingRef.current[turnId]) {
@@ -1568,7 +2182,7 @@ function useTurnQueue(sessionId, options = {}) {
1568
2182
  },
1569
2183
  [acceptSnapshot, load, sessionId, targetKey]
1570
2184
  );
1571
- const moveTurn = useCallback4(
2185
+ const moveTurn = useCallback7(
1572
2186
  async (turnId, beforeTurnId) => {
1573
2187
  const result = await mutate(
1574
2188
  turnId,
@@ -1583,7 +2197,7 @@ function useTurnQueue(sessionId, options = {}) {
1583
2197
  },
1584
2198
  [client, mutate, sessionId, workspaceId]
1585
2199
  );
1586
- const editTurn = useCallback4(
2200
+ const editTurn = useCallback7(
1587
2201
  async (turnId, edit) => {
1588
2202
  const result = await mutate(
1589
2203
  turnId,
@@ -1599,7 +2213,7 @@ function useTurnQueue(sessionId, options = {}) {
1599
2213
  },
1600
2214
  [client, mutate, sessionId, workspaceId]
1601
2215
  );
1602
- const steerTurn = useCallback4(
2216
+ const steerTurn = useCallback7(
1603
2217
  async (turnId) => {
1604
2218
  const ownedTargetKey = targetKey;
1605
2219
  const turn = snapshotRef.current?.items.find((candidate) => candidate.id === turnId);
@@ -1651,7 +2265,7 @@ function useTurnQueue(sessionId, options = {}) {
1651
2265
  },
1652
2266
  [client, load, mutate, sessionId, targetKey, workspaceId]
1653
2267
  );
1654
- const removeTurn = useCallback4(
2268
+ const removeTurn = useCallback7(
1655
2269
  async (turnId) => {
1656
2270
  const result = await mutate(
1657
2271
  turnId,
@@ -1673,7 +2287,7 @@ function useTurnQueue(sessionId, options = {}) {
1673
2287
  () => Object.keys(visiblePendingByTurn).length > 0,
1674
2288
  [visiblePendingByTurn]
1675
2289
  );
1676
- const mutationFor = useCallback4(
2290
+ const mutationFor = useCallback7(
1677
2291
  (turnId) => visiblePendingByTurn[turnId] ?? null,
1678
2292
  [visiblePendingByTurn]
1679
2293
  );
@@ -1696,7 +2310,7 @@ function useTurnQueue(sessionId, options = {}) {
1696
2310
  mutationFor,
1697
2311
  mutating,
1698
2312
  mutationError: identityMatches ? mutationError : null,
1699
- clearMutationError: useCallback4(() => {
2313
+ clearMutationError: useCallback7(() => {
1700
2314
  if (targetKeyRef.current === targetKey) setMutationError(null);
1701
2315
  }, [targetKey]),
1702
2316
  acceptedSteers: identityMatches ? acceptedSteers : []
@@ -1706,15 +2320,175 @@ function operationKey() {
1706
2320
  return globalThis.crypto.randomUUID();
1707
2321
  }
1708
2322
  function asError(cause) {
1709
- return cause instanceof Error ? cause : new Error(String(cause));
2323
+ return normalizeError(cause);
1710
2324
  }
1711
2325
  function isOutcomeUnknownError(cause) {
1712
2326
  return typeof cause === "object" && cause !== null && cause.outcomeUnknown === true;
1713
2327
  }
1714
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
+
1715
2489
  // src/hooks/use-goal.ts
1716
2490
  import { OpenGeniApiError } from "@opengeni/sdk";
1717
- 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";
1718
2492
  function isGoalEvent(event) {
1719
2493
  return event.type.startsWith("goal.");
1720
2494
  }
@@ -1728,20 +2502,38 @@ function useGoal(sessionId, options = {}) {
1728
2502
  const sharedEvents = options.events;
1729
2503
  const sharedFeed = sharedEvents !== void 0;
1730
2504
  const pageLive = usePageLiveActivity();
1731
- const [goal, setGoal] = useState5(null);
1732
- const [loading, setLoading] = useState5(enabled);
1733
- const [error, setError] = useState5(null);
2505
+ const [goal, setGoal] = useState7(null);
2506
+ const [loading, setLoading] = useState7(enabled);
2507
+ const [error, setError] = useState7(null);
1734
2508
  const { run, mutating, mutationError, clearMutationError } = useMutationRunner();
1735
- const generation = useRef5(0);
1736
- const targetKeyRef = useRef5(null);
1737
- const load = useCallback5(async () => {
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(() => {
2520
+ generation.current += 1;
2521
+ loadAbort.current?.abort();
2522
+ loadAbort.current = null;
2523
+ }, []);
2524
+ const load = useCallback8(async () => {
1738
2525
  if (!sessionId) {
1739
2526
  return;
1740
2527
  }
1741
2528
  const ticket = ++generation.current;
2529
+ pendingRefreshRef.current = "none";
2530
+ loadAbort.current?.abort();
2531
+ const controller = new AbortController();
2532
+ loadAbort.current = controller;
1742
2533
  try {
1743
- const fetched = await client.getGoal(workspaceId, sessionId);
2534
+ const fetched = typeof client.findGoal === "function" ? await client.findGoal(workspaceId, sessionId, { signal: controller.signal }) : await client.getGoal(workspaceId, sessionId, { signal: controller.signal });
1744
2535
  if (ticket === generation.current) {
2536
+ goalAbsentRef.current = fetched === null;
1745
2537
  setGoal(fetched);
1746
2538
  setError(null);
1747
2539
  setLoading(false);
@@ -1751,29 +2543,53 @@ function useGoal(sessionId, options = {}) {
1751
2543
  return;
1752
2544
  }
1753
2545
  if (cause instanceof OpenGeniApiError && cause.status === 404) {
2546
+ goalAbsentRef.current = true;
1754
2547
  setGoal(null);
1755
2548
  setError(null);
1756
2549
  } else {
1757
- setError(cause instanceof Error ? cause : new Error(String(cause)));
2550
+ setError(normalizeError(cause));
1758
2551
  }
1759
2552
  setLoading(false);
2553
+ } finally {
2554
+ if (loadAbort.current === controller) loadAbort.current = null;
1760
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();
1761
2573
  }, [client, workspaceId, sessionId]);
1762
- useEffect5(() => {
2574
+ useEffect7(() => {
1763
2575
  const targetKey = `${workspaceId} ${sessionId ?? ""}`;
1764
2576
  if (targetKeyRef.current !== targetKey) {
1765
2577
  targetKeyRef.current = targetKey;
2578
+ goalAbsentRef.current = false;
2579
+ pendingRefreshRef.current = "none";
1766
2580
  setGoal(null);
1767
2581
  setError(null);
1768
2582
  }
1769
2583
  if (!enabled || !pageLive) {
2584
+ retireLoads();
1770
2585
  setLoading(false);
1771
2586
  return;
1772
2587
  }
1773
2588
  if (sharedFeed) {
1774
- setLoading(false);
2589
+ setLoading(true);
2590
+ void load();
1775
2591
  return () => {
1776
- generation.current += 1;
2592
+ retireLoads();
1777
2593
  };
1778
2594
  }
1779
2595
  setLoading(true);
@@ -1781,7 +2597,7 @@ function useGoal(sessionId, options = {}) {
1781
2597
  if (pollIntervalMs === void 0 || pollIntervalMs <= 0) {
1782
2598
  void load();
1783
2599
  return () => {
1784
- generation.current += 1;
2600
+ retireLoads();
1785
2601
  };
1786
2602
  }
1787
2603
  let cancelled = false;
@@ -1797,541 +2613,394 @@ function useGoal(sessionId, options = {}) {
1797
2613
  return () => {
1798
2614
  cancelled = true;
1799
2615
  if (timer !== null) clearTimeout(timer);
1800
- generation.current += 1;
2616
+ retireLoads();
1801
2617
  };
1802
- }, [load, enabled, pageLive, workspaceId, sessionId, options.pollIntervalMs, sharedFeed]);
1803
- const scheduleRefresh = useDebouncedCallback(() => void load());
1804
- useSessionEventTrigger(client, workspaceId, sessionId, isGoalRefreshEvent, scheduleRefresh, {
2618
+ }, [
2619
+ load,
1805
2620
  enabled,
1806
- ...sharedEvents !== void 0 ? { events: sharedEvents } : {}
1807
- });
1808
- const pause = useCallback5(
1809
- async (rationale) => {
1810
- if (!sessionId) {
1811
- return null;
1812
- }
1813
- const result = await run(
1814
- () => client.updateGoal(workspaceId, sessionId, {
1815
- status: "paused",
1816
- ...rationale !== void 0 ? { rationale } : {}
1817
- })
1818
- );
1819
- if (result) {
1820
- setGoal(result);
1821
- }
1822
- return result;
1823
- },
1824
- [client, workspaceId, sessionId, run]
1825
- );
1826
- const resume = useCallback5(async () => {
1827
- if (!sessionId) {
1828
- return null;
1829
- }
1830
- const result = await run(() => client.updateGoal(workspaceId, sessionId, { status: "active" }));
1831
- if (result) {
1832
- setGoal(result);
1833
- }
1834
- return result;
1835
- }, [client, workspaceId, sessionId, run]);
1836
- const clearGoal = useCallback5(async () => {
1837
- if (!sessionId) {
2621
+ pageLive,
2622
+ workspaceId,
2623
+ sessionId,
2624
+ options.pollIntervalMs,
2625
+ retireLoads,
2626
+ sharedFeed
2627
+ ]);
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";
1838
2633
  return;
1839
2634
  }
1840
- const ok = await run(async () => {
1841
- await client.deleteGoal(workspaceId, sessionId);
1842
- return true;
1843
- });
1844
- if (ok) {
1845
- generation.current += 1;
1846
- setGoal(null);
1847
- setError(null);
1848
- }
1849
- }, [client, workspaceId, sessionId, run]);
1850
- return {
1851
- goal,
1852
- isActive: goal?.status === "active",
1853
- isPaused: goal?.status === "paused",
1854
- isCompleted: goal?.status === "completed",
1855
- loading,
1856
- error,
1857
- refresh: load,
1858
- pause,
1859
- resume,
1860
- clearGoal,
1861
- deleteGoal: clearGoal,
1862
- updating: mutating,
1863
- mutationError,
1864
- clearMutationError
1865
- };
1866
- }
1867
-
1868
- // src/hooks/use-session-control.ts
1869
- import { useCallback as useCallback6, useLayoutEffect as useLayoutEffect2, useRef as useRef6 } from "react";
1870
- function useSessionControl(sessionId, options = {}) {
1871
- const { client, workspaceId } = useEmbeddedSession(options);
1872
- const approvalTargetKey = `${workspaceId}\0${sessionId ?? ""}`;
1873
- const pendingApproval = useRef6(null);
1874
- useLayoutEffect2(() => {
1875
- if (pendingApproval.current?.targetKey !== approvalTargetKey) {
1876
- pendingApproval.current = null;
1877
- }
1878
- }, [approvalTargetKey]);
1879
- const {
1880
- run: runControl,
1881
- mutating: controlling,
1882
- mutationError: controlError,
1883
- clearMutationError: clearControlError
1884
- } = useMutationRunner(approvalTargetKey);
1885
- const {
1886
- run: runApproval,
1887
- mutating: responding,
1888
- mutationError: approvalError,
1889
- clearMutationError: clearApprovalError
1890
- } = useMutationRunner(approvalTargetKey);
1891
- const pause = useCallback6(
1892
- async (reason) => {
1893
- if (!sessionId) {
1894
- return null;
1895
- }
1896
- return await runControl(
1897
- () => client.pauseSession(workspaceId, sessionId, reason !== void 0 ? { reason } : {})
1898
- );
1899
- },
1900
- [client, workspaceId, sessionId, runControl]
1901
- );
1902
- const resume = useCallback6(
1903
- async (reason) => {
1904
- if (!sessionId) return null;
1905
- return await runControl(
1906
- () => client.resumeSession(workspaceId, sessionId, reason !== void 0 ? { reason } : {})
1907
- );
1908
- },
1909
- [client, workspaceId, sessionId, runControl]
1910
- );
1911
- const decide = useCallback6(
1912
- async (approvalId, decision, message) => {
1913
- if (!sessionId) {
1914
- return null;
1915
- }
1916
- const decisionKey = JSON.stringify([approvalId, decision, message ?? null]);
1917
- if (pendingApproval.current?.decisionKey !== decisionKey) {
1918
- pendingApproval.current = {
1919
- targetKey: approvalTargetKey,
1920
- decisionKey,
1921
- clientEventId: crypto.randomUUID()
1922
- };
1923
- }
1924
- const clientEventId = pendingApproval.current.clientEventId;
1925
- const accepted = await runApproval(
1926
- () => client.sendApprovalDecision(workspaceId, sessionId, {
1927
- approvalId,
1928
- decision,
1929
- ...message !== void 0 ? { message } : {},
1930
- clientEventId
1931
- })
1932
- );
1933
- if (accepted !== null && pendingApproval.current?.targetKey === approvalTargetKey && pendingApproval.current.clientEventId === clientEventId) {
1934
- pendingApproval.current = null;
1935
- }
1936
- return accepted;
1937
- },
1938
- [approvalTargetKey, client, workspaceId, sessionId, runApproval]
1939
- );
1940
- const approve = useCallback6(
1941
- async (approvalId, message) => await decide(approvalId, "approve", message),
1942
- [decide]
1943
- );
1944
- const reject = useCallback6(
1945
- async (approvalId, message) => await decide(approvalId, "reject", message),
1946
- [decide]
1947
- );
1948
- const error = approvalError ?? controlError;
1949
- const clearError = useCallback6(() => {
1950
- clearControlError();
1951
- clearApprovalError();
1952
- }, [clearControlError, clearApprovalError]);
1953
- return {
1954
- pause,
1955
- resume,
1956
- controlling,
1957
- approve,
1958
- reject,
1959
- responding,
1960
- error,
1961
- clearError
1962
- };
1963
- }
1964
-
1965
- // src/hooks/use-session-mcp-approval-policy.ts
1966
- import { useCallback as useCallback7, useEffect as useEffect6, useLayoutEffect as useLayoutEffect3, useMemo as useMemo4, useRef as useRef7, useState as useState6 } from "react";
1967
- function isSessionMcpApprovalPolicyEvent(event, serverId) {
1968
- if (event.type !== "session.mcp.approval_policy.updated") {
1969
- return false;
1970
- }
1971
- if (serverId === void 0) {
1972
- return true;
1973
- }
1974
- const payload = event.payload;
1975
- return payload?.serverId === serverId;
1976
- }
1977
- function useSessionMcpApprovalPolicy(sessionId, serverId, options = {}) {
1978
- const { client, workspaceId } = useEmbeddedSessionMcpApprovalPolicy(options);
1979
- const enabled = (options.enabled ?? true) && Boolean(sessionId && serverId);
1980
- const [override, setOverride] = useState6(null);
1981
- const targetKey = `${workspaceId}\0${sessionId ?? ""}\0${serverId ?? ""}`;
1982
- const authoritativeGeneration = useRef7(0);
1983
- const mutationIdentity = useMemo4(() => ({ client, targetKey }), [client, targetKey]);
1984
- const { run, mutating, mutationError, clearMutationError } = useMutationRunner(mutationIdentity);
1985
- useLayoutEffect3(() => {
1986
- authoritativeGeneration.current += 1;
1987
- setOverride(null);
1988
- }, [client, targetKey]);
1989
- const load = useCallback7(async () => {
1990
- const generation = authoritativeGeneration.current;
1991
- if (!sessionId || !serverId) {
1992
- return { targetKey, generation, server: null };
1993
- }
1994
- const session = await client.getSession(workspaceId, sessionId);
1995
- return {
1996
- targetKey,
1997
- generation,
1998
- server: session.mcpServers.find((server2) => server2.id === serverId) ?? null
1999
- };
2000
- }, [client, serverId, sessionId, targetKey, workspaceId]);
2001
- const {
2002
- data,
2003
- loading,
2004
- error: loadError,
2005
- refresh
2006
- } = usePolledValue(load, {
2007
- enabled,
2008
- pollIntervalMs: options.pollIntervalMs
2635
+ void load();
2009
2636
  });
2010
- useEffect6(() => {
2011
- if (data?.targetKey === targetKey && data.generation === authoritativeGeneration.current) {
2012
- setOverride(null);
2013
- }
2014
- }, [data, targetKey]);
2015
- const scheduleRefresh = useDebouncedCallback(() => void refresh());
2016
- const matchesPolicyEvent = useCallback7(
2017
- (event) => isSessionMcpApprovalPolicyEvent(event, serverId ?? void 0),
2018
- [serverId]
2019
- );
2020
- useSessionEventTrigger(
2021
- client,
2022
- workspaceId,
2023
- sessionId,
2024
- matchesPolicyEvent,
2025
- scheduleRefresh,
2026
- {
2027
- enabled,
2028
- ...options.events !== void 0 ? { events: options.events } : {}
2637
+ useLayoutEffect4(() => {
2638
+ scheduleRefreshRef.current = scheduleRefresh;
2639
+ }, [scheduleRefresh]);
2640
+ const isRefreshEvent = useCallback8(
2641
+ (event) => goalAbsentRef.current ? isGoalEvent(event) : isGoalRefreshEvent(event),
2642
+ []
2643
+ );
2644
+ const onRefreshEvent = useCallback8(
2645
+ (event) => {
2646
+ if (isGoalEvent(event)) pendingRefreshRef.current = "goal";
2647
+ else if (pendingRefreshRef.current === "none") pendingRefreshRef.current = "refresh";
2648
+ scheduleRefresh();
2029
2649
  },
2030
- async () => await refresh()
2650
+ [scheduleRefresh]
2031
2651
  );
2032
- const update = useCallback7(
2033
- async (policy) => {
2034
- 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) {
2035
2659
  return null;
2036
2660
  }
2037
- const response = await run(
2038
- () => client.updateSessionMcpApprovalPolicy(workspaceId, sessionId, serverId, {
2039
- requireApproval: policy
2661
+ const result = await run(
2662
+ () => client.updateGoal(workspaceId, sessionId, {
2663
+ status: "paused",
2664
+ ...rationale !== void 0 ? { rationale } : {}
2040
2665
  })
2041
2666
  );
2042
- if (response) {
2043
- authoritativeGeneration.current += 1;
2044
- setOverride(response.server);
2045
- void refresh();
2667
+ if (result) {
2668
+ setGoal(result);
2046
2669
  }
2047
- return response;
2670
+ return result;
2048
2671
  },
2049
- [client, refresh, run, serverId, sessionId, workspaceId]
2672
+ [client, workspaceId, sessionId, run]
2050
2673
  );
2051
- 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]);
2052
2698
  return {
2053
- server,
2054
- policy: server?.requireApproval ?? null,
2699
+ goal,
2700
+ isActive: goal?.status === "active",
2701
+ isPaused: goal?.status === "paused",
2702
+ isCompleted: goal?.status === "completed",
2055
2703
  loading,
2056
- error: mutationError ?? loadError,
2057
- refresh: async () => {
2058
- await refresh();
2059
- },
2060
- update,
2704
+ error,
2705
+ refresh: load,
2706
+ pause,
2707
+ resume,
2708
+ clearGoal,
2709
+ deleteGoal: clearGoal,
2061
2710
  updating: mutating,
2062
- clearError: clearMutationError
2711
+ mutationError,
2712
+ clearMutationError
2063
2713
  };
2064
2714
  }
2065
2715
 
2066
- // src/hooks/use-session-lineage.ts
2067
- import { useCallback as useCallback8, useEffect as useEffect7, useRef as useRef8 } from "react";
2068
- function isLineageRefreshEvent(event) {
2069
- if (event.type === "session.status.changed" || event.type === "session.created") {
2070
- return true;
2071
- }
2072
- if (event.type === "agent.toolCall.output") {
2073
- const payload = event.payload;
2074
- const name = typeof payload?.name === "string" ? payload.name : typeof payload?.toolName === "string" ? payload.toolName : "";
2075
- return name === "session_create" || name.endsWith("__session_create");
2076
- }
2077
- return false;
2078
- }
2079
- function isSessionCreateToolCallCreated(event) {
2080
- if (event.type !== "agent.toolCall.created") {
2081
- return false;
2082
- }
2083
- const payload = event.payload;
2084
- const name = typeof payload?.name === "string" ? payload.name : typeof payload?.toolName === "string" ? payload.toolName : "";
2085
- return name === "session_create" || name.endsWith("__session_create");
2086
- }
2087
- function useSessionLineage(sessionId, options = {}) {
2088
- const { client, workspaceId, workspaceControlEvent, registerSessionReconciler } = useEmbeddedSessionLineage(options);
2089
- const enabled = (options.enabled ?? true) && Boolean(sessionId);
2090
- const nextReadGeneration = useRef8(0);
2091
- const beginRead = options.beginRead;
2092
- const load = useCallback8(async () => {
2093
- if (!sessionId) {
2094
- return {
2095
- lineage: { ancestors: [], children: [], truncated: false },
2096
- readGeneration: 0
2097
- };
2098
- }
2099
- let readGeneration = 0;
2100
- const lineage = await client.getSessionLineage(workspaceId, sessionId, {
2101
- onRequestStart: (sharedReadGeneration) => {
2102
- readGeneration = sharedReadGeneration ?? beginRead?.() ?? ++nextReadGeneration.current;
2103
- }
2104
- });
2105
- return {
2106
- lineage,
2107
- readGeneration
2108
- };
2109
- }, [beginRead, client, workspaceId, sessionId]);
2110
- const state = usePolledValue(load, { pollIntervalMs: options.pollIntervalMs, enabled });
2111
- const refresh = state.refresh;
2112
- useEffect7(() => {
2113
- if (enabled && workspaceControlEvent) void refresh();
2114
- }, [enabled, refresh, workspaceControlEvent]);
2115
- useEffect7(() => {
2116
- if (!sessionId || !enabled) return;
2117
- return registerSessionReconciler(sessionId, "lineage", refresh);
2118
- }, [enabled, refresh, registerSessionReconciler, sessionId]);
2119
- const refreshSoon = useDebouncedCallback(() => void refresh(), 150);
2120
- const delayedChildRefreshRef = useRef8(null);
2121
- useEffect7(() => {
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]
2754
+ );
2755
+ const committedIdentity = useRef9(identity);
2756
+ useLayoutEffect5(() => {
2757
+ lifetime.mounted = true;
2122
2758
  return () => {
2123
- if (delayedChildRefreshRef.current !== null) {
2124
- clearTimeout(delayedChildRefreshRef.current);
2125
- delayedChildRefreshRef.current = null;
2126
- }
2759
+ lifetime.mounted = false;
2760
+ lifetime.generation++;
2127
2761
  };
2128
- }, [sessionId, workspaceId]);
2129
- const refreshAfterChildCreate = useCallback8(() => {
2130
- void refresh();
2131
- if (delayedChildRefreshRef.current !== null) {
2132
- 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";
2133
2797
  }
2134
- delayedChildRefreshRef.current = setTimeout(() => {
2135
- delayedChildRefreshRef.current = null;
2136
- void refresh();
2137
- }, 2500);
2138
- }, [refresh]);
2139
- useSessionEventTrigger(
2140
- client,
2141
- workspaceId,
2142
- sessionId,
2143
- isLineageRefreshEvent,
2144
- refreshSoon,
2145
- // SHARED-FEED ONLY: without a caller-provided events log the trigger would
2146
- // open its OWN streamEvents tail — a second live SSE connection next to the
2147
- // session route's useSessionEvents. A caller with no feed opts into polling
2148
- // (pollIntervalMs), never a duplicate stream.
2149
- { events: options.events, enabled: enabled && options.events !== void 0 }
2150
- );
2151
- useSessionEventTrigger(
2152
- client,
2153
- workspaceId,
2154
- sessionId,
2155
- isSessionCreateToolCallCreated,
2156
- refreshAfterChildCreate,
2157
- { events: options.events, enabled: enabled && options.events !== void 0 }
2158
- );
2159
- return {
2160
- lineage: state.data?.lineage ?? null,
2161
- loading: state.loading,
2162
- error: state.error,
2163
- readGeneration: state.data?.readGeneration ?? 0,
2164
- refresh
2798
+ choiceRef.current = next;
2799
+ setChoice(choiceRef.current);
2165
2800
  };
2166
- }
2167
-
2168
- // src/approvals.ts
2169
- function approvalsFromRequiresAction(payload) {
2170
- const approvals = payload && typeof payload === "object" ? payload.approvals : void 0;
2171
- if (!Array.isArray(approvals)) {
2172
- return [];
2173
- }
2174
- return approvals.map((approval, index) => {
2175
- const raw = approval && typeof approval === "object" ? approval : {};
2176
- const rawItem = raw.rawItem && typeof raw.rawItem === "object" ? raw.rawItem : {};
2177
- return {
2178
- // Mirror the runtime's canonical interruption identity before retaining
2179
- // the legacy top-level callId fallback for already-normalized payloads.
2180
- id: String(rawItem.callId ?? rawItem.id ?? raw.id ?? raw.callId ?? raw.name ?? index),
2181
- name: String(raw.name ?? raw.toolName ?? rawItem.name ?? "approval"),
2182
- arguments: raw.arguments ?? rawItem.arguments,
2183
- raw: approval
2184
- };
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;
2185
2821
  });
2186
- }
2187
- function projectPendingApprovals(events) {
2188
- let pending = [];
2189
- let owningTurnId = null;
2190
- for (const event of events) {
2191
- switch (event.type) {
2192
- case "session.requiresAction": {
2193
- pending = approvalsFromRequiresAction(event.payload);
2194
- owningTurnId = event.turnId ?? null;
2195
- break;
2196
- }
2197
- case "user.approvalDecision": {
2198
- const payload = event.payload && typeof event.payload === "object" ? event.payload : {};
2199
- if (typeof payload.approvalId === "string") {
2200
- pending = pending.filter((approval) => approval.id !== payload.approvalId);
2201
- }
2202
- 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);
2203
2883
  }
2204
- case "turn.completed":
2205
- case "turn.failed":
2206
- case "turn.cancelled": {
2207
- if (owningTurnId === null || event.turnId == null || event.turnId === owningTurnId) {
2208
- pending = [];
2209
- owningTurnId = null;
2210
- }
2211
- break;
2884
+ return false;
2885
+ } finally {
2886
+ if (lifetime.mounted && lifetime.generation === ownedGeneration) {
2887
+ inFlight.current = false;
2888
+ setSending(false);
2212
2889
  }
2213
- default:
2214
- break;
2215
2890
  }
2216
- }
2217
- return pending;
2218
- }
2219
-
2220
- // src/hooks/use-human-input.ts
2221
- import { useCallback as useCallback9, useEffect as useEffect8, useRef as useRef9, useState as useState7 } from "react";
2222
- function isHumanInputEvent(event) {
2223
- return event.type === "session.humanInput.requested" || event.type === "user.humanInputResponse" || event.type === "turn.completed" || event.type === "turn.failed" || event.type === "turn.cancelled";
2224
- }
2225
- function useHumanInputRequests(sessionId, options = {}) {
2226
- const { client, workspaceId, registerSessionReconciler } = useEmbeddedHumanInputSession(options);
2227
- const enabled = (options.enabled ?? true) && Boolean(sessionId);
2228
- const load = useCallback9(
2229
- async () => sessionId ? await client.listHumanInputRequests(workspaceId, sessionId, { status: "pending" }) : [],
2230
- [client, sessionId, workspaceId]
2231
- );
2232
- const state = usePolledValue(load, {
2233
- enabled,
2234
- pollIntervalMs: options.pollIntervalMs
2235
- });
2236
- const loadedRequests = state.data;
2237
- const refreshRequests = state.refresh;
2238
- const mutation = useMutationRunner(`${workspaceId}\0${sessionId ?? ""}`);
2239
- const [respondingRequestId, setRespondingRequestId] = useState7(null);
2240
- const [deadlineVersion, setDeadlineVersion] = useState7(0);
2241
- const respondingRequestRef = useRef9(null);
2242
- useEffect8(() => {
2243
- respondingRequestRef.current = null;
2244
- setRespondingRequestId(null);
2245
- }, [sessionId, workspaceId]);
2246
- useEffect8(() => {
2247
- if (!sessionId || !enabled) return;
2248
- return registerSessionReconciler(sessionId, "human-input", state.refresh);
2249
- }, [enabled, registerSessionReconciler, sessionId, state.refresh]);
2250
- const scheduleRefresh = useDebouncedCallback(() => void state.refresh());
2251
- useSessionEventTrigger(client, workspaceId, sessionId, isHumanInputEvent, scheduleRefresh, {
2252
- enabled,
2253
- ...options.events !== void 0 ? { events: options.events } : {}
2254
- });
2255
- useEffect8(() => {
2256
- const now2 = Date.now();
2257
- const nextDeadline = (loadedRequests ?? []).reduce((next, request) => {
2258
- if (request.status !== "pending" || !request.expiresAt) return next;
2259
- const deadline = Date.parse(request.expiresAt);
2260
- if (!Number.isFinite(deadline) || deadline <= now2) return next;
2261
- return next === null ? deadline : Math.min(next, deadline);
2262
- }, null);
2263
- if (nextDeadline === null) return;
2264
- const timer = globalThis.setTimeout(
2265
- () => {
2266
- setDeadlineVersion((current) => current + 1);
2267
- void refreshRequests();
2268
- },
2269
- Math.min(2147483647, Math.max(0, nextDeadline - now2 + 1))
2270
- );
2271
- return () => globalThis.clearTimeout(timer);
2272
- }, [loadedRequests, refreshRequests]);
2273
- const respond = useCallback9(
2274
- async (requestId, response) => {
2275
- if (!sessionId || respondingRequestRef.current !== null) return null;
2276
- respondingRequestRef.current = requestId;
2277
- setRespondingRequestId(requestId);
2278
- try {
2279
- const event = await mutation.run(
2280
- () => client.submitHumanInputResponse(workspaceId, sessionId, requestId, response, {
2281
- clientEventId: globalThis.crypto.randomUUID()
2282
- })
2283
- );
2284
- if (event) await state.refresh();
2285
- return event;
2286
- } finally {
2287
- if (respondingRequestRef.current === requestId) {
2288
- respondingRequestRef.current = null;
2289
- setRespondingRequestId(null);
2290
- }
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 } : {}
2291
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: () => {
2292
2949
  },
2293
- [client, mutation, sessionId, state, workspaceId]
2294
- );
2295
- const now = Date.now();
2296
- 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
+ };
2297
2960
  return {
2298
- requests: [...state.data ?? []].filter((request) => isActionableHumanInputRequest(request, now)).sort(
2299
- (left, right) => left.createdAt.localeCompare(right.createdAt) || left.id.localeCompare(right.id)
2300
- ),
2301
- loading: state.loading,
2302
- error: state.error,
2303
- refresh: state.refresh,
2304
- respond,
2305
- respondingRequestId,
2306
- mutationError: mutation.mutationError,
2307
- 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
2308
2975
  };
2309
2976
  }
2310
2977
 
2311
2978
  export {
2312
- createEmbeddedSessionClient,
2979
+ approvalsFromRequiresAction,
2980
+ projectPendingApprovals,
2981
+ invokeOlderHistoryLoaderWithReceiptCapture,
2982
+ createOlderHistoryLoadReceipt,
2983
+ conversationTimeline,
2984
+ useAvailableModels,
2985
+ useWorkspaceModelCatalog,
2986
+ useClientConfigFlags,
2987
+ useFileAttachments,
2988
+ isHumanInputEvent,
2989
+ useHumanInputRequests,
2313
2990
  isTitleEvent,
2314
2991
  useSession,
2992
+ useSessionControl,
2315
2993
  SESSION_EVENT_BROWSER_MAX_BYTES,
2316
2994
  SESSION_EVENT_BROWSER_MAX_COUNT,
2317
- SESSION_EVENT_BROWSER_SINGLE_EVENT_MAX_BYTES,
2318
2995
  SESSION_EVENT_BROWSER_PENDING_MAX_BYTES,
2319
2996
  SESSION_EVENT_BROWSER_PENDING_MAX_COUNT,
2320
2997
  useSessionEvents,
2321
2998
  boundBrowserSessionEventWindow,
2322
- useFileAttachments,
2323
2999
  isTurnQueueEvent,
2324
3000
  useTurnQueue,
3001
+ useSessionConversation,
2325
3002
  isGoalEvent,
2326
3003
  useGoal,
2327
- useSessionControl,
2328
- isSessionMcpApprovalPolicyEvent,
2329
- useSessionMcpApprovalPolicy,
2330
- isLineageRefreshEvent,
2331
- useSessionLineage,
2332
- approvalsFromRequiresAction,
2333
- projectPendingApprovals,
2334
- isHumanInputEvent,
2335
- useHumanInputRequests
3004
+ useNewConversation
2336
3005
  };
2337
- //# sourceMappingURL=chunk-YJFGAFBD.js.map
3006
+ //# sourceMappingURL=chunk-X5UVUIMF.js.map