@opengeni/react 0.48.0 → 0.57.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (325) hide show
  1. package/README.md +138 -2
  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 +199 -0
  13. package/dist/artifacts.js.map +1 -0
  14. package/dist/{chunk-NJ7M2B6Z.js → chunk-5UQPNLIO.js} +16 -8
  15. package/dist/{chunk-NJ7M2B6Z.js.map → chunk-5UQPNLIO.js.map} +1 -1
  16. package/dist/{chunk-LHLYWG5R.js → chunk-6KLX2RH5.js} +461 -38
  17. package/dist/chunk-6KLX2RH5.js.map +1 -0
  18. package/dist/chunk-7WCLAEFV.js +2828 -0
  19. package/dist/chunk-7WCLAEFV.js.map +1 -0
  20. package/dist/{chunk-I4MPFDRP.js → chunk-B6WPZSTT.js} +104 -104
  21. package/dist/{chunk-I4MPFDRP.js.map → chunk-B6WPZSTT.js.map} +1 -1
  22. package/dist/{chunk-EKZH6IDG.js → chunk-C5ZPSSS5.js} +111 -1
  23. package/dist/chunk-C5ZPSSS5.js.map +1 -0
  24. package/dist/{chunk-OL6QVXGF.js → chunk-HA46CYQK.js} +152 -8
  25. package/dist/chunk-HA46CYQK.js.map +1 -0
  26. package/dist/chunk-HUIKIDM5.js +2148 -0
  27. package/dist/chunk-HUIKIDM5.js.map +1 -0
  28. package/dist/{chunk-FWXGSNXT.js → chunk-IGC6A2ZI.js} +585 -34
  29. package/dist/chunk-IGC6A2ZI.js.map +1 -0
  30. package/dist/chunk-JVYQRRC4.js +53 -0
  31. package/dist/chunk-JVYQRRC4.js.map +1 -0
  32. package/dist/chunk-KCGXPP5V.js +157 -0
  33. package/dist/chunk-KCGXPP5V.js.map +1 -0
  34. package/dist/chunk-LLOLUHVG.js +5937 -0
  35. package/dist/chunk-LLOLUHVG.js.map +1 -0
  36. package/dist/{chunk-WYA65Y3F.js → chunk-MKPIAGVL.js} +13 -3
  37. package/dist/chunk-MKPIAGVL.js.map +1 -0
  38. package/dist/chunk-PEI6RANI.js +3234 -0
  39. package/dist/chunk-PEI6RANI.js.map +1 -0
  40. package/dist/chunk-R3AZOAB5.js +933 -0
  41. package/dist/chunk-R3AZOAB5.js.map +1 -0
  42. package/dist/{chunk-O4VXOBGF.js → chunk-WSK7G5LE.js} +70 -2
  43. package/dist/chunk-WSK7G5LE.js.map +1 -0
  44. package/dist/{chunk-23EJ676W.js → chunk-XBZWOM2K.js} +9 -4
  45. package/dist/chunk-XBZWOM2K.js.map +1 -0
  46. package/dist/{chunk-XCNTXWBB.js → chunk-Y7OEDUJ5.js} +856 -338
  47. package/dist/chunk-Y7OEDUJ5.js.map +1 -0
  48. package/dist/client.d.ts +10 -2
  49. package/dist/clipboard.d.ts +1 -0
  50. package/dist/clipboard.js +7 -0
  51. package/dist/clipboard.js.map +1 -0
  52. package/dist/commands/index.d.ts +3 -3
  53. package/dist/commands/registry.d.ts +1 -1
  54. package/dist/commands/types.d.ts +1 -1
  55. package/dist/components/approval-surface.d.ts +2 -2
  56. package/dist/components/artifacts/artifact-surface.d.ts +18 -0
  57. package/dist/components/artifacts/document-editor.d.ts +172 -0
  58. package/dist/components/artifacts/editable-artifact-ui.d.ts +13 -0
  59. package/dist/components/artifacts/editable-artifact-workbench.d.ts +49 -0
  60. package/dist/components/artifacts/editable-document.d.ts +24 -0
  61. package/dist/components/artifacts/editable-presentation.d.ts +24 -0
  62. package/dist/components/artifacts/editable-spreadsheet.d.ts +32 -0
  63. package/dist/components/artifacts/index.d.ts +9 -0
  64. package/dist/components/artifacts/presentation-editor.d.ts +297 -0
  65. package/dist/components/artifacts/published-html-artifact-frame.d.ts +14 -0
  66. package/dist/components/artifacts/spreadsheet-canvas.d.ts +120 -0
  67. package/dist/components/artifacts/spreadsheet-grid.d.ts +113 -0
  68. package/dist/components/browser-input.d.ts +7 -0
  69. package/dist/components/browser-viewer.d.ts +30 -0
  70. package/dist/components/chat-composer.d.ts +8 -8
  71. package/dist/components/code-editor.d.ts +25 -3
  72. package/dist/components/command-palette.d.ts +2 -2
  73. package/dist/components/composer-transcription-control.d.ts +2 -2
  74. package/dist/components/composer.d.ts +17 -17
  75. package/dist/components/computer-viewer.d.ts +24 -0
  76. package/dist/components/copy-button.d.ts +2 -2
  77. package/dist/components/desktop-viewer.d.ts +50 -22
  78. package/dist/components/diff-view.d.ts +1 -1
  79. package/dist/components/enrollment-consent.d.ts +1 -1
  80. package/dist/components/enrollment-device-flow.d.ts +1 -1
  81. package/dist/components/file-browser.d.ts +2 -2
  82. package/dist/components/fleet-tile.d.ts +1 -1
  83. package/dist/components/generated-video-player.d.ts +10 -0
  84. package/dist/components/human-input-form.d.ts +1 -1
  85. package/dist/components/human-input-surface.d.ts +2 -2
  86. package/dist/components/interaction-intervention-banner.d.ts +11 -0
  87. package/dist/components/interactive-workbench-viewers.d.ts +2 -0
  88. package/dist/components/machine-card.d.ts +2 -2
  89. package/dist/components/machine-dock-bar.d.ts +3 -3
  90. package/dist/components/machine-health-pill.d.ts +2 -2
  91. package/dist/components/machine-input-display.d.ts +1 -1
  92. package/dist/components/machine-metrics.d.ts +2 -2
  93. package/dist/components/machine-status-pill.d.ts +4 -4
  94. package/dist/components/machines/health.d.ts +1 -1
  95. package/dist/components/machines/machine-detail.d.ts +3 -3
  96. package/dist/components/machines/metric-history-chart.d.ts +1 -1
  97. package/dist/components/machines/metric-sparkline.d.ts +2 -2
  98. package/dist/components/machines/series.d.ts +2 -2
  99. package/dist/components/machines-dashboard.d.ts +2 -2
  100. package/dist/components/markdown.d.ts +1 -1
  101. package/dist/components/message-timeline.d.ts +12 -5
  102. package/dist/components/model-picker.d.ts +2 -2
  103. package/dist/components/model-policy-picker.d.ts +7 -7
  104. package/dist/components/pierre-diff.d.ts +1 -1
  105. package/dist/components/pierre-file.d.ts +1 -1
  106. package/dist/components/queue-draft-policy.d.ts +1 -1
  107. package/dist/components/queue-surface-implementation.d.ts +3 -3
  108. package/dist/components/queue-surface-state.d.ts +4 -4
  109. package/dist/components/queue-surface.d.ts +5 -5
  110. package/dist/components/sandbox-files.d.ts +3 -3
  111. package/dist/components/sandbox-terminal.d.ts +24 -4
  112. package/dist/components/sandbox-workspace.d.ts +28 -9
  113. package/dist/components/session-chrome.d.ts +4 -4
  114. package/dist/components/session-status.d.ts +2 -2
  115. package/dist/components/timeline-annotation-selection.d.ts +9 -0
  116. package/dist/components/timeline-annotations-dialog.d.ts +16 -0
  117. package/dist/components/timeline-annotations.d.ts +13 -0
  118. package/dist/components/tooltip.d.ts +4 -4
  119. package/dist/components/user-message-body.d.ts +2 -2
  120. package/dist/components/workbench-changes.d.ts +2 -2
  121. package/dist/components/workspace-dock.d.ts +1 -1
  122. package/dist/composer.d.ts +11 -11
  123. package/dist/composer.js +7 -7
  124. package/dist/hooks/internal.d.ts +1 -1
  125. package/dist/hooks/use-attached-browsers.d.ts +21 -0
  126. package/dist/hooks/use-auth-runs.d.ts +32 -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-downloads.d.ts +25 -0
  130. package/dist/hooks/use-browser-frame-stream.d.ts +28 -0
  131. package/dist/hooks/use-browser-identities.d.ts +30 -0
  132. package/dist/hooks/use-browser-session.d.ts +29 -0
  133. package/dist/hooks/use-browser-sessions.d.ts +32 -0
  134. package/dist/hooks/use-channels.d.ts +26 -0
  135. package/dist/hooks/use-codex-accounts.d.ts +2 -2
  136. package/dist/hooks/use-composer.d.ts +36 -9
  137. package/dist/hooks/use-computer-frame-stream.d.ts +24 -0
  138. package/dist/hooks/use-computer-session.d.ts +29 -0
  139. package/dist/hooks/use-computer-sessions.d.ts +28 -0
  140. package/dist/hooks/use-desktop-stream.d.ts +5 -1
  141. package/dist/hooks/use-environments.d.ts +1 -1
  142. package/dist/hooks/use-file-attachments.d.ts +1 -1
  143. package/dist/hooks/use-goal.d.ts +2 -2
  144. package/dist/hooks/use-human-input.d.ts +2 -2
  145. package/dist/hooks/use-interaction-interventions.d.ts +27 -0
  146. package/dist/hooks/use-interaction-invalidation.d.ts +19 -0
  147. package/dist/hooks/use-last-started-turn-policy.d.ts +2 -2
  148. package/dist/hooks/use-machine-chip.d.ts +2 -2
  149. package/dist/hooks/use-machines.d.ts +4 -4
  150. package/dist/hooks/use-network-routes.d.ts +25 -0
  151. package/dist/hooks/use-packs.d.ts +7 -2
  152. package/dist/hooks/use-rigs.d.ts +1 -1
  153. package/dist/hooks/use-sandbox-files.d.ts +6 -4
  154. package/dist/hooks/use-sandbox-git.d.ts +6 -5
  155. package/dist/hooks/use-sandbox-terminal.d.ts +1 -1
  156. package/dist/hooks/use-scheduled-tasks.d.ts +1 -1
  157. package/dist/hooks/use-session-capabilities.d.ts +5 -4
  158. package/dist/hooks/use-session-control.d.ts +1 -1
  159. package/dist/hooks/use-session-events.d.ts +2 -2
  160. package/dist/hooks/use-session-lineage.d.ts +1 -1
  161. package/dist/hooks/use-session-mcp-approval-policy.d.ts +2 -2
  162. package/dist/hooks/use-session.d.ts +2 -2
  163. package/dist/hooks/use-site-auth-connections.d.ts +25 -0
  164. package/dist/hooks/use-slash-commands.d.ts +1 -1
  165. package/dist/hooks/use-supergrok-accounts.d.ts +26 -0
  166. package/dist/hooks/use-terminal-stream.d.ts +11 -2
  167. package/dist/hooks/use-turn-queue.d.ts +2 -2
  168. package/dist/hooks/use-video-artifact-playback.d.ts +9 -0
  169. package/dist/hooks/use-voice-input.d.ts +1 -1
  170. package/dist/hooks/use-workspace-capture.d.ts +1 -1
  171. package/dist/hooks/use-workspace-edit.d.ts +1 -1
  172. package/dist/hooks/use-workspace-sessions.d.ts +1 -1
  173. package/dist/hooks/use-workspaces.d.ts +1 -1
  174. package/dist/index.d.ts +162 -155
  175. package/dist/index.js +1822 -1703
  176. package/dist/index.js.map +1 -1
  177. package/dist/interaction.d.ts +33 -0
  178. package/dist/interaction.js +550 -0
  179. package/dist/interaction.js.map +1 -0
  180. package/dist/interactive-workbench-viewers-TOHBXJEE.js +15 -0
  181. package/dist/interactive-workbench-viewers-TOHBXJEE.js.map +1 -0
  182. package/dist/lib/host-platform.d.ts +5 -0
  183. package/dist/lib/relay-wire.d.ts +24 -1
  184. package/dist/lib/sandbox-liveness.d.ts +9 -0
  185. package/dist/lib/terminal-capability.d.ts +5 -0
  186. package/dist/lib/xterm-theme.d.ts +1 -1
  187. package/dist/machines.d.ts +30 -30
  188. package/dist/machines.js +2 -2
  189. package/dist/model-policy.d.ts +1 -1
  190. package/dist/model-policy.js +1 -1
  191. package/dist/provider.d.ts +6 -5
  192. package/dist/{queue-surface-implementation-U5JFG7HT.js → queue-surface-implementation-2KVJTYXH.js} +100 -48
  193. package/dist/queue-surface-implementation-2KVJTYXH.js.map +1 -0
  194. package/dist/realtime/dropdown-menu.d.ts +8 -8
  195. package/dist/realtime/realtime-control.d.ts +8 -8
  196. package/dist/realtime.d.ts +4 -4
  197. package/dist/realtime.js +38 -11
  198. package/dist/realtime.js.map +1 -1
  199. package/dist/session-context.d.ts +28 -2
  200. package/dist/session-ui.d.ts +14 -14
  201. package/dist/session-ui.js +5 -4
  202. package/dist/session.d.ts +28 -28
  203. package/dist/session.js +10 -10
  204. package/dist/timeline/activity-rail.d.ts +5 -3
  205. package/dist/timeline/disclosure-context.d.ts +1 -1
  206. package/dist/timeline/fleet-decision-projection.d.ts +1 -1
  207. package/dist/timeline/fleet-decision-row.d.ts +3 -3
  208. package/dist/timeline/index.d.ts +19 -19
  209. package/dist/timeline/parsers.d.ts +4 -1
  210. package/dist/timeline/projection.d.ts +3 -3
  211. package/dist/timeline/registry.d.ts +8 -2
  212. package/dist/timeline/screenshot-lightbox.d.ts +2 -2
  213. package/dist/timeline/shared.d.ts +21 -10
  214. package/dist/timeline/tool-diff.d.ts +2 -2
  215. package/dist/timeline/tool-renderers.d.ts +1 -1
  216. package/dist/timeline/turn-summary.d.ts +3 -3
  217. package/dist/timeline/types.d.ts +44 -3
  218. package/dist/timeline-annotation-selection-3L7LHGG2.js +187 -0
  219. package/dist/timeline-annotation-selection-3L7LHGG2.js.map +1 -0
  220. package/dist/timeline-annotations-dialog-U7EVLR75.js +196 -0
  221. package/dist/timeline-annotations-dialog-U7EVLR75.js.map +1 -0
  222. package/package.json +36 -7
  223. package/src/artifacts-document.ts +31 -0
  224. package/src/artifacts-presentation.ts +36 -0
  225. package/src/artifacts-spreadsheet.ts +21 -0
  226. package/src/artifacts.ts +89 -0
  227. package/src/client.ts +157 -1
  228. package/src/clipboard.ts +2 -0
  229. package/src/components/artifacts/artifact-surface.tsx +93 -0
  230. package/src/components/artifacts/document-editor.tsx +2255 -0
  231. package/src/components/artifacts/editable-artifact-ui.tsx +89 -0
  232. package/src/components/artifacts/editable-artifact-workbench.tsx +203 -0
  233. package/src/components/artifacts/editable-document.tsx +657 -0
  234. package/src/components/artifacts/editable-presentation.tsx +1020 -0
  235. package/src/components/artifacts/editable-spreadsheet.tsx +733 -0
  236. package/src/components/artifacts/index.ts +104 -0
  237. package/src/components/artifacts/presentation-editor.tsx +3148 -0
  238. package/src/components/artifacts/published-html-artifact-frame.tsx +38 -0
  239. package/src/components/artifacts/spreadsheet-canvas.ts +799 -0
  240. package/src/components/artifacts/spreadsheet-grid.tsx +2129 -0
  241. package/src/components/browser-input.ts +65 -0
  242. package/src/components/browser-viewer.tsx +2702 -0
  243. package/src/components/chat-composer.tsx +13 -0
  244. package/src/components/code-editor.tsx +89 -30
  245. package/src/components/composer-transcription-control.tsx +10 -6
  246. package/src/components/computer-viewer.tsx +1636 -0
  247. package/src/components/desktop-viewer.tsx +215 -74
  248. package/src/components/generated-video-player.tsx +89 -0
  249. package/src/components/interaction-intervention-banner.tsx +99 -0
  250. package/src/components/interactive-workbench-viewers.ts +2 -0
  251. package/src/components/machine-input-display.ts +3 -0
  252. package/src/components/machines/machine-detail.tsx +1 -1
  253. package/src/components/message-timeline.tsx +289 -32
  254. package/src/components/model-policy-picker.tsx +15 -0
  255. package/src/components/queue-surface-implementation.tsx +116 -60
  256. package/src/components/sandbox-files.tsx +2 -2
  257. package/src/components/sandbox-terminal.tsx +128 -46
  258. package/src/components/sandbox-workspace.tsx +256 -90
  259. package/src/components/session-chrome.tsx +53 -13
  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/workbench-changes.tsx +188 -31
  264. package/src/components/workspace-dock.tsx +59 -5
  265. package/src/hooks/use-attached-browsers.ts +154 -0
  266. package/src/hooks/use-auth-runs.ts +278 -0
  267. package/src/hooks/use-browser-downloads.ts +218 -0
  268. package/src/hooks/use-browser-frame-stream.ts +425 -0
  269. package/src/hooks/use-browser-identities.ts +266 -0
  270. package/src/hooks/use-browser-session.ts +543 -0
  271. package/src/hooks/use-browser-sessions.ts +286 -0
  272. package/src/hooks/use-channels.ts +99 -0
  273. package/src/hooks/use-composer.ts +769 -24
  274. package/src/hooks/use-computer-frame-stream.ts +434 -0
  275. package/src/hooks/use-computer-session.ts +462 -0
  276. package/src/hooks/use-computer-sessions.ts +251 -0
  277. package/src/hooks/use-desktop-stream.ts +39 -7
  278. package/src/hooks/use-interaction-interventions.ts +242 -0
  279. package/src/hooks/use-interaction-invalidation.ts +66 -0
  280. package/src/hooks/use-machine-chip.ts +15 -3
  281. package/src/hooks/use-machines.ts +33 -3
  282. package/src/hooks/use-network-routes.ts +214 -0
  283. package/src/hooks/use-packs.ts +51 -0
  284. package/src/hooks/use-sandbox-files.ts +98 -55
  285. package/src/hooks/use-sandbox-git.ts +93 -38
  286. package/src/hooks/use-sandbox-terminal.ts +3 -2
  287. package/src/hooks/use-session-capabilities.ts +59 -46
  288. package/src/hooks/use-site-auth-connections.ts +226 -0
  289. package/src/hooks/use-supergrok-accounts.ts +81 -0
  290. package/src/hooks/use-terminal-stream.ts +359 -47
  291. package/src/hooks/use-video-artifact-playback.ts +28 -0
  292. package/src/hooks/use-workspace-edit.ts +2 -1
  293. package/src/index.ts +18 -0
  294. package/src/interaction.ts +101 -0
  295. package/src/lib/host-platform.ts +10 -0
  296. package/src/lib/relay-wire.ts +47 -4
  297. package/src/lib/sandbox-liveness.ts +11 -0
  298. package/src/lib/terminal-capability.ts +10 -0
  299. package/src/model-policy.ts +19 -4
  300. package/src/provider.tsx +133 -1
  301. package/src/realtime/realtime-control.tsx +34 -6
  302. package/src/session-context.ts +156 -1
  303. package/src/timeline/activity-rail.tsx +25 -3
  304. package/src/timeline/index.ts +3 -0
  305. package/src/timeline/parsers.ts +14 -1
  306. package/src/timeline/projection.ts +269 -14
  307. package/src/timeline/registry.ts +13 -1
  308. package/src/timeline/screenshot-lightbox.tsx +19 -9
  309. package/src/timeline/shared.tsx +56 -8
  310. package/src/timeline/tool-renderers.tsx +303 -50
  311. package/src/timeline/types.ts +57 -3
  312. package/styles/compiled.css +312 -21
  313. package/styles/index.css +15 -0
  314. package/styles/responsive.css +13 -1
  315. package/dist/chunk-23EJ676W.js.map +0 -1
  316. package/dist/chunk-EKZH6IDG.js.map +0 -1
  317. package/dist/chunk-FWXGSNXT.js.map +0 -1
  318. package/dist/chunk-LHLYWG5R.js.map +0 -1
  319. package/dist/chunk-O4VXOBGF.js.map +0 -1
  320. package/dist/chunk-OL6QVXGF.js.map +0 -1
  321. package/dist/chunk-WYA65Y3F.js.map +0 -1
  322. package/dist/chunk-XCNTXWBB.js.map +0 -1
  323. package/dist/fleet-decision-projection-SLNZMS42.js +0 -241
  324. package/dist/fleet-decision-projection-SLNZMS42.js.map +0 -1
  325. package/dist/queue-surface-implementation-U5JFG7HT.js.map +0 -1
