@opengeni/react 7.1.0 → 7.1.2-canary.36079201993001
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/dist/artifacts-document.js +2 -2
- package/dist/artifacts-presentation.js +2 -2
- package/dist/artifacts-spreadsheet.js +2 -2
- package/dist/artifacts.js +4 -4
- package/dist/{browser-viewer-MN7MOYQT.js → browser-viewer-K2H3SLIW.js} +2 -2
- package/dist/{chunk-4ACTWOOW.js → chunk-6N3AZUOV.js} +31 -14
- package/dist/chunk-6N3AZUOV.js.map +1 -0
- package/dist/{chunk-KCGXPP5V.js → chunk-6P7T4RPE.js} +7 -3
- package/dist/chunk-6P7T4RPE.js.map +1 -0
- package/dist/{chunk-SHRCATV6.js → chunk-7J4DUR3E.js} +12 -5
- package/dist/chunk-7J4DUR3E.js.map +1 -0
- package/dist/{chunk-ZC2DK5SC.js → chunk-AWIX4UAC.js} +88 -7
- package/dist/chunk-AWIX4UAC.js.map +1 -0
- package/dist/{chunk-UQS7BGNQ.js → chunk-ETA37UZ7.js} +82 -11
- package/dist/chunk-ETA37UZ7.js.map +1 -0
- package/dist/{chunk-VR4Z73EU.js → chunk-G2SCXNXW.js} +9 -2
- package/dist/chunk-G2SCXNXW.js.map +1 -0
- package/dist/{chunk-5UONOQ3Y.js → chunk-K3MQZO7R.js} +150 -67
- package/dist/chunk-K3MQZO7R.js.map +1 -0
- package/dist/{chunk-OWHPKAOV.js → chunk-L6MYP3QU.js} +4 -2
- package/dist/{chunk-OWHPKAOV.js.map → chunk-L6MYP3QU.js.map} +1 -1
- package/dist/{chunk-VI7HELBI.js → chunk-SJ5HE4ZA.js} +21 -2
- package/dist/chunk-SJ5HE4ZA.js.map +1 -0
- package/dist/{chunk-XMXRI5JK.js → chunk-ZZ4744OZ.js} +128 -71
- package/dist/chunk-ZZ4744OZ.js.map +1 -0
- package/dist/components/artifacts/artifact-surface.d.ts +3 -1
- package/dist/components/artifacts/document-editor.d.ts +4 -2
- package/dist/components/artifacts/editable-document.d.ts +1 -1
- package/dist/components/artifacts/editable-presentation.d.ts +1 -1
- package/dist/components/artifacts/editable-spreadsheet.d.ts +2 -1
- package/dist/components/artifacts/presentation-editor.d.ts +4 -2
- package/dist/components/browser-viewer.d.ts +4 -1
- package/dist/components/markdown.d.ts +2 -0
- package/dist/components/session-capability-frame.d.ts +5 -1
- package/dist/composer.js +1 -1
- package/dist/connect.js +2 -2
- package/dist/hooks/use-browser-session.d.ts +3 -0
- package/dist/index.js +7 -6
- package/dist/index.js.map +1 -1
- package/dist/interaction.js +1 -1
- package/dist/session-ui.js +4 -4
- package/dist/session.js +1 -1
- package/dist/timeline/types.d.ts +2 -0
- package/package.json +3 -3
- package/src/components/artifacts/artifact-surface.tsx +39 -19
- package/src/components/artifacts/document-editor.tsx +77 -9
- package/src/components/artifacts/editable-document.tsx +3 -0
- package/src/components/artifacts/editable-presentation.tsx +3 -0
- package/src/components/artifacts/editable-spreadsheet.tsx +3 -0
- package/src/components/artifacts/presentation-editor.tsx +6 -0
- package/src/components/browser-viewer.tsx +11 -5
- package/src/components/composer-transcription-control.tsx +4 -2
- package/src/components/composer.tsx +20 -9
- package/src/components/markdown.tsx +9 -2
- package/src/components/sandbox-workspace.tsx +1 -0
- package/src/components/session-capability-frame.tsx +11 -3
- package/src/components/session-mcp-capability-card.tsx +174 -70
- package/src/hooks/use-browser-frame-stream.ts +3 -1
- package/src/hooks/use-browser-session.ts +107 -3
- package/src/hooks/use-voice-input.ts +12 -1
- package/src/timeline/compaction-copy.ts +2 -0
- package/src/timeline/parsers.ts +2 -2
- package/src/timeline/projection.ts +31 -5
- package/src/timeline/tool-renderers.tsx +105 -16
- package/src/timeline/turn-summary.tsx +10 -2
- package/src/timeline/types.ts +2 -0
- package/styles/compiled.css +6 -0
- package/styles/session-capability.css +2 -0
- package/dist/chunk-4ACTWOOW.js.map +0 -1
- package/dist/chunk-5UONOQ3Y.js.map +0 -1
- package/dist/chunk-KCGXPP5V.js.map +0 -1
- package/dist/chunk-SHRCATV6.js.map +0 -1
- package/dist/chunk-UQS7BGNQ.js.map +0 -1
- package/dist/chunk-VI7HELBI.js.map +0 -1
- package/dist/chunk-VR4Z73EU.js.map +0 -1
- package/dist/chunk-XMXRI5JK.js.map +0 -1
- package/dist/chunk-ZC2DK5SC.js.map +0 -1
- /package/dist/{browser-viewer-MN7MOYQT.js.map → browser-viewer-K2H3SLIW.js.map} +0 -0
|
@@ -23,6 +23,9 @@ export type UseBrowserSessionOptions = EmbeddedBrowserInteractionClientOverride
|
|
|
23
23
|
browserSessionId: string | null;
|
|
24
24
|
enabled?: boolean | undefined;
|
|
25
25
|
pollIntervalMs?: number | undefined;
|
|
26
|
+
/** Keep session/tab inventory current without repeatedly fetching the full
|
|
27
|
+
* accessibility tree while a live frame is available or the viewer is hidden. */
|
|
28
|
+
semanticObservationEnabled?: boolean | undefined;
|
|
26
29
|
};
|
|
27
30
|
|
|
28
31
|
export type UseBrowserSessionResult = {
|
|
@@ -58,6 +61,13 @@ export function useBrowserSession(options: UseBrowserSessionOptions): UseBrowser
|
|
|
58
61
|
const { client, workspaceId } = useEmbeddedBrowserInteraction(options);
|
|
59
62
|
const browserSessionId = options.browserSessionId;
|
|
60
63
|
const enabled = (options.enabled ?? true) && browserSessionId !== null;
|
|
64
|
+
const semanticObservationEnabled = options.semanticObservationEnabled ?? true;
|
|
65
|
+
const semanticObservationEnabledRef = useRef(semanticObservationEnabled);
|
|
66
|
+
semanticObservationEnabledRef.current = semanticObservationEnabled;
|
|
67
|
+
const previousSemanticObservationRef = useRef({
|
|
68
|
+
browserSessionId,
|
|
69
|
+
enabled: semanticObservationEnabled,
|
|
70
|
+
});
|
|
61
71
|
const pageLive = usePageLiveActivity();
|
|
62
72
|
const pollIntervalMs = Math.max(750, options.pollIntervalMs ?? 2_000);
|
|
63
73
|
const [state, setState] = useState<{
|
|
@@ -75,10 +85,16 @@ export function useBrowserSession(options: UseBrowserSessionOptions): UseBrowser
|
|
|
75
85
|
const refreshBlocked = isNonRetryableInteractionError(visible.error);
|
|
76
86
|
const selectedTargetIdRef = useRef<string | null>(visible.selectedTargetId);
|
|
77
87
|
selectedTargetIdRef.current = visible.selectedTargetId;
|
|
88
|
+
const selectedTargetRef = useRef<BrowserTarget | null>(
|
|
89
|
+
visible.targets.find((target) => target.id === visible.selectedTargetId) ?? null,
|
|
90
|
+
);
|
|
91
|
+
selectedTargetRef.current =
|
|
92
|
+
visible.targets.find((target) => target.id === visible.selectedTargetId) ?? null;
|
|
78
93
|
const observationRef = useRef<{
|
|
79
94
|
browserSessionId: string | null;
|
|
80
95
|
observation: BrowserObservation | null;
|
|
81
96
|
}>({ browserSessionId, observation: visible.observation });
|
|
97
|
+
const lastObservationAtRef = useRef(0);
|
|
82
98
|
if (observationRef.current.browserSessionId !== browserSessionId) {
|
|
83
99
|
observationRef.current = {
|
|
84
100
|
browserSessionId,
|
|
@@ -124,7 +140,15 @@ export function useBrowserSession(options: UseBrowserSessionOptions): UseBrowser
|
|
|
124
140
|
if (!mountedRef.current || requestRef.current.id !== id) return;
|
|
125
141
|
const selected = chooseTarget(targetResponse.targets, selectedTargetIdRef.current);
|
|
126
142
|
selectedTargetIdRef.current = selected?.id ?? null;
|
|
127
|
-
|
|
143
|
+
const previousObservation =
|
|
144
|
+
observationRef.current.browserSessionId === browserSessionId
|
|
145
|
+
? observationRef.current.observation
|
|
146
|
+
: null;
|
|
147
|
+
const retainedObservation =
|
|
148
|
+
selected && previousObservation && sameObservationTarget(previousObservation, selected)
|
|
149
|
+
? previousObservation
|
|
150
|
+
: null;
|
|
151
|
+
observationRef.current = { browserSessionId, observation: retainedObservation };
|
|
128
152
|
setState((current) =>
|
|
129
153
|
current.browserSessionId === browserSessionId
|
|
130
154
|
? {
|
|
@@ -132,13 +156,13 @@ export function useBrowserSession(options: UseBrowserSessionOptions): UseBrowser
|
|
|
132
156
|
session,
|
|
133
157
|
targets: targetResponse.targets,
|
|
134
158
|
selectedTargetId: selected?.id ?? null,
|
|
135
|
-
observation:
|
|
159
|
+
observation: retainedObservation,
|
|
136
160
|
loading: false,
|
|
137
161
|
error: null,
|
|
138
162
|
}
|
|
139
163
|
: current,
|
|
140
164
|
);
|
|
141
|
-
if (!selected) return;
|
|
165
|
+
if (!selected || !semanticObservationEnabledRef.current) return;
|
|
142
166
|
try {
|
|
143
167
|
const observation = await client.observeBrowserTarget(
|
|
144
168
|
workspaceId,
|
|
@@ -148,6 +172,7 @@ export function useBrowserSession(options: UseBrowserSessionOptions): UseBrowser
|
|
|
148
172
|
);
|
|
149
173
|
if (!mountedRef.current || requestRef.current.id !== id) return;
|
|
150
174
|
observationRef.current = { browserSessionId, observation };
|
|
175
|
+
lastObservationAtRef.current = Date.now();
|
|
151
176
|
setState((current) =>
|
|
152
177
|
current.browserSessionId === browserSessionId &&
|
|
153
178
|
current.selectedTargetId === observation.target.id
|
|
@@ -180,6 +205,37 @@ export function useBrowserSession(options: UseBrowserSessionOptions): UseBrowser
|
|
|
180
205
|
}
|
|
181
206
|
}, [browserSessionId, client, enabled, workspaceId]);
|
|
182
207
|
|
|
208
|
+
const refreshSemantic = useCallback(async (): Promise<void> => {
|
|
209
|
+
if (!enabled || !browserSessionId) return;
|
|
210
|
+
const target = selectedTargetRef.current;
|
|
211
|
+
if (!target) return;
|
|
212
|
+
const id = requestRef.current.id + 1;
|
|
213
|
+
requestRef.current.controller?.abort();
|
|
214
|
+
const controller = new AbortController();
|
|
215
|
+
requestRef.current = { id, controller };
|
|
216
|
+
try {
|
|
217
|
+
const observation = await client.observeBrowserTarget(
|
|
218
|
+
workspaceId,
|
|
219
|
+
browserSessionId,
|
|
220
|
+
target.id,
|
|
221
|
+
{ signal: controller.signal },
|
|
222
|
+
);
|
|
223
|
+
if (!mountedRef.current || requestRef.current.id !== id) return;
|
|
224
|
+
if (!sameObservationTarget(observation, selectedTargetRef.current)) return;
|
|
225
|
+
observationRef.current = { browserSessionId, observation };
|
|
226
|
+
lastObservationAtRef.current = Date.now();
|
|
227
|
+
setState((current) =>
|
|
228
|
+
current.browserSessionId === browserSessionId && current.selectedTargetId === target.id
|
|
229
|
+
? { ...current, observation }
|
|
230
|
+
: current,
|
|
231
|
+
);
|
|
232
|
+
} catch {
|
|
233
|
+
// A failed semantic fallback cannot disable an otherwise live browser.
|
|
234
|
+
} finally {
|
|
235
|
+
if (requestRef.current.id === id) requestRef.current = { id, controller: null };
|
|
236
|
+
}
|
|
237
|
+
}, [browserSessionId, client, enabled, workspaceId]);
|
|
238
|
+
|
|
183
239
|
useEffect(() => {
|
|
184
240
|
mountedRef.current = true;
|
|
185
241
|
if (!enabled) {
|
|
@@ -199,6 +255,37 @@ export function useBrowserSession(options: UseBrowserSessionOptions): UseBrowser
|
|
|
199
255
|
};
|
|
200
256
|
}, [browserSessionId, enabled, invalidateRefresh, refresh]);
|
|
201
257
|
|
|
258
|
+
useEffect(() => {
|
|
259
|
+
const previous = previousSemanticObservationRef.current;
|
|
260
|
+
previousSemanticObservationRef.current = {
|
|
261
|
+
browserSessionId,
|
|
262
|
+
enabled: semanticObservationEnabled,
|
|
263
|
+
};
|
|
264
|
+
if (
|
|
265
|
+
enabled &&
|
|
266
|
+
semanticObservationEnabled &&
|
|
267
|
+
previous.browserSessionId === browserSessionId &&
|
|
268
|
+
!previous.enabled
|
|
269
|
+
) {
|
|
270
|
+
// The media stream disappeared or the user returned to the Browser tab.
|
|
271
|
+
// A just-returned action/tab observation is already a fresh fallback.
|
|
272
|
+
// Otherwise drop the old tree/fence until a new read settles.
|
|
273
|
+
const currentObservation = observationRef.current.observation;
|
|
274
|
+
if (
|
|
275
|
+
currentObservation &&
|
|
276
|
+
sameObservationTarget(currentObservation, selectedTargetRef.current) &&
|
|
277
|
+
Date.now() - lastObservationAtRef.current < pollIntervalMs
|
|
278
|
+
) {
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
observationRef.current = { browserSessionId, observation: null };
|
|
282
|
+
setState((current) =>
|
|
283
|
+
current.browserSessionId === browserSessionId ? { ...current, observation: null } : current,
|
|
284
|
+
);
|
|
285
|
+
void refreshSemantic();
|
|
286
|
+
}
|
|
287
|
+
}, [browserSessionId, enabled, pollIntervalMs, refreshSemantic, semanticObservationEnabled]);
|
|
288
|
+
|
|
202
289
|
useEffect(() => {
|
|
203
290
|
if (!enabled || !pageLive || visible.mutating || refreshBlocked) return;
|
|
204
291
|
const timer = setInterval(() => {
|
|
@@ -301,6 +388,7 @@ export function useBrowserSession(options: UseBrowserSessionOptions): UseBrowser
|
|
|
301
388
|
}
|
|
302
389
|
selectedTargetIdRef.current = target.id;
|
|
303
390
|
observationRef.current = { browserSessionId, observation };
|
|
391
|
+
lastObservationAtRef.current = Date.now();
|
|
304
392
|
setState((current) =>
|
|
305
393
|
current.browserSessionId === browserSessionId
|
|
306
394
|
? {
|
|
@@ -332,6 +420,7 @@ export function useBrowserSession(options: UseBrowserSessionOptions): UseBrowser
|
|
|
332
420
|
const target = observation.target;
|
|
333
421
|
selectedTargetIdRef.current = target.id;
|
|
334
422
|
observationRef.current = { browserSessionId, observation };
|
|
423
|
+
lastObservationAtRef.current = Date.now();
|
|
335
424
|
setState((current) =>
|
|
336
425
|
current.browserSessionId === browserSessionId
|
|
337
426
|
? {
|
|
@@ -365,6 +454,7 @@ export function useBrowserSession(options: UseBrowserSessionOptions): UseBrowser
|
|
|
365
454
|
: null;
|
|
366
455
|
selectedTargetIdRef.current = selected?.id ?? null;
|
|
367
456
|
observationRef.current = { browserSessionId, observation };
|
|
457
|
+
if (observation) lastObservationAtRef.current = Date.now();
|
|
368
458
|
setState((current) =>
|
|
369
459
|
current.browserSessionId === browserSessionId
|
|
370
460
|
? {
|
|
@@ -424,6 +514,7 @@ export function useBrowserSession(options: UseBrowserSessionOptions): UseBrowser
|
|
|
424
514
|
browserSessionId,
|
|
425
515
|
observation: receipt.observation,
|
|
426
516
|
};
|
|
517
|
+
lastObservationAtRef.current = Date.now();
|
|
427
518
|
setState((current) =>
|
|
428
519
|
current.browserSessionId === browserSessionId
|
|
429
520
|
? {
|
|
@@ -540,6 +631,19 @@ function replaceTarget(targets: readonly BrowserTarget[], target: BrowserTarget)
|
|
|
540
631
|
return next.sort((left, right) => left.createdAt.localeCompare(right.createdAt));
|
|
541
632
|
}
|
|
542
633
|
|
|
634
|
+
function sameObservationTarget(
|
|
635
|
+
observation: BrowserObservation,
|
|
636
|
+
target: BrowserTarget | null,
|
|
637
|
+
): boolean {
|
|
638
|
+
return (
|
|
639
|
+
target !== null &&
|
|
640
|
+
observation.target.id === target.id &&
|
|
641
|
+
observation.target.controllerGeneration === target.controllerGeneration &&
|
|
642
|
+
observation.target.targetGeneration === target.targetGeneration &&
|
|
643
|
+
observation.target.documentGeneration === target.documentGeneration
|
|
644
|
+
);
|
|
645
|
+
}
|
|
646
|
+
|
|
543
647
|
function isMissingBrowserTarget(error: unknown): boolean {
|
|
544
648
|
return error instanceof OpenGeniApiError && error.code === "target_not_found";
|
|
545
649
|
}
|
|
@@ -163,6 +163,7 @@ export function useVoiceInput({
|
|
|
163
163
|
const automaticRetryAttemptRef = useRef(0);
|
|
164
164
|
const ownerHeartbeatTimerRef = useRef<ReturnType<typeof setInterval> | null>(null);
|
|
165
165
|
const controllerRef = useRef<AbortController | null>(null);
|
|
166
|
+
const disabledRef = useRef(disabled);
|
|
166
167
|
const valueRef = useRef(value);
|
|
167
168
|
const storeRef = useRef<VoiceRecordingStore | null>(null);
|
|
168
169
|
const storePromiseRef = useRef<Promise<VoiceRecordingStore> | null>(null);
|
|
@@ -176,6 +177,7 @@ export function useVoiceInput({
|
|
|
176
177
|
const statusRef = useRef(status);
|
|
177
178
|
valueRef.current = value;
|
|
178
179
|
statusRef.current = status;
|
|
180
|
+
disabledRef.current = disabled;
|
|
179
181
|
workspaceIdRef.current = workspaceId;
|
|
180
182
|
|
|
181
183
|
const ensureOwnerId = useCallback(async (): Promise<string> => {
|
|
@@ -468,6 +470,14 @@ export function useVoiceInput({
|
|
|
468
470
|
return;
|
|
469
471
|
}
|
|
470
472
|
|
|
473
|
+
// Session creation can disable the composer while a recording is
|
|
474
|
+
// transcribing. Keep the durable transcript for explicit insertion
|
|
475
|
+
// later; never append text after Send captured its visible snapshot.
|
|
476
|
+
if (disabledRef.current) {
|
|
477
|
+
setStatus("transcript-ready");
|
|
478
|
+
return;
|
|
479
|
+
}
|
|
480
|
+
|
|
471
481
|
const next = appendFinalTranscript(valueRef.current, response.text);
|
|
472
482
|
if (next !== valueRef.current) {
|
|
473
483
|
valueRef.current = next;
|
|
@@ -944,6 +954,7 @@ export function useVoiceInput({
|
|
|
944
954
|
const insertSavedTranscript = useCallback(async (): Promise<void> => {
|
|
945
955
|
const manifest = manifestRef.current;
|
|
946
956
|
if (
|
|
957
|
+
disabledRef.current ||
|
|
947
958
|
!manifest ||
|
|
948
959
|
manifest.workspaceId !== workspaceId ||
|
|
949
960
|
manifest.finalizationState !== "transcript-ready" ||
|
|
@@ -955,7 +966,7 @@ export function useVoiceInput({
|
|
|
955
966
|
controllerRef.current?.abort();
|
|
956
967
|
controllerRef.current = null;
|
|
957
968
|
const store = await ensureStore();
|
|
958
|
-
if (generation !== generationRef.current) return;
|
|
969
|
+
if (generation !== generationRef.current || disabledRef.current) return;
|
|
959
970
|
const next = appendFinalTranscript(valueRef.current, manifest.transcriptText);
|
|
960
971
|
if (next !== valueRef.current) {
|
|
961
972
|
valueRef.current = next;
|
|
@@ -15,6 +15,8 @@ export function compactionSkipSubtitle(
|
|
|
15
15
|
return "No active history to compact";
|
|
16
16
|
case "replacement_not_smaller":
|
|
17
17
|
return "Checkpoint would not reduce memory size";
|
|
18
|
+
case "replacement_exceeds_model_budget":
|
|
19
|
+
return "Checkpoint exceeds this model's context budget. Chat history is unchanged.";
|
|
18
20
|
case "replacement_unchanged":
|
|
19
21
|
return "Checkpoint made no progress";
|
|
20
22
|
case "summarization_failed":
|
package/src/timeline/parsers.ts
CHANGED
|
@@ -566,8 +566,8 @@ export function retainedScreenshotMetadata(out: unknown): RetainedArtifactMetada
|
|
|
566
566
|
if (!value.available) {
|
|
567
567
|
return typeof value.reason === "string" ? (value as unknown as RetainedArtifactMetadata) : null;
|
|
568
568
|
}
|
|
569
|
-
return value.kind === "computer_screenshot" &&
|
|
570
|
-
|
|
569
|
+
return (value.kind === "computer_screenshot" || value.kind === "browser_screenshot") &&
|
|
570
|
+
["image/png", "image/jpeg", "image/webp"].includes(String(value.contentType)) &&
|
|
571
571
|
typeof value.originalBytes === "number" &&
|
|
572
572
|
typeof value.sha256 === "string" &&
|
|
573
573
|
value.dimensions !== null &&
|
|
@@ -1016,6 +1016,7 @@ export function buildTimeline(
|
|
|
1016
1016
|
case "tool.auth_needed":
|
|
1017
1017
|
case "credential.auth_needed": {
|
|
1018
1018
|
const capability = capabilityAuthorizationRequest(payload.capability);
|
|
1019
|
+
const setupRequest = customMcpSetupRequest(payload.setupRequest);
|
|
1019
1020
|
if (
|
|
1020
1021
|
event.type === "tool.auth_needed" &&
|
|
1021
1022
|
!stringValue(payload.toolName) &&
|
|
@@ -1045,11 +1046,12 @@ export function buildTimeline(
|
|
|
1045
1046
|
payload.connectionSubjectScope === "workspace"
|
|
1046
1047
|
? payload.connectionSubjectScope
|
|
1047
1048
|
: null,
|
|
1048
|
-
source:
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1049
|
+
source:
|
|
1050
|
+
capability || setupRequest
|
|
1051
|
+
? "capability"
|
|
1052
|
+
: event.type === "tool.auth_needed"
|
|
1053
|
+
? "tool"
|
|
1054
|
+
: "credential",
|
|
1053
1055
|
providerDomain: stringValue(payload.providerDomain),
|
|
1054
1056
|
connectionId: typeof payload.connectionId === "string" ? payload.connectionId : null,
|
|
1055
1057
|
authoritySource: payload.authoritySource === "host" ? "host" : null,
|
|
@@ -1064,6 +1066,7 @@ export function buildTimeline(
|
|
|
1064
1066
|
authorizationUrl:
|
|
1065
1067
|
typeof payload.authorizationUrl === "string" ? payload.authorizationUrl : null,
|
|
1066
1068
|
capability,
|
|
1069
|
+
setupRequest,
|
|
1067
1070
|
occurredAt: event.occurredAt,
|
|
1068
1071
|
});
|
|
1069
1072
|
break;
|
|
@@ -2623,6 +2626,29 @@ function capabilityAuthorizationRequest(
|
|
|
2623
2626
|
};
|
|
2624
2627
|
}
|
|
2625
2628
|
|
|
2629
|
+
function customMcpSetupRequest(value: unknown): NonNullable<AuthNeededItem["setupRequest"]> | null {
|
|
2630
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
2631
|
+
const input = value as Record<string, unknown>;
|
|
2632
|
+
if (
|
|
2633
|
+
input.kind !== "mcp" ||
|
|
2634
|
+
typeof input.name !== "string" ||
|
|
2635
|
+
typeof input.rationale !== "string" ||
|
|
2636
|
+
typeof input.endpointUrl !== "string"
|
|
2637
|
+
)
|
|
2638
|
+
return null;
|
|
2639
|
+
try {
|
|
2640
|
+
if (new URL(input.endpointUrl).protocol !== "https:") return null;
|
|
2641
|
+
} catch {
|
|
2642
|
+
return null;
|
|
2643
|
+
}
|
|
2644
|
+
return {
|
|
2645
|
+
kind: "mcp",
|
|
2646
|
+
name: input.name,
|
|
2647
|
+
endpointUrl: input.endpointUrl,
|
|
2648
|
+
rationale: input.rationale,
|
|
2649
|
+
};
|
|
2650
|
+
}
|
|
2651
|
+
|
|
2626
2652
|
function stringList(value: unknown): string[] {
|
|
2627
2653
|
return Array.isArray(value)
|
|
2628
2654
|
? value.filter((entry): entry is string => typeof entry === "string" && entry.trim().length > 0)
|
|
@@ -772,21 +772,73 @@ function ComputerCallRenderer({ item, loadRetainedScreenshot }: ToolRendererProp
|
|
|
772
772
|
);
|
|
773
773
|
}
|
|
774
774
|
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
775
|
+
/** Browser tool images share authenticated screenshot loading, with their own media kind. */
|
|
776
|
+
function BrowserScreenshotRenderer(props: ToolRendererProps) {
|
|
777
|
+
const { item, loadRetainedScreenshot } = props;
|
|
778
|
+
const leaf = mcpToolLeaf(item.name);
|
|
779
|
+
const title =
|
|
780
|
+
leaf === "browser_observe"
|
|
781
|
+
? "Observed browser"
|
|
782
|
+
: leaf === "browser_act"
|
|
783
|
+
? "Used browser"
|
|
784
|
+
: "Browser screenshot";
|
|
785
|
+
const retained = retainedScreenshotMetadata(item.output);
|
|
786
|
+
const isFailed = item.status === "failed";
|
|
787
|
+
const isCancelled = item.status === "cancelled";
|
|
788
|
+
|
|
789
|
+
if (item.status === "running" && leaf === "browser_screenshot") {
|
|
790
|
+
return (
|
|
791
|
+
<ActivityDisclosure
|
|
792
|
+
icon={<CameraIcon className={ICON_SIZE} />}
|
|
793
|
+
iconTone="running"
|
|
794
|
+
title={title}
|
|
795
|
+
running
|
|
796
|
+
media={<MediaSkeleton />}
|
|
797
|
+
>
|
|
798
|
+
<BodyNote>capturing frame…</BodyNote>
|
|
799
|
+
</ActivityDisclosure>
|
|
800
|
+
);
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
if (!retained) return <GenericRenderer {...props} />;
|
|
804
|
+
if (!retained.available) {
|
|
805
|
+
const state =
|
|
806
|
+
retained.reason === "expired" || retained.reason === "deleted"
|
|
807
|
+
? retained.reason
|
|
808
|
+
: "unavailable";
|
|
809
|
+
return (
|
|
810
|
+
<ActivityDisclosure
|
|
811
|
+
icon={<CameraOffIcon className={ICON_SIZE} />}
|
|
812
|
+
iconTone={isFailed ? "failed" : "muted"}
|
|
813
|
+
title={`${title} · ${state}`}
|
|
814
|
+
failed={isFailed}
|
|
815
|
+
cancelled={isCancelled}
|
|
816
|
+
preview={`screenshot ${state}`}
|
|
817
|
+
media={<MediaEmpty />}
|
|
818
|
+
>
|
|
819
|
+
<BodyNote tone={isFailed ? "error" : undefined}>
|
|
820
|
+
Screenshot {state}: {retained.reason.replaceAll("_", " ")}.
|
|
821
|
+
</BodyNote>
|
|
822
|
+
</ActivityDisclosure>
|
|
823
|
+
);
|
|
824
|
+
}
|
|
825
|
+
return (
|
|
826
|
+
<RetainedSessionImageDisclosure
|
|
827
|
+
artifact={retained}
|
|
828
|
+
load={loadRetainedScreenshot}
|
|
829
|
+
title={title}
|
|
830
|
+
caption={title}
|
|
831
|
+
noun="screenshot"
|
|
832
|
+
icon={<CameraIcon className={ICON_SIZE} />}
|
|
833
|
+
lightboxLabel="Browser screenshot"
|
|
834
|
+
batched={null}
|
|
835
|
+
failed={isFailed}
|
|
836
|
+
cancelled={isCancelled}
|
|
837
|
+
/>
|
|
838
|
+
);
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
type RetainedSessionImageDisclosureProps = {
|
|
790
842
|
artifact: RetainedArtifactReference;
|
|
791
843
|
load: ToolRendererProps["loadRetainedArtifact"];
|
|
792
844
|
title: string;
|
|
@@ -800,7 +852,41 @@ function RetainedSessionImageDisclosure({
|
|
|
800
852
|
filename?: string;
|
|
801
853
|
defaultOpen?: boolean;
|
|
802
854
|
children?: ReactNode;
|
|
803
|
-
}
|
|
855
|
+
};
|
|
856
|
+
|
|
857
|
+
function RetainedSessionImageDisclosure(props: RetainedSessionImageDisclosureProps) {
|
|
858
|
+
const compact = useContext(CompactActivityContext);
|
|
859
|
+
// The rolling progress label has no screenshot loader by design. Do not run
|
|
860
|
+
// the full image hook there: it would report the missing loader as an error.
|
|
861
|
+
if (compact) {
|
|
862
|
+
return (
|
|
863
|
+
<ActivityDisclosure
|
|
864
|
+
icon={props.icon}
|
|
865
|
+
title={props.title}
|
|
866
|
+
compactPreview={null}
|
|
867
|
+
failed={props.failed}
|
|
868
|
+
cancelled={props.cancelled}
|
|
869
|
+
/>
|
|
870
|
+
);
|
|
871
|
+
}
|
|
872
|
+
return <LoadedRetainedSessionImageDisclosure {...props} />;
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
function LoadedRetainedSessionImageDisclosure({
|
|
876
|
+
artifact,
|
|
877
|
+
load,
|
|
878
|
+
title,
|
|
879
|
+
caption,
|
|
880
|
+
noun,
|
|
881
|
+
icon,
|
|
882
|
+
lightboxLabel,
|
|
883
|
+
batched,
|
|
884
|
+
failed,
|
|
885
|
+
cancelled,
|
|
886
|
+
filename,
|
|
887
|
+
defaultOpen,
|
|
888
|
+
children,
|
|
889
|
+
}: RetainedSessionImageDisclosureProps) {
|
|
804
890
|
const state = useRetainedImageObjectUrl(artifact, load);
|
|
805
891
|
const downloadFilename = filename ?? retainedImageFilename(artifact);
|
|
806
892
|
|
|
@@ -2401,6 +2487,9 @@ const BASE_ENTRIES: ToolRegistryEntry[] = [
|
|
|
2401
2487
|
{ match: "name", name: "apply_patch_call", render: ApplyPatchRenderer },
|
|
2402
2488
|
{ match: "name", name: "apply_patch", render: ApplyPatchRenderer },
|
|
2403
2489
|
{ match: "name", name: "computer_call", render: ComputerCallRenderer },
|
|
2490
|
+
{ match: "name", name: "browser_screenshot", render: BrowserScreenshotRenderer },
|
|
2491
|
+
{ match: "name", name: "browser_observe", render: BrowserScreenshotRenderer },
|
|
2492
|
+
{ match: "name", name: "browser_act", render: BrowserScreenshotRenderer },
|
|
2404
2493
|
// Function-mode computer tools (codex / chat-wire transports).
|
|
2405
2494
|
{ match: "name", name: "computer_screenshot", render: ComputerCallRenderer },
|
|
2406
2495
|
{ match: "name", name: "computer_click", render: ComputerCallRenderer },
|
|
@@ -21,9 +21,11 @@ import {
|
|
|
21
21
|
applyPatchOpsFromToolItem,
|
|
22
22
|
isApplyPatch,
|
|
23
23
|
mediaPreviewFact,
|
|
24
|
+
retainedScreenshotMetadata,
|
|
24
25
|
screenshotDataUrl,
|
|
25
26
|
} from "./parsers";
|
|
26
27
|
import { rawTypeOf } from "./registry";
|
|
28
|
+
import { mcpToolLeaf } from "./tool-display-name";
|
|
27
29
|
import type { ActivityItem, ToolCallItem, TurnOutcome } from "./types";
|
|
28
30
|
export type { TurnOutcome } from "./types";
|
|
29
31
|
|
|
@@ -626,11 +628,17 @@ const BUILT_IN_TURN_SUMMARY_FACETS: readonly TurnSummaryFacet[] = Object.freeze(
|
|
|
626
628
|
summarize: ({ toolCalls }) => {
|
|
627
629
|
let screenshots = 0;
|
|
628
630
|
for (const item of toolCalls) {
|
|
631
|
+
const leaf = mcpToolLeaf(item.name);
|
|
629
632
|
if (
|
|
630
633
|
(rawTypeOf(item) === "computer_call" ||
|
|
631
634
|
item.name === "computer_call" ||
|
|
632
|
-
item.name === "computer_screenshot"
|
|
633
|
-
|
|
635
|
+
item.name === "computer_screenshot" ||
|
|
636
|
+
leaf === "browser_screenshot" ||
|
|
637
|
+
leaf === "browser_observe" ||
|
|
638
|
+
leaf === "browser_act") &&
|
|
639
|
+
(retainedScreenshotMetadata(item.output) !== null ||
|
|
640
|
+
screenshotDataUrl(item.output) !== null ||
|
|
641
|
+
mediaPreviewFact(item.output) !== null)
|
|
634
642
|
) {
|
|
635
643
|
screenshots += 1;
|
|
636
644
|
}
|
package/src/timeline/types.ts
CHANGED
|
@@ -475,6 +475,8 @@ export type AuthNeededItem = {
|
|
|
475
475
|
authorizationUrl: string | null;
|
|
476
476
|
/** Agent-selected catalog recommendation. The host still owns authorization. */
|
|
477
477
|
capability?: NonNullable<ToolAuthNeededPayload["capability"]> | null | undefined;
|
|
478
|
+
/** Human-reviewed custom integration proposal. Never treated as catalog authority. */
|
|
479
|
+
setupRequest?: ToolAuthNeededPayload["setupRequest"] | null | undefined;
|
|
478
480
|
occurredAt: string;
|
|
479
481
|
};
|
|
480
482
|
|
package/styles/compiled.css
CHANGED
|
@@ -809,6 +809,9 @@
|
|
|
809
809
|
:where(.og-root).og-session-capability-setup [role=alert],:where(.og-root) .og-session-capability-setup [role=alert] {
|
|
810
810
|
color: var(--og-color-danger,#b42318);
|
|
811
811
|
}
|
|
812
|
+
:where(.og-root).og-session-capability-setup .og-session-capability-notice,:where(.og-root) .og-session-capability-setup .og-session-capability-notice {
|
|
813
|
+
color: var(--og-color-fg-muted);
|
|
814
|
+
}
|
|
812
815
|
:where(.og-root).og-session-capability-shell[data-state="complete"],:where(.og-root) .og-session-capability-shell[data-state="complete"] {
|
|
813
816
|
border-color: var(--og-color-status-success,#16804a);
|
|
814
817
|
@supports (color: color-mix(in lab, red, red)) {
|
|
@@ -857,6 +860,9 @@
|
|
|
857
860
|
color: var(--og-color-fg-muted);
|
|
858
861
|
padding-block: 8px;
|
|
859
862
|
}
|
|
863
|
+
:where(.og-root).og-session-capability-setup .og-session-capability-stop,:where(.og-root) .og-session-capability-setup .og-session-capability-stop {
|
|
864
|
+
justify-self: end;
|
|
865
|
+
}
|
|
860
866
|
@media (pointer:coarse) {
|
|
861
867
|
:where(.og-root).og-session-capability-actions button,:where(.og-root) .og-session-capability-actions button,:where(.og-root).og-session-capability-setup button,:where(.og-root) .og-session-capability-setup button {
|
|
862
868
|
min-height: 44px;
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
.og-session-capability-setup button { font:inherit; padding:9px 12px; border-radius:7px; border:1px solid var(--og-color-border); background:var(--og-color-surface-2); color:inherit; cursor:pointer; }
|
|
29
29
|
.og-session-capability-setup button:disabled { opacity:.55; cursor:not-allowed; }
|
|
30
30
|
.og-session-capability-setup [role=alert] { color:var(--og-color-danger,#b42318); }
|
|
31
|
+
.og-session-capability-setup .og-session-capability-notice { color:var(--og-color-fg-muted); }
|
|
31
32
|
.og-session-capability-shell[data-state="complete"] { border-color:color-mix(in srgb,var(--og-color-status-success,#16804a) 35%,var(--og-color-border)); }
|
|
32
33
|
.og-session-capability-shell[data-state="complete"] .og-session-capability-header > svg,
|
|
33
34
|
.og-session-capability-shell[data-state="complete"] .og-session-capability-copy { color:var(--og-color-status-success,#16804a); }
|
|
@@ -39,4 +40,5 @@
|
|
|
39
40
|
.og-session-capability-setup .og-session-capability-primary { justify-self:end; min-height:40px; padding:10px 16px; border:0; background:var(--og-color-accent-deep,#006cdb); color:var(--og-color-accent-fg,#fff); font-weight:500; }
|
|
40
41
|
.og-session-capability-setup .og-session-capability-primary:hover:not(:disabled) { filter:brightness(.94); }
|
|
41
42
|
.og-session-capability-progress { color:var(--og-color-fg-muted); padding-block:8px; }
|
|
43
|
+
.og-session-capability-setup .og-session-capability-stop { justify-self:end; }
|
|
42
44
|
@media (pointer:coarse) { .og-session-capability-actions button,.og-session-capability-setup button { min-height:44px; } }
|