@opengeni/react 0.52.1 → 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.
- package/README.md +48 -0
- package/dist/artifacts.d.ts +1 -1
- package/dist/artifacts.js +43 -14
- package/dist/artifacts.js.map +1 -1
- package/dist/{chunk-ROIRTM37.js → chunk-5UQPNLIO.js} +6 -6
- package/dist/{chunk-JR3QFY6B.js → chunk-6KLX2RH5.js} +49 -2
- package/dist/chunk-6KLX2RH5.js.map +1 -0
- package/dist/{chunk-FWS3KC3Z.js → chunk-B6WPZSTT.js} +104 -104
- package/dist/{chunk-FWS3KC3Z.js.map → chunk-B6WPZSTT.js.map} +1 -1
- package/dist/{chunk-HDBG7XGO.js → chunk-C5ZPSSS5.js} +50 -1
- package/dist/chunk-C5ZPSSS5.js.map +1 -0
- package/dist/{chunk-X4S56Y2H.js → chunk-HA46CYQK.js} +135 -11
- package/dist/chunk-HA46CYQK.js.map +1 -0
- package/dist/{chunk-724BW4TW.js → chunk-IGC6A2ZI.js} +2 -2
- package/dist/chunk-LLOLUHVG.js +5937 -0
- package/dist/chunk-LLOLUHVG.js.map +1 -0
- package/dist/{chunk-WYA65Y3F.js → chunk-MKPIAGVL.js} +13 -3
- package/dist/chunk-MKPIAGVL.js.map +1 -0
- package/dist/chunk-R3AZOAB5.js +933 -0
- package/dist/chunk-R3AZOAB5.js.map +1 -0
- package/dist/{chunk-23EJ676W.js → chunk-XBZWOM2K.js} +9 -4
- package/dist/chunk-XBZWOM2K.js.map +1 -0
- package/dist/{chunk-JAGDUCZQ.js → chunk-Y7OEDUJ5.js} +17 -13
- package/dist/chunk-Y7OEDUJ5.js.map +1 -0
- package/dist/client.d.ts +6 -4
- package/dist/components/artifacts/index.d.ts +1 -0
- package/dist/components/artifacts/published-html-artifact-frame.d.ts +14 -0
- package/dist/components/browser-input.d.ts +7 -0
- package/dist/components/browser-viewer.d.ts +5 -4
- package/dist/components/computer-viewer.d.ts +2 -1
- package/dist/components/desktop-viewer.d.ts +26 -2
- package/dist/components/interaction-intervention-banner.d.ts +11 -0
- package/dist/components/sandbox-terminal.d.ts +4 -1
- package/dist/components/sandbox-workspace.d.ts +3 -0
- package/dist/composer.js +6 -6
- package/dist/hooks/use-auth-runs.d.ts +32 -0
- package/dist/hooks/use-browser-downloads.d.ts +25 -0
- package/dist/hooks/use-browser-identities.d.ts +5 -1
- package/dist/hooks/use-browser-session.d.ts +2 -1
- package/dist/hooks/use-browser-sessions.d.ts +1 -1
- package/dist/hooks/use-channels.d.ts +26 -0
- package/dist/hooks/use-computer-frame-stream.d.ts +1 -1
- package/dist/hooks/use-computer-session.d.ts +2 -1
- package/dist/hooks/use-computer-sessions.d.ts +1 -1
- package/dist/hooks/use-desktop-stream.d.ts +4 -0
- package/dist/hooks/use-interaction-interventions.d.ts +27 -0
- package/dist/hooks/use-interaction-invalidation.d.ts +19 -0
- package/dist/hooks/use-network-routes.d.ts +25 -0
- package/dist/hooks/use-site-auth-connections.d.ts +25 -0
- package/dist/hooks/use-supergrok-accounts.d.ts +26 -0
- package/dist/hooks/use-terminal-stream.d.ts +3 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +1220 -1545
- package/dist/index.js.map +1 -1
- package/dist/interaction.d.ts +13 -3
- package/dist/interaction.js +520 -6
- package/dist/interaction.js.map +1 -1
- package/dist/interactive-workbench-viewers-TOHBXJEE.js +15 -0
- package/dist/lib/host-platform.d.ts +5 -0
- package/dist/lib/relay-wire.d.ts +20 -1
- package/dist/machines.js +3 -3
- package/dist/model-policy.d.ts +1 -1
- package/dist/model-policy.js +1 -1
- package/dist/provider.d.ts +3 -2
- package/dist/realtime/realtime-control.d.ts +2 -2
- package/dist/realtime.js +36 -9
- package/dist/realtime.js.map +1 -1
- package/dist/session-context.d.ts +10 -2
- package/dist/session-ui.js +3 -3
- package/dist/session.js +11 -11
- package/dist/timeline/types.d.ts +5 -0
- package/package.json +2 -2
- package/src/artifacts.ts +3 -0
- package/src/client.ts +73 -3
- package/src/components/artifacts/index.ts +5 -0
- package/src/components/artifacts/published-html-artifact-frame.tsx +38 -0
- package/src/components/browser-input.ts +65 -0
- package/src/components/browser-viewer.tsx +1287 -177
- package/src/components/computer-viewer.tsx +611 -114
- package/src/components/desktop-viewer.tsx +114 -1
- package/src/components/interaction-intervention-banner.tsx +99 -0
- package/src/components/machines/machine-detail.tsx +1 -1
- package/src/components/model-policy-picker.tsx +15 -0
- package/src/components/sandbox-terminal.tsx +24 -7
- package/src/components/sandbox-workspace.tsx +42 -4
- package/src/components/workspace-dock.tsx +59 -5
- package/src/hooks/use-attached-browsers.ts +13 -11
- package/src/hooks/use-auth-runs.ts +278 -0
- package/src/hooks/use-browser-downloads.ts +218 -0
- package/src/hooks/use-browser-frame-stream.ts +168 -59
- package/src/hooks/use-browser-identities.ts +47 -6
- package/src/hooks/use-browser-session.ts +94 -27
- package/src/hooks/use-browser-sessions.ts +19 -14
- package/src/hooks/use-channels.ts +99 -0
- package/src/hooks/use-computer-frame-stream.ts +184 -61
- package/src/hooks/use-computer-session.ts +22 -2
- package/src/hooks/use-computer-sessions.ts +19 -13
- package/src/hooks/use-desktop-stream.ts +39 -7
- package/src/hooks/use-interaction-interventions.ts +242 -0
- package/src/hooks/use-interaction-invalidation.ts +66 -0
- package/src/hooks/use-machine-chip.ts +13 -3
- package/src/hooks/use-network-routes.ts +214 -0
- package/src/hooks/use-site-auth-connections.ts +226 -0
- package/src/hooks/use-supergrok-accounts.ts +81 -0
- package/src/hooks/use-terminal-stream.ts +288 -55
- package/src/index.ts +8 -0
- package/src/interaction.ts +30 -0
- package/src/lib/host-platform.ts +10 -0
- package/src/lib/relay-wire.ts +43 -4
- package/src/lib/terminal-capability.ts +1 -1
- package/src/model-policy.ts +19 -4
- package/src/provider.tsx +133 -1
- package/src/realtime/realtime-control.tsx +34 -6
- package/src/session-context.ts +66 -1
- package/src/timeline/activity-rail.tsx +6 -0
- package/src/timeline/projection.ts +58 -0
- package/src/timeline/types.ts +2 -0
- package/styles/compiled.css +64 -6
- package/dist/chunk-23EJ676W.js.map +0 -1
- package/dist/chunk-6BBWWOOU.js +0 -82
- package/dist/chunk-6BBWWOOU.js.map +0 -1
- package/dist/chunk-7I2KB3OK.js +0 -3895
- package/dist/chunk-7I2KB3OK.js.map +0 -1
- package/dist/chunk-HDBG7XGO.js.map +0 -1
- package/dist/chunk-JAGDUCZQ.js.map +0 -1
- package/dist/chunk-JR3QFY6B.js.map +0 -1
- package/dist/chunk-WYA65Y3F.js.map +0 -1
- package/dist/chunk-X4S56Y2H.js.map +0 -1
- package/dist/interactive-workbench-viewers-KHNOHWQE.js +0 -13
- /package/dist/{chunk-ROIRTM37.js.map → chunk-5UQPNLIO.js.map} +0 -0
- /package/dist/{chunk-724BW4TW.js.map → chunk-IGC6A2ZI.js.map} +0 -0
- /package/dist/{interactive-workbench-viewers-KHNOHWQE.js.map → interactive-workbench-viewers-TOHBXJEE.js.map} +0 -0
package/src/model-policy.ts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import type { ClientModel, ReasoningEffort, WorkspaceModelCatalogModel } from "@opengeni/sdk";
|
|
2
2
|
|
|
3
|
-
export type PickerBillingClass =
|
|
3
|
+
export type PickerBillingClass =
|
|
4
|
+
| "opengeni_credits"
|
|
5
|
+
| "codex_subscription"
|
|
6
|
+
| "supergrok_subscription"
|
|
7
|
+
| "byok";
|
|
4
8
|
|
|
5
9
|
export type PickerModelRow<TCatalog extends ClientModel = WorkspaceModelCatalogModel> = {
|
|
6
10
|
id: string;
|
|
@@ -21,12 +25,14 @@ export type LatencyModeId = "standard" | "priority" | "fast";
|
|
|
21
25
|
const BILLING_CLASS_ORDER: PickerBillingClass[] = [
|
|
22
26
|
"opengeni_credits",
|
|
23
27
|
"codex_subscription",
|
|
28
|
+
"supergrok_subscription",
|
|
24
29
|
"byok",
|
|
25
30
|
];
|
|
26
31
|
|
|
27
32
|
const BILLING_CLASS_LABELS: Record<PickerBillingClass, string> = {
|
|
28
33
|
opengeni_credits: "OpenGeni",
|
|
29
34
|
codex_subscription: "Codex",
|
|
35
|
+
supergrok_subscription: "SuperGrok",
|
|
30
36
|
byok: "Your Gateway",
|
|
31
37
|
};
|
|
32
38
|
|
|
@@ -41,6 +47,9 @@ const AVAILABILITY_REASON_LABELS: Record<string, string> = {
|
|
|
41
47
|
};
|
|
42
48
|
|
|
43
49
|
export function billingClassForModel(model: ClientModel): PickerBillingClass {
|
|
50
|
+
if (model.source === "supergrok") {
|
|
51
|
+
return "supergrok_subscription";
|
|
52
|
+
}
|
|
44
53
|
if (model.source === "codex") {
|
|
45
54
|
return "codex_subscription";
|
|
46
55
|
}
|
|
@@ -48,7 +57,9 @@ export function billingClassForModel(model: ClientModel): PickerBillingClass {
|
|
|
48
57
|
return "byok";
|
|
49
58
|
}
|
|
50
59
|
if (model.credentialSource?.kind === "connected_subscription") {
|
|
51
|
-
return "
|
|
60
|
+
return model.credentialSource.provider === "xai"
|
|
61
|
+
? "supergrok_subscription"
|
|
62
|
+
: "codex_subscription";
|
|
52
63
|
}
|
|
53
64
|
if (model.credentialSource?.kind === "workspace_connection") {
|
|
54
65
|
return "byok";
|
|
@@ -135,7 +146,9 @@ export function payerSummaryForModel(model: ClientModel): string {
|
|
|
135
146
|
return "OpenGeni credits · automatic managed route";
|
|
136
147
|
}
|
|
137
148
|
if (billing.upstreamPayer === "connected_subscription") {
|
|
138
|
-
return
|
|
149
|
+
return model.source === "supergrok"
|
|
150
|
+
? "SuperGrok subscription · external billing"
|
|
151
|
+
: "Codex subscription · external billing";
|
|
139
152
|
}
|
|
140
153
|
if (billing.upstreamPayer === "workspace") {
|
|
141
154
|
return "Billed to your AI Gateway";
|
|
@@ -149,7 +162,9 @@ export function advancedSourceSummary(model: ClientModel): string | null {
|
|
|
149
162
|
return null;
|
|
150
163
|
}
|
|
151
164
|
if (source.kind === "connected_subscription") {
|
|
152
|
-
return
|
|
165
|
+
return source.provider === "xai"
|
|
166
|
+
? "Connected SuperGrok subscription"
|
|
167
|
+
: "Connected Codex subscription";
|
|
153
168
|
}
|
|
154
169
|
if (source.kind === "workspace_connection") {
|
|
155
170
|
return "Workspace AI Gateway";
|
package/src/provider.tsx
CHANGED
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
OPENGENI_API_CONTRACT_REVISION,
|
|
4
4
|
type StreamConnectionState,
|
|
5
5
|
type WorkspaceControlEvent,
|
|
6
|
+
type WorkspaceInteractionRevisionEvent,
|
|
6
7
|
} from "@opengeni/sdk";
|
|
7
8
|
import { useCallback, useEffect, useMemo, useRef, useState, type ReactNode } from "react";
|
|
8
9
|
import type { SessionClientLike } from "./client";
|
|
@@ -17,6 +18,7 @@ export type OpenGeniProviderProps = {
|
|
|
17
18
|
client: SessionClientLike;
|
|
18
19
|
workspaceId: string;
|
|
19
20
|
onWorkspaceControlEvent?: ((event: WorkspaceControlEvent) => void) | undefined;
|
|
21
|
+
onWorkspaceInteractionEvent?: ((event: WorkspaceInteractionRevisionEvent) => void) | undefined;
|
|
20
22
|
children?: ReactNode;
|
|
21
23
|
};
|
|
22
24
|
|
|
@@ -28,6 +30,7 @@ export function OpenGeniProvider({
|
|
|
28
30
|
client,
|
|
29
31
|
workspaceId,
|
|
30
32
|
onWorkspaceControlEvent,
|
|
33
|
+
onWorkspaceInteractionEvent,
|
|
31
34
|
children,
|
|
32
35
|
}: OpenGeniProviderProps) {
|
|
33
36
|
const [workspaceControlEvent, setWorkspaceControlEvent] = useState<WorkspaceControlEvent | null>(
|
|
@@ -36,15 +39,25 @@ export function OpenGeniProvider({
|
|
|
36
39
|
const [workspaceControlConnectionState, setWorkspaceControlConnectionState] = useState<
|
|
37
40
|
StreamConnectionState | "idle" | "error"
|
|
38
41
|
>("idle");
|
|
42
|
+
const [workspaceInteractionEvent, setWorkspaceInteractionEvent] =
|
|
43
|
+
useState<WorkspaceInteractionRevisionEvent | null>(null);
|
|
44
|
+
const [workspaceInteractionConnectionState, setWorkspaceInteractionConnectionState] = useState<
|
|
45
|
+
StreamConnectionState | "idle" | "error"
|
|
46
|
+
>("idle");
|
|
39
47
|
const [contractMismatch, setContractMismatch] = useState<OpenGeniApiContractMismatchError | null>(
|
|
40
48
|
null,
|
|
41
49
|
);
|
|
42
50
|
const callbackRef = useRef(onWorkspaceControlEvent);
|
|
51
|
+
const interactionCallbackRef = useRef(onWorkspaceInteractionEvent);
|
|
43
52
|
const reconcilersRef = useRef(new Map<string, Map<string, () => Promise<void>>>());
|
|
44
53
|
const reconcileInFlightRef = useRef(new Map<string, Promise<void>>());
|
|
45
54
|
const workspaceControlSequencesRef = useRef(new Map<string, number>());
|
|
55
|
+
const workspaceInteractionSequencesRef = useRef(new Map<string, number>());
|
|
46
56
|
const pageLive = usePageLiveActivity();
|
|
57
|
+
const supportsWorkspaceLiveStream =
|
|
58
|
+
"streamWorkspaceLiveEvents" in client && typeof client.streamWorkspaceLiveEvents === "function";
|
|
47
59
|
callbackRef.current = onWorkspaceControlEvent;
|
|
60
|
+
interactionCallbackRef.current = onWorkspaceInteractionEvent;
|
|
48
61
|
|
|
49
62
|
const verifyApiContract = useCallback(async (): Promise<void> => {
|
|
50
63
|
try {
|
|
@@ -105,6 +118,78 @@ export function OpenGeniProvider({
|
|
|
105
118
|
);
|
|
106
119
|
|
|
107
120
|
useEffect(() => {
|
|
121
|
+
if (!supportsWorkspaceLiveStream) return;
|
|
122
|
+
const openLiveStream = client.streamWorkspaceLiveEvents!;
|
|
123
|
+
setWorkspaceControlEvent(null);
|
|
124
|
+
setWorkspaceInteractionEvent(null);
|
|
125
|
+
if (!pageLive) {
|
|
126
|
+
setWorkspaceControlConnectionState("idle");
|
|
127
|
+
setWorkspaceInteractionConnectionState("idle");
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
const controller = new AbortController();
|
|
131
|
+
const setConnectionState = (state: StreamConnectionState) => {
|
|
132
|
+
setWorkspaceControlConnectionState(state);
|
|
133
|
+
setWorkspaceInteractionConnectionState(state);
|
|
134
|
+
};
|
|
135
|
+
setConnectionState("connecting");
|
|
136
|
+
void (async () => {
|
|
137
|
+
try {
|
|
138
|
+
await verifyApiContract();
|
|
139
|
+
const workspace = await client.getWorkspace(workspaceId);
|
|
140
|
+
const controlAfter = Math.max(
|
|
141
|
+
workspaceControlSequencesRef.current.get(workspaceId) ?? 0,
|
|
142
|
+
workspace.inferenceControl.revision,
|
|
143
|
+
);
|
|
144
|
+
const interactionAfter = workspaceInteractionSequencesRef.current.get(workspaceId) ?? 0;
|
|
145
|
+
workspaceControlSequencesRef.current.set(workspaceId, controlAfter);
|
|
146
|
+
const stream = openLiveStream.call(client, workspaceId, {
|
|
147
|
+
controlAfter,
|
|
148
|
+
interactionAfter,
|
|
149
|
+
signal: controller.signal,
|
|
150
|
+
onStateChange: setConnectionState,
|
|
151
|
+
});
|
|
152
|
+
for await (const event of stream) {
|
|
153
|
+
if (controller.signal.aborted) return;
|
|
154
|
+
if (event.type === "workspace.control.changed") {
|
|
155
|
+
workspaceControlSequencesRef.current.set(
|
|
156
|
+
workspaceId,
|
|
157
|
+
Math.max(workspaceControlSequencesRef.current.get(workspaceId) ?? 0, event.sequence),
|
|
158
|
+
);
|
|
159
|
+
setWorkspaceControlEvent((current) =>
|
|
160
|
+
!current || event.sequence > current.sequence ? event : current,
|
|
161
|
+
);
|
|
162
|
+
callbackRef.current?.(event);
|
|
163
|
+
} else {
|
|
164
|
+
workspaceInteractionSequencesRef.current.set(
|
|
165
|
+
workspaceId,
|
|
166
|
+
Math.max(
|
|
167
|
+
workspaceInteractionSequencesRef.current.get(workspaceId) ?? 0,
|
|
168
|
+
event.sequence,
|
|
169
|
+
),
|
|
170
|
+
);
|
|
171
|
+
setWorkspaceInteractionEvent((current) =>
|
|
172
|
+
!current || event.sequence > current.sequence ? event : current,
|
|
173
|
+
);
|
|
174
|
+
interactionCallbackRef.current?.(event);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
} catch (error) {
|
|
178
|
+
if (error instanceof OpenGeniApiContractMismatchError) {
|
|
179
|
+
setContractMismatch(error);
|
|
180
|
+
reloadForContractMismatchOnce(error);
|
|
181
|
+
}
|
|
182
|
+
if (!controller.signal.aborted) {
|
|
183
|
+
setWorkspaceControlConnectionState("error");
|
|
184
|
+
setWorkspaceInteractionConnectionState("error");
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
})();
|
|
188
|
+
return () => controller.abort();
|
|
189
|
+
}, [client, pageLive, supportsWorkspaceLiveStream, verifyApiContract, workspaceId]);
|
|
190
|
+
|
|
191
|
+
useEffect(() => {
|
|
192
|
+
if (supportsWorkspaceLiveStream) return;
|
|
108
193
|
if (!pageLive) {
|
|
109
194
|
setWorkspaceControlConnectionState("idle");
|
|
110
195
|
return;
|
|
@@ -145,7 +230,50 @@ export function OpenGeniProvider({
|
|
|
145
230
|
}
|
|
146
231
|
})();
|
|
147
232
|
return () => controller.abort();
|
|
148
|
-
}, [client, pageLive, verifyApiContract, workspaceId]);
|
|
233
|
+
}, [client, pageLive, supportsWorkspaceLiveStream, verifyApiContract, workspaceId]);
|
|
234
|
+
|
|
235
|
+
useEffect(() => {
|
|
236
|
+
if (supportsWorkspaceLiveStream) return;
|
|
237
|
+
setWorkspaceInteractionEvent(null);
|
|
238
|
+
if (!pageLive) {
|
|
239
|
+
setWorkspaceInteractionConnectionState("idle");
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
const controller = new AbortController();
|
|
243
|
+
setWorkspaceInteractionConnectionState("connecting");
|
|
244
|
+
void (async () => {
|
|
245
|
+
try {
|
|
246
|
+
await verifyApiContract();
|
|
247
|
+
const resumeAfter = workspaceInteractionSequencesRef.current.get(workspaceId) ?? 0;
|
|
248
|
+
const stream = client.streamWorkspaceInteractionRevisions(workspaceId, {
|
|
249
|
+
after: resumeAfter,
|
|
250
|
+
signal: controller.signal,
|
|
251
|
+
onStateChange: setWorkspaceInteractionConnectionState,
|
|
252
|
+
});
|
|
253
|
+
for await (const event of stream) {
|
|
254
|
+
if (controller.signal.aborted) return;
|
|
255
|
+
workspaceInteractionSequencesRef.current.set(
|
|
256
|
+
workspaceId,
|
|
257
|
+
Math.max(
|
|
258
|
+
workspaceInteractionSequencesRef.current.get(workspaceId) ?? 0,
|
|
259
|
+
event.sequence,
|
|
260
|
+
),
|
|
261
|
+
);
|
|
262
|
+
setWorkspaceInteractionEvent((current) =>
|
|
263
|
+
!current || event.sequence > current.sequence ? event : current,
|
|
264
|
+
);
|
|
265
|
+
interactionCallbackRef.current?.(event);
|
|
266
|
+
}
|
|
267
|
+
} catch (error) {
|
|
268
|
+
if (error instanceof OpenGeniApiContractMismatchError) {
|
|
269
|
+
setContractMismatch(error);
|
|
270
|
+
reloadForContractMismatchOnce(error);
|
|
271
|
+
}
|
|
272
|
+
if (!controller.signal.aborted) setWorkspaceInteractionConnectionState("error");
|
|
273
|
+
}
|
|
274
|
+
})();
|
|
275
|
+
return () => controller.abort();
|
|
276
|
+
}, [client, pageLive, supportsWorkspaceLiveStream, verifyApiContract, workspaceId]);
|
|
149
277
|
|
|
150
278
|
const value = useMemo(
|
|
151
279
|
() => ({
|
|
@@ -153,6 +281,8 @@ export function OpenGeniProvider({
|
|
|
153
281
|
workspaceId,
|
|
154
282
|
workspaceControlEvent,
|
|
155
283
|
workspaceControlConnectionState,
|
|
284
|
+
workspaceInteractionEvent,
|
|
285
|
+
workspaceInteractionConnectionState,
|
|
156
286
|
registerSessionReconciler,
|
|
157
287
|
reconcileSession,
|
|
158
288
|
}),
|
|
@@ -161,6 +291,8 @@ export function OpenGeniProvider({
|
|
|
161
291
|
workspaceId,
|
|
162
292
|
workspaceControlEvent,
|
|
163
293
|
workspaceControlConnectionState,
|
|
294
|
+
workspaceInteractionEvent,
|
|
295
|
+
workspaceInteractionConnectionState,
|
|
164
296
|
registerSessionReconciler,
|
|
165
297
|
reconcileSession,
|
|
166
298
|
],
|
|
@@ -57,7 +57,7 @@ import {
|
|
|
57
57
|
export type RealtimeModelOption = {
|
|
58
58
|
id: SessionRealtimeModel;
|
|
59
59
|
label: string;
|
|
60
|
-
provider: "OpenGeni" | "Connected Codex" | "Your Gateway";
|
|
60
|
+
provider: "OpenGeni" | "Connected Codex" | "Connected SuperGrok" | "Your Gateway";
|
|
61
61
|
description: string;
|
|
62
62
|
available: boolean;
|
|
63
63
|
unavailableReason: string | null;
|
|
@@ -74,7 +74,12 @@ const CODEX_LIVE_MODEL: RealtimeModelOption = {
|
|
|
74
74
|
recommended: false,
|
|
75
75
|
};
|
|
76
76
|
|
|
77
|
-
const REALTIME_MODEL_PROVIDERS = [
|
|
77
|
+
const REALTIME_MODEL_PROVIDERS = [
|
|
78
|
+
"OpenGeni",
|
|
79
|
+
"Connected Codex",
|
|
80
|
+
"Connected SuperGrok",
|
|
81
|
+
"Your Gateway",
|
|
82
|
+
] as const;
|
|
78
83
|
type RealtimeModelProvider = (typeof REALTIME_MODEL_PROVIDERS)[number];
|
|
79
84
|
|
|
80
85
|
const REALTIME_PROVIDER_META: Record<
|
|
@@ -86,6 +91,10 @@ const REALTIME_PROVIDER_META: Record<
|
|
|
86
91
|
billingClass: "codex_subscription",
|
|
87
92
|
hint: "ChatGPT / Codex plan",
|
|
88
93
|
},
|
|
94
|
+
"Connected SuperGrok": {
|
|
95
|
+
billingClass: "supergrok_subscription",
|
|
96
|
+
hint: "SuperGrok / xAI plan",
|
|
97
|
+
},
|
|
89
98
|
"Your Gateway": { billingClass: "byok", hint: "Billed to your AI Gateway" },
|
|
90
99
|
};
|
|
91
100
|
const REALTIME_MODEL_STORAGE_PREFIX = "opengeni:realtime-model";
|
|
@@ -141,7 +150,11 @@ export function useSessionRealtime(options: {
|
|
|
141
150
|
const controllerRef = useRef<SessionRealtimeController | null>(null);
|
|
142
151
|
const controllerModelRef = useRef<SessionRealtimeModel | null>(null);
|
|
143
152
|
const [snapshot, setSnapshot] = useState<SessionRealtimeControllerSnapshot>(() => ({
|
|
144
|
-
status: hasStoredSessionRealtimeOwnerProof({
|
|
153
|
+
status: hasStoredSessionRealtimeOwnerProof({
|
|
154
|
+
workspaceId,
|
|
155
|
+
sessionId: options.sessionId,
|
|
156
|
+
model,
|
|
157
|
+
})
|
|
145
158
|
? "recovering"
|
|
146
159
|
: "idle",
|
|
147
160
|
realtimeId: null,
|
|
@@ -684,7 +697,13 @@ export function RealtimeVoiceControl(props: {
|
|
|
684
697
|
opacity: 1,
|
|
685
698
|
transition: reduceMotion
|
|
686
699
|
? { duration: 0 }
|
|
687
|
-
: {
|
|
700
|
+
: {
|
|
701
|
+
delay: 0.32,
|
|
702
|
+
type: "spring",
|
|
703
|
+
stiffness: 320,
|
|
704
|
+
damping: 30,
|
|
705
|
+
mass: 0.8,
|
|
706
|
+
},
|
|
688
707
|
}}
|
|
689
708
|
{...(reduceMotion
|
|
690
709
|
? {}
|
|
@@ -1160,7 +1179,11 @@ function statusContent(
|
|
|
1160
1179
|
};
|
|
1161
1180
|
}
|
|
1162
1181
|
if (!canStart && admissionBlocker) {
|
|
1163
|
-
return {
|
|
1182
|
+
return {
|
|
1183
|
+
phase: "unavailable",
|
|
1184
|
+
label: "Voice unavailable",
|
|
1185
|
+
detail: admissionBlocker,
|
|
1186
|
+
};
|
|
1164
1187
|
}
|
|
1165
1188
|
return {
|
|
1166
1189
|
phase: "idle",
|
|
@@ -1268,7 +1291,11 @@ function RealtimeStatusDot(props: { phase: RealtimeVisualPhase; reduceMotion: bo
|
|
|
1268
1291
|
<motion.span
|
|
1269
1292
|
className="absolute inset-0 rounded-full bg-og-status-running"
|
|
1270
1293
|
animate={{ opacity: [0.45, 0], scale: [1, 2.2] }}
|
|
1271
|
-
transition={{
|
|
1294
|
+
transition={{
|
|
1295
|
+
duration: 1.4,
|
|
1296
|
+
ease: "easeOut",
|
|
1297
|
+
repeat: Number.POSITIVE_INFINITY,
|
|
1298
|
+
}}
|
|
1272
1299
|
/>
|
|
1273
1300
|
) : null}
|
|
1274
1301
|
<span
|
|
@@ -1343,6 +1370,7 @@ function isRealtimeModel(value: string | null): value is SessionRealtimeModel {
|
|
|
1343
1370
|
value === "opengeni-gateway/openai/gpt-realtime-2.1" ||
|
|
1344
1371
|
value === "opengeni-gateway/openai/gpt-realtime-mini" ||
|
|
1345
1372
|
value === "opengeni-gateway/xai/grok-voice-think-fast-2.0" ||
|
|
1373
|
+
value === "supergrok/grok-voice-think-fast-2.0" ||
|
|
1346
1374
|
value === "workspace-gateway/openai/gpt-realtime-2.1" ||
|
|
1347
1375
|
value === "workspace-gateway/openai/gpt-realtime-mini" ||
|
|
1348
1376
|
value === "workspace-gateway/xai/grok-voice-think-fast-2.0"
|
package/src/session-context.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
StreamConnectionState,
|
|
3
|
+
WorkspaceControlEvent,
|
|
4
|
+
WorkspaceInteractionRevisionEvent,
|
|
5
|
+
} from "@opengeni/sdk";
|
|
2
6
|
import { createContext, useContext } from "react";
|
|
3
7
|
import type {
|
|
4
8
|
EmbeddedFileAttachmentClientLike,
|
|
@@ -7,6 +11,7 @@ import type {
|
|
|
7
11
|
EmbeddedBrowserInteractionClientLike,
|
|
8
12
|
EmbeddedComputerInteractionClientLike,
|
|
9
13
|
EmbeddedInteractionClientLike,
|
|
14
|
+
EmbeddedInterventionClientLike,
|
|
10
15
|
EmbeddedRealtimeSessionClientLike,
|
|
11
16
|
EmbeddedSessionClientLike,
|
|
12
17
|
EmbeddedSessionLineageClientLike,
|
|
@@ -20,6 +25,8 @@ export type OpenGeniContextValue = {
|
|
|
20
25
|
workspaceId: string;
|
|
21
26
|
workspaceControlEvent: WorkspaceControlEvent | null;
|
|
22
27
|
workspaceControlConnectionState: StreamConnectionState | "idle" | "error";
|
|
28
|
+
workspaceInteractionEvent: WorkspaceInteractionRevisionEvent | null;
|
|
29
|
+
workspaceInteractionConnectionState: StreamConnectionState | "idle" | "error";
|
|
23
30
|
registerSessionReconciler: (
|
|
24
31
|
sessionId: string,
|
|
25
32
|
key: string,
|
|
@@ -84,6 +91,11 @@ export type EmbeddedInteractionClientOverride = {
|
|
|
84
91
|
workspaceId?: string | undefined;
|
|
85
92
|
};
|
|
86
93
|
|
|
94
|
+
export type EmbeddedInterventionClientOverride = {
|
|
95
|
+
client?: EmbeddedInterventionClientLike | undefined;
|
|
96
|
+
workspaceId?: string | undefined;
|
|
97
|
+
};
|
|
98
|
+
|
|
87
99
|
export type EmbeddedBrowserInteractionClientOverride = {
|
|
88
100
|
client?: EmbeddedBrowserInteractionClientLike | undefined;
|
|
89
101
|
workspaceId?: string | undefined;
|
|
@@ -132,6 +144,8 @@ function useEmbeddedClientRefinement<TClient extends object>(
|
|
|
132
144
|
workspaceId,
|
|
133
145
|
workspaceControlEvent: context?.workspaceControlEvent ?? null,
|
|
134
146
|
workspaceControlConnectionState: context?.workspaceControlConnectionState ?? "idle",
|
|
147
|
+
workspaceInteractionEvent: context?.workspaceInteractionEvent ?? null,
|
|
148
|
+
workspaceInteractionConnectionState: context?.workspaceInteractionConnectionState ?? "idle",
|
|
135
149
|
registerSessionReconciler: context?.registerSessionReconciler ?? NOOP_REGISTER_RECONCILER,
|
|
136
150
|
reconcileSession: context?.reconcileSession ?? NOOP_RECONCILE_SESSION,
|
|
137
151
|
};
|
|
@@ -158,20 +172,46 @@ export function useOpenGeni(override: ClientOverride = {}): OpenGeniContextValue
|
|
|
158
172
|
workspaceId,
|
|
159
173
|
workspaceControlEvent: context?.workspaceControlEvent ?? null,
|
|
160
174
|
workspaceControlConnectionState: context?.workspaceControlConnectionState ?? "idle",
|
|
175
|
+
workspaceInteractionEvent: context?.workspaceInteractionEvent ?? null,
|
|
176
|
+
workspaceInteractionConnectionState: context?.workspaceInteractionConnectionState ?? "idle",
|
|
161
177
|
registerSessionReconciler: context?.registerSessionReconciler ?? NOOP_REGISTER_RECONCILER,
|
|
162
178
|
reconcileSession: context?.reconcileSession ?? NOOP_RECONCILE_SESSION,
|
|
163
179
|
};
|
|
164
180
|
}
|
|
165
181
|
|
|
166
182
|
const BROWSER_INTERACTION_METHODS = [
|
|
183
|
+
"streamWorkspaceInteractionRevisions",
|
|
184
|
+
"listNetworkRoutes",
|
|
185
|
+
"getNetworkRoute",
|
|
186
|
+
"createNetworkRoute",
|
|
187
|
+
"updateNetworkRoute",
|
|
188
|
+
"listSiteAuthConnections",
|
|
189
|
+
"getSiteAuthConnection",
|
|
190
|
+
"createSiteAuthConnection",
|
|
191
|
+
"updateSiteAuthConnection",
|
|
192
|
+
"listAuthRuns",
|
|
193
|
+
"getAuthRun",
|
|
194
|
+
"startBrowserAuthRun",
|
|
195
|
+
"reportBrowserAuthRun",
|
|
196
|
+
"protectedBrowserAuthFill",
|
|
197
|
+
"verifyBrowserAuthRun",
|
|
198
|
+
"listInteractionInterventions",
|
|
199
|
+
"getInteractionIntervention",
|
|
200
|
+
"createInteractionIntervention",
|
|
201
|
+
"resolveInteractionIntervention",
|
|
167
202
|
"listAttachedBrowsers",
|
|
168
203
|
"getAttachedBrowser",
|
|
169
204
|
"listBrowserIdentities",
|
|
170
205
|
"getBrowserIdentity",
|
|
171
206
|
"createBrowserIdentity",
|
|
207
|
+
"updateBrowserIdentity",
|
|
172
208
|
"listBrowserRevisions",
|
|
173
209
|
"listBrowserSessions",
|
|
174
210
|
"getBrowserSession",
|
|
211
|
+
"readBrowserClipboard",
|
|
212
|
+
"listBrowserDownloads",
|
|
213
|
+
"getBrowserDownload",
|
|
214
|
+
"saveBrowserDownload",
|
|
175
215
|
"createBrowserSession",
|
|
176
216
|
"listBrowserTargets",
|
|
177
217
|
"openBrowserTarget",
|
|
@@ -190,8 +230,14 @@ const BROWSER_INTERACTION_METHODS = [
|
|
|
190
230
|
] as const;
|
|
191
231
|
|
|
192
232
|
const COMPUTER_INTERACTION_METHODS = [
|
|
233
|
+
"streamWorkspaceInteractionRevisions",
|
|
234
|
+
"listInteractionInterventions",
|
|
235
|
+
"getInteractionIntervention",
|
|
236
|
+
"createInteractionIntervention",
|
|
237
|
+
"resolveInteractionIntervention",
|
|
193
238
|
"listComputerSessions",
|
|
194
239
|
"getComputerSession",
|
|
240
|
+
"readComputerClipboard",
|
|
195
241
|
"createComputerSession",
|
|
196
242
|
"listComputerTargets",
|
|
197
243
|
"observeComputerTarget",
|
|
@@ -202,6 +248,25 @@ const COMPUTER_INTERACTION_METHODS = [
|
|
|
202
248
|
"endComputerSession",
|
|
203
249
|
] as const;
|
|
204
250
|
|
|
251
|
+
const INTERVENTION_METHODS = [
|
|
252
|
+
"streamWorkspaceInteractionRevisions",
|
|
253
|
+
"listInteractionInterventions",
|
|
254
|
+
"getInteractionIntervention",
|
|
255
|
+
"createInteractionIntervention",
|
|
256
|
+
"resolveInteractionIntervention",
|
|
257
|
+
] as const;
|
|
258
|
+
|
|
259
|
+
/** Resolve only the workspace-wide Browser/Computer intervention surface. */
|
|
260
|
+
export function useEmbeddedInterventions(
|
|
261
|
+
override: EmbeddedInterventionClientOverride = {},
|
|
262
|
+
): EmbeddedClientContextValue<EmbeddedInterventionClientLike> {
|
|
263
|
+
return useEmbeddedClientRefinement(
|
|
264
|
+
override,
|
|
265
|
+
INTERVENTION_METHODS,
|
|
266
|
+
"interaction intervention hooks",
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
|
|
205
270
|
/** Resolve only BrowserSession methods for a standalone Browser embed. */
|
|
206
271
|
export function useEmbeddedBrowserInteraction(
|
|
207
272
|
override: EmbeddedBrowserInteractionClientOverride = {},
|
|
@@ -417,6 +417,12 @@ function WorkerRow({
|
|
|
417
417
|
{truncate(item.prompt, 140)}
|
|
418
418
|
</p>
|
|
419
419
|
) : null}
|
|
420
|
+
{failed && item.failure ? (
|
|
421
|
+
<div className="mt-1 min-w-0 text-og-sm text-og-status-failed">
|
|
422
|
+
<p className="font-og-mono text-og-xs">{item.failure.code}</p>
|
|
423
|
+
<p className="mt-0.5 break-words">{item.failure.message}</p>
|
|
424
|
+
</div>
|
|
425
|
+
) : null}
|
|
420
426
|
</div>
|
|
421
427
|
{failed ? (
|
|
422
428
|
<span className="inline-flex shrink-0 self-center items-center gap-1.5 font-og-mono text-og-xs leading-none text-og-status-failed">
|
|
@@ -49,6 +49,8 @@ export { toolDisplayName, mcpToolLeaf, toolMatchesLeaf } from "./tool-display-na
|
|
|
49
49
|
/** Tool leaves on the first-party OpenGeni MCP server that operate on sessions. */
|
|
50
50
|
const WORKER_SPAWN_TOOL = "session_create";
|
|
51
51
|
const WORKER_MESSAGE_TOOL = "session_send_message";
|
|
52
|
+
const WORKER_FAILURE_CODE_MAX_LENGTH = 128;
|
|
53
|
+
const WORKER_FAILURE_MESSAGE_MAX_UTF8_BYTES = 1_024;
|
|
52
54
|
|
|
53
55
|
/**
|
|
54
56
|
* Tools whose durable side-effect events already own the timeline (MemoryRow).
|
|
@@ -359,6 +361,7 @@ export function buildTimeline(events: SessionEvent[]): TimelineItem[] {
|
|
|
359
361
|
action: toolMatchesLeaf(name, WORKER_SPAWN_TOOL) ? "spawn" : "message",
|
|
360
362
|
prompt: workerPrompt(args),
|
|
361
363
|
workerSessionId: extractSessionRef(args),
|
|
364
|
+
failure: null,
|
|
362
365
|
status: "running",
|
|
363
366
|
occurredAt: event.occurredAt,
|
|
364
367
|
});
|
|
@@ -417,6 +420,7 @@ export function buildTimeline(events: SessionEvent[]): TimelineItem[] {
|
|
|
417
420
|
// isError result) settles to "failed" too, so WorkerRow surfaces it.
|
|
418
421
|
target.status = isErrorOutput(payload) ? "failed" : "complete";
|
|
419
422
|
target.workerSessionId = target.workerSessionId ?? extractSessionRef(payload.output);
|
|
423
|
+
target.failure = target.status === "failed" ? workerFailure(payload.output) : null;
|
|
420
424
|
break;
|
|
421
425
|
}
|
|
422
426
|
// An output carrying an explicit error flag (or an MCP isError result)
|
|
@@ -1300,6 +1304,8 @@ function isTurnBoundary(group: TimelineGroup | undefined): boolean {
|
|
|
1300
1304
|
(group?.kind === "item" &&
|
|
1301
1305
|
(group.item.kind === "user-message" ||
|
|
1302
1306
|
group.item.kind === "context-compaction" ||
|
|
1307
|
+
(group.item.kind === "machine-input-batch" &&
|
|
1308
|
+
group.item.members.some((member) => member.kind === "media_generation_result")) ||
|
|
1303
1309
|
(group.item.kind === "notice" && group.item.tone === "input")))
|
|
1304
1310
|
);
|
|
1305
1311
|
}
|
|
@@ -1795,6 +1801,58 @@ function workerPrompt(args: unknown): string | null {
|
|
|
1795
1801
|
return null;
|
|
1796
1802
|
}
|
|
1797
1803
|
|
|
1804
|
+
function boundedWorkerFailureMessage(value: string): string | null {
|
|
1805
|
+
const normalized = value.replace(/[\u0000-\u001f\u007f]+/g, " ").trim();
|
|
1806
|
+
if (!normalized) return null;
|
|
1807
|
+
const encoded = new TextEncoder().encode(normalized);
|
|
1808
|
+
if (encoded.byteLength <= WORKER_FAILURE_MESSAGE_MAX_UTF8_BYTES) return normalized;
|
|
1809
|
+
let end = WORKER_FAILURE_MESSAGE_MAX_UTF8_BYTES;
|
|
1810
|
+
while (end > 0 && (encoded[end]! & 0xc0) === 0x80) end -= 1;
|
|
1811
|
+
return new TextDecoder().decode(encoded.slice(0, end)).trim();
|
|
1812
|
+
}
|
|
1813
|
+
|
|
1814
|
+
function boundedWorkerFailure(value: unknown): WorkerItem["failure"] {
|
|
1815
|
+
const record = asRecord(value);
|
|
1816
|
+
const code = typeof record.code === "string" ? record.code.trim() : "";
|
|
1817
|
+
const message =
|
|
1818
|
+
typeof record.message === "string" ? boundedWorkerFailureMessage(record.message) : null;
|
|
1819
|
+
if (!/^[A-Za-z0-9._:-]{1,128}$/.test(code) || !message) return null;
|
|
1820
|
+
return { code: code.slice(0, WORKER_FAILURE_CODE_MAX_LENGTH), message };
|
|
1821
|
+
}
|
|
1822
|
+
|
|
1823
|
+
/**
|
|
1824
|
+
* Extract the API's structured orchestration failure from raw objects, JSON
|
|
1825
|
+
* text, or retained MCP CallToolResult wrappers. Legacy flat strings remain
|
|
1826
|
+
* unprojected rather than being mistaken for the trusted diagnostic contract.
|
|
1827
|
+
*/
|
|
1828
|
+
function workerFailure(value: unknown, depth = 0): WorkerItem["failure"] {
|
|
1829
|
+
if (depth > 6 || value === null || value === undefined) return null;
|
|
1830
|
+
if (typeof value === "string") {
|
|
1831
|
+
const parsed = tryParseJson(value);
|
|
1832
|
+
return parsed === value ? null : workerFailure(parsed, depth + 1);
|
|
1833
|
+
}
|
|
1834
|
+
if (Array.isArray(value)) {
|
|
1835
|
+
for (const entry of value) {
|
|
1836
|
+
const failure = workerFailure(entry, depth + 1);
|
|
1837
|
+
if (failure) return failure;
|
|
1838
|
+
}
|
|
1839
|
+
return null;
|
|
1840
|
+
}
|
|
1841
|
+
if (typeof value !== "object") return null;
|
|
1842
|
+
const record = value as Record<string, unknown>;
|
|
1843
|
+
const direct = boundedWorkerFailure(record.error);
|
|
1844
|
+
if (direct) return direct;
|
|
1845
|
+
for (const key of ["structuredContent", "content", "result", "output"] as const) {
|
|
1846
|
+
if (!(key in record)) continue;
|
|
1847
|
+
const failure = workerFailure(record[key], depth + 1);
|
|
1848
|
+
if (failure) return failure;
|
|
1849
|
+
}
|
|
1850
|
+
if (record.type === "text" && typeof record.text === "string") {
|
|
1851
|
+
return workerFailure(record.text, depth + 1);
|
|
1852
|
+
}
|
|
1853
|
+
return null;
|
|
1854
|
+
}
|
|
1855
|
+
|
|
1798
1856
|
/**
|
|
1799
1857
|
* Find a session id in orchestration tool arguments or output. Handles raw
|
|
1800
1858
|
* objects, JSON strings, and MCP tool results (`{ content: [{ type: "text",
|
package/src/timeline/types.ts
CHANGED
|
@@ -127,6 +127,8 @@ export type WorkerItem = {
|
|
|
127
127
|
prompt: string | null;
|
|
128
128
|
/** The target/spawned worker session id, when parseable from args/output. */
|
|
129
129
|
workerSessionId: string | null;
|
|
130
|
+
/** Bounded structured failure retained from session_create/session_send_message. */
|
|
131
|
+
failure: { code: string; message: string } | null;
|
|
130
132
|
status: "running" | "complete" | "failed" | "cancelled";
|
|
131
133
|
occurredAt: string;
|
|
132
134
|
};
|