@@ -6,6 +6,7 @@ export const MACHINE_INPUT_META: Record<MachineInputMember["kind"], string> = {
6
6
  agent_message: "Agent update",
7
7
  agent_steer_instruction: "Agent direction",
8
8
  child_terminal_result: "Agent finished",
9
+ media_generation_result: "Video update",
9
10
  };
10
11
 
11
12
  /**
@@ -32,6 +33,8 @@ export function machineInputBatchLabel(members: readonly MachineInputMember[]):
32
33
  return n === 1 ? "Agent update" : `${n} agent updates`;
33
34
  case "agent_steer_instruction":
34
35
  return n === 1 ? "Agent direction" : `${n} agent directions`;
36
+ case "media_generation_result":
37
+ return n === 1 ? "Video ready" : `${n} video updates`;
35
38
  }
36
39
  }
37
40
  const parts = [...counts.entries()].map(([kind, count]) => {
@@ -95,7 +95,7 @@ export function MachineDetail({
95
95
  data-remove-machine
96
96
  aria-label={`Remove machine ${machine.name}`}
97
97
  onClick={() => onRemove(machine)}
98
- className="inline-flex min-h-9 items-center gap-1.5 rounded-og-sm border border-og-status-failed/40 px-2.5 py-1 font-medium text-og-xs text-og-status-failed transition-colors hover:bg-og-status-failed/10 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-og-status-failed/50 pointer-coarse:min-h-11"
98
+ className="inline-flex min-h-9 items-center gap-1.5 rounded-og-sm border border-og-status-failed/40 px-2.5 py-1 font-medium text-og-xs text-og-status-failed transition-colors hover:bg-og-status-failed/10 focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-og-status-failed/50 pointer-coarse:min-h-11"
99
99
  >
100
100
  <Trash2Icon className="size-3.5" aria-hidden />
101
101
  <span className="hidden sm:inline">Remove machine</span>
@@ -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,7 +75,10 @@ import {
68
75
  type NoticeItem,
69
76
  type TimelineGroup,
70
77
  type TimelineItem,
78
+ type TimelineAnnotationSourceDescriptor,
79
+ type RetainedArtifactLoader,
71
80
  type RetainedScreenshotLoader,
81
+ type VideoArtifactPlaybackLoader,
72
82
  type ToolRegistry,
73
83
  type TurnSummaryOptions,
74
84
  type UserMessageItem,
@@ -81,6 +91,7 @@ import {
81
91
  useTurnSettleOpen,
82
92
  } from "../timeline";
83
93
  import { CopyHoverFrame } from "./copy-button";
94
+ import { GeneratedVideoPlayer } from "./generated-video-player";
84
95
  import {
85
96
  MACHINE_INPUT_META,
86
97
  cleanMachineInputSummary,
@@ -92,8 +103,11 @@ import { SESSION_STATUS_META, StatusDot } from "./session-status";
92
103
  import { TimelineComputeLabelProvider } from "../timeline/compute-label";
93
104
  import { EntranceAnimationProvider, useEntranceAnimation } from "../timeline/entrance";
94
105
  import { SeenActivityIdsProvider } from "../timeline/seen-activity-ids";
106
+ import { TimelineAnnotationsChip } from "./timeline-annotations";
95
107
  import { TooltipProvider } from "./tooltip";
96
108
 
109
+ const TimelineAnnotationSelection = lazy(() => import("./timeline-annotation-selection"));
110
+
97
111
  export type MessageTimelineProps = {
98
112
  /** Raw session events (projected internally) … */
