@opengeni/react 1.4.4 → 1.4.5-canary.37458084490001

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 (234) hide show
  1. package/README.md +17 -6
  2. package/dist/artifacts.d.ts +1 -0
  3. package/dist/artifacts.js +22 -15
  4. package/dist/artifacts.js.map +1 -1
  5. package/dist/{browser-viewer-5EEAS3IU.js → browser-viewer-CV23VIHB.js} +8 -6
  6. package/dist/chunk-3ZC6QPOM.js +174 -0
  7. package/dist/chunk-3ZC6QPOM.js.map +1 -0
  8. package/dist/{chunk-YVFRBXST.js → chunk-4BCVCJSU.js} +2 -2
  9. package/dist/{chunk-JHTIMBOL.js → chunk-5JSUNNQN.js} +26 -4
  10. package/dist/{chunk-JHTIMBOL.js.map → chunk-5JSUNNQN.js.map} +1 -1
  11. package/dist/{chunk-I6OY3MWC.js → chunk-6FOTSVWU.js} +2 -2
  12. package/dist/{chunk-IUHHPMAX.js → chunk-7RRBX5EM.js} +14 -85
  13. package/dist/chunk-7RRBX5EM.js.map +1 -0
  14. package/dist/{chunk-L2J3PU7A.js → chunk-7ZB7I3KP.js} +24 -98
  15. package/dist/chunk-7ZB7I3KP.js.map +1 -0
  16. package/dist/{chunk-LVS3MH5T.js → chunk-ATIRUHBZ.js} +128 -10
  17. package/dist/chunk-ATIRUHBZ.js.map +1 -0
  18. package/dist/chunk-BOFEQC6K.js +162 -0
  19. package/dist/chunk-BOFEQC6K.js.map +1 -0
  20. package/dist/{chunk-2OQA3CPB.js → chunk-CESBPQP5.js} +19 -5
  21. package/dist/chunk-CESBPQP5.js.map +1 -0
  22. package/dist/{chunk-IYQCWGU5.js → chunk-GHJU7XG7.js} +35 -62
  23. package/dist/chunk-GHJU7XG7.js.map +1 -0
  24. package/dist/{chunk-G6YRCIYJ.js → chunk-HNVWMDNN.js} +55 -60
  25. package/dist/chunk-HNVWMDNN.js.map +1 -0
  26. package/dist/{chunk-PHBFJCEU.js → chunk-I5EGRXXG.js} +110 -7
  27. package/dist/chunk-I5EGRXXG.js.map +1 -0
  28. package/dist/{chunk-PBCZL4JP.js → chunk-INIZSNXT.js} +2 -2
  29. package/dist/{chunk-XS67YG5R.js → chunk-IX3K4THH.js} +24 -3
  30. package/dist/chunk-IX3K4THH.js.map +1 -0
  31. package/dist/chunk-IZV5FTJC.js +55 -0
  32. package/dist/chunk-IZV5FTJC.js.map +1 -0
  33. package/dist/chunk-JDJJMBB3.js +1883 -0
  34. package/dist/chunk-JDJJMBB3.js.map +1 -0
  35. package/dist/{chunk-C5BF4L26.js → chunk-KHF2VRXF.js} +7 -7
  36. package/dist/{chunk-QMMCJGFI.js → chunk-L5OKM2VX.js} +2 -2
  37. package/dist/chunk-LHF6BVM4.js +83 -0
  38. package/dist/chunk-LHF6BVM4.js.map +1 -0
  39. package/dist/chunk-N7S72EXK.js +25 -0
  40. package/dist/chunk-N7S72EXK.js.map +1 -0
  41. package/dist/{chunk-CVYCLFMH.js → chunk-PEHTX3V7.js} +19 -1
  42. package/dist/chunk-PEHTX3V7.js.map +1 -0
  43. package/dist/chunk-R5R5RQQD.js +51 -0
  44. package/dist/chunk-R5R5RQQD.js.map +1 -0
  45. package/dist/chunk-RI42H5IU.js +326 -0
  46. package/dist/chunk-RI42H5IU.js.map +1 -0
  47. package/dist/{chunk-JH7V627O.js → chunk-SHLZMAJN.js} +2 -11
  48. package/dist/chunk-SHLZMAJN.js.map +1 -0
  49. package/dist/{chunk-EAOSEIXB.js → chunk-TZQSHCZN.js} +692 -1996
  50. package/dist/chunk-TZQSHCZN.js.map +1 -0
  51. package/dist/chunk-ZIB4H5LN.js +101 -0
  52. package/dist/chunk-ZIB4H5LN.js.map +1 -0
  53. package/dist/client.d.ts +9 -3
  54. package/dist/clipboard.js +1 -1
  55. package/dist/components/browser-viewer.d.ts +6 -0
  56. package/dist/components/composer.d.ts +3 -69
  57. package/dist/components/human-input-form.d.ts +3 -32
  58. package/dist/components/markdown.d.ts +8 -0
  59. package/dist/components/model-policy-picker.d.ts +4 -37
  60. package/dist/components/opper-mark.d.ts +7 -0
  61. package/dist/components/provider-recovery-notice.d.ts +15 -0
  62. package/dist/components/remark-soft-line-breaks.d.ts +38 -0
  63. package/dist/components/sandbox-workspace.d.ts +13 -0
  64. package/dist/components/session-chrome.d.ts +5 -3
  65. package/dist/components/session-conversation.d.ts +15 -3
  66. package/dist/composer-messages.d.ts +74 -0
  67. package/dist/composer-messages.js +9 -0
  68. package/dist/composer.js +15 -10
  69. package/dist/{computer-viewer-ELNLZGHI.js → computer-viewer-K4CKYRX6.js} +4 -4
  70. package/dist/connect-setup.d.ts +2 -0
  71. package/dist/connect.d.ts +1 -1
  72. package/dist/connect.js +21 -6
  73. package/dist/connect.js.map +1 -1
  74. package/dist/{embedded-voice-NIL4RDX3.js → embedded-voice-3ZFWWJ6O.js} +6 -4
  75. package/dist/{embedded-voice-NIL4RDX3.js.map → embedded-voice-3ZFWWJ6O.js.map} +1 -1
  76. package/dist/hooks/shared-machines-list.d.ts +4 -0
  77. package/dist/hooks/use-client-config-flags.d.ts +6 -0
  78. package/dist/hooks/use-goal.d.ts +2 -1
  79. package/dist/human-input-model.d.ts +40 -0
  80. package/dist/index.d.ts +4 -1
  81. package/dist/index.js +116 -73
  82. package/dist/index.js.map +1 -1
  83. package/dist/interaction.js +5 -5
  84. package/dist/{latest-question-2POFPYNB.js → latest-question-SWC6QEVN.js} +3 -3
  85. package/dist/lib/clipboard.d.ts +7 -0
  86. package/dist/lib/provider-recovery.d.ts +49 -0
  87. package/dist/machines.js +2 -2
  88. package/dist/model-mark-paths.d.ts +34 -0
  89. package/dist/model-mark-paths.js +16 -0
  90. package/dist/model-picker-model.d.ts +111 -0
  91. package/dist/model-picker-model.js +25 -0
  92. package/dist/model-picker-model.js.map +1 -0
  93. package/dist/{model-policy-picker-menu-WRNSRFQC.js → model-policy-picker-menu-NM4RPTY6.js} +6 -4
  94. package/dist/model-policy-picker-menu-NM4RPTY6.js.map +1 -0
  95. package/dist/model-policy.d.ts +10 -0
  96. package/dist/model-policy.js +5 -1
  97. package/dist/organization-model.d.ts +32 -0
  98. package/dist/organization-model.js +65 -0
  99. package/dist/organization-model.js.map +1 -0
  100. package/dist/{platform-activity-row-PJJVATTZ.js → platform-activity-row-NJ47YBXW.js} +7 -84
  101. package/dist/platform-activity-row-NJ47YBXW.js.map +1 -0
  102. package/dist/queue-presentation-model.d.ts +72 -0
  103. package/dist/queue-presentation-model.js +38 -0
  104. package/dist/queue-presentation-model.js.map +1 -0
  105. package/dist/{queue-surface-implementation-67FS7FUI.js → queue-surface-implementation-YPJFQ3O6.js} +24 -252
  106. package/dist/queue-surface-implementation-YPJFQ3O6.js.map +1 -0
  107. package/dist/realtime.js +5 -3
  108. package/dist/{sandbox-files-34CJJNOY.js → sandbox-files-RCVCMXG7.js} +5 -5
  109. package/dist/sandbox-files-RCVCMXG7.js.map +1 -0
  110. package/dist/sandbox-label-model.d.ts +23 -0
  111. package/dist/sandbox-label-model.js +28 -0
  112. package/dist/sandbox-label-model.js.map +1 -0
  113. package/dist/session-attention-model.d.ts +8 -0
  114. package/dist/session-attention-model.js +46 -0
  115. package/dist/session-attention-model.js.map +1 -0
  116. package/dist/session-feedback-model.d.ts +20 -0
  117. package/dist/session-feedback-model.js +56 -0
  118. package/dist/session-feedback-model.js.map +1 -0
  119. package/dist/session-list-model.d.ts +96 -0
  120. package/dist/session-list-model.js +219 -0
  121. package/dist/session-list-model.js.map +1 -0
  122. package/dist/session-status-model.d.ts +19 -0
  123. package/dist/session-ui.js +26 -17
  124. package/dist/session-ui.js.map +1 -1
  125. package/dist/session.js +8 -6
  126. package/dist/testing/session-scenarios.d.ts +10 -0
  127. package/dist/testing/session-scenarios.js +303 -0
  128. package/dist/testing/session-scenarios.js.map +1 -0
  129. package/dist/timeline/genie-copy.d.ts +3 -0
  130. package/dist/timeline/genie-loading.d.ts +11 -0
  131. package/dist/timeline/index.d.ts +1 -1
  132. package/dist/timeline/notice-presentation.d.ts +7 -0
  133. package/dist/timeline/patch-apply-command.d.ts +18 -0
  134. package/dist/timeline/platform-activity-presentation.d.ts +7 -0
  135. package/dist/timeline/presented-image.d.ts +6 -1
  136. package/dist/timeline/question-nav-model.d.ts +23 -0
  137. package/dist/timeline/registry.d.ts +10 -1
  138. package/dist/timeline/retained-loaders.d.ts +1 -1
  139. package/dist/timeline/tool-presentation.d.ts +172 -0
  140. package/dist/timeline/turn-summary-model.d.ts +90 -0
  141. package/dist/timeline/turn-summary.d.ts +4 -74
  142. package/dist/timeline/work-presentation.d.ts +42 -0
  143. package/dist/timeline-model.d.ts +24 -0
  144. package/dist/timeline-model.js +152 -0
  145. package/dist/timeline-model.js.map +1 -0
  146. package/package.json +47 -3
  147. package/src/artifacts.ts +1 -0
  148. package/src/client.ts +9 -3
  149. package/src/components/browser-viewer.tsx +23 -2
  150. package/src/components/claude-mark.tsx +2 -1
  151. package/src/components/composer.tsx +4 -144
  152. package/src/components/grok-mark.tsx +2 -1
  153. package/src/components/human-input-form.tsx +13 -124
  154. package/src/components/markdown.tsx +19 -1
  155. package/src/components/message-timeline.tsx +45 -99
  156. package/src/components/model-mark.tsx +2 -1
  157. package/src/components/model-policy-picker-menu.tsx +30 -85
  158. package/src/components/model-policy-picker.tsx +15 -148
  159. package/src/components/opper-mark.tsx +18 -0
  160. package/src/components/provider-recovery-notice.tsx +41 -0
  161. package/src/components/queue-surface-implementation.tsx +20 -349
  162. package/src/components/remark-soft-line-breaks.ts +166 -0
  163. package/src/components/sandbox-workspace.tsx +17 -1
  164. package/src/components/session-chrome.tsx +114 -99
  165. package/src/components/session-conversation.tsx +55 -6
  166. package/src/components/session-status.tsx +17 -16
  167. package/src/composer-messages.ts +150 -0
  168. package/src/connect-setup.tsx +45 -11
  169. package/src/connect.ts +1 -1
  170. package/src/hooks/shared-machines-list.ts +34 -1
  171. package/src/hooks/use-client-config-flags.ts +8 -0
  172. package/src/hooks/use-goal.ts +66 -10
  173. package/src/human-input-model.ts +147 -0
  174. package/src/index.ts +16 -0
  175. package/src/lib/clipboard.ts +29 -0
  176. package/src/lib/format.ts +7 -0
  177. package/src/lib/provider-recovery.ts +202 -0
  178. package/src/model-mark-paths.ts +53 -0
  179. package/src/model-picker-model.ts +330 -0
  180. package/src/model-policy.ts +43 -2
  181. package/src/organization-model.ts +118 -0
  182. package/src/queue-presentation-model.ts +464 -0
  183. package/src/sandbox-label-model.ts +41 -0
  184. package/src/session-attention-model.ts +59 -0
  185. package/src/session-feedback-model.ts +95 -0
  186. package/src/session-list-model.ts +327 -0
  187. package/src/session-status-model.ts +62 -0
  188. package/src/testing/session-scenarios.ts +326 -0
  189. package/src/timeline/activity-rail.tsx +5 -17
  190. package/src/timeline/genie-copy.ts +19 -0
  191. package/src/timeline/genie-loading.tsx +13 -18
  192. package/src/timeline/index.ts +1 -0
  193. package/src/timeline/notice-presentation.ts +22 -0
  194. package/src/timeline/patch-apply-command.tsx +135 -0
  195. package/src/timeline/platform-activity-presentation.ts +122 -0
  196. package/src/timeline/platform-activity-row.tsx +6 -96
  197. package/src/timeline/presented-image.ts +16 -2
  198. package/src/timeline/question-nav-model.ts +28 -0
  199. package/src/timeline/registry.ts +11 -1
  200. package/src/timeline/retained-loaders.ts +9 -4
  201. package/src/timeline/rolling-activity.tsx +7 -19
  202. package/src/timeline/tool-presentation.ts +1316 -0
  203. package/src/timeline/tool-renderers.tsx +226 -1025
  204. package/src/timeline/turn-summary-model.ts +338 -0
  205. package/src/timeline/turn-summary.tsx +21 -326
  206. package/src/timeline/work-presentation.ts +139 -0
  207. package/src/timeline-model.ts +108 -0
  208. package/styles/compiled.css +3 -0
  209. package/styles/connect.css +4 -0
  210. package/dist/chunk-2OQA3CPB.js.map +0 -1
  211. package/dist/chunk-CVYCLFMH.js.map +0 -1
  212. package/dist/chunk-EAOSEIXB.js.map +0 -1
  213. package/dist/chunk-G6YRCIYJ.js.map +0 -1
  214. package/dist/chunk-HXYOSSQO.js +0 -117
  215. package/dist/chunk-HXYOSSQO.js.map +0 -1
  216. package/dist/chunk-IUHHPMAX.js.map +0 -1
  217. package/dist/chunk-IYQCWGU5.js.map +0 -1
  218. package/dist/chunk-JH7V627O.js.map +0 -1
  219. package/dist/chunk-L2J3PU7A.js.map +0 -1
  220. package/dist/chunk-LVS3MH5T.js.map +0 -1
  221. package/dist/chunk-PHBFJCEU.js.map +0 -1
  222. package/dist/chunk-XS67YG5R.js.map +0 -1
  223. package/dist/platform-activity-row-PJJVATTZ.js.map +0 -1
  224. package/dist/queue-surface-implementation-67FS7FUI.js.map +0 -1
  225. /package/dist/{browser-viewer-5EEAS3IU.js.map → browser-viewer-CV23VIHB.js.map} +0 -0
  226. /package/dist/{chunk-YVFRBXST.js.map → chunk-4BCVCJSU.js.map} +0 -0
  227. /package/dist/{chunk-I6OY3MWC.js.map → chunk-6FOTSVWU.js.map} +0 -0
  228. /package/dist/{chunk-PBCZL4JP.js.map → chunk-INIZSNXT.js.map} +0 -0
  229. /package/dist/{chunk-C5BF4L26.js.map → chunk-KHF2VRXF.js.map} +0 -0
  230. /package/dist/{chunk-QMMCJGFI.js.map → chunk-L5OKM2VX.js.map} +0 -0
  231. /package/dist/{computer-viewer-ELNLZGHI.js.map → composer-messages.js.map} +0 -0
  232. /package/dist/{model-policy-picker-menu-WRNSRFQC.js.map → computer-viewer-K4CKYRX6.js.map} +0 -0
  233. /package/dist/{latest-question-2POFPYNB.js.map → latest-question-SWC6QEVN.js.map} +0 -0
  234. /package/dist/{sandbox-files-34CJJNOY.js.map → model-mark-paths.js.map} +0 -0
