@opengeni/react 0.28.2 → 0.32.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 (208) hide show
  1. package/dist/approvals.d.ts +14 -0
  2. package/dist/{chunk-M7X4JZOD.js → chunk-4QOQ2DSR.js} +8 -1
  3. package/dist/chunk-4QOQ2DSR.js.map +1 -0
  4. package/dist/{chunk-HQFPO7VX.js → chunk-7CJOAW3V.js} +24 -3
  5. package/dist/{chunk-HQFPO7VX.js.map → chunk-7CJOAW3V.js.map} +1 -1
  6. package/dist/{chunk-PMSGE233.js → chunk-A2WITZTI.js} +377 -113
  7. package/dist/chunk-A2WITZTI.js.map +1 -0
  8. package/dist/{chunk-BC6RKIHS.js → chunk-GZ4KR67U.js} +11 -11
  9. package/dist/{chunk-4F2YLLHP.js → chunk-KZRUYCL4.js} +14 -17
  10. package/dist/chunk-KZRUYCL4.js.map +1 -0
  11. package/dist/{chunk-OCHBZ2X7.js → chunk-TCCE5ZU5.js} +192 -28
  12. package/dist/chunk-TCCE5ZU5.js.map +1 -0
  13. package/dist/chunk-U255M5EZ.js +178 -0
  14. package/dist/chunk-U255M5EZ.js.map +1 -0
  15. package/dist/{chunk-XVVQP7A4.js → chunk-WY2QN2CX.js} +2539 -2174
  16. package/dist/chunk-WY2QN2CX.js.map +1 -0
  17. package/dist/chunk-ZNNE2VES.js +6413 -0
  18. package/dist/chunk-ZNNE2VES.js.map +1 -0
  19. package/dist/client.d.ts +33 -0
  20. package/dist/commands/index.d.ts +3 -0
  21. package/dist/commands/registry.d.ts +41 -0
  22. package/dist/commands/types.d.ts +85 -0
  23. package/dist/components/approval-surface.d.ts +28 -0
  24. package/dist/components/chat-composer.d.ts +51 -0
  25. package/dist/components/code-editor.d.ts +43 -0
  26. package/dist/components/command-palette.d.ts +25 -0
  27. package/dist/components/composer-transcription-control.d.ts +36 -0
  28. package/dist/components/composer.d.ts +277 -0
  29. package/dist/components/copy-button.d.ts +26 -0
  30. package/dist/components/desktop-viewer.d.ts +85 -0
  31. package/dist/components/diff-view.d.ts +38 -0
  32. package/dist/components/enrollment-consent.d.ts +33 -0
  33. package/dist/components/enrollment-device-flow.d.ts +27 -0
  34. package/dist/components/file-browser.d.ts +47 -0
  35. package/dist/components/fleet-tile.d.ts +18 -0
  36. package/dist/components/human-input-form.d.ts +47 -0
  37. package/dist/components/machine-card.d.ts +21 -0
  38. package/dist/components/machine-dock-bar.d.ts +31 -0
  39. package/dist/components/machine-health-pill.d.ts +9 -0
  40. package/dist/components/machine-metrics.d.ts +15 -0
  41. package/dist/components/machine-status-pill.d.ts +54 -0
  42. package/dist/components/machines/health.d.ts +25 -0
  43. package/dist/components/machines/machine-detail.d.ts +14 -0
  44. package/dist/components/machines/metric-history-chart.d.ts +24 -0
  45. package/dist/components/machines/metric-sparkline.d.ts +9 -0
  46. package/dist/components/machines/series.d.ts +24 -0
  47. package/dist/components/machines-dashboard.d.ts +30 -0
  48. package/dist/components/markdown.d.ts +31 -0
  49. package/dist/components/message-timeline.d.ts +98 -0
  50. package/dist/components/model-picker.d.ts +19 -0
  51. package/dist/components/pierre-diff.d.ts +34 -0
  52. package/dist/components/pierre-file.d.ts +27 -0
  53. package/dist/components/queue-draft-policy.d.ts +7 -0
  54. package/dist/components/queue-surface-implementation.d.ts +20 -0
  55. package/dist/components/queue-surface-state.d.ts +12 -0
  56. package/dist/components/queue-surface.d.ts +12 -0
  57. package/dist/components/sandbox-files.d.ts +49 -0
  58. package/dist/components/sandbox-terminal.d.ts +123 -0
  59. package/dist/components/sandbox-workspace.d.ts +128 -0
  60. package/dist/components/session-chrome.d.ts +70 -0
  61. package/dist/components/session-status.d.ts +25 -0
  62. package/dist/components/soften-streaming-markdown.d.ts +13 -0
  63. package/dist/components/stream-reveal.d.ts +37 -0
  64. package/dist/components/tip-follow.d.ts +139 -0
  65. package/dist/components/tooltip.d.ts +16 -0
  66. package/dist/components/workbench-changes.d.ts +34 -0
  67. package/dist/components/workspace-dock.d.ts +35 -0
  68. package/dist/composer.d.ts +11 -7
  69. package/dist/composer.js +9 -6
  70. package/dist/hooks/internal.d.ts +46 -0
  71. package/dist/hooks/use-available-models.d.ts +37 -0
  72. package/dist/hooks/use-billing-usage.d.ts +26 -0
  73. package/dist/hooks/use-codex-accounts.d.ts +69 -0
  74. package/dist/{use-composer-ClHy1rkL.d.ts → hooks/use-composer.d.ts} +16 -24
  75. package/dist/hooks/use-desktop-stream.d.ts +43 -0
  76. package/dist/hooks/use-environments.d.ts +35 -0
  77. package/dist/{use-file-attachments-BmT8WtqO.d.ts → hooks/use-file-attachments.d.ts} +6 -9
  78. package/dist/hooks/use-goal.d.ts +46 -0
  79. package/dist/hooks/use-human-input.d.ts +26 -0
  80. package/dist/hooks/use-last-started-turn-policy.d.ts +27 -0
  81. package/dist/hooks/use-machine-chip.d.ts +54 -0
  82. package/dist/hooks/use-machines.d.ts +81 -0
  83. package/dist/hooks/use-packs.d.ts +27 -0
  84. package/dist/hooks/use-relay-frame-stream.d.ts +53 -0
  85. package/dist/hooks/use-rigs.d.ts +87 -0
  86. package/dist/hooks/use-sandbox-files.d.ts +108 -0
  87. package/dist/hooks/use-sandbox-git.d.ts +58 -0
  88. package/dist/hooks/use-sandbox-terminal.d.ts +62 -0
  89. package/dist/hooks/use-scheduled-tasks.d.ts +15 -0
  90. package/dist/hooks/use-session-capabilities.d.ts +83 -0
  91. package/dist/hooks/use-session-control.d.ts +22 -0
  92. package/dist/hooks/use-session-events.d.ts +92 -0
  93. package/dist/hooks/use-session-lineage.d.ts +17 -0
  94. package/dist/hooks/use-session-mcp-approval-policy.d.ts +23 -0
  95. package/dist/hooks/use-session.d.ts +23 -0
  96. package/dist/hooks/use-slash-commands.d.ts +66 -0
  97. package/dist/hooks/use-terminal-stream.d.ts +46 -0
  98. package/dist/hooks/use-transcription.d.ts +73 -0
  99. package/dist/hooks/use-turn-queue.d.ts +44 -0
  100. package/dist/hooks/use-voice-input.d.ts +25 -0
  101. package/dist/hooks/use-windowed-sections.d.ts +42 -0
  102. package/dist/hooks/use-workspace-capture.d.ts +49 -0
  103. package/dist/hooks/use-workspace-edit.d.ts +73 -0
  104. package/dist/hooks/use-workspace-sessions.d.ts +30 -0
  105. package/dist/hooks/use-workspaces.d.ts +22 -0
  106. package/dist/human-input.d.ts +18 -0
  107. package/dist/index.d.ts +144 -2119
  108. package/dist/index.js +421 -324
  109. package/dist/index.js.map +1 -1
  110. package/dist/lib/clipboard.d.ts +7 -0
  111. package/dist/lib/cn.d.ts +3 -0
  112. package/dist/lib/format.d.ts +48 -0
  113. package/dist/lib/git-patch.d.ts +8 -0
  114. package/dist/lib/motion-inspect.d.ts +5 -0
  115. package/dist/lib/motion.d.ts +14 -0
  116. package/dist/lib/noto-sans-arabic-loader.d.ts +1 -0
  117. package/dist/lib/noto-sans-jp-loader.generated.d.ts +1 -0
  118. package/dist/lib/relay-wire.d.ts +30 -0
  119. package/dist/lib/use-portal-token-style.d.ts +9 -0
  120. package/dist/lib/use-theme-type.d.ts +5 -0
  121. package/dist/lib/use-unicode-fonts.d.ts +12 -0
  122. package/dist/lib/xterm-renderer.d.ts +19 -0
  123. package/dist/lib/xterm-theme.d.ts +75 -0
  124. package/dist/machines.d.ts +30 -413
  125. package/dist/machines.js +3 -3
  126. package/dist/model-policy.d.ts +32 -0
  127. package/dist/model-policy.js +33 -0
  128. package/dist/model-policy.js.map +1 -0
  129. package/dist/provider.d.ts +16 -0
  130. package/dist/{queue-surface-implementation-UQ535OKQ.js → queue-surface-implementation-THPAKGMD.js} +2 -2
  131. package/dist/{queue-surface-implementation-UQ535OKQ.js.map → queue-surface-implementation-THPAKGMD.js.map} +1 -1
  132. package/dist/session-context.d.ts +82 -0
  133. package/dist/session-ui.d.ts +10 -7
  134. package/dist/session-ui.js +9 -5
  135. package/dist/session.d.ts +28 -6
  136. package/dist/session.js +11 -11
  137. package/dist/timeline/activity-rail.d.ts +19 -0
  138. package/dist/timeline/disclosure-context.d.ts +14 -0
  139. package/dist/timeline/entrance.d.ts +14 -0
  140. package/dist/timeline/fleet-decision-projection.d.ts +9 -0
  141. package/dist/timeline/fleet-decision-row.d.ts +11 -0
  142. package/dist/timeline/fold-memory.d.ts +15 -0
  143. package/dist/timeline/index.d.ts +18 -0
  144. package/dist/timeline/parsers.d.ts +96 -0
  145. package/dist/timeline/projection.d.ts +38 -0
  146. package/dist/timeline/registry.d.ts +40 -0
  147. package/dist/timeline/screenshot-lightbox.d.ts +26 -0
  148. package/dist/timeline/seen-activity-ids.d.ts +2 -0
  149. package/dist/timeline/shared.d.ts +125 -0
  150. package/dist/timeline/tool-diff.d.ts +8 -0
  151. package/dist/timeline/tool-display-name.d.ts +10 -0
  152. package/dist/timeline/tool-renderers.d.ts +5 -0
  153. package/dist/timeline/turn-summary.d.ts +116 -0
  154. package/dist/{use-turn-queue-Drm_ypue.d.ts → timeline/types.d.ts} +57 -67
  155. package/dist/types/machines.d.ts +11 -0
  156. package/package.json +8 -4
  157. package/src/client.ts +1 -0
  158. package/src/components/composer-transcription-control.tsx +278 -130
  159. package/src/components/composer.tsx +20 -15
  160. package/src/components/copy-button.tsx +127 -0
  161. package/src/components/markdown.tsx +231 -32
  162. package/src/components/message-timeline.tsx +1382 -378
  163. package/src/components/model-picker.tsx +83 -43
  164. package/src/components/queue-surface-implementation.tsx +1 -1
  165. package/src/components/session-chrome.tsx +942 -0
  166. package/src/components/soften-streaming-markdown.ts +205 -0
  167. package/src/components/stream-reveal.ts +195 -0
  168. package/src/components/tip-follow.ts +459 -0
  169. package/src/components/tooltip.tsx +103 -0
  170. package/src/composer.ts +6 -0
  171. package/src/hooks/internal.ts +30 -4
  172. package/src/hooks/use-available-models.ts +51 -4
  173. package/src/hooks/use-composer.ts +24 -18
  174. package/src/hooks/use-last-started-turn-policy.ts +95 -0
  175. package/src/hooks/use-session-events.ts +359 -5
  176. package/src/hooks/use-voice-input.ts +232 -0
  177. package/src/index.ts +49 -1
  178. package/src/lib/clipboard.ts +58 -0
  179. package/src/lib/format.ts +18 -0
  180. package/src/lib/motion-inspect.ts +5 -0
  181. package/src/lib/motion.ts +80 -0
  182. package/src/model-policy.ts +210 -0
  183. package/src/session-ui.ts +7 -0
  184. package/src/session.ts +2 -0
  185. package/src/timeline/activity-rail.tsx +46 -7
  186. package/src/timeline/entrance.tsx +10 -0
  187. package/src/timeline/fold-memory.ts +52 -0
  188. package/src/timeline/index.ts +11 -1
  189. package/src/timeline/projection.ts +252 -47
  190. package/src/timeline/seen-activity-ids.ts +19 -0
  191. package/src/timeline/shared.tsx +1 -1
  192. package/src/timeline/tool-renderers.tsx +75 -6
  193. package/src/timeline/turn-summary.tsx +387 -97
  194. package/src/timeline/types.ts +47 -0
  195. package/styles/index.css +99 -7
  196. package/styles/tokens.css +44 -3
  197. package/dist/chunk-4F2YLLHP.js.map +0 -1
  198. package/dist/chunk-M7X4JZOD.js.map +0 -1
  199. package/dist/chunk-OCHBZ2X7.js.map +0 -1
  200. package/dist/chunk-PMSGE233.js.map +0 -1
  201. package/dist/chunk-WAA247AB.js +0 -3858
  202. package/dist/chunk-WAA247AB.js.map +0 -1
  203. package/dist/chunk-XVVQP7A4.js.map +0 -1
  204. package/dist/composer-BMzimGTO.d.ts +0 -528
  205. package/dist/session-C3tIXLim.d.ts +0 -264
  206. package/dist/session-context-CAjlaeZb.d.ts +0 -84
  207. package/dist/session-ui-B_7cfN5u.d.ts +0 -259
  208. /package/dist/{chunk-BC6RKIHS.js.map → chunk-GZ4KR67U.js.map} +0 -0
