@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
|
@@ -1,26 +1,21 @@
|
|
|
1
1
|
import { CheckIcon, ShieldCheckIcon, XIcon } from "lucide-react";
|
|
2
|
-
import {
|
|
2
|
+
import { useEffect, useId, useRef, useState, type ReactNode } from "react";
|
|
3
3
|
import type { PendingApproval } from "../approvals";
|
|
4
4
|
import { cn } from "../lib/cn";
|
|
5
|
-
import { useErrorMessage } from "../lib/error-message";
|
|
6
5
|
import { toolDisplayName } from "../timeline/tool-display-name";
|
|
7
6
|
|
|
8
7
|
export type ApprovalSurfaceMessages = {
|
|
9
8
|
title: string;
|
|
10
|
-
/** Screen-reader context for the decision; not shown visually. */
|
|
11
9
|
description: string;
|
|
12
10
|
approve: string;
|
|
13
11
|
reject: string;
|
|
14
12
|
approving: string;
|
|
15
13
|
rejecting: string;
|
|
16
14
|
formatToolName: (name: string) => string;
|
|
17
|
-
/** Toggle for the exact arguments when they are shown as readable fields. */
|
|
18
|
-
showDetails?: string | undefined;
|
|
19
|
-
hideDetails?: string | undefined;
|
|
20
15
|
};
|
|
21
16
|
|
|
22
17
|
export const defaultApprovalSurfaceMessages: ApprovalSurfaceMessages = {
|
|
23
|
-
title: "Approval
|
|
18
|
+
title: "Approval required",
|
|
24
19
|
description: "Review the requested action before the agent continues.",
|
|
25
20
|
approve: "Approve",
|
|
26
21
|
reject: "Reject",
|
|
@@ -30,8 +25,6 @@ export const defaultApprovalSurfaceMessages: ApprovalSurfaceMessages = {
|
|
|
30
25
|
name.includes("__") || /^[a-f0-9]{64}$/.test(name)
|
|
31
26
|
? toolDisplayName(name)
|
|
32
27
|
: name.replaceAll("_", " ").replaceAll(".", " › "),
|
|
33
|
-
showDetails: "Show exact arguments",
|
|
34
|
-
hideDetails: "Hide exact arguments",
|
|
35
28
|
};
|
|
36
29
|
|
|
37
30
|
export type ApprovalSurfaceProps = {
|
|
@@ -46,8 +39,6 @@ export type ApprovalSurfaceProps = {
|
|
|
46
39
|
};
|
|
47
40
|
|
|
48
41
|
const APPROVAL_ARGUMENT_PREVIEW_CHARACTERS = 4_000;
|
|
49
|
-
const APPROVAL_FIELD_LIMIT = 12;
|
|
50
|
-
const APPROVAL_LIST_LIMIT = 10;
|
|
51
42
|
|
|
52
43
|
function approvalArgumentsPreview(value: unknown): string | null {
|
|
53
44
|
if (value === undefined) return null;
|
|
@@ -63,68 +54,6 @@ function approvalArgumentsPreview(value: unknown): string | null {
|
|
|
63
54
|
return `${characters.slice(0, APPROVAL_ARGUMENT_PREVIEW_CHARACTERS).join("")}\n… ${characters.length - APPROVAL_ARGUMENT_PREVIEW_CHARACTERS} characters omitted`;
|
|
64
55
|
}
|
|
65
56
|
|
|
66
|
-
export type ApprovalField = { key: string; label: string; value: string };
|
|
67
|
-
|
|
68
|
-
const ACRONYMS = new Set(["id", "ids", "url", "uri", "api", "ip", "sku", "utc", "iban", "vat"]);
|
|
69
|
-
|
|
70
|
-
/** "paymentId" / "payment_id" → "Payment ID". */
|
|
71
|
-
function fieldLabel(key: string): string {
|
|
72
|
-
const words = key
|
|
73
|
-
.replace(/([a-z0-9])([A-Z])/g, "$1 $2")
|
|
74
|
-
.replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2")
|
|
75
|
-
.split(/[\s_.-]+/)
|
|
76
|
-
.filter(Boolean)
|
|
77
|
-
.map((word) => (ACRONYMS.has(word.toLowerCase()) ? word.toUpperCase() : word.toLowerCase()));
|
|
78
|
-
const label = words.join(" ");
|
|
79
|
-
return label ? label.charAt(0).toUpperCase() + label.slice(1) : key;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
function scalarText(value: unknown): string | null {
|
|
83
|
-
if (typeof value === "string") return value;
|
|
84
|
-
if (typeof value === "number" || typeof value === "bigint") return String(value);
|
|
85
|
-
if (typeof value === "boolean") return value ? "Yes" : "No";
|
|
86
|
-
if (value === null) return "None";
|
|
87
|
-
return null;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Readable fields for a flat argument object (scalars and short scalar lists).
|
|
92
|
-
* Returns null when the arguments are nested, large, or not an object, so the
|
|
93
|
-
* caller shows the exact JSON instead of a lossy summary.
|
|
94
|
-
*/
|
|
95
|
-
export function approvalArgumentFields(value: unknown): ApprovalField[] | null {
|
|
96
|
-
let record = value;
|
|
97
|
-
if (typeof record === "string") {
|
|
98
|
-
try {
|
|
99
|
-
record = JSON.parse(record) as unknown;
|
|
100
|
-
} catch {
|
|
101
|
-
return null;
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
if (!record || typeof record !== "object" || Array.isArray(record)) return null;
|
|
105
|
-
const entries = Object.entries(record as Record<string, unknown>);
|
|
106
|
-
if (entries.length === 0 || entries.length > APPROVAL_FIELD_LIMIT) return null;
|
|
107
|
-
const fields: ApprovalField[] = [];
|
|
108
|
-
for (const [key, entry] of entries) {
|
|
109
|
-
let text = scalarText(entry);
|
|
110
|
-
if (text === null && Array.isArray(entry) && entry.length <= APPROVAL_LIST_LIMIT) {
|
|
111
|
-
const items = entry.map(scalarText);
|
|
112
|
-
if (items.every((item): item is string => item !== null)) text = items.join(", ");
|
|
113
|
-
}
|
|
114
|
-
if (text === null || text.length > APPROVAL_ARGUMENT_PREVIEW_CHARACTERS) return null;
|
|
115
|
-
fields.push({ key, label: fieldLabel(key), value: text });
|
|
116
|
-
}
|
|
117
|
-
return fields;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
function isEmptyArguments(value: unknown): boolean {
|
|
121
|
-
return (
|
|
122
|
-
value === undefined ||
|
|
123
|
-
value === null ||
|
|
124
|
-
(typeof value === "object" && !Array.isArray(value) && Object.keys(value).length === 0)
|
|
125
|
-
);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
57
|
function approvalOwnershipKey(approval: PendingApproval): string {
|
|
129
58
|
return `${approval.id}\u0000${approval.name}\u0000${approvalArgumentsPreview(approval.arguments) ?? ""}`;
|
|
130
59
|
}
|
|
@@ -145,8 +74,6 @@ export function ApprovalSurface({
|
|
|
145
74
|
className,
|
|
146
75
|
}: ApprovalSurfaceProps) {
|
|
147
76
|
const titleId = useId();
|
|
148
|
-
const formatError = useErrorMessage();
|
|
149
|
-
const descriptionId = useId();
|
|
150
77
|
const messages = { ...defaultApprovalSurfaceMessages, ...overrides };
|
|
151
78
|
const [pending, setPending] = useState<{
|
|
152
79
|
approvalKey: string;
|
|
@@ -154,7 +81,7 @@ export function ApprovalSurface({
|
|
|
154
81
|
token: symbol;
|
|
155
82
|
} | null>(null);
|
|
156
83
|
const pendingRef = useRef<{ approvalKey: string; token: symbol } | null>(null);
|
|
157
|
-
const [decisionError, setDecisionError] = useState<
|
|
84
|
+
const [decisionError, setDecisionError] = useState<Error | null>(null);
|
|
158
85
|
|
|
159
86
|
useEffect(() => {
|
|
160
87
|
if (
|
|
@@ -189,58 +116,69 @@ export function ApprovalSurface({
|
|
|
189
116
|
if (pendingRef.current?.token === token) {
|
|
190
117
|
pendingRef.current = null;
|
|
191
118
|
setPending((current) => (current?.token === token ? null : current));
|
|
192
|
-
setDecisionError(
|
|
119
|
+
setDecisionError(cause instanceof Error ? cause : new Error(String(cause)));
|
|
193
120
|
}
|
|
194
121
|
}
|
|
195
122
|
};
|
|
196
|
-
const errorMessage = decisionError
|
|
197
|
-
? formatError(decisionError.cause)
|
|
198
|
-
: error instanceof Error
|
|
199
|
-
? formatError(error)
|
|
200
|
-
: error;
|
|
123
|
+
const errorMessage = decisionError?.message ?? (error instanceof Error ? error.message : error);
|
|
201
124
|
|
|
202
125
|
return (
|
|
203
126
|
<section
|
|
204
127
|
className={cn(
|
|
205
|
-
"og-root
|
|
128
|
+
"og-root flex w-full flex-col gap-3 rounded-og-lg border border-og-status-waiting/35 bg-og-status-waiting/5 p-4 shadow-og-sm",
|
|
206
129
|
className,
|
|
207
130
|
)}
|
|
208
131
|
aria-labelledby={titleId}
|
|
209
|
-
aria-describedby={descriptionId}
|
|
210
|
-
data-og-approval-surface=""
|
|
211
132
|
>
|
|
212
|
-
<
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
133
|
+
<header className="flex items-start gap-3">
|
|
134
|
+
<span className="mt-0.5 inline-flex size-8 shrink-0 items-center justify-center rounded-og-md bg-og-status-waiting/12 text-og-status-waiting">
|
|
135
|
+
<ShieldCheckIcon aria-hidden="true" className="size-4" />
|
|
136
|
+
</span>
|
|
137
|
+
<div className="min-w-0">
|
|
138
|
+
<h2 id={titleId} className="text-og-md font-semibold text-og-fg">
|
|
139
|
+
{messages.title}
|
|
140
|
+
</h2>
|
|
141
|
+
<p className="mt-0.5 text-og-sm text-og-fg-muted">{messages.description}</p>
|
|
142
|
+
</div>
|
|
143
|
+
</header>
|
|
144
|
+
|
|
145
|
+
<div className="flex flex-col gap-2">
|
|
219
146
|
{approvals.map((approval) => {
|
|
220
147
|
const approvalKey = approvalOwnershipKey(approval);
|
|
221
148
|
const active = pending?.approvalKey === approvalKey ? pending.decision : null;
|
|
149
|
+
const argumentsPreview = approvalArgumentsPreview(approval.arguments);
|
|
222
150
|
return (
|
|
223
151
|
<article
|
|
224
152
|
key={approval.id}
|
|
225
153
|
data-approval-id={approval.id}
|
|
226
|
-
className="
|
|
154
|
+
className="rounded-og-md border border-og-border bg-og-surface-1 p-3"
|
|
227
155
|
>
|
|
228
156
|
{renderApproval ? (
|
|
229
|
-
|
|
230
|
-
<p className="mb-1 text-og-xs font-medium text-og-status-waiting" aria-hidden>
|
|
231
|
-
{messages.title}
|
|
232
|
-
</p>
|
|
233
|
-
{renderApproval(approval)}
|
|
234
|
-
</div>
|
|
157
|
+
renderApproval(approval)
|
|
235
158
|
) : (
|
|
236
|
-
|
|
159
|
+
<>
|
|
160
|
+
<p className="text-og-sm font-medium text-og-fg">
|
|
161
|
+
{approval.display?.title ??
|
|
162
|
+
messages.formatToolName(approval.display?.toolName ?? approval.name)}
|
|
163
|
+
</p>
|
|
164
|
+
{approval.display?.accountLabel ? (
|
|
165
|
+
<p className="mt-1 break-words text-og-xs text-og-fg-muted">
|
|
166
|
+
{approval.display.accountLabel}
|
|
167
|
+
</p>
|
|
168
|
+
) : null}
|
|
169
|
+
{argumentsPreview !== null ? (
|
|
170
|
+
<pre className="mt-2 max-h-48 overflow-auto whitespace-pre-wrap break-words rounded-og-sm bg-og-surface-2 p-2 font-mono text-og-xs text-og-fg-muted">
|
|
171
|
+
{argumentsPreview}
|
|
172
|
+
</pre>
|
|
173
|
+
) : null}
|
|
174
|
+
</>
|
|
237
175
|
)}
|
|
238
|
-
<div className="flex flex-wrap
|
|
176
|
+
<div className="mt-3 flex flex-wrap justify-end gap-2">
|
|
239
177
|
<button
|
|
240
178
|
type="button"
|
|
241
179
|
disabled={busy}
|
|
242
180
|
onClick={() => void decide(approval, "reject")}
|
|
243
|
-
className="inline-flex min-h-
|
|
181
|
+
className="inline-flex min-h-9 items-center gap-1.5 rounded-og-md border border-og-border px-3 py-1.5 text-og-sm font-medium text-og-fg-muted transition-colors hover:bg-og-surface-2 hover:text-og-fg disabled:opacity-50"
|
|
244
182
|
>
|
|
245
183
|
<XIcon aria-hidden="true" className="size-3.5" />
|
|
246
184
|
{active === "reject" ? messages.rejecting : messages.reject}
|
|
@@ -249,7 +187,7 @@ export function ApprovalSurface({
|
|
|
249
187
|
type="button"
|
|
250
188
|
disabled={busy}
|
|
251
189
|
onClick={() => void decide(approval, "approve")}
|
|
252
|
-
className="inline-flex min-h-
|
|
190
|
+
className="inline-flex min-h-9 items-center gap-1.5 rounded-og-md border border-og-primary-border bg-og-primary text-og-primary-fg px-3 py-1.5 text-og-sm font-medium transition-colors hover:bg-og-primary-hover disabled:opacity-50"
|
|
253
191
|
>
|
|
254
192
|
<CheckIcon aria-hidden="true" className="size-3.5" />
|
|
255
193
|
{active === "approve" ? messages.approving : messages.approve}
|
|
@@ -261,72 +199,10 @@ export function ApprovalSurface({
|
|
|
261
199
|
</div>
|
|
262
200
|
|
|
263
201
|
{errorMessage ? (
|
|
264
|
-
<p role="alert" className="
|
|
202
|
+
<p role="alert" className="text-og-sm text-og-status-failed">
|
|
265
203
|
{errorMessage}
|
|
266
204
|
</p>
|
|
267
205
|
) : null}
|
|
268
206
|
</section>
|
|
269
207
|
);
|
|
270
208
|
}
|
|
271
|
-
|
|
272
|
-
function ApprovalDetails({
|
|
273
|
-
approval,
|
|
274
|
-
messages,
|
|
275
|
-
}: {
|
|
276
|
-
approval: PendingApproval;
|
|
277
|
-
messages: ApprovalSurfaceMessages;
|
|
278
|
-
}) {
|
|
279
|
-
const [showExact, setShowExact] = useState(false);
|
|
280
|
-
const fields = approvalArgumentFields(approval.arguments);
|
|
281
|
-
const exact = isEmptyArguments(approval.arguments)
|
|
282
|
-
? null
|
|
283
|
-
: approvalArgumentsPreview(approval.arguments);
|
|
284
|
-
return (
|
|
285
|
-
<div className="flex min-w-0 items-start gap-3">
|
|
286
|
-
<span className="mt-0.5 inline-flex size-8 shrink-0 items-center justify-center rounded-og-md bg-og-status-waiting/12 text-og-status-waiting">
|
|
287
|
-
<ShieldCheckIcon aria-hidden="true" className="size-4" />
|
|
288
|
-
</span>
|
|
289
|
-
<div className="min-w-0 flex-1">
|
|
290
|
-
<p className="text-og-xs font-medium text-og-status-waiting">{messages.title}</p>
|
|
291
|
-
<p className="mt-0.5 break-words text-og-md font-semibold text-og-fg">
|
|
292
|
-
{approval.display?.title ??
|
|
293
|
-
messages.formatToolName(approval.display?.toolName ?? approval.name)}
|
|
294
|
-
</p>
|
|
295
|
-
{approval.display?.accountLabel ? (
|
|
296
|
-
<p className="mt-0.5 break-words text-og-xs text-og-fg-muted">
|
|
297
|
-
{approval.display.accountLabel}
|
|
298
|
-
</p>
|
|
299
|
-
) : null}
|
|
300
|
-
{fields ? (
|
|
301
|
-
<dl className="m-0 mt-3 grid grid-cols-[minmax(5.5rem,max-content)_minmax(0,1fr)] gap-x-5 gap-y-1.5 text-og-sm">
|
|
302
|
-
{fields.map((field) => (
|
|
303
|
-
<Fragment key={field.key}>
|
|
304
|
-
<dt className="m-0 text-og-fg-muted">{field.label}</dt>
|
|
305
|
-
<dd className="m-0 max-h-32 min-w-0 overflow-auto whitespace-pre-wrap break-words text-og-fg">
|
|
306
|
-
{field.value}
|
|
307
|
-
</dd>
|
|
308
|
-
</Fragment>
|
|
309
|
-
))}
|
|
310
|
-
</dl>
|
|
311
|
-
) : null}
|
|
312
|
-
{exact !== null && (!fields || showExact) ? (
|
|
313
|
-
<pre className="mt-3 max-h-48 overflow-auto whitespace-pre-wrap break-words rounded-og-sm bg-og-fg/5 p-2.5 font-mono text-og-xs text-og-fg-muted">
|
|
314
|
-
{exact}
|
|
315
|
-
</pre>
|
|
316
|
-
) : null}
|
|
317
|
-
{fields && exact !== null ? (
|
|
318
|
-
<button
|
|
319
|
-
type="button"
|
|
320
|
-
aria-expanded={showExact}
|
|
321
|
-
onClick={() => setShowExact((value) => !value)}
|
|
322
|
-
className="mt-2 text-og-xs text-og-fg-muted underline-offset-2 hover:text-og-fg hover:underline"
|
|
323
|
-
>
|
|
324
|
-
{showExact
|
|
325
|
-
? (messages.hideDetails ?? defaultApprovalSurfaceMessages.hideDetails)
|
|
326
|
-
: (messages.showDetails ?? defaultApprovalSurfaceMessages.showDetails)}
|
|
327
|
-
</button>
|
|
328
|
-
) : null}
|
|
329
|
-
</div>
|
|
330
|
-
</div>
|
|
331
|
-
);
|
|
332
|
-
}
|
|
@@ -20,6 +20,7 @@ import type {
|
|
|
20
20
|
SiteAuthConnection,
|
|
21
21
|
} from "@opengeni/sdk/interaction";
|
|
22
22
|
import { interactionControlFailureFromError } from "@opengeni/sdk/interaction";
|
|
23
|
+
import { OpenGeniApiError } from "@opengeni/sdk";
|
|
23
24
|
import {
|
|
24
25
|
BugIcon,
|
|
25
26
|
ArchiveIcon,
|
|
@@ -70,10 +71,7 @@ import { useSiteAuthConnections } from "../hooks/use-site-auth-connections";
|
|
|
70
71
|
import { cn } from "../lib/cn";
|
|
71
72
|
import { copyTextToClipboard } from "../lib/clipboard";
|
|
72
73
|
import { formatBytes } from "../lib/format";
|
|
73
|
-
import {
|
|
74
|
-
isInteractionControlUnavailable,
|
|
75
|
-
isSourcePlacementChangedError,
|
|
76
|
-
} from "../lib/interaction-errors";
|
|
74
|
+
import { isSourcePlacementChangedError } from "../lib/interaction-errors";
|
|
77
75
|
import type { EmbeddedBrowserInteractionClientOverride } from "../session-context";
|
|
78
76
|
import { browserKey, HUMAN_BROWSER_HOME_URL, normalizeBrowserAddress } from "./browser-input";
|
|
79
77
|
import { InteractionInterventionBanner } from "./interaction-intervention-banner";
|
|
@@ -436,7 +434,7 @@ export function BrowserViewer({
|
|
|
436
434
|
}, [frameIsLive]);
|
|
437
435
|
const supportsLiveFrames =
|
|
438
436
|
(browser.session ?? selectedRegistrySession)?.capabilities.liveFrames === true;
|
|
439
|
-
const controlUnavailable =
|
|
437
|
+
const controlUnavailable = isBrowserControlUnavailable(browser.error);
|
|
440
438
|
const connectionError = controlUnavailable ? browser.error : (frames.error ?? browser.error);
|
|
441
439
|
// A managed controller can reject a stale attachment while the same browser
|
|
442
440
|
// remains healthy. Only extension-attached Chrome requires a new browser on
|
|
@@ -921,32 +919,6 @@ export function BrowserViewer({
|
|
|
921
919
|
.catch((cause) => notifyError(cause, "Could not reload."));
|
|
922
920
|
}}
|
|
923
921
|
/>
|
|
924
|
-
{browser.inputFailure ? (
|
|
925
|
-
<div
|
|
926
|
-
role="alert"
|
|
927
|
-
aria-label="Browser input status"
|
|
928
|
-
className="flex shrink-0 items-center gap-2 border-t border-og-border bg-og-surface-1 px-3 py-2 text-og-xs"
|
|
929
|
-
>
|
|
930
|
-
<CircleAlertIcon className="size-4 shrink-0 text-og-status-failed" />
|
|
931
|
-
<div className="min-w-0 flex-1">
|
|
932
|
-
<span className="font-medium">
|
|
933
|
-
{browser.inputFailure.state === "failed"
|
|
934
|
-
? "Browser input failed"
|
|
935
|
-
: "Input result unknown"}
|
|
936
|
-
</span>
|
|
937
|
-
<p className="text-og-fg-subtle">
|
|
938
|
-
{browser.inputFailure.error?.message ?? "Inspect the page before continuing."}
|
|
939
|
-
</p>
|
|
940
|
-
</div>
|
|
941
|
-
<button
|
|
942
|
-
type="button"
|
|
943
|
-
onClick={() => void browser.refresh()}
|
|
944
|
-
className="shrink-0 rounded border border-og-border px-2 py-1 transition hover:bg-og-surface-2"
|
|
945
|
-
>
|
|
946
|
-
Check browser
|
|
947
|
-
</button>
|
|
948
|
-
</div>
|
|
949
|
-
) : null}
|
|
950
922
|
<div className="relative flex min-h-0 min-w-0 flex-1 overflow-hidden">
|
|
951
923
|
<BrowserViewport
|
|
952
924
|
// A new control scope must discard every buffered gesture and queued action.
|
|
@@ -1050,7 +1022,6 @@ export function BrowserViewer({
|
|
|
1050
1022
|
profile={selectedProfile}
|
|
1051
1023
|
target={browser.selectedTarget}
|
|
1052
1024
|
observation={browser.observation}
|
|
1053
|
-
inputFailure={browser.inputFailure}
|
|
1054
1025
|
connectionState={displayConnectionState}
|
|
1055
1026
|
refreshing={registry.refreshing}
|
|
1056
1027
|
diagnosticsOpen={diagnosticsView !== null}
|
|
@@ -2669,7 +2640,6 @@ function BrowserStatusBar(props: {
|
|
|
2669
2640
|
profile: BrowserIdentity | null;
|
|
2670
2641
|
target: BrowserTarget | null;
|
|
2671
2642
|
observation: ReturnType<typeof useBrowserSession>["observation"];
|
|
2672
|
-
inputFailure: ReturnType<typeof useBrowserSession>["inputFailure"];
|
|
2673
2643
|
connectionState: string;
|
|
2674
2644
|
refreshing: boolean;
|
|
2675
2645
|
diagnosticsOpen: boolean;
|
|
@@ -2697,11 +2667,6 @@ function BrowserStatusBar(props: {
|
|
|
2697
2667
|
? "Semantic"
|
|
2698
2668
|
: browserConnectionLabel(props.connectionState)}
|
|
2699
2669
|
</span>
|
|
2700
|
-
{props.inputFailure ? (
|
|
2701
|
-
<span className="text-og-status-failed">
|
|
2702
|
-
{props.inputFailure.state === "failed" ? "Input failed" : "Input result unknown"}
|
|
2703
|
-
</span>
|
|
2704
|
-
) : null}
|
|
2705
2670
|
<span>{props.profile?.name ?? "Temporary browser"}</span>
|
|
2706
2671
|
<span className="min-w-0 flex-1 truncate">{props.target?.title}</span>
|
|
2707
2672
|
{props.refreshing ? <LoaderCircleIcon className="size-3 animate-spin" /> : null}
|
|
@@ -3315,6 +3280,12 @@ function attachedChromeGenerationLoss(
|
|
|
3315
3280
|
return lost?.placement.kind === "attached_device" ? { deviceId: lost.placement.deviceId } : null;
|
|
3316
3281
|
}
|
|
3317
3282
|
|
|
3283
|
+
function isBrowserControlUnavailable(error: Error | null): boolean {
|
|
3284
|
+
// Receiving pixels proves only the media channel. A failed control request
|
|
3285
|
+
// must not leave a frozen screenshot presented as an interactive live page.
|
|
3286
|
+
return error instanceof OpenGeniApiError && (error.status >= 500 || error.status === 0);
|
|
3287
|
+
}
|
|
3288
|
+
|
|
3318
3289
|
function isAttachedChromeGenerationLossError(error: Error | null): boolean {
|
|
3319
3290
|
if (!error) return false;
|
|
3320
3291
|
return /placement instance changed|controller authority changed/i.test(error.message);
|
|
@@ -36,7 +36,6 @@ import {
|
|
|
36
36
|
type RefObject,
|
|
37
37
|
} from "react";
|
|
38
38
|
import { argHint, defaultCommands } from "../commands/registry";
|
|
39
|
-
import { useErrorMessage } from "../lib/error-message";
|
|
40
39
|
import type { Notice, SlashCommand } from "../commands/types";
|
|
41
40
|
import type { ComposerState } from "../hooks/use-composer";
|
|
42
41
|
import { shouldSteerOnKey, shouldSubmitOnKey } from "../hooks/use-composer";
|
|
@@ -176,9 +175,7 @@ export const defaultChatComposerMessages: ChatComposerMessages = {
|
|
|
176
175
|
messagePlaceholder: "Message the agent…",
|
|
177
176
|
pausedPlaceholder: "Message the agent — it will wait in the queue…",
|
|
178
177
|
inputLabel: "Message the agent",
|
|
179
|
-
|
|
180
|
-
// the host supplies its own hint.
|
|
181
|
-
keyboardHint: "",
|
|
178
|
+
keyboardHint: "Enter to queue · Cmd/Ctrl+Enter to steer · Shift+Enter for a new line",
|
|
182
179
|
slashCommandBlocked:
|
|
183
180
|
"That's a slash command — press Enter in the command list to run it, or edit the line to send a message.",
|
|
184
181
|
controlChangedError:
|
|
@@ -377,7 +374,6 @@ export function useChatComposerController({
|
|
|
377
374
|
onPaste,
|
|
378
375
|
messages: messageOverrides,
|
|
379
376
|
}: UseChatComposerControllerOptions) {
|
|
380
|
-
const formatError = useErrorMessage();
|
|
381
377
|
const messages = useMemo(
|
|
382
378
|
() => ({ ...defaultChatComposerMessages, ...messageOverrides }),
|
|
383
379
|
[messageOverrides],
|
|
@@ -667,10 +663,7 @@ export function useChatComposerController({
|
|
|
667
663
|
tone: "error" as const,
|
|
668
664
|
message: /control changed|paused while/i.test(delivery.error.message)
|
|
669
665
|
? messages.controlChangedError
|
|
670
|
-
:
|
|
671
|
-
delivery.error,
|
|
672
|
-
composerSubmissionErrorMessage(delivery.error) || messages.sendFailedError,
|
|
673
|
-
),
|
|
666
|
+
: composerSubmissionErrorMessage(delivery.error) || messages.sendFailedError,
|
|
674
667
|
}
|
|
675
668
|
: null);
|
|
676
669
|
useEffect(() => {
|