@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
@@ -16,7 +16,7 @@ import {
16
16
  verticalListSortingStrategy,
17
17
  } from "@dnd-kit/sortable";
18
18
  import { CSS } from "@dnd-kit/utilities";
19
- import type { SessionPendingInputPreview, SessionTurn } from "@opengeni/sdk";
19
+ import type { SessionPendingInputPreview, SessionTurn, TimelineAnnotation } from "@opengeni/sdk";
20
20
  import {
21
21
  ArrowDownToLineIcon,
22
22
  ArrowUpToLineIcon,
@@ -46,6 +46,7 @@ import {
46
46
  } from "../lib/use-portal-token-style";
47
47
  import { requestQueueDraftEdit } from "./queue-draft-policy";
48
48
  import { QueueErrorAlert, QueueStoppingStatus } from "./queue-surface-state";
49
+ import { TimelineAnnotationsChip } from "./timeline-annotations";
49
50
 
50
51
  /** The sole pending-input surface: compact above Goal, Agents, and composer. */
51
52
  type QueueSurfaceCommonProps = {
@@ -99,14 +100,15 @@ export function QueueSurface({
99
100
  (input) => !attachedInputIds.includes(input.id),
100
101
  );
101
102
  const canEditInComposer = queueComposerCheckoutEnabled(composer, readOnly);
102
- const collapsedPreview = useMemo(
103
- () =>
104
- queuePromptPreview(
105
- queue.queue[0]?.prompt ?? queue.pendingInputs[0]?.summary ?? "",
106
- QUEUE_COLLAPSED_PREVIEW_CHARACTERS,
107
- ),
108
- [queue.pendingInputs, queue.queue],
109
- );
103
+ const collapsedPreview = useMemo(() => {
104
+ const firstTurn = queue.queue[0];
105
+ return firstTurn
106
+ ? queueTurnPreview(firstTurn, QUEUE_COLLAPSED_PREVIEW_CHARACTERS)
107
+ : queuePromptPreview(
108
+ queue.pendingInputs[0]?.summary ?? "",
109
+ QUEUE_COLLAPSED_PREVIEW_CHARACTERS,
110
+ );
111
+ }, [queue.pendingInputs, queue.queue]);
110
112
  const sensors = useSensors(useSensor(PointerSensor, { activationConstraint: { distance: 6 } }));
111
113
 
112
114
  const displayedQueue = useMemo(() => {
@@ -409,8 +411,8 @@ export function QueueSurface({
409
411
  dir="auto"
410
412
  >
411
413
  {
412
- queuePromptPreview(
413
- queue.queue.find((turn) => turn.id === draggedTurnId)?.prompt ?? "",
414
+ queueTurnPreview(
415
+ queue.queue.find((turn) => turn.id === draggedTurnId),
414
416
  QUEUE_ROW_PREVIEW_CHARACTERS,
415
417
  ).summary
416
418
  }
@@ -490,6 +492,29 @@ type QueuePromptPreview = {
490
492
  isFallback: boolean;
491
493
  };
492
494
 
495
+ function annotationOnlyQueuePreview(count: number): QueuePromptPreview {
496
+ const summary = `${count} timeline ${count === 1 ? "annotation" : "annotations"}`;
497
+ return {
498
+ summary,
499
+ collapsedVisual: summary,
500
+ visibleStart: summary,
501
+ visibleIdentity: null,
502
+ visibleIdentityLabel: null,
503
+ isFallback: false,
504
+ };
505
+ }
506
+
507
+ function queueTurnPreview(
508
+ turn: SessionTurn | undefined,
509
+ maxCharacters: number,
510
+ ): QueuePromptPreview {
511
+ if (!turn) return queuePromptPreview("", maxCharacters);
512
+ const annotations = turn.annotations ?? [];
513
+ return turn.prompt.length === 0 && annotations.length > 0
514
+ ? annotationOnlyQueuePreview(annotations.length)
515
+ : queuePromptPreview(turn.prompt, maxCharacters);
516
+ }
517
+
493
518
  /**
494
519
  * Build a bounded head/tail summary of an arbitrary queued prompt. Sampling
495
520
  * both ends distinguishes prompts with equal long prefixes. Each sampled edge
@@ -752,73 +777,94 @@ function isLowSurrogate(codeUnit: number): boolean {
752
777
 
753
778
  function QueuePrompt({
754
779
  prompt,
780
+ annotations,
755
781
  index,
756
782
  onDisclosureChange,
757
783
  }: {
758
784
  prompt: string;
785
+ annotations: readonly TimelineAnnotation[];
759
786
  index: number;
760
787
  onDisclosureChange: (expanded: boolean) => void;
761
788
  }) {
762
789
  const [expanded, setExpanded] = useState(false);
763
790
  const fullContentId = useId();
764
- const preview = useMemo(() => queuePromptPreview(prompt, QUEUE_ROW_PREVIEW_CHARACTERS), [prompt]);
791
+ const preview = useMemo(
792
+ () =>
793
+ prompt.length === 0 && annotations.length > 0
794
+ ? annotationOnlyQueuePreview(annotations.length)
795
+ : queuePromptPreview(prompt, QUEUE_ROW_PREVIEW_CHARACTERS),
796
+ [annotations.length, prompt],
797
+ );
765
798
 
766
799
  return (
767
800
  <div className="w-full min-w-0 max-w-full">
768
- <div
769
- aria-label={`Queued prompt ${index + 1} summary: ${preview.summary}`}
770
- className="max-w-full overflow-hidden text-og-control leading-5 text-fg"
771
- data-testid={`queue-prompt-preview-${index + 1}`}
772
- dir="auto"
773
- role="note"
774
- >
775
- <span aria-hidden="true" className="flex min-w-0 max-w-full items-baseline gap-2 sm:block">
801
+ {prompt ? (
802
+ <div
803
+ aria-label={`Queued prompt ${index + 1} summary: ${preview.summary}`}
804
+ className="max-w-full overflow-hidden text-og-control leading-5 text-fg"
805
+ data-testid={`queue-prompt-preview-${index + 1}`}
806
+ dir="auto"
807
+ role="note"
808
+ >
776
809
  <span
777
- className={`${preview.isFallback ? "" : "line-clamp-1 sm:line-clamp-2"} min-w-0 flex-1 whitespace-pre-wrap break-all [unicode-bidi:plaintext]`}
778
- data-testid={`queue-prompt-start-${index + 1}`}
779
- dir="auto"
810
+ aria-hidden="true"
811
+ className="flex min-w-0 max-w-full items-baseline gap-2 sm:block"
780
812
  >
781
- {preview.visibleStart}
782
- </span>
783
- {preview.visibleIdentity && preview.visibleIdentityLabel ? (
784
813
  <span
785
- className="flex min-w-0 max-w-[70%] shrink-0 items-center gap-1 text-og-xs leading-4 text-fg-muted sm:mt-0.5 sm:max-w-full"
786
- data-testid={`queue-prompt-identity-row-${index + 1}`}
814
+ className={`${preview.isFallback ? "" : "line-clamp-1 sm:line-clamp-2"} min-w-0 flex-1 whitespace-pre-wrap break-all [unicode-bidi:plaintext]`}
815
+ data-testid={`queue-prompt-start-${index + 1}`}
816
+ dir="auto"
787
817
  >
788
- <span className="shrink-0 font-medium text-fg-subtle">
789
- {preview.visibleIdentityLabel}
790
- </span>
818
+ {preview.visibleStart}
819
+ </span>
820
+ {preview.visibleIdentity && preview.visibleIdentityLabel ? (
791
821
  <span
792
- className="min-w-0 overflow-hidden text-ellipsis whitespace-nowrap font-mono [unicode-bidi:plaintext]"
793
- data-testid={`queue-prompt-identity-${index + 1}`}
794
- dir="auto"
822
+ className="flex min-w-0 max-w-[70%] shrink-0 items-center gap-1 text-og-xs leading-4 text-fg-muted sm:mt-0.5 sm:max-w-full"
823
+ data-testid={`queue-prompt-identity-row-${index + 1}`}
795
824
  >
796
- {preview.visibleIdentity}
825
+ <span className="shrink-0 font-medium text-fg-subtle">
826
+ {preview.visibleIdentityLabel}
827
+ </span>
828
+ <span
829
+ className="min-w-0 overflow-hidden text-ellipsis whitespace-nowrap font-mono [unicode-bidi:plaintext]"
830
+ data-testid={`queue-prompt-identity-${index + 1}`}
831
+ dir="auto"
832
+ >
833
+ {preview.visibleIdentity}
834
+ </span>
797
835
  </span>
798
- </span>
799
- ) : null}
800
- </span>
801
- </div>
802
- <button
803
- type="button"
804
- aria-controls={fullContentId}
805
- aria-expanded={expanded}
806
- aria-label={`${expanded ? "Hide" : "Show"} full content for queued prompt ${index + 1}`}
807
- className="mt-1 inline-flex min-h-7 min-w-0 max-w-full items-center gap-1 whitespace-normal rounded-md text-left text-og-xs font-medium text-fg outline-hidden focus-visible:ring-2 focus-visible:ring-ring/40 pointer-coarse:min-h-[44px]"
808
- data-testid={`queue-prompt-disclosure-${index + 1}`}
809
- onClick={() => {
810
- const next = !expanded;
811
- setExpanded(next);
812
- onDisclosureChange(next);
813
- }}
814
- >
815
- <ChevronDownIcon
816
- aria-hidden="true"
817
- className={`size-3 shrink-0 transition-transform ${expanded ? "rotate-180" : ""}`}
836
+ ) : null}
837
+ </span>
838
+ </div>
839
+ ) : null}
840
+ {annotations.length > 0 ? (
841
+ <TimelineAnnotationsChip
842
+ annotations={annotations}
843
+ className={prompt ? "mt-1" : undefined}
818
844
  />
819
- {expanded ? "Hide full prompt" : "View full prompt"}
820
- </button>
821
- {expanded ? (
845
+ ) : null}
846
+ {prompt ? (
847
+ <button
848
+ type="button"
849
+ aria-controls={fullContentId}
850
+ aria-expanded={expanded}
851
+ aria-label={`${expanded ? "Hide" : "Show"} full content for queued prompt ${index + 1}`}
852
+ className="mt-1 inline-flex min-h-7 min-w-0 max-w-full items-center gap-1 whitespace-normal rounded-md text-left text-og-xs font-medium text-fg outline-hidden focus-visible:ring-2 focus-visible:ring-ring/40 pointer-coarse:min-h-[44px]"
853
+ data-testid={`queue-prompt-disclosure-${index + 1}`}
854
+ onClick={() => {
855
+ const next = !expanded;
856
+ setExpanded(next);
857
+ onDisclosureChange(next);
858
+ }}
859
+ >
860
+ <ChevronDownIcon
861
+ aria-hidden="true"
862
+ className={`size-3 shrink-0 transition-transform ${expanded ? "rotate-180" : ""}`}
863
+ />
864
+ {expanded ? "Hide full prompt" : "View full prompt"}
865
+ </button>
866
+ ) : null}
867
+ {expanded && prompt ? (
822
868
  <pre
823
869
  id={fullContentId}
824
870
  role="region"
@@ -850,7 +896,12 @@ function ReadOnlyQueueRow({
850
896
  <li className="flex min-w-0 items-start gap-2 bg-surface px-3 py-2">
851
897
  <span className="mt-1 shrink-0 font-mono text-og-xs text-fg-subtle">{index + 1}</span>
852
898
  <div className="min-w-0 flex-1">
853
- <QueuePrompt prompt={turn.prompt} index={index} onDisclosureChange={onDisclosureChange} />
899
+ <QueuePrompt
900
+ prompt={turn.prompt}
901
+ annotations={turn.annotations ?? []}
902
+ index={index}
903
+ onDisclosureChange={onDisclosureChange}
904
+ />
854
905
  {attachedInputs.length > 0 ? (
855
906
  <PendingMachineInputs inputs={attachedInputs} attached />
856
907
  ) : null}
@@ -941,7 +992,12 @@ function SortableQueueRow({
941
992
  {index + 1}
942
993
  </span>
943
994
  <div className="col-span-full row-start-2 min-w-0 sm:col-span-1 sm:col-start-3 sm:row-start-1">
944
- <QueuePrompt prompt={turn.prompt} index={index} onDisclosureChange={onDisclosureChange} />
995
+ <QueuePrompt
996
+ prompt={turn.prompt}
997
+ annotations={turn.annotations ?? []}
998
+ index={index}
999
+ onDisclosureChange={onDisclosureChange}
1000
+ />
945
1001
  {attachedInputs.length > 0 ? (
946
1002
  <PendingMachineInputs inputs={attachedInputs} attached />
947
1003
  ) : null}
@@ -319,8 +319,8 @@ export function SandboxFiles({
319
319
  path={viewPath}
320
320
  initialContents={fileView.content}
321
321
  themeType={resolvedTheme}
322
- onSave={(contents) =>
323
- files.writeFile(viewPath, contents, { expectedContent: fileView.content! })
322
+ onSave={(contents, expectedContents) =>
323
+ files.writeFile(viewPath, contents, { expectedContent: expectedContents })
324
324
  }
325
325
  onOverwrite={(contents) => files.writeFile(viewPath, contents, { force: true })}
326
326
  onReload={() => setViewReloadRevision((revision) => revision + 1)}
@@ -1,9 +1,11 @@
1
1
  import type { TerminalCapability } from "@opengeni/sdk";
2
- import { type ReactNode, useEffect, useRef, useState } from "react";
2
+ import { type ReactNode, useEffect, useLayoutEffect, useRef, useState } from "react";
3
3
  import { cn } from "../lib/cn";
4
- import { useTerminalStream } from "../hooks/use-terminal-stream";
4
+ import { type TerminalStreamStatus, useTerminalStream } from "../hooks/use-terminal-stream";
5
5
  import type { UseSandboxTerminalResult } from "../hooks/use-sandbox-terminal";
6
6
  import { resolveTerminalFont, xtermThemeFromTokens } from "../lib/xterm-theme";
7
+ import { sandboxAcceptsLiveIo } from "../lib/sandbox-liveness";
8
+ import { terminalCanAcquirePty } from "../lib/terminal-capability";
7
9
  import { attachRenderer, type RendererLoaders, type RendererTier } from "../lib/xterm-renderer";
8
10
 
9
11
  /**
@@ -60,7 +62,7 @@ export type SandboxTerminalProps = {
60
62
  fontSize?: number | undefined;
61
63
  /**
62
64
  * Lease liveness (`cold` | `warming` | `warm` | `draining`). Drives the
63
- * boot-in-terminal status lines: once the user has focused the terminal and
65
+ * boot-in-terminal status lines: once the user has engaged the terminal and
64
66
  * the box is not yet warm, a styled "● waking machine — Ns…" line is rendered
65
67
  * INSIDE xterm (no overlay/spinner) until the live PTY connects.
66
68
  */
@@ -85,6 +87,8 @@ export type SandboxTerminalProps = {
85
87
  * the firehose-only default).
86
88
  */
87
89
  onActivate?: (() => void) | undefined;
90
+ /** Re-negotiate the terminal capability after an unexpected socket failure. */
91
+ onReconnectNeeded?: (() => void) | undefined;
88
92
  className?: string | undefined;
89
93
  };
90
94
 
@@ -92,6 +96,7 @@ export type SandboxTerminalProps = {
92
96
  // drive it without a hard type dep on the lazily-imported lib.
93
97
  type XtermLike = {
94
98
  open: (el: HTMLElement) => void;
99
+ focus: () => void;
95
100
  write: (data: string) => void;
96
101
  clear: () => void;
97
102
  onData: (cb: (data: string) => void) => { dispose: () => void };
@@ -110,6 +115,44 @@ type XtermLike = {
110
115
  };
111
116
  type FitAddonLike = { fit: () => void };
112
117
 
118
+ export type TerminalSurfaceState =
119
+ | "loading"
120
+ | "connecting"
121
+ | "error"
122
+ | "waking"
123
+ | "output-only"
124
+ | "interactive";
125
+
126
+ /**
127
+ * Stable semantic state for hosts and browser acceptance. The xterm helper
128
+ * textarea exists before the live PTY socket is ready, so its presence alone
129
+ * must never be treated as proof that keystrokes can reach the sandbox.
130
+ */
131
+ export function terminalSurfaceState(input: {
132
+ ready: boolean;
133
+ acceptsInput: boolean;
134
+ inputPending: boolean;
135
+ ptyStatus: TerminalStreamStatus;
136
+ booting: boolean;
137
+ }): TerminalSurfaceState {
138
+ if (!input.ready) return "loading";
139
+ if (input.acceptsInput) return "interactive";
140
+ if (input.inputPending) return "connecting";
141
+ if (input.ptyStatus === "connecting") return "connecting";
142
+ if (input.ptyStatus === "error") return "error";
143
+ if (input.booting) return "waking";
144
+ return "output-only";
145
+ }
146
+
147
+ export function subscribeTerminalInput(
148
+ term: Pick<XtermLike, "onData"> | null,
149
+ sink: ((data: string) => void) | null,
150
+ enabled: boolean,
151
+ ): { dispose: () => void } | null {
152
+ if (!enabled || !term || !sink) return null;
153
+ return term.onData(sink);
154
+ }
155
+
113
156
  /** Clear the transient wake line and put the first live prompt at cell 1,1.
114
157
  * `Terminal.clear()` removes scrollback but deliberately preserves the cursor,
115
158
  * which lets the first PTY frame append to the old wake message. */
@@ -183,6 +226,7 @@ export function SandboxTerminal({
183
226
  showHeader,
184
227
  shell,
185
228
  onActivate,
229
+ onReconnectNeeded,
186
230
  className,
187
231
  }: SandboxTerminalProps) {
188
232
  const containerRef = useRef<HTMLDivElement | null>(null);
@@ -193,50 +237,63 @@ export function SandboxTerminal({
193
237
  const wroteFirehoseRef = useRef(false);
194
238
  const bootActiveRef = useRef(false);
195
239
  const [ready, setReady] = useState(false);
240
+ const [inputReady, setInputReady] = useState(false);
196
241
  const [activated, setActivated] = useState(false);
197
242
 
198
243
  // ── Interactive transport switch ─────────────────────────────────────────────
199
- const ptyWs = terminalCapability?.transport === "pty-ws" && Boolean(terminalCapability?.url);
200
- // Output frames buffer here until xterm has mounted; the write effect drains it.
201
- const ptyOutputQueueRef = useRef<string[]>([]);
244
+ const ptyDescriptor =
245
+ terminalCapability?.transport === "pty-ws" || terminalCapability?.transport === "relay-pty";
246
+ const ptyAttachable = terminalCanAcquirePty(terminalCapability);
202
247
  const {
203
248
  status: ptyStatus,
204
249
  write: ptyWrite,
205
250
  resize: resizePty,
206
251
  connected: ptyConnected,
207
252
  } = useTerminalStream({
208
- capability: ptyWs
209
- ? {
210
- transport: terminalCapability!.transport,
211
- url: terminalCapability!.url,
212
- token: terminalCapability!.token,
213
- }
214
- : null,
253
+ // Connect only after xterm exists. This makes the renderer the sole output
254
+ // owner from the first websocket frame and removes an otherwise unbounded
255
+ // pre-mount output queue during large command bursts.
256
+ capability:
257
+ ptyAttachable && ready
258
+ ? {
259
+ transport: terminalCapability!.transport,
260
+ url: ptyDescriptor ? terminalCapability!.url : null,
261
+ token: ptyDescriptor ? terminalCapability!.token : null,
262
+ expiresAt: ptyDescriptor ? terminalCapability!.expiresAt : null,
263
+ }
264
+ : null,
215
265
  onOutput: (data) => {
216
- const term = termRef.current;
217
- if (term) term.write(data);
218
- else ptyOutputQueueRef.current.push(data);
266
+ termRef.current?.write(data);
219
267
  },
268
+ onReconnectNeeded,
220
269
  });
221
270
 
222
- // PTY mode = a live ttyd socket drives the screen. Firehose mode = the
223
- // read-only projection (or the legacy HTTP-write fallback).
224
- const ptyMode = ptyWs && ptyStatus !== "closed";
225
- const interactive = !readOnly && (ptyMode ? ptyConnected : result.write !== null);
226
- const terminalModeLabel = interactive
227
- ? null
228
- : readOnly
229
- ? "read-only"
230
- : ptyMode && ptyStatus === "connecting"
231
- ? "connecting"
232
- : ptyMode && ptyStatus === "error"
233
- ? "connection error"
234
- : "output only";
235
-
236
- // Boot-in-terminal: after the user focuses a not-yet-warm box, show styled
271
+ // A pty-ws capability exclusively owns the screen even while connecting or
272
+ // after a visible connection failure; never silently switch one terminal UI
273
+ // between two PTYs. Input is enabled during the handshake because the stream
274
+ // hook buffers it strictly and flushes only after ttyd auth succeeds.
275
+ const ptyMode = ptyDescriptor;
276
+ // Keep xterm stdin live for an attachable descriptor even before its URL has
277
+ // arrived. Focus/click triggers the grant; any immediately following input is
278
+ // bounded in the stream hook instead of disappearing in that HTTP interval.
279
+ const ptyCapturesInput = ptyAttachable && ptyStatus !== "error";
280
+ const interactive = !readOnly && (ptyAttachable ? ptyCapturesInput : result.write !== null);
281
+ // Public "interactive" means the mounted input subscription can reach a live
282
+ // PTY now. Before that point xterm still captures into the bounded queue, but
283
+ // acceptance and embedders see an explicit connecting state.
284
+ const acceptsInput = inputReady && (!ptyAttachable || ptyConnected);
285
+ const inputPending = inputReady && ptyAttachable && !ptyConnected && activated;
286
+ let terminalModeLabel: string | null;
287
+ if (readOnly) terminalModeLabel = "read-only";
288
+ else if (!ptyAttachable) terminalModeLabel = acceptsInput ? null : "output only";
289
+ else if (ptyStatus === "error") terminalModeLabel = "connection error";
290
+ else if (acceptsInput) terminalModeLabel = null;
291
+ else terminalModeLabel = activated ? "connecting" : "connect on input";
292
+
293
+ // Boot-in-terminal: after the user engages a not-yet-warm box, show styled
237
294
  // status lines INSIDE xterm instead of an overlay — but only when there is no
238
295
  // firehose transcript to show yet (otherwise the projected output is shown).
239
- const warm = liveness === "warm" || liveness === "draining";
296
+ const warm = sandboxAcceptsLiveIo(liveness);
240
297
  const booting =
241
298
  activated &&
242
299
  !ptyMode &&
@@ -244,8 +301,25 @@ export function SandboxTerminal({
244
301
  liveness !== null &&
245
302
  liveness !== undefined &&
246
303
  result.chunks.length === 0;
304
+ const surfaceState = terminalSurfaceState({
305
+ ready,
306
+ acceptsInput,
307
+ inputPending,
308
+ ptyStatus,
309
+ booting,
310
+ });
247
311
 
248
312
  function handleActivate() {
313
+ // Capture-phase activation may synchronously negotiate a fresh terminal
314
+ // capability and rerender the shell. Focus xterm before that state change so
315
+ // the user's first keystroke or paste reaches its already-bounded input
316
+ // queue instead of landing on the surrounding dock.
317
+ const term = termRef.current;
318
+ if (term && !readOnly && (ptyAttachable || result.write !== null)) {
319
+ term.options.disableStdin = false;
320
+ term.options.cursorBlink = true;
321
+ }
322
+ term?.focus();
249
323
  if (!activated) setActivated(true);
250
324
  onActivate?.();
251
325
  }
@@ -361,10 +435,6 @@ export function SandboxTerminal({
361
435
  requestAnimationFrame(() => {
362
436
  if (disposed) return;
363
437
  settle();
364
- if (ptyOutputQueueRef.current.length > 0) {
365
- for (const data of ptyOutputQueueRef.current) term.write(data);
366
- ptyOutputQueueRef.current = [];
367
- }
368
438
  el.setAttribute("data-og-term-ready", "true");
369
439
  if (typeof globalThis.__OG_TERMINAL_DEBUG__ === "function") {
370
440
  globalThis.__OG_TERMINAL_DEBUG__({
@@ -397,7 +467,7 @@ export function SandboxTerminal({
397
467
 
398
468
  // Sync stdin/cursor at runtime (NOT a remount) when interactivity flips, so the
399
469
  // projection→PTY handoff keeps the single Terminal instance + its scrollback.
400
- useEffect(() => {
470
+ useLayoutEffect(() => {
401
471
  const term = termRef.current;
402
472
  if (!ready || !term) return;
403
473
  term.options.disableStdin = !interactive;
@@ -455,16 +525,24 @@ export function SandboxTerminal({
455
525
  }
456
526
  }, [ready, result.chunks, ptyMode]);
457
527
 
458
- // Wire interactive input when allowed. PTY mode pipes keystrokes to the ttyd
459
- // socket; firehose mode uses the legacy ptyWrite-over-HTTP fn.
460
- useEffect(() => {
528
+ // Wire interactive input when allowed. `ptyWrite` is stable across socket
529
+ // status changes, so this subscription does not tear down while a grant opens
530
+ // or rotates and cannot create a first-keystroke gap.
531
+ useLayoutEffect(() => {
461
532
  const term = termRef.current;
462
- if (!ready || !term || !interactive) return;
463
- const sink = ptyMode ? ptyWrite : result.write;
464
- if (!sink) return;
465
- const sub = term.onData((data) => sink(data));
466
- return () => sub.dispose();
467
- }, [ready, interactive, ptyMode, ptyWrite, result.write]);
533
+ if (!ready || !term || !interactive) {
534
+ setInputReady(false);
535
+ return;
536
+ }
537
+ const sink = ptyAttachable ? ptyWrite : result.write;
538
+ if (!sink) {
539
+ setInputReady(false);
540
+ return;
541
+ }
542
+ const sub = subscribeTerminalInput(term, sink, true);
543
+ setInputReady(sub !== null);
544
+ return () => sub?.dispose();
545
+ }, [ready, interactive, ptyAttachable, ptyWrite, result.write]);
468
546
 
469
547
  // In PTY mode, tell ttyd the window size on the xterm resize event.
470
548
  useEffect(() => {
@@ -551,6 +629,10 @@ export function SandboxTerminal({
551
629
  className="h-full w-full"
552
630
  style={{ visibility: ready ? "visible" : "hidden" }}
553
631
  data-opengeni-terminal
632
+ data-opengeni-terminal-ready={ready ? "true" : "false"}
633
+ data-opengeni-terminal-interactive={surfaceState === "interactive" ? "true" : "false"}
634
+ data-opengeni-terminal-state={surfaceState}
635
+ data-opengeni-terminal-status={ptyAttachable ? ptyStatus : "firehose"}
554
636
  />
555
637
  </div>
556
638
  </div>