@opengeni/react 0.46.1 → 0.52.1

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 (341) hide show
  1. package/README.md +158 -14
  2. package/dist/artifacts-document.d.ts +3 -0
  3. package/dist/artifacts-document.js +17 -0
  4. package/dist/artifacts-document.js.map +1 -0
  5. package/dist/artifacts-presentation.d.ts +3 -0
  6. package/dist/artifacts-presentation.js +17 -0
  7. package/dist/artifacts-presentation.js.map +1 -0
  8. package/dist/artifacts-spreadsheet.d.ts +3 -0
  9. package/dist/artifacts-spreadsheet.js +17 -0
  10. package/dist/artifacts-spreadsheet.js.map +1 -0
  11. package/dist/artifacts.d.ts +2 -0
  12. package/dist/artifacts.js +170 -0
  13. package/dist/artifacts.js.map +1 -0
  14. package/dist/chunk-6BBWWOOU.js +82 -0
  15. package/dist/chunk-6BBWWOOU.js.map +1 -0
  16. package/dist/{chunk-HFO4ERGQ.js → chunk-724BW4TW.js} +586 -35
  17. package/dist/chunk-724BW4TW.js.map +1 -0
  18. package/dist/chunk-7I2KB3OK.js +3895 -0
  19. package/dist/chunk-7I2KB3OK.js.map +1 -0
  20. package/dist/chunk-7WCLAEFV.js +2828 -0
  21. package/dist/chunk-7WCLAEFV.js.map +1 -0
  22. package/dist/{chunk-U3CSCMWA.js → chunk-FK6VG4LL.js} +9 -13
  23. package/dist/chunk-FK6VG4LL.js.map +1 -0
  24. package/dist/{chunk-SRFUT2ZU.js → chunk-FWS3KC3Z.js} +120 -64
  25. package/dist/chunk-FWS3KC3Z.js.map +1 -0
  26. package/dist/{chunk-EKZH6IDG.js → chunk-HDBG7XGO.js} +62 -1
  27. package/dist/chunk-HDBG7XGO.js.map +1 -0
  28. package/dist/chunk-HUIKIDM5.js +2148 -0
  29. package/dist/chunk-HUIKIDM5.js.map +1 -0
  30. package/dist/{chunk-LGVMUIRV.js → chunk-JAGDUCZQ.js} +1053 -390
  31. package/dist/chunk-JAGDUCZQ.js.map +1 -0
  32. package/dist/{chunk-LWR4MXSS.js → chunk-JR3QFY6B.js} +420 -35
  33. package/dist/chunk-JR3QFY6B.js.map +1 -0
  34. package/dist/chunk-JVYQRRC4.js +53 -0
  35. package/dist/chunk-JVYQRRC4.js.map +1 -0
  36. package/dist/chunk-KCGXPP5V.js +157 -0
  37. package/dist/chunk-KCGXPP5V.js.map +1 -0
  38. package/dist/{chunk-EB67J347.js → chunk-KZ73RL76.js} +111 -31
  39. package/dist/chunk-KZ73RL76.js.map +1 -0
  40. package/dist/chunk-PEI6RANI.js +3234 -0
  41. package/dist/chunk-PEI6RANI.js.map +1 -0
  42. package/dist/{chunk-3PB7MIT6.js → chunk-ROIRTM37.js} +220 -72
  43. package/dist/chunk-ROIRTM37.js.map +1 -0
  44. package/dist/chunk-VZTQ73XT.js +86 -0
  45. package/dist/chunk-VZTQ73XT.js.map +1 -0
  46. package/dist/{chunk-VCBFUSUZ.js → chunk-WSK7G5LE.js} +72 -4
  47. package/dist/chunk-WSK7G5LE.js.map +1 -0
  48. package/dist/{chunk-FGZUCXZF.js → chunk-WYA65Y3F.js} +16 -16
  49. package/dist/chunk-WYA65Y3F.js.map +1 -0
  50. package/dist/{chunk-6EDV6YFE.js → chunk-X4S56Y2H.js} +147 -35
  51. package/dist/chunk-X4S56Y2H.js.map +1 -0
  52. package/dist/client.d.ts +7 -1
  53. package/dist/clipboard.d.ts +1 -0
  54. package/dist/clipboard.js +7 -0
  55. package/dist/clipboard.js.map +1 -0
  56. package/dist/commands/index.d.ts +3 -3
  57. package/dist/commands/registry.d.ts +1 -1
  58. package/dist/commands/types.d.ts +1 -1
  59. package/dist/components/approval-surface.d.ts +2 -2
  60. package/dist/components/artifacts/artifact-surface.d.ts +18 -0
  61. package/dist/components/artifacts/document-editor.d.ts +172 -0
  62. package/dist/components/artifacts/editable-artifact-ui.d.ts +13 -0
  63. package/dist/components/artifacts/editable-artifact-workbench.d.ts +49 -0
  64. package/dist/components/artifacts/editable-document.d.ts +24 -0
  65. package/dist/components/artifacts/editable-presentation.d.ts +24 -0
  66. package/dist/components/artifacts/editable-spreadsheet.d.ts +32 -0
  67. package/dist/components/artifacts/index.d.ts +8 -0
  68. package/dist/components/artifacts/presentation-editor.d.ts +297 -0
  69. package/dist/components/artifacts/spreadsheet-canvas.d.ts +120 -0
  70. package/dist/components/artifacts/spreadsheet-grid.d.ts +113 -0
  71. package/dist/components/browser-viewer.d.ts +29 -0
  72. package/dist/components/chat-composer.d.ts +13 -8
  73. package/dist/components/code-editor.d.ts +25 -3
  74. package/dist/components/command-palette.d.ts +2 -2
  75. package/dist/components/composer-transcription-control.d.ts +4 -2
  76. package/dist/components/composer.d.ts +29 -15
  77. package/dist/components/computer-viewer.d.ts +23 -0
  78. package/dist/components/copy-button.d.ts +2 -2
  79. package/dist/components/desktop-viewer.d.ts +25 -21
  80. package/dist/components/diff-view.d.ts +1 -1
  81. package/dist/components/enrollment-consent.d.ts +1 -1
  82. package/dist/components/enrollment-device-flow.d.ts +1 -1
  83. package/dist/components/file-browser.d.ts +2 -2
  84. package/dist/components/fleet-tile.d.ts +1 -1
  85. package/dist/components/generated-video-player.d.ts +10 -0
  86. package/dist/components/human-input-form.d.ts +1 -1
  87. package/dist/components/human-input-surface.d.ts +2 -2
  88. package/dist/components/interactive-workbench-viewers.d.ts +2 -0
  89. package/dist/components/machine-card.d.ts +2 -2
  90. package/dist/components/machine-dock-bar.d.ts +3 -3
  91. package/dist/components/machine-health-pill.d.ts +2 -2
  92. package/dist/components/machine-input-display.d.ts +1 -1
  93. package/dist/components/machine-metrics.d.ts +2 -2
  94. package/dist/components/machine-status-pill.d.ts +4 -4
  95. package/dist/components/machines/health.d.ts +1 -1
  96. package/dist/components/machines/machine-detail.d.ts +5 -3
  97. package/dist/components/machines/metric-history-chart.d.ts +1 -1
  98. package/dist/components/machines/metric-sparkline.d.ts +2 -2
  99. package/dist/components/machines/series.d.ts +2 -2
  100. package/dist/components/machines-dashboard.d.ts +5 -5
  101. package/dist/components/markdown.d.ts +1 -1
  102. package/dist/components/message-timeline.d.ts +22 -5
  103. package/dist/components/model-picker.d.ts +2 -2
  104. package/dist/components/model-policy-picker.d.ts +7 -7
  105. package/dist/components/pierre-diff.d.ts +1 -1
  106. package/dist/components/pierre-file.d.ts +1 -1
  107. package/dist/components/queue-draft-policy.d.ts +1 -1
  108. package/dist/components/queue-surface-implementation.d.ts +3 -3
  109. package/dist/components/queue-surface-state.d.ts +4 -4
  110. package/dist/components/queue-surface.d.ts +5 -5
  111. package/dist/components/sandbox-files.d.ts +5 -5
  112. package/dist/components/sandbox-terminal.d.ts +50 -4
  113. package/dist/components/sandbox-workspace.d.ts +30 -10
  114. package/dist/components/session-chrome.d.ts +4 -4
  115. package/dist/components/session-status.d.ts +2 -2
  116. package/dist/components/timeline-annotation-selection.d.ts +9 -0
  117. package/dist/components/timeline-annotations-dialog.d.ts +16 -0
  118. package/dist/components/timeline-annotations.d.ts +13 -0
  119. package/dist/components/tooltip.d.ts +4 -4
  120. package/dist/components/user-message-body.d.ts +2 -2
  121. package/dist/components/workbench-changes.d.ts +2 -2
  122. package/dist/components/workspace-dock.d.ts +3 -1
  123. package/dist/composer.d.ts +11 -11
  124. package/dist/composer.js +7 -7
  125. package/dist/hooks/internal.d.ts +7 -1
  126. package/dist/hooks/use-attached-browsers.d.ts +21 -0
  127. package/dist/hooks/use-available-models.d.ts +2 -2
  128. package/dist/hooks/use-billing-usage.d.ts +1 -1
  129. package/dist/hooks/use-browser-frame-stream.d.ts +28 -0
  130. package/dist/hooks/use-browser-identities.d.ts +26 -0
  131. package/dist/hooks/use-browser-session.d.ts +28 -0
  132. package/dist/hooks/use-browser-sessions.d.ts +32 -0
  133. package/dist/hooks/use-codex-accounts.d.ts +3 -3
  134. package/dist/hooks/use-composer.d.ts +36 -9
  135. package/dist/hooks/use-computer-frame-stream.d.ts +24 -0
  136. package/dist/hooks/use-computer-session.d.ts +28 -0
  137. package/dist/hooks/use-computer-sessions.d.ts +28 -0
  138. package/dist/hooks/use-desktop-stream.d.ts +1 -1
  139. package/dist/hooks/use-environments.d.ts +11 -5
  140. package/dist/hooks/use-file-attachments.d.ts +1 -1
  141. package/dist/hooks/use-goal.d.ts +2 -2
  142. package/dist/hooks/use-human-input.d.ts +2 -2
  143. package/dist/hooks/use-last-started-turn-policy.d.ts +2 -2
  144. package/dist/hooks/use-machine-chip.d.ts +2 -2
  145. package/dist/hooks/use-machines.d.ts +10 -3
  146. package/dist/hooks/use-packs.d.ts +7 -2
  147. package/dist/hooks/use-rigs.d.ts +1 -1
  148. package/dist/hooks/use-sandbox-files.d.ts +25 -4
  149. package/dist/hooks/use-sandbox-git.d.ts +13 -5
  150. package/dist/hooks/use-sandbox-terminal.d.ts +1 -1
  151. package/dist/hooks/use-scheduled-tasks.d.ts +1 -1
  152. package/dist/hooks/use-session-capabilities.d.ts +5 -4
  153. package/dist/hooks/use-session-control.d.ts +1 -1
  154. package/dist/hooks/use-session-events.d.ts +2 -2
  155. package/dist/hooks/use-session-lineage.d.ts +1 -1
  156. package/dist/hooks/use-session-mcp-approval-policy.d.ts +2 -2
  157. package/dist/hooks/use-session.d.ts +2 -2
  158. package/dist/hooks/use-slash-commands.d.ts +1 -1
  159. package/dist/hooks/use-terminal-stream.d.ts +8 -2
  160. package/dist/hooks/use-transcription.d.ts +2 -2
  161. package/dist/hooks/use-turn-queue.d.ts +2 -2
  162. package/dist/hooks/use-video-artifact-playback.d.ts +9 -0
  163. package/dist/hooks/use-voice-input.d.ts +6 -4
  164. package/dist/hooks/use-workspace-capture.d.ts +1 -1
  165. package/dist/hooks/use-workspace-edit.d.ts +1 -1
  166. package/dist/hooks/use-workspace-sessions.d.ts +1 -1
  167. package/dist/hooks/use-workspaces.d.ts +1 -1
  168. package/dist/index.d.ts +158 -154
  169. package/dist/index.js +1718 -908
  170. package/dist/index.js.map +1 -1
  171. package/dist/interaction.d.ts +23 -0
  172. package/dist/interaction.js +36 -0
  173. package/dist/interaction.js.map +1 -0
  174. package/dist/interactive-workbench-viewers-KHNOHWQE.js +13 -0
  175. package/dist/interactive-workbench-viewers-KHNOHWQE.js.map +1 -0
  176. package/dist/lib/composer-responsive-context.d.ts +9 -0
  177. package/dist/lib/relay-wire.d.ts +4 -0
  178. package/dist/lib/sandbox-liveness.d.ts +9 -0
  179. package/dist/lib/terminal-capability.d.ts +5 -0
  180. package/dist/lib/use-portal-token-style.d.ts +14 -3
  181. package/dist/lib/xterm-theme.d.ts +1 -1
  182. package/dist/machines.d.ts +30 -30
  183. package/dist/machines.js +3 -3
  184. package/dist/provider.d.ts +4 -4
  185. package/dist/{queue-surface-implementation-J4XJFKRW.js → queue-surface-implementation-2KVJTYXH.js} +117 -64
  186. package/dist/queue-surface-implementation-2KVJTYXH.js.map +1 -0
  187. package/dist/realtime/dropdown-menu.d.ts +8 -8
  188. package/dist/realtime/realtime-control.d.ts +15 -6
  189. package/dist/realtime.d.ts +4 -4
  190. package/dist/realtime.js +30 -33
  191. package/dist/realtime.js.map +1 -1
  192. package/dist/session-context.d.ts +19 -1
  193. package/dist/session-ui.d.ts +14 -14
  194. package/dist/session-ui.js +6 -5
  195. package/dist/session.d.ts +28 -28
  196. package/dist/session.js +5 -5
  197. package/dist/timeline/activity-rail.d.ts +6 -3
  198. package/dist/timeline/disclosure-context.d.ts +1 -1
  199. package/dist/timeline/fleet-decision-projection.d.ts +1 -1
  200. package/dist/timeline/fleet-decision-row.d.ts +3 -3
  201. package/dist/timeline/index.d.ts +19 -19
  202. package/dist/timeline/parsers.d.ts +6 -3
  203. package/dist/timeline/projection.d.ts +3 -3
  204. package/dist/timeline/registry.d.ts +10 -1
  205. package/dist/timeline/screenshot-lightbox.d.ts +2 -2
  206. package/dist/timeline/shared.d.ts +21 -10
  207. package/dist/timeline/tool-diff.d.ts +2 -2
  208. package/dist/timeline/tool-renderers.d.ts +1 -1
  209. package/dist/timeline/turn-summary.d.ts +3 -3
  210. package/dist/timeline/types.d.ts +42 -2
  211. package/dist/timeline-annotation-selection-3L7LHGG2.js +187 -0
  212. package/dist/timeline-annotation-selection-3L7LHGG2.js.map +1 -0
  213. package/dist/timeline-annotations-dialog-U7EVLR75.js +196 -0
  214. package/dist/timeline-annotations-dialog-U7EVLR75.js.map +1 -0
  215. package/dist/voice-recording-store.d.ts +8 -2
  216. package/package.json +52 -7
  217. package/src/artifacts-document.ts +31 -0
  218. package/src/artifacts-presentation.ts +36 -0
  219. package/src/artifacts-spreadsheet.ts +21 -0
  220. package/src/artifacts.ts +86 -0
  221. package/src/client.ts +90 -1
  222. package/src/clipboard.ts +2 -0
  223. package/src/components/artifacts/artifact-surface.tsx +93 -0
  224. package/src/components/artifacts/document-editor.tsx +2255 -0
  225. package/src/components/artifacts/editable-artifact-ui.tsx +89 -0
  226. package/src/components/artifacts/editable-artifact-workbench.tsx +203 -0
  227. package/src/components/artifacts/editable-document.tsx +657 -0
  228. package/src/components/artifacts/editable-presentation.tsx +1020 -0
  229. package/src/components/artifacts/editable-spreadsheet.tsx +733 -0
  230. package/src/components/artifacts/index.ts +99 -0
  231. package/src/components/artifacts/presentation-editor.tsx +3148 -0
  232. package/src/components/artifacts/spreadsheet-canvas.ts +799 -0
  233. package/src/components/artifacts/spreadsheet-grid.tsx +2129 -0
  234. package/src/components/browser-viewer.tsx +1592 -0
  235. package/src/components/chat-composer.tsx +25 -2
  236. package/src/components/code-editor.tsx +90 -31
  237. package/src/components/command-palette.tsx +1 -1
  238. package/src/components/composer-transcription-control.tsx +51 -22
  239. package/src/components/composer.tsx +49 -23
  240. package/src/components/computer-viewer.tsx +1139 -0
  241. package/src/components/copy-button.tsx +1 -1
  242. package/src/components/desktop-viewer.tsx +103 -75
  243. package/src/components/file-browser.tsx +39 -17
  244. package/src/components/generated-video-player.tsx +89 -0
  245. package/src/components/human-input-form.tsx +2 -2
  246. package/src/components/interactive-workbench-viewers.ts +2 -0
  247. package/src/components/machine-card.tsx +1 -1
  248. package/src/components/machine-input-display.ts +3 -0
  249. package/src/components/machines/machine-detail.tsx +39 -15
  250. package/src/components/machines-dashboard.tsx +7 -6
  251. package/src/components/message-timeline.tsx +327 -37
  252. package/src/components/model-picker.tsx +2 -2
  253. package/src/components/model-policy-picker.tsx +16 -15
  254. package/src/components/queue-surface-implementation.tsx +135 -75
  255. package/src/components/queue-surface-state.tsx +2 -2
  256. package/src/components/sandbox-files.tsx +142 -62
  257. package/src/components/sandbox-terminal.tsx +119 -35
  258. package/src/components/sandbox-workspace.tsx +394 -228
  259. package/src/components/session-chrome.tsx +67 -27
  260. package/src/components/timeline-annotation-selection.tsx +221 -0
  261. package/src/components/timeline-annotations-dialog.tsx +213 -0
  262. package/src/components/timeline-annotations.tsx +84 -0
  263. package/src/components/tooltip.tsx +13 -16
  264. package/src/components/user-message-body.tsx +1 -1
  265. package/src/components/workbench-changes.tsx +192 -35
  266. package/src/components/workspace-dock.tsx +13 -5
  267. package/src/composer.ts +3 -0
  268. package/src/hooks/internal.ts +132 -38
  269. package/src/hooks/use-attached-browsers.ts +152 -0
  270. package/src/hooks/use-browser-frame-stream.ts +316 -0
  271. package/src/hooks/use-browser-identities.ts +225 -0
  272. package/src/hooks/use-browser-session.ts +476 -0
  273. package/src/hooks/use-browser-sessions.ts +281 -0
  274. package/src/hooks/use-codex-accounts.ts +4 -4
  275. package/src/hooks/use-composer.ts +769 -24
  276. package/src/hooks/use-computer-frame-stream.ts +311 -0
  277. package/src/hooks/use-computer-session.ts +442 -0
  278. package/src/hooks/use-computer-sessions.ts +245 -0
  279. package/src/hooks/use-environments.ts +24 -5
  280. package/src/hooks/use-file-attachments.ts +70 -32
  281. package/src/hooks/use-goal.ts +17 -5
  282. package/src/hooks/use-machine-chip.ts +5 -3
  283. package/src/hooks/use-machines.ts +79 -3
  284. package/src/hooks/use-packs.ts +51 -0
  285. package/src/hooks/use-sandbox-files.ts +382 -88
  286. package/src/hooks/use-sandbox-git.ts +208 -46
  287. package/src/hooks/use-sandbox-terminal.ts +3 -2
  288. package/src/hooks/use-session-capabilities.ts +94 -70
  289. package/src/hooks/use-session-events.ts +18 -4
  290. package/src/hooks/use-terminal-stream.ts +102 -23
  291. package/src/hooks/use-transcription.ts +4 -14
  292. package/src/hooks/use-turn-queue.ts +17 -5
  293. package/src/hooks/use-video-artifact-playback.ts +28 -0
  294. package/src/hooks/use-voice-input.ts +163 -19
  295. package/src/hooks/use-windowed-sections.ts +9 -7
  296. package/src/hooks/use-workspace-edit.ts +2 -1
  297. package/src/index.ts +21 -6
  298. package/src/interaction.ts +71 -0
  299. package/src/lib/composer-responsive-context.ts +15 -0
  300. package/src/lib/relay-wire.ts +4 -0
  301. package/src/lib/sandbox-liveness.ts +11 -0
  302. package/src/lib/terminal-capability.ts +10 -0
  303. package/src/lib/use-portal-token-style.ts +60 -15
  304. package/src/provider.tsx +52 -7
  305. package/src/realtime/dropdown-menu.tsx +15 -19
  306. package/src/realtime/realtime-control.tsx +16 -5
  307. package/src/session-context.ts +90 -0
  308. package/src/timeline/activity-rail.tsx +31 -6
  309. package/src/timeline/index.ts +4 -1
  310. package/src/timeline/parsers.ts +46 -20
  311. package/src/timeline/projection.ts +233 -13
  312. package/src/timeline/registry.ts +19 -0
  313. package/src/timeline/screenshot-lightbox.tsx +30 -9
  314. package/src/timeline/shared.tsx +59 -11
  315. package/src/timeline/tool-renderers.tsx +421 -24
  316. package/src/timeline/turn-summary.tsx +2 -2
  317. package/src/timeline/types.ts +58 -2
  318. package/src/voice-recording-store.ts +15 -0
  319. package/styles/compiled.css +5298 -0
  320. package/styles/compiled.d.ts +2 -0
  321. package/styles/effective-tokens.css +27 -0
  322. package/styles/index.css +18 -0
  323. package/styles/responsive.css +208 -0
  324. package/styles/responsive.d.ts +2 -0
  325. package/styles/tokens.css +50 -17
  326. package/dist/chunk-3PB7MIT6.js.map +0 -1
  327. package/dist/chunk-6EDV6YFE.js.map +0 -1
  328. package/dist/chunk-EB67J347.js.map +0 -1
  329. package/dist/chunk-EKZH6IDG.js.map +0 -1
  330. package/dist/chunk-FGZUCXZF.js.map +0 -1
  331. package/dist/chunk-HFO4ERGQ.js.map +0 -1
  332. package/dist/chunk-JB5725BI.js +0 -48
  333. package/dist/chunk-JB5725BI.js.map +0 -1
  334. package/dist/chunk-LGVMUIRV.js.map +0 -1
  335. package/dist/chunk-LWR4MXSS.js.map +0 -1
  336. package/dist/chunk-SRFUT2ZU.js.map +0 -1
  337. package/dist/chunk-U3CSCMWA.js.map +0 -1
  338. package/dist/chunk-VCBFUSUZ.js.map +0 -1
  339. package/dist/fleet-decision-projection-SLNZMS42.js +0 -241
  340. package/dist/fleet-decision-projection-SLNZMS42.js.map +0 -1
  341. package/dist/queue-surface-implementation-J4XJFKRW.js.map +0 -1