99
113
  events?: SessionEvent[] | undefined;
@@ -146,6 +160,10 @@ export type MessageTimelineProps = {
146
160
  toolRegistry?: ToolRegistry | undefined;
147
161
  /** Resolve opaque retained screenshot receipts through the authenticated host SDK. */
148
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;
149
167
  /**
150
168
  * Display name of the session's active compute target (Connected Machine or
151
169
  * cloud sandbox). When set, exec_command collapsed previews prefix `on {label}`.
@@ -155,6 +173,8 @@ export type MessageTimelineProps = {
155
173
  turnSummary?: TurnSummaryOptions | undefined;
156
174
  /** Follow new events when pinned to the bottom. Defaults to true. */
157
175
  autoFollow?: boolean | undefined;
176
+ /** Capture a same-row text selection into the host's canonical composer draft. */
177
+ onAnnotate?: ((annotation: DraftTimelineAnnotation) => void) | undefined;
158
178
  /** Older durable history exists above the current window (see useSessionEvents). */
159
179
  hasOlder?: boolean | undefined;
160
180
  /** An older window is being fetched; shows the quiet top shimmer. */
@@ -292,9 +312,12 @@ export function MessageTimeline({
292
312
  resolveProviderLogo,
293
313
  toolRegistry = defaultToolRegistry,
294
314
  loadRetainedScreenshot,
315
+ loadRetainedArtifact,
316
+ loadVideoArtifactPlayback,
295
317
  computeLabel = null,
296
318
  turnSummary,
297
319
  autoFollow = true,
320
+ onAnnotate,
298
321
  hasOlder = false,
299
322
  loadingOlder = false,
300
323
  onLoadOlder,
@@ -316,6 +339,20 @@ export function MessageTimeline({
316
339
  return projected.filter((item) => item.kind !== "auth-needed" || shouldRenderAuthNeeded(item));
317
340
  }, [items, events, shouldRenderAuthNeeded]);
318
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]);
319
356
  const groups = useStableTimelineGroupKeys(allGroups);
320
357
  const scrollRef = useRef<HTMLDivElement | null>(null);
321
358
  const topSentinelRef = useRef<HTMLDivElement | null>(null);
@@ -1282,6 +1319,15 @@ export function MessageTimeline({
1282
1319
  <EntranceAnimationProvider value={!bulkRender}>
1283
1320
  <TooltipProvider delayDuration={400}>
1284
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}
1285
1331
  {/* Pinned: anchoring off so the tip-follow camera owns the motion.
1286
1332
  Unpinned: native scroll anchoring holds the reader's place. */}
1287
1333
  <div
@@ -1346,6 +1392,8 @@ export function MessageTimeline({
1346
1392
  resolveProviderLogo,
1347
1393
  toolRegistry,
1348
1394
  loadRetainedScreenshot,
1395
+ loadRetainedArtifact,
1396
+ loadVideoArtifactPlayback,
1349
1397
  turnSummary,
1350
1398
  ]}
1351
1399
  >
@@ -1359,6 +1407,8 @@ export function MessageTimeline({
1359
1407
  resolveProviderLogo={resolveProviderLogo}
1360
1408
  toolRegistry={toolRegistry}
1361
1409
  loadRetainedScreenshot={loadRetainedScreenshot}
1410
+ loadRetainedArtifact={loadRetainedArtifact}
1411
+ loadVideoArtifactPlayback={loadVideoArtifactPlayback}
1362
1412
  turnSummary={turnSummary}
1363
1413
  foldLiveCluster={isAgentProgress(next)}
1364
1414
  trailingAgentText={trailingAgentTextAfterTurn(group, next)}
@@ -1703,6 +1753,8 @@ const TimelineGroupView = memo(function TimelineGroupView({
1703
1753
  resolveProviderLogo,
1704
1754
  toolRegistry,
1705
1755
  loadRetainedScreenshot,
1756
+ loadRetainedArtifact,
1757
+ loadVideoArtifactPlayback,
1706
1758
  turnSummary,
1707
1759
  insideTurn = false,
1708
1760
  nestClusterChips = false,
@@ -1720,6 +1772,8 @@ const TimelineGroupView = memo(function TimelineGroupView({
1720
1772
  resolveProviderLogo?: ((providerDomain: string) => string | null | undefined) | undefined;
1721
1773
  toolRegistry: ToolRegistry;
1722
1774
  loadRetainedScreenshot?: RetainedScreenshotLoader | undefined;
1775
+ loadRetainedArtifact?: RetainedArtifactLoader | undefined;
1776
+ loadVideoArtifactPlayback?: VideoArtifactPlaybackLoader | undefined;
1723
1777
  turnSummary?: TurnSummaryOptions | undefined;
1724
1778
  /** A completed cluster of a still-RUNNING turn (not the live tail) folds
1725
1779
  behind a neutral chip — the one place activity without an outcome still
@@ -1754,13 +1808,16 @@ const TimelineGroupView = memo(function TimelineGroupView({
1754
1808
  const activityShouldFold =
1755
1809
  group.kind === "activity" &&
1756
1810
  Boolean(group.outcome || (foldLiveCluster && clusterIsSettled(group)));
1811
+ const containsGeneratedImage = timelineGroupContainsGeneratedImage(group);
1757
1812
  // Latch live→folded so a top-level shell that was already mounted open can
1758
1813
  // start the settle beat without remounting bare rail → wrapper.
1759
1814
  const liveActivitySettle = useLiveSettleFold(activityShouldFold && !insideTurn);
1760
1815
  const turnDefaultOpen =
1761
1816
  !insideTurn &&
1762
1817
  group.kind === "turn" &&
1763
- (group.outcome === "failed" || timelineGroupContainsAuthNeeded(group));
1818
+ (group.outcome === "failed" ||
1819
+ timelineGroupContainsAuthNeeded(group) ||
1820
+ containsGeneratedImage);
1764
1821
  // activity-* → turn-* remount: carry resting state so settleFold does not
1765
1822
  // re-open a chip the reader already watched collapse.
1766
1823
  if (group.kind === "turn" && foldMemory && !insideTurn) {
@@ -1785,7 +1842,7 @@ const TimelineGroupView = memo(function TimelineGroupView({
1785
1842
  // settled closed pre-wrap was showing as a CHIP, so mounting it closed
1786
1843
  // is both the stable height and the honest state — force-opening it
1787
1844
  // was the "already-collapsed cluster auto-expands at the end" reopen.
1788
- const useNestedChip = nestClusterChips && activityShouldFold;
1845
+ const useNestedChip = nestClusterChips && activityShouldFold && !containsGeneratedImage;
1789
1846
  if (!useNestedChip) {
1790
1847
  return (
1791
1848
  <ActivityRail
@@ -1794,6 +1851,7 @@ const TimelineGroupView = memo(function TimelineGroupView({
1794
1851
  onMemoryClick={onMemoryClick}
1795
1852
  toolRegistry={toolRegistry}
1796
1853
  loadRetainedScreenshot={loadRetainedScreenshot}
1854
+ loadRetainedArtifact={loadRetainedArtifact}
1797
1855
  bare
1798
1856
  />
1799
1857
  );
@@ -1816,6 +1874,7 @@ const TimelineGroupView = memo(function TimelineGroupView({
1816
1874
  onMemoryClick={onMemoryClick}
1817
1875
  toolRegistry={toolRegistry}
1818
1876
  loadRetainedScreenshot={loadRetainedScreenshot}
1877
+ loadRetainedArtifact={loadRetainedArtifact}
1819
1878
  bare
1820
1879
  />
1821
1880
  </TurnSummary>
@@ -1842,6 +1901,7 @@ const TimelineGroupView = memo(function TimelineGroupView({
1842
1901
  onMemoryClick={onMemoryClick}
1843
1902
  toolRegistry={toolRegistry}
1844
1903
  loadRetainedScreenshot={loadRetainedScreenshot}
1904
+ loadRetainedArtifact={loadRetainedArtifact}
1845
1905
  bare
1846
1906
  />
1847
1907
  </TurnRailFrame>
@@ -1869,6 +1929,8 @@ const TimelineGroupView = memo(function TimelineGroupView({
1869
1929
  resolveProviderLogo,
1870
1930
  toolRegistry,
1871
1931
  loadRetainedScreenshot,
1932
+ loadRetainedArtifact,
1933
+ loadVideoArtifactPlayback,
1872
1934
  turnSummary,
1873
1935
  ]}
1874
1936
  >
@@ -1881,6 +1943,8 @@ const TimelineGroupView = memo(function TimelineGroupView({
1881
1943
  resolveProviderLogo={resolveProviderLogo}
1882
1944
  toolRegistry={toolRegistry}
1883
1945
  loadRetainedScreenshot={loadRetainedScreenshot}
1946
+ loadRetainedArtifact={loadRetainedArtifact}
1947
+ loadVideoArtifactPlayback={loadVideoArtifactPlayback}
1884
1948
  turnSummary={turnSummary}
1885
1949
  insideTurn
1886
1950
  nestClusterChips={nestClusters}
@@ -1922,6 +1986,7 @@ const TimelineGroupView = memo(function TimelineGroupView({
1922
1986
  onReconnect={onReconnect}
1923
1987
  resolveProviderLogo={resolveProviderLogo}
1924
1988
  onOpenSession={onOpenSession}
1989
+ loadVideoArtifactPlayback={loadVideoArtifactPlayback}
1925
1990
  />
1926
1991
  );
1927
1992
  }
@@ -1985,6 +2050,20 @@ function timelineGroupContainsAuthNeeded(group: TimelineGroup): boolean {
1985
2050
  }
1986
2051
  }
1987
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
+
1988
2067
  function timelineGroupItemIds(group: TimelineGroup): string[] {
1989
2068
  switch (group.kind) {
1990
2069
  case "item":
@@ -2156,6 +2235,7 @@ export function TimelineRow({
2156
2235
  onReconnect,
2157
2236
  resolveProviderLogo,
2158
2237
  onOpenSession,
2238
+ loadVideoArtifactPlayback,
2159
2239
  }: {
2160
2240
  item: TimelineItem;
2161
2241
  renderMessageText?:
@@ -2164,6 +2244,7 @@ export function TimelineRow({
2164
2244
  onReconnect?: ((item: AuthNeededItem) => void | Promise<void>) | undefined;
2165
2245
  resolveProviderLogo?: ((providerDomain: string) => string | null | undefined) | undefined;
2166
2246
  onOpenSession?: ((sessionId: string) => void) | undefined;
2247
+ loadVideoArtifactPlayback?: VideoArtifactPlaybackLoader | undefined;
2167
2248
  }) {
2168
2249
  switch (item.kind) {
2169
2250
  case "user-message":
@@ -2177,7 +2258,9 @@ export function TimelineRow({
2177
2258
  case "goal":
2178
2259
  return <GoalRow item={item} />;
2179
2260
  case "machine-input-batch":
2180
- return <MachineInputBatchRow item={item} />;
2261
+ return (
2262
+ <MachineInputBatchRow item={item} loadVideoArtifactPlayback={loadVideoArtifactPlayback} />
2263
+ );
2181
2264
  case "notice":
2182
2265
  return <NoticeRow item={item} />;
2183
2266
  case "context-compaction":
@@ -2297,25 +2380,71 @@ function UserMessageRow({
2297
2380
  | undefined;
2298
2381
  }) {
2299
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;
2300
2391
  return (
2301
2392
  <div className={cn(enter && "animate-og-enter", "flex justify-end")}>
2302
2393
  <div className="flex max-w-[85%] min-w-0 flex-col items-end gap-1">
2303
2394
  <CopyHoverFrame
2304
- copyText={item.text}
2395
+ copyText={
2396
+ item.text ||
2397
+ (item.annotations ?? [])
2398
+ .map((annotation) => `${annotation.quote}\n${annotation.note}`)
2399
+ .join("\n\n")
2400
+ }
2305
2401
  label="Copy message"
2306
2402
  className="w-fit max-w-full min-w-0"
2307
2403
  trailing={<MessageFooterTime occurredAt={item.occurredAt} />}
2308
2404
  >
2309
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">
2310
- {renderMessageText ? (
2311
- renderMessageText(item.text, item)
2312
- ) : (
2313
- <UserMessageBody messageId={item.id} text={item.text}>
2314
- <Markdown>{item.text}</Markdown>
2315
- </UserMessageBody>
2316
- )}
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}
2317
2423
  </div>
2318
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}
2319
2448
  </div>
2320
2449
  </div>
2321
2450
  );
@@ -2341,15 +2470,17 @@ function AgentMessageRow({
2341
2470
  // While streaming, copy is still useful (current text) but keep chrome calm —
2342
2471
  // stamp only after the message finishes (occurredAt tracks completion).
2343
2472
  return (
2344
- <CopyHoverFrame
2345
- copyText={item.text}
2346
- label="Copy message"
2347
- align="start"
2348
- className={cn(enter && "animate-og-enter", "min-w-0 text-og-md leading-7 text-og-fg")}
2349
- trailing={item.streaming ? null : <MessageFooterTime occurredAt={item.occurredAt} />}
2350
- >
2351
- {body}
2352
- </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>
2353
2484
  );
2354
2485
  }
2355
2486
 
@@ -2599,10 +2730,24 @@ function GoalRow({ item }: { item: GoalItem }) {
2599
2730
  );
2600
2731
  }
2601
2732
 
2602
- function MachineInputBatchRow({ item }: { item: MachineInputBatchItem }) {
2733
+ function MachineInputBatchRow({
2734
+ item,
2735
+ loadVideoArtifactPlayback,
2736
+ }: {
2737
+ item: MachineInputBatchItem;
2738
+ loadVideoArtifactPlayback?: VideoArtifactPlaybackLoader | undefined;
2739
+ }) {
2603
2740
  const enter = useEntranceAnimation();
2604
2741
  const label = machineInputBatchLabel(item.members);
2605
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
+ }
2606
2751
  const singleSummary = single ? cleanMachineInputSummary(single.summary) : "";
2607
2752
  const showCollapsedSummary =
2608
2753
  single != null && machineInputSummaryIsUseful(single.kind, singleSummary);
@@ -2626,7 +2771,11 @@ function MachineInputBatchRow({ item }: { item: MachineInputBatchItem }) {
2626
2771
  </summary>
2627
2772
  <div className="mx-auto mt-2 w-full max-w-lg space-y-2 border-t border-og-border/50 pt-2">
2628
2773
  {item.members.map((member) => (
2629
- <MachineInputRow key={member.id} member={member} />
2774
+ <MachineInputRow
2775
+ key={member.id}
2776
+ member={member}
2777
+ loadVideoArtifactPlayback={loadVideoArtifactPlayback}
2778
+ />
2630
2779
  ))}
2631
2780
  </div>
2632
2781
  </details>
@@ -2639,7 +2788,22 @@ function MachineInputBatchRow({ item }: { item: MachineInputBatchItem }) {
2639
2788
  );
2640
2789
  }
2641
2790
 
2642
- 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
+ }
2643
2807
  const source = readableMachineInputSource(member.sourceId);
2644
2808
  const summary = cleanMachineInputSummary(member.summary);
2645
2809
  return (
@@ -2660,6 +2824,72 @@ function MachineInputRow({ member }: { member: MachineInputBatchItem["members"][
2660
2824
  );
2661
2825
  }
2662
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
+
2663
2893
  function NoticeRow({ item }: { item: NoticeItem }) {
2664
2894
  const enter = useEntranceAnimation();
2665
2895
  const tone =
@@ -2725,14 +2955,32 @@ function AuthNeededRow({
2725
2955
  const enter = useEntranceAnimation();
2726
2956
  const [busy, setBusy] = useState(false);
2727
2957
  const [failed, setFailed] = useState(false);
2958
+ const recommendation = item.capability ?? null;
2728
2959
  const provider =
2729
- item.serverId === "codex_apps" ? "Codex Apps" : providerLabel(item.providerDomain);
2960
+ recommendation?.name ??
2961
+ (item.serverId === "codex_apps" ? "Codex Apps" : providerLabel(item.providerDomain));
2730
2962
  const unavailable =
2731
2963
  item.reason === "personal_authority_unavailable" ||
2732
2964
  item.reason === "unsupported_auth" ||
2733
2965
  item.reason === "resource_scope_unavailable";
2734
2966
  const missing = item.reason === "missing_connection";
2735
- 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);
2736
2984
 
2737
2985
  const start = async () => {
2738
2986
  if (!onReconnect || busy) {
@@ -2761,10 +3009,18 @@ function AuthNeededRow({
2761
3009
  label={provider}
2762
3010
  />
2763
3011
  <div className="min-w-0">
2764
- <p className="truncate text-og-md font-medium text-og-fg">
2765
- {unavailable ? `${provider} tools unavailable` : `${actionLabel} ${provider}`}
2766
- </p>
2767
- <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}
2768
3024
  </div>
2769
3025
  </div>
2770
3026
  {!unavailable && onReconnect ? (
@@ -2797,8 +3053,9 @@ function AuthNeededRow({
2797
3053
  </div>
2798
3054
  {!unavailable ? (
2799
3055
  <p className="px-1 text-og-xs text-og-fg-subtle">
2800
- This tool call wasn't replayed. After {missing ? "connecting" : "reconnecting"}, send a
2801
- 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.`}
2802
3059
  </p>
2803
3060
  ) : null}
2804
3061
  {failed ? (
@@ -65,6 +65,7 @@ export const defaultModelPolicyPickerMessages: ModelPolicyPickerMessages = {
65
65
  billingHints: {
66
66
  opengeni_credits: "Will use credits",
67
67
  codex_subscription: "ChatGPT / Codex plan",
68
+ supergrok_subscription: "SuperGrok / xAI plan",
68
69
  byok: "Billed to your AI Gateway",
69
70
  },
70
71
  };
@@ -132,6 +133,17 @@ function ChatGptMark(props: SVGProps<SVGSVGElement>) {
132
133
  );
133
134
  }
134
135
 
136
+ function XaiMark(props: SVGProps<SVGSVGElement>) {
137
+ return (
138
+ <svg viewBox="0 0 466.04 516.93" fill="currentColor" aria-hidden="true" {...props}>
139
+ <polygon points="0.12 182.71 234.14 516.92 338.15 516.92 104.13 182.71 0.12 182.71" />
140
+ <polygon points="0 516.92 104.08 516.92 156.08 442.67 104.04 368.34 0 516.92" />
141
+ <polygon points="466.04 0 361.96 0 182.1 256.86 234.15 331.18 466.04 0" />
142
+ <polygon points="380.78 516.92 466.04 516.92 466.04 37.16 380.78 158.92 380.78 516.92" />
143
+ </svg>
144
+ );
145
+ }
146
+
135
147
  export function BillingClassMark(props: {
136
148
  billingClass: PickerBillingClass;
137
149
  className?: string | undefined;
@@ -140,6 +152,7 @@ export function BillingClassMark(props: {
140
152
  const labels: Record<PickerBillingClass, string> = {
141
153
  opengeni_credits: "OpenGeni",
142
154
  codex_subscription: "Codex",
155
+ supergrok_subscription: "SuperGrok",
143
156
  byok: "Bring your own key",
144
157
  };
145
158
  const label = props["aria-label"] ?? labels[props.billingClass];
@@ -162,6 +175,8 @@ export function BillingClassMark(props: {
162
175
  <OpenGeniMark className={mark} />
163
176
  ) : props.billingClass === "codex_subscription" ? (
164
177
  <ChatGptMark className={mark} />
178
+ ) : props.billingClass === "supergrok_subscription" ? (
179
+ <XaiMark className={mark} />
165
180
  ) : (
166
181
  <KeyRoundIcon className={mark} aria-hidden />
167
182
  )}