@@ -0,0 +1,232 @@
1
+ import type { ClientVoiceInputConfig, OpenGeniClient } from "@opengeni/sdk";
2
+ import { useCallback, useEffect, useRef, useState } from "react";
3
+ import { appendFinalTranscript } from "./use-transcription";
4
+
5
+ export type VoiceInputStatus =
6
+ | "idle"
7
+ | "requesting-permission"
8
+ | "recording"
9
+ | "transcribing"
10
+ | "error";
11
+
12
+ export type UseVoiceInputOptions = {
13
+ client: Pick<OpenGeniClient, "transcribeAudio"> | null;
14
+ workspaceId: string;
15
+ capability: ClientVoiceInputConfig | null;
16
+ enabled: boolean;
17
+ value: string;
18
+ setValue: (value: string) => void;
19
+ focusInput: () => void;
20
+ disabled?: boolean | undefined;
21
+ };
22
+
23
+ export type UseVoiceInputResult = {
24
+ status: VoiceInputStatus;
25
+ error: string | null;
26
+ available: boolean;
27
+ /** Live mic stream while recording; null once stopped/cancelled. */
28
+ stream: MediaStream | null;
29
+ start: () => Promise<boolean>;
30
+ /** Stop capture and immediately upload/transcribe into the draft (no Send). */
31
+ stop: () => void;
32
+ /** Discard the in-flight recording or transcription; Escape also cancels. */
33
+ cancel: () => void;
34
+ };
35
+
36
+ const MIME_PREFERENCES = ["audio/webm;codecs=opus", "audio/mp4", "audio/ogg;codecs=opus"];
37
+
38
+ export function useVoiceInput({
39
+ client,
40
+ workspaceId,
41
+ capability,
42
+ enabled,
43
+ value,
44
+ setValue,
45
+ focusInput,
46
+ disabled = false,
47
+ }: UseVoiceInputOptions): UseVoiceInputResult {
48
+ const [status, setStatus] = useState<VoiceInputStatus>("idle");
49
+ const [error, setError] = useState<string | null>(null);
50
+ const [stream, setStream] = useState<MediaStream | null>(null);
51
+ const generationRef = useRef(0);
52
+ const recorderRef = useRef<MediaRecorder | null>(null);
53
+ const streamRef = useRef<MediaStream | null>(null);
54
+ const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
55
+ const controllerRef = useRef<AbortController | null>(null);
56
+ const valueRef = useRef(value);
57
+ valueRef.current = value;
58
+
59
+ const clearRuntime = useCallback(() => {
60
+ if (timerRef.current) clearTimeout(timerRef.current);
61
+ timerRef.current = null;
62
+ streamRef.current?.getTracks().forEach((track) => track.stop());
63
+ streamRef.current = null;
64
+ recorderRef.current = null;
65
+ setStream(null);
66
+ }, []);
67
+
68
+ const cancel = useCallback(() => {
69
+ generationRef.current += 1;
70
+ controllerRef.current?.abort();
71
+ controllerRef.current = null;
72
+ const recorder = recorderRef.current;
73
+ clearRuntime();
74
+ if (recorder && recorder.state !== "inactive") recorder.stop();
75
+ setStatus("idle");
76
+ setError(null);
77
+ focusInput();
78
+ }, [clearRuntime, focusInput]);
79
+
80
+ const stop = () => {
81
+ const recorder = recorderRef.current;
82
+ if (!recorder || recorder.state === "inactive") return;
83
+ if (timerRef.current) clearTimeout(timerRef.current);
84
+ timerRef.current = null;
85
+ setStatus("transcribing");
86
+ recorder.stop();
87
+ };
88
+
89
+ const start = async (): Promise<boolean> => {
90
+ if (
91
+ disabled ||
92
+ !client ||
93
+ !enabled ||
94
+ !capability?.available ||
95
+ status === "requesting-permission" ||
96
+ status === "recording" ||
97
+ status === "transcribing" ||
98
+ !navigator.mediaDevices?.getUserMedia ||
99
+ typeof MediaRecorder === "undefined"
100
+ ) {
101
+ return false;
102
+ }
103
+ const voiceClient = client;
104
+ const generation = ++generationRef.current;
105
+ setStatus("requesting-permission");
106
+ setError(null);
107
+ try {
108
+ const mediaStream = await navigator.mediaDevices.getUserMedia({ audio: true });
109
+ if (generation !== generationRef.current) {
110
+ mediaStream.getTracks().forEach((track) => track.stop());
111
+ return false;
112
+ }
113
+ const mimeType = chooseMimeType(capability.acceptedMimeTypes);
114
+ const recorder = new MediaRecorder(mediaStream, mimeType ? { mimeType } : undefined);
115
+ const chunks: BlobPart[] = [];
116
+ const startedAt = Date.now();
117
+ streamRef.current = mediaStream;
118
+ recorderRef.current = recorder;
119
+ setStream(mediaStream);
120
+ recorder.ondataavailable = (event) => {
121
+ if (event.data.size > 0) chunks.push(event.data);
122
+ };
123
+ recorder.onstop = () => {
124
+ clearRuntime();
125
+ if (generation !== generationRef.current) return;
126
+ const audio = new Blob(chunks, { type: recorder.mimeType || mimeType || "audio/webm" });
127
+ if (audio.size === 0) {
128
+ setStatus("error");
129
+ setError("invalid_audio");
130
+ return;
131
+ }
132
+ const controller = new AbortController();
133
+ controllerRef.current = controller;
134
+ void voiceClient
135
+ .transcribeAudio(workspaceId, {
136
+ audio,
137
+ mimeType: audio.type,
138
+ durationSeconds: Math.max(0, (Date.now() - startedAt) / 1000),
139
+ signal: controller.signal,
140
+ })
141
+ .then((response) => {
142
+ if (generation !== generationRef.current || controller.signal.aborted) return;
143
+ const next = appendFinalTranscript(valueRef.current, response.text);
144
+ if (next !== valueRef.current) {
145
+ valueRef.current = next;
146
+ setValue(next);
147
+ }
148
+ setStatus("idle");
149
+ focusInput();
150
+ })
151
+ .catch((reason: unknown) => {
152
+ if (generation !== generationRef.current || controller.signal.aborted) return;
153
+ setStatus("error");
154
+ setError(errorCode(reason));
155
+ })
156
+ .finally(() => {
157
+ if (controllerRef.current === controller) controllerRef.current = null;
158
+ });
159
+ };
160
+ recorder.start();
161
+ setStatus("recording");
162
+ timerRef.current = setTimeout(
163
+ () => stop(),
164
+ Math.min(capability.maxDurationSeconds, 60) * 1000,
165
+ );
166
+ return true;
167
+ } catch (reason) {
168
+ if (generation !== generationRef.current) return false;
169
+ clearRuntime();
170
+ setStatus("error");
171
+ setError(errorCode(reason));
172
+ return false;
173
+ }
174
+ };
175
+
176
+ useEffect(() => {
177
+ const onKeyDown = (event: KeyboardEvent) => {
178
+ if (event.key === "Escape" && status !== "idle") {
179
+ event.preventDefault();
180
+ cancel();
181
+ }
182
+ };
183
+ document.addEventListener("keydown", onKeyDown);
184
+ return () => document.removeEventListener("keydown", onKeyDown);
185
+ }, [cancel, status]);
186
+
187
+ useEffect(
188
+ () => () => {
189
+ generationRef.current += 1;
190
+ controllerRef.current?.abort();
191
+ const recorder = recorderRef.current;
192
+ clearRuntime();
193
+ if (recorder && recorder.state !== "inactive") recorder.stop();
194
+ },
195
+ [clearRuntime],
196
+ );
197
+
198
+ return {
199
+ status,
200
+ error,
201
+ available: Boolean(capability?.available && enabled && !disabled),
202
+ stream,
203
+ start,
204
+ stop,
205
+ cancel,
206
+ };
207
+ }
208
+
209
+ function chooseMimeType(accepted: string[]): string | undefined {
210
+ const normalized = accepted.map((value) => value.trim().toLowerCase());
211
+ return MIME_PREFERENCES.find((mimeType) => {
212
+ const bare = mimeType.split(";")[0] ?? mimeType;
213
+ const allowed =
214
+ normalized.includes(mimeType) ||
215
+ normalized.includes(bare) ||
216
+ normalized.some((candidate) => candidate.split(";")[0] === bare);
217
+ return (
218
+ allowed &&
219
+ (typeof MediaRecorder.isTypeSupported !== "function" ||
220
+ MediaRecorder.isTypeSupported(mimeType))
221
+ );
222
+ });
223
+ }
224
+
225
+ function errorCode(error: unknown): string {
226
+ if (error && typeof error === "object" && "code" in error && typeof error.code === "string") {
227
+ return error.code;
228
+ }
229
+ if (error instanceof DOMException && error.name === "NotAllowedError") return "permission_denied";
230
+ if (error instanceof DOMException && error.name === "NotSupportedError") return "not_supported";
231
+ return "unknown";
232
+ }
package/src/index.ts CHANGED
@@ -49,6 +49,13 @@ export {
49
49
  FILE_ONLY_MESSAGE_TEXT,
50
50
  } from "./hooks/use-composer";
