@opengeni/react 0.48.0 → 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 (284) hide show
  1. package/README.md +90 -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 +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-FWXGSNXT.js → chunk-724BW4TW.js} +585 -34
  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-I4MPFDRP.js → chunk-FWS3KC3Z.js} +3 -3
  23. package/dist/{chunk-EKZH6IDG.js → chunk-HDBG7XGO.js} +62 -1
  24. package/dist/chunk-HDBG7XGO.js.map +1 -0
  25. package/dist/chunk-HUIKIDM5.js +2148 -0
  26. package/dist/chunk-HUIKIDM5.js.map +1 -0
  27. package/dist/{chunk-XCNTXWBB.js → chunk-JAGDUCZQ.js} +854 -340
  28. package/dist/chunk-JAGDUCZQ.js.map +1 -0
  29. package/dist/{chunk-LHLYWG5R.js → chunk-JR3QFY6B.js} +413 -37
  30. package/dist/chunk-JR3QFY6B.js.map +1 -0
  31. package/dist/chunk-JVYQRRC4.js +53 -0
  32. package/dist/chunk-JVYQRRC4.js.map +1 -0
  33. package/dist/chunk-KCGXPP5V.js +157 -0
  34. package/dist/chunk-KCGXPP5V.js.map +1 -0
  35. package/dist/chunk-PEI6RANI.js +3234 -0
  36. package/dist/chunk-PEI6RANI.js.map +1 -0
  37. package/dist/{chunk-NJ7M2B6Z.js → chunk-ROIRTM37.js} +18 -10
  38. package/dist/{chunk-NJ7M2B6Z.js.map → chunk-ROIRTM37.js.map} +1 -1
  39. package/dist/{chunk-O4VXOBGF.js → chunk-WSK7G5LE.js} +70 -2
  40. package/dist/chunk-WSK7G5LE.js.map +1 -0
  41. package/dist/{chunk-OL6QVXGF.js → chunk-X4S56Y2H.js} +27 -7
  42. package/dist/{chunk-OL6QVXGF.js.map → chunk-X4S56Y2H.js.map} +1 -1
  43. package/dist/client.d.ts +7 -1
  44. package/dist/clipboard.d.ts +1 -0
  45. package/dist/clipboard.js +7 -0
  46. package/dist/clipboard.js.map +1 -0
  47. package/dist/commands/index.d.ts +3 -3
  48. package/dist/commands/registry.d.ts +1 -1
  49. package/dist/commands/types.d.ts +1 -1
  50. package/dist/components/approval-surface.d.ts +2 -2
  51. package/dist/components/artifacts/artifact-surface.d.ts +18 -0
  52. package/dist/components/artifacts/document-editor.d.ts +172 -0
  53. package/dist/components/artifacts/editable-artifact-ui.d.ts +13 -0
  54. package/dist/components/artifacts/editable-artifact-workbench.d.ts +49 -0
  55. package/dist/components/artifacts/editable-document.d.ts +24 -0
  56. package/dist/components/artifacts/editable-presentation.d.ts +24 -0
  57. package/dist/components/artifacts/editable-spreadsheet.d.ts +32 -0
  58. package/dist/components/artifacts/index.d.ts +8 -0
  59. package/dist/components/artifacts/presentation-editor.d.ts +297 -0
  60. package/dist/components/artifacts/spreadsheet-canvas.d.ts +120 -0
  61. package/dist/components/artifacts/spreadsheet-grid.d.ts +113 -0
  62. package/dist/components/browser-viewer.d.ts +29 -0
  63. package/dist/components/chat-composer.d.ts +8 -8
  64. package/dist/components/code-editor.d.ts +25 -3
  65. package/dist/components/command-palette.d.ts +2 -2
  66. package/dist/components/composer-transcription-control.d.ts +2 -2
  67. package/dist/components/composer.d.ts +17 -17
  68. package/dist/components/computer-viewer.d.ts +23 -0
  69. package/dist/components/copy-button.d.ts +2 -2
  70. package/dist/components/desktop-viewer.d.ts +25 -21
  71. package/dist/components/diff-view.d.ts +1 -1
  72. package/dist/components/enrollment-consent.d.ts +1 -1
  73. package/dist/components/enrollment-device-flow.d.ts +1 -1
  74. package/dist/components/file-browser.d.ts +2 -2
  75. package/dist/components/fleet-tile.d.ts +1 -1
  76. package/dist/components/generated-video-player.d.ts +10 -0
  77. package/dist/components/human-input-form.d.ts +1 -1
  78. package/dist/components/human-input-surface.d.ts +2 -2
  79. package/dist/components/interactive-workbench-viewers.d.ts +2 -0
  80. package/dist/components/machine-card.d.ts +2 -2
  81. package/dist/components/machine-dock-bar.d.ts +3 -3
  82. package/dist/components/machine-health-pill.d.ts +2 -2
  83. package/dist/components/machine-input-display.d.ts +1 -1
  84. package/dist/components/machine-metrics.d.ts +2 -2
  85. package/dist/components/machine-status-pill.d.ts +4 -4
  86. package/dist/components/machines/health.d.ts +1 -1
  87. package/dist/components/machines/machine-detail.d.ts +3 -3
  88. package/dist/components/machines/metric-history-chart.d.ts +1 -1
  89. package/dist/components/machines/metric-sparkline.d.ts +2 -2
  90. package/dist/components/machines/series.d.ts +2 -2
  91. package/dist/components/machines-dashboard.d.ts +2 -2
  92. package/dist/components/markdown.d.ts +1 -1
  93. package/dist/components/message-timeline.d.ts +12 -5
  94. package/dist/components/model-picker.d.ts +2 -2
  95. package/dist/components/model-policy-picker.d.ts +7 -7
  96. package/dist/components/pierre-diff.d.ts +1 -1
  97. package/dist/components/pierre-file.d.ts +1 -1
  98. package/dist/components/queue-draft-policy.d.ts +1 -1
  99. package/dist/components/queue-surface-implementation.d.ts +3 -3
  100. package/dist/components/queue-surface-state.d.ts +4 -4
  101. package/dist/components/queue-surface.d.ts +5 -5
  102. package/dist/components/sandbox-files.d.ts +3 -3
  103. package/dist/components/sandbox-terminal.d.ts +21 -4
  104. package/dist/components/sandbox-workspace.d.ts +25 -9
  105. package/dist/components/session-chrome.d.ts +4 -4
  106. package/dist/components/session-status.d.ts +2 -2
  107. package/dist/components/timeline-annotation-selection.d.ts +9 -0
  108. package/dist/components/timeline-annotations-dialog.d.ts +16 -0
  109. package/dist/components/timeline-annotations.d.ts +13 -0
  110. package/dist/components/tooltip.d.ts +4 -4
  111. package/dist/components/user-message-body.d.ts +2 -2
  112. package/dist/components/workbench-changes.d.ts +2 -2
  113. package/dist/components/workspace-dock.d.ts +1 -1
  114. package/dist/composer.d.ts +11 -11
  115. package/dist/composer.js +5 -5
  116. package/dist/hooks/internal.d.ts +1 -1
  117. package/dist/hooks/use-attached-browsers.d.ts +21 -0
  118. package/dist/hooks/use-available-models.d.ts +2 -2
  119. package/dist/hooks/use-billing-usage.d.ts +1 -1
  120. package/dist/hooks/use-browser-frame-stream.d.ts +28 -0
  121. package/dist/hooks/use-browser-identities.d.ts +26 -0
  122. package/dist/hooks/use-browser-session.d.ts +28 -0
  123. package/dist/hooks/use-browser-sessions.d.ts +32 -0
  124. package/dist/hooks/use-codex-accounts.d.ts +2 -2
  125. package/dist/hooks/use-composer.d.ts +36 -9
  126. package/dist/hooks/use-computer-frame-stream.d.ts +24 -0
  127. package/dist/hooks/use-computer-session.d.ts +28 -0
  128. package/dist/hooks/use-computer-sessions.d.ts +28 -0
  129. package/dist/hooks/use-desktop-stream.d.ts +1 -1
  130. package/dist/hooks/use-environments.d.ts +1 -1
  131. package/dist/hooks/use-file-attachments.d.ts +1 -1
  132. package/dist/hooks/use-goal.d.ts +2 -2
  133. package/dist/hooks/use-human-input.d.ts +2 -2
  134. package/dist/hooks/use-last-started-turn-policy.d.ts +2 -2
  135. package/dist/hooks/use-machine-chip.d.ts +2 -2
  136. package/dist/hooks/use-machines.d.ts +4 -4
  137. package/dist/hooks/use-packs.d.ts +7 -2
  138. package/dist/hooks/use-rigs.d.ts +1 -1
  139. package/dist/hooks/use-sandbox-files.d.ts +6 -4
  140. package/dist/hooks/use-sandbox-git.d.ts +6 -5
  141. package/dist/hooks/use-sandbox-terminal.d.ts +1 -1
  142. package/dist/hooks/use-scheduled-tasks.d.ts +1 -1
  143. package/dist/hooks/use-session-capabilities.d.ts +5 -4
  144. package/dist/hooks/use-session-control.d.ts +1 -1
  145. package/dist/hooks/use-session-events.d.ts +2 -2
  146. package/dist/hooks/use-session-lineage.d.ts +1 -1
  147. package/dist/hooks/use-session-mcp-approval-policy.d.ts +2 -2
  148. package/dist/hooks/use-session.d.ts +2 -2
  149. package/dist/hooks/use-slash-commands.d.ts +1 -1
  150. package/dist/hooks/use-terminal-stream.d.ts +8 -2
  151. package/dist/hooks/use-turn-queue.d.ts +2 -2
  152. package/dist/hooks/use-video-artifact-playback.d.ts +9 -0
  153. package/dist/hooks/use-voice-input.d.ts +1 -1
  154. package/dist/hooks/use-workspace-capture.d.ts +1 -1
  155. package/dist/hooks/use-workspace-edit.d.ts +1 -1
  156. package/dist/hooks/use-workspace-sessions.d.ts +1 -1
  157. package/dist/hooks/use-workspaces.d.ts +1 -1
  158. package/dist/index.d.ts +158 -155
  159. package/dist/index.js +906 -462
  160. package/dist/index.js.map +1 -1
  161. package/dist/interaction.d.ts +23 -0
  162. package/dist/interaction.js +36 -0
  163. package/dist/interaction.js.map +1 -0
  164. package/dist/interactive-workbench-viewers-KHNOHWQE.js +13 -0
  165. package/dist/interactive-workbench-viewers-KHNOHWQE.js.map +1 -0
  166. package/dist/lib/relay-wire.d.ts +4 -0
  167. package/dist/lib/sandbox-liveness.d.ts +9 -0
  168. package/dist/lib/terminal-capability.d.ts +5 -0
  169. package/dist/lib/xterm-theme.d.ts +1 -1
  170. package/dist/machines.d.ts +30 -30
  171. package/dist/machines.js +3 -3
  172. package/dist/provider.d.ts +4 -4
  173. package/dist/{queue-surface-implementation-U5JFG7HT.js → queue-surface-implementation-2KVJTYXH.js} +100 -48
  174. package/dist/queue-surface-implementation-2KVJTYXH.js.map +1 -0
  175. package/dist/realtime/dropdown-menu.d.ts +8 -8
  176. package/dist/realtime/realtime-control.d.ts +6 -6
  177. package/dist/realtime.d.ts +4 -4
  178. package/dist/realtime.js +3 -3
  179. package/dist/session-context.d.ts +19 -1
  180. package/dist/session-ui.d.ts +14 -14
  181. package/dist/session-ui.js +5 -4
  182. package/dist/session.d.ts +28 -28
  183. package/dist/session.js +5 -5
  184. package/dist/timeline/activity-rail.d.ts +5 -3
  185. package/dist/timeline/disclosure-context.d.ts +1 -1
  186. package/dist/timeline/fleet-decision-projection.d.ts +1 -1
  187. package/dist/timeline/fleet-decision-row.d.ts +3 -3
  188. package/dist/timeline/index.d.ts +19 -19
  189. package/dist/timeline/parsers.d.ts +4 -1
  190. package/dist/timeline/projection.d.ts +3 -3
  191. package/dist/timeline/registry.d.ts +8 -2
  192. package/dist/timeline/screenshot-lightbox.d.ts +2 -2
  193. package/dist/timeline/shared.d.ts +21 -10
  194. package/dist/timeline/tool-diff.d.ts +2 -2
  195. package/dist/timeline/tool-renderers.d.ts +1 -1
  196. package/dist/timeline/turn-summary.d.ts +3 -3
  197. package/dist/timeline/types.d.ts +39 -3
  198. package/dist/timeline-annotation-selection-3L7LHGG2.js +187 -0
  199. package/dist/timeline-annotation-selection-3L7LHGG2.js.map +1 -0
  200. package/dist/timeline-annotations-dialog-U7EVLR75.js +196 -0
  201. package/dist/timeline-annotations-dialog-U7EVLR75.js.map +1 -0
  202. package/package.json +36 -7
  203. package/src/artifacts-document.ts +31 -0
  204. package/src/artifacts-presentation.ts +36 -0
  205. package/src/artifacts-spreadsheet.ts +21 -0
  206. package/src/artifacts.ts +86 -0
  207. package/src/client.ts +87 -1
  208. package/src/clipboard.ts +2 -0
  209. package/src/components/artifacts/artifact-surface.tsx +93 -0
  210. package/src/components/artifacts/document-editor.tsx +2255 -0
  211. package/src/components/artifacts/editable-artifact-ui.tsx +89 -0
  212. package/src/components/artifacts/editable-artifact-workbench.tsx +203 -0
  213. package/src/components/artifacts/editable-document.tsx +657 -0
  214. package/src/components/artifacts/editable-presentation.tsx +1020 -0
  215. package/src/components/artifacts/editable-spreadsheet.tsx +733 -0
  216. package/src/components/artifacts/index.ts +99 -0
  217. package/src/components/artifacts/presentation-editor.tsx +3148 -0
  218. package/src/components/artifacts/spreadsheet-canvas.ts +799 -0
  219. package/src/components/artifacts/spreadsheet-grid.tsx +2129 -0
  220. package/src/components/browser-viewer.tsx +1592 -0
  221. package/src/components/chat-composer.tsx +13 -0
  222. package/src/components/code-editor.tsx +89 -30
  223. package/src/components/composer-transcription-control.tsx +10 -6
  224. package/src/components/computer-viewer.tsx +1139 -0
  225. package/src/components/desktop-viewer.tsx +101 -73
  226. package/src/components/generated-video-player.tsx +89 -0
  227. package/src/components/interactive-workbench-viewers.ts +2 -0
  228. package/src/components/machine-input-display.ts +3 -0
  229. package/src/components/message-timeline.tsx +289 -32
  230. package/src/components/queue-surface-implementation.tsx +116 -60
  231. package/src/components/sandbox-files.tsx +2 -2
  232. package/src/components/sandbox-terminal.tsx +106 -41
  233. package/src/components/sandbox-workspace.tsx +215 -87
  234. package/src/components/session-chrome.tsx +53 -13
  235. package/src/components/timeline-annotation-selection.tsx +221 -0
  236. package/src/components/timeline-annotations-dialog.tsx +213 -0
  237. package/src/components/timeline-annotations.tsx +84 -0
  238. package/src/components/workbench-changes.tsx +188 -31
  239. package/src/hooks/use-attached-browsers.ts +152 -0
  240. package/src/hooks/use-browser-frame-stream.ts +316 -0
  241. package/src/hooks/use-browser-identities.ts +225 -0
  242. package/src/hooks/use-browser-session.ts +476 -0
  243. package/src/hooks/use-browser-sessions.ts +281 -0
  244. package/src/hooks/use-composer.ts +769 -24
  245. package/src/hooks/use-computer-frame-stream.ts +311 -0
  246. package/src/hooks/use-computer-session.ts +442 -0
  247. package/src/hooks/use-computer-sessions.ts +245 -0
  248. package/src/hooks/use-machine-chip.ts +5 -3
  249. package/src/hooks/use-machines.ts +33 -3
  250. package/src/hooks/use-packs.ts +51 -0
  251. package/src/hooks/use-sandbox-files.ts +98 -55
  252. package/src/hooks/use-sandbox-git.ts +93 -38
  253. package/src/hooks/use-sandbox-terminal.ts +3 -2
  254. package/src/hooks/use-session-capabilities.ts +59 -46
  255. package/src/hooks/use-terminal-stream.ts +102 -23
  256. package/src/hooks/use-video-artifact-playback.ts +28 -0
  257. package/src/hooks/use-workspace-edit.ts +2 -1
  258. package/src/index.ts +10 -0
  259. package/src/interaction.ts +71 -0
  260. package/src/lib/relay-wire.ts +4 -0
  261. package/src/lib/sandbox-liveness.ts +11 -0
  262. package/src/lib/terminal-capability.ts +10 -0
  263. package/src/session-context.ts +90 -0
  264. package/src/timeline/activity-rail.tsx +19 -3
  265. package/src/timeline/index.ts +3 -0
  266. package/src/timeline/parsers.ts +14 -1
  267. package/src/timeline/projection.ts +211 -14
  268. package/src/timeline/registry.ts +13 -1
  269. package/src/timeline/screenshot-lightbox.tsx +19 -9
  270. package/src/timeline/shared.tsx +56 -8
  271. package/src/timeline/tool-renderers.tsx +303 -50
  272. package/src/timeline/types.ts +55 -3
  273. package/styles/compiled.css +248 -15
  274. package/styles/index.css +15 -0
  275. package/styles/responsive.css +13 -1
  276. package/dist/chunk-EKZH6IDG.js.map +0 -1
  277. package/dist/chunk-FWXGSNXT.js.map +0 -1
  278. package/dist/chunk-LHLYWG5R.js.map +0 -1
  279. package/dist/chunk-O4VXOBGF.js.map +0 -1
  280. package/dist/chunk-XCNTXWBB.js.map +0 -1
  281. package/dist/fleet-decision-projection-SLNZMS42.js +0 -241
  282. package/dist/fleet-decision-projection-SLNZMS42.js.map +0 -1
  283. package/dist/queue-surface-implementation-U5JFG7HT.js.map +0 -1
  284. /package/dist/{chunk-I4MPFDRP.js.map → chunk-FWS3KC3Z.js.map} +0 -0
