@opengeni/react 3.3.2 → 3.5.1-canary.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/artifacts.js +12 -12
- package/dist/{browser-viewer-MKKFRRRV.js → browser-viewer-E5DDEFQ4.js} +3 -3
- package/dist/{chunk-2DZVYHVO.js → chunk-4JFUT22W.js} +39 -13
- package/dist/chunk-4JFUT22W.js.map +1 -0
- package/dist/{chunk-ELIYWBZR.js → chunk-BV5A4OTE.js} +4 -4
- package/dist/{chunk-3AGBGTRT.js → chunk-IB27C53Y.js} +130 -16
- package/dist/chunk-IB27C53Y.js.map +1 -0
- package/dist/{chunk-JAQ5K7W6.js → chunk-TJFMMDQU.js} +56 -53
- package/dist/chunk-TJFMMDQU.js.map +1 -0
- package/dist/{chunk-XYRDDSXF.js → chunk-UNCXXIQR.js} +16 -12
- package/dist/{chunk-XYRDDSXF.js.map → chunk-UNCXXIQR.js.map} +1 -1
- package/dist/{chunk-ZPBTI3TE.js → chunk-W2RGYBCV.js} +4 -4
- package/dist/{chunk-WSK7G5LE.js → chunk-W3OUAH6M.js} +80 -80
- package/dist/chunk-W3OUAH6M.js.map +1 -0
- package/dist/{chunk-X4A66EWZ.js → chunk-WMGF4Z7X.js} +41 -43
- package/dist/chunk-WMGF4Z7X.js.map +1 -0
- package/dist/{chunk-YG4QEQF5.js → chunk-WY3MELVJ.js} +7769 -7753
- package/dist/chunk-WY3MELVJ.js.map +1 -0
- package/dist/composer.js +4 -4
- package/dist/{computer-viewer-FWEZISW4.js → computer-viewer-7TEZC6Y2.js} +3 -3
- package/dist/hooks/use-file-attachments.d.ts +2 -0
- package/dist/hooks/use-session-events.d.ts +1 -1
- package/dist/index.js +85 -87
- package/dist/index.js.map +1 -1
- package/dist/interaction.js +3 -3
- package/dist/model-policy.d.ts +2 -1
- package/dist/model-policy.js +1 -1
- package/dist/{queue-surface-implementation-BSKUWHRI.js → queue-surface-implementation-XL2A6PBM.js} +2 -2
- package/dist/realtime.js +2 -2
- package/dist/session-ui.js +6 -6
- package/dist/session.js +4 -4
- package/dist/timeline/types.d.ts +2 -0
- package/package.json +2 -2
- package/src/components/composer.tsx +15 -5
- package/src/components/message-timeline.tsx +16 -2
- package/src/components/model-policy-picker.tsx +37 -10
- package/src/components/sandbox-workspace.tsx +7 -6
- package/src/hooks/use-file-attachments.ts +28 -2
- package/src/hooks/use-session-events.ts +168 -8
- package/src/model-policy.ts +48 -53
- package/src/timeline/projection.ts +6 -1
- package/src/timeline/types.ts +2 -0
- package/styles/compiled.css +5 -0
- package/dist/chunk-2DZVYHVO.js.map +0 -1
- package/dist/chunk-3AGBGTRT.js.map +0 -1
- package/dist/chunk-JAQ5K7W6.js.map +0 -1
- package/dist/chunk-WSK7G5LE.js.map +0 -1
- package/dist/chunk-X4A66EWZ.js.map +0 -1
- package/dist/chunk-YG4QEQF5.js.map +0 -1
- /package/dist/{browser-viewer-MKKFRRRV.js.map → browser-viewer-E5DDEFQ4.js.map} +0 -0
- /package/dist/{chunk-ELIYWBZR.js.map → chunk-BV5A4OTE.js.map} +0 -0
- /package/dist/{chunk-ZPBTI3TE.js.map → chunk-W2RGYBCV.js.map} +0 -0
- /package/dist/{computer-viewer-FWEZISW4.js.map → computer-viewer-7TEZC6Y2.js.map} +0 -0
- /package/dist/{queue-surface-implementation-BSKUWHRI.js.map → queue-surface-implementation-XL2A6PBM.js.map} +0 -0
package/dist/interaction.js
CHANGED
|
@@ -7,19 +7,20 @@ import {
|
|
|
7
7
|
useBrowserSession,
|
|
8
8
|
useBrowserSessions,
|
|
9
9
|
useSiteAuthConnections
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-W2RGYBCV.js";
|
|
11
11
|
import {
|
|
12
12
|
ComputerViewer,
|
|
13
13
|
useComputerFrameStream,
|
|
14
14
|
useComputerSession,
|
|
15
15
|
useComputerSessions
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-BV5A4OTE.js";
|
|
17
17
|
import {
|
|
18
18
|
useInteractionInterventions,
|
|
19
19
|
useInteractionInvalidation
|
|
20
20
|
} from "./chunk-H5YGYNXS.js";
|
|
21
21
|
import "./chunk-4MDLEHXL.js";
|
|
22
22
|
import "./chunk-5YK2ZDIG.js";
|
|
23
|
+
import "./chunk-JVYQRRC4.js";
|
|
23
24
|
import "./chunk-KZ73RL76.js";
|
|
24
25
|
import {
|
|
25
26
|
useEmbeddedBrowserInteraction,
|
|
@@ -27,7 +28,6 @@ import {
|
|
|
27
28
|
useEmbeddedInteraction,
|
|
28
29
|
useEmbeddedInterventions
|
|
29
30
|
} from "./chunk-NEXLLTP4.js";
|
|
30
|
-
import "./chunk-JVYQRRC4.js";
|
|
31
31
|
import "./chunk-VTOREEXF.js";
|
|
32
32
|
import "./chunk-22KP6LR5.js";
|
|
33
33
|
|
package/dist/model-policy.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { type ModelPickerBillingClass } from "@opengeni/sdk/model-picker-order";
|
|
1
2
|
import type { ClientModel, ReasoningEffort, WorkspaceModelCatalogModel } from "@opengeni/sdk";
|
|
2
|
-
export type PickerBillingClass =
|
|
3
|
+
export type PickerBillingClass = ModelPickerBillingClass;
|
|
3
4
|
export type PickerModelRow<TCatalog extends ClientModel = WorkspaceModelCatalogModel> = {
|
|
4
5
|
id: string;
|
|
5
6
|
label: string;
|
package/dist/model-policy.js
CHANGED
package/dist/{queue-surface-implementation-BSKUWHRI.js → queue-surface-implementation-XL2A6PBM.js}
RENAMED
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
QueueStoppingStatus,
|
|
4
4
|
TimelineAnnotationsChip,
|
|
5
5
|
requestQueueDraftEdit
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-W3OUAH6M.js";
|
|
7
7
|
import {
|
|
8
8
|
usePortalTokenSource,
|
|
9
9
|
usePortalTokenStyle
|
|
@@ -1047,4 +1047,4 @@ export {
|
|
|
1047
1047
|
QueueSurface,
|
|
1048
1048
|
queueComposerCheckoutEnabled
|
|
1049
1049
|
};
|
|
1050
|
-
//# sourceMappingURL=queue-surface-implementation-
|
|
1050
|
+
//# sourceMappingURL=queue-surface-implementation-XL2A6PBM.js.map
|
package/dist/realtime.js
CHANGED
|
@@ -3,8 +3,7 @@ import {
|
|
|
3
3
|
PickerAnimatedPage,
|
|
4
4
|
PickerBackHeader,
|
|
5
5
|
PickerNavRow
|
|
6
|
-
} from "./chunk-
|
|
7
|
-
import "./chunk-X4A66EWZ.js";
|
|
6
|
+
} from "./chunk-4JFUT22W.js";
|
|
8
7
|
import {
|
|
9
8
|
usePortalTokenSource,
|
|
10
9
|
usePortalTokenStyle
|
|
@@ -12,6 +11,7 @@ import {
|
|
|
12
11
|
import {
|
|
13
12
|
useEmbeddedRealtimeSession
|
|
14
13
|
} from "./chunk-NEXLLTP4.js";
|
|
14
|
+
import "./chunk-WMGF4Z7X.js";
|
|
15
15
|
import {
|
|
16
16
|
cn
|
|
17
17
|
} from "./chunk-22KP6LR5.js";
|
package/dist/session-ui.js
CHANGED
|
@@ -11,14 +11,14 @@ import {
|
|
|
11
11
|
sessionChromeGoalPillLabel,
|
|
12
12
|
sessionChromeGoalPillState,
|
|
13
13
|
userMessageLikelyNeedsDisclosure
|
|
14
|
-
} from "./chunk-
|
|
15
|
-
import
|
|
16
|
-
|
|
17
|
-
} from "./chunk-JAQ5K7W6.js";
|
|
18
|
-
import "./chunk-WSK7G5LE.js";
|
|
14
|
+
} from "./chunk-WY3MELVJ.js";
|
|
15
|
+
import "./chunk-W3OUAH6M.js";
|
|
16
|
+
import "./chunk-JVYQRRC4.js";
|
|
19
17
|
import "./chunk-7EAFGICR.js";
|
|
20
18
|
import "./chunk-VZTQ73XT.js";
|
|
21
|
-
import
|
|
19
|
+
import {
|
|
20
|
+
createOlderHistoryLoadReceipt
|
|
21
|
+
} from "./chunk-TJFMMDQU.js";
|
|
22
22
|
import "./chunk-VTOREEXF.js";
|
|
23
23
|
import "./chunk-22KP6LR5.js";
|
|
24
24
|
export {
|
package/dist/session.js
CHANGED
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
useSessionLineage,
|
|
18
18
|
useSessionMcpApprovalPolicy,
|
|
19
19
|
useTurnQueue
|
|
20
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-IB27C53Y.js";
|
|
21
21
|
import {
|
|
22
22
|
FILE_ONLY_MESSAGE_TEXT,
|
|
23
23
|
composeSendInput,
|
|
@@ -25,6 +25,8 @@ import {
|
|
|
25
25
|
shouldSubmitOnKey,
|
|
26
26
|
useComposer
|
|
27
27
|
} from "./chunk-IOLVN74D.js";
|
|
28
|
+
import "./chunk-KZ73RL76.js";
|
|
29
|
+
import "./chunk-NEXLLTP4.js";
|
|
28
30
|
import {
|
|
29
31
|
buildTimeline,
|
|
30
32
|
createOlderHistoryLoadReceipt,
|
|
@@ -35,9 +37,7 @@ import {
|
|
|
35
37
|
projectPendingHumanInputRequests,
|
|
36
38
|
sessionStatusFromEvents,
|
|
37
39
|
toolDisplayName
|
|
38
|
-
} from "./chunk-
|
|
39
|
-
import "./chunk-KZ73RL76.js";
|
|
40
|
-
import "./chunk-NEXLLTP4.js";
|
|
40
|
+
} from "./chunk-TJFMMDQU.js";
|
|
41
41
|
import "./chunk-VTOREEXF.js";
|
|
42
42
|
export {
|
|
43
43
|
FILE_ONLY_MESSAGE_TEXT,
|
package/dist/timeline/types.d.ts
CHANGED
|
@@ -332,6 +332,8 @@ export type AuthNeededItem = {
|
|
|
332
332
|
providerDomain: string;
|
|
333
333
|
/** The lapsed connection to reconnect, when the row survived. */
|
|
334
334
|
connectionId: string | null;
|
|
335
|
+
/** Host-owned bindings must never be routed into OpenGeni's native reconnect flow. */
|
|
336
|
+
authoritySource?: ToolAuthNeededPayload["authoritySource"] | null | undefined;
|
|
335
337
|
reason: ToolAuthNeededPayload["reason"] | null;
|
|
336
338
|
/** Scopes the provider now needs; may inform the copy, never shown as a raw label. */
|
|
337
339
|
scopes: string[];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opengeni/react",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.5.1-canary.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": {
|
|
@@ -120,7 +120,7 @@
|
|
|
120
120
|
"@dnd-kit/utilities": "3.2.2",
|
|
121
121
|
"@fontsource-variable/noto-sans-arabic": "5.2.10",
|
|
122
122
|
"@fontsource-variable/noto-sans-jp": "5.2.10",
|
|
123
|
-
"@opengeni/sdk": "^3.
|
|
123
|
+
"@opengeni/sdk": "^3.5.1-canary.0",
|
|
124
124
|
"clsx": "^2.1.1",
|
|
125
125
|
"dequal": "2.0.3",
|
|
126
126
|
"lucide-react": "^1.8.0",
|
|
@@ -1650,6 +1650,7 @@ function AttachmentChips({
|
|
|
1650
1650
|
<div className="flex flex-wrap gap-2 px-3 py-2">
|
|
1651
1651
|
{attachments.map((attachment) => {
|
|
1652
1652
|
const failed = attachment.status === "failed";
|
|
1653
|
+
const secureContextRequired = attachment.errorCode === "secure_context_required";
|
|
1653
1654
|
const statusText =
|
|
1654
1655
|
attachment.status === "uploading"
|
|
1655
1656
|
? messages.uploading
|
|
@@ -1660,10 +1661,15 @@ function AttachmentChips({
|
|
|
1660
1661
|
<div
|
|
1661
1662
|
key={attachment.id}
|
|
1662
1663
|
className={cn(
|
|
1663
|
-
"flex min-w-0
|
|
1664
|
+
"flex min-w-0 items-center gap-2 rounded-og-md border px-2 py-1.5 text-og-sm",
|
|
1664
1665
|
failed
|
|
1665
|
-
?
|
|
1666
|
-
|
|
1666
|
+
? cn(
|
|
1667
|
+
"border-og-status-failed/40 bg-og-status-failed/10",
|
|
1668
|
+
secureContextRequired
|
|
1669
|
+
? "max-w-full items-start sm:max-w-[32rem]"
|
|
1670
|
+
: "max-w-[240px]",
|
|
1671
|
+
)
|
|
1672
|
+
: "max-w-[240px] border-og-border bg-og-surface-2",
|
|
1667
1673
|
)}
|
|
1668
1674
|
>
|
|
1669
1675
|
{attachment.previewUrl && lightbox ? (
|
|
@@ -1706,7 +1712,11 @@ function AttachmentChips({
|
|
|
1706
1712
|
)}
|
|
1707
1713
|
<div className="min-w-0 flex-1">
|
|
1708
1714
|
<div className="truncate font-medium text-og-fg">{attachment.name}</div>
|
|
1709
|
-
{
|
|
1715
|
+
{secureContextRequired ? (
|
|
1716
|
+
<div className="break-words text-og-xs leading-4 text-og-status-failed">
|
|
1717
|
+
{statusText}
|
|
1718
|
+
</div>
|
|
1719
|
+
) : failed ? (
|
|
1710
1720
|
<ComposerTip tip={statusText}>
|
|
1711
1721
|
<div className="truncate text-og-xs text-og-status-failed">{statusText}</div>
|
|
1712
1722
|
</ComposerTip>
|
|
@@ -1717,7 +1727,7 @@ function AttachmentChips({
|
|
|
1717
1727
|
{attachment.status === "uploading" ? (
|
|
1718
1728
|
<LoaderCircleIcon className="size-3.5 shrink-0 animate-og-spin" />
|
|
1719
1729
|
) : null}
|
|
1720
|
-
{failed && onRetry ? (
|
|
1730
|
+
{failed && !secureContextRequired && onRetry ? (
|
|
1721
1731
|
<ComposerTip tip={messages.retryUpload}>
|
|
1722
1732
|
<button
|
|
1723
1733
|
type="button"
|
|
@@ -3581,6 +3581,7 @@ function AuthNeededRow({
|
|
|
3581
3581
|
? `${provider} tools unavailable`
|
|
3582
3582
|
: `${actionLabel} ${provider}`;
|
|
3583
3583
|
const reasonLine = recommendation?.rationale ?? authReasonLine(item.reason);
|
|
3584
|
+
const hostAuthorizationUrl = item.authoritySource === "host" ? item.authorizationUrl : null;
|
|
3584
3585
|
|
|
3585
3586
|
const start = async () => {
|
|
3586
3587
|
if (!onReconnect || busy) {
|
|
@@ -3623,7 +3624,20 @@ function AuthNeededRow({
|
|
|
3623
3624
|
) : null}
|
|
3624
3625
|
</div>
|
|
3625
3626
|
</div>
|
|
3626
|
-
{
|
|
3627
|
+
{hostAuthorizationUrl ? (
|
|
3628
|
+
<a
|
|
3629
|
+
href={hostAuthorizationUrl}
|
|
3630
|
+
rel="noreferrer"
|
|
3631
|
+
target="_blank"
|
|
3632
|
+
className={cn(
|
|
3633
|
+
PRIMARY_ACTION_CLASS,
|
|
3634
|
+
"transition-colors hover:bg-og-accent-strong pointer-coarse:min-h-9",
|
|
3635
|
+
)}
|
|
3636
|
+
>
|
|
3637
|
+
<RefreshCwIcon className="size-3.5" aria-hidden />
|
|
3638
|
+
{actionLabel}
|
|
3639
|
+
</a>
|
|
3640
|
+
) : item.authoritySource !== "host" && !unavailable && onReconnect ? (
|
|
3627
3641
|
<button
|
|
3628
3642
|
type="button"
|
|
3629
3643
|
onClick={() => void start()}
|
|
@@ -3636,7 +3650,7 @@ function AuthNeededRow({
|
|
|
3636
3650
|
<RefreshCwIcon className={cn("size-3.5", busy && "animate-og-spin")} aria-hidden />
|
|
3637
3651
|
{busy ? "Opening…" : actionLabel}
|
|
3638
3652
|
</button>
|
|
3639
|
-
) : !unavailable && item.authorizationUrl ? (
|
|
3653
|
+
) : item.authoritySource !== "host" && !unavailable && item.authorizationUrl ? (
|
|
3640
3654
|
<a
|
|
3641
3655
|
href={item.authorizationUrl}
|
|
3642
3656
|
rel="noreferrer"
|
|
@@ -26,6 +26,7 @@ import {
|
|
|
26
26
|
findPickerRow,
|
|
27
27
|
groupPickerRowsByBillingClass,
|
|
28
28
|
labelReasoningEffort,
|
|
29
|
+
payerSummaryForModel,
|
|
29
30
|
projectClientModelRows,
|
|
30
31
|
runnableLatencyModesForModel,
|
|
31
32
|
type PickerBillingClass,
|
|
@@ -68,7 +69,8 @@ export const defaultModelPolicyPickerMessages: ModelPolicyPickerMessages = {
|
|
|
68
69
|
external: "Provider terms and limits apply",
|
|
69
70
|
codex_subscription: "ChatGPT / Codex plan",
|
|
70
71
|
supergrok_subscription: "SuperGrok / xAI plan",
|
|
71
|
-
byok: "Billed to
|
|
72
|
+
byok: "Billed to the workspace provider account",
|
|
73
|
+
organization_byok: "Billed to the organization provider account",
|
|
72
74
|
},
|
|
73
75
|
};
|
|
74
76
|
|
|
@@ -158,7 +160,8 @@ export function BillingClassMark(props: {
|
|
|
158
160
|
external: "External provider",
|
|
159
161
|
codex_subscription: "Codex",
|
|
160
162
|
supergrok_subscription: "SuperGrok",
|
|
161
|
-
byok: "
|
|
163
|
+
byok: "Workspace provider account",
|
|
164
|
+
organization_byok: "Organization provider account",
|
|
162
165
|
};
|
|
163
166
|
const label = props["aria-label"] ?? labels[props.billingClass];
|
|
164
167
|
const accessibility =
|
|
@@ -195,6 +198,18 @@ function isCodexModel(model: ClientModel): boolean {
|
|
|
195
198
|
return model.id.startsWith("codex/") || model.source === "codex";
|
|
196
199
|
}
|
|
197
200
|
|
|
201
|
+
function billingClassForMissingSelection(modelId: string): PickerBillingClass {
|
|
202
|
+
if (modelId.startsWith("workspace-gateway/")) return "byok";
|
|
203
|
+
if (modelId.startsWith("workspace-openrouter/")) return "byok";
|
|
204
|
+
// A deployment OpenRouter ID does not encode its workspace-facing cost.
|
|
205
|
+
// Missing rows therefore use the credits-safe rail instead of falsely
|
|
206
|
+
// claiming that an unknown former selection was externally funded.
|
|
207
|
+
if (modelId.startsWith("openrouter/")) return "opengeni_credits";
|
|
208
|
+
if (modelId.startsWith("codex/")) return "codex_subscription";
|
|
209
|
+
if (modelId.startsWith("supergrok/")) return "supergrok_subscription";
|
|
210
|
+
return "opengeni_credits";
|
|
211
|
+
}
|
|
212
|
+
|
|
198
213
|
function applyCodexOnly(
|
|
199
214
|
rows: ClientPickerModelRow[],
|
|
200
215
|
codexOnly: boolean,
|
|
@@ -227,7 +242,11 @@ function defaultNavState(rows: ClientPickerModelRow[], modelId: string): PickerN
|
|
|
227
242
|
? { level: "models", rail: onlyRail, modelId: null }
|
|
228
243
|
: { level: "providers", rail: null, modelId: null };
|
|
229
244
|
}
|
|
230
|
-
return {
|
|
245
|
+
return {
|
|
246
|
+
level: "thinking",
|
|
247
|
+
rail: selected.billingClass,
|
|
248
|
+
modelId: selected.id,
|
|
249
|
+
};
|
|
231
250
|
}
|
|
232
251
|
|
|
233
252
|
function usePickerNavState(
|
|
@@ -352,7 +371,11 @@ export function ModelPolicyPickerMenu(
|
|
|
352
371
|
|
|
353
372
|
const effectiveNav =
|
|
354
373
|
nav.level === "providers" && groups.length === 1
|
|
355
|
-
? {
|
|
374
|
+
? {
|
|
375
|
+
level: "models" as const,
|
|
376
|
+
rail: groups[0]!.billingClass,
|
|
377
|
+
modelId: null,
|
|
378
|
+
}
|
|
356
379
|
: nav;
|
|
357
380
|
const activeGroup =
|
|
358
381
|
effectiveNav.rail === null
|
|
@@ -416,14 +439,21 @@ export function ModelPolicyPickerMenu(
|
|
|
416
439
|
<PickerNavRow
|
|
417
440
|
key={`${row.billingClass}:${row.id}`}
|
|
418
441
|
label={row.label}
|
|
419
|
-
hint={row.unavailableReason ??
|
|
442
|
+
hint={row.unavailableReason ?? payerSummaryForModel(row.catalog)}
|
|
420
443
|
disabled={!row.selectable}
|
|
421
444
|
title={row.unavailableReason ?? undefined}
|
|
422
445
|
active={row.id === props.model}
|
|
423
446
|
testId={`model-picker-choice-${row.id}`}
|
|
424
447
|
onClick={() => {
|
|
425
448
|
if (row.selectable) {
|
|
426
|
-
go(
|
|
449
|
+
go(
|
|
450
|
+
{
|
|
451
|
+
level: "thinking",
|
|
452
|
+
rail: row.billingClass,
|
|
453
|
+
modelId: row.id,
|
|
454
|
+
},
|
|
455
|
+
1,
|
|
456
|
+
);
|
|
427
457
|
}
|
|
428
458
|
}}
|
|
429
459
|
/>
|
|
@@ -605,10 +635,7 @@ export function ModelPolicyPicker(props: ModelPolicyPickerProps) {
|
|
|
605
635
|
)}
|
|
606
636
|
>
|
|
607
637
|
<BillingClassMark
|
|
608
|
-
billingClass={
|
|
609
|
-
selected?.billingClass ??
|
|
610
|
-
(props.model.startsWith("codex/") ? "codex_subscription" : "opengeni_credits")
|
|
611
|
-
}
|
|
638
|
+
billingClass={selected?.billingClass ?? billingClassForMissingSelection(props.model)}
|
|
612
639
|
className="text-og-fg"
|
|
613
640
|
/>
|
|
614
641
|
<span className="og-model-policy-label-full min-w-0 truncate font-medium text-og-fg max-sm:hidden">
|
|
@@ -424,6 +424,11 @@ export function useSandboxWorkspaceTabs(
|
|
|
424
424
|
const warmIntents =
|
|
425
425
|
storedWarmIntents.sessionId === sessionId ? storedWarmIntents : emptyWarmIntents(sessionId);
|
|
426
426
|
const { warmTerminal, warmFiles } = warmIntents;
|
|
427
|
+
// A host-requested file is itself explicit live-file intent. Derive the wake
|
|
428
|
+
// directly from the request instead of relying on a follow-up effect to copy
|
|
429
|
+
// it into local state; otherwise a missing or delayed effect leaves the file
|
|
430
|
+
// waiting for a sandbox that this hook never asks to start.
|
|
431
|
+
const filesWarmRequested = warmFiles || Boolean(requestedFilePath);
|
|
427
432
|
const requestWarmIntent = useCallback(
|
|
428
433
|
(intent: Exclude<keyof SessionWarmIntents, "sessionId">) => {
|
|
429
434
|
setStoredWarmIntents((previous) => {
|
|
@@ -433,10 +438,6 @@ export function useSandboxWorkspaceTabs(
|
|
|
433
438
|
},
|
|
434
439
|
[sessionId],
|
|
435
440
|
);
|
|
436
|
-
useEffect(() => {
|
|
437
|
-
if (requestedFilePath) requestWarmIntent("warmFiles");
|
|
438
|
-
}, [requestWarmIntent, requestedFilePath, requestedFileRequestId]);
|
|
439
|
-
|
|
440
441
|
// The session's machine fleet + the active-sandbox pointer. Drives the header
|
|
441
442
|
// chip (which machine + its connection state). Shares the session list poll.
|
|
442
443
|
const machines = useMachines({
|
|
@@ -457,7 +458,7 @@ export function useSandboxWorkspaceTabs(
|
|
|
457
458
|
attachTerminal: terminalEnabled && warmTerminal,
|
|
458
459
|
// Explicit live-file intent only — NOT "the Files tab is open". A cold edit
|
|
459
460
|
// or guarded-file open wakes the box; a glance at the tree/diff does not.
|
|
460
|
-
attachFiles: workspaceDataEnabled &&
|
|
461
|
+
attachFiles: workspaceDataEnabled && filesWarmRequested,
|
|
461
462
|
});
|
|
462
463
|
const capabilities = caps.capabilities;
|
|
463
464
|
const liveness = capabilities?.liveness;
|
|
@@ -665,7 +666,7 @@ export function useSandboxWorkspaceTabs(
|
|
|
665
666
|
capabilitiesState: caps.state,
|
|
666
667
|
activeMachineState: activeMachine?.state ?? null,
|
|
667
668
|
activeIsSelfhosted: activeMachine?.kind === "selfhosted",
|
|
668
|
-
wantsWarm: (terminalEnabled && warmTerminal) || (workspaceDataEnabled &&
|
|
669
|
+
wantsWarm: (terminalEnabled && warmTerminal) || (workspaceDataEnabled && filesWarmRequested),
|
|
669
670
|
capturedAt: captureState.capturedAt,
|
|
670
671
|
});
|
|
671
672
|
const workspaceWaking = chip.state === "waking";
|
|
@@ -26,6 +26,8 @@ export type FileAttachment = {
|
|
|
26
26
|
file?: FileAsset | undefined;
|
|
27
27
|
/** Object-URL for an inline preview; minted for `image/*` files only. */
|
|
28
28
|
previewUrl?: string | undefined;
|
|
29
|
+
/** Stable SDK failure code for UI behavior that must not parse error copy. */
|
|
30
|
+
errorCode?: "secure_context_required" | undefined;
|
|
29
31
|
error?: string | undefined;
|
|
30
32
|
};
|
|
31
33
|
|
|
@@ -76,6 +78,27 @@ export type UseFileAttachmentsResult = {
|
|
|
76
78
|
|
|
77
79
|
const isImage = (file: File): boolean => file.type.startsWith("image/");
|
|
78
80
|
|
|
81
|
+
let fallbackAttachmentId = 0;
|
|
82
|
+
|
|
83
|
+
function createAttachmentId(): string {
|
|
84
|
+
const cryptoSource = globalThis.crypto;
|
|
85
|
+
if (typeof cryptoSource?.randomUUID === "function") return cryptoSource.randomUUID();
|
|
86
|
+
fallbackAttachmentId += 1;
|
|
87
|
+
// This id is only a browser-local React key and retry lookup, never durable
|
|
88
|
+
// authority. Keep attachment tracking usable when HTTP withholds randomUUID
|
|
89
|
+
// or Web Crypto is unavailable so the SDK's typed failure reaches the card.
|
|
90
|
+
return `attachment:${Date.now().toString(36)}:${fallbackAttachmentId.toString(36)}`;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function secureContextRequiredErrorCode(error: unknown): "secure_context_required" | undefined {
|
|
94
|
+
return typeof error === "object" &&
|
|
95
|
+
error !== null &&
|
|
96
|
+
"code" in error &&
|
|
97
|
+
error.code === "secure_context_required"
|
|
98
|
+
? error.code
|
|
99
|
+
: undefined;
|
|
100
|
+
}
|
|
101
|
+
|
|
79
102
|
/**
|
|
80
103
|
* Upload-and-track state for files attached to the next message. Owns the
|
|
81
104
|
* full client-side upload layer: a per-file `uploading | ready | failed`
|
|
@@ -198,6 +221,7 @@ export function useFileAttachments(
|
|
|
198
221
|
name: asset.filename,
|
|
199
222
|
contentType: asset.contentType,
|
|
200
223
|
sizeBytes: asset.sizeBytes,
|
|
224
|
+
errorCode: undefined,
|
|
201
225
|
error: undefined,
|
|
202
226
|
}
|
|
203
227
|
: attachment,
|
|
@@ -212,6 +236,7 @@ export function useFileAttachments(
|
|
|
212
236
|
? {
|
|
213
237
|
...attachment,
|
|
214
238
|
status: "failed",
|
|
239
|
+
errorCode: secureContextRequiredErrorCode(error),
|
|
215
240
|
error: error instanceof Error ? error.message : String(error),
|
|
216
241
|
}
|
|
217
242
|
: attachment,
|
|
@@ -225,7 +250,7 @@ export function useFileAttachments(
|
|
|
225
250
|
const addFiles = useCallback(
|
|
226
251
|
(files: Iterable<File>) => {
|
|
227
252
|
for (const file of files) {
|
|
228
|
-
const id =
|
|
253
|
+
const id = createAttachmentId();
|
|
229
254
|
sources.current.set(id, file);
|
|
230
255
|
const previewUrl = isImage(file) ? URL.createObjectURL(file) : undefined;
|
|
231
256
|
if (previewUrl) previewUrls.current.set(id, previewUrl);
|
|
@@ -255,7 +280,7 @@ export function useFileAttachments(
|
|
|
255
280
|
setAttachments((current) =>
|
|
256
281
|
current.map((attachment) =>
|
|
257
282
|
attachment.id === id
|
|
258
|
-
? { ...attachment, status: "uploading", error: undefined }
|
|
283
|
+
? { ...attachment, status: "uploading", errorCode: undefined, error: undefined }
|
|
259
284
|
: attachment,
|
|
260
285
|
),
|
|
261
286
|
);
|
|
@@ -305,6 +330,7 @@ export function useFileAttachments(
|
|
|
305
330
|
sizeBytes: file.sizeBytes,
|
|
306
331
|
status: "ready",
|
|
307
332
|
file,
|
|
333
|
+
errorCode: undefined,
|
|
308
334
|
error: undefined,
|
|
309
335
|
}
|
|
310
336
|
: {
|