@@ -0,0 +1,122 @@
1
+ import type { SandboxItem, StartupPhaseItem, WorkerItem } from "./types";
2
+
3
+ /* Pure titles and durations for platform activity rows (startup phases and
4
+ sandbox operations), shared by DOM and non-DOM renderers. */
5
+
6
+ export function sandboxRowTitle(item: SandboxItem, displayName: (name: string) => string): string {
7
+ if (item.name === "sandbox.provision") {
8
+ return startupPhaseTitle(
9
+ "sandbox",
10
+ item.status === "cancelled" ? "complete" : item.status,
11
+ item.origin ?? null,
12
+ );
13
+ }
14
+ return displayName(item.name);
15
+ }
16
+
17
+ export function startupPhaseTitle(
18
+ phase: StartupPhaseItem["phase"],
19
+ status: StartupPhaseItem["status"],
20
+ outcome: StartupPhaseItem["outcome"],
21
+ ): string {
22
+ if (status === "complete" && phase === "sandbox" && outcome && outcome !== "skipped") {
23
+ return `Sandbox ${outcome === "resumed" ? "reattached" : outcome}`;
24
+ }
25
+ if (status === "complete" && phase === "rig" && outcome === "skipped") {
26
+ return "Sandbox Environment already ready";
27
+ }
28
+ const statusIndex =
29
+ status === "running" ? 0 : status === "failed" ? 1 : status === "cancelled" ? 2 : 3;
30
+ return STARTUP_PHASE_TITLES[phase][statusIndex];
31
+ }
32
+
33
+ export const STARTUP_WAIT_TITLES: Record<NonNullable<StartupPhaseItem["blockedReason"]>, string> = {
34
+ capture_in_progress: "Waiting for sandbox checkpoint",
35
+ rotation_in_progress: "Waiting for sandbox rotation",
36
+ provider_recovery_in_progress: "Waiting for sandbox recovery",
37
+ };
38
+
39
+ const STARTUP_PHASE_TITLES: Record<
40
+ StartupPhaseItem["phase"],
41
+ readonly [string, string, string, string]
42
+ > = {
43
+ queue: [
44
+ "Waiting for a worker",
45
+ "Worker startup failed",
46
+ "Worker wait interrupted",
47
+ "Worker started",
48
+ ],
49
+ sandbox: [
50
+ "Starting sandbox",
51
+ "Sandbox didn’t start",
52
+ "Sandbox startup interrupted",
53
+ "Sandbox ready",
54
+ ],
55
+ rig: [
56
+ "Setting up sandbox environment",
57
+ "Sandbox Environment setup failed",
58
+ "Sandbox Environment setup interrupted",
59
+ "Sandbox Environment ready",
60
+ ],
61
+ repository: [
62
+ "Preparing repository",
63
+ "Repository preparation failed",
64
+ "Repository preparation interrupted",
65
+ "Repository ready",
66
+ ],
67
+ files: [
68
+ "Preparing files",
69
+ "File preparation failed",
70
+ "File preparation interrupted",
71
+ "Files ready",
72
+ ],
73
+ tools: [
74
+ "Connecting tools",
75
+ "Tool connection failed",
76
+ "Tool connection interrupted",
77
+ "Tools ready",
78
+ ],
79
+ model_preparation: [
80
+ "Preparing runtime and model request",
81
+ "Runtime/model preparation failed",
82
+ "Runtime/model preparation interrupted",
83
+ "Model request dispatched",
84
+ ],
85
+ provider_first_byte: [
86
+ "Waiting for model",
87
+ "Model didn’t respond",
88
+ "Model wait interrupted",
89
+ "Model started responding",
90
+ ],
91
+ };
92
+
93
+ export function startupDuration(durationMs: number | null): string | null {
94
+ if (durationMs === null || !Number.isFinite(durationMs) || durationMs < 0) return null;
95
+ if (durationMs < 1_000) return `${Math.round(durationMs)}ms`;
96
+ if (durationMs < 60_000) return `${(durationMs / 1_000).toFixed(1)}s`;
97
+ const minutes = Math.floor(durationMs / 60_000);
98
+ const seconds = Math.round((durationMs % 60_000) / 1_000);
99
+ return `${minutes}m ${String(seconds).padStart(2, "0")}s`;
100
+ }
101
+
102
+ /** The worker step title for a spawn or message, by status. */
103
+ export function workerRowTitle(item: Pick<WorkerItem, "action" | "status">): string {
104
+ const running = item.status === "running";
105
+ const failed = item.status === "failed";
106
+ const cancelled = item.status === "cancelled";
107
+ return item.action === "spawn"
108
+ ? running
109
+ ? "Spawning worker"
110
+ : failed
111
+ ? "Worker spawn failed"
112
+ : cancelled
113
+ ? "Worker interrupted"
114
+ : "Worker spawned"
115
+ : running
116
+ ? "Messaging worker"
117
+ : failed
118
+ ? "Worker message failed"
119
+ : cancelled
120
+ ? "Worker interrupted"
121
+ : "Worker messaged";
122
+ }
@@ -1,3 +1,9 @@
1
+ import {
2
+ sandboxRowTitle,
3
+ startupDuration,
4
+ startupPhaseTitle,
5
+ STARTUP_WAIT_TITLES,
6
+ } from "./platform-activity-presentation";
1
7
  import type { ReasoningItem, SandboxItem, StartupPhaseItem } from "./types";