@@ -0,0 +1,10 @@
1
+ import type { TerminalCapability } from "@opengeni/sdk";
2
+
3
+ /** Whether an explicit viewer grant can upgrade this cell to a real PTY.
4
+ * Keep acquisition feasibility separate from the current transport: a cold
5
+ * real-PTY backend reports sse-events until the just-in-time grant warms it. */
6
+ export function terminalCanAcquirePty(cell: TerminalCapability | null | undefined): boolean {
7
+ if (!cell?.ptyCapable) return false;
8
+ if (cell.transport === "pty-ws") return true;
9
+ return cell.reason === "lease_cold" || cell.reason === "not_provisioned";
10
+ }
@@ -4,6 +4,9 @@ import type {
4
4
  EmbeddedFileAttachmentClientLike,
5
5
  EmbeddedGoalClientLike,
6
6
  EmbeddedHumanInputSessionClientLike,
7
+ EmbeddedBrowserInteractionClientLike,
8
+ EmbeddedComputerInteractionClientLike,
9
+ EmbeddedInteractionClientLike,
7
10
  EmbeddedRealtimeSessionClientLike,
8
11
  EmbeddedSessionClientLike,
9
12
  EmbeddedSessionLineageClientLike,
@@ -76,6 +79,21 @@ export type EmbeddedFileAttachmentClientOverride = {
76
79
  workspaceId?: string | undefined;
77
80
  };
78
81
 
82
+ export type EmbeddedInteractionClientOverride = {
83
+ client?: EmbeddedInteractionClientLike | undefined;
84
+ workspaceId?: string | undefined;
85
+ };
86
+
87
+ export type EmbeddedBrowserInteractionClientOverride = {
88
+ client?: EmbeddedBrowserInteractionClientLike | undefined;
89
+ workspaceId?: string | undefined;
90
+ };
91
+
92
+ export type EmbeddedComputerInteractionClientOverride = {
93
+ client?: EmbeddedComputerInteractionClientLike | undefined;
94
+ workspaceId?: string | undefined;
95
+ };
96
+
79
97
  export type EmbeddedSessionContextValue = Omit<OpenGeniContextValue, "client"> & {
80
98
  client: EmbeddedSessionClientLike;
81
99
  };
@@ -145,6 +163,78 @@ export function useOpenGeni(override: ClientOverride = {}): OpenGeniContextValue
145
163
  };
