@opengeni/react 0.57.0 → 2.2.0-canary.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 +36 -11
- package/dist/artifacts-spreadsheet.js +1 -1
- package/dist/artifacts.js +1 -1
- package/dist/browser-viewer-GXUICIAG.js +14 -0
- package/dist/{chunk-R3AZOAB5.js → chunk-4MDLEHXL.js} +9 -115
- package/dist/chunk-4MDLEHXL.js.map +1 -0
- package/dist/{chunk-Y7OEDUJ5.js → chunk-5RZN3DD5.js} +1014 -420
- package/dist/chunk-5RZN3DD5.js.map +1 -0
- package/dist/chunk-5YK2ZDIG.js +116 -0
- package/dist/chunk-5YK2ZDIG.js.map +1 -0
- package/dist/chunk-H5YGYNXS.js +329 -0
- package/dist/chunk-H5YGYNXS.js.map +1 -0
- package/dist/{chunk-7WCLAEFV.js → chunk-ICA6R6TW.js} +2 -2
- package/dist/{chunk-7WCLAEFV.js.map → chunk-ICA6R6TW.js.map} +1 -1
- package/dist/{chunk-IGC6A2ZI.js → chunk-JOAVA2TW.js} +589 -217
- package/dist/chunk-JOAVA2TW.js.map +1 -0
- package/dist/{chunk-5UQPNLIO.js → chunk-LTCHJ4MS.js} +9 -9
- package/dist/chunk-LTCHJ4MS.js.map +1 -0
- package/dist/{chunk-C5ZPSSS5.js → chunk-NEXLLTP4.js} +2 -1
- package/dist/{chunk-C5ZPSSS5.js.map → chunk-NEXLLTP4.js.map} +1 -1
- package/dist/chunk-QGBJ6GHG.js +2300 -0
- package/dist/chunk-QGBJ6GHG.js.map +1 -0
- package/dist/{chunk-B6WPZSTT.js → chunk-UOLJWT5Y.js} +78 -16
- package/dist/chunk-UOLJWT5Y.js.map +1 -0
- package/dist/{chunk-MKPIAGVL.js → chunk-W6NFJXSA.js} +8 -19
- package/dist/chunk-W6NFJXSA.js.map +1 -0
- package/dist/{chunk-XBZWOM2K.js → chunk-X4A66EWZ.js} +8 -3
- package/dist/chunk-X4A66EWZ.js.map +1 -0
- package/dist/chunk-X4JAFD5R.js +3907 -0
- package/dist/chunk-X4JAFD5R.js.map +1 -0
- package/dist/{chunk-6KLX2RH5.js → chunk-XSA2QAKP.js} +334 -19
- package/dist/chunk-XSA2QAKP.js.map +1 -0
- package/dist/{chunk-HA46CYQK.js → chunk-XXWH2JLC.js} +792 -206
- package/dist/chunk-XXWH2JLC.js.map +1 -0
- package/dist/client.d.ts +2 -2
- package/dist/components/browser-viewer.d.ts +7 -1
- package/dist/components/computer-viewer.d.ts +4 -1
- package/dist/components/enrollment-consent.d.ts +6 -3
- package/dist/components/file-browser.d.ts +7 -1
- package/dist/components/human-input-form.d.ts +1 -1
- package/dist/components/human-input-surface.d.ts +1 -1
- package/dist/components/machine-card.d.ts +4 -1
- package/dist/components/machines/machine-detail.d.ts +4 -1
- package/dist/components/machines/machine-operation-policy.d.ts +11 -0
- package/dist/components/machines-dashboard.d.ts +3 -1
- package/dist/components/markdown.d.ts +20 -1
- package/dist/components/message-timeline.d.ts +1 -1
- package/dist/components/model-policy-picker.d.ts +2 -0
- package/dist/components/pierre-file.d.ts +1 -1
- package/dist/components/sandbox-files.d.ts +8 -2
- package/dist/components/sandbox-workspace.d.ts +33 -3
- package/dist/components/session-chrome.d.ts +15 -3
- package/dist/components/workspace-dock.d.ts +6 -2
- package/dist/composer.js +5 -5
- package/dist/computer-viewer-ZOP4Q7A7.js +16 -0
- package/dist/computer-viewer-ZOP4Q7A7.js.map +1 -0
- package/dist/hooks/channel-cache.d.ts +23 -0
- package/dist/hooks/shared-machines-list.d.ts +14 -0
- package/dist/hooks/use-attached-browsers.d.ts +2 -1
- package/dist/hooks/use-channels.d.ts +1 -0
- package/dist/hooks/use-composer.d.ts +37 -9
- package/dist/hooks/use-machine-chip.d.ts +3 -3
- package/dist/hooks/use-machines.d.ts +19 -1
- package/dist/hooks/use-sandbox-files.d.ts +1 -0
- package/dist/hooks/use-session-background-commands.d.ts +14 -0
- package/dist/hooks/use-turn-queue.d.ts +12 -0
- package/dist/hooks/use-workspace-sessions.d.ts +1 -0
- package/dist/index.d.ts +7 -5
- package/dist/index.js +1096 -623
- package/dist/index.js.map +1 -1
- package/dist/interaction.js +29 -181
- package/dist/interaction.js.map +1 -1
- package/dist/lib/interaction-errors.d.ts +3 -0
- package/dist/machines.d.ts +4 -2
- package/dist/machines.js +10 -2
- package/dist/model-policy.d.ts +1 -1
- package/dist/model-policy.js +1 -1
- package/dist/platform-activity-row-BJHUHXAT.js +169 -0
- package/dist/platform-activity-row-BJHUHXAT.js.map +1 -0
- package/dist/{queue-surface-implementation-2KVJTYXH.js → queue-surface-implementation-BSKUWHRI.js} +76 -16
- package/dist/queue-surface-implementation-BSKUWHRI.js.map +1 -0
- package/dist/realtime/realtime-control.d.ts +10 -3
- package/dist/realtime.js +138 -29
- package/dist/realtime.js.map +1 -1
- package/dist/session-ui.d.ts +1 -1
- package/dist/session-ui.js +6 -2
- package/dist/session.d.ts +2 -2
- package/dist/session.js +4 -4
- package/dist/timeline/index.d.ts +1 -1
- package/dist/timeline/platform-activity-row.d.ts +15 -0
- package/dist/timeline/screenshot-lightbox.d.ts +1 -1
- package/dist/timeline/shared.d.ts +4 -2
- package/dist/timeline/types.d.ts +48 -5
- package/dist/types/machines.d.ts +2 -2
- package/package.json +3 -3
- package/src/client.ts +12 -1
- package/src/components/artifacts/editable-spreadsheet.tsx +1 -1
- package/src/components/browser-viewer.tsx +361 -58
- package/src/components/chat-composer.tsx +1 -1
- package/src/components/composer.tsx +6 -6
- package/src/components/computer-viewer.tsx +368 -115
- package/src/components/enrollment-consent.tsx +64 -6
- package/src/components/file-browser.tsx +117 -2
- package/src/components/human-input-form.tsx +91 -55
- package/src/components/human-input-surface.tsx +1 -1
- package/src/components/machine-card.tsx +111 -0
- package/src/components/machine-input-display.ts +15 -0
- package/src/components/machines/machine-detail.tsx +47 -0
- package/src/components/machines/machine-operation-policy.tsx +179 -0
- package/src/components/machines-dashboard.tsx +6 -0
- package/src/components/markdown.tsx +174 -20
- package/src/components/message-timeline.tsx +162 -17
- package/src/components/model-policy-picker.tsx +9 -17
- package/src/components/pierre-file.tsx +1 -1
- package/src/components/queue-surface-implementation.tsx +82 -16
- package/src/components/sandbox-files.tsx +135 -19
- package/src/components/sandbox-workspace.tsx +204 -92
- package/src/components/session-chrome.tsx +415 -208
- package/src/components/session-status.tsx +1 -1
- package/src/components/user-message-body.tsx +168 -48
- package/src/components/workspace-dock.tsx +141 -175
- package/src/hooks/channel-cache.ts +104 -0
- package/src/hooks/shared-machines-list.ts +239 -0
- package/src/hooks/use-attached-browsers.ts +5 -1
- package/src/hooks/use-browser-frame-stream.ts +6 -1
- package/src/hooks/use-browser-session.ts +6 -4
- package/src/hooks/use-browser-sessions.ts +7 -1
- package/src/hooks/use-channels.ts +32 -10
- package/src/hooks/use-composer.ts +760 -291
- package/src/hooks/use-computer-frame-stream.ts +14 -9
- package/src/hooks/use-computer-session.ts +16 -14
- package/src/hooks/use-computer-sessions.ts +8 -1
- package/src/hooks/use-last-started-turn-policy.ts +1 -1
- package/src/hooks/use-machine-chip.ts +23 -6
- package/src/hooks/use-machines.ts +104 -6
- package/src/hooks/use-sandbox-files.ts +195 -140
- package/src/hooks/use-sandbox-git.ts +58 -29
- package/src/hooks/use-session-background-commands.ts +56 -0
- package/src/hooks/use-session-capabilities.ts +18 -0
- package/src/hooks/use-turn-queue.ts +111 -12
- package/src/hooks/use-workspace-sessions.ts +5 -1
- package/src/index.ts +26 -4
- package/src/lib/interaction-errors.ts +18 -0
- package/src/machines.ts +9 -1
- package/src/model-policy.ts +10 -2
- package/src/realtime/realtime-control.tsx +200 -37
- package/src/session-context.ts +1 -0
- package/src/session-ui.ts +6 -1
- package/src/session.ts +9 -1
- package/src/timeline/activity-rail.tsx +20 -75
- package/src/timeline/index.ts +4 -0
- package/src/timeline/platform-activity-row.tsx +201 -0
- package/src/timeline/projection.ts +476 -23
- package/src/timeline/screenshot-lightbox.tsx +48 -14
- package/src/timeline/shared.tsx +8 -2
- package/src/timeline/tool-renderers.tsx +104 -6
- package/src/timeline/turn-summary.tsx +6 -1
- package/src/timeline/types.ts +75 -2
- package/src/types/machines.ts +10 -0
- package/styles/compiled.css +115 -104
- package/dist/chunk-5UQPNLIO.js.map +0 -1
- package/dist/chunk-6KLX2RH5.js.map +0 -1
- package/dist/chunk-B6WPZSTT.js.map +0 -1
- package/dist/chunk-HA46CYQK.js.map +0 -1
- package/dist/chunk-IGC6A2ZI.js.map +0 -1
- package/dist/chunk-LLOLUHVG.js +0 -5937
- package/dist/chunk-LLOLUHVG.js.map +0 -1
- package/dist/chunk-MKPIAGVL.js.map +0 -1
- package/dist/chunk-R3AZOAB5.js.map +0 -1
- package/dist/chunk-XBZWOM2K.js.map +0 -1
- package/dist/chunk-Y7OEDUJ5.js.map +0 -1
- package/dist/interactive-workbench-viewers-TOHBXJEE.js +0 -15
- package/dist/queue-surface-implementation-2KVJTYXH.js.map +0 -1
- /package/dist/{interactive-workbench-viewers-TOHBXJEE.js.map → browser-viewer-GXUICIAG.js.map} +0 -0
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
type SessionRealtimeController,
|
|
7
7
|
type SessionRealtimeControllerSnapshot,
|
|
8
8
|
type CreateSessionRealtimeControllerOptions,
|
|
9
|
+
type SessionRealtimeLifecycleProjection,
|
|
9
10
|
type SessionRealtimeModel,
|
|
10
11
|
type WorkspaceRealtimeModelCatalogItem,
|
|
11
12
|
} from "@opengeni/sdk/realtime";
|
|
@@ -98,9 +99,108 @@ const REALTIME_PROVIDER_META: Record<
|
|
|
98
99
|
"Your Gateway": { billingClass: "byok", hint: "Billed to your AI Gateway" },
|
|
99
100
|
};
|
|
100
101
|
const REALTIME_MODEL_STORAGE_PREFIX = "opengeni:realtime-model";
|
|
102
|
+
const REALTIME_MODEL_CATALOG_CACHE_TTL_MS = 60_000;
|
|
103
|
+
const REALTIME_MODEL_CATALOG_CACHE_MAX_WORKSPACES = 64;
|
|
101
104
|
|
|
102
105
|
type RealtimeControllerClient = EmbeddedRealtimeSessionClientLike & SessionRealtimeClientLike;
|
|
103
106
|
|
|
107
|
+
type RealtimeModelCatalogCacheEntry =
|
|
108
|
+
| { state: "loading"; promise: Promise<RealtimeModelOption[]> }
|
|
109
|
+
| { state: "ready"; models: RealtimeModelOption[]; expiresAt: number };
|
|
110
|
+
|
|
111
|
+
// Scope advisory availability to the exact client object and workspace. The
|
|
112
|
+
// server still authorizes every realtime operation; changing principals should
|
|
113
|
+
// replace the client just as it does for the rest of the SDK state.
|
|
114
|
+
const realtimeModelCatalogCache = new WeakMap<
|
|
115
|
+
RealtimeControllerClient,
|
|
116
|
+
Map<string, RealtimeModelCatalogCacheEntry>
|
|
117
|
+
>();
|
|
118
|
+
|
|
119
|
+
function realtimeModelFallback(codexConnected: boolean): RealtimeModelOption[] {
|
|
120
|
+
return [
|
|
121
|
+
{
|
|
122
|
+
...CODEX_LIVE_MODEL,
|
|
123
|
+
available: codexConnected,
|
|
124
|
+
unavailableReason: codexConnected ? null : CODEX_LIVE_MODEL.unavailableReason,
|
|
125
|
+
},
|
|
126
|
+
];
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function realtimeModelCatalogCacheFor(
|
|
130
|
+
client: RealtimeControllerClient,
|
|
131
|
+
): Map<string, RealtimeModelCatalogCacheEntry> {
|
|
132
|
+
const existing = realtimeModelCatalogCache.get(client);
|
|
133
|
+
if (existing) return existing;
|
|
134
|
+
const created = new Map<string, RealtimeModelCatalogCacheEntry>();
|
|
135
|
+
realtimeModelCatalogCache.set(client, created);
|
|
136
|
+
return created;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function readCachedRealtimeModelCatalog(
|
|
140
|
+
client: RealtimeControllerClient,
|
|
141
|
+
workspaceId: string,
|
|
142
|
+
now = Date.now(),
|
|
143
|
+
): RealtimeModelOption[] | null {
|
|
144
|
+
const cache = realtimeModelCatalogCache.get(client);
|
|
145
|
+
const entry = cache?.get(workspaceId);
|
|
146
|
+
if (!entry || entry.state === "loading") return null;
|
|
147
|
+
if (entry.expiresAt > now) return entry.models;
|
|
148
|
+
cache?.delete(workspaceId);
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function pruneRealtimeModelCatalogCache(
|
|
153
|
+
cache: Map<string, RealtimeModelCatalogCacheEntry>,
|
|
154
|
+
now: number,
|
|
155
|
+
): void {
|
|
156
|
+
for (const [workspaceId, entry] of cache) {
|
|
157
|
+
if (entry.state === "ready" && entry.expiresAt <= now) cache.delete(workspaceId);
|
|
158
|
+
}
|
|
159
|
+
while (cache.size >= REALTIME_MODEL_CATALOG_CACHE_MAX_WORKSPACES) {
|
|
160
|
+
const oldestWorkspaceId = cache.keys().next().value;
|
|
161
|
+
if (oldestWorkspaceId === undefined) return;
|
|
162
|
+
cache.delete(oldestWorkspaceId);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function loadRealtimeModelCatalog(
|
|
167
|
+
client: RealtimeControllerClient,
|
|
168
|
+
workspaceId: string,
|
|
169
|
+
): Promise<RealtimeModelOption[]> | null {
|
|
170
|
+
const load = client.getWorkspaceRealtimeModelCatalog;
|
|
171
|
+
if (!load) return null;
|
|
172
|
+
const now = Date.now();
|
|
173
|
+
const cache = realtimeModelCatalogCacheFor(client);
|
|
174
|
+
const entry = cache.get(workspaceId);
|
|
175
|
+
if (entry?.state === "loading") return entry.promise;
|
|
176
|
+
if (entry?.state === "ready" && entry.expiresAt > now) return Promise.resolve(entry.models);
|
|
177
|
+
if (entry) cache.delete(workspaceId);
|
|
178
|
+
pruneRealtimeModelCatalogCache(cache, now);
|
|
179
|
+
|
|
180
|
+
const promise = load
|
|
181
|
+
.call(client, workspaceId)
|
|
182
|
+
.then((response) => response.models.map(toRealtimeModelOption))
|
|
183
|
+
.then((models) => {
|
|
184
|
+
const current = cache.get(workspaceId);
|
|
185
|
+
if (current?.state === "loading" && current.promise === promise) {
|
|
186
|
+
cache.delete(workspaceId);
|
|
187
|
+
cache.set(workspaceId, {
|
|
188
|
+
state: "ready",
|
|
189
|
+
models,
|
|
190
|
+
expiresAt: Date.now() + REALTIME_MODEL_CATALOG_CACHE_TTL_MS,
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
return models;
|
|
194
|
+
})
|
|
195
|
+
.catch((error: unknown) => {
|
|
196
|
+
const current = cache.get(workspaceId);
|
|
197
|
+
if (current?.state === "loading" && current.promise === promise) cache.delete(workspaceId);
|
|
198
|
+
throw error;
|
|
199
|
+
});
|
|
200
|
+
cache.set(workspaceId, { state: "loading", promise });
|
|
201
|
+
return promise;
|
|
202
|
+
}
|
|
203
|
+
|
|
104
204
|
export type SessionRealtimeControllerFactory = (
|
|
105
205
|
options: CreateSessionRealtimeControllerOptions,
|
|
106
206
|
) => SessionRealtimeController;
|
|
@@ -138,6 +238,8 @@ export function useSessionRealtime(options: {
|
|
|
138
238
|
model?: SessionRealtimeModel | undefined;
|
|
139
239
|
modelAvailable?: boolean | undefined;
|
|
140
240
|
modelUnavailableReason?: string | null | undefined;
|
|
241
|
+
/** Model-visible application context captured with each durable realtime message. */
|
|
242
|
+
getModelContext?: (() => string | undefined) | undefined;
|
|
141
243
|
/** Deterministic browser-test/demo seam. Production hosts should use the SDK default. */
|
|
142
244
|
controllerFactory?: SessionRealtimeControllerFactory | undefined;
|
|
143
245
|
}) {
|
|
@@ -149,6 +251,8 @@ export function useSessionRealtime(options: {
|
|
|
149
251
|
const audioRef = useRef<HTMLAudioElement>(null);
|
|
150
252
|
const controllerRef = useRef<SessionRealtimeController | null>(null);
|
|
151
253
|
const controllerModelRef = useRef<SessionRealtimeModel | null>(null);
|
|
254
|
+
const modelContextProviderRef = useRef(options.getModelContext);
|
|
255
|
+
modelContextProviderRef.current = options.getModelContext;
|
|
152
256
|
const [snapshot, setSnapshot] = useState<SessionRealtimeControllerSnapshot>(() => ({
|
|
153
257
|
status: hasStoredSessionRealtimeOwnerProof({
|
|
154
258
|
workspaceId,
|
|
@@ -177,7 +281,7 @@ export function useSessionRealtime(options: {
|
|
|
177
281
|
const eventsReadyRef = useRef(options.eventsReady);
|
|
178
282
|
lifecycleRef.current = lifecycle;
|
|
179
283
|
eventsReadyRef.current = options.eventsReady;
|
|
180
|
-
const lifecycleActive = lifecycle
|
|
284
|
+
const lifecycleActive = sessionRealtimeLifecycleIsActive(lifecycle);
|
|
181
285
|
|
|
182
286
|
useEffect(() => {
|
|
183
287
|
let disposed = false;
|
|
@@ -193,6 +297,7 @@ export function useSessionRealtime(options: {
|
|
|
193
297
|
sessionId: options.sessionId,
|
|
194
298
|
...(audioRef.current ? { remoteAudio: audioRef.current } : {}),
|
|
195
299
|
model,
|
|
300
|
+
getModelContext: () => modelContextProviderRef.current?.(),
|
|
196
301
|
});
|
|
197
302
|
controllerRef.current = controller;
|
|
198
303
|
controllerModelRef.current = model;
|
|
@@ -297,6 +402,14 @@ export function useSessionRealtime(options: {
|
|
|
297
402
|
};
|
|
298
403
|
}
|
|
299
404
|
|
|
405
|
+
function sessionRealtimeLifecycleIsActive(
|
|
406
|
+
lifecycle: SessionRealtimeLifecycleProjection | null,
|
|
407
|
+
): boolean {
|
|
408
|
+
if (lifecycle?.state !== "active") return false;
|
|
409
|
+
const leaseExpiresAt = Date.parse(lifecycle.leaseExpiresAt);
|
|
410
|
+
return !Number.isFinite(leaseExpiresAt) || leaseExpiresAt > Date.now();
|
|
411
|
+
}
|
|
412
|
+
|
|
300
413
|
export function SessionRealtimeControl(props: {
|
|
301
414
|
client?: RealtimeControllerClient | undefined;
|
|
302
415
|
workspaceId?: string | undefined;
|
|
@@ -306,6 +419,8 @@ export function SessionRealtimeControl(props: {
|
|
|
306
419
|
events: SessionEvent[];
|
|
307
420
|
eventsReady: boolean;
|
|
308
421
|
codexConnected: boolean;
|
|
422
|
+
/** Model-visible application context captured with each durable realtime message. */
|
|
423
|
+
getModelContext?: (() => string | undefined) | undefined;
|
|
309
424
|
realtimeAutostartModel?: SessionRealtimeModel | undefined;
|
|
310
425
|
onRealtimeAutostartConsumed?: (() => void) | undefined;
|
|
311
426
|
/** Host hides dictate (and can choreograph layout) while realtime voice is live. */
|
|
@@ -317,15 +432,18 @@ export function SessionRealtimeControl(props: {
|
|
|
317
432
|
* `none` never attaches a model menu to the bar button.
|
|
318
433
|
*/
|
|
319
434
|
modelMenu?: "split" | "split-desktop" | "none" | undefined;
|
|
435
|
+
/** Explicitly expose low-level controller diagnostics; disabled by default. */
|
|
436
|
+
showDiagnostics?: boolean | undefined;
|
|
320
437
|
/** Deterministic browser-test/demo seam. Production hosts should use the SDK default. */
|
|
321
438
|
controllerFactory?: SessionRealtimeControllerFactory | undefined;
|
|
322
439
|
}) {
|
|
323
440
|
const lifecycle = useMemo(() => projectSessionRealtimeLifecycle(props.events), [props.events]);
|
|
441
|
+
const lifecycleActive = sessionRealtimeLifecycleIsActive(lifecycle);
|
|
324
442
|
const selection = useRealtimeModelSelection({
|
|
325
443
|
client: props.client,
|
|
326
444
|
workspaceId: props.workspaceId,
|
|
327
445
|
codexConnected: props.codexConnected,
|
|
328
|
-
activeModel: lifecycle?.state === "active" ? lifecycle.model : null,
|
|
446
|
+
activeModel: lifecycleActive && lifecycle?.state === "active" ? lifecycle.model : null,
|
|
329
447
|
});
|
|
330
448
|
const selectedModel = selection.selectedModel;
|
|
331
449
|
const realtime = useSessionRealtime({
|
|
@@ -349,7 +467,7 @@ export function SessionRealtimeControl(props: {
|
|
|
349
467
|
|
|
350
468
|
useEffect(() => {
|
|
351
469
|
const pending = autostartModelRef.current;
|
|
352
|
-
if (!pending || autostartStartedRef.current ||
|
|
470
|
+
if (!pending || autostartStartedRef.current || lifecycleActive) return;
|
|
353
471
|
const available = models.some((model) => model.id === pending && model.available);
|
|
354
472
|
if (!available) return;
|
|
355
473
|
if (selectedRealtimeModel.id !== pending) {
|
|
@@ -368,7 +486,7 @@ export function SessionRealtimeControl(props: {
|
|
|
368
486
|
});
|
|
369
487
|
}, [
|
|
370
488
|
canStart,
|
|
371
|
-
|
|
489
|
+
lifecycleActive,
|
|
372
490
|
models,
|
|
373
491
|
props.sessionId,
|
|
374
492
|
props.workspaceId,
|
|
@@ -386,6 +504,7 @@ export function SessionRealtimeControl(props: {
|
|
|
386
504
|
modelAvailable={selectedModel.available}
|
|
387
505
|
menuSide="top"
|
|
388
506
|
modelMenu={props.modelMenu ?? "split"}
|
|
507
|
+
showDiagnostics={props.showDiagnostics}
|
|
389
508
|
audioRef={realtime.audioRef}
|
|
390
509
|
selectedModel={selectedModel}
|
|
391
510
|
models={selection.models}
|
|
@@ -411,31 +530,52 @@ export function useRealtimeModelSelection(options: {
|
|
|
411
530
|
workspaceId: options.workspaceId,
|
|
412
531
|
});
|
|
413
532
|
const activeModel = options.activeModel;
|
|
414
|
-
const
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
available: options.codexConnected,
|
|
418
|
-
unavailableReason: options.codexConnected ? null : CODEX_LIVE_MODEL.unavailableReason,
|
|
419
|
-
},
|
|
420
|
-
]);
|
|
421
|
-
const [selectedModelId, setSelectedModelId] = useState<SessionRealtimeModel>(
|
|
422
|
-
() => readRealtimeModelPreference(workspaceId) ?? CODEX_LIVE_MODEL.id,
|
|
533
|
+
const initialCachedCatalog = readCachedRealtimeModelCatalog(client, workspaceId);
|
|
534
|
+
const [catalog, setCatalog] = useState<RealtimeModelOption[]>(
|
|
535
|
+
() => initialCachedCatalog ?? realtimeModelFallback(options.codexConnected),
|
|
423
536
|
);
|
|
537
|
+
const catalogScopeRef = useRef({
|
|
538
|
+
client,
|
|
539
|
+
workspaceId,
|
|
540
|
+
source: initialCachedCatalog ? ("cache" as const) : ("fallback" as const),
|
|
541
|
+
});
|
|
542
|
+
const [selectedModelId, setSelectedModelId] = useState<SessionRealtimeModel>(() => {
|
|
543
|
+
const preferred = readRealtimeModelPreference(workspaceId) ?? CODEX_LIVE_MODEL.id;
|
|
544
|
+
if (!initialCachedCatalog) return preferred;
|
|
545
|
+
if (initialCachedCatalog.some((model) => model.id === preferred && model.available)) {
|
|
546
|
+
return preferred;
|
|
547
|
+
}
|
|
548
|
+
return initialCachedCatalog.find((model) => model.available)?.id ?? CODEX_LIVE_MODEL.id;
|
|
549
|
+
});
|
|
424
550
|
|
|
425
551
|
useEffect(() => {
|
|
426
552
|
let disposed = false;
|
|
427
|
-
const
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
553
|
+
const applyCatalog = (models: RealtimeModelOption[]) => {
|
|
554
|
+
catalogScopeRef.current = { client, workspaceId, source: "cache" };
|
|
555
|
+
setCatalog(models);
|
|
556
|
+
setSelectedModelId((current) => {
|
|
557
|
+
if (models.some((model) => model.id === current && model.available)) return current;
|
|
558
|
+
return models.find((model) => model.available)?.id ?? CODEX_LIVE_MODEL.id;
|
|
559
|
+
});
|
|
560
|
+
};
|
|
561
|
+
const cached = readCachedRealtimeModelCatalog(client, workspaceId);
|
|
562
|
+
if (cached) {
|
|
563
|
+
const scope = catalogScopeRef.current;
|
|
564
|
+
if (
|
|
565
|
+
scope.client !== client ||
|
|
566
|
+
scope.workspaceId !== workspaceId ||
|
|
567
|
+
scope.source !== "cache"
|
|
568
|
+
) {
|
|
569
|
+
applyCatalog(cached);
|
|
570
|
+
}
|
|
571
|
+
return;
|
|
572
|
+
}
|
|
573
|
+
const loading = loadRealtimeModelCatalog(client, workspaceId);
|
|
574
|
+
if (!loading) return;
|
|
575
|
+
void loading
|
|
576
|
+
.then((models) => {
|
|
432
577
|
if (disposed) return;
|
|
433
|
-
|
|
434
|
-
setCatalog(models);
|
|
435
|
-
setSelectedModelId((current) => {
|
|
436
|
-
if (models.some((model) => model.id === current && model.available)) return current;
|
|
437
|
-
return models.find((model) => model.available)?.id ?? CODEX_LIVE_MODEL.id;
|
|
438
|
-
});
|
|
578
|
+
applyCatalog(models);
|
|
439
579
|
})
|
|
440
580
|
.catch(() => undefined);
|
|
441
581
|
return () => {
|
|
@@ -482,7 +622,7 @@ const IDLE_REALTIME_SNAPSHOT: SessionRealtimeControllerSnapshot = {
|
|
|
482
622
|
error: null,
|
|
483
623
|
};
|
|
484
624
|
|
|
485
|
-
|
|
625
|
+
type NewSessionRealtimeControlProps = {
|
|
486
626
|
client?: RealtimeControllerClient | undefined;
|
|
487
627
|
workspaceId?: string | undefined;
|
|
488
628
|
codexConnected: boolean;
|
|
@@ -500,20 +640,43 @@ export function NewSessionRealtimeControl(props: {
|
|
|
500
640
|
* `none` never attaches a model menu to the bar button.
|
|
501
641
|
*/
|
|
502
642
|
modelMenu?: "split" | "split-desktop" | "none" | undefined;
|
|
503
|
-
}
|
|
504
|
-
|
|
643
|
+
};
|
|
644
|
+
|
|
645
|
+
type NewSessionRealtimeSelection = {
|
|
646
|
+
models: readonly RealtimeModelOption[];
|
|
647
|
+
selectedModel: RealtimeModelOption;
|
|
648
|
+
selectModel: (modelId: string) => void;
|
|
649
|
+
};
|
|
650
|
+
|
|
651
|
+
export function NewSessionRealtimeControl(props: NewSessionRealtimeControlProps) {
|
|
652
|
+
if (props.models && props.selectedModel && props.onSelectModel) {
|
|
653
|
+
return (
|
|
654
|
+
<NewSessionRealtimeControlView
|
|
655
|
+
{...props}
|
|
656
|
+
selection={{
|
|
657
|
+
models: props.models,
|
|
658
|
+
selectedModel: props.selectedModel,
|
|
659
|
+
selectModel: props.onSelectModel,
|
|
660
|
+
}}
|
|
661
|
+
/>
|
|
662
|
+
);
|
|
663
|
+
}
|
|
664
|
+
return <NewSessionRealtimeControlWithInternalSelection {...props} />;
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
function NewSessionRealtimeControlWithInternalSelection(props: NewSessionRealtimeControlProps) {
|
|
668
|
+
const selection = useRealtimeModelSelection({
|
|
505
669
|
client: props.client,
|
|
506
670
|
workspaceId: props.workspaceId,
|
|
507
671
|
codexConnected: props.codexConnected,
|
|
508
672
|
});
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
: internalSelection;
|
|
673
|
+
return <NewSessionRealtimeControlView {...props} selection={selection} />;
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
function NewSessionRealtimeControlView(
|
|
677
|
+
props: NewSessionRealtimeControlProps & { selection: NewSessionRealtimeSelection },
|
|
678
|
+
) {
|
|
679
|
+
const selection = props.selection;
|
|
517
680
|
const audioRef = useRef<HTMLAudioElement>(null);
|
|
518
681
|
const [starting, setStarting] = useState(false);
|
|
519
682
|
const [error, setError] = useState<string | null>(null);
|
|
@@ -582,7 +745,7 @@ export function RealtimeVoiceControl(props: {
|
|
|
582
745
|
* `none` = start button only.
|
|
583
746
|
*/
|
|
584
747
|
modelMenu?: "split" | "split-desktop" | "none" | undefined;
|
|
585
|
-
showDiagnostics?: boolean;
|
|
748
|
+
showDiagnostics?: boolean | undefined;
|
|
586
749
|
/** Extra classes on the in-bar mute cluster (e.g. `max-sm:hidden` when mutes live in “+”). */
|
|
587
750
|
muteControlsClassName?: string | undefined;
|
|
588
751
|
audioRef: RefObject<HTMLAudioElement | null>;
|
|
@@ -636,7 +799,7 @@ export function RealtimeVoiceControl(props: {
|
|
|
636
799
|
: modeOwned
|
|
637
800
|
? props.onStop
|
|
638
801
|
: props.onStart;
|
|
639
|
-
const diagnosticsVisible = props.showDiagnostics ??
|
|
802
|
+
const diagnosticsVisible = props.showDiagnostics ?? false;
|
|
640
803
|
const modelMenu = props.modelMenu ?? "split";
|
|
641
804
|
const showAttachedModelMenu = modelMenu === "split" || modelMenu === "split-desktop";
|
|
642
805
|
const desktopOnlyModelMenu = modelMenu === "split-desktop";
|
package/src/session-context.ts
CHANGED
package/src/session-ui.ts
CHANGED
|
@@ -24,7 +24,12 @@ export type {
|
|
|
24
24
|
} from "./timeline/turn-summary";
|
|
25
25
|
export { QueueSurface } from "./components/queue-surface";
|
|
26
26
|
export type { QueueSurfaceProps } from "./components/queue-surface";
|
|
27
|
-
export {
|
|
27
|
+
export {
|
|
28
|
+
SessionChrome,
|
|
29
|
+
sessionChromeGoalPillExplanation,
|
|
30
|
+
sessionChromeGoalPillLabel,
|
|
31
|
+
sessionChromeGoalPillState,
|
|
32
|
+
} from "./components/session-chrome";
|
|
28
33
|
export type {
|
|
29
34
|
SessionChromeAgentsSignal,
|
|
30
35
|
SessionChromeProps,
|
package/src/session.ts
CHANGED
|
@@ -38,7 +38,13 @@ export {
|
|
|
38
38
|
shouldSubmitOnKey,
|
|
39
39
|
FILE_ONLY_MESSAGE_TEXT,
|
|
40
40
|
} from "./hooks/use-composer";
|
|
41
|
-
export type {
|
|
41
|
+
export type {
|
|
42
|
+
ComposerControllerState,
|
|
43
|
+
ComposerPolicy,
|
|
44
|
+
ComposerSendExtras,
|
|
45
|
+
ComposerState,
|
|
46
|
+
UseComposerOptions,
|
|
47
|
+
} from "./hooks/use-composer";
|
|
42
48
|
export { useFileAttachments } from "./hooks/use-file-attachments";
|
|
43
49
|
export type {
|
|
44
50
|
FileAttachment,
|
|
@@ -96,6 +102,8 @@ export type {
|
|
|
96
102
|
AgentMessageItem,
|
|
97
103
|
AuthNeededItem,
|
|
98
104
|
GoalItem,
|
|
105
|
+
HumanInputAnswerSummary,
|
|
106
|
+
HumanInputItem,
|
|
99
107
|
MachineInputBatchItem,
|
|
100
108
|
MachineInputMember,
|
|
101
109
|
MemoryItem,
|
|
@@ -1,10 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
ArrowRightIcon,
|
|
3
|
-
BotIcon,
|
|
4
|
-
BrainCircuitIcon,
|
|
5
|
-
BrainIcon,
|
|
6
|
-
SquareTerminalIcon,
|
|
7
|
-
} from "lucide-react";
|
|
1
|
+
import { ArrowRightIcon, BotIcon, BrainCircuitIcon } from "lucide-react";
|
|
8
2
|
import { lazy, Suspense, useLayoutEffect, useRef } from "react";
|
|
9
3
|
import { jsx as rowJsx, jsxs as rowJsxs } from "react/jsx-runtime";
|
|
10
4
|
import { Markdown } from "../components/markdown";
|
|
@@ -16,9 +10,10 @@ import type { RetainedArtifactLoader, RetainedScreenshotLoader, ToolRegistry } f
|
|
|
16
10
|
import { useSeenActivityIds } from "./seen-activity-ids";
|
|
17
11
|
import { BodyNote, PayloadBlock, ActivityDisclosure, ToolCallTruncationProvider } from "./shared";
|
|
18
12
|
import { toolDisplayName } from "./tool-display-name";
|
|
19
|
-
import type { ActivityItem, MemoryItem,
|
|
13
|
+
import type { ActivityItem, MemoryItem, WorkerItem } from "./types";
|
|
20
14
|
|
|
21
15
|
const LazyFleetDecisionRow = lazy(() => import("./fleet-decision-row"));
|
|
16
|
+
const LazyPlatformActivityRow = lazy(() => import("./platform-activity-row"));
|
|
22
17
|
|
|
23
18
|
/* ----------------------------------------------------------------------------
|
|
24
19
|
Activity rail
|
|
@@ -157,7 +152,23 @@ function renderActivity(
|
|
|
157
152
|
) {
|
|
158
153
|
switch (item.kind) {
|
|
159
154
|
case "reasoning":
|
|
160
|
-
|
|
155
|
+
case "sandbox":
|
|
156
|
+
case "startup-phase":
|
|
157
|
+
return (
|
|
158
|
+
<Suspense fallback={null}>
|
|
159
|
+
<LazyPlatformActivityRow
|
|
160
|
+
item={item}
|
|
161
|
+
d={ActivityDisclosure}
|
|
162
|
+
p={PayloadBlock}
|
|
163
|
+
t={toolDisplayName}
|
|
164
|
+
b={BotIcon}
|
|
165
|
+
m={Markdown}
|
|
166
|
+
r={truncate}
|
|
167
|
+
j={rowJsx}
|
|
168
|
+
s={rowJsxs}
|
|
169
|
+
/>
|
|
170
|
+
</Suspense>
|
|
171
|
+
);
|
|
161
172
|
case "tool-call": {
|
|
162
173
|
const Renderer = toolRegistry.resolve(item);
|
|
163
174
|
return (
|
|
@@ -172,8 +183,6 @@ function renderActivity(
|
|
|
172
183
|
}
|
|
173
184
|
case "worker":
|
|
174
185
|
return <WorkerRow item={item} onOpenSession={onOpenSession} />;
|
|
175
|
-
case "sandbox":
|
|
176
|
-
return <SandboxRow item={item} />;
|
|
177
186
|
case "memory":
|
|
178
187
|
return <MemoryRow item={item} onMemoryClick={onMemoryClick} />;
|
|
179
188
|
case "fleet-decision":
|
|
@@ -193,32 +202,6 @@ function renderActivity(
|
|
|
193
202
|
}
|
|
194
203
|
}
|
|
195
204
|
|
|
196
|
-
function ReasoningRow({ item }: { item: ReasoningItem }) {
|
|
197
|
-
// Reasoning recedes: a dimmer, lighter-weight title so action rows lead and
|
|
198
|
-
// thought rows sit a half-step back in the hierarchy. Body uses the same
|
|
199
|
-
// GFM markdown path as agent messages — Codex reasoning often carries
|
|
200
|
-
// headings/lists/bold that were previously shown as raw plaintext.
|
|
201
|
-
return (
|
|
202
|
-
<ActivityDisclosure
|
|
203
|
-
icon={<BrainIcon className="size-3.5" />}
|
|
204
|
-
iconTone="muted"
|
|
205
|
-
title={
|
|
206
|
-
item.streaming ? (
|
|
207
|
-
"Thinking"
|
|
208
|
-
) : (
|
|
209
|
-
<span className="font-normal italic text-og-fg-subtle">Thought</span>
|
|
210
|
-
)
|
|
211
|
-
}
|
|
212
|
-
running={item.streaming}
|
|
213
|
-
preview={truncate(item.text, 110)}
|
|
214
|
-
>
|
|
215
|
-
<div className="text-og-base leading-6 text-og-fg-muted [&_strong]:text-og-fg-muted">
|
|
216
|
-
<Markdown streaming={item.streaming}>{item.text}</Markdown>
|
|
217
|
-
</div>
|
|
218
|
-
</ActivityDisclosure>
|
|
219
|
-
);
|
|
220
|
-
}
|
|
221
|
-
|
|
222
205
|
/**
|
|
223
206
|
* Human labels for the memory kinds, translated at the SDK boundary so a raw
|
|
224
207
|
* enum slug never renders as UI. Kept local to the library (the app has its own
|
|
@@ -317,44 +300,6 @@ function MemoryRow({
|
|
|
317
300
|
);
|
|
318
301
|
}
|
|
319
302
|
|
|
320
|
-
/**
|
|
321
|
-
* A human title for a sandbox operation row. Named platform operations (the lazy
|
|
322
|
-
* `sandbox.provision` first-establish that now runs mid-turn) read as calm,
|
|
323
|
-
* status-aware English instead of a raw op id — the box coming up should look
|
|
324
|
-
* like "Starting sandbox", never an unexplained long-running command. Unnamed /
|
|
325
|
-
* unknown ops fall back to the generic id-to-words {@link toolDisplayName}.
|
|
326
|
-
*/
|
|
327
|
-
function sandboxRowTitle(item: SandboxItem): string {
|
|
328
|
-
if (item.name === "sandbox.provision") {
|
|
329
|
-
if (item.status === "failed") return "Sandbox didn’t start";
|
|
330
|
-
if (item.status === "running") return "Starting sandbox";
|
|
331
|
-
if (item.origin === "resumed") return "Sandbox reattached";
|
|
332
|
-
if (item.origin === "restored") return "Sandbox restored";
|
|
333
|
-
if (item.origin === "created") return "Sandbox created";
|
|
334
|
-
return "Sandbox ready";
|
|
335
|
-
}
|
|
336
|
-
return toolDisplayName(item.name);
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
function SandboxRow({ item }: { item: SandboxItem }) {
|
|
340
|
-
return (
|
|
341
|
-
<ActivityDisclosure
|
|
342
|
-
icon={<SquareTerminalIcon className="size-3.5" />}
|
|
343
|
-
iconTone={
|
|
344
|
-
item.status === "failed" ? "failed" : item.status === "running" ? "running" : "muted"
|
|
345
|
-
}
|
|
346
|
-
title={sandboxRowTitle(item)}
|
|
347
|
-
running={item.status === "running"}
|
|
348
|
-
failed={item.status === "failed"}
|
|
349
|
-
cancelled={item.status === "cancelled"}
|
|
350
|
-
preview={item.command ?? undefined}
|
|
351
|
-
>
|
|
352
|
-
{item.command ? <PayloadBlock label="Command" value={item.command} /> : null}
|
|
353
|
-
{item.output ? <PayloadBlock label="Output" value={item.output} /> : null}
|
|
354
|
-
</ActivityDisclosure>
|
|
355
|
-
);
|
|
356
|
-
}
|
|
357
|
-
|
|
358
303
|
/** Spawned/messaged worker sessions get a first-class card, not a tool row. */
|
|
359
304
|
function WorkerRow({
|
|
360
305
|
item,
|
package/src/timeline/index.ts
CHANGED
|
@@ -36,12 +36,16 @@ export type {
|
|
|
36
36
|
FleetDecisionItem,
|
|
37
37
|
FleetDecisionScoreItem,
|
|
38
38
|
GoalItem,
|
|
39
|
+
HumanInputAnswerSummary,
|
|
40
|
+
HumanInputItem,
|
|
39
41
|
MachineInputBatchItem,
|
|
40
42
|
MachineInputMember,
|
|
41
43
|
MemoryItem,
|
|
42
44
|
NoticeItem,
|
|
43
45
|
ReasoningItem,
|
|
44
46
|
SandboxItem,
|
|
47
|
+
StartupPhase,
|
|
48
|
+
StartupPhaseItem,
|
|
45
49
|
SessionStatusItem,
|
|
46
50
|
TimelineGroup,
|
|
47
51
|
TimelineItem,
|