2
8
 
3
9
  type RowJsx = typeof import("react/jsx-runtime").jsx;
@@ -118,99 +124,3 @@ function lucideIcon(j: RowJsx, s: RowJsxs, name: string, children: ReturnType<Ro
118
124
  children,
119
125
  });
120
126
  }
121
-
122
- function sandboxRowTitle(item: SandboxItem, displayName: (name: string) => string): string {
123
- if (item.name === "sandbox.provision") {
124
- return startupPhaseTitle(
125
- "sandbox",
126
- item.status === "cancelled" ? "complete" : item.status,
127
- item.origin ?? null,
128
- );
129
- }
130
- return displayName(item.name);
131
- }
132
-
133
- function startupPhaseTitle(
134
- phase: StartupPhaseItem["phase"],
135
- status: StartupPhaseItem["status"],
136
- outcome: StartupPhaseItem["outcome"],
137
- ): string {
138
- if (status === "complete" && phase === "sandbox" && outcome && outcome !== "skipped") {
139
- return `Sandbox ${outcome === "resumed" ? "reattached" : outcome}`;
140
- }
141
- if (status === "complete" && phase === "rig" && outcome === "skipped") {
142
- return "Sandbox Environment already ready";
143
- }
144
- const statusIndex =
145
- status === "running" ? 0 : status === "failed" ? 1 : status === "cancelled" ? 2 : 3;
146
- return STARTUP_PHASE_TITLES[phase][statusIndex];
147
- }
148
-
149
- const STARTUP_WAIT_TITLES: Record<NonNullable<StartupPhaseItem["blockedReason"]>, string> = {
150
- capture_in_progress: "Waiting for sandbox checkpoint",
151
- rotation_in_progress: "Waiting for sandbox rotation",
152
- provider_recovery_in_progress: "Waiting for sandbox recovery",
153
- };
154
-
155
- const STARTUP_PHASE_TITLES: Record<
156
- StartupPhaseItem["phase"],
157
- readonly [string, string, string, string]
158
- > = {
159
- queue: [
160
- "Waiting for a worker",
161
- "Worker startup failed",
162
- "Worker wait interrupted",
163
- "Worker started",
164
- ],
165
- sandbox: [
166
- "Starting sandbox",
167
- "Sandbox didn’t start",
168
- "Sandbox startup interrupted",
169
- "Sandbox ready",
170
- ],
171
- rig: [
172
- "Setting up sandbox environment",
173
- "Sandbox Environment setup failed",
174
- "Sandbox Environment setup interrupted",
175
- "Sandbox Environment ready",
176
- ],
177
- repository: [
178
- "Preparing repository",
179
- "Repository preparation failed",
180
- "Repository preparation interrupted",
181
- "Repository ready",
182
- ],
183
- files: [
184
- "Preparing files",
185
- "File preparation failed",
186
- "File preparation interrupted",
187
- "Files ready",
188
- ],
189
- tools: [
190
- "Connecting tools",
191
- "Tool connection failed",
192
- "Tool connection interrupted",
193
- "Tools ready",
194
- ],
195
- model_preparation: [
196
- "Preparing runtime and model request",
197
- "Runtime/model preparation failed",
198
- "Runtime/model preparation interrupted",
199
- "Model request dispatched",
200
- ],
201
- provider_first_byte: [
202
- "Waiting for model",
203
- "Model didn’t respond",
204
- "Model wait interrupted",
205
- "Model started responding",
206
- ],
207
- };
208
-
209
- function startupDuration(durationMs: number | null): string | null {
210
- if (durationMs === null || !Number.isFinite(durationMs) || durationMs < 0) return null;
211
- if (durationMs < 1_000) return `${Math.round(durationMs)}ms`;
212
- if (durationMs < 60_000) return `${(durationMs / 1_000).toFixed(1)}s`;
213
- const minutes = Math.floor(durationMs / 60_000);
214
- const seconds = Math.round((durationMs % 60_000) / 1_000);
215
- return `${minutes}m ${String(seconds).padStart(2, "0")}s`;
216
- }
@@ -20,7 +20,15 @@ export function isRetainedImageContentType(contentType: string): boolean {
20
20
  ].includes(contentType);