146
164
  }
147
165
 
166
+ const BROWSER_INTERACTION_METHODS = [
167
+ "listAttachedBrowsers",
168
+ "getAttachedBrowser",
169
+ "listBrowserIdentities",
170
+ "getBrowserIdentity",
171
+ "createBrowserIdentity",
172
+ "listBrowserRevisions",
173
+ "listBrowserSessions",
174
+ "getBrowserSession",
175
+ "createBrowserSession",
176
+ "listBrowserTargets",
177
+ "openBrowserTarget",
178
+ "selectBrowserTarget",
179
+ "closeBrowserTarget",
180
+ "observeBrowserTarget",
181
+ "actInBrowser",
182
+ "getBrowserActionReceipt",
183
+ "listBrowserDiagnostics",
184
+ "attachBrowserSession",
185
+ "heartbeatBrowserSession",
186
+ "publishBrowserRevision",
187
+ "suspendBrowserSession",
188
+ "resumeBrowserSession",
189
+ "endBrowserSession",
190
+ ] as const;
191
+
192
+ const COMPUTER_INTERACTION_METHODS = [
193
+ "listComputerSessions",
194
+ "getComputerSession",
195
+ "createComputerSession",
196
+ "listComputerTargets",
197
+ "observeComputerTarget",
198
+ "actInComputer",
199
+ "getComputerActionReceipt",
200
+ "attachComputerSession",
201
+ "heartbeatComputerSession",
202
+ "endComputerSession",
203
+ ] as const;
204
+
205
+ /** Resolve only BrowserSession methods for a standalone Browser embed. */
206
+ export function useEmbeddedBrowserInteraction(
207
+ override: EmbeddedBrowserInteractionClientOverride = {},
208
+ ): EmbeddedClientContextValue<EmbeddedBrowserInteractionClientLike> {
209
+ return useEmbeddedClientRefinement(
210
+ override,
211
+ BROWSER_INTERACTION_METHODS,
212
+ "browser interaction hooks",
213
+ );
214
+ }
215
+
216
+ /** Resolve only ComputerSession methods for a standalone Computer embed. */
217
+ export function useEmbeddedComputerInteraction(
218
+ override: EmbeddedComputerInteractionClientOverride = {},
219
+ ): EmbeddedClientContextValue<EmbeddedComputerInteractionClientLike> {
220
+ return useEmbeddedClientRefinement(
221
+ override,
222
+ COMPUTER_INTERACTION_METHODS,
223
+ "computer interaction hooks",
224
+ );
225
+ }
226
+
227
+ /** Resolve the complete Browser + Computer client surface. */
228
+ export function useEmbeddedInteraction(
229
+ override: EmbeddedInteractionClientOverride = {},
230
+ ): EmbeddedClientContextValue<EmbeddedInteractionClientLike> {
231
+ return useEmbeddedClientRefinement(
232
+ override,
233
+ [...BROWSER_INTERACTION_METHODS, ...COMPUTER_INTERACTION_METHODS],
234
+ "interaction hooks",
235
+ );
236
+ }
237
+
148
238
  /**
149
239
  * Resolve the narrow client required by the session-only hooks. The full
150
240
  * provider client is structurally compatible, while an explicit host proxy
@@ -12,9 +12,9 @@ import { cn } from "../lib/cn";
12
12
  import { truncate } from "../lib/format";
13
13
  import { defaultToolRegistry } from "./tool-renderers";
14
14
  import { useEntranceAnimation, useEntranceAnimationLive } from "./entrance";
15
- import type { RetainedScreenshotLoader, ToolRegistry } from "./registry";
15
+ import type { RetainedArtifactLoader, RetainedScreenshotLoader, ToolRegistry } from "./registry";
16
16
  import { useSeenActivityIds } from "./seen-activity-ids";
17
- import { BodyNote, PayloadBlock, ActivityDisclosure } from "./shared";
17
+ import { BodyNote, PayloadBlock, ActivityDisclosure, ToolCallTruncationProvider } from "./shared";
18
18
  import { toolDisplayName } from "./tool-display-name";
19
19
  import type { ActivityItem, MemoryItem, ReasoningItem, SandboxItem, WorkerItem } from "./types";
20
20
 
@@ -44,6 +44,8 @@ export type ActivityRailProps = {
44
44
  */