51
51
  export type { ComposerSendExtras, ComposerState, UseComposerOptions } from "./hooks/use-composer";
52
+ export { useVoiceInput } from "./hooks/use-voice-input";
53
+ export type {
54
+ UseVoiceInputOptions,
55
+ UseVoiceInputResult,
56
+ VoiceInputStatus,
57
+ } from "./hooks/use-voice-input";
58
+ export { COMPOSER_PAYMENT_REQUIRED_MESSAGE, composerSubmissionErrorMessage } from "./lib/format";
52
59
  export {
53
60
  INITIAL_TRANSCRIPTION_CONTROL_STATE,
54
61
  appendFinalTranscript,
@@ -74,8 +81,25 @@ export type {
74
81
  UseTurnQueueOptions,
75
82
  UseTurnQueueResult,
76
83
  } from "./hooks/use-turn-queue";
84
+ export {
85
+ useLastStartedTurnPolicy,
86
+ isLastStartedTurnPolicyEvent,
87
+ } from "./hooks/use-last-started-turn-policy";
88
+ export type {
89
+ LastStartedTurnPolicy,
90
+ UseLastStartedTurnPolicyOptions,
91
+ UseLastStartedTurnPolicyResult,
92
+ } from "./hooks/use-last-started-turn-policy";
77
93
  export { QueueSurface } from "./components/queue-surface";
78
94
  export type { QueueSurfaceProps } from "./components/queue-surface";
95
+ export { SessionChrome, sessionChromeGoalPillState } from "./components/session-chrome";
96
+ export type {
97
+ SessionChromeAgentsSignal,
98
+ SessionChromeProps,
99
+ SessionChromeSignalId,
100
+ SessionChromeSignalTone,
101
+ } from "./components/session-chrome";
102
+ export { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "./components/tooltip";
79
103
  export { OPEN_WORKSTREAM_CONTROL_EVENT } from "./components/chat-composer";
80
104
  export { useGoal, isGoalEvent } from "./hooks/use-goal";
81
105
  export type { UseGoalOptions, UseGoalResult } from "./hooks/use-goal";
@@ -131,10 +155,12 @@ export { useWorkspaces } from "./hooks/use-workspaces";
131
155
  export type { UseWorkspacesOptions, UseWorkspacesResult } from "./hooks/use-workspaces";
132
156
  export { useBillingUsage } from "./hooks/use-billing-usage";
133
157
  export type { UseBillingUsageOptions, UseBillingUsageResult } from "./hooks/use-billing-usage";
134
- export { useAvailableModels } from "./hooks/use-available-models";
158
+ export { useAvailableModels, useWorkspaceModelCatalog } from "./hooks/use-available-models";
135
159
  export type {
136
160
  UseAvailableModelsOptions,
137
161
  UseAvailableModelsResult,
162
+ UseWorkspaceModelCatalogOptions,
163
+ UseWorkspaceModelCatalogResult,
138
164
  } from "./hooks/use-available-models";
139
165
 
140
166
  // Sandbox surfacing (Phase 5): capability negotiation + terminal/files/diff/desktop
@@ -244,7 +270,10 @@ export type {
244
270
  ActivityItem,
245
271
  AgentMessageItem,
246
272
  AuthNeededItem,
273
+ ContextCompactionItem,
247
274
  GoalItem,
275
+ MachineInputBatchItem,
276
+ MachineInputMember,
248
277
  NoticeItem,
249
278
  ReasoningItem,
250
279
  SandboxItem,
@@ -364,9 +393,28 @@ export { defaultChatComposerMessages } from "./components/composer";
364
393
  export type { ChatComposerMessages } from "./components/composer";
365
394
  export { ModelPicker } from "./components/model-picker";
366
395
  export type { ModelPickerProps } from "./components/model-picker";
396
+ export {
397
+ advancedSourceSummary,
398
+ availabilityReasonLabel,
399
+ billingClassForModel,
400
+ billingClassLabel,
401
+ coerceReasoningEffortForModel,
402
+ defaultEffortForModel,
403
+ effortOptionsForModel,
404
+ findPickerRow,
405
+ groupPickerRowsByBillingClass,
406
+ labelLatencyMode,
407
+ payerSummaryForModel,
408
+ projectPickerRows,
409
+ runnableLatencyModesForModel,
410
+ sortPickerRows,
411
+ } from "./model-policy";
412
+ export type { LatencyModeId, PickerBillingClass, PickerModelRow } from "./model-policy";
367
413
  export { MessageTimeline, TimelineRow } from "./components/message-timeline";
368
414
  export type { MessageTimelineProps } from "./components/message-timeline";
369
415
  export { Markdown } from "./components/markdown";
416
+ export { CopyButton, CopyHoverFrame } from "./components/copy-button";
417
+ export { copyTextToClipboard, tableElementToTsv } from "./lib/clipboard";
370
418
  export type { MarkdownProps } from "./components/markdown";
371
419
  export { SessionStatus, StatusDot, SESSION_STATUS_META } from "./components/session-status";
372
420
  export type {
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Copy plain text to the clipboard. Prefers the async Clipboard API; falls
3
+ * back to a short-lived textarea for older embeds / denied permissions.
4
+ */
5
+ export async function copyTextToClipboard(text: string): Promise<boolean> {
6
+ const value = text.replace(/\u00a0/g, " ");
7
+ if (value.length === 0) {
8
+ return false;
9
+ }
10
+ try {
11
+ if (typeof navigator !== "undefined" && navigator.clipboard?.writeText) {
12
+ await navigator.clipboard.writeText(value);
13
+ return true;
14
+ }
15
+ } catch {
16
+ // Fall through to execCommand path.
17
+ }
18
+ if (typeof document === "undefined") {
19
+ return false;
20
+ }
21
+ try {
22
+ const area = document.createElement("textarea");
23
+ area.value = value;
24
+ area.setAttribute("readonly", "");
25
+ area.style.position = "fixed";
26
+ area.style.left = "-9999px";
27
+ area.style.top = "0";
28
+ document.body.appendChild(area);
29
+ area.select();
30
+ const ok = document.execCommand("copy");
31
+ document.body.removeChild(area);
32
+ return ok;
33
+ } catch {
34
+ return false;
35
+ }
36
+ }
37
+
38
+ /** Serialize an HTML table to tab-separated values (spreadsheet-friendly). */
39
+ export function tableElementToTsv(table: HTMLTableElement | null | undefined): string {
40
+ if (!table) {
41
+ return "";
42
+ }
43
+ const rows = Array.from(table.querySelectorAll("tr"));
44
+ return rows
45
+ .map((row) =>
46
+ Array.from(row.querySelectorAll("th,td"))
47
+ .map((cell) => {
48
+ const raw = (cell.textContent ?? "").replace(/\s+/g, " ").trim();
49
+ if (/[\t\n"]/.test(raw)) {
50
+ return `"${raw.replace(/"/g, '""')}"`;
51
+ }
52
+ return raw;
53
+ })
54
+ .join("\t"),
55
+ )
56
+ .filter((line) => line.length > 0)
57
+ .join("\n");
58
+ }
package/src/lib/format.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import { OpenGeniApiError } from "@opengeni/sdk";
2
+
1
3
  /** Compact relative time: "now", "42s", "7m", "3h", "2d", then a date. */
2
4
  export function formatRelativeTime(iso: string, now: Date = new Date()): string {
3
5
  const then = new Date(iso).getTime();
@@ -93,6 +95,22 @@ export function tryParseJson(text: string): unknown {
93
95
  export const CREDIT_EXHAUSTION_MESSAGE =
94
96
  "Out of OpenGeni credits — this workspace's balance is empty. Add credits to continue; the conversation is preserved.";
95
97
 
98
+ /**
99
+ * Actionable composer copy for an edge rejection before a turn is accepted.
100
+ * Unlike an in-flight credit exhaustion, this path has not consumed the
101
+ * actor-private draft or any finalized attachment.
102
+ */
103
+ export const COMPOSER_PAYMENT_REQUIRED_MESSAGE =
104
+ "This turn requires OpenGeni managed credits, but the account balance is empty. Add credits or choose a connected Codex subscription model, then retry. Your draft and attachments are preserved.";
105
+
106
+ export function composerSubmissionErrorMessage(error: Error): string {
107
+ return error instanceof OpenGeniApiError &&
108
+ error.status === 402 &&
109
+ error.code === "payment_required"
110
+ ? COMPOSER_PAYMENT_REQUIRED_MESSAGE
111
+ : error.message;
112
+ }
113
+
96
114
  /**
97
115
  * Does this failure/completion payload (or raw error string) mean the
98
116
  * workspace ran out of OpenGeni credits? Matches the engine's
@@ -0,0 +1,5 @@
1
+ /**
2
+ * TEMP eyeball scale for vertical motion (tip-follow, settle fold, CSS).
3
+ * Set back to `1` before landing — not a product knob.
4
+ */
5
+ export const MOTION_INSPECT_SCALE = 1;
@@ -0,0 +1,80 @@
1
+ import { flushSync } from "react-dom";
2
+
3
+ /**
4
+ * Live `prefers-reduced-motion` check for JS-driven motion (scroll glides,
5
+ * streaming word reveals). CSS animations are already neutralized globally in
6
+ * styles/index.css; this lets the imperative paths skip their work entirely.
7
+ */
8
+ let query: MediaQueryList | null | undefined;
9
+
10
+ export function prefersReducedMotion(): boolean {
11
+ if (query === undefined) {
12
+ query =
13
+ typeof window !== "undefined" && typeof window.matchMedia === "function"
14
+ ? window.matchMedia("(prefers-reduced-motion: reduce)")
15
+ : null;
16
+ }
17
+ return query?.matches ?? false;
18
+ }
19
+
20
+ /** Test seam: forget the cached MediaQueryList so a stubbed matchMedia applies. */
21
+ export function resetReducedMotionCache(): void {
22
+ query = undefined;
23
+ }
24
+
25
+ type ViewTransitionResult = { finished: Promise<void> };
26
+ type ViewTransitionCallback = () => void;
27
+ type ViewTransitionOptions = {
28
+ update: ViewTransitionCallback;
29
+ types?: string[];
30
+ };
31
+ type ViewTransitionStarter = {
32
+ (callback: ViewTransitionCallback): ViewTransitionResult;
33
+ (options: ViewTransitionOptions): ViewTransitionResult;
34
+ };
35
+
36
+ /** Active VT type for markdown crystallize — scoped CSS, not a full-page blink. */
37
+ export const MARKDOWN_CRYSTALLIZE_VT_TYPE = "og-md-crystallize";
38
+
39
+ /**
40
+ * Run a DOM update inside the browser View Transition API when available.
41
+ * Snapshots the before/after paint and cross-fades (or custom CSS morphs)
42
+ * between them — the platform "morph" primitive React's experimental
43
+ * `<ViewTransition>` wraps. Falls back to a synchronous update.
44
+ */
45
+ export function runViewTransition(update: () => void, options?: { types?: string[] }): void {
46
+ if (prefersReducedMotion()) {
47
+ update();
48
+ return;
49
+ }
50
+ const doc = typeof document !== "undefined" ? document : null;
51
+ const start = doc
52
+ ? ((doc as Document & { startViewTransition?: ViewTransitionStarter }).startViewTransition ??
53
+ null)
54
+ : null;
55
+ if (typeof start !== "function") {
56
+ update();
57
+ return;
58
+ }
59
+ const types = options?.types;
60
+ if (types && types.length > 0) {
61
+ try {
62
+ Reflect.apply(start, doc, [
63
+ {
64
+ update: () => {
65
+ flushSync(update);
66
+ },
67
+ types,
68
+ },
69
+ ]);
70
+ return;
71
+ } catch {
72
+ // Older engines only accept the callback form.
73
+ }
74
+ }
75
+ Reflect.apply(start, doc, [
76
+ () => {
77
+ flushSync(update);
78
+ },
79
+ ]);
80
+ }