21
21
  }
22
22
 
23
- /** Deliberately published images are primary output, not incidental screenshots. */
23
+ /** A published patch, applied locally with `git apply`. */
24
+ export function isPatchFilename(filename: string): boolean {
25
+ return /\.(patch|diff)$/iu.test(filename.trim());
26
+ }
27
+
28
+ /**
29
+ * Deliberately published images and patches are primary output, not
30
+ * incidental screenshots or work files.
31
+ */
24
32
  export function activityPresentsImage(items: readonly ActivityItem[]): boolean {
25
33
  return items.some((item) => {
26
34
  if (item.kind !== "tool-call") return false;
@@ -30,7 +38,13 @@ export function activityPresentsImage(items: readonly ActivityItem[]): boolean {
30
38
  const output = normalizeMcpOutput(item.output);
31
39
  if (output.isError) return false;
32
40
  const receipt = parseSandboxFileArtifactReceipt(output.text);
33
- return receipt !== null && isRetainedImageContentType(receipt.artifact.contentType);
41
+ return (
42
+ receipt !== null &&
43
+ (isRetainedImageContentType(receipt.artifact.contentType) ||
44
+ // A published patch is the deliverable when there's no pull request:
45
+ // keep its card (with the apply command) visible like an image.
46
+ isPatchFilename(receipt.filename))
47
+ );
34
48
  });
35
49
  }
36
50
 
@@ -0,0 +1,28 @@
1
+ /**
2
+ * "Back to your message": which prompt the reader is reading the answer to, and
3
+ * whether that prompt has scrolled out of view. Renderer-neutral geometry so
4
+ * the web timeline (DOM rects) and native timelines (layout offsets) agree.
5
+ */
6
+
7
+ /** How far above the viewport a prompt must be before the pill appears. */
8
+ export const QUESTION_NAV_HIDDEN_PX = 24;
9
+ /** Breathing room above the question when the reader jumps back to it. */
10
+ export const QUESTION_NAV_MARGIN_PX = 12;
11
+
12
+ export type QuestionNavPrompt = { key: string; top: number; bottom: number };
13
+
14
+ /**
15
+ * The last prompt before the reading position (the viewport's middle) owns the
16
+ * response in view; it is a target only once it has scrolled fully above the
17
+ * viewport. Prompts are in document order; coordinates share one axis.
18
+ */
19
+ export function questionNavTarget(
20
+ prompts: readonly QuestionNavPrompt[],
21
+ view: { top: number; height: number },
22
+ ): string | null {
23
+ const middle = view.top + view.height / 2;
24
+ const prompt = [...prompts].reverse().find((candidate) => candidate.top <= middle);
25
+ if (!prompt) return null;
26
+ if (prompt.top >= view.top - QUESTION_NAV_HIDDEN_PX || prompt.bottom > view.top) return null;
27
+ return prompt.key;
28
+ }
@@ -35,7 +35,17 @@ export type RetainedScreenshotLoader = (
35
35
  export type RetainedArtifactLoader = (
36
36
  artifact: RetainedArtifactReference,
37
37
  signal: AbortSignal,
38
- ) => Promise<Uint8Array | { url: string } | null>;
38
+ options?: RetainedArtifactLoadOptions,
39
+ ) => Promise<Uint8Array | { url: string; expiresAt?: string | undefined } | null>;
40
+
41
+ export type RetainedArtifactLoadOptions = {
42
+ /**
43
+ * Ask for a short-lived download URL instead of bytes, for example to build a
44
+ * copyable `curl … | git apply` command. A loader that cannot mint one may
45
+ * still return bytes; callers must handle that.
46
+ */
47
+ prefer?: "url" | undefined;
48
+ };
39
49
 
40
50
  /** Mint an expiring source for native browser playback without loading video bytes in JS. */
41
51
  export type VideoArtifactPlaybackLoader = (
@@ -1,5 +1,6 @@
1
1
  import type { OpenGeniClient, RetainedArtifactReference } from "@opengeni/sdk";
2
2
  import type {
3
+ RetainedArtifactLoadOptions,
3
4
  RetainedArtifactLoader,
4
5
  RetainedScreenshotLoader,
5
6
  VideoArtifactPlaybackLoader,
@@ -21,21 +22,25 @@ export type RetainedVideoLoaderClient = Pick<OpenGeniClient, "createVideoArtifac
21
22
  * Bind permanent retained-artifact retrieval (generated images, published
22
23
  * files) to one authenticated workspace, for `MessageTimeline`'s
23
24
  * `loadRetainedArtifact`. Images get a short-lived URL; files are assembled
24
- * from verified byte ranges.
25
+ * from verified byte ranges unless the caller asks for a URL.
25
26
  */
26
27
  export function createWorkspaceRetainedArtifactLoader(
27
28
  client: RetainedArtifactLoaderClient,
28
29
  workspaceId: string,
29
30
  ): RetainedArtifactLoader {
30
- return async (artifact: RetainedArtifactReference, signal: AbortSignal) => {
31
- if (artifact.kind === "file") {
31
+ return async (
32
+ artifact: RetainedArtifactReference,
33
+ signal: AbortSignal,
34
+ options?: RetainedArtifactLoadOptions,
35
+ ) => {
36
+ if (artifact.kind === "file" && options?.prefer !== "url") {
32
37
  const download = await client.downloadRetainedArtifact(workspaceId, artifact, { signal });
33
38
  return download.bytes;
34
39
  }
35
40
  const download = await client.createRetainedArtifactDownloadUrl(workspaceId, artifact, {
36
41
  signal,
37
42
  });
38
- return { url: download.url };
43
+ return { url: download.url, expiresAt: download.expiresAt };
39
44
  };
40
45
  }
41
46
 
@@ -7,6 +7,7 @@ import { defaultToolRegistry } from "./tool-renderers";
7
7
  import type { ToolRegistry } from "./registry";
8
8
  import { toolDisplayName } from "./tool-display-name";
9
9
  import type { ActivityItem } from "./types";
10
+ import { rollingActivityItem } from "./work-presentation";
10
11
 
11
12
  /** One stable viewport; updates replace its content without moving the conversation. */
12
13
  export function RollingActivity({
@@ -25,21 +26,11 @@ export function RollingActivity({
25
26
  const reduced = useReducedMotion();
26
27
  const [mounted, setMounted] = useState(false);
27
28
  useEffect(() => setMounted(true), []);
28
- // Progress notes have their own line in the status row; the reel shows steps.
29
- const work = items.filter(
30
- (item) => item.kind !== "startup-phase" && item.kind !== "agent-message",
31
- );
32
- const active = work.filter((item) =>
33
- item.kind === "reasoning" ? item.streaming : "status" in item && item.status === "running",
34
- );
35
- // Advance with the event order; finishing a parallel tool must not replay an older one.
36
- const item =
37
- !mounted && previousItem && previousItem.kind !== "agent-message" ? previousItem : work.at(-1);
38
- if (!item) return null;
39
- const earlierCount = Math.max(
40
- 0,
41
- work.findIndex((entry) => entry.id === item.id),
42
- );
29
+ // Progress notes have their own line in the status row; the reel shows steps, advancing
30
+ // with the event order so finishing a parallel tool never replays an older one.
31
+ const selected = rollingActivityItem(items, previousItem, mounted);
32
+ if (!selected) return null;
33
+ const { item, earlierCount } = selected;
43
34
  const fallback = (
44
35
  <span className="og-rolling-label">
45
36
  <WrenchIcon className="size-3.5" />
@@ -49,10 +40,7 @@ export function RollingActivity({
49
40
  </span>
50
41
  );
51
42
  return (
52
- <span
53
- className="og-rolling-status"
54
- data-running={active.some((entry) => entry.id === item.id) ? "true" : undefined}
55
- >
43
+ <span className="og-rolling-status" data-running={selected.running ? "true" : undefined}>
56
44
  <span className="sr-only">
57
45
  {item.kind === "tool-call"
58
46
  ? toolDisplayName(item.name, item.display)