@opengeni/react 1.3.0 → 1.4.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 +21 -1
- package/dist/artifacts.js +2 -2
- package/dist/artifacts.js.map +1 -1
- package/dist/{chunk-ERGYJIBB.js → chunk-AWZLIMKX.js} +2 -2
- package/dist/{chunk-ERGYJIBB.js.map → chunk-AWZLIMKX.js.map} +1 -1
- package/dist/{chunk-FZCP527Y.js → chunk-CJUO7J5Z.js} +2 -2
- package/dist/{chunk-742CZ3L3.js → chunk-H5X55XH7.js} +16 -200
- package/dist/chunk-H5X55XH7.js.map +1 -0
- package/dist/{chunk-PGSX7UCP.js → chunk-LJ2UTJJE.js} +1369 -785
- package/dist/chunk-LJ2UTJJE.js.map +1 -0
- package/dist/{chunk-CY7WK5FD.js → chunk-LN6TCXD3.js} +22 -1
- package/dist/chunk-LN6TCXD3.js.map +1 -0
- package/dist/{chunk-TMYGL6X5.js → chunk-MBMPHOMA.js} +71 -6
- package/dist/chunk-MBMPHOMA.js.map +1 -0
- package/dist/{chunk-RC6BZ44Q.js → chunk-OA6HFRQP.js} +2 -2
- package/dist/{chunk-WWEUO2PL.js → chunk-WZMYFJD3.js} +190 -11
- package/dist/chunk-WZMYFJD3.js.map +1 -0
- package/dist/components/composer-transcription-control.d.ts +3 -0
- package/dist/components/open-geni-chat.d.ts +6 -2
- package/dist/components/session-conversation.d.ts +25 -19
- package/dist/components/session-list.d.ts +4 -1
- package/dist/components/tool-action-review.d.ts +29 -2
- package/dist/components/tool-review-history.d.ts +8 -4
- package/dist/composer.js +1 -1
- package/dist/connect.js +1 -1
- package/dist/hooks/use-client-config-flags.d.ts +37 -0
- package/dist/index.d.ts +3 -3
- package/dist/index.js +21 -11
- package/dist/index.js.map +1 -1
- package/dist/{latest-question-YTOBHCWR.js → latest-question-GPYM2JNU.js} +3 -3
- package/dist/realtime/realtime-control.d.ts +2 -0
- package/dist/realtime.js +25 -4
- package/dist/realtime.js.map +1 -1
- package/dist/session-capability-policy.d.ts +20 -1
- package/dist/session-ui.js +6 -6
- package/dist/session.js +6 -6
- package/dist/timeline/index.d.ts +2 -0
- package/dist/timeline/retained-loaders.d.ts +27 -0
- package/package.json +3 -3
- package/src/components/approval-surface.tsx +190 -79
- package/src/components/artifacts/session-artifact-viewer.tsx +4 -2
- package/src/components/composer-transcription-control.tsx +14 -0
- package/src/components/composer.tsx +1 -0
- package/src/components/open-geni-chat.tsx +53 -13
- package/src/components/session-conversation.tsx +229 -63
- package/src/components/session-list.tsx +9 -2
- package/src/components/tool-action-review.tsx +502 -239
- package/src/components/tool-review-history.tsx +18 -43
- package/src/hooks/use-client-config-flags.ts +102 -0
- package/src/hooks/use-session-events.ts +19 -16
- package/src/hooks/use-transcription.ts +6 -0
- package/src/hooks/use-voice-input.ts +6 -0
- package/src/index.ts +9 -0
- package/src/realtime/realtime-control.tsx +28 -1
- package/src/session-capability-policy.ts +119 -6
- package/src/timeline/index.ts +10 -0
- package/src/timeline/retained-loaders.ts +86 -0
- package/src/timeline/tool-display-name.ts +5 -1
- package/src/timeline/tool-renderers.tsx +143 -10
- package/styles/compiled.css +65 -21
- package/styles/tokens.css +1 -1
- package/dist/chunk-742CZ3L3.js.map +0 -1
- package/dist/chunk-CY7WK5FD.js.map +0 -1
- package/dist/chunk-PGSX7UCP.js.map +0 -1
- package/dist/chunk-TMYGL6X5.js.map +0 -1
- package/dist/chunk-WWEUO2PL.js.map +0 -1
- /package/dist/{chunk-FZCP527Y.js.map → chunk-CJUO7J5Z.js.map} +0 -0
- /package/dist/{chunk-RC6BZ44Q.js.map → chunk-OA6HFRQP.js.map} +0 -0
- /package/dist/{latest-question-YTOBHCWR.js.map → latest-question-GPYM2JNU.js.map} +0 -0
package/dist/session-ui.js
CHANGED
|
@@ -27,21 +27,21 @@ import {
|
|
|
27
27
|
setStartupDetails,
|
|
28
28
|
useStartupDetails,
|
|
29
29
|
userMessageLikelyNeedsDisclosure
|
|
30
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-LJ2UTJJE.js";
|
|
31
31
|
import "./chunk-UK4TINKT.js";
|
|
32
32
|
import "./chunk-J2QWVSQY.js";
|
|
33
33
|
import "./chunk-C5X5AJXK.js";
|
|
34
34
|
import "./chunk-ZYRGEKWS.js";
|
|
35
|
-
import "./chunk-
|
|
35
|
+
import "./chunk-CJUO7J5Z.js";
|
|
36
36
|
import {
|
|
37
37
|
ArtifactLabelsProvider,
|
|
38
38
|
DEFAULT_ARTIFACT_LABELS
|
|
39
39
|
} from "./chunk-5CVXZA3Y.js";
|
|
40
40
|
import {
|
|
41
41
|
createOlderHistoryLoadReceipt
|
|
42
|
-
} from "./chunk-
|
|
43
|
-
import "./chunk-
|
|
44
|
-
import "./chunk-
|
|
42
|
+
} from "./chunk-WZMYFJD3.js";
|
|
43
|
+
import "./chunk-OA6HFRQP.js";
|
|
44
|
+
import "./chunk-AWZLIMKX.js";
|
|
45
45
|
import {
|
|
46
46
|
OpenGeniProvider
|
|
47
47
|
} from "./chunk-3CTRFMAK.js";
|
|
@@ -57,7 +57,7 @@ import {
|
|
|
57
57
|
} from "./chunk-XCHWDJ22.js";
|
|
58
58
|
import "./chunk-A2PI6UMY.js";
|
|
59
59
|
import "./chunk-JVYQRRC4.js";
|
|
60
|
-
import "./chunk-
|
|
60
|
+
import "./chunk-LN6TCXD3.js";
|
|
61
61
|
import "./chunk-RSI7XYXL.js";
|
|
62
62
|
import "./chunk-BRM6FVZA.js";
|
|
63
63
|
import "./chunk-LNIRAOA3.js";
|
package/dist/session.js
CHANGED
|
@@ -1,39 +1,39 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createEmbeddedSessionClient,
|
|
3
|
-
isGoalEvent,
|
|
4
3
|
isLineageRefreshEvent,
|
|
5
4
|
isSessionMcpApprovalPolicyEvent,
|
|
6
|
-
useGoal,
|
|
7
5
|
useSessionLineage,
|
|
8
6
|
useSessionMcpApprovalPolicy
|
|
9
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-H5X55XH7.js";
|
|
10
8
|
import {
|
|
11
9
|
approvalsFromRequiresAction,
|
|
12
10
|
conversationTimeline,
|
|
13
11
|
createOlderHistoryLoadReceipt,
|
|
14
12
|
humanInputRequestFromEvent,
|
|
13
|
+
isGoalEvent,
|
|
15
14
|
isHumanInputEvent,
|
|
16
15
|
isTitleEvent,
|
|
17
16
|
isTurnQueueEvent,
|
|
18
17
|
projectPendingApprovals,
|
|
19
18
|
projectPendingHumanInputRequests,
|
|
20
19
|
useFileAttachments,
|
|
20
|
+
useGoal,
|
|
21
21
|
useHumanInputRequests,
|
|
22
22
|
useSession,
|
|
23
23
|
useSessionControl,
|
|
24
24
|
useSessionEvents,
|
|
25
25
|
useTurnQueue
|
|
26
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-WZMYFJD3.js";
|
|
27
27
|
import {
|
|
28
28
|
buildTimeline,
|
|
29
29
|
creditExhaustedFromEvents,
|
|
30
30
|
extractSessionRef,
|
|
31
31
|
groupTimeline,
|
|
32
32
|
sessionStatusFromEvents
|
|
33
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-OA6HFRQP.js";
|
|
34
34
|
import {
|
|
35
35
|
toolDisplayName
|
|
36
|
-
} from "./chunk-
|
|
36
|
+
} from "./chunk-AWZLIMKX.js";
|
|
37
37
|
import {
|
|
38
38
|
FILE_ONLY_MESSAGE_TEXT,
|
|
39
39
|
composeSendInput,
|
package/dist/timeline/index.d.ts
CHANGED
|
@@ -2,6 +2,8 @@ export { buildTimeline, creditExhaustedFromEvents, extractSessionRef, groupTimel
|
|
|
2
2
|
export type { GroupTimelineOptions } from "./projection.js";
|
|
3
3
|
export type { ActivityItem, AgentMessageItem, AuthNeededItem, ContextCompactionItem, FleetDecisionItem, FleetDecisionScoreItem, GoalItem, HumanInputAnswerSummary, HumanInputItem, MachineInputBatchItem, MachineInputMember, MemoryItem, KnowledgeItem, NoticeItem, ReasoningItem, SandboxItem, StartupPhase, StartupPhaseItem, SessionStatusItem, TimelineGroup, TimelineItem, TimelineAnnotationSourceDescriptor, TurnEndItem, ToolCallItem, UserMessageItem, WorkerCompletionItem, WorkerItem, } from "./types.js";
|
|
4
4
|
export { createToolRegistry, rawTypeOf } from "./registry.js";
|
|
5
|
+
export { createSessionRetainedScreenshotLoader, createWorkspaceRetainedArtifactLoader, createWorkspaceRetainedVideoLoader, } from "./retained-loaders.js";
|
|
6
|
+
export type { RetainedArtifactLoaderClient, RetainedScreenshotLoaderClient, RetainedVideoLoaderClient, } from "./retained-loaders.js";
|
|
5
7
|
export type { CreateToolRegistryOptions, RetainedArtifactLoader, RetainedScreenshotLoader, VideoArtifactPlaybackLoader, ToolRegistry, ToolRegistryEntry, ToolRenderer, ToolRendererProps, } from "./registry.js";
|
|
6
8
|
export { createDefaultToolRegistry, defaultToolRegistry } from "./tool-renderers.js";
|
|
7
9
|
export { ActivityRail } from "./activity-rail.js";
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { OpenGeniClient } from "@opengeni/sdk";
|
|
2
|
+
import type { RetainedArtifactLoader, RetainedScreenshotLoader, VideoArtifactPlaybackLoader } from "./registry.js";
|
|
3
|
+
/** SDK methods the workspace retained-artifact loader needs. */
|
|
4
|
+
export type RetainedArtifactLoaderClient = Pick<OpenGeniClient, "createRetainedArtifactDownloadUrl" | "downloadRetainedArtifact">;
|
|
5
|
+
/** SDK method the session retained-screenshot loader needs. */
|
|
6
|
+
export type RetainedScreenshotLoaderClient = Pick<OpenGeniClient, "downloadRetainedScreenshot">;
|
|
7
|
+
/** SDK method the generated-video playback loader needs. */
|
|
8
|
+
export type RetainedVideoLoaderClient = Pick<OpenGeniClient, "createVideoArtifactPlaybackSource">;
|
|
9
|
+
/**
|
|
10
|
+
* Bind permanent retained-artifact retrieval (generated images, published
|
|
11
|
+
* files) to one authenticated workspace, for `MessageTimeline`'s
|
|
12
|
+
* `loadRetainedArtifact`. Images get a short-lived URL; files are assembled
|
|
13
|
+
* from verified byte ranges.
|
|
14
|
+
*/
|
|
15
|
+
export declare function createWorkspaceRetainedArtifactLoader(client: RetainedArtifactLoaderClient, workspaceId: string): RetainedArtifactLoader;
|
|
16
|
+
/**
|
|
17
|
+
* Bind the authenticated screenshot downloader to one workspace and session,
|
|
18
|
+
* for `MessageTimeline`'s `loadRetainedScreenshot`. Fetched metadata must match
|
|
19
|
+
* the timeline receipt before bytes are handed to React; errors deliberately
|
|
20
|
+
* contain no storage locator or content.
|
|
21
|
+
*/
|
|
22
|
+
export declare function createSessionRetainedScreenshotLoader(client: RetainedScreenshotLoaderClient, workspaceId: string, sessionId: string): RetainedScreenshotLoader;
|
|
23
|
+
/**
|
|
24
|
+
* Bind expiring generated-video playback sources to one authenticated
|
|
25
|
+
* workspace, for `MessageTimeline`'s `loadVideoArtifactPlayback`.
|
|
26
|
+
*/
|
|
27
|
+
export declare function createWorkspaceRetainedVideoLoader(client: RetainedVideoLoaderClient, workspaceId: string): VideoArtifactPlaybackLoader;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opengeni/react",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "React hooks and styled components for OpenGeni: live session streaming, chat composer, message timeline, session status, and fleet views — token-themed (CSS variables), dark-first, built on Tailwind v4 + Radix + Motion.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -153,8 +153,8 @@
|
|
|
153
153
|
"@dnd-kit/utilities": "3.2.2",
|
|
154
154
|
"@fontsource-variable/noto-sans-arabic": "5.2.10",
|
|
155
155
|
"@fontsource-variable/noto-sans-jp": "5.2.10",
|
|
156
|
-
"@opengeni/connect": "1.
|
|
157
|
-
"@opengeni/sdk": "1.
|
|
156
|
+
"@opengeni/connect": "1.4.0",
|
|
157
|
+
"@opengeni/sdk": "1.4.0",
|
|
158
158
|
"clsx": "^2.1.1",
|
|
159
159
|
"dequal": "2.0.3",
|
|
160
160
|
"lucide-react": "^1.8.0",
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import { useEffect, useRef, useState, type ReactNode } from "react";
|
|
1
|
+
import { useCallback, useEffect, useMemo, useRef, useState, type ReactNode } from "react";
|
|
2
|
+
import { dequal } from "dequal";
|
|
3
|
+
import { ChevronLeftIcon, ChevronRightIcon, ShieldCheckIcon } from "lucide-react";
|
|
2
4
|
import {
|
|
3
5
|
OpenGeniApiError,
|
|
4
6
|
toolReviewAction,
|
|
@@ -13,6 +15,9 @@ import { toolDisplayName } from "../timeline/tool-display-name";
|
|
|
13
15
|
import {
|
|
14
16
|
ToolActionReviewCard,
|
|
15
17
|
ToolActionReviewDetails,
|
|
18
|
+
ToolReviewFrame,
|
|
19
|
+
DeclineButton,
|
|
20
|
+
UNRECOVERABLE_TEXT,
|
|
16
21
|
type ToolReviewDetailsLoader,
|
|
17
22
|
} from "./tool-action-review";
|
|
18
23
|
|
|
@@ -37,10 +42,15 @@ export const defaultApprovalSurfaceMessages: ApprovalSurfaceMessages = {
|
|
|
37
42
|
formatToolName: (name) =>
|
|
38
43
|
name.includes("__") || /^[a-f0-9]{64}$/.test(name)
|
|
39
44
|
? toolDisplayName(name)
|
|
40
|
-
: name.replaceAll("_", " ").replaceAll(".", " › "),
|
|
45
|
+
: humanToolName(name.replaceAll("_", " ").replaceAll(".", " › ")),
|
|
41
46
|
showDetails: "View full details",
|
|
42
47
|
hideDetails: "Back to review",
|
|
43
48
|
};
|
|
49
|
+
/** "batch modify messages" → "Modify messages": how a call is grouped is not what it does. */
|
|
50
|
+
function humanToolName(name: string): string {
|
|
51
|
+
const phrase = name.replace(/^batch\s+/i, "").trim();
|
|
52
|
+
return phrase ? phrase.charAt(0).toUpperCase() + phrase.slice(1) : name;
|
|
53
|
+
}
|
|
44
54
|
export type ApprovalSurfaceProps = {
|
|
45
55
|
approvals: PendingApproval[];
|
|
46
56
|
onApprove: (approval: PendingApproval) => void | Promise<void>;
|
|
@@ -88,9 +98,10 @@ export function ApprovalSurface({
|
|
|
88
98
|
}: ApprovalSurfaceProps) {
|
|
89
99
|
const messages = { ...defaultApprovalSurfaceMessages, ...overrides };
|
|
90
100
|
const formatError = useErrorMessage();
|
|
91
|
-
const [pending, setPending] = useState<{
|
|
92
|
-
|
|
93
|
-
|
|
101
|
+
const [pending, setPending] = useState<{
|
|
102
|
+
id: string;
|
|
103
|
+
decision: "approve" | "reject";
|
|
104
|
+
} | null>(null);
|
|
94
105
|
const pendingRef = useRef<string | null>(null);
|
|
95
106
|
const [decisionError, setDecisionError] = useState<{ cause: unknown } | null>(null);
|
|
96
107
|
const [localSelectedId, setLocalSelectedId] = useState<string | null>(null);
|
|
@@ -137,39 +148,43 @@ export function ApprovalSurface({
|
|
|
137
148
|
<section
|
|
138
149
|
ref={surface}
|
|
139
150
|
aria-label={messages.title}
|
|
140
|
-
className={cn("og-root box-border w-full min-w-0
|
|
151
|
+
className={cn("og-root box-border w-full min-w-0 space-y-2 py-2", className)}
|
|
141
152
|
data-og-approval-surface=""
|
|
142
153
|
>
|
|
143
154
|
{approvals.length > 1 && (
|
|
144
155
|
<nav
|
|
145
156
|
aria-label="Actions to review"
|
|
146
|
-
className="flex flex-wrap items-center justify-between gap-
|
|
157
|
+
className="flex flex-wrap items-center justify-between gap-x-3 gap-y-1 text-og-xs text-og-fg-muted"
|
|
147
158
|
>
|
|
148
|
-
<span
|
|
149
|
-
|
|
159
|
+
<span className="font-medium text-og-status-waiting">
|
|
160
|
+
{approvals.length} actions are waiting for you
|
|
161
|
+
</span>
|
|
162
|
+
<div className="flex items-center gap-1">
|
|
150
163
|
<button
|
|
151
164
|
type="button"
|
|
165
|
+
aria-label="Previous action"
|
|
152
166
|
disabled={selectedIndex === 0}
|
|
153
167
|
onClick={() => setSelectedId(approvals[selectedIndex - 1]!.id)}
|
|
154
|
-
className="min-h-
|
|
168
|
+
className="inline-flex min-h-8 min-w-8 items-center justify-center rounded-og-md hover:bg-og-hover hover:text-og-fg disabled:opacity-40 disabled:hover:bg-transparent [@media(pointer:coarse)]:min-h-11 [@media(pointer:coarse)]:min-w-11 focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-og-accent/40"
|
|
155
169
|
>
|
|
156
|
-
|
|
170
|
+
<ChevronLeftIcon className="size-4" aria-hidden="true" />
|
|
157
171
|
</button>
|
|
158
|
-
<span>
|
|
172
|
+
<span className="tabular-nums" aria-live="polite">
|
|
159
173
|
{selectedIndex + 1} of {approvals.length}
|
|
160
174
|
</span>
|
|
161
175
|
<button
|
|
162
176
|
type="button"
|
|
177
|
+
aria-label="Next action"
|
|
163
178
|
disabled={selectedIndex === approvals.length - 1}
|
|
164
179
|
onClick={() => setSelectedId(approvals[selectedIndex + 1]!.id)}
|
|
165
|
-
className="min-h-
|
|
180
|
+
className="inline-flex min-h-8 min-w-8 items-center justify-center rounded-og-md hover:bg-og-hover hover:text-og-fg disabled:opacity-40 disabled:hover:bg-transparent [@media(pointer:coarse)]:min-h-11 [@media(pointer:coarse)]:min-w-11 focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-og-accent/40"
|
|
166
181
|
>
|
|
167
|
-
|
|
182
|
+
<ChevronRightIcon className="size-4" aria-hidden="true" />
|
|
168
183
|
</button>
|
|
169
184
|
</div>
|
|
170
185
|
</nav>
|
|
171
186
|
)}
|
|
172
|
-
<div
|
|
187
|
+
<div>
|
|
173
188
|
{[selected].map((approval) => (
|
|
174
189
|
<ApprovalRequest
|
|
175
190
|
key={approval.id}
|
|
@@ -187,7 +202,7 @@ export function ApprovalSurface({
|
|
|
187
202
|
))}
|
|
188
203
|
</div>
|
|
189
204
|
{errorText && (
|
|
190
|
-
<p role="alert" className="m-0
|
|
205
|
+
<p role="alert" className="m-0 px-1 text-og-sm text-og-status-failed">
|
|
191
206
|
{errorText}
|
|
192
207
|
</p>
|
|
193
208
|
)}
|
|
@@ -215,96 +230,197 @@ function ApprovalRequest({
|
|
|
215
230
|
onApprove: () => void;
|
|
216
231
|
onReject: () => void;
|
|
217
232
|
}) {
|
|
218
|
-
const [loaded, setLoaded] = useState<ToolActionReview | null>(null);
|
|
219
|
-
const [failed, setFailed] = useState(false);
|
|
220
|
-
const [legacy, setLegacy] = useState(false);
|
|
221
233
|
const [retry, setRetry] = useState(0);
|
|
234
|
+
// Event projection recreates the same approval on unrelated timeline updates.
|
|
235
|
+
// Keep one request for equal facts, but never reuse it across changed facts,
|
|
236
|
+
// an explicit retry, or a loader change (the host's client/access boundary).
|
|
237
|
+
const requestRef = useRef({ approval, loadReview, retry });
|
|
238
|
+
if (
|
|
239
|
+
requestRef.current.loadReview !== loadReview ||
|
|
240
|
+
requestRef.current.retry !== retry ||
|
|
241
|
+
!dequal(requestRef.current.approval, approval)
|
|
242
|
+
) {
|
|
243
|
+
requestRef.current = { approval, loadReview, retry };
|
|
244
|
+
}
|
|
245
|
+
const request = requestRef.current;
|
|
246
|
+
const [result, setResult] = useState<{
|
|
247
|
+
request: typeof request;
|
|
248
|
+
review?: ToolActionReview;
|
|
249
|
+
failed?: boolean;
|
|
250
|
+
legacy?: boolean;
|
|
251
|
+
denied?: boolean;
|
|
252
|
+
} | null>(null);
|
|
253
|
+
// Hide stale facts during render, before passive effect cleanup can run.
|
|
254
|
+
const current = result?.request === request ? result : null;
|
|
255
|
+
const loaded = current?.review;
|
|
256
|
+
const failed = current?.failed;
|
|
257
|
+
const legacy = current?.legacy;
|
|
258
|
+
const denied = current?.denied;
|
|
222
259
|
const [detailPath, setDetailPath] = useState<string | null>(null);
|
|
223
260
|
useEffect(() => {
|
|
224
261
|
let active = true;
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
setLegacy(false);
|
|
228
|
-
if (loadReview)
|
|
229
|
-
void loadReview(approval).then(
|
|
262
|
+
if (request.loadReview)
|
|
263
|
+
void request.loadReview(request.approval).then(
|
|
230
264
|
(review) => {
|
|
231
265
|
if (active) {
|
|
232
|
-
|
|
233
|
-
|
|
266
|
+
setResult(
|
|
267
|
+
review.id !== request.approval.id ? { request, failed: true } : { request, review },
|
|
268
|
+
);
|
|
234
269
|
}
|
|
235
270
|
},
|
|
236
271
|
(error) => {
|
|
237
272
|
if (active) {
|
|
238
|
-
|
|
273
|
+
// 404: no saved review (older or non-connector actions); use the
|
|
274
|
+
// approval's own arguments. 401/403: this person cannot review it.
|
|
275
|
+
if (error instanceof OpenGeniApiError && error.status === 404)
|
|
276
|
+
setResult({ request, legacy: true });
|
|
277
|
+
else if (
|
|
239
278
|
error instanceof OpenGeniApiError &&
|
|
240
|
-
error.status ===
|
|
241
|
-
approval.arguments !== undefined
|
|
279
|
+
(error.status === 401 || error.status === 403)
|
|
242
280
|
)
|
|
243
|
-
|
|
244
|
-
else
|
|
281
|
+
setResult({ request, failed: true, denied: true });
|
|
282
|
+
else setResult({ request, failed: true });
|
|
245
283
|
}
|
|
246
284
|
},
|
|
247
285
|
);
|
|
248
286
|
return () => {
|
|
249
287
|
active = false;
|
|
250
288
|
};
|
|
251
|
-
}, [
|
|
289
|
+
}, [request]);
|
|
290
|
+
const stableApproval = request.approval;
|
|
291
|
+
const { formatToolName, approve: approveLabel } = messages;
|
|
292
|
+
const fallback = useMemo<ToolActionReview>(
|
|
293
|
+
() => ({
|
|
294
|
+
version: 1,
|
|
295
|
+
id: stableApproval.id,
|
|
296
|
+
actionDigest: "",
|
|
297
|
+
revision: "legacy",
|
|
298
|
+
status: "pending",
|
|
299
|
+
...toolReviewAction(stableApproval.name, stableApproval.arguments, {
|
|
300
|
+
kind: "generic",
|
|
301
|
+
title:
|
|
302
|
+
stableApproval.display?.title ??
|
|
303
|
+
formatToolName(stableApproval.display?.toolName ?? stableApproval.name),
|
|
304
|
+
}),
|
|
305
|
+
...(stableApproval.display?.accountLabel
|
|
306
|
+
? { accountLabel: stableApproval.display.accountLabel }
|
|
307
|
+
: {}),
|
|
308
|
+
...toolReviewFields(stableApproval.arguments),
|
|
309
|
+
reason: "",
|
|
310
|
+
createdAt: "",
|
|
311
|
+
updatedAt: "",
|
|
312
|
+
approveLabel,
|
|
313
|
+
availableActions: stableApproval.arguments === undefined ? [] : ["approve", "reject"],
|
|
314
|
+
detailsAvailable: stableApproval.arguments !== undefined,
|
|
315
|
+
}),
|
|
316
|
+
[stableApproval, formatToolName, approveLabel],
|
|
317
|
+
);
|
|
318
|
+
const loadLegacyDetails = useCallback<ToolReviewDetailsLoader>(
|
|
319
|
+
async (value, path, offset) => ({
|
|
320
|
+
version: 1,
|
|
321
|
+
id: value.id,
|
|
322
|
+
actionDigest: value.actionDigest,
|
|
323
|
+
...toolReviewDetails(stableApproval.arguments, undefined, path, offset),
|
|
324
|
+
}),
|
|
325
|
+
[stableApproval],
|
|
326
|
+
);
|
|
252
327
|
if (loadReview && !loaded && !failed && !legacy)
|
|
253
328
|
return (
|
|
254
|
-
<
|
|
255
|
-
|
|
256
|
-
|
|
329
|
+
<ToolReviewFrame
|
|
330
|
+
tone="waiting"
|
|
331
|
+
icon={<ShieldCheckIcon className="size-4" aria-hidden="true" />}
|
|
332
|
+
eyebrow={messages.title}
|
|
333
|
+
title={fallback.title}
|
|
334
|
+
subtitle={fallback.accountLabel}
|
|
335
|
+
aria-busy
|
|
336
|
+
data-og-tool-review-loading=""
|
|
337
|
+
>
|
|
338
|
+
<div role="status" className="space-y-2">
|
|
339
|
+
<span className="sr-only">Loading action details…</span>
|
|
340
|
+
<div className="h-3 w-3/4 animate-pulse rounded-og-sm bg-og-fg/8" aria-hidden="true" />
|
|
341
|
+
<div className="h-3 w-1/2 animate-pulse rounded-og-sm bg-og-fg/8" aria-hidden="true" />
|
|
342
|
+
</div>
|
|
343
|
+
</ToolReviewFrame>
|
|
344
|
+
);
|
|
345
|
+
if (denied)
|
|
346
|
+
return (
|
|
347
|
+
<ToolReviewFrame
|
|
348
|
+
tone="waiting"
|
|
349
|
+
icon={<ShieldCheckIcon className="size-4" aria-hidden="true" />}
|
|
350
|
+
eyebrow={messages.title}
|
|
351
|
+
title={fallback.title}
|
|
352
|
+
subtitle={fallback.accountLabel}
|
|
353
|
+
>
|
|
354
|
+
<p role="alert" className="m-0 text-og-sm text-og-fg">
|
|
355
|
+
You don't have access to review this action. Ask someone with access to this chat to
|
|
356
|
+
decide.
|
|
357
|
+
</p>
|
|
358
|
+
</ToolReviewFrame>
|
|
257
359
|
);
|
|
258
360
|
if (failed)
|
|
259
361
|
return (
|
|
260
|
-
<
|
|
261
|
-
|
|
262
|
-
<
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
362
|
+
<ToolReviewFrame
|
|
363
|
+
tone="waiting"
|
|
364
|
+
icon={<ShieldCheckIcon className="size-4" aria-hidden="true" />}
|
|
365
|
+
eyebrow={messages.title}
|
|
366
|
+
title={fallback.title}
|
|
367
|
+
subtitle={fallback.accountLabel}
|
|
368
|
+
footer={
|
|
369
|
+
<div className="ml-auto">
|
|
370
|
+
<DeclineButton
|
|
371
|
+
busy={Boolean(actions.disabled || actions.submitting)}
|
|
372
|
+
submitting={actions.submitting === "reject"}
|
|
373
|
+
label={messages.reject}
|
|
374
|
+
onClick={actions.onReject}
|
|
375
|
+
/>
|
|
376
|
+
</div>
|
|
377
|
+
}
|
|
378
|
+
>
|
|
379
|
+
<p role="alert" className="m-0 text-og-sm text-og-fg">
|
|
380
|
+
Couldn't load this action, so it can't be approved yet. You can still decline it.{" "}
|
|
381
|
+
<button
|
|
382
|
+
type="button"
|
|
383
|
+
className="min-h-8 font-medium underline underline-offset-4 [@media(pointer:coarse)]:min-h-11 focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-og-accent/40"
|
|
384
|
+
onClick={() => setRetry((value) => value + 1)}
|
|
385
|
+
>
|
|
386
|
+
Try again
|
|
387
|
+
</button>
|
|
388
|
+
</p>
|
|
389
|
+
</ToolReviewFrame>
|
|
390
|
+
);
|
|
391
|
+
if (!loaded && stableApproval.arguments === undefined)
|
|
392
|
+
return (
|
|
393
|
+
<div data-approval-id={approval.id} data-review-origin="pending">
|
|
394
|
+
{custom}
|
|
395
|
+
<ToolReviewFrame
|
|
396
|
+
tone="waiting"
|
|
397
|
+
icon={<ShieldCheckIcon className="size-4" aria-hidden="true" />}
|
|
398
|
+
eyebrow={messages.title}
|
|
399
|
+
title={fallback.title}
|
|
400
|
+
subtitle={fallback.accountLabel}
|
|
401
|
+
data-og-tool-review=""
|
|
402
|
+
footer={
|
|
403
|
+
<div className="ml-auto">
|
|
404
|
+
<DeclineButton
|
|
405
|
+
busy={Boolean(actions.disabled || actions.submitting)}
|
|
406
|
+
submitting={actions.submitting === "reject"}
|
|
407
|
+
label={messages.reject}
|
|
408
|
+
onClick={actions.onReject}
|
|
409
|
+
/>
|
|
410
|
+
</div>
|
|
411
|
+
}
|
|
266
412
|
>
|
|
267
|
-
|
|
268
|
-
</
|
|
413
|
+
<p className="m-0 text-og-sm text-og-fg">{UNRECOVERABLE_TEXT}</p>
|
|
414
|
+
</ToolReviewFrame>
|
|
269
415
|
</div>
|
|
270
416
|
);
|
|
271
|
-
const fallback: ToolActionReview = {
|
|
272
|
-
version: 1,
|
|
273
|
-
id: approval.id,
|
|
274
|
-
actionDigest: "",
|
|
275
|
-
revision: "legacy",
|
|
276
|
-
status: "pending",
|
|
277
|
-
...toolReviewAction(approval.name, approval.arguments, {
|
|
278
|
-
kind: "generic",
|
|
279
|
-
title:
|
|
280
|
-
approval.display?.title ??
|
|
281
|
-
messages.formatToolName(approval.display?.toolName ?? approval.name),
|
|
282
|
-
}),
|
|
283
|
-
...(approval.display?.accountLabel ? { accountLabel: approval.display.accountLabel } : {}),
|
|
284
|
-
...toolReviewFields(approval.arguments),
|
|
285
|
-
reason: "This action requires your approval.",
|
|
286
|
-
createdAt: "",
|
|
287
|
-
updatedAt: "",
|
|
288
|
-
approveLabel: messages.approve,
|
|
289
|
-
availableActions: approval.arguments === undefined ? [] : ["approve", "reject"],
|
|
290
|
-
detailsAvailable: approval.arguments !== undefined,
|
|
291
|
-
};
|
|
292
417
|
const review = loaded ?? fallback;
|
|
293
418
|
if (detailPath !== null)
|
|
294
419
|
return (
|
|
295
420
|
<ToolActionReviewDetails
|
|
296
421
|
review={review}
|
|
297
422
|
path={detailPath}
|
|
298
|
-
load={
|
|
299
|
-
loaded && loadDetails
|
|
300
|
-
? loadDetails
|
|
301
|
-
: async (value, path, offset) => ({
|
|
302
|
-
version: 1,
|
|
303
|
-
id: value.id,
|
|
304
|
-
actionDigest: value.actionDigest,
|
|
305
|
-
...toolReviewDetails(approval.arguments, undefined, path, offset),
|
|
306
|
-
})
|
|
307
|
-
}
|
|
423
|
+
load={loaded && loadDetails ? loadDetails : loadLegacyDetails}
|
|
308
424
|
onBack={() => {
|
|
309
425
|
const path = detailPath;
|
|
310
426
|
setDetailPath(null);
|
|
@@ -340,11 +456,6 @@ function ApprovalRequest({
|
|
|
340
456
|
: undefined
|
|
341
457
|
}
|
|
342
458
|
/>
|
|
343
|
-
{!loaded && approval.arguments === undefined && (
|
|
344
|
-
<p className="pb-3 text-og-sm text-og-fg-muted">
|
|
345
|
-
Open the task with a compatible client to review this action.
|
|
346
|
-
</p>
|
|
347
|
-
)}
|
|
348
459
|
</div>
|
|
349
460
|
);
|
|
350
461
|
}
|
|
@@ -203,7 +203,7 @@ function EditableBody({
|
|
|
203
203
|
workspaceId: string;
|
|
204
204
|
sessionId: string;
|
|
205
205
|
result:
|
|
206
|
-
| { kind: "ready"; config: NonNullable<ClientConfig["artifacts"]> }
|
|
206
|
+
| { kind: "ready"; config: Exclude<NonNullable<ClientConfig["artifacts"]>, false> }
|
|
207
207
|
| { kind: "unavailable" }
|
|
208
208
|
| { kind: "error"; error: unknown };
|
|
209
209
|
} | null>(null);
|
|
@@ -359,7 +359,9 @@ function EditableBody({
|
|
|
359
359
|
);
|
|
360
360
|
}
|
|
361
361
|
|
|
362
|
-
function artifactCapabilityKey(
|
|
362
|
+
function artifactCapabilityKey(
|
|
363
|
+
config: Exclude<NonNullable<ClientConfig["artifacts"]>, false>,
|
|
364
|
+
): string {
|
|
363
365
|
return JSON.stringify([
|
|
364
366
|
config.editableLiveUrl,
|
|
365
367
|
config.cachePartition.accountId,
|
|
@@ -50,6 +50,9 @@ export type ComposerTranscriptionMessages = {
|
|
|
50
50
|
errorPermissionDenied: string;
|
|
51
51
|
errorNotSupported: string;
|
|
52
52
|
errorUnavailable: string;
|
|
53
|
+
errorInsufficientCredits: string;
|
|
54
|
+
errorAllowanceExhausted: string;
|
|
55
|
+
errorPolicyBlocked: string;
|
|
53
56
|
errorTooLarge: string;
|
|
54
57
|
errorInvalidAudio: string;
|
|
55
58
|
errorStorageUnavailable: string;
|
|
@@ -78,6 +81,10 @@ const defaultMessages: ComposerTranscriptionMessages = {
|
|
|
78
81
|
errorPermissionDenied: "Microphone permission was denied. Your draft was not changed.",
|
|
79
82
|
errorNotSupported: "Voice input is not supported on this device.",
|
|
80
83
|
errorUnavailable: "Voice input is not configured.",
|
|
84
|
+
errorInsufficientCredits:
|
|
85
|
+
"Voice input needs Opengeni credits. Add credits, then retry your saved recording.",
|
|
86
|
+
errorAllowanceExhausted: "Your usage limit was reached. Your recording is saved for later.",
|
|
87
|
+
errorPolicyBlocked: "Voice input isn't allowed for this account or workspace.",
|
|
81
88
|
errorTooLarge: "Recording is too large. Try a shorter message.",
|
|
82
89
|
errorInvalidAudio: "The recording could not be read. Try again.",
|
|
83
90
|
errorStorageUnavailable: "Voice input stopped because audio could not be saved safely.",
|
|
@@ -518,6 +525,13 @@ function restingLevels(count: number): number[] {
|
|
|
518
525
|
|
|
519
526
|
function transcriptionErrorMessage(code: string, messages: ComposerTranscriptionMessages): string {
|
|
520
527
|
switch (code) {
|
|
528
|
+
case "insufficient_credits":
|
|
529
|
+
return messages.errorInsufficientCredits;
|
|
530
|
+
case "allowance_exhausted":
|
|
531
|
+
case "monthly_model_cost_limit":
|
|
532
|
+
return messages.errorAllowanceExhausted;
|
|
533
|
+
case "policy_blocked":
|
|
534
|
+
return messages.errorPolicyBlocked;
|
|
521
535
|
case "permission_denied":
|
|
522
536
|
return messages.errorPermissionDenied;
|
|
523
537
|
case "not_supported":
|
|
@@ -967,6 +967,7 @@ export const Surface = forwardRef<HTMLDivElement, ComposerSurfaceProps>(function
|
|
|
967
967
|
const controller = useComposerController();
|
|
968
968
|
return (
|
|
969
969
|
<div
|
|
970
|
+
data-og-composer-surface=""
|
|
970
971
|
{...props}
|
|
971
972
|
ref={ref}
|
|
972
973
|
onDragOver={controller.attachments ? controller.handleDragOver : undefined}
|