@opengeni/react 7.6.1 → 7.7.0-canary.37111190284001
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 +47 -2
- package/dist/{browser-viewer-5X47I6T2.js → browser-viewer-63TXMINB.js} +6 -5
- package/dist/{chunk-5EXGU7UF.js → chunk-55DDDVKU.js} +3 -3
- package/dist/{chunk-UINDK6C3.js → chunk-7CYYXWHU.js} +5 -8
- package/dist/chunk-7CYYXWHU.js.map +1 -0
- package/dist/{chunk-LZ3KHA55.js → chunk-A2L5N3EI.js} +332 -339
- package/dist/chunk-A2L5N3EI.js.map +1 -0
- package/dist/{chunk-KGENSZKS.js → chunk-AAG4R3QN.js} +4 -3
- package/dist/chunk-AAG4R3QN.js.map +1 -0
- package/dist/{chunk-Z3DISEQN.js → chunk-CWWKTNKK.js} +35 -18
- package/dist/chunk-CWWKTNKK.js.map +1 -0
- package/dist/{chunk-WZ55DVGK.js → chunk-ELCE5SXV.js} +8 -4
- package/dist/chunk-ELCE5SXV.js.map +1 -0
- package/dist/{chunk-YSMMDOOE.js → chunk-GISEVGS6.js} +10 -2
- package/dist/chunk-GISEVGS6.js.map +1 -0
- package/dist/{chunk-BI57CQUV.js → chunk-I7IWRQLN.js} +2 -2
- package/dist/{chunk-XA6R7DJQ.js → chunk-IQUCRAT7.js} +3 -3
- package/dist/{chunk-CHPKSLPN.js → chunk-NBZ64YY7.js} +4 -2
- package/dist/chunk-NBZ64YY7.js.map +1 -0
- package/dist/{chunk-DV2DBNPQ.js → chunk-Q5QBEZIQ.js} +48 -29
- package/dist/chunk-Q5QBEZIQ.js.map +1 -0
- package/dist/{chunk-FGBCYQOT.js → chunk-RDKBXMWX.js} +120 -74
- package/dist/chunk-RDKBXMWX.js.map +1 -0
- package/dist/{chunk-45GE35O7.js → chunk-TKB4SB44.js} +10 -2
- package/dist/chunk-TKB4SB44.js.map +1 -0
- package/dist/{chunk-YCGBREUV.js → chunk-TOAH2BKA.js} +6 -2
- package/dist/{chunk-YCGBREUV.js.map → chunk-TOAH2BKA.js.map} +1 -1
- package/dist/chunk-UGLEYEMP.js +33 -0
- package/dist/chunk-UGLEYEMP.js.map +1 -0
- package/dist/{chunk-UJRYEWZI.js → chunk-UTRTG6LH.js} +48 -8
- package/dist/chunk-UTRTG6LH.js.map +1 -0
- package/dist/{chunk-YRYF2GLG.js → chunk-WYVLAAJA.js} +39 -31
- package/dist/chunk-WYVLAAJA.js.map +1 -0
- package/dist/{chunk-VGIKFIO2.js → chunk-XOAMRDDK.js} +6 -3
- package/dist/chunk-XOAMRDDK.js.map +1 -0
- package/dist/{chunk-6NEZPF53.js → chunk-YN7I3STW.js} +153 -32
- package/dist/chunk-YN7I3STW.js.map +1 -0
- package/dist/{chunk-KZ73RL76.js → chunk-YQEAA6LT.js} +7 -3
- package/dist/chunk-YQEAA6LT.js.map +1 -0
- package/dist/client.d.ts +4 -1
- package/dist/commands/types.d.ts +2 -0
- package/dist/components/approval-surface.d.ts +15 -0
- package/dist/components/session-conversation.d.ts +3 -0
- package/dist/composer.js +8 -7
- package/dist/{computer-viewer-7GF35SNY.js → computer-viewer-X2MBRZY4.js} +5 -4
- package/dist/connect.js +3 -3
- package/dist/hooks/use-browser-session.d.ts +4 -0
- package/dist/hooks/use-composer.d.ts +2 -0
- package/dist/hooks/use-computer-session.d.ts +2 -0
- package/dist/hooks/use-file-attachments.d.ts +2 -0
- package/dist/hooks/use-session-events.d.ts +1 -1
- package/dist/hooks/use-workspace-sessions.d.ts +15 -5
- package/dist/index.d.ts +2 -2
- package/dist/index.js +28 -21
- package/dist/index.js.map +1 -1
- package/dist/interaction.d.ts +1 -1
- package/dist/interaction.js +6 -5
- package/dist/interaction.js.map +1 -1
- package/dist/{latest-question-3BJGKQQ6.js → latest-question-CNEZ5KAN.js} +3 -3
- package/dist/lib/error-message.d.ts +7 -0
- package/dist/lib/format.d.ts +27 -3
- package/dist/lib/interaction-errors.d.ts +3 -0
- package/dist/machines.js +5 -4
- package/dist/{model-policy-picker-menu-PUTA5JOB.js → model-policy-picker-menu-GN2LTB7Z.js} +4 -4
- package/dist/model-policy.js +1 -1
- package/dist/provider.d.ts +5 -1
- package/dist/{queue-surface-implementation-2CAQAIHM.js → queue-surface-implementation-HENWYFWA.js} +3 -2
- package/dist/{queue-surface-implementation-2CAQAIHM.js.map → queue-surface-implementation-HENWYFWA.js.map} +1 -1
- package/dist/realtime.js +2 -2
- package/dist/{sandbox-files-AEJTWGT4.js → sandbox-files-BBC47SLD.js} +4 -3
- package/dist/session-ui.d.ts +1 -1
- package/dist/session-ui.js +13 -12
- package/dist/session-ui.js.map +1 -1
- package/dist/session.js +7 -6
- package/dist/usage.js +2 -1
- package/dist/usage.js.map +1 -1
- package/package.json +3 -3
- package/src/client.ts +5 -1
- package/src/commands/registry.ts +18 -15
- package/src/commands/types.ts +2 -0
- package/src/components/approval-surface.tsx +166 -42
- package/src/components/browser-viewer.tsx +38 -9
- package/src/components/composer.tsx +9 -2
- package/src/components/computer-viewer.tsx +160 -30
- package/src/components/generated-video-player.tsx +4 -2
- package/src/components/human-input-form.tsx +18 -5
- package/src/components/message-timeline.tsx +120 -168
- package/src/components/model-policy-picker.tsx +2 -15
- package/src/components/open-geni-chat.tsx +11 -4
- package/src/components/queue-surface-state.tsx +3 -1
- package/src/components/session-chrome.tsx +5 -2
- package/src/components/session-commands-panel.tsx +3 -1
- package/src/components/session-conversation.tsx +10 -3
- package/src/components/session-list.tsx +7 -5
- package/src/components/session-mcp-capability-card.tsx +11 -1
- package/src/hooks/internal.ts +3 -2
- package/src/hooks/use-browser-session.ts +115 -79
- package/src/hooks/use-composer.ts +70 -31
- package/src/hooks/use-computer-session.ts +57 -6
- package/src/hooks/use-file-attachments.ts +16 -3
- package/src/hooks/use-goal.ts +2 -1
- package/src/hooks/use-session-capabilities.ts +3 -2
- package/src/hooks/use-session-events.ts +15 -8
- package/src/hooks/use-slash-commands.ts +6 -11
- package/src/hooks/use-turn-queue.ts +2 -1
- package/src/hooks/use-workspace-sessions.ts +40 -7
- package/src/index.ts +7 -2
- package/src/interaction.ts +1 -0
- package/src/lib/error-message.ts +37 -0
- package/src/lib/format.ts +73 -9
- package/src/lib/interaction-errors.ts +15 -0
- package/src/model-policy.ts +6 -1
- package/src/provider.tsx +12 -4
- package/src/session-ui.ts +1 -1
- package/src/timeline/projection.ts +12 -1
- package/styles/compiled.css +14 -30
- package/dist/chunk-45GE35O7.js.map +0 -1
- package/dist/chunk-6NEZPF53.js.map +0 -1
- package/dist/chunk-CHPKSLPN.js.map +0 -1
- package/dist/chunk-DV2DBNPQ.js.map +0 -1
- package/dist/chunk-FGBCYQOT.js.map +0 -1
- package/dist/chunk-KGENSZKS.js.map +0 -1
- package/dist/chunk-KZ73RL76.js.map +0 -1
- package/dist/chunk-LZ3KHA55.js.map +0 -1
- package/dist/chunk-UINDK6C3.js.map +0 -1
- package/dist/chunk-UJRYEWZI.js.map +0 -1
- package/dist/chunk-VGIKFIO2.js.map +0 -1
- package/dist/chunk-WZ55DVGK.js.map +0 -1
- package/dist/chunk-YRYF2GLG.js.map +0 -1
- package/dist/chunk-YSMMDOOE.js.map +0 -1
- package/dist/chunk-Z3DISEQN.js.map +0 -1
- package/dist/components/timeline-pill-geometry.d.ts +0 -8
- package/dist/components/timeline-pill-placement.d.ts +0 -24
- package/dist/timeline-pill-geometry-Z23V243M.js +0 -110
- package/dist/timeline-pill-geometry-Z23V243M.js.map +0 -1
- package/src/components/timeline-pill-geometry.ts +0 -169
- package/src/components/timeline-pill-placement.ts +0 -99
- /package/dist/{browser-viewer-5X47I6T2.js.map → browser-viewer-63TXMINB.js.map} +0 -0
- /package/dist/{chunk-5EXGU7UF.js.map → chunk-55DDDVKU.js.map} +0 -0
- /package/dist/{chunk-BI57CQUV.js.map → chunk-I7IWRQLN.js.map} +0 -0
- /package/dist/{chunk-XA6R7DJQ.js.map → chunk-IQUCRAT7.js.map} +0 -0
- /package/dist/{computer-viewer-7GF35SNY.js.map → computer-viewer-X2MBRZY4.js.map} +0 -0
- /package/dist/{latest-question-3BJGKQQ6.js.map → latest-question-CNEZ5KAN.js.map} +0 -0
- /package/dist/{model-policy-picker-menu-PUTA5JOB.js.map → model-policy-picker-menu-GN2LTB7Z.js.map} +0 -0
- /package/dist/{sandbox-files-AEJTWGT4.js.map → sandbox-files-BBC47SLD.js.map} +0 -0
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
usePageLiveActivity
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-YQEAA6LT.js";
|
|
4
|
+
import {
|
|
5
|
+
ErrorMessageContext
|
|
6
|
+
} from "./chunk-UGLEYEMP.js";
|
|
4
7
|
import {
|
|
5
8
|
OpenGeniContext
|
|
6
9
|
} from "./chunk-NEXLLTP4.js";
|
|
@@ -19,6 +22,7 @@ function OpenGeniProvider({
|
|
|
19
22
|
onWorkspaceControlEvent,
|
|
20
23
|
onWorkspaceInteractionEvent,
|
|
21
24
|
reloadOnApiContractChange = false,
|
|
25
|
+
formatError,
|
|
22
26
|
children
|
|
23
27
|
}) {
|
|
24
28
|
const [workspaceControlEvent, setWorkspaceControlEvent] = useState(
|
|
@@ -292,10 +296,10 @@ function OpenGeniProvider({
|
|
|
292
296
|
reconcileSession
|
|
293
297
|
]
|
|
294
298
|
);
|
|
295
|
-
return /* @__PURE__ */ jsxs(OpenGeniContext.Provider, { value, children: [
|
|
299
|
+
return /* @__PURE__ */ jsx(ErrorMessageContext.Provider, { value: formatError, children: /* @__PURE__ */ jsxs(OpenGeniContext.Provider, { value, children: [
|
|
296
300
|
children,
|
|
297
301
|
contractMismatch ? /* @__PURE__ */ jsx(ApiContractMismatchScreen, { mismatch: contractMismatch }) : null
|
|
298
|
-
] });
|
|
302
|
+
] }) });
|
|
299
303
|
}
|
|
300
304
|
function withTimeout(promise, timeoutMs, label) {
|
|
301
305
|
let timer;
|
|
@@ -352,4 +356,4 @@ function reloadForContractMismatchOnce(mismatch) {
|
|
|
352
356
|
export {
|
|
353
357
|
OpenGeniProvider
|
|
354
358
|
};
|
|
355
|
-
//# sourceMappingURL=chunk-
|
|
359
|
+
//# sourceMappingURL=chunk-ELCE5SXV.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/provider.tsx"],"sourcesContent":["import {\n OpenGeniApiContractMismatchError,\n OPENGENI_API_CONTRACT_REVISION,\n type StreamConnectionState,\n type WorkspaceControlEvent,\n type WorkspaceInteractionRevisionEvent,\n} from \"@opengeni/sdk\";\nimport { useCallback, useEffect, useMemo, useRef, useState, type ReactNode } from \"react\";\nimport type { SessionClientLike } from \"./client\";\nimport { usePageLiveActivity } from \"./hooks/internal\";\nimport { OpenGeniContext } from \"./session-context\";\nimport { ErrorMessageContext, type ErrorMessageFormatter } from \"./lib/error-message\";\n\nexport type { ErrorMessageFormatter } from \"./lib/error-message\";\n\nexport { useOpenGeni, useOpenGeniClient } from \"./session-context\";\nexport type { ClientOverride, OpenGeniContextValue } from \"./session-context\";\nconst CONTRACT_RELOAD_STORAGE_PREFIX = \"opengeni.reloadForApiContract:\";\n\nexport type OpenGeniProviderProps = {\n client: SessionClientLike;\n workspaceId: string;\n onWorkspaceControlEvent?: ((event: WorkspaceControlEvent) => void) | undefined;\n onWorkspaceInteractionEvent?: ((event: WorkspaceInteractionRevisionEvent) => void) | undefined;\n /**\n * Stale-tab protection for the stock OpenGeni web app: when the server's API\n * contract revision differs from this bundle's, cover the page and reload it\n * once. Off by default, because an embedded product's page must never be\n * blocked or reloaded when OpenGeni deploys; there a mismatch is ignored and\n * the SDK's tolerant-reader compatibility applies.\n */\n reloadOnApiContractChange?: boolean | undefined;\n /** Host-owned error copy. Receives the original diagnostic error and neutral default copy. */\n formatError?: ErrorMessageFormatter | undefined;\n children?: ReactNode;\n};\n\n/**\n * Supplies the OpenGeni client + workspace to all hooks below it. Hooks also\n * accept `{ client, workspaceId }` overrides per call for multi-workspace UIs.\n */\nexport function OpenGeniProvider({\n client,\n workspaceId,\n onWorkspaceControlEvent,\n onWorkspaceInteractionEvent,\n reloadOnApiContractChange = false,\n formatError,\n children,\n}: OpenGeniProviderProps) {\n const [workspaceControlEvent, setWorkspaceControlEvent] = useState<WorkspaceControlEvent | null>(\n null,\n );\n const [workspaceControlConnectionState, setWorkspaceControlConnectionState] = useState<\n StreamConnectionState | \"idle\" | \"error\"\n >(\"idle\");\n const [workspaceInteractionEvent, setWorkspaceInteractionEvent] =\n useState<WorkspaceInteractionRevisionEvent | null>(null);\n const [workspaceInteractionConnectionState, setWorkspaceInteractionConnectionState] = useState<\n StreamConnectionState | \"idle\" | \"error\"\n >(\"idle\");\n const [contractMismatch, setContractMismatch] = useState<OpenGeniApiContractMismatchError | null>(\n null,\n );\n const callbackRef = useRef(onWorkspaceControlEvent);\n const interactionCallbackRef = useRef(onWorkspaceInteractionEvent);\n const reconcilersRef = useRef(new Map<string, Map<string, () => Promise<void>>>());\n const reconcileInFlightRef = useRef(new Map<string, Promise<void>>());\n const workspaceControlSequencesRef = useRef(new Map<string, number>());\n const workspaceInteractionSequencesRef = useRef(new Map<string, number>());\n const pageLive = usePageLiveActivity();\n const supportsWorkspaceLiveStream =\n \"streamWorkspaceLiveEvents\" in client && typeof client.streamWorkspaceLiveEvents === \"function\";\n callbackRef.current = onWorkspaceControlEvent;\n interactionCallbackRef.current = onWorkspaceInteractionEvent;\n\n const strictContract = reloadOnApiContractChange;\n const verifyApiContract = useCallback(async (): Promise<void> => {\n try {\n const config = await client.getClientConfig();\n if (strictContract && config.apiContractRevision !== OPENGENI_API_CONTRACT_REVISION) {\n throw new OpenGeniApiContractMismatchError(\n OPENGENI_API_CONTRACT_REVISION,\n String(config.apiContractRevision || \"(missing)\"),\n );\n }\n } catch (error) {\n if (error instanceof OpenGeniApiContractMismatchError) {\n // Embedded hosts: a revision difference is not a reason to block or reload the page.\n if (!strictContract) return;\n setContractMismatch(error);\n reloadForContractMismatchOnce(error);\n }\n throw error;\n }\n }, [client, strictContract]);\n\n const registerSessionReconciler = useMemo(\n () =>\n (sessionId: string, key: string, reconcile: () => Promise<void>): (() => void) => {\n const sessionReconcilers = reconcilersRef.current.get(sessionId) ?? new Map();\n sessionReconcilers.set(key, reconcile);\n reconcilersRef.current.set(sessionId, sessionReconcilers);\n return () => {\n const current = reconcilersRef.current.get(sessionId);\n current?.delete(key);\n if (current?.size === 0) reconcilersRef.current.delete(sessionId);\n };\n },\n [],\n );\n const reconcileSession = useMemo(\n () =>\n (sessionId: string): Promise<void> => {\n const existing = reconcileInFlightRef.current.get(sessionId);\n if (existing) return existing;\n // This read also crosses the exact API-contract handshake before stale\n // state can be presented as live after a deployment.\n const promise = (async () => {\n await withTimeout(verifyApiContract(), 10_000, \"API contract reconciliation\");\n const callbacks = [...(reconcilersRef.current.get(sessionId)?.values() ?? [])];\n await withTimeout(\n Promise.all(callbacks.map((reconcile) => reconcile())).then(() => undefined),\n 15_000,\n \"session reconciliation\",\n );\n })().finally(() => {\n if (reconcileInFlightRef.current.get(sessionId) === promise) {\n reconcileInFlightRef.current.delete(sessionId);\n }\n });\n reconcileInFlightRef.current.set(sessionId, promise);\n return promise;\n },\n [verifyApiContract],\n );\n\n useEffect(() => {\n if (!supportsWorkspaceLiveStream) return;\n const openLiveStream = client.streamWorkspaceLiveEvents!;\n setWorkspaceControlEvent(null);\n setWorkspaceInteractionEvent(null);\n if (!pageLive) {\n setWorkspaceControlConnectionState(\"idle\");\n setWorkspaceInteractionConnectionState(\"idle\");\n return;\n }\n const controller = new AbortController();\n const setConnectionState = (state: StreamConnectionState) => {\n setWorkspaceControlConnectionState(state);\n setWorkspaceInteractionConnectionState(state);\n };\n setConnectionState(\"connecting\");\n void (async () => {\n try {\n await verifyApiContract();\n const workspace = await client.getWorkspace(workspaceId);\n const controlAfter = Math.max(\n workspaceControlSequencesRef.current.get(workspaceId) ?? 0,\n workspace.inferenceControl.revision,\n );\n const interactionAfter = workspaceInteractionSequencesRef.current.get(workspaceId) ?? 0;\n workspaceControlSequencesRef.current.set(workspaceId, controlAfter);\n const stream = openLiveStream.call(client, workspaceId, {\n controlAfter,\n interactionAfter,\n signal: controller.signal,\n onStateChange: setConnectionState,\n });\n for await (const event of stream) {\n if (controller.signal.aborted) return;\n if (event.type === \"workspace.control.changed\") {\n workspaceControlSequencesRef.current.set(\n workspaceId,\n Math.max(workspaceControlSequencesRef.current.get(workspaceId) ?? 0, event.sequence),\n );\n setWorkspaceControlEvent((current) =>\n !current || event.sequence > current.sequence ? event : current,\n );\n callbackRef.current?.(event);\n } else {\n workspaceInteractionSequencesRef.current.set(\n workspaceId,\n Math.max(\n workspaceInteractionSequencesRef.current.get(workspaceId) ?? 0,\n event.sequence,\n ),\n );\n setWorkspaceInteractionEvent((current) =>\n !current || event.sequence > current.sequence ? event : current,\n );\n interactionCallbackRef.current?.(event);\n }\n }\n } catch (error) {\n if (strictContract && error instanceof OpenGeniApiContractMismatchError) {\n setContractMismatch(error);\n reloadForContractMismatchOnce(error);\n }\n if (!controller.signal.aborted) {\n setWorkspaceControlConnectionState(\"error\");\n setWorkspaceInteractionConnectionState(\"error\");\n }\n }\n })();\n return () => controller.abort();\n }, [\n client,\n pageLive,\n strictContract,\n supportsWorkspaceLiveStream,\n verifyApiContract,\n workspaceId,\n ]);\n\n useEffect(() => {\n if (supportsWorkspaceLiveStream) return;\n if (!pageLive) {\n setWorkspaceControlConnectionState(\"idle\");\n return;\n }\n const controller = new AbortController();\n setWorkspaceControlConnectionState(\"connecting\");\n void (async () => {\n try {\n await verifyApiContract();\n const workspace = await client.getWorkspace(workspaceId);\n const resumeAfter = Math.max(\n workspaceControlSequencesRef.current.get(workspaceId) ?? 0,\n workspace.inferenceControl.revision,\n );\n workspaceControlSequencesRef.current.set(workspaceId, resumeAfter);\n const stream = client.streamWorkspaceControlEvents(workspaceId, {\n after: resumeAfter,\n signal: controller.signal,\n onStateChange: setWorkspaceControlConnectionState,\n });\n for await (const event of stream) {\n if (controller.signal.aborted) return;\n workspaceControlSequencesRef.current.set(\n workspaceId,\n Math.max(workspaceControlSequencesRef.current.get(workspaceId) ?? 0, event.sequence),\n );\n setWorkspaceControlEvent((current) =>\n !current || event.sequence > current.sequence ? event : current,\n );\n callbackRef.current?.(event);\n }\n } catch (error) {\n if (strictContract && error instanceof OpenGeniApiContractMismatchError) {\n setContractMismatch(error);\n reloadForContractMismatchOnce(error);\n }\n if (!controller.signal.aborted) setWorkspaceControlConnectionState(\"error\");\n }\n })();\n return () => controller.abort();\n }, [\n client,\n pageLive,\n strictContract,\n supportsWorkspaceLiveStream,\n verifyApiContract,\n workspaceId,\n ]);\n\n useEffect(() => {\n if (supportsWorkspaceLiveStream) return;\n setWorkspaceInteractionEvent(null);\n if (!pageLive) {\n setWorkspaceInteractionConnectionState(\"idle\");\n return;\n }\n const controller = new AbortController();\n setWorkspaceInteractionConnectionState(\"connecting\");\n void (async () => {\n try {\n await verifyApiContract();\n const resumeAfter = workspaceInteractionSequencesRef.current.get(workspaceId) ?? 0;\n const stream = client.streamWorkspaceInteractionRevisions(workspaceId, {\n after: resumeAfter,\n signal: controller.signal,\n onStateChange: setWorkspaceInteractionConnectionState,\n });\n for await (const event of stream) {\n if (controller.signal.aborted) return;\n workspaceInteractionSequencesRef.current.set(\n workspaceId,\n Math.max(\n workspaceInteractionSequencesRef.current.get(workspaceId) ?? 0,\n event.sequence,\n ),\n );\n setWorkspaceInteractionEvent((current) =>\n !current || event.sequence > current.sequence ? event : current,\n );\n interactionCallbackRef.current?.(event);\n }\n } catch (error) {\n if (strictContract && error instanceof OpenGeniApiContractMismatchError) {\n setContractMismatch(error);\n reloadForContractMismatchOnce(error);\n }\n if (!controller.signal.aborted) setWorkspaceInteractionConnectionState(\"error\");\n }\n })();\n return () => controller.abort();\n }, [\n client,\n pageLive,\n strictContract,\n supportsWorkspaceLiveStream,\n verifyApiContract,\n workspaceId,\n ]);\n\n const value = useMemo(\n () => ({\n client,\n workspaceId,\n workspaceControlEvent,\n workspaceControlConnectionState,\n workspaceInteractionEvent,\n workspaceInteractionConnectionState,\n registerSessionReconciler,\n reconcileSession,\n }),\n [\n client,\n workspaceId,\n workspaceControlEvent,\n workspaceControlConnectionState,\n workspaceInteractionEvent,\n workspaceInteractionConnectionState,\n registerSessionReconciler,\n reconcileSession,\n ],\n );\n return (\n <ErrorMessageContext.Provider value={formatError}>\n <OpenGeniContext.Provider value={value}>\n {children}\n {contractMismatch ? <ApiContractMismatchScreen mismatch={contractMismatch} /> : null}\n </OpenGeniContext.Provider>\n </ErrorMessageContext.Provider>\n );\n}\n\nfunction withTimeout<T>(promise: Promise<T>, timeoutMs: number, label: string): Promise<T> {\n let timer: ReturnType<typeof setTimeout> | undefined;\n return Promise.race([\n promise,\n new Promise<T>((_resolve, reject) => {\n timer = setTimeout(\n () => reject(new Error(`${label} timed out after ${timeoutMs}ms`)),\n timeoutMs,\n );\n }),\n ]).finally(() => {\n if (timer !== undefined) clearTimeout(timer);\n });\n}\n\nfunction ApiContractMismatchScreen({ mismatch }: { mismatch: OpenGeniApiContractMismatchError }) {\n return (\n <div\n className=\"og-root fixed inset-0 z-[2147483647] grid place-items-center bg-og-bg/95 p-6 backdrop-blur-sm\"\n role=\"alert\"\n aria-live=\"assertive\"\n data-opengeni-api-contract-mismatch\n >\n <div className=\"w-full max-w-md rounded-xl border border-og-border bg-og-surface p-6 shadow-2xl\">\n <p className=\"text-og-menu font-semibold text-og-fg\">Opengeni updated</p>\n <p className=\"mt-2 text-og-menu leading-6 text-og-muted\">\n This tab cannot safely continue with the new server version. Reload it before sending or\n controlling work.\n </p>\n <p className=\"mt-3 font-mono text-og-control text-og-subtle\">\n Client {mismatch.expected} · API {mismatch.actual}\n </p>\n <button\n type=\"button\"\n className=\"mt-5 inline-flex h-9 items-center rounded-md border border-og-primary-border bg-og-primary text-og-primary-fg hover:bg-og-primary-hover px-3 text-og-menu font-medium\"\n onClick={() => window.location.reload()}\n >\n Reload now\n </button>\n </div>\n </div>\n );\n}\n\nfunction reloadForContractMismatchOnce(mismatch: OpenGeniApiContractMismatchError): void {\n if (typeof window === \"undefined\" || typeof sessionStorage === \"undefined\") return;\n const key = `${CONTRACT_RELOAD_STORAGE_PREFIX}${mismatch.actual}`;\n if (sessionStorage.getItem(key) === OPENGENI_API_CONTRACT_REVISION) return;\n sessionStorage.setItem(key, OPENGENI_API_CONTRACT_REVISION);\n window.setTimeout(() => window.location.reload(), 150);\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
usePageLiveActivity
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-YQEAA6LT.js";
|
|
4
4
|
import {
|
|
5
5
|
useEmbeddedInterventions
|
|
6
6
|
} from "./chunk-NEXLLTP4.js";
|
|
@@ -225,6 +225,13 @@ function asError(cause) {
|
|
|
225
225
|
|
|
226
226
|
// src/lib/interaction-errors.ts
|
|
227
227
|
import { OpenGeniApiError } from "@opengeni/sdk";
|
|
228
|
+
import { interactionControlFailureFromError } from "@opengeni/sdk/interaction";
|
|
229
|
+
function isInteractionControlUnavailable(error, request = "control") {
|
|
230
|
+
if (!(error instanceof OpenGeniApiError)) return false;
|
|
231
|
+
const failure = interactionControlFailureFromError(error);
|
|
232
|
+
if (failure) return failure.code === "agent_offline" || failure.code === "draining";
|
|
233
|
+
return request === "control" && !error.outcomeUnknown && (error.status >= 500 || error.status === 0);
|
|
234
|
+
}
|
|
228
235
|
function isNonRetryableInteractionError(error) {
|
|
229
236
|
return error instanceof OpenGeniApiError && error.retryable === false;
|
|
230
237
|
}
|
|
@@ -322,8 +329,9 @@ function interventionTitle(kind) {
|
|
|
322
329
|
export {
|
|
323
330
|
useInteractionInvalidation,
|
|
324
331
|
useInteractionInterventions,
|
|
332
|
+
isInteractionControlUnavailable,
|
|
325
333
|
isNonRetryableInteractionError,
|
|
326
334
|
isSourcePlacementChangedError,
|
|
327
335
|
InteractionInterventionBanner
|
|
328
336
|
};
|
|
329
|
-
//# sourceMappingURL=chunk-
|
|
337
|
+
//# sourceMappingURL=chunk-GISEVGS6.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/hooks/use-interaction-interventions.ts","../src/hooks/use-interaction-invalidation.ts","../src/lib/interaction-errors.ts","../src/components/interaction-intervention-banner.tsx"],"sourcesContent":["import type {\n CreateInteractionInterventionRequest,\n InteractionIntervention,\n InteractionInterventionMutationResponse,\n ResolveInteractionInterventionRequest,\n} from \"@opengeni/sdk/interaction\";\nimport { useCallback, useEffect, useRef, useState } from \"react\";\nimport type { EmbeddedInterventionClientLike } from \"../client\";\nimport {\n type EmbeddedInterventionClientOverride,\n useEmbeddedInterventions,\n} from \"../session-context\";\nimport { useInteractionInvalidation } from \"./use-interaction-invalidation\";\n\nexport type UseInteractionInterventionsOptions = EmbeddedInterventionClientOverride & {\n enabled?: boolean | undefined;\n resourceKind?: \"browser_session\" | \"computer_session\" | undefined;\n resourceId?: string | null | undefined;\n includeSettled?: boolean | undefined;\n pollIntervalMs?: number | undefined;\n};\n\nexport type UseInteractionInterventionsResult = {\n interventions: InteractionIntervention[];\n loading: boolean;\n refreshing: boolean;\n mutating: boolean;\n error: Error | null;\n refresh: () => Promise<void>;\n create: (\n request: Omit<CreateInteractionInterventionRequest, \"operationId\"> & {\n operationId?: string;\n },\n ) => Promise<InteractionInterventionMutationResponse>;\n resolve: (\n interventionId: string,\n request: Omit<ResolveInteractionInterventionRequest, \"operationId\"> & {\n operationId?: string;\n },\n ) => Promise<InteractionInterventionMutationResponse>;\n};\n\n/** Human-action bridge shared by Browser and Computer surfaces. */\nexport function useInteractionInterventions(\n options: UseInteractionInterventionsOptions = {},\n): UseInteractionInterventionsResult {\n const { client, workspaceId, workspaceInteractionEvent, workspaceInteractionConnectionState } =\n useEmbeddedInterventions(options);\n const enabled = (options.enabled ?? true) && options.resourceId !== null;\n const resourceKind = options.resourceKind;\n const resourceId = options.resourceId ?? undefined;\n const includeSettled = options.includeSettled ?? false;\n const key = `${workspaceId}:${resourceKind ?? \"*\"}:${resourceId ?? \"*\"}:${includeSettled}`;\n const [state, setState] = useState(() => emptyState(key, enabled));\n const [mutationCount, setMutationCount] = useState(0);\n const requestRef = useRef<{ id: number; controller: AbortController | null }>({\n id: 0,\n controller: null,\n });\n const mountedRef = useRef(true);\n const visible = state.key === key ? state : emptyState(key, enabled);\n\n const cancelLoad = useCallback(() => {\n requestRef.current.controller?.abort();\n requestRef.current = { id: requestRef.current.id + 1, controller: null };\n }, []);\n\n const load = useCallback(\n async (foreground: boolean, replace = true): Promise<void> => {\n if (!enabled) return;\n if (requestRef.current.controller && !replace) return;\n const id = requestRef.current.id + 1;\n requestRef.current.controller?.abort();\n const controller = new AbortController();\n requestRef.current = { id, controller };\n if (foreground || replace) {\n setState((current) => ({\n ...(current.key === key ? current : emptyState(key, true)),\n loading: foreground,\n refreshing: !foreground,\n error: foreground ? null : current.error,\n }));\n }\n try {\n const response = await client.listInteractionInterventions(workspaceId, {\n ...(resourceKind ? { resourceKind } : {}),\n ...(resourceId ? { resourceId } : {}),\n includeSettled,\n signal: controller.signal,\n });\n if (!mountedRef.current || requestRef.current.id !== id) return;\n setState({\n key,\n interventions: sortInterventions(response.interventions),\n loading: false,\n refreshing: false,\n error: null,\n });\n } catch (cause) {\n if (controller.signal.aborted || !mountedRef.current || requestRef.current.id !== id)\n return;\n setState((current) => ({\n ...(current.key === key ? current : emptyState(key, true)),\n loading: false,\n refreshing: false,\n error: asError(cause),\n }));\n } finally {\n if (requestRef.current.id === id) requestRef.current = { id, controller: null };\n }\n },\n [client, enabled, includeSettled, key, resourceId, resourceKind, workspaceId],\n );\n\n const refresh = useCallback(async () => await load(false, true), [load]);\n\n useEffect(() => {\n mountedRef.current = true;\n if (!enabled) {\n cancelLoad();\n setState(emptyState(key, false));\n return;\n }\n void load(true);\n return () => {\n mountedRef.current = false;\n cancelLoad();\n };\n }, [cancelLoad, enabled, key, load]);\n\n useInteractionInvalidation({\n workspaceId,\n key,\n enabled,\n event: workspaceInteractionEvent,\n connectionState: workspaceInteractionConnectionState,\n refresh,\n fallbackPollIntervalMs: options.pollIntervalMs ?? 2_000,\n });\n\n const mutate = useCallback(async <T>(operation: () => Promise<T>): Promise<T> => {\n setMutationCount((count) => count + 1);\n try {\n return await operation();\n } finally {\n if (mountedRef.current) setMutationCount((count) => Math.max(0, count - 1));\n }\n }, []);\n\n const merge = useCallback(\n (intervention: InteractionIntervention) => {\n setState((current) => {\n if (current.key !== key) return current;\n const interventions = current.interventions.filter(\n (candidate) => candidate.id !== intervention.id,\n );\n const matches =\n (!resourceKind || intervention.resourceKind === resourceKind) &&\n (!resourceId || intervention.resourceId === resourceId);\n if (matches && (includeSettled || intervention.status === \"open\")) {\n interventions.push(intervention);\n }\n return {\n ...current,\n interventions: sortInterventions(interventions),\n error: null,\n };\n });\n },\n [includeSettled, key, resourceId, resourceKind],\n );\n\n const create = useCallback(\n async (\n request: Omit<CreateInteractionInterventionRequest, \"operationId\"> & {\n operationId?: string;\n },\n ): Promise<InteractionInterventionMutationResponse> =>\n await mutate(async () => {\n const response = await client.createInteractionIntervention(workspaceId, {\n ...request,\n operationId: request.operationId ?? crypto.randomUUID(),\n });\n merge(response.intervention);\n return response;\n }),\n [client, merge, mutate, workspaceId],\n );\n\n const resolve = useCallback(\n async (\n interventionId: string,\n request: Omit<ResolveInteractionInterventionRequest, \"operationId\"> & {\n operationId?: string;\n },\n ): Promise<InteractionInterventionMutationResponse> =>\n await mutate(async () => {\n const response = await client.resolveInteractionIntervention(workspaceId, interventionId, {\n ...request,\n operationId: request.operationId ?? crypto.randomUUID(),\n });\n merge(response.intervention);\n return response;\n }),\n [client, merge, mutate, workspaceId],\n );\n\n return {\n interventions: visible.interventions,\n loading: visible.loading,\n refreshing: visible.refreshing,\n mutating: mutationCount > 0,\n error: visible.error,\n refresh,\n create,\n resolve,\n };\n}\n\nfunction emptyState(key: string, loading: boolean) {\n return {\n key,\n interventions: [] as InteractionIntervention[],\n loading,\n refreshing: false,\n error: null as Error | null,\n };\n}\n\nfunction sortInterventions(\n interventions: readonly InteractionIntervention[],\n): InteractionIntervention[] {\n return [...interventions].sort(\n (left, right) => Date.parse(right.updatedAt) - Date.parse(left.updatedAt),\n );\n}\n\nfunction asError(cause: unknown): Error {\n return cause instanceof Error ? cause : new Error(String(cause));\n}\n\nexport type InteractionInterventionsClient = EmbeddedInterventionClientLike;\n","import type { StreamConnectionState, WorkspaceInteractionRevisionEvent } from \"@opengeni/sdk\";\nimport { useEffect, useRef } from \"react\";\nimport { usePageLiveActivity } from \"./internal\";\n\nexport type InteractionInvalidationOptions = {\n workspaceId: string;\n /** Distinguishes filtered projections of the same workspace catalog. */\n key: string;\n enabled: boolean;\n event: WorkspaceInteractionRevisionEvent | null;\n connectionState: StreamConnectionState | \"idle\" | \"error\";\n /** Catalog responses carry the workspace revision; live-state lists do not. */\n knownRevision?: number | undefined;\n refresh: () => Promise<void>;\n fallbackPollIntervalMs?: number | undefined;\n};\n\n/**\n * Refetch authoritative lists on the shared latest-revision stream. Poll only\n * while that stream is unavailable, preserving standalone-hook degradation\n * without multiplying healthy-provider traffic.\n */\nexport function useInteractionInvalidation(options: InteractionInvalidationOptions): void {\n const {\n workspaceId,\n key,\n enabled,\n event,\n connectionState,\n knownRevision,\n refresh,\n fallbackPollIntervalMs,\n } = options;\n const pageLive = usePageLiveActivity();\n const lastUnversionedEvent = useRef<{ key: string; sequence: number }>({\n key,\n sequence: 0,\n });\n const eventSequence = event?.workspaceId === workspaceId ? event.sequence : 0;\n\n useEffect(() => {\n if (!enabled || eventSequence <= 0) return;\n if (knownRevision !== undefined) {\n if (eventSequence > knownRevision) void refresh();\n return;\n }\n if (lastUnversionedEvent.current.key !== key) {\n lastUnversionedEvent.current = { key, sequence: 0 };\n }\n if (eventSequence <= lastUnversionedEvent.current.sequence) return;\n lastUnversionedEvent.current = { key, sequence: eventSequence };\n void refresh();\n }, [enabled, eventSequence, key, knownRevision, refresh]);\n\n useEffect(() => {\n if (!enabled || !pageLive || connectionState === \"live\") return;\n const interval = boundedPollInterval(fallbackPollIntervalMs ?? 3_000);\n const timer = setInterval(() => void refresh(), interval);\n return () => clearInterval(timer);\n }, [connectionState, enabled, fallbackPollIntervalMs, pageLive, refresh]);\n}\n\nfunction boundedPollInterval(value: number): number {\n if (!Number.isFinite(value)) return 3_000;\n return Math.min(60_000, Math.max(1_000, Math.floor(value)));\n}\n","import { OpenGeniApiError } from \"@opengeni/sdk\";\nimport { interactionControlFailureFromError } from \"@opengeni/sdk/interaction\";\n\n/** Media can remain live when the independent control service is unavailable.\n * A target refusal or an uncertain mutation does not establish a service outage. */\nexport function isInteractionControlUnavailable(\n error: unknown,\n request: \"control\" | \"observation\" = \"control\",\n): error is OpenGeniApiError {\n if (!(error instanceof OpenGeniApiError)) return false;\n const failure = interactionControlFailureFromError(error);\n if (failure) return failure.code === \"agent_offline\" || failure.code === \"draining\";\n return (\n request === \"control\" && !error.outcomeUnknown && (error.status >= 500 || error.status === 0)\n );\n}\n\nexport function isNonRetryableInteractionError(error: unknown): error is OpenGeniApiError {\n return error instanceof OpenGeniApiError && error.retryable === false;\n}\n\nexport function isSourcePlacementChangedError(\n error: unknown,\n resource: \"browser_session\" | \"computer_session\",\n): error is OpenGeniApiError {\n return (\n isNonRetryableInteractionError(error) &&\n error.status === 409 &&\n error.details?.interactionResource === resource &&\n error.details?.interactionFailureCode === \"source_placement_changed\" &&\n error.details?.interactionLifecycle === \"lost\"\n );\n}\n","import type { InteractionIntervention } from \"@opengeni/sdk/interaction\";\nimport { CheckIcon, CircleAlertIcon, LoaderCircleIcon, XIcon } from \"lucide-react\";\nimport { cn } from \"../lib/cn\";\n\nexport type InteractionInterventionBannerProps = {\n interventions: InteractionIntervention[];\n activeTargetId?: string | null | undefined;\n mutating?: boolean | undefined;\n className?: string | undefined;\n onOpen?: ((intervention: InteractionIntervention) => void) | undefined;\n onResolve: (intervention: InteractionIntervention, outcome: \"completed\" | \"dismissed\") => void;\n};\n\n/** Compact durable human-action prompt shared by Browser and Computer viewers. */\nexport function InteractionInterventionBanner({\n interventions,\n activeTargetId,\n mutating = false,\n className,\n onOpen,\n onResolve,\n}: InteractionInterventionBannerProps) {\n const intervention = interventions[0];\n if (!intervention) return null;\n const targetOpen = activeTargetId === undefined || activeTargetId === intervention.targetId;\n return (\n <div\n className={cn(\n \"flex shrink-0 items-center gap-2 border-b border-og-status-waiting/25 bg-og-status-waiting/[0.07] px-3 py-2\",\n className,\n )}\n role=\"status\"\n >\n <span className=\"grid size-7 shrink-0 place-items-center rounded-og-sm bg-og-status-waiting/10 text-og-status-waiting\">\n <CircleAlertIcon className=\"size-3.5\" />\n </span>\n <span className=\"min-w-0 flex-1\">\n <span className=\"flex items-center gap-1.5 text-og-control font-medium text-og-fg\">\n {interventionTitle(intervention.kind)}\n {interventions.length > 1 ? (\n <span className=\"rounded-full bg-og-surface-2 px-1.5 py-0.5 text-[10px] font-normal text-og-muted\">\n +{interventions.length - 1}\n </span>\n ) : null}\n </span>\n <span className=\"block truncate text-og-xs text-og-muted\">{intervention.reason}</span>\n </span>\n {!targetOpen && onOpen ? (\n <button\n type=\"button\"\n onClick={() => onOpen(intervention)}\n disabled={mutating}\n className=\"h-7 shrink-0 rounded-og-sm border border-og-primary-border bg-og-primary px-2.5 text-og-control font-medium text-og-primary-fg transition hover:bg-og-primary-hover disabled:opacity-50\"\n >\n Open\n </button>\n ) : (\n <button\n type=\"button\"\n onClick={() => onResolve(intervention, \"completed\")}\n disabled={mutating}\n className=\"inline-flex h-7 shrink-0 items-center gap-1 rounded-og-sm border border-og-primary-border bg-og-primary text-og-primary-fg px-2.5 text-og-control font-medium transition hover:bg-og-primary-hover disabled:opacity-50\"\n >\n {mutating ? (\n <LoaderCircleIcon className=\"size-3 animate-spin\" />\n ) : (\n <CheckIcon className=\"size-3\" />\n )}\n Done\n </button>\n )}\n <button\n type=\"button\"\n onClick={() => onResolve(intervention, \"dismissed\")}\n disabled={mutating}\n className=\"grid size-7 shrink-0 place-items-center rounded-og-sm text-og-muted transition hover:bg-og-surface-2 hover:text-og-fg disabled:opacity-50\"\n aria-label=\"Cancel request\"\n title=\"Cancel request\"\n >\n <XIcon className=\"size-3.5\" />\n </button>\n </div>\n );\n}\n\nfunction interventionTitle(kind: InteractionIntervention[\"kind\"]): string {\n switch (kind) {\n case \"manual_login\":\n return \"Sign in needed\";\n case \"mfa\":\n return \"Verification needed\";\n case \"external_action\":\n return \"Action needed\";\n case \"confirmation\":\n return \"Confirmation needed\";\n case \"other\":\n return \"Agent needs your help\";\n }\n}\n"],"mappings":";;;;;;;;;;;AAMA,SAAS,aAAa,aAAAA,YAAW,UAAAC,SAAQ,gBAAgB;;;ACLzD,SAAS,WAAW,cAAc;AAqB3B,SAAS,2BAA2B,SAA+C;AACxF,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,IAAI;AACJ,QAAM,WAAW,oBAAoB;AACrC,QAAM,uBAAuB,OAA0C;AAAA,IACrE;AAAA,IACA,UAAU;AAAA,EACZ,CAAC;AACD,QAAM,gBAAgB,OAAO,gBAAgB,cAAc,MAAM,WAAW;AAE5E,YAAU,MAAM;AACd,QAAI,CAAC,WAAW,iBAAiB,EAAG;AACpC,QAAI,kBAAkB,QAAW;AAC/B,UAAI,gBAAgB,cAAe,MAAK,QAAQ;AAChD;AAAA,IACF;AACA,QAAI,qBAAqB,QAAQ,QAAQ,KAAK;AAC5C,2BAAqB,UAAU,EAAE,KAAK,UAAU,EAAE;AAAA,IACpD;AACA,QAAI,iBAAiB,qBAAqB,QAAQ,SAAU;AAC5D,yBAAqB,UAAU,EAAE,KAAK,UAAU,cAAc;AAC9D,SAAK,QAAQ;AAAA,EACf,GAAG,CAAC,SAAS,eAAe,KAAK,eAAe,OAAO,CAAC;AAExD,YAAU,MAAM;AACd,QAAI,CAAC,WAAW,CAAC,YAAY,oBAAoB,OAAQ;AACzD,UAAM,WAAW,oBAAoB,0BAA0B,GAAK;AACpE,UAAM,QAAQ,YAAY,MAAM,KAAK,QAAQ,GAAG,QAAQ;AACxD,WAAO,MAAM,cAAc,KAAK;AAAA,EAClC,GAAG,CAAC,iBAAiB,SAAS,wBAAwB,UAAU,OAAO,CAAC;AAC1E;AAEA,SAAS,oBAAoB,OAAuB;AAClD,MAAI,CAAC,OAAO,SAAS,KAAK,EAAG,QAAO;AACpC,SAAO,KAAK,IAAI,KAAQ,KAAK,IAAI,KAAO,KAAK,MAAM,KAAK,CAAC,CAAC;AAC5D;;;ADtBO,SAAS,4BACd,UAA8C,CAAC,GACZ;AACnC,QAAM,EAAE,QAAQ,aAAa,2BAA2B,oCAAoC,IAC1F,yBAAyB,OAAO;AAClC,QAAM,WAAW,QAAQ,WAAW,SAAS,QAAQ,eAAe;AACpE,QAAM,eAAe,QAAQ;AAC7B,QAAM,aAAa,QAAQ,cAAc;AACzC,QAAM,iBAAiB,QAAQ,kBAAkB;AACjD,QAAM,MAAM,GAAG,WAAW,IAAI,gBAAgB,GAAG,IAAI,cAAc,GAAG,IAAI,cAAc;AACxF,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAS,MAAM,WAAW,KAAK,OAAO,CAAC;AACjE,QAAM,CAAC,eAAe,gBAAgB,IAAI,SAAS,CAAC;AACpD,QAAM,aAAaC,QAA2D;AAAA,IAC5E,IAAI;AAAA,IACJ,YAAY;AAAA,EACd,CAAC;AACD,QAAM,aAAaA,QAAO,IAAI;AAC9B,QAAM,UAAU,MAAM,QAAQ,MAAM,QAAQ,WAAW,KAAK,OAAO;AAEnE,QAAM,aAAa,YAAY,MAAM;AACnC,eAAW,QAAQ,YAAY,MAAM;AACrC,eAAW,UAAU,EAAE,IAAI,WAAW,QAAQ,KAAK,GAAG,YAAY,KAAK;AAAA,EACzE,GAAG,CAAC,CAAC;AAEL,QAAM,OAAO;AAAA,IACX,OAAO,YAAqB,UAAU,SAAwB;AAC5D,UAAI,CAAC,QAAS;AACd,UAAI,WAAW,QAAQ,cAAc,CAAC,QAAS;AAC/C,YAAM,KAAK,WAAW,QAAQ,KAAK;AACnC,iBAAW,QAAQ,YAAY,MAAM;AACrC,YAAM,aAAa,IAAI,gBAAgB;AACvC,iBAAW,UAAU,EAAE,IAAI,WAAW;AACtC,UAAI,cAAc,SAAS;AACzB,iBAAS,CAAC,aAAa;AAAA,UACrB,GAAI,QAAQ,QAAQ,MAAM,UAAU,WAAW,KAAK,IAAI;AAAA,UACxD,SAAS;AAAA,UACT,YAAY,CAAC;AAAA,UACb,OAAO,aAAa,OAAO,QAAQ;AAAA,QACrC,EAAE;AAAA,MACJ;AACA,UAAI;AACF,cAAM,WAAW,MAAM,OAAO,6BAA6B,aAAa;AAAA,UACtE,GAAI,eAAe,EAAE,aAAa,IAAI,CAAC;AAAA,UACvC,GAAI,aAAa,EAAE,WAAW,IAAI,CAAC;AAAA,UACnC;AAAA,UACA,QAAQ,WAAW;AAAA,QACrB,CAAC;AACD,YAAI,CAAC,WAAW,WAAW,WAAW,QAAQ,OAAO,GAAI;AACzD,iBAAS;AAAA,UACP;AAAA,UACA,eAAe,kBAAkB,SAAS,aAAa;AAAA,UACvD,SAAS;AAAA,UACT,YAAY;AAAA,UACZ,OAAO;AAAA,QACT,CAAC;AAAA,MACH,SAAS,OAAO;AACd,YAAI,WAAW,OAAO,WAAW,CAAC,WAAW,WAAW,WAAW,QAAQ,OAAO;AAChF;AACF,iBAAS,CAAC,aAAa;AAAA,UACrB,GAAI,QAAQ,QAAQ,MAAM,UAAU,WAAW,KAAK,IAAI;AAAA,UACxD,SAAS;AAAA,UACT,YAAY;AAAA,UACZ,OAAO,QAAQ,KAAK;AAAA,QACtB,EAAE;AAAA,MACJ,UAAE;AACA,YAAI,WAAW,QAAQ,OAAO,GAAI,YAAW,UAAU,EAAE,IAAI,YAAY,KAAK;AAAA,MAChF;AAAA,IACF;AAAA,IACA,CAAC,QAAQ,SAAS,gBAAgB,KAAK,YAAY,cAAc,WAAW;AAAA,EAC9E;AAEA,QAAM,UAAU,YAAY,YAAY,MAAM,KAAK,OAAO,IAAI,GAAG,CAAC,IAAI,CAAC;AAEvE,EAAAC,WAAU,MAAM;AACd,eAAW,UAAU;AACrB,QAAI,CAAC,SAAS;AACZ,iBAAW;AACX,eAAS,WAAW,KAAK,KAAK,CAAC;AAC/B;AAAA,IACF;AACA,SAAK,KAAK,IAAI;AACd,WAAO,MAAM;AACX,iBAAW,UAAU;AACrB,iBAAW;AAAA,IACb;AAAA,EACF,GAAG,CAAC,YAAY,SAAS,KAAK,IAAI,CAAC;AAEnC,6BAA2B;AAAA,IACzB;AAAA,IACA;AAAA,IACA;AAAA,IACA,OAAO;AAAA,IACP,iBAAiB;AAAA,IACjB;AAAA,IACA,wBAAwB,QAAQ,kBAAkB;AAAA,EACpD,CAAC;AAED,QAAM,SAAS,YAAY,OAAU,cAA4C;AAC/E,qBAAiB,CAAC,UAAU,QAAQ,CAAC;AACrC,QAAI;AACF,aAAO,MAAM,UAAU;AAAA,IACzB,UAAE;AACA,UAAI,WAAW,QAAS,kBAAiB,CAAC,UAAU,KAAK,IAAI,GAAG,QAAQ,CAAC,CAAC;AAAA,IAC5E;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,QAAM,QAAQ;AAAA,IACZ,CAAC,iBAA0C;AACzC,eAAS,CAAC,YAAY;AACpB,YAAI,QAAQ,QAAQ,IAAK,QAAO;AAChC,cAAM,gBAAgB,QAAQ,cAAc;AAAA,UAC1C,CAAC,cAAc,UAAU,OAAO,aAAa;AAAA,QAC/C;AACA,cAAM,WACH,CAAC,gBAAgB,aAAa,iBAAiB,kBAC/C,CAAC,cAAc,aAAa,eAAe;AAC9C,YAAI,YAAY,kBAAkB,aAAa,WAAW,SAAS;AACjE,wBAAc,KAAK,YAAY;AAAA,QACjC;AACA,eAAO;AAAA,UACL,GAAG;AAAA,UACH,eAAe,kBAAkB,aAAa;AAAA,UAC9C,OAAO;AAAA,QACT;AAAA,MACF,CAAC;AAAA,IACH;AAAA,IACA,CAAC,gBAAgB,KAAK,YAAY,YAAY;AAAA,EAChD;AAEA,QAAM,SAAS;AAAA,IACb,OACE,YAIA,MAAM,OAAO,YAAY;AACvB,YAAM,WAAW,MAAM,OAAO,8BAA8B,aAAa;AAAA,QACvE,GAAG;AAAA,QACH,aAAa,QAAQ,eAAe,OAAO,WAAW;AAAA,MACxD,CAAC;AACD,YAAM,SAAS,YAAY;AAC3B,aAAO;AAAA,IACT,CAAC;AAAA,IACH,CAAC,QAAQ,OAAO,QAAQ,WAAW;AAAA,EACrC;AAEA,QAAM,UAAU;AAAA,IACd,OACE,gBACA,YAIA,MAAM,OAAO,YAAY;AACvB,YAAM,WAAW,MAAM,OAAO,+BAA+B,aAAa,gBAAgB;AAAA,QACxF,GAAG;AAAA,QACH,aAAa,QAAQ,eAAe,OAAO,WAAW;AAAA,MACxD,CAAC;AACD,YAAM,SAAS,YAAY;AAC3B,aAAO;AAAA,IACT,CAAC;AAAA,IACH,CAAC,QAAQ,OAAO,QAAQ,WAAW;AAAA,EACrC;AAEA,SAAO;AAAA,IACL,eAAe,QAAQ;AAAA,IACvB,SAAS,QAAQ;AAAA,IACjB,YAAY,QAAQ;AAAA,IACpB,UAAU,gBAAgB;AAAA,IAC1B,OAAO,QAAQ;AAAA,IACf;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAEA,SAAS,WAAW,KAAa,SAAkB;AACjD,SAAO;AAAA,IACL;AAAA,IACA,eAAe,CAAC;AAAA,IAChB;AAAA,IACA,YAAY;AAAA,IACZ,OAAO;AAAA,EACT;AACF;AAEA,SAAS,kBACP,eAC2B;AAC3B,SAAO,CAAC,GAAG,aAAa,EAAE;AAAA,IACxB,CAAC,MAAM,UAAU,KAAK,MAAM,MAAM,SAAS,IAAI,KAAK,MAAM,KAAK,SAAS;AAAA,EAC1E;AACF;AAEA,SAAS,QAAQ,OAAuB;AACtC,SAAO,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC;AACjE;;;AE/OA,SAAS,wBAAwB;AACjC,SAAS,0CAA0C;AAI5C,SAAS,gCACd,OACA,UAAqC,WACV;AAC3B,MAAI,EAAE,iBAAiB,kBAAmB,QAAO;AACjD,QAAM,UAAU,mCAAmC,KAAK;AACxD,MAAI,QAAS,QAAO,QAAQ,SAAS,mBAAmB,QAAQ,SAAS;AACzE,SACE,YAAY,aAAa,CAAC,MAAM,mBAAmB,MAAM,UAAU,OAAO,MAAM,WAAW;AAE/F;AAEO,SAAS,+BAA+B,OAA2C;AACxF,SAAO,iBAAiB,oBAAoB,MAAM,cAAc;AAClE;AAEO,SAAS,8BACd,OACA,UAC2B;AAC3B,SACE,+BAA+B,KAAK,KACpC,MAAM,WAAW,OACjB,MAAM,SAAS,wBAAwB,YACvC,MAAM,SAAS,2BAA2B,8BAC1C,MAAM,SAAS,yBAAyB;AAE5C;;;AC/BA,SAAS,WAAW,iBAAiB,kBAAkB,aAAa;AAiC5D,cAMI,YANJ;AApBD,SAAS,8BAA8B;AAAA,EAC5C;AAAA,EACA;AAAA,EACA,WAAW;AAAA,EACX;AAAA,EACA;AAAA,EACA;AACF,GAAuC;AACrC,QAAM,eAAe,cAAc,CAAC;AACpC,MAAI,CAAC,aAAc,QAAO;AAC1B,QAAM,aAAa,mBAAmB,UAAa,mBAAmB,aAAa;AACnF,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACA,MAAK;AAAA,MAEL;AAAA,4BAAC,UAAK,WAAU,wGACd,8BAAC,mBAAgB,WAAU,YAAW,GACxC;AAAA,QACA,qBAAC,UAAK,WAAU,kBACd;AAAA,+BAAC,UAAK,WAAU,oEACb;AAAA,8BAAkB,aAAa,IAAI;AAAA,YACnC,cAAc,SAAS,IACtB,qBAAC,UAAK,WAAU,oFAAmF;AAAA;AAAA,cAC/F,cAAc,SAAS;AAAA,eAC3B,IACE;AAAA,aACN;AAAA,UACA,oBAAC,UAAK,WAAU,2CAA2C,uBAAa,QAAO;AAAA,WACjF;AAAA,QACC,CAAC,cAAc,SACd;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,SAAS,MAAM,OAAO,YAAY;AAAA,YAClC,UAAU;AAAA,YACV,WAAU;AAAA,YACX;AAAA;AAAA,QAED,IAEA;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,SAAS,MAAM,UAAU,cAAc,WAAW;AAAA,YAClD,UAAU;AAAA,YACV,WAAU;AAAA,YAET;AAAA,yBACC,oBAAC,oBAAiB,WAAU,uBAAsB,IAElD,oBAAC,aAAU,WAAU,UAAS;AAAA,cAC9B;AAAA;AAAA;AAAA,QAEJ;AAAA,QAEF;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,SAAS,MAAM,UAAU,cAAc,WAAW;AAAA,YAClD,UAAU;AAAA,YACV,WAAU;AAAA,YACV,cAAW;AAAA,YACX,OAAM;AAAA,YAEN,8BAAC,SAAM,WAAU,YAAW;AAAA;AAAA,QAC9B;AAAA;AAAA;AAAA,EACF;AAEJ;AAEA,SAAS,kBAAkB,MAA+C;AACxE,UAAQ,MAAM;AAAA,IACZ,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,EACX;AACF;","names":["useEffect","useRef","useRef","useEffect"]}
|
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
} from "./chunk-BRM6FVZA.js";
|
|
24
24
|
import {
|
|
25
25
|
stringifyPayload
|
|
26
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-UTRTG6LH.js";
|
|
27
27
|
import {
|
|
28
28
|
cn
|
|
29
29
|
} from "./chunk-22KP6LR5.js";
|
|
@@ -1605,4 +1605,4 @@ export {
|
|
|
1605
1605
|
retainedImageId,
|
|
1606
1606
|
Markdown
|
|
1607
1607
|
};
|
|
1608
|
-
//# sourceMappingURL=chunk-
|
|
1608
|
+
//# sourceMappingURL=chunk-I7IWRQLN.js.map
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
formatBytes,
|
|
3
3
|
formatRelativeTime
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-UTRTG6LH.js";
|
|
5
5
|
import {
|
|
6
6
|
useMutationRunner,
|
|
7
7
|
usePageLiveActivity
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-YQEAA6LT.js";
|
|
9
9
|
import {
|
|
10
10
|
useOpenGeni
|
|
11
11
|
} from "./chunk-NEXLLTP4.js";
|
|
@@ -2448,4 +2448,4 @@ export {
|
|
|
2448
2448
|
EnrollmentDeviceFlow,
|
|
2449
2449
|
EnrollmentConsent
|
|
2450
2450
|
};
|
|
2451
|
-
//# sourceMappingURL=chunk-
|
|
2451
|
+
//# sourceMappingURL=chunk-IQUCRAT7.js.map
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
modelPickerBillingClassFor
|
|
5
5
|
} from "@opengeni/sdk/model-picker-order";
|
|
6
6
|
var BILLING_CLASS_LABELS = {
|
|
7
|
-
opengeni_credits: "
|
|
7
|
+
opengeni_credits: "Models",
|
|
8
8
|
external: "External",
|
|
9
9
|
codex_subscription: "Codex",
|
|
10
10
|
supergrok_subscription: "SuperGrok",
|
|
@@ -25,6 +25,8 @@ function billingClassForModel(model) {
|
|
|
25
25
|
if (model.provider === "organization-gateway" || model.provider === "organization-openrouter") {
|
|
26
26
|
return "organization_byok";
|
|
27
27
|
}
|
|
28
|
+
if (model.provider?.startsWith("workspace-openai-") || model.provider?.startsWith("workspace-azure-openai-"))
|
|
29
|
+
return "byok";
|
|
28
30
|
if (model.provider === "workspace-gateway" || model.provider === "workspace-openrouter") {
|
|
29
31
|
return "byok";
|
|
30
32
|
}
|
|
@@ -252,4 +254,4 @@ export {
|
|
|
252
254
|
findPickerRow,
|
|
253
255
|
groupPickerRowsByBillingClass
|
|
254
256
|
};
|
|
255
|
-
//# sourceMappingURL=chunk-
|
|
257
|
+
//# sourceMappingURL=chunk-NBZ64YY7.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/model-policy.ts"],"sourcesContent":["import {\n compareModelPickerOrder,\n modelPickerBillingClassFor,\n type ModelPickerBillingClass,\n} from \"@opengeni/sdk/model-picker-order\";\nimport type { ClientModel, ReasoningEffort, WorkspaceModelCatalogModel } from \"@opengeni/sdk\";\n\nexport type PickerBillingClass = ModelPickerBillingClass;\n\nexport type PickerModelRow<TCatalog extends ClientModel = WorkspaceModelCatalogModel> = {\n id: string;\n label: string;\n /** Catalog-curated compact label for dense UI; fall back to `label` when absent. */\n shortLabel?: string | undefined;\n billingClass: PickerBillingClass;\n billingClassLabel: string;\n selectable: boolean;\n unavailableReason: string | null;\n provider: string;\n providerLabel: string;\n catalog: TCatalog;\n};\n\nexport type LatencyModeId = \"standard\" | \"priority\" | \"fast\";\n\nconst BILLING_CLASS_LABELS: Record<PickerBillingClass, string> = {\n opengeni_credits: \"Models\",\n external: \"External\",\n codex_subscription: \"Codex\",\n supergrok_subscription: \"SuperGrok\",\n claude_subscription: \"Claude subscription\",\n byok: \"Workspace providers\",\n organization_byok: \"Organization providers\",\n};\n\nconst AVAILABILITY_REASON_LABELS: Record<string, string> = {\n missing_credential: \"Credentials required\",\n needs_reauth: \"Reconnect required\",\n credential_not_ready: \"Credential not ready\",\n not_entitled: \"Not entitled\",\n provider_unhealthy: \"Provider unavailable\",\n policy_blocked: \"Blocked by workspace policy\",\n unsupported: \"Unsupported\",\n};\n\nexport function billingClassForModel(model: ClientModel): PickerBillingClass {\n if (model.provider === \"organization-gateway\" || model.provider === \"organization-openrouter\") {\n return \"organization_byok\";\n }\n if (\n model.provider?.startsWith(\"workspace-openai-\") ||\n model.provider?.startsWith(\"workspace-azure-openai-\")\n )\n return \"byok\";\n if (model.provider === \"workspace-gateway\" || model.provider === \"workspace-openrouter\") {\n return \"byok\";\n }\n return modelPickerBillingClassFor(model);\n}\n\nexport function billingClassLabel(billingClass: PickerBillingClass): string {\n return BILLING_CLASS_LABELS[billingClass];\n}\n\nexport function availabilityReasonLabel(\n reason: WorkspaceModelCatalogModel[\"availability\"][\"reason\"],\n): string | null {\n if (!reason) {\n return null;\n }\n return AVAILABILITY_REASON_LABELS[reason] ?? \"Unavailable\";\n}\n\nexport function effortOptionsForModel(model: ClientModel): ReasoningEffort[] {\n const efforts = model.capabilities?.reasoning.efforts;\n if (!efforts || efforts.length === 0) {\n return [\"low\"];\n }\n const order: ReasoningEffort[] = [\"none\", \"minimal\", \"low\", \"medium\", \"high\", \"xhigh\", \"max\"];\n return order.filter((effort) => efforts.includes(effort));\n}\n\nexport function defaultEffortForModel(model: ClientModel): ReasoningEffort {\n const options = effortOptionsForModel(model);\n const configured = model.capabilities?.reasoning.defaultEffort;\n if (configured && options.includes(configured)) {\n return configured;\n }\n return options[0] ?? \"low\";\n}\n\nexport function coerceReasoningEffortForModel(\n model: ClientModel,\n effort: ReasoningEffort,\n): ReasoningEffort {\n const options = effortOptionsForModel(model);\n if (options.includes(effort)) {\n return effort;\n }\n return defaultEffortForModel(model);\n}\n\nexport function runnableLatencyModesForModel(model: ClientModel): LatencyModeId[] {\n const modes = model.capabilities?.latencyModes ?? [];\n return modes.filter((mode) => mode.runnable).map((mode) => mode.id);\n}\n\nexport function labelLatencyMode(mode: LatencyModeId): string {\n if (mode === \"fast\") {\n return \"Fast\";\n }\n if (mode === \"priority\") {\n return \"Priority\";\n }\n return \"Standard\";\n}\n\nexport function labelReasoningEffort(effort: ReasoningEffort): string {\n if (effort === \"xhigh\") {\n return \"Extra high\";\n }\n return effort.slice(0, 1).toUpperCase() + effort.slice(1);\n}\n\n/** Payment prompts must use cost policy, never the presentation group. */\nexport function modelUsesCredits(model: ClientModel | undefined): boolean {\n if (model?.cost !== undefined) return model.cost === \"credits\";\n return model?.billing?.metering === \"opengeni_credits\";\n}\n\nexport function payerSummaryForModel(model: ClientModel): string {\n if (model.cost === \"free\") {\n return \"Free in this deployment\";\n }\n if (model.cost === \"credits\") {\n return \"Opengeni credits\";\n }\n if (model.cost === \"subscription\") {\n return model.source === \"supergrok\"\n ? \"SuperGrok subscription · external billing\"\n : \"Codex subscription · external billing\";\n }\n if (model.cost === \"workspace\") {\n return workspaceProviderPayerSummary(model);\n }\n if (model.cost === \"organization\") {\n return organizationProviderPayerSummary(model);\n }\n\n // Older client-config payloads do not carry `cost`; preserve their existing\n // settlement-derived label until every deployment has rolled forward.\n const billing = model.billing;\n if (!billing) {\n return \"Route unknown\";\n }\n if (billing.metering === \"opengeni_credits\") {\n return \"Opengeni credits · automatic managed route\";\n }\n if (billing.upstreamPayer === \"connected_subscription\") {\n return model.source === \"supergrok\"\n ? \"SuperGrok subscription · external billing\"\n : \"Codex subscription · external billing\";\n }\n if (billing.upstreamPayer === \"workspace\") {\n return workspaceProviderPayerSummary(model);\n }\n if (billing.upstreamPayer === \"organization\") {\n return organizationProviderPayerSummary(model);\n }\n return billing.upstreamPayer === \"deployment\"\n ? \"Opengeni · no model credits\"\n : \"External provider · no Opengeni credits\";\n}\n\nexport function advancedSourceSummary(model: ClientModel): string | null {\n const source = model.credentialSource;\n if (!source) {\n return model.billing?.metering === \"external\" && model.billing.upstreamPayer === \"deployment\"\n ? \"Deployment-provided connection\"\n : null;\n }\n if (source.kind === \"connected_subscription\") {\n return source.provider === \"xai\"\n ? \"Connected SuperGrok subscription\"\n : \"Connected Codex subscription\";\n }\n if (source.kind === \"workspace_connection\") {\n if (model.provider === \"workspace-openrouter\") {\n return \"Workspace OpenRouter connection\";\n }\n if (model.provider === \"workspace-gateway\" || model.source === \"workspace_gateway\") {\n return \"Workspace Vercel AI Gateway\";\n }\n return \"Workspace provider connection\";\n }\n if (source.kind === \"deployment\") {\n return source.mechanism === \"azure_ad_bearer\"\n ? \"Deployment Azure identity\"\n : \"Deployment API key\";\n }\n return null;\n}\n\nfunction workspaceProviderPayerSummary(model: ClientModel): string {\n if (model.provider === \"workspace-anthropic\")\n return \"Billed to the workspace Anthropic API account\";\n if (model.provider === \"workspace-claude-subscription\")\n return \"Uses the workspace Claude subscription · no OpenGeni credits\";\n if (model.provider === \"workspace-openrouter\") {\n return \"Billed to the workspace OpenRouter account\";\n }\n if (model.provider === \"workspace-gateway\" || model.source === \"workspace_gateway\") {\n return \"Billed to the workspace Vercel account\";\n }\n return \"Billed to the workspace provider account\";\n}\n\nfunction organizationProviderPayerSummary(model: ClientModel): string {\n if (model.provider === \"organization-anthropic\")\n return \"Billed to the organization Anthropic API account\";\n if (model.provider === \"organization-claude-subscription\")\n return \"Uses the connected Claude subscription · no OpenGeni credits\";\n if (model.provider === \"organization-openrouter\") {\n return \"Billed to the organization OpenRouter account\";\n }\n if (model.provider === \"organization-gateway\") {\n return \"Billed to the organization Vercel account\";\n }\n return \"Billed to the organization provider account\";\n}\n\nexport function projectPickerRows(models: WorkspaceModelCatalogModel[]): PickerModelRow[] {\n return models\n .filter((catalog) => catalog.credentialReadiness.status === \"ready\")\n .map((catalog) => {\n const billingClass = billingClassForModel(catalog);\n return {\n id: catalog.id,\n label: catalog.label,\n ...(catalog.shortLabel ? { shortLabel: catalog.shortLabel } : {}),\n billingClass,\n billingClassLabel: billingClassLabel(billingClass),\n selectable: catalog.availability.selectable,\n unavailableReason: catalog.availability.selectable\n ? null\n : availabilityReasonLabel(catalog.availability.reason),\n provider: catalog.provider,\n providerLabel: catalog.providerLabel,\n catalog,\n };\n });\n}\n\n/** Project the lightweight client-config model list into the same picker contract. */\nexport function projectClientModelRows(models: ClientModel[]): PickerModelRow<ClientModel>[] {\n return models.map((catalog) => {\n const billingClass = billingClassForModel(catalog);\n return {\n id: catalog.id,\n label: catalog.label,\n ...(catalog.shortLabel ? { shortLabel: catalog.shortLabel } : {}),\n billingClass,\n billingClassLabel: billingClassLabel(billingClass),\n selectable: true,\n unavailableReason: null,\n provider: catalog.provider,\n providerLabel: catalog.providerLabel,\n catalog,\n };\n });\n}\n\nexport function sortPickerRows<TCatalog extends ClientModel>(\n rows: PickerModelRow<TCatalog>[],\n): PickerModelRow<TCatalog>[] {\n return [...rows].sort(compareModelPickerOrder);\n}\n\nexport function findPickerRow<TCatalog extends ClientModel>(\n rows: PickerModelRow<TCatalog>[],\n modelId: string,\n): PickerModelRow<TCatalog> | null {\n return rows.find((row) => row.id === modelId) ?? null;\n}\n\nexport function groupPickerRowsByBillingClass(\n rows: PickerModelRow[],\n options?: { codexOnly?: boolean },\n): Array<{ billingClass: PickerBillingClass; label: string; rows: PickerModelRow[] }>;\nexport function groupPickerRowsByBillingClass<TCatalog extends ClientModel>(\n rows: PickerModelRow<TCatalog>[],\n options?: { codexOnly?: boolean },\n): Array<{\n billingClass: PickerBillingClass;\n label: string;\n rows: PickerModelRow<TCatalog>[];\n}>;\nexport function groupPickerRowsByBillingClass<TCatalog extends ClientModel>(\n rows: PickerModelRow<TCatalog>[],\n options?: { codexOnly?: boolean },\n): Array<{\n billingClass: PickerBillingClass;\n label: string;\n rows: PickerModelRow<TCatalog>[];\n}> {\n const sorted = sortPickerRows(rows);\n const groups: Array<{\n billingClass: PickerBillingClass;\n label: string;\n rows: PickerModelRow<TCatalog>[];\n }> = [];\n for (const row of sorted) {\n const existing = groups.find((group) => group.billingClass === row.billingClass);\n if (existing) {\n existing.rows.push(row);\n continue;\n }\n groups.push({\n billingClass: row.billingClass,\n label: row.billingClassLabel,\n rows: [row],\n });\n }\n const codexIndex = groups.findIndex((group) => group.billingClass === \"codex_subscription\");\n const codex = groups[codexIndex];\n const selectableOpenGeni = groups\n .find((group) => group.billingClass === \"opengeni_credits\")\n ?.rows.filter((row) => row.selectable);\n // Presentation only: never change the catalog/default model ordering or selection.\n const onlyFreeOpenGeni =\n selectableOpenGeni !== undefined &&\n selectableOpenGeni.length > 0 &&\n selectableOpenGeni.every((row) => row.catalog.cost === \"free\");\n if (\n codex &&\n codex.rows.some((row) => row.selectable) &&\n (options?.codexOnly || onlyFreeOpenGeni)\n ) {\n groups.splice(codexIndex, 1);\n groups.unshift(codex);\n }\n return groups;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,17 +1,26 @@
|
|
|
1
1
|
import {
|
|
2
|
+
COMPOSER_MODEL_UNAVAILABLE_MESSAGE,
|
|
3
|
+
ComposerReconciliationRequiredError,
|
|
4
|
+
ComposerStateError,
|
|
5
|
+
ComposerWorkspaceControlUnavailableError,
|
|
2
6
|
composerSubmissionCanRetry,
|
|
3
7
|
composerSubmissionErrorMessage
|
|
4
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-UTRTG6LH.js";
|
|
5
9
|
import {
|
|
6
10
|
useSessionEventTrigger
|
|
7
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-YQEAA6LT.js";
|
|
12
|
+
import {
|
|
13
|
+
normalizeError,
|
|
14
|
+
useErrorMessage
|
|
15
|
+
} from "./chunk-UGLEYEMP.js";
|
|
8
16
|
import {
|
|
9
17
|
useEmbeddedSession
|
|
10
18
|
} from "./chunk-NEXLLTP4.js";
|
|
11
19
|
|
|
12
20
|
// src/hooks/use-composer.ts
|
|
13
21
|
import {
|
|
14
|
-
DEFAULT_FILE_RESOURCE_MOUNT_ROOT
|
|
22
|
+
DEFAULT_FILE_RESOURCE_MOUNT_ROOT,
|
|
23
|
+
OpenGeniApiError
|
|
15
24
|
} from "@opengeni/sdk";
|
|
16
25
|
import { useCallback, useEffect, useLayoutEffect, useRef, useState } from "react";
|
|
17
26
|
var PENDING_COMPOSER_STORAGE_PREFIX = "opengeni.pending-composer.v1:";
|
|
@@ -55,12 +64,23 @@ function restoreOptimisticSendOperations(key) {
|
|
|
55
64
|
newerShadow: operation.newerShadow ?? { text: "", resources: [], annotations: [] },
|
|
56
65
|
state: operation.state === "sending" || operation.state === "queued" ? "sending" : operation.state,
|
|
57
66
|
outcomeUnknown: operation.state === "sending" || operation.state === "queued" ? true : operation.outcomeUnknown,
|
|
58
|
-
|
|
67
|
+
correlationId: restoredCorrelationId(operation.correlationId),
|
|
68
|
+
error: restoredErrorReference(
|
|
69
|
+
operation.correlationId,
|
|
70
|
+
hasMcpCredentialUpdates && (operation.state === "sending" || operation.outcomeUnknown) ? composerSubmissionErrorMessage(new ComposerReconciliationRequiredError()) : operation.state === "sending" ? "Delivery was interrupted; retry to reconcile this message." : operation.outcomeUnknown ? "The request could not be confirmed. Check its status before retrying." : operation.error ? composerSubmissionErrorMessage(new Error(operation.error)) : void 0
|
|
71
|
+
),
|
|
59
72
|
retryable: operation.state === "sending" || operation.outcomeUnknown ? true : operation.retryable ?? (operation.error ? composerSubmissionCanRetry(new Error(operation.error)) : true),
|
|
60
73
|
input: operation.input,
|
|
61
74
|
canRetry: !hasMcpCredentialUpdates
|
|
62
75
|
}));
|
|
63
76
|
}
|
|
77
|
+
function restoredCorrelationId(value) {
|
|
78
|
+
return typeof value === "string" && /^[\w.:-]{1,128}$/u.test(value) ? value : void 0;
|
|
79
|
+
}
|
|
80
|
+
function restoredErrorReference(reference, message) {
|
|
81
|
+
const correlationId = restoredCorrelationId(reference);
|
|
82
|
+
return message && correlationId && message !== COMPOSER_MODEL_UNAVAILABLE_MESSAGE ? `${message} Reference: ${correlationId}.` : message;
|
|
83
|
+
}
|
|
64
84
|
function rememberOptimisticSendOperations(key, operations) {
|
|
65
85
|
if (!key) return;
|
|
66
86
|
const stored = operations.filter((operation) => operation.state !== "queued").map((operation) => {
|
|
@@ -286,6 +306,7 @@ function reconcileOptimisticSendFromEvents(operation, events) {
|
|
|
286
306
|
}
|
|
287
307
|
function useComposer(sessionId, options = {}) {
|
|
288
308
|
const { client, workspaceId, registerSessionReconciler } = useEmbeddedSession(options);
|
|
309
|
+
const formatError = useErrorMessage();
|
|
289
310
|
const durableDrafts = options.draftPersistence !== "disabled";
|
|
290
311
|
if (!durableDrafts && !options.initialPolicy) {
|
|
291
312
|
throw new Error("useComposer requires initialPolicy when draft persistence is disabled");
|
|
@@ -648,7 +669,7 @@ function useComposer(sessionId, options = {}) {
|
|
|
648
669
|
const problem = asError(cause);
|
|
649
670
|
const timedOut = problem.name === "TimeoutError";
|
|
650
671
|
setDraftReadError(
|
|
651
|
-
timedOut ? new
|
|
672
|
+
timedOut ? new ComposerStateError("Draft sync timed out. Retrying\u2026", { cause }) : problem
|
|
652
673
|
);
|
|
653
674
|
if (timedOut || cause instanceof TypeError || cause && typeof cause === "object" && "retryable" in cause && cause.retryable === true) {
|
|
654
675
|
retry.failures += 1;
|
|
@@ -913,18 +934,23 @@ function useComposer(sessionId, options = {}) {
|
|
|
913
934
|
optimisticProcessorBusyRef.current = true;
|
|
914
935
|
void (async () => {
|
|
915
936
|
let mutationFailureObserved = false;
|
|
916
|
-
const markFailed = (
|
|
937
|
+
const markFailed = (cause, outcomeUnknown) => {
|
|
917
938
|
if (targetKeyRef.current !== ownedTargetKey || targetGeneration.current !== ownedGeneration) {
|
|
918
939
|
return;
|
|
919
940
|
}
|
|
941
|
+
const problem = asError(cause);
|
|
920
942
|
replaceOptimisticSends(
|
|
921
943
|
(current) => current.map(
|
|
922
944
|
(candidate) => candidate.clientEventId === operation.clientEventId ? {
|
|
923
945
|
...candidate,
|
|
924
946
|
state: "failed",
|
|
925
|
-
error:
|
|
947
|
+
error: formatError(
|
|
948
|
+
cause,
|
|
949
|
+
outcomeUnknown && !isOutcomeUnknownError(problem) && !(problem instanceof ComposerReconciliationRequiredError) ? `The request could not be confirmed. Check its status before retrying.${problem instanceof OpenGeniApiError && problem.correlationId ? ` Reference: ${problem.correlationId}.` : ""}` : composerSubmissionErrorMessage(problem)
|
|
950
|
+
),
|
|
926
951
|
retryable: outcomeUnknown || composerSubmissionCanRetry(problem),
|
|
927
|
-
outcomeUnknown
|
|
952
|
+
outcomeUnknown,
|
|
953
|
+
correlationId: problem instanceof OpenGeniApiError ? problem.correlationId : candidate.correlationId
|
|
928
954
|
} : candidate
|
|
929
955
|
)
|
|
930
956
|
);
|
|
@@ -947,7 +973,7 @@ function useComposer(sessionId, options = {}) {
|
|
|
947
973
|
payloadMode: "full"
|
|
948
974
|
});
|
|
949
975
|
} catch (cause) {
|
|
950
|
-
markFailed(
|
|
976
|
+
markFailed(cause, true);
|
|
951
977
|
return;
|
|
952
978
|
}
|
|
953
979
|
const reconciled = reconcileOptimisticSendFromEvents(operation, events);
|
|
@@ -969,15 +995,13 @@ function useComposer(sessionId, options = {}) {
|
|
|
969
995
|
return;
|
|
970
996
|
}
|
|
971
997
|
if (!operation.canRetry) {
|
|
972
|
-
throw new
|
|
973
|
-
"Opengeni cannot safely retry this uncertain request after remount; reconcile the session before sending again."
|
|
974
|
-
);
|
|
998
|
+
throw new ComposerReconciliationRequiredError();
|
|
975
999
|
}
|
|
976
1000
|
}
|
|
977
1001
|
let expectedDraftRevision;
|
|
978
1002
|
if (durableDrafts && operation.draftPayload) {
|
|
979
1003
|
if (!await persistPayload(operation.draftPayload)) {
|
|
980
|
-
throw new
|
|
1004
|
+
throw new ComposerStateError("The message draft could not be saved before delivery.");
|
|
981
1005
|
}
|
|
982
1006
|
expectedDraftRevision = draftRef.current?.revision;
|
|
983
1007
|
}
|
|
@@ -992,7 +1016,7 @@ function useComposer(sessionId, options = {}) {
|
|
|
992
1016
|
let acceptedEvent = null;
|
|
993
1017
|
if (durableDrafts) {
|
|
994
1018
|
if (!operation.draftPayload || expectedDraftRevision === void 0) {
|
|
995
|
-
throw new
|
|
1019
|
+
throw new ComposerStateError("The durable composer draft is not ready for delivery.");
|
|
996
1020
|
}
|
|
997
1021
|
const result = await client.submitComposerDraft(workspaceId, sessionId, {
|
|
998
1022
|
text: operation.draftPayload.text,
|
|
@@ -1058,7 +1082,7 @@ function useComposer(sessionId, options = {}) {
|
|
|
1058
1082
|
const problem = asError(cause);
|
|
1059
1083
|
onDeliveryErrorRef.current?.(problem, operation.input, "send");
|
|
1060
1084
|
const outcomeUnknown = operation.outcomeUnknown && !mutationFailureObserved ? true : isOutcomeUnknownError(cause);
|
|
1061
|
-
markFailed(
|
|
1085
|
+
markFailed(cause, outcomeUnknown);
|
|
1062
1086
|
} finally {
|
|
1063
1087
|
optimisticProcessorBusyRef.current = false;
|
|
1064
1088
|
if (targetKeyRef.current === ownedTargetKey) {
|
|
@@ -1070,6 +1094,7 @@ function useComposer(sessionId, options = {}) {
|
|
|
1070
1094
|
adoptDraftBase,
|
|
1071
1095
|
client,
|
|
1072
1096
|
durableDrafts,
|
|
1097
|
+
formatError,
|
|
1073
1098
|
markOptimisticAccepted,
|
|
1074
1099
|
loadDraft,
|
|
1075
1100
|
onSent,
|
|
@@ -1194,7 +1219,7 @@ function useComposer(sessionId, options = {}) {
|
|
|
1194
1219
|
if (durableDrafts) {
|
|
1195
1220
|
const input = operation.input;
|
|
1196
1221
|
if (input.expectedDraftRevision === void 0 || input.clientEventId === void 0 || input.model === void 0 || input.reasoningEffort === void 0 || input.latencyMode === void 0) {
|
|
1197
|
-
throw new
|
|
1222
|
+
throw new ComposerStateError("The durable composer draft is not ready for delivery.");
|
|
1198
1223
|
}
|
|
1199
1224
|
const result = await client.submitComposerDraft(workspaceId, sessionId, {
|
|
1200
1225
|
text: input.text,
|
|
@@ -1285,11 +1310,7 @@ function useComposer(sessionId, options = {}) {
|
|
|
1285
1310
|
return true;
|
|
1286
1311
|
}
|
|
1287
1312
|
if (!pending.canRetry) {
|
|
1288
|
-
setError(
|
|
1289
|
-
new Error(
|
|
1290
|
-
"Opengeni cannot safely retry this uncertain request after remount; reconcile the session before sending again."
|
|
1291
|
-
)
|
|
1292
|
-
);
|
|
1313
|
+
setError(new ComposerReconciliationRequiredError());
|
|
1293
1314
|
return false;
|
|
1294
1315
|
}
|
|
1295
1316
|
try {
|
|
@@ -1613,7 +1634,7 @@ function useComposer(sessionId, options = {}) {
|
|
|
1613
1634
|
}
|
|
1614
1635
|
} catch (cause) {
|
|
1615
1636
|
if (targetKeyRef.current === ownedTargetKey && targetGeneration.current === ownedGeneration) {
|
|
1616
|
-
setError(
|
|
1637
|
+
setError(normalizeError(cause));
|
|
1617
1638
|
}
|
|
1618
1639
|
} finally {
|
|
1619
1640
|
if (targetKeyRef.current === ownedTargetKey && targetGeneration.current === ownedGeneration) {
|
|
@@ -1645,7 +1666,7 @@ function useComposer(sessionId, options = {}) {
|
|
|
1645
1666
|
}
|
|
1646
1667
|
} catch (cause) {
|
|
1647
1668
|
if (targetKeyRef.current === ownedTargetKey && targetGeneration.current === ownedGeneration) {
|
|
1648
|
-
setError(
|
|
1669
|
+
setError(normalizeError(cause));
|
|
1649
1670
|
}
|
|
1650
1671
|
} finally {
|
|
1651
1672
|
if (targetKeyRef.current === ownedTargetKey && targetGeneration.current === ownedGeneration) {
|
|
@@ -1668,9 +1689,7 @@ function useComposer(sessionId, options = {}) {
|
|
|
1668
1689
|
(blocker) => blocker.kind === "workspace"
|
|
1669
1690
|
);
|
|
1670
1691
|
if (!client.setWorkspaceInferenceState) {
|
|
1671
|
-
throw new
|
|
1672
|
-
"@opengeni/react: workspace-scoped resume requires setWorkspaceInferenceState."
|
|
1673
|
-
);
|
|
1692
|
+
throw new ComposerWorkspaceControlUnavailableError();
|
|
1674
1693
|
}
|
|
1675
1694
|
const clientEventId = generateClientEventId();
|
|
1676
1695
|
await replayOutcomeUnknown(
|
|
@@ -1783,7 +1802,7 @@ function useComposer(sessionId, options = {}) {
|
|
|
1783
1802
|
const addAnnotation = useCallback(
|
|
1784
1803
|
(annotation) => {
|
|
1785
1804
|
if (annotationsRef.current.length >= 12) {
|
|
1786
|
-
setError(new
|
|
1805
|
+
setError(new ComposerStateError("A message can include at most 12 timeline annotations."));
|
|
1787
1806
|
return;
|
|
1788
1807
|
}
|
|
1789
1808
|
const next = [...annotationsRef.current, annotation];
|
|
@@ -2070,7 +2089,7 @@ async function replayOutcomeUnknown(command) {
|
|
|
2070
2089
|
}
|
|
2071
2090
|
}
|
|
2072
2091
|
function asError(cause) {
|
|
2073
|
-
return
|
|
2092
|
+
return normalizeError(cause);
|
|
2074
2093
|
}
|
|
2075
2094
|
function promptRoutingFromAcceptedEvent(event) {
|
|
2076
2095
|
if (typeof event?.payload !== "object" || event.payload === null || Array.isArray(event.payload)) {
|
|
@@ -2133,4 +2152,4 @@ export {
|
|
|
2133
2152
|
shouldSubmitOnKey,
|
|
2134
2153
|
shouldSteerOnKey
|
|
2135
2154
|
};
|
|
2136
|
-
//# sourceMappingURL=chunk-
|
|
2155
|
+
//# sourceMappingURL=chunk-Q5QBEZIQ.js.map
|