@opengeni/react 7.6.1-canary.37073905159001 → 7.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -19
- package/dist/{browser-viewer-63TXMINB.js → browser-viewer-5X47I6T2.js} +5 -6
- package/dist/{chunk-TKB4SB44.js → chunk-45GE35O7.js} +2 -10
- package/dist/chunk-45GE35O7.js.map +1 -0
- package/dist/{chunk-DOJWJQ3F.js → chunk-5EXGU7UF.js} +3 -3
- package/dist/{chunk-YN7I3STW.js → chunk-6NEZPF53.js} +32 -153
- package/dist/chunk-6NEZPF53.js.map +1 -0
- package/dist/{chunk-I7IWRQLN.js → chunk-BI57CQUV.js} +2 -2
- package/dist/{chunk-6ZSWSH4I.js → chunk-CHPKSLPN.js} +1 -3
- package/dist/chunk-CHPKSLPN.js.map +1 -0
- package/dist/{chunk-Q5QBEZIQ.js → chunk-DV2DBNPQ.js} +29 -48
- package/dist/chunk-DV2DBNPQ.js.map +1 -0
- package/dist/{chunk-RDKBXMWX.js → chunk-FGBCYQOT.js} +74 -120
- package/dist/chunk-FGBCYQOT.js.map +1 -0
- package/dist/{chunk-AAG4R3QN.js → chunk-KGENSZKS.js} +3 -4
- package/dist/chunk-KGENSZKS.js.map +1 -0
- package/dist/{chunk-YQEAA6LT.js → chunk-KZ73RL76.js} +3 -7
- package/dist/chunk-KZ73RL76.js.map +1 -0
- package/dist/{chunk-LFXYGZWK.js → chunk-LZ3KHA55.js} +60 -169
- package/dist/chunk-LZ3KHA55.js.map +1 -0
- package/dist/{chunk-2QUSQVF2.js → chunk-UINDK6C3.js} +3 -3
- package/dist/{chunk-UTRTG6LH.js → chunk-UJRYEWZI.js} +8 -48
- package/dist/chunk-UJRYEWZI.js.map +1 -0
- package/dist/{chunk-XOAMRDDK.js → chunk-VGIKFIO2.js} +3 -6
- package/dist/chunk-VGIKFIO2.js.map +1 -0
- package/dist/{chunk-ELCE5SXV.js → chunk-WZ55DVGK.js} +4 -8
- package/dist/chunk-WZ55DVGK.js.map +1 -0
- package/dist/{chunk-IQUCRAT7.js → chunk-XA6R7DJQ.js} +3 -3
- package/dist/{chunk-TOAH2BKA.js → chunk-YCGBREUV.js} +2 -6
- package/dist/{chunk-TOAH2BKA.js.map → chunk-YCGBREUV.js.map} +1 -1
- package/dist/{chunk-HNFCAQ5R.js → chunk-YRYF2GLG.js} +31 -39
- package/dist/chunk-YRYF2GLG.js.map +1 -0
- package/dist/{chunk-GISEVGS6.js → chunk-YSMMDOOE.js} +2 -10
- package/dist/chunk-YSMMDOOE.js.map +1 -0
- package/dist/{chunk-CWWKTNKK.js → chunk-Z3DISEQN.js} +18 -35
- package/dist/chunk-Z3DISEQN.js.map +1 -0
- package/dist/client.d.ts +1 -4
- package/dist/commands/types.d.ts +0 -2
- package/dist/components/approval-surface.d.ts +0 -15
- package/dist/composer.js +7 -8
- package/dist/{computer-viewer-X2MBRZY4.js → computer-viewer-7GF35SNY.js} +4 -5
- package/dist/connect.js +3 -3
- package/dist/hooks/use-browser-session.d.ts +0 -4
- package/dist/hooks/use-composer.d.ts +0 -2
- package/dist/hooks/use-computer-session.d.ts +0 -2
- package/dist/hooks/use-file-attachments.d.ts +0 -2
- package/dist/hooks/use-session-events.d.ts +1 -1
- package/dist/hooks/use-workspace-sessions.d.ts +5 -15
- package/dist/index.d.ts +2 -2
- package/dist/index.js +21 -28
- package/dist/index.js.map +1 -1
- package/dist/interaction.d.ts +1 -1
- package/dist/interaction.js +5 -6
- package/dist/interaction.js.map +1 -1
- package/dist/{latest-question-CNEZ5KAN.js → latest-question-3BJGKQQ6.js} +3 -3
- package/dist/lib/format.d.ts +3 -27
- package/dist/lib/interaction-errors.d.ts +0 -3
- package/dist/machines.js +4 -5
- package/dist/{model-policy-picker-menu-JSA64TNN.js → model-policy-picker-menu-PUTA5JOB.js} +4 -4
- package/dist/model-policy.js +1 -1
- package/dist/provider.d.ts +1 -5
- package/dist/{queue-surface-implementation-HENWYFWA.js → queue-surface-implementation-2CAQAIHM.js} +2 -3
- package/dist/{queue-surface-implementation-HENWYFWA.js.map → queue-surface-implementation-2CAQAIHM.js.map} +1 -1
- package/dist/realtime.js +2 -2
- package/dist/{sandbox-files-BBC47SLD.js → sandbox-files-AEJTWGT4.js} +3 -4
- package/dist/session-ui.d.ts +1 -1
- package/dist/session-ui.js +12 -13
- package/dist/session-ui.js.map +1 -1
- package/dist/session.js +6 -7
- package/dist/usage.js +1 -2
- package/dist/usage.js.map +1 -1
- package/package.json +3 -3
- package/src/client.ts +1 -5
- package/src/commands/registry.ts +15 -18
- package/src/commands/types.ts +0 -2
- package/src/components/approval-surface.tsx +42 -166
- package/src/components/browser-viewer.tsx +9 -38
- package/src/components/composer.tsx +2 -9
- package/src/components/computer-viewer.tsx +30 -160
- package/src/components/generated-video-player.tsx +2 -4
- package/src/components/human-input-form.tsx +5 -18
- package/src/components/message-timeline.tsx +2 -4
- package/src/components/open-geni-chat.tsx +4 -11
- package/src/components/queue-surface-state.tsx +1 -3
- package/src/components/session-chrome.tsx +2 -5
- package/src/components/session-commands-panel.tsx +1 -3
- package/src/components/session-conversation.tsx +2 -4
- package/src/components/session-list.tsx +5 -7
- package/src/components/session-mcp-capability-card.tsx +1 -11
- package/src/hooks/internal.ts +2 -3
- package/src/hooks/use-browser-session.ts +79 -115
- package/src/hooks/use-composer.ts +31 -70
- package/src/hooks/use-computer-session.ts +6 -57
- package/src/hooks/use-file-attachments.ts +3 -16
- package/src/hooks/use-goal.ts +1 -2
- package/src/hooks/use-session-capabilities.ts +2 -3
- package/src/hooks/use-session-events.ts +8 -15
- package/src/hooks/use-slash-commands.ts +11 -6
- package/src/hooks/use-turn-queue.ts +1 -2
- package/src/hooks/use-workspace-sessions.ts +7 -40
- package/src/index.ts +2 -7
- package/src/interaction.ts +0 -1
- package/src/lib/format.ts +9 -73
- package/src/lib/interaction-errors.ts +0 -15
- package/src/model-policy.ts +0 -5
- package/src/provider.tsx +4 -12
- package/src/session-ui.ts +1 -1
- package/src/timeline/projection.ts +1 -12
- package/styles/compiled.css +0 -14
- package/dist/chunk-6ZSWSH4I.js.map +0 -1
- package/dist/chunk-AAG4R3QN.js.map +0 -1
- package/dist/chunk-CWWKTNKK.js.map +0 -1
- package/dist/chunk-ELCE5SXV.js.map +0 -1
- package/dist/chunk-GISEVGS6.js.map +0 -1
- package/dist/chunk-HNFCAQ5R.js.map +0 -1
- package/dist/chunk-LFXYGZWK.js.map +0 -1
- package/dist/chunk-Q5QBEZIQ.js.map +0 -1
- package/dist/chunk-RDKBXMWX.js.map +0 -1
- package/dist/chunk-TKB4SB44.js.map +0 -1
- package/dist/chunk-UGLEYEMP.js +0 -33
- package/dist/chunk-UGLEYEMP.js.map +0 -1
- package/dist/chunk-UTRTG6LH.js.map +0 -1
- package/dist/chunk-XOAMRDDK.js.map +0 -1
- package/dist/chunk-YN7I3STW.js.map +0 -1
- package/dist/chunk-YQEAA6LT.js.map +0 -1
- package/dist/lib/error-message.d.ts +0 -7
- package/src/lib/error-message.ts +0 -37
- /package/dist/{browser-viewer-63TXMINB.js.map → browser-viewer-5X47I6T2.js.map} +0 -0
- /package/dist/{chunk-DOJWJQ3F.js.map → chunk-5EXGU7UF.js.map} +0 -0
- /package/dist/{chunk-I7IWRQLN.js.map → chunk-BI57CQUV.js.map} +0 -0
- /package/dist/{chunk-2QUSQVF2.js.map → chunk-UINDK6C3.js.map} +0 -0
- /package/dist/{chunk-IQUCRAT7.js.map → chunk-XA6R7DJQ.js.map} +0 -0
- /package/dist/{computer-viewer-X2MBRZY4.js.map → computer-viewer-7GF35SNY.js.map} +0 -0
- /package/dist/{latest-question-CNEZ5KAN.js.map → latest-question-3BJGKQQ6.js.map} +0 -0
- /package/dist/{model-policy-picker-menu-JSA64TNN.js.map → model-policy-picker-menu-PUTA5JOB.js.map} +0 -0
- /package/dist/{sandbox-files-BBC47SLD.js.map → sandbox-files-AEJTWGT4.js.map} +0 -0
|
@@ -2,7 +2,6 @@ import { ChevronRightIcon, Loader2Icon, SquareIcon } from "lucide-react";
|
|
|
2
2
|
import { useState } from "react";
|
|
3
3
|
import type { UseSessionBackgroundCommandsResult } from "../hooks/use-session-background-commands";
|
|
4
4
|
import { formatClockTime } from "../lib/format";
|
|
5
|
-
import { useErrorMessage } from "../lib/error-message";
|
|
6
5
|
|
|
7
6
|
/** The current session's live commands. Settled commands belong to the timeline. */
|
|
8
7
|
export function SessionCommandsPanel({
|
|
@@ -12,7 +11,6 @@ export function SessionCommandsPanel({
|
|
|
12
11
|
commands: UseSessionBackgroundCommandsResult;
|
|
13
12
|
readOnly?: boolean;
|
|
14
13
|
}) {
|
|
15
|
-
const formatError = useErrorMessage();
|
|
16
14
|
const [pending, setPending] = useState<string | null>(null);
|
|
17
15
|
const [error, setError] = useState<string | null>(null);
|
|
18
16
|
const active = state.commands.filter(
|
|
@@ -24,7 +22,7 @@ export function SessionCommandsPanel({
|
|
|
24
22
|
try {
|
|
25
23
|
await state.cancel(id);
|
|
26
24
|
} catch (cause) {
|
|
27
|
-
setError(
|
|
25
|
+
setError(cause instanceof Error ? cause.message : "Stop was not confirmed. Try again.");
|
|
28
26
|
} finally {
|
|
29
27
|
setPending(null);
|
|
30
28
|
}
|
|
@@ -37,7 +37,6 @@ import {
|
|
|
37
37
|
import type { UserMessageDisclosureLabels } from "./user-message-body";
|
|
38
38
|
import { conversationTimeline } from "../conversation-timeline";
|
|
39
39
|
import { cn } from "../lib/cn";
|
|
40
|
-
import { useErrorMessage } from "../lib/error-message";
|
|
41
40
|
|
|
42
41
|
export type SessionConversationProps = ClientOverride & {
|
|
43
42
|
sessionId: string;
|
|
@@ -121,7 +120,6 @@ function Conversation({
|
|
|
121
120
|
}: SessionConversationProps) {
|
|
122
121
|
const scope = { client, workspaceId };
|
|
123
122
|
const context = useOpenGeni(scope);
|
|
124
|
-
const formatError = useErrorMessage();
|
|
125
123
|
const config = useClientConfigFlags(context.client);
|
|
126
124
|
const showModelPicker = modelPicker ?? config.modelSelection;
|
|
127
125
|
const catalog = useWorkspaceModelCatalog({
|
|
@@ -205,7 +203,7 @@ function Conversation({
|
|
|
205
203
|
style={{ height }}
|
|
206
204
|
data-og-conversation=""
|
|
207
205
|
>
|
|
208
|
-
{error && <p role="alert">{
|
|
206
|
+
{error && <p role="alert">{error.message}</p>}
|
|
209
207
|
<MessageTimeline
|
|
210
208
|
renderMessageText={renderMessageText}
|
|
211
209
|
resolveLink={links}
|
|
@@ -267,7 +265,7 @@ function Conversation({
|
|
|
267
265
|
await human.respond(id, response);
|
|
268
266
|
}}
|
|
269
267
|
respondingRequestId={human.respondingRequestId}
|
|
270
|
-
error={human.mutationError
|
|
268
|
+
error={human.mutationError?.message}
|
|
271
269
|
autoFocus={false}
|
|
272
270
|
/>
|
|
273
271
|
{terminal ? (
|
|
@@ -4,7 +4,6 @@ import { useEffect, useRef, useState, type FormEvent } from "react";
|
|
|
4
4
|
import { useWorkspaceSessions } from "../hooks/use-workspace-sessions";
|
|
5
5
|
import { cn } from "../lib/cn";
|
|
6
6
|
import { formatRelativeTime } from "../lib/format";
|
|
7
|
-
import { useErrorMessage } from "../lib/error-message";
|
|
8
7
|
import { useOpenGeni, type ClientOverride } from "../session-context";
|
|
9
8
|
|
|
10
9
|
export type SessionListLabels = {
|
|
@@ -91,8 +90,7 @@ export function SessionList({
|
|
|
91
90
|
}, [refresh, refreshKey]);
|
|
92
91
|
const [editing, setEditing] = useState<{ id: string; title: string } | null>(null);
|
|
93
92
|
const [busy, setBusy] = useState<string | null>(null);
|
|
94
|
-
const [error, setError] = useState<
|
|
95
|
-
const formatError = useErrorMessage();
|
|
93
|
+
const [error, setError] = useState<string | null>(null);
|
|
96
94
|
const archiveClient = context.client as unknown as ArchiveClient;
|
|
97
95
|
const canArchive = archive && typeof archiveClient.updateSessionArchive === "function";
|
|
98
96
|
|
|
@@ -103,7 +101,7 @@ export function SessionList({
|
|
|
103
101
|
await action();
|
|
104
102
|
await list.refresh();
|
|
105
103
|
} catch (cause) {
|
|
106
|
-
setError(
|
|
104
|
+
setError(cause instanceof Error ? cause.message : String(cause));
|
|
107
105
|
} finally {
|
|
108
106
|
setBusy(null);
|
|
109
107
|
}
|
|
@@ -150,16 +148,16 @@ export function SessionList({
|
|
|
150
148
|
</button>
|
|
151
149
|
) : null}
|
|
152
150
|
</div>
|
|
153
|
-
{error
|
|
151
|
+
{error ? (
|
|
154
152
|
<p role="alert" className="px-2 text-og-xs text-og-status-failed">
|
|
155
|
-
{
|
|
153
|
+
{error}
|
|
156
154
|
</p>
|
|
157
155
|
) : null}
|
|
158
156
|
<ul className="flex min-h-0 flex-1 flex-col gap-0.5 overflow-y-auto px-1 pb-2">
|
|
159
157
|
{list.loading && sessions.length === 0 ? (
|
|
160
158
|
<li className="px-2 py-2 text-og-xs text-og-fg-subtle">{labels.loading}</li>
|
|
161
159
|
) : null}
|
|
162
|
-
{!list.loading &&
|
|
160
|
+
{!list.loading && sessions.length === 0 ? (
|
|
163
161
|
<li className="px-2 py-2 text-og-xs text-og-fg-subtle">{labels.empty}</li>
|
|
164
162
|
) : null}
|
|
165
163
|
{sessions.map((session) => {
|
|
@@ -127,17 +127,7 @@ function ScopedCard({
|
|
|
127
127
|
]);
|
|
128
128
|
if (!invocation || invocation.signal.aborted || lifetime.current !== invocation || !active())
|
|
129
129
|
return null;
|
|
130
|
-
|
|
131
|
-
// Custom catalog entries may not declare authentication. Discover it from
|
|
132
|
-
// the live catalog's endpoint, never from recommendation/host copy.
|
|
133
|
-
if (resolved?.kind === "mcp" && !resolved.enabled && !resolved.authKind) {
|
|
134
|
-
const endpoint = resolved.mcpUrl ?? resolved.endpointUrl;
|
|
135
|
-
if (endpoint) {
|
|
136
|
-
const inspected = await client.inspectMcpAuthentication(workspaceId, endpoint);
|
|
137
|
-
if (invocation.signal.aborted || lifetime.current !== invocation || !active()) return null;
|
|
138
|
-
if (inspected.kind === "oauth2") resolved = { ...resolved, authKind: "oauth2" };
|
|
139
|
-
}
|
|
140
|
-
}
|
|
130
|
+
const resolved = catalog.items.find((entry) => entry.id === capabilityId);
|
|
141
131
|
if (!resolved || resolved.kind !== "mcp" || resolved.authKind !== "oauth2")
|
|
142
132
|
throw new Error(
|
|
143
133
|
"This recommendation is not an available OAuth MCP integration. Review the current connection catalog.",
|
package/src/hooks/internal.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import type { SessionEvent } from "@opengeni/sdk";
|
|
2
2
|
import { useCallback, useEffect, useLayoutEffect, useRef, useState } from "react";
|
|
3
3
|
import type { EmbeddedSessionEventClientLike } from "../client";
|
|
4
|
-
import { normalizeError } from "../lib/error-message";
|
|
5
4
|
|
|
6
5
|
const HIDDEN_STREAM_GRACE_MS = 2_000;
|
|
7
6
|
|
|
@@ -117,7 +116,7 @@ export function usePolledValue<T>(
|
|
|
117
116
|
activeLoadRef.current === load &&
|
|
118
117
|
!requestAbort.signal.aborted
|
|
119
118
|
) {
|
|
120
|
-
setError(
|
|
119
|
+
setError(cause instanceof Error ? cause : new Error(String(cause)));
|
|
121
120
|
setLoading(false);
|
|
122
121
|
}
|
|
123
122
|
} finally {
|
|
@@ -259,7 +258,7 @@ export function useMutationRunner(identity: unknown = undefined): MutationState
|
|
|
259
258
|
generation.current === ownedGeneration &&
|
|
260
259
|
Object.is(identityRef.current, ownedIdentity)
|
|
261
260
|
) {
|
|
262
|
-
setMutationError(
|
|
261
|
+
setMutationError(cause instanceof Error ? cause : new Error(String(cause)));
|
|
263
262
|
}
|
|
264
263
|
return null;
|
|
265
264
|
} finally {
|
|
@@ -30,10 +30,6 @@ export type BrowserFrameInputFence = Pick<
|
|
|
30
30
|
| "frameId"
|
|
31
31
|
>;
|
|
32
32
|
|
|
33
|
-
export type BrowserInputFailure = Pick<BrowserActionReceipt, "operationId" | "error"> & {
|
|
34
|
-
state: Exclude<BrowserActionReceipt["state"], "completed">;
|
|
35
|
-
};
|
|
36
|
-
|
|
37
33
|
export type UseBrowserSessionOptions = EmbeddedBrowserInteractionClientOverride & {
|
|
38
34
|
browserSessionId: string | null;
|
|
39
35
|
enabled?: boolean | undefined;
|
|
@@ -51,7 +47,6 @@ export type UseBrowserSessionResult = {
|
|
|
51
47
|
loading: boolean;
|
|
52
48
|
mutating: boolean;
|
|
53
49
|
error: Error | null;
|
|
54
|
-
inputFailure: BrowserInputFailure | null;
|
|
55
50
|
refresh: () => Promise<void>;
|
|
56
51
|
observeForInput: () => Promise<BrowserObservation>;
|
|
57
52
|
actFromObservation: (
|
|
@@ -101,13 +96,10 @@ export function useBrowserSession(options: UseBrowserSessionOptions): UseBrowser
|
|
|
101
96
|
loading: boolean;
|
|
102
97
|
mutating: boolean;
|
|
103
98
|
error: Error | null;
|
|
104
|
-
inputFailure: BrowserInputFailure | null;
|
|
105
99
|
}>(() => emptyState(browserSessionId, enabled));
|
|
106
100
|
const visible =
|
|
107
101
|
state.browserSessionId === browserSessionId ? state : emptyState(browserSessionId, enabled);
|
|
108
102
|
const refreshBlocked = isNonRetryableInteractionError(visible.error);
|
|
109
|
-
const inputFailureRef = useRef(visible.inputFailure);
|
|
110
|
-
inputFailureRef.current = visible.inputFailure;
|
|
111
103
|
const selectedTargetIdRef = useRef<string | null>(visible.selectedTargetId);
|
|
112
104
|
selectedTargetIdRef.current = visible.selectedTargetId;
|
|
113
105
|
const selectedTargetRef = useRef<BrowserTarget | null>(
|
|
@@ -147,104 +139,88 @@ export function useBrowserSession(options: UseBrowserSessionOptions): UseBrowser
|
|
|
147
139
|
requestRef.current = { id, controller: null };
|
|
148
140
|
}, []);
|
|
149
141
|
|
|
150
|
-
const
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
142
|
+
const refresh = useCallback(async (): Promise<void> => {
|
|
143
|
+
if (!enabled || !browserSessionId) return;
|
|
144
|
+
const id = requestRef.current.id + 1;
|
|
145
|
+
requestRef.current.controller?.abort();
|
|
146
|
+
const controller = new AbortController();
|
|
147
|
+
requestRef.current = { id, controller };
|
|
148
|
+
try {
|
|
149
|
+
const [session, targetResponse] = await Promise.all([
|
|
150
|
+
client.getBrowserSession(workspaceId, browserSessionId, {
|
|
151
|
+
signal: controller.signal,
|
|
152
|
+
}),
|
|
153
|
+
client.listBrowserTargets(workspaceId, browserSessionId, {
|
|
154
|
+
signal: controller.signal,
|
|
155
|
+
}),
|
|
156
|
+
]);
|
|
157
|
+
if (!mountedRef.current || requestRef.current.id !== id) return;
|
|
158
|
+
const selected = chooseTarget(targetResponse.targets, selectedTargetIdRef.current);
|
|
159
|
+
selectedTargetIdRef.current = selected?.id ?? null;
|
|
160
|
+
const previousObservation =
|
|
161
|
+
observationRef.current.browserSessionId === browserSessionId
|
|
162
|
+
? observationRef.current.observation
|
|
163
|
+
: null;
|
|
164
|
+
const retainedObservation =
|
|
165
|
+
selected && previousObservation && sameObservationTarget(previousObservation, selected)
|
|
166
|
+
? previousObservation
|
|
167
|
+
: null;
|
|
168
|
+
observationRef.current = { browserSessionId, observation: retainedObservation };
|
|
169
|
+
setState((current) =>
|
|
170
|
+
current.browserSessionId === browserSessionId
|
|
171
|
+
? {
|
|
172
|
+
...current,
|
|
173
|
+
session,
|
|
174
|
+
targets: targetResponse.targets,
|
|
175
|
+
selectedTargetId: selected?.id ?? null,
|
|
176
|
+
observation: retainedObservation,
|
|
177
|
+
loading: false,
|
|
178
|
+
error: null,
|
|
179
|
+
}
|
|
180
|
+
: current,
|
|
181
|
+
);
|
|
182
|
+
if (!selected || !semanticObservationEnabledRef.current) return;
|
|
158
183
|
try {
|
|
159
|
-
const
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
}),
|
|
166
|
-
]);
|
|
184
|
+
const observation = await client.observeBrowserTarget(
|
|
185
|
+
workspaceId,
|
|
186
|
+
browserSessionId,
|
|
187
|
+
selected.id,
|
|
188
|
+
{ signal: controller.signal },
|
|
189
|
+
);
|
|
167
190
|
if (!mountedRef.current || requestRef.current.id !== id) return;
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
const previousObservation =
|
|
171
|
-
observationRef.current.browserSessionId === browserSessionId
|
|
172
|
-
? observationRef.current.observation
|
|
173
|
-
: null;
|
|
174
|
-
const retainedObservation =
|
|
175
|
-
selected && previousObservation && sameObservationTarget(previousObservation, selected)
|
|
176
|
-
? previousObservation
|
|
177
|
-
: null;
|
|
178
|
-
observationRef.current = { browserSessionId, observation: retainedObservation };
|
|
191
|
+
observationRef.current = { browserSessionId, observation };
|
|
192
|
+
lastObservationAtRef.current = Date.now();
|
|
179
193
|
setState((current) =>
|
|
180
|
-
current.browserSessionId === browserSessionId
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
session,
|
|
184
|
-
targets: targetResponse.targets,
|
|
185
|
-
selectedTargetId: selected?.id ?? null,
|
|
186
|
-
observation: retainedObservation,
|
|
187
|
-
loading: false,
|
|
188
|
-
error: null,
|
|
189
|
-
// Media and background polling cannot confirm an input outcome.
|
|
190
|
-
// Only an explicit fresh check clears the notice it began with.
|
|
191
|
-
inputFailure:
|
|
192
|
-
checkInput &&
|
|
193
|
-
session.id === browserSessionId &&
|
|
194
|
-
targetResponse.browserSessionId === browserSessionId &&
|
|
195
|
-
current.inputFailure === checkedInputFailure
|
|
196
|
-
? null
|
|
197
|
-
: current.inputFailure,
|
|
198
|
-
}
|
|
194
|
+
current.browserSessionId === browserSessionId &&
|
|
195
|
+
current.selectedTargetId === observation.target.id
|
|
196
|
+
? { ...current, observation }
|
|
199
197
|
: current,
|
|
200
198
|
);
|
|
201
|
-
if (!selected || !semanticObservationEnabledRef.current) return;
|
|
202
|
-
try {
|
|
203
|
-
const observation = await client.observeBrowserTarget(
|
|
204
|
-
workspaceId,
|
|
205
|
-
browserSessionId,
|
|
206
|
-
selected.id,
|
|
207
|
-
{ signal: controller.signal },
|
|
208
|
-
);
|
|
209
|
-
if (!mountedRef.current || requestRef.current.id !== id) return;
|
|
210
|
-
observationRef.current = { browserSessionId, observation };
|
|
211
|
-
lastObservationAtRef.current = Date.now();
|
|
212
|
-
setState((current) =>
|
|
213
|
-
current.browserSessionId === browserSessionId &&
|
|
214
|
-
current.selectedTargetId === observation.target.id
|
|
215
|
-
? { ...current, observation }
|
|
216
|
-
: current,
|
|
217
|
-
);
|
|
218
|
-
} catch (cause) {
|
|
219
|
-
if (controller.signal.aborted || !mountedRef.current || requestRef.current.id !== id)
|
|
220
|
-
return;
|
|
221
|
-
// Target inventory and the media plane remain authoritative for the
|
|
222
|
-
// human-facing browser. A failed semantic snapshot must not blank or
|
|
223
|
-
// disable an otherwise live visual browser.
|
|
224
|
-
void cause;
|
|
225
|
-
}
|
|
226
199
|
} catch (cause) {
|
|
227
200
|
if (controller.signal.aborted || !mountedRef.current || requestRef.current.id !== id)
|
|
228
201
|
return;
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
loading: false,
|
|
234
|
-
error: cause instanceof Error ? cause : new Error(String(cause)),
|
|
235
|
-
}
|
|
236
|
-
: current,
|
|
237
|
-
);
|
|
238
|
-
} finally {
|
|
239
|
-
if (requestRef.current.id === id) {
|
|
240
|
-
requestRef.current = { id, controller: null };
|
|
241
|
-
}
|
|
202
|
+
// Target inventory and the media plane remain authoritative for the
|
|
203
|
+
// human-facing browser. A failed semantic snapshot must not blank or
|
|
204
|
+
// disable an otherwise live visual browser.
|
|
205
|
+
void cause;
|
|
242
206
|
}
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
207
|
+
} catch (cause) {
|
|
208
|
+
if (controller.signal.aborted || !mountedRef.current || requestRef.current.id !== id) return;
|
|
209
|
+
setState((current) =>
|
|
210
|
+
current.browserSessionId === browserSessionId
|
|
211
|
+
? {
|
|
212
|
+
...current,
|
|
213
|
+
loading: false,
|
|
214
|
+
error: cause instanceof Error ? cause : new Error(String(cause)),
|
|
215
|
+
}
|
|
216
|
+
: current,
|
|
217
|
+
);
|
|
218
|
+
} finally {
|
|
219
|
+
if (requestRef.current.id === id) {
|
|
220
|
+
requestRef.current = { id, controller: null };
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}, [browserSessionId, client, enabled, workspaceId]);
|
|
248
224
|
|
|
249
225
|
const refreshSemantic = useCallback(async (): Promise<void> => {
|
|
250
226
|
if (!enabled || !browserSessionId) return;
|
|
@@ -289,12 +265,12 @@ export function useBrowserSession(options: UseBrowserSessionOptions): UseBrowser
|
|
|
289
265
|
? { ...current, loading: true }
|
|
290
266
|
: emptyState(browserSessionId, true),
|
|
291
267
|
);
|
|
292
|
-
void
|
|
268
|
+
void refresh();
|
|
293
269
|
return () => {
|
|
294
270
|
mountedRef.current = false;
|
|
295
271
|
invalidateRefresh();
|
|
296
272
|
};
|
|
297
|
-
}, [browserSessionId, enabled, invalidateRefresh,
|
|
273
|
+
}, [browserSessionId, enabled, invalidateRefresh, refresh]);
|
|
298
274
|
|
|
299
275
|
useEffect(() => {
|
|
300
276
|
const previous = previousSemanticObservationRef.current;
|
|
@@ -330,17 +306,17 @@ export function useBrowserSession(options: UseBrowserSessionOptions): UseBrowser
|
|
|
330
306
|
useEffect(() => {
|
|
331
307
|
if (!enabled || !pageLive || visible.mutating || refreshBlocked) return;
|
|
332
308
|
const timer = setInterval(() => {
|
|
333
|
-
if (!requestRef.current.controller) void
|
|
309
|
+
if (!requestRef.current.controller) void refresh();
|
|
334
310
|
}, pollIntervalMs);
|
|
335
311
|
return () => clearInterval(timer);
|
|
336
|
-
}, [enabled, pageLive, pollIntervalMs,
|
|
312
|
+
}, [enabled, pageLive, pollIntervalMs, refresh, refreshBlocked, visible.mutating]);
|
|
337
313
|
|
|
338
314
|
useEffect(() => {
|
|
339
315
|
if (!enabled || !browserSessionId || !pageLive || refreshBlocked) return;
|
|
340
316
|
let disposed = false;
|
|
341
317
|
const heartbeat = () => {
|
|
342
318
|
void client.heartbeatBrowserSession(workspaceId, browserSessionId).catch(() => {
|
|
343
|
-
if (!disposed) void
|
|
319
|
+
if (!disposed) void refresh();
|
|
344
320
|
});
|
|
345
321
|
};
|
|
346
322
|
const timer = setInterval(heartbeat, 30_000);
|
|
@@ -348,7 +324,7 @@ export function useBrowserSession(options: UseBrowserSessionOptions): UseBrowser
|
|
|
348
324
|
disposed = true;
|
|
349
325
|
clearInterval(timer);
|
|
350
326
|
};
|
|
351
|
-
}, [browserSessionId, client, enabled, pageLive,
|
|
327
|
+
}, [browserSessionId, client, enabled, pageLive, refresh, refreshBlocked, workspaceId]);
|
|
352
328
|
|
|
353
329
|
const runMutation = useCallback(
|
|
354
330
|
async <T>(scopeBrowserSessionId: string, operation: () => Promise<T>): Promise<T> => {
|
|
@@ -556,16 +532,6 @@ export function useBrowserSession(options: UseBrowserSessionOptions): UseBrowser
|
|
|
556
532
|
observationMode,
|
|
557
533
|
action,
|
|
558
534
|
});
|
|
559
|
-
if (receipt.state !== "completed") {
|
|
560
|
-
const inputFailure: BrowserInputFailure = {
|
|
561
|
-
operationId: receipt.operationId,
|
|
562
|
-
state: receipt.state,
|
|
563
|
-
error: receipt.error,
|
|
564
|
-
};
|
|
565
|
-
setState((current) =>
|
|
566
|
-
current.browserSessionId === browserSessionId ? { ...current, inputFailure } : current,
|
|
567
|
-
);
|
|
568
|
-
}
|
|
569
535
|
if (receipt.observation) {
|
|
570
536
|
observationRef.current = {
|
|
571
537
|
browserSessionId,
|
|
@@ -677,7 +643,6 @@ export function useBrowserSession(options: UseBrowserSessionOptions): UseBrowser
|
|
|
677
643
|
loading: visible.loading,
|
|
678
644
|
mutating: visible.mutating,
|
|
679
645
|
error: visible.error,
|
|
680
|
-
inputFailure: visible.inputFailure,
|
|
681
646
|
refresh,
|
|
682
647
|
observeForInput,
|
|
683
648
|
actFromObservation,
|
|
@@ -701,7 +666,6 @@ function emptyState(browserSessionId: string | null, loading: boolean) {
|
|
|
701
666
|
loading,
|
|
702
667
|
mutating: false,
|
|
703
668
|
error: null as Error | null,
|
|
704
|
-
inputFailure: null as BrowserInputFailure | null,
|
|
705
669
|
};
|
|
706
670
|
}
|
|
707
671
|
|