@@ -1,4 +1,9 @@
1
- import type { SessionEvent, SessionStatus } from "@opengeni/sdk";
1
+ import type {
2
+ DraftTimelineAnnotation,
3
+ MediaGenerationResult,
4
+ SessionEvent,
5
+ SessionStatus,
6
+ } from "@opengeni/sdk";
2
7
  import {
3
8
  ArrowDownIcon,
4
9
  ArrowRightIcon,
@@ -23,6 +28,8 @@ import { AnimatePresence, motion } from "motion/react";
23
28
  import { Collapsible } from "radix-ui";
24
29
  import {
25
30
  Component,
31
+ Suspense,
32
+ lazy,
26
33
  memo,
27
34
  useCallback,
28
35
  useEffect,
@@ -68,6 +75,10 @@ import {
68
75
  type NoticeItem,
69
76
  type TimelineGroup,
70
77
  type TimelineItem,
78
+ type TimelineAnnotationSourceDescriptor,
79
+ type RetainedArtifactLoader,
80
+ type RetainedScreenshotLoader,
81
+ type VideoArtifactPlaybackLoader,
71
82
  type ToolRegistry,
72
83
  type TurnSummaryOptions,
73
84
  type UserMessageItem,
@@ -80,6 +91,7 @@ import {
80
91
  useTurnSettleOpen,
81
92
  } from "../timeline";
82
93
  import { CopyHoverFrame } from "./copy-button";
94
+ import { GeneratedVideoPlayer } from "./generated-video-player";
83
95
  import {
84
96
  MACHINE_INPUT_META,
85
97
  cleanMachineInputSummary,
@@ -91,8 +103,11 @@ import { SESSION_STATUS_META, StatusDot } from "./session-status";
91
103
  import { TimelineComputeLabelProvider } from "../timeline/compute-label";
92
104
  import { EntranceAnimationProvider, useEntranceAnimation } from "../timeline/entrance";
93
105
  import { SeenActivityIdsProvider } from "../timeline/seen-activity-ids";
106
+ import { TimelineAnnotationsChip } from "./timeline-annotations";
94
107
  import { TooltipProvider } from "./tooltip";
95
108
 
109
+ const TimelineAnnotationSelection = lazy(() => import("./timeline-annotation-selection"));
110
+
96
111
  export type MessageTimelineProps = {
97
112
  /** Raw session events (projected internally) … */
98
113
  events?: SessionEvent[] | undefined;
@@ -123,6 +138,12 @@ export type MessageTimelineProps = {
123
138
  * a Reconnect button without a handler to run it.
124
139
  */
125
140
  onReconnect?: ((item: AuthNeededItem) => void | Promise<void>) | undefined;
141
+ /**
142
+ * Decide which durable authentication notices this timeline presents.
143
+ * Defaults to showing every notice. Embedded hosts can suppress notices for
144
+ * credentials they manage elsewhere without discarding the underlying event.
145
+ */
146
+ shouldRenderAuthNeeded?: ((item: AuthNeededItem) => boolean) | undefined;
126
147
  /**
127
148
  * Resolve a provider domain (from a reconnect card) to a logo URL the host
128
149
  * serves itself — the app maps it through its catalog + `catalogAssetUrl`.
@@ -137,6 +158,12 @@ export type MessageTimelineProps = {
137
158
  * `createDefaultToolRegistry({ entries })` to add custom tool renderers.
138
159
  */
139
160
  toolRegistry?: ToolRegistry | undefined;
161
+ /** Resolve opaque retained screenshot receipts through the authenticated host SDK. */
162
+ loadRetainedScreenshot?: RetainedScreenshotLoader | undefined;
163
+ /** Resolve permanent generated-image receipts through the authenticated host SDK. */
164
+ loadRetainedArtifact?: RetainedArtifactLoader | undefined;
165
+ /** Mint short-lived native playback sources for retained generated videos. */
166
+ loadVideoArtifactPlayback?: VideoArtifactPlaybackLoader | undefined;
140
167
  /**
141
168
  * Display name of the session's active compute target (Connected Machine or
142
169
  * cloud sandbox). When set, exec_command collapsed previews prefix `on {label}`.
@@ -146,6 +173,8 @@ export type MessageTimelineProps = {
146
173
  turnSummary?: TurnSummaryOptions | undefined;
147
174
  /** Follow new events when pinned to the bottom. Defaults to true. */
148
175
  autoFollow?: boolean | undefined;
176
+ /** Capture a same-row text selection into the host's canonical composer draft. */
177
+ onAnnotate?: ((annotation: DraftTimelineAnnotation) => void) | undefined;
149
178
  /** Older durable history exists above the current window (see useSessionEvents). */
150
179
  hasOlder?: boolean | undefined;
151
180
  /** An older window is being fetched; shows the quiet top shimmer. */
@@ -167,6 +196,8 @@ export type MessageTimelineProps = {
167
196
  * scrolls the in-memory window.
168
197
  */
169
198
  onJumpToLatest?: (() => void | Promise<void>) | undefined;
199
+ /** Host-owned content appended after timeline groups, such as startup progress. */
200
+ trailingState?: ReactNode | undefined;
170
201
  emptyState?: ReactNode | undefined;
171
202
  className?: string | undefined;
172
203
  };
@@ -277,11 +308,16 @@ export function MessageTimeline({
277
308
  onOpenSession,
278
309
  onMemoryClick,
279
310
  onReconnect,
311
+ shouldRenderAuthNeeded,
280
312
  resolveProviderLogo,
281
313
  toolRegistry = defaultToolRegistry,
314
+ loadRetainedScreenshot,
315
+ loadRetainedArtifact,
316
+ loadVideoArtifactPlayback,
282
317
  computeLabel = null,
283
318
  turnSummary,
284
319
  autoFollow = true,
320
+ onAnnotate,
285
321
  hasOlder = false,
286
322
  loadingOlder = false,
287
323
  onLoadOlder,
@@ -291,11 +327,32 @@ export function MessageTimeline({
291
327
  loadingNewer = false,
292
328
  onLoadNewer,
293
329
  onJumpToLatest,
330
+ trailingState,
294
331
  emptyState,
295
332
  className,
296
333
  }: MessageTimelineProps) {
297
- const resolvedItems = useMemo(() => items ?? buildTimeline(events ?? []), [items, events]);
334
+ const resolvedItems = useMemo(() => {
335
+ const projected = items ?? buildTimeline(events ?? []);
336
+ if (!shouldRenderAuthNeeded) {
337
+ return projected;
338
+ }
339
+ return projected.filter((item) => item.kind !== "auth-needed" || shouldRenderAuthNeeded(item));
340
+ }, [items, events, shouldRenderAuthNeeded]);
298
341
  const allGroups = useMemo(() => groupTimeline(resolvedItems), [resolvedItems]);
342
+ const annotationSources = useMemo(() => {
343
+ const sources = new Map<string, TimelineAnnotationSourceDescriptor>();
344
+ for (const item of resolvedItems) {
345
+ if (
346
+ (item.kind === "user-message" ||
347
+ item.kind === "agent-message" ||
348
+ item.kind === "tool-call") &&
349
+ item.annotationSource
350
+ ) {
351
+ sources.set(item.annotationSource.eventId, item.annotationSource);
352
+ }
353
+ }
354
+ return sources;
355
+ }, [resolvedItems]);
299
356
  const groups = useStableTimelineGroupKeys(allGroups);
300
357
  const scrollRef = useRef<HTMLDivElement | null>(null);
301
358
  const topSentinelRef = useRef<HTMLDivElement | null>(null);
@@ -1262,6 +1319,15 @@ export function MessageTimeline({
1262
1319
  <EntranceAnimationProvider value={!bulkRender}>
1263
1320
  <TooltipProvider delayDuration={400}>
1264
1321
  <div className={cn("og-root relative flex min-h-0 flex-col", className)}>
1322
+ {onAnnotate ? (
1323
+ <Suspense fallback={null}>
1324
+ <TimelineAnnotationSelection
1325
+ rootRef={scrollRef}
1326
+ sources={annotationSources}
1327
+ onAnnotate={onAnnotate}
1328
+ />
1329
+ </Suspense>
1330
+ ) : null}
1265
1331
  {/* Pinned: anchoring off so the tip-follow camera owns the motion.
1266
1332
  Unpinned: native scroll anchoring holds the reader's place. */}
1267
1333
  <div
@@ -1278,7 +1344,7 @@ export function MessageTimeline({
1278
1344
  className={cn(
1279
1345
  // tabIndex=-1 is programmatic only — never paint a focus ring on
1280
1346
  // the whole scroller (click + Shift used to flash a blue outline).
1281
- "min-h-0 flex-1 overflow-y-auto overscroll-contain px-4 py-6 sm:px-6 outline-none",
1347
+ "min-h-0 flex-1 overflow-y-auto overscroll-contain px-4 py-6 sm:px-6 outline-hidden",
1282
1348
  autoFollow && pinned && !hasNewer
1283
1349
  ? "[overflow-anchor:none]"
1284
1350
  : "[overflow-anchor:auto]",
@@ -1325,6 +1391,9 @@ export function MessageTimeline({
1325
1391
  onReconnect,
1326
1392
  resolveProviderLogo,
1327
1393
  toolRegistry,
1394
+ loadRetainedScreenshot,
1395
+ loadRetainedArtifact,
1396
+ loadVideoArtifactPlayback,
1328
1397
  turnSummary,
1329
1398
  ]}
1330
1399
  >
@@ -1337,6 +1406,9 @@ export function MessageTimeline({
1337
1406
  onReconnect={onReconnect}
1338
1407
  resolveProviderLogo={resolveProviderLogo}
1339
1408
  toolRegistry={toolRegistry}
1409
+ loadRetainedScreenshot={loadRetainedScreenshot}
1410
+ loadRetainedArtifact={loadRetainedArtifact}
1411
+ loadVideoArtifactPlayback={loadVideoArtifactPlayback}
1340
1412
  turnSummary={turnSummary}
1341
1413
  foldLiveCluster={isAgentProgress(next)}
1342
1414
  trailingAgentText={trailingAgentTextAfterTurn(group, next)}
@@ -1349,6 +1421,9 @@ export function MessageTimeline({
1349
1421
  </div>
1350
1422
  );
1351
1423
  })}
1424
+ {groups.length > 0 && trailingState ? (
1425
+ <div data-og-timeline-trailing-state="">{trailingState}</div>
1426
+ ) : null}
1352
1427
  {hasNewer ? (
1353
1428
  <div
1354
1429
  ref={bottomSentinelRef}
@@ -1502,7 +1577,7 @@ export function MessageTimeline({
1502
1577
  }
1503
1578
  }}
1504
1579
  className={cn(
1505
- "absolute bottom-4 left-1/2 -translate-x-1/2",
1580
+ "absolute inset-x-0 bottom-4 mx-auto w-fit",
1506
1581
  "inline-flex items-center gap-1.5 rounded-full border border-og-border bg-og-surface-3/90 px-3 py-1.5",
1507
1582
  "text-og-control font-medium text-og-fg shadow-og-md backdrop-blur",
1508
1583
  "hover:border-og-border-strong",
@@ -1677,6 +1752,9 @@ const TimelineGroupView = memo(function TimelineGroupView({
1677
1752
  onReconnect,
1678
1753
  resolveProviderLogo,
1679
1754
  toolRegistry,
1755
+ loadRetainedScreenshot,
1756
+ loadRetainedArtifact,
1757
+ loadVideoArtifactPlayback,
1680
1758
  turnSummary,
1681
1759
  insideTurn = false,
1682
1760
  nestClusterChips = false,
@@ -1693,6 +1771,9 @@ const TimelineGroupView = memo(function TimelineGroupView({
1693
1771
  onReconnect?: ((item: AuthNeededItem) => void | Promise<void>) | undefined;
1694
1772
  resolveProviderLogo?: ((providerDomain: string) => string | null | undefined) | undefined;
1695
1773
  toolRegistry: ToolRegistry;
1774
+ loadRetainedScreenshot?: RetainedScreenshotLoader | undefined;
1775
+ loadRetainedArtifact?: RetainedArtifactLoader | undefined;
1776
+ loadVideoArtifactPlayback?: VideoArtifactPlaybackLoader | undefined;
1696
1777
  turnSummary?: TurnSummaryOptions | undefined;
1697
1778
  /** A completed cluster of a still-RUNNING turn (not the live tail) folds
1698
1779
  behind a neutral chip — the one place activity without an outcome still
@@ -1727,13 +1808,16 @@ const TimelineGroupView = memo(function TimelineGroupView({
1727
1808
  const activityShouldFold =
1728
1809
  group.kind === "activity" &&
1729
1810
  Boolean(group.outcome || (foldLiveCluster && clusterIsSettled(group)));
1811
+ const containsGeneratedImage = timelineGroupContainsGeneratedImage(group);
1730
1812
  // Latch live→folded so a top-level shell that was already mounted open can
1731
1813
  // start the settle beat without remounting bare rail → wrapper.
1732
1814
  const liveActivitySettle = useLiveSettleFold(activityShouldFold && !insideTurn);
1733
1815
  const turnDefaultOpen =
1734
1816
  !insideTurn &&
1735
1817
  group.kind === "turn" &&
1736
- (group.outcome === "failed" || timelineGroupContainsAuthNeeded(group));
1818
+ (group.outcome === "failed" ||
1819
+ timelineGroupContainsAuthNeeded(group) ||
1820
+ containsGeneratedImage);
1737
1821
  // activity-* → turn-* remount: carry resting state so settleFold does not
1738
1822
  // re-open a chip the reader already watched collapse.
1739
1823
  if (group.kind === "turn" && foldMemory && !insideTurn) {
@@ -1758,7 +1842,7 @@ const TimelineGroupView = memo(function TimelineGroupView({
1758
1842
  // settled closed pre-wrap was showing as a CHIP, so mounting it closed
1759
1843
  // is both the stable height and the honest state — force-opening it
1760
1844
  // was the "already-collapsed cluster auto-expands at the end" reopen.
1761
- const useNestedChip = nestClusterChips && activityShouldFold;
1845
+ const useNestedChip = nestClusterChips && activityShouldFold && !containsGeneratedImage;
1762
1846
  if (!useNestedChip) {
1763
1847
  return (
1764
1848
  <ActivityRail
@@ -1766,6 +1850,8 @@ const TimelineGroupView = memo(function TimelineGroupView({
1766
1850
  onOpenSession={onOpenSession}
1767
1851
  onMemoryClick={onMemoryClick}
1768
1852
  toolRegistry={toolRegistry}
1853
+ loadRetainedScreenshot={loadRetainedScreenshot}
1854
+ loadRetainedArtifact={loadRetainedArtifact}
1769
1855
  bare
1770
1856
  />
1771
1857
  );
@@ -1787,6 +1873,8 @@ const TimelineGroupView = memo(function TimelineGroupView({
1787
1873
  onOpenSession={onOpenSession}
1788
1874
  onMemoryClick={onMemoryClick}
1789
1875
  toolRegistry={toolRegistry}
1876
+ loadRetainedScreenshot={loadRetainedScreenshot}
1877
+ loadRetainedArtifact={loadRetainedArtifact}
1790
1878
  bare
1791
1879
  />
1792
1880
  </TurnSummary>
@@ -1812,6 +1900,8 @@ const TimelineGroupView = memo(function TimelineGroupView({
1812
1900
  onOpenSession={onOpenSession}
1813
1901
  onMemoryClick={onMemoryClick}
1814
1902
  toolRegistry={toolRegistry}
1903
+ loadRetainedScreenshot={loadRetainedScreenshot}
1904
+ loadRetainedArtifact={loadRetainedArtifact}
1815
1905
  bare
1816
1906
  />
1817
1907
  </TurnRailFrame>
@@ -1838,6 +1928,9 @@ const TimelineGroupView = memo(function TimelineGroupView({
1838
1928
  onReconnect,
1839
1929
  resolveProviderLogo,
1840
1930
  toolRegistry,
1931
+ loadRetainedScreenshot,
1932
+ loadRetainedArtifact,
1933
+ loadVideoArtifactPlayback,
1841
1934
  turnSummary,
1842
1935
  ]}
1843
1936
  >
@@ -1849,6 +1942,9 @@ const TimelineGroupView = memo(function TimelineGroupView({
1849
1942
  onReconnect={onReconnect}
1850
1943
  resolveProviderLogo={resolveProviderLogo}
1851
1944
  toolRegistry={toolRegistry}
1945
+ loadRetainedScreenshot={loadRetainedScreenshot}
1946
+ loadRetainedArtifact={loadRetainedArtifact}
1947
+ loadVideoArtifactPlayback={loadVideoArtifactPlayback}
1852
1948
  turnSummary={turnSummary}
1853
1949
  insideTurn
1854
1950
  nestClusterChips={nestClusters}
@@ -1890,6 +1986,7 @@ const TimelineGroupView = memo(function TimelineGroupView({
1890
1986
  onReconnect={onReconnect}
1891
1987
  resolveProviderLogo={resolveProviderLogo}
1892
1988
  onOpenSession={onOpenSession}
1989
+ loadVideoArtifactPlayback={loadVideoArtifactPlayback}
1893
1990
  />
1894
1991
  );
1895
1992
  }
@@ -1953,6 +2050,20 @@ function timelineGroupContainsAuthNeeded(group: TimelineGroup): boolean {
1953
2050
  }
1954
2051
  }
1955
2052
 
2053
+ /** Generated images are primary user-visible output, not incidental activity. */
2054
+ function timelineGroupContainsGeneratedImage(group: TimelineGroup): boolean {
2055
+ switch (group.kind) {
2056
+ case "item":
2057
+ return false;
2058
+ case "activity":
2059
+ return group.items.some(
2060
+ (item) => item.kind === "tool-call" && item.name === "generate_image",
2061
+ );
2062
+ case "turn":
2063
+ return group.groups.some(timelineGroupContainsGeneratedImage);
2064
+ }
2065
+ }
2066
+
1956
2067
  function timelineGroupItemIds(group: TimelineGroup): string[] {
1957
2068
  switch (group.kind) {
1958
2069
  case "item":
@@ -2124,6 +2235,7 @@ export function TimelineRow({
2124
2235
  onReconnect,
2125
2236
  resolveProviderLogo,
2126
2237
  onOpenSession,
2238
+ loadVideoArtifactPlayback,
2127
2239
  }: {
2128
2240
  item: TimelineItem;
2129
2241
  renderMessageText?:
@@ -2132,6 +2244,7 @@ export function TimelineRow({
2132
2244
  onReconnect?: ((item: AuthNeededItem) => void | Promise<void>) | undefined;
2133
2245
  resolveProviderLogo?: ((providerDomain: string) => string | null | undefined) | undefined;
2134
2246
  onOpenSession?: ((sessionId: string) => void) | undefined;
2247
+ loadVideoArtifactPlayback?: VideoArtifactPlaybackLoader | undefined;
2135
2248
  }) {
2136
2249
  switch (item.kind) {
2137
2250
  case "user-message":
@@ -2145,7 +2258,9 @@ export function TimelineRow({
2145
2258
  case "goal":
2146
2259
  return <GoalRow item={item} />;
2147
2260
  case "machine-input-batch":
2148
- return <MachineInputBatchRow item={item} />;
2261
+ return (
2262
+ <MachineInputBatchRow item={item} loadVideoArtifactPlayback={loadVideoArtifactPlayback} />
2263
+ );
2149
2264
  case "notice":
2150
2265
  return <NoticeRow item={item} />;
2151
2266
  case "context-compaction":
@@ -2265,25 +2380,71 @@ function UserMessageRow({
2265
2380
  | undefined;
2266
2381
  }) {
2267
2382
  const enter = useEntranceAnimation();
2383
+ const deliveryLabel =
2384
+ item.delivery?.state === "sending"
2385
+ ? "Sending…"
2386
+ : item.delivery?.state === "queued"
2387
+ ? "Queued"
2388
+ : item.delivery?.state === "failed"
2389
+ ? "Not sent"
2390
+ : null;
2268
2391
  return (
2269
2392
  <div className={cn(enter && "animate-og-enter", "flex justify-end")}>
2270
2393
  <div className="flex max-w-[85%] min-w-0 flex-col items-end gap-1">
2271
2394
  <CopyHoverFrame
2272
- copyText={item.text}
2395
+ copyText={
2396
+ item.text ||
2397
+ (item.annotations ?? [])
2398
+ .map((annotation) => `${annotation.quote}\n${annotation.note}`)
2399
+ .join("\n\n")
2400
+ }
2273
2401
  label="Copy message"
2274
2402
  className="w-fit max-w-full min-w-0"
2275
2403
  trailing={<MessageFooterTime occurredAt={item.occurredAt} />}
2276
2404
  >
2277
2405
  <div className="w-fit max-w-full min-w-0 rounded-og-lg rounded-br-og-xs border border-og-border bg-og-surface-2 px-4 py-2.5 text-og-md leading-6 text-og-fg">
2278
- {renderMessageText ? (
2279
- renderMessageText(item.text, item)
2280
- ) : (
2281
- <UserMessageBody messageId={item.id} text={item.text}>
2282
- <Markdown>{item.text}</Markdown>
2283
- </UserMessageBody>
2284
- )}
2406
+ {item.text ? (
2407
+ <div data-og-annotation-source-key={item.annotationSource?.eventId}>
2408
+ {renderMessageText ? (
2409
+ renderMessageText(item.text, item)
2410
+ ) : (
2411
+ <UserMessageBody messageId={item.id} text={item.text}>
2412
+ <Markdown>{item.text}</Markdown>
2413
+ </UserMessageBody>
2414
+ )}
2415
+ </div>
2416
+ ) : null}
2417
+ {(item.annotations?.length ?? 0) > 0 ? (
2418
+ <TimelineAnnotationsChip
2419
+ annotations={item.annotations ?? []}
2420
+ className={item.text ? "mt-2" : undefined}
2421
+ />
2422
+ ) : null}
2285
2423
  </div>
2286
2424
  </CopyHoverFrame>
2425
+ {deliveryLabel ? (
2426
+ <div className="flex max-w-full items-center gap-2 px-1 text-og-xs text-og-fg-subtle">
2427
+ <span title={item.delivery?.error}>{deliveryLabel}</span>
2428
+ {item.delivery?.state === "failed" && item.delivery.onRetry ? (
2429
+ <button
2430
+ type="button"
2431
+ className="font-medium text-og-fg underline decoration-og-border underline-offset-2 hover:text-og-fg-strong"
2432
+ onClick={item.delivery.onRetry}
2433
+ >
2434
+ Retry
2435
+ </button>
2436
+ ) : null}
2437
+ {item.delivery?.state === "failed" && item.delivery.onRemove ? (
2438
+ <button
2439
+ type="button"
2440
+ className="font-medium text-og-fg-subtle underline decoration-og-border underline-offset-2 hover:text-og-fg"
2441
+ onClick={item.delivery.onRemove}
2442
+ >
2443
+ Remove
2444
+ </button>
2445
+ ) : null}
2446
+ </div>
2447
+ ) : null}
2287
2448
  </div>
2288
2449
  </div>
2289
2450
  );
@@ -2309,15 +2470,17 @@ function AgentMessageRow({
2309
2470
  // While streaming, copy is still useful (current text) but keep chrome calm —
2310
2471
  // stamp only after the message finishes (occurredAt tracks completion).
2311
2472
  return (
2312
- <CopyHoverFrame
2313
- copyText={item.text}
2314
- label="Copy message"
2315
- align="start"
2316
- className={cn(enter && "animate-og-enter", "min-w-0 text-og-md leading-7 text-og-fg")}
2317
- trailing={item.streaming ? null : <MessageFooterTime occurredAt={item.occurredAt} />}
2318
- >
2319
- {body}
2320
- </CopyHoverFrame>
2473
+ <div data-og-annotation-source-key={item.annotationSource?.eventId}>
2474
+ <CopyHoverFrame
2475
+ copyText={item.text}
2476
+ label="Copy message"
2477
+ align="start"
2478
+ className={cn(enter && "animate-og-enter", "min-w-0 text-og-md leading-7 text-og-fg")}
2479
+ trailing={item.streaming ? null : <MessageFooterTime occurredAt={item.occurredAt} />}
2480
+ >
2481
+ {body}
2482
+ </CopyHoverFrame>
2483
+ </div>
2321
2484
  );
2322
2485
  }
2323
2486
 
@@ -2428,7 +2591,7 @@ function WorkerCompletionRow({
2428
2591
  onClick={() => onOpenSession(item.childSessionId)}
2429
2592
  className={cn(
2430
2593
  "-my-0.5 -mr-1 inline-flex shrink-0 items-center gap-1 rounded-og-sm px-2 py-1 text-og-sm font-medium text-og-fg-muted pointer-coarse:py-2",
2431
- "outline-none transition-colors duration-150 hover:bg-og-surface-2 hover:text-og-fg",
2594
+ "outline-hidden transition-colors duration-150 hover:bg-og-surface-2 hover:text-og-fg",
2432
2595
  "focus-visible:ring-2 focus-visible:ring-og-accent",
2433
2596
  )}
2434
2597
  >
@@ -2444,7 +2607,7 @@ function WorkerCompletionRow({
2444
2607
  type="button"
2445
2608
  className={cn(
2446
2609
  "group/wc -mx-1 inline-flex w-fit items-center gap-1 rounded-og-sm px-1 py-0.5 text-og-xs font-medium text-og-fg-subtle",
2447
- "outline-none transition-colors duration-150 hover:text-og-fg-muted focus-visible:ring-2 focus-visible:ring-og-accent",
2610
+ "outline-hidden transition-colors duration-150 hover:text-og-fg-muted focus-visible:ring-2 focus-visible:ring-og-accent",
2448
2611
  )}
2449
2612
  >
2450
2613
  <ChevronRightIcon className="size-3 transition-transform duration-150 ease-og-in-out group-data-[state=open]/wc:rotate-90" />
@@ -2567,10 +2730,24 @@ function GoalRow({ item }: { item: GoalItem }) {
2567
2730
  );
2568
2731
  }
2569
2732
 
2570
- function MachineInputBatchRow({ item }: { item: MachineInputBatchItem }) {
2733
+ function MachineInputBatchRow({
2734
+ item,
2735
+ loadVideoArtifactPlayback,
2736
+ }: {
2737
+ item: MachineInputBatchItem;
2738
+ loadVideoArtifactPlayback?: VideoArtifactPlaybackLoader | undefined;
2739
+ }) {
2571
2740
  const enter = useEntranceAnimation();
2572
2741
  const label = machineInputBatchLabel(item.members);
2573
2742
  const single = item.members.length === 1 ? item.members[0]! : null;
2743
+ if (single?.kind === "media_generation_result" && single.result) {
2744
+ return (
2745
+ <VideoGenerationResultRow
2746
+ result={single.result}
2747
+ loadVideoArtifactPlayback={loadVideoArtifactPlayback}
2748
+ />
2749
+ );
2750
+ }
2574
2751
  const singleSummary = single ? cleanMachineInputSummary(single.summary) : "";
2575
2752
  const showCollapsedSummary =
2576
2753
  single != null && machineInputSummaryIsUseful(single.kind, singleSummary);
@@ -2594,7 +2771,11 @@ function MachineInputBatchRow({ item }: { item: MachineInputBatchItem }) {
2594
2771
  </summary>
2595
2772
  <div className="mx-auto mt-2 w-full max-w-lg space-y-2 border-t border-og-border/50 pt-2">
2596
2773
  {item.members.map((member) => (
2597
- <MachineInputRow key={member.id} member={member} />
2774
+ <MachineInputRow
2775
+ key={member.id}
2776
+ member={member}
2777
+ loadVideoArtifactPlayback={loadVideoArtifactPlayback}
2778
+ />
2598
2779
  ))}
2599
2780
  </div>
2600
2781
  </details>
@@ -2607,7 +2788,22 @@ function MachineInputBatchRow({ item }: { item: MachineInputBatchItem }) {
2607
2788
  );
2608
2789
  }
2609
2790
 
2610
- function MachineInputRow({ member }: { member: MachineInputBatchItem["members"][number] }) {
2791
+ function MachineInputRow({
2792
+ member,
2793
+ loadVideoArtifactPlayback,
2794
+ }: {
2795
+ member: MachineInputBatchItem["members"][number];
2796
+ loadVideoArtifactPlayback?: VideoArtifactPlaybackLoader | undefined;
2797
+ }) {
2798
+ if (member.kind === "media_generation_result" && member.result) {
2799
+ return (
2800
+ <VideoGenerationResultRow
2801
+ result={member.result}
2802
+ loadVideoArtifactPlayback={loadVideoArtifactPlayback}
2803
+ compact
2804
+ />
2805
+ );
2806
+ }
2611
2807
  const source = readableMachineInputSource(member.sourceId);
2612
2808
  const summary = cleanMachineInputSummary(member.summary);
2613
2809
  return (
@@ -2628,6 +2824,72 @@ function MachineInputRow({ member }: { member: MachineInputBatchItem["members"][
2628
2824
  );
2629
2825
  }
2630
2826
 
2827
+ function VideoGenerationResultRow({
2828
+ result,
2829
+ loadVideoArtifactPlayback,
2830
+ compact = false,
2831
+ }: {
2832
+ result: MediaGenerationResult;
2833
+ loadVideoArtifactPlayback?: VideoArtifactPlaybackLoader | undefined;
2834
+ compact?: boolean | undefined;
2835
+ }) {
2836
+ const enter = useEntranceAnimation();
2837
+ if (result.status !== "ready") {
2838
+ return (
2839
+ <div
2840
+ className={cn(
2841
+ enter && "animate-og-enter",
2842
+ "mx-auto w-full max-w-lg rounded-og-md border border-og-status-failed/30 bg-og-status-failed/5 px-3.5 py-3",
2843
+ )}
2844
+ >
2845
+ <div className="flex items-center gap-2 text-og-sm font-medium text-og-status-failed">
2846
+ <XCircleIcon aria-hidden className="size-4" />
2847
+ Video generation failed
2848
+ </div>
2849
+ <p className="mt-1 text-og-sm leading-5 text-og-fg-muted">{result.boundedPublicReason}</p>
2850
+ </div>
2851
+ );
2852
+ }
2853
+ const { receipt } = result;
2854
+ const facts = receipt.video;
2855
+ return (
2856
+ <section
2857
+ aria-label="Generated video"
2858
+ className={cn(
2859
+ enter && "animate-og-enter",
2860
+ "mx-auto w-full max-w-2xl overflow-hidden rounded-og-lg border border-og-border bg-og-surface-1 shadow-sm",
2861
+ compact && "max-w-lg",
2862
+ )}
2863
+ >
2864
+ {loadVideoArtifactPlayback ? (
2865
+ <GeneratedVideoPlayer
2866
+ receipt={receipt}
2867
+ loadPlaybackSource={loadVideoArtifactPlayback}
2868
+ className="rounded-none border-0 shadow-none"
2869
+ />
2870
+ ) : (
2871
+ <div className="flex aspect-video items-center justify-center bg-og-surface-2 text-og-fg-subtle">
2872
+ <PlayIcon aria-hidden className="size-6" />
2873
+ </div>
2874
+ )}
2875
+ <div className="flex items-center justify-between gap-4 px-3.5 py-2.5">
2876
+ <div className="min-w-0">
2877
+ <p className="text-og-sm font-medium text-og-fg">Generated video</p>
2878
+ <p className="truncate text-og-xs text-og-fg-subtle">
2879
+ {facts.width}×{facts.height} · {formatVideoDuration(facts.durationSeconds)}
2880
+ {facts.hasAudio ? " · Audio" : ""}
2881
+ </p>
2882
+ </div>
2883
+ <CheckCircle2Icon aria-label="Ready" className="size-4 shrink-0 text-og-status-success" />
2884
+ </div>
2885
+ </section>
2886
+ );
2887
+ }
2888
+
2889
+ function formatVideoDuration(seconds: number): string {
2890
+ return `${Math.round(seconds * 10) / 10}s`;
2891
+ }
2892
+
2631
2893
  function NoticeRow({ item }: { item: NoticeItem }) {
2632
2894
  const enter = useEntranceAnimation();
2633
2895
  const tone =
@@ -2693,13 +2955,32 @@ function AuthNeededRow({
2693
2955
  const enter = useEntranceAnimation();
2694
2956
  const [busy, setBusy] = useState(false);
2695
2957
  const [failed, setFailed] = useState(false);
2696
- const provider = providerLabel(item.providerDomain);
2958
+ const recommendation = item.capability ?? null;
2959
+ const provider =
2960
+ recommendation?.name ??
2961
+ (item.serverId === "codex_apps" ? "Codex Apps" : providerLabel(item.providerDomain));
2697
2962
  const unavailable =
2698
2963
  item.reason === "personal_authority_unavailable" ||
2699
2964
  item.reason === "unsupported_auth" ||
2700
2965
  item.reason === "resource_scope_unavailable";
2701
2966
  const missing = item.reason === "missing_connection";
2702
- const actionLabel = missing ? "Connect" : "Reconnect";
2967
+ const actionLabel = recommendation
2968
+ ? recommendation.action === "connect"
2969
+ ? "Connect"
2970
+ : "Review"
2971
+ : missing
2972
+ ? "Connect"
2973
+ : "Reconnect";
2974
+ const title = recommendation
2975
+ ? recommendation.action === "connect"
2976
+ ? `Connect ${provider}`
2977
+ : recommendation.action === "add_credentials"
2978
+ ? `Set up ${provider}`
2979
+ : `Enable ${provider}`
2980
+ : unavailable
2981
+ ? `${provider} tools unavailable`
2982
+ : `${actionLabel} ${provider}`;
2983
+ const reasonLine = recommendation?.rationale ?? authReasonLine(item.reason);
2703
2984
 
2704
2985
  const start = async () => {
2705
2986
  if (!onReconnect || busy) {
@@ -2728,10 +3009,18 @@ function AuthNeededRow({
2728
3009
  label={provider}
2729
3010
  />
2730
3011
  <div className="min-w-0">
2731
- <p className="truncate text-og-md font-medium text-og-fg">
2732
- {unavailable ? `${provider} tools unavailable` : `${actionLabel} ${provider}`}
2733
- </p>
2734
- <p className="truncate text-og-sm text-og-fg-subtle">{authReasonLine(item.reason)}</p>
3012
+ <p className="truncate text-og-md font-medium text-og-fg">{title}</p>
3013
+ <p className="line-clamp-2 text-og-sm text-og-fg-subtle">{reasonLine}</p>
3014
+ {recommendation ? (
3015
+ <p className="mt-1 truncate text-og-xs text-og-fg-muted">
3016
+ Provider: {item.providerDomain}
3017
+ </p>
3018
+ ) : null}
3019
+ {recommendation && recommendation.requiredVariables.length > 0 ? (
3020
+ <p className="mt-1 truncate text-og-xs text-og-fg-muted">
3021
+ Needs variables: {recommendation.requiredVariables.join(", ")}
3022
+ </p>
3023
+ ) : null}
2735
3024
  </div>
2736
3025
  </div>
2737
3026
  {!unavailable && onReconnect ? (
@@ -2764,8 +3053,9 @@ function AuthNeededRow({
2764
3053
  </div>
2765
3054
  {!unavailable ? (
2766
3055
  <p className="px-1 text-og-xs text-og-fg-subtle">
2767
- This tool call wasn't replayed. After {missing ? "connecting" : "reconnecting"}, send a
2768
- new message to try again.
3056
+ {recommendation
3057
+ ? "No access has been granted. Review and confirm the provider before continuing."
3058
+ : `This tool call wasn't replayed. After ${missing ? "connecting" : "reconnecting"}, send a new message to try again.`}
2769
3059
  </p>
2770
3060
  ) : null}
2771
3061
  {failed ? (