45
45
  onMemoryClick?: ((memoryId: string) => void) | undefined;
46
46
  loadRetainedScreenshot?: RetainedScreenshotLoader | undefined;
47
+ /** Resolve permanent generated-image receipts through the authenticated host SDK. */
48
+ loadRetainedArtifact?: RetainedArtifactLoader | undefined;
47
49
  /** Drop the left rule + indent (used inside a folded turn summary). */
48
50
  bare?: boolean | undefined;
49
51
  className?: string | undefined;
@@ -67,6 +69,7 @@ export function ActivityRail({
67
69
  onOpenSession,
68
70
  onMemoryClick,
69
71
  loadRetainedScreenshot,
72
+ loadRetainedArtifact,
70
73
  bare,
71
74
  className,
72
75
  }: ActivityRailProps) {
@@ -120,11 +123,15 @@ export function ActivityRail({
120
123
  onOpenSession,
121
124
  onMemoryClick,
122
125
  loadRetainedScreenshot,
126
+ loadRetainedArtifact,
123
127
  );
124
128
  return (
125
129
  <div
126
130
  key={item.id}
127
131
  data-og-timeline-row-anchor=""
132
+ data-og-annotation-source-key={
133
+ item.kind === "tool-call" ? item.annotationSource?.eventId : undefined
134
+ }
128
135
  className={cn(newFamily && "mt-3", enteringIds.has(item.id) && "animate-og-row-enter")}
129
136
  >
130
137
  {row}
@@ -146,13 +153,22 @@ function renderActivity(
146
153
  onOpenSession: ((sessionId: string) => void) | undefined,
147
154
  onMemoryClick: ((memoryId: string) => void) | undefined,
148
155
  loadRetainedScreenshot: RetainedScreenshotLoader | undefined,
156
+ loadRetainedArtifact: RetainedArtifactLoader | undefined,
149
157
  ) {
150
158
  switch (item.kind) {
151
159
  case "reasoning":
152
160
  return <ReasoningRow item={item} />;
153
161
  case "tool-call": {
154
162
  const Renderer = toolRegistry.resolve(item);
155
- return <Renderer item={item} loadRetainedScreenshot={loadRetainedScreenshot} />;
163
+ return (
164
+ <ToolCallTruncationProvider value={item.truncation ?? null}>
165
+ <Renderer
166
+ item={item}
167
+ loadRetainedScreenshot={loadRetainedScreenshot}
168
+ loadRetainedArtifact={loadRetainedArtifact}
169
+ />
170
+ </ToolCallTruncationProvider>
171
+ );
156
172
  }
157
173
  case "worker":
158
174
  return <WorkerRow item={item} onOpenSession={onOpenSession} />;
@@ -45,6 +45,7 @@ export type {
45
45
  SessionStatusItem,
46
46
  TimelineGroup,
47
47
  TimelineItem,
48
+ TimelineAnnotationSourceDescriptor,
48
49
  TurnEndItem,
49
50
  ToolCallItem,
50
51
  UserMessageItem,
@@ -56,7 +57,9 @@ export type {
56
57
  export { createToolRegistry, rawTypeOf } from "./registry";
57
58
  export type {
58
59
  CreateToolRegistryOptions,
60
+ RetainedArtifactLoader,
59
61
  RetainedScreenshotLoader,
62
+ VideoArtifactPlaybackLoader,
60
63
  ToolRegistry,
61
64
  ToolRegistryEntry,
62
65
  ToolRenderer,
@@ -1,6 +1,14 @@
1
- import { normalizeMcpOutput, type GitFileDiff, type RetainedArtifactMetadata } from "@opengeni/sdk";
1
+ import {
2
+ normalizeMcpOutput,
3
+ parseGeneratedImageReceipt,
4
+ type GeneratedImageReceipt,
5
+ type GitFileDiff,
6
+ type RetainedArtifactMetadata,
7
+ } from "@opengeni/sdk";
2
8
  import { tryParseJson } from "../lib/format";
3
9
 
10
+ export type { GeneratedImageReceipt };
11
+
4
12
  /* ----------------------------------------------------------------------------
5
13
  Pure parsers for the provider-native tool shapes that the timeline renders.
6
14
 
@@ -571,6 +579,11 @@ export function retainedScreenshotMetadata(out: unknown): RetainedArtifactMetada
571
579
  : null;
572
580
  }
573
581
 
582
+ /** Parse the closed permanent generated-image receipt from native/function tools. */
583
+ export function generatedImageReceipt(out: unknown): GeneratedImageReceipt | null {
584
+ return parseGeneratedImageReceipt(out);
585
+ }
586
+
574
587
  export type TimelineMediaPreview = {
575
588
  type: "media_preview";
576
589
  mediaType: string;
@@ -1,5 +1,10 @@
1
- import type { SessionEvent, SessionStatus } from "@opengeni/sdk";
2
- const { default: fleetDecisionItem } = await import("./fleet-decision-projection");
1
+ import {
2
+ parseMediaGenerationResult,
3
+ type SessionEvent,
4
+ type SessionStatus,
5
+ type TimelineAnnotation,
6
+ } from "@opengeni/sdk";
7
+ import fleetDecisionItem from "./fleet-decision-projection";
3
8
  import {
4
9
  CREDIT_EXHAUSTION_MESSAGE,
5
10
  humanizeFailureReason,
@@ -21,6 +26,7 @@ import type {
21
26
  TimelineItem,
22
27
  TurnEndItem,
23
28
  ToolCallItem,
29
+ ToolCallTruncation,
24
30
  WorkerItem,
25
31
  } from "./types";
26
32
 
@@ -61,6 +67,56 @@ const WORKER_MESSAGE_TOOL = "session_send_message";
61
67
  * render as machine-input rows.
62
68
  */
63
69
  const LANDMARK_ONLY_TOOL_LEAVES = new Set(["memory_save", "memory_correct"]);
70
+ const TIMELINE_ANNOTATION_ANSI_SEQUENCE = new RegExp("\\u001B\\[[0-?]*[ -/]*[@-~]", "g");
71
+
72
+ function safeTimelineAnnotationJson(value: unknown): string {
73
+ try {
74
+ return JSON.stringify(value) ?? "";
75
+ } catch {
76
+ return String(value ?? "");
77
+ }
78
+ }
79
+
80
+ function timelineAnnotationToolOutputValue(value: unknown, depth = 0): string {
81
+ if (depth > 8 || value === null || value === undefined) {
82
+ return value == null ? "" : safeTimelineAnnotationJson(value);
83
+ }
84
+ if (typeof value === "string") return value;
85
+ if (typeof value !== "object" || Array.isArray(value)) {
86
+ return safeTimelineAnnotationJson(value);
87
+ }
88
+ const record = value as Record<string, unknown>;
89
+ if (record.type === "text" && typeof record.text === "string") return record.text;
90
+ if (Array.isArray(record.content)) {
91
+ const text = record.content.find(
92
+ (part) =>
93
+ part !== null &&
94
+ typeof part === "object" &&
95
+ (part as { type?: unknown }).type === "text" &&
96
+ typeof (part as { text?: unknown }).text === "string",
97
+ ) as { text: string } | undefined;
98
+ if (text) return text.text;
99
+ }
100
+ if ("structuredContent" in record) {
101
+ return timelineAnnotationToolOutputValue(record.structuredContent, depth + 1);
102
+ }
103
+ if ("result" in record) return timelineAnnotationToolOutputValue(record.result, depth + 1);
104
+ return safeTimelineAnnotationJson(value);
105
+ }
106
+
107
+ /** Browser mirror of the server's canonical annotation source projection. */
108
+ function timelineAnnotationToolOutputText(output: unknown): string | null {
109
+ const raw = timelineAnnotationToolOutputValue(output);
110
+ const marker = raw.indexOf("\nOutput:\n");
111
+ const text = (
112
+ marker >= 0
113
+ ? raw.slice(marker + "\nOutput:\n".length)
114
+ : raw.startsWith("Output:\n")
115
+ ? raw.slice("Output:\n".length)
116
+ : raw
117
+ ).replace(TIMELINE_ANNOTATION_ANSI_SEQUENCE, "");
118
+ return text.length > 0 ? text : null;
119
+ }
64
120
 
65
121
  export function buildTimeline(events: SessionEvent[]): TimelineItem[] {
66
122
  const items: TimelineItem[] = [];
@@ -134,6 +190,15 @@ export function buildTimeline(events: SessionEvent[]): TimelineItem[] {
134
190
  kind: "user-message",
135
191
  id: event.id,
136
192
  text: voiceMessage?.text ?? stringValue(payload.text),
193
+ annotations: timelineAnnotations(payload.annotations),
194
+ annotationSource: {
195
+ kind: "user_message",
196
+ eventId: event.id,
197
+ eventType: "user.message",
198
+ sequence: event.sequence,
199
+ turnId,
200
+ text: voiceMessage?.text ?? stringValue(payload.text),
201
+ },
137
202
  ...(voiceMessage ? { presentation: voiceMessage.presentation } : {}),
138
203
  resources: resourceRefs(payload.resources),
139
204
  tools: toolRefs(payload.tools),
@@ -212,6 +277,17 @@ export function buildTimeline(events: SessionEvent[]): TimelineItem[] {
212
277
  // Completion time is what the footer shows ("finished at"); keep the
213
278
  // first-delta stamp only until this event arrives.
214
279
  open.occurredAt = event.occurredAt;
280
+ open.annotationSource =
281
+ text && open.text === text
282
+ ? {
283
+ kind: "assistant_message",
284
+ eventId: event.id,
285
+ eventType: "agent.message.completed",
286
+ sequence: event.sequence,
287
+ turnId,
288
+ text,
289
+ }
290
+ : undefined;
215
291
  // The SDK can emit a hosted-tool item only after its provider-native
216
292
  // operation has completed, even though answer deltas were already
217
293
  // streamed. The completed message event is the durable ordering
@@ -231,6 +307,14 @@ export function buildTimeline(events: SessionEvent[]): TimelineItem[] {
231
307
  text,
232
308
  streaming: false,
233
309
  occurredAt: event.occurredAt,
310
+ annotationSource: {
311
+ kind: "assistant_message",
312
+ eventId: event.id,
313
+ eventType: "agent.message.completed",
314
+ sequence: event.sequence,
315
+ turnId,
316
+ text,
317
+ },
234
318
  });
235
319
  }
236
320
  break;
@@ -312,6 +396,7 @@ export function buildTimeline(events: SessionEvent[]): TimelineItem[] {
312
396
  name,
313
397
  arguments: args,
314
398
  output: undefined,
399
+ truncation: null,
315
400
  // The provider-native item drives the per-tool renderers (apply_patch
316
401
  // operation, computer_call action, web_search providerData, …).
317
402
  raw: payload.raw,
@@ -336,8 +421,30 @@ export function buildTimeline(events: SessionEvent[]): TimelineItem[] {
336
421
  }
337
422
  // An output carrying an explicit error flag (or an MCP isError result)
338
423
  // settles the tool to "failed" so the renderer can surface it loudly.
424
+ const truncation = toolCallTruncation(payload);
339
425
  target.status = isErrorOutput(payload) ? "failed" : "complete";
340
- target.output = payload.output;
426
+ target.output = Object.prototype.hasOwnProperty.call(payload, "output")
427
+ ? payload.output
428
+ : truncation && typeof payload.preview === "string"
429
+ ? payload.preview
430
+ : payload.output;
431
+ target.truncation = truncation;
432
+ const sourceText = timelineAnnotationToolOutputText(
433
+ Object.prototype.hasOwnProperty.call(payload, "output")
434
+ ? payload.output
435
+ : payload.preview,
436
+ );
437
+ if (sourceText !== null) {
438
+ target.annotationSource = {
439
+ kind: "tool_output",
440
+ eventId: event.id,
441
+ eventType: "agent.toolCall.output",
442
+ sequence: event.sequence,
443
+ turnId,
444
+ text: sourceText,
445
+ label: target.name,
446
+ };
447
+ }
341
448
  break;
342
449
  }
343
450
 
@@ -540,6 +647,7 @@ export function buildTimeline(events: SessionEvent[]): TimelineItem[] {
540
647
 
541
648
  case "tool.auth_needed":
542
649
  case "credential.auth_needed": {
650
+ const capability = capabilityAuthorizationRequest(payload.capability);
543
651
  if (
544
652
  event.type === "tool.auth_needed" &&
545
653
  !stringValue(payload.toolName) &&
@@ -562,7 +670,11 @@ export function buildTimeline(events: SessionEvent[]): TimelineItem[] {
562
670
  id: event.id,
563
671
  turnId,
564
672
  serverId: typeof payload.serverId === "string" ? payload.serverId : null,
565
- source: event.type === "tool.auth_needed" ? "tool" : "credential",
673
+ source: capability
674
+ ? "capability"
675
+ : event.type === "tool.auth_needed"
676
+ ? "tool"
677
+ : "credential",
566
678
  providerDomain: stringValue(payload.providerDomain),
567
679
  connectionId: typeof payload.connectionId === "string" ? payload.connectionId : null,
568
680
  reason: authNeededReason(payload.reason),
@@ -571,6 +683,7 @@ export function buildTimeline(events: SessionEvent[]): TimelineItem[] {
571
683
  toolName: typeof payload.toolName === "string" ? payload.toolName : null,
572
684
  authorizationUrl:
573
685
  typeof payload.authorizationUrl === "string" ? payload.authorizationUrl : null,
686
+ capability,
574
687
  occurredAt: event.occurredAt,
575
688
  });
576
689
  break;
@@ -1315,6 +1428,7 @@ function machineInputMembers(value: unknown): MachineInputBatchItem["members"] {
1315
1428
  "agent_message",
1316
1429
  "agent_steer_instruction",
1317
1430
  "child_terminal_result",
1431
+ "media_generation_result",
1318
1432
  ]);
1319
1433
  const classifications = new Set<MachineInputBatchItem["members"][number]["classification"]>([
1320
1434
  "success",
@@ -1333,16 +1447,24 @@ function machineInputMembers(value: unknown): MachineInputBatchItem["members"] {
1333
1447
  member.classification as MachineInputBatchItem["members"][number]["classification"],
1334
1448
  ) &&
1335
1449
  typeof member.sourceId === "string"
1336
- ? [
1337
- {
1338
- id: member.id,
1339
- kind: member.kind as MachineInputBatchItem["members"][number]["kind"],
1340
- classification:
1341
- member.classification as MachineInputBatchItem["members"][number]["classification"],
1342
- sourceId: member.sourceId,
1343
- summary: stringValue(member.summary),
1344
- },
1345
- ]
1450
+ ? (() => {
1451
+ const kind = member.kind as MachineInputBatchItem["members"][number]["kind"];
1452
+ const result =
1453
+ kind === "media_generation_result"
1454
+ ? parseMediaGenerationResult(member.result)
1455
+ : null;
1456
+ return [
1457
+ {
1458
+ id: member.id,
1459
+ kind,
1460
+ classification:
1461
+ member.classification as MachineInputBatchItem["members"][number]["classification"],
1462
+ sourceId: member.sourceId,
1463
+ summary: stringValue(member.summary),
1464
+ ...(result ? { result } : {}),
1465
+ },
1466
+ ];
1467
+ })()
1346
1468
  : [];
1347
1469
  })
1348
1470
  : [];
@@ -1378,6 +1500,25 @@ function resourceRefs(value: unknown): import("@opengeni/sdk").ResourceRef[] {
1378
1500
  });
1379
1501
  }
1380
1502
 
1503
+ function timelineAnnotations(value: unknown): TimelineAnnotation[] {
1504
+ if (!Array.isArray(value)) return [];
1505
+ return value.filter((candidate): candidate is TimelineAnnotation => {
1506
+ const annotation = asRecord(candidate);
1507
+ const source = asRecord(annotation.source);
1508
+ return (
1509
+ typeof annotation.id === "string" &&
1510
+ typeof annotation.ordinal === "number" &&
1511
+ typeof annotation.quote === "string" &&
1512
+ typeof annotation.note === "string" &&
1513
+ typeof source.eventId === "string" &&
1514
+ typeof source.eventType === "string" &&
1515
+ typeof source.sequence === "number" &&
1516
+ typeof source.startOffset === "number" &&
1517
+ typeof source.endOffset === "number"
1518
+ );
1519
+ });
1520
+ }
1521
+
1381
1522
  function toolRefs(value: unknown): import("@opengeni/sdk").ToolRef[] {
1382
1523
  if (!Array.isArray(value)) {
1383
1524
  return [];
@@ -1440,6 +1581,31 @@ function isErrorOutput(payload: Record<string, unknown>): boolean {
1440
1581
  );
1441
1582
  }
1442
1583
 
1584
+ function toolCallTruncation(payload: Record<string, unknown>): ToolCallTruncation | null {
1585
+ const truncation = asRecord(payload.truncation);
1586
+ const fullEvidence = asRecord(truncation.fullEvidence);
1587
+ const surface = stringValue(truncation.surface);
1588
+ const reason = stringValue(truncation.reason);
1589
+ if (
1590
+ truncation.truncated !== true ||
1591
+ !surface ||
1592
+ !reason ||
1593
+ typeof fullEvidence.available !== "boolean"
1594
+ ) {
1595
+ return null;
1596
+ }
1597
+ return {
1598
+ truncated: true,
1599
+ surface,
1600
+ reason,
1601
+ omittedBytes: numberOrNull(truncation.omittedBytes),
1602
+ fullEvidence: {
1603
+ available: fullEvidence.available,
1604
+ reason: stringValue(fullEvidence.reason) || null,
1605
+ },
1606
+ };
1607
+ }
1608
+
1443
1609
  function findOpenCall(
1444
1610
  items: TimelineItem[],
1445
1611
  callId: string | null,
@@ -1563,6 +1729,37 @@ function authNeededReason(value: unknown): AuthNeededItem["reason"] {
1563
1729
  : null;
1564
1730
  }
1565
1731
 
1732
+ function capabilityAuthorizationRequest(
1733
+ value: unknown,
1734
+ ): NonNullable<AuthNeededItem["capability"]> | null {
1735
+ if (!value || typeof value !== "object" || Array.isArray(value)) return null;
1736
+ const record = value as Record<string, unknown>;
1737
+ const kind = record.kind;
1738
+ const source = record.source;
1739
+ const action = record.action;
1740
+ if (
1741
+ typeof record.id !== "string" ||
1742
+ typeof record.name !== "string" ||
1743
+ !["pack", "mcp", "api", "skill", "plugin"].includes(String(kind)) ||
1744
+ !["built_in", "library", "configured", "public_registry", "registry", "manual"].includes(
1745
+ String(source),
1746
+ ) ||
1747
+ !["connect", "add_credentials", "enable"].includes(String(action)) ||
1748
+ typeof record.rationale !== "string"
1749
+ ) {
1750
+ return null;
1751
+ }
1752
+ return {
1753
+ id: record.id,
1754
+ name: record.name,
1755
+ kind: kind as NonNullable<AuthNeededItem["capability"]>["kind"],
1756
+ source: source as NonNullable<AuthNeededItem["capability"]>["source"],
1757
+ action: action as NonNullable<AuthNeededItem["capability"]>["action"],
1758
+ rationale: record.rationale,
1759
+ requiredVariables: stringList(record.requiredVariables),
1760
+ };
1761
+ }
1762
+
1566
1763
  function stringList(value: unknown): string[] {
1567
1764
  return Array.isArray(value)
1568
1765
  ? value.filter((entry): entry is string => typeof entry === "string" && entry.trim().length > 0)
@@ -1,4 +1,4 @@
1
- import type { RetainedArtifactReference } from "@opengeni/sdk";
1
+ import type { RetainedArtifactReference, VideoArtifactPlaybackSource } from "@opengeni/sdk";
2
2
  import type { ComponentType } from "react";
3
3
  import { mcpToolLeaf } from "./tool-display-name";
4
4
  import type { ToolCallItem } from "./types";
@@ -24,6 +24,7 @@ import type { ToolCallItem } from "./types";
24
24
  export type ToolRendererProps = {
25
25
  item: ToolCallItem;
26
26
  loadRetainedScreenshot?: RetainedScreenshotLoader | undefined;
27
+ loadRetainedArtifact?: RetainedArtifactLoader | undefined;
27
28
  };
28
29
 
29
30
  export type RetainedScreenshotLoader = (
@@ -31,6 +32,17 @@ export type RetainedScreenshotLoader = (
31
32
  signal: AbortSignal,
32
33
  ) => Promise<Uint8Array | null>;
33
34
 
35
+ export type RetainedArtifactLoader = (
36
+ artifact: RetainedArtifactReference,
37
+ signal: AbortSignal,
38
+ ) => Promise<Uint8Array | { url: string } | null>;
39
+
40
+ /** Mint an expiring source for native browser playback without loading video bytes in JS. */
41
+ export type VideoArtifactPlaybackLoader = (
42
+ artifactId: string,
43
+ signal: AbortSignal,
44
+ ) => Promise<VideoArtifactPlaybackSource>;
45
+
34
46
  export type ToolRenderer = ComponentType<ToolRendererProps>;
35
47
 
36
48
  /** A registry entry: which key it matches and the component that renders it. */
@@ -19,7 +19,7 @@ import { usePortalTokenStyle } from "../lib/use-portal-token-style";
19
19
  -------------------------------------------------------------------------- */
20
20
 
21
21
  type LightboxController = {
22
- open: (src: string, caption?: string, source?: HTMLElement | null) => void;
22
+ open: (src: string, caption?: string, source?: HTMLElement | null, label?: string) => void;
23
23
  };
24
24
 
25
25
  const LightboxContext = createContext<LightboxController | null>(null);
@@ -59,14 +59,21 @@ export function LightboxProvider({ children }: { children: ReactNode }) {
59
59
  }
60
60
 
61
61
  function LightboxRoot({ children }: { children: ReactNode }) {
62
- const [state, setState] = useState<{ src: string; caption?: string } | null>(null);
62
+ const [state, setState] = useState<{
63
+ src: string;
64
+ caption?: string;
65
+ label: string;
66
+ } | null>(null);
63
67
  const [tokenSource, setTokenSource] = useState<HTMLElement | null>(null);
64
68
  const portalStyle = usePortalTokenStyle(tokenSource);
65
69
 
66
- const open = useCallback((src: string, caption?: string, source?: HTMLElement | null) => {
67
- setTokenSource(source ?? null);
68
- setState(caption ? { src, caption } : { src });
69
- }, []);
70
+ const open = useCallback(
71
+ (src: string, caption?: string, source?: HTMLElement | null, label = "Screenshot") => {
72
+ setTokenSource(source ?? null);
73
+ setState(caption ? { src, caption, label } : { src, label });
74
+ },
75
+ [],
76
+ );
70
77
 
71
78
  const close = useCallback(() => {
72
79
  setState(null);
@@ -95,7 +102,7 @@ function LightboxRoot({ children }: { children: ReactNode }) {
95
102
  <Dialog.Content
96
103
  asChild
97
104
  forceMount
98
- aria-label="Screenshot"
105
+ aria-label={state.label}
99
106
  onOpenAutoFocus={(event) => event.preventDefault()}
100
107
  >
101
108
  <motion.div
@@ -117,7 +124,10 @@ function LightboxRoot({ children }: { children: ReactNode }) {
117
124
  className="og-root fixed inset-0 z-50 flex items-center justify-center p-6 sm:p-12"
118
125
  style={portalStyle}
119
126
  >
120
- <Dialog.Title className="sr-only">Screenshot</Dialog.Title>
127
+ <Dialog.Title className="sr-only">{state.label}</Dialog.Title>
128
+ <Dialog.Description className="sr-only">
129
+ {state.caption ?? `Expanded ${state.label.toLowerCase()}`}
130
+ </Dialog.Description>
121
131
  {/* The figure is one self-contained object: image, caption, and
122
132
  its own close control. The close button anchors to a wrapper
123
133
  sized to the IMAGE (w-fit), so it hugs the real top-right
@@ -128,7 +138,7 @@ function LightboxRoot({ children }: { children: ReactNode }) {
128
138
  {/* A plain <img>: this SDK is framework-agnostic, with no host Image component. */}
129
139
  <img
130
140
  src={state.src}
131
- alt={state.caption ?? "Screenshot"}
141
+ alt={state.caption ?? state.label}
132
142
  className="min-h-0 max-h-[82vh] w-auto max-w-full rounded-og-md border border-white/10 object-contain shadow-og-lg"
133
143
  />
134
144
  <Dialog.Close