@opengeni/react 7.6.1 → 7.7.0-canary.37090756480001
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 +19 -2
- package/dist/{browser-viewer-5X47I6T2.js → browser-viewer-63TXMINB.js} +6 -5
- package/dist/{chunk-UINDK6C3.js → chunk-2QUSQVF2.js} +3 -3
- package/dist/{chunk-CHPKSLPN.js → chunk-6ZSWSH4I.js} +3 -1
- package/dist/chunk-6ZSWSH4I.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-5EXGU7UF.js → chunk-DOJWJQ3F.js} +3 -3
- 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-YRYF2GLG.js → chunk-HNFCAQ5R.js} +39 -31
- package/dist/chunk-HNFCAQ5R.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-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-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/{chunk-LZ3KHA55.js → chunk-ZYALTAEQ.js} +329 -339
- package/dist/chunk-ZYALTAEQ.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/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-JSA64TNN.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/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 +4 -2
- 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 +5 -0
- 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-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-UINDK6C3.js.map → chunk-2QUSQVF2.js.map} +0 -0
- /package/dist/{chunk-5EXGU7UF.js.map → chunk-DOJWJQ3F.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-JSA64TNN.js.map} +0 -0
- /package/dist/{sandbox-files-AEJTWGT4.js.map → sandbox-files-BBC47SLD.js.map} +0 -0
|
@@ -307,7 +307,15 @@ function ScopedCard({
|
|
|
307
307
|
]);
|
|
308
308
|
if (!invocation || invocation.signal.aborted || lifetime.current !== invocation || !active())
|
|
309
309
|
return null;
|
|
310
|
-
|
|
310
|
+
let resolved = catalog.items.find((entry) => entry.id === capabilityId);
|
|
311
|
+
if (resolved?.kind === "mcp" && !resolved.enabled && !resolved.authKind) {
|
|
312
|
+
const endpoint = resolved.mcpUrl ?? resolved.endpointUrl;
|
|
313
|
+
if (endpoint) {
|
|
314
|
+
const inspected = await client.inspectMcpAuthentication(workspaceId, endpoint);
|
|
315
|
+
if (invocation.signal.aborted || lifetime.current !== invocation || !active()) return null;
|
|
316
|
+
if (inspected.kind === "oauth2") resolved = { ...resolved, authKind: "oauth2" };
|
|
317
|
+
}
|
|
318
|
+
}
|
|
311
319
|
if (!resolved || resolved.kind !== "mcp" || resolved.authKind !== "oauth2")
|
|
312
320
|
throw new Error(
|
|
313
321
|
"This recommendation is not an available OAuth MCP integration. Review the current connection catalog."
|
|
@@ -723,4 +731,4 @@ export {
|
|
|
723
731
|
SessionMcpCapabilityCard,
|
|
724
732
|
McpConnectionCard
|
|
725
733
|
};
|
|
726
|
-
//# sourceMappingURL=chunk-
|
|
734
|
+
//# sourceMappingURL=chunk-TKB4SB44.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/hooks/use-connect.ts","../src/session-capability-policy.ts","../src/components/session-capability-frame.tsx","../src/components/session-mcp-capability-card.tsx","../src/mcp-connection-status.ts"],"sourcesContent":["import type { ConnectController } from \"@opengeni/connect\";\nimport { useMemo, useSyncExternalStore } from \"react\";\n\n/** Internal consumers import the hook directly so setup surfaces stay lazy. */\nexport function useConnect(controller: ConnectController) {\n const snapshot = useSyncExternalStore(\n controller.subscribe,\n controller.getSnapshot,\n controller.getSnapshot,\n );\n const actions = useMemo(\n () => ({\n begin: controller.begin.bind(controller),\n recover: controller.recover.bind(controller),\n refresh: controller.refresh.bind(controller),\n waitForAction: controller.waitForAction.bind(controller),\n advance: controller.advance.bind(controller),\n cancel: controller.cancel.bind(controller),\n }),\n [controller],\n );\n return useMemo(() => ({ ...snapshot, ...actions }), [snapshot, actions]);\n}\n","import type { FirstPartyMcpToolName, CapabilityCatalogItem } from \"@opengeni/sdk\";\nimport type { OpenGeniBrowserClient as OpenGeniClient } from \"@opengeni/sdk/browser\";\n\n/** Catalog tool discovery may be lazy; selecting the installed server does not\n * require its individual tool inventory to have been fetched. */\nexport function sessionCapabilityTools(item: CapabilityCatalogItem) {\n const serverId = item.kind === \"mcp\" ? item.runtime?.mcpServerId : undefined;\n return serverId && !item.tools.some((tool) => tool.kind === \"mcp\" && tool.id === serverId)\n ? [...item.tools, { kind: \"mcp\" as const, id: serverId }]\n : item.tools;\n}\n\n/** A connection never resets the human's existing tool selection. Default-mode\n * sessions discover newly enabled servers at admission; explicit/inherited\n * policies add only this reviewed capability, with the existing CAS fence. */\nexport async function attachSessionCapability(\n client: OpenGeniClient,\n workspaceId: string,\n sessionId: string,\n item: CapabilityCatalogItem,\n stillCurrent: () => boolean = () => true,\n): Promise<void> {\n const tools = sessionCapabilityTools(item);\n if (item.kind === \"skill\" || tools.length === 0) return;\n if (!stillCurrent()) throw new Error(\"Connection setup was interrupted.\");\n const session = await client.getSession(workspaceId, sessionId);\n if (!stillCurrent()) throw new Error(\"Connection setup was interrupted.\");\n // Session.tools includes mandatory runtime servers, which must never be\n // echoed into the human's explicit selection.\n let existing = session.tools.filter(\n (tool) => !session.effectiveToolPolicy?.mandatoryIds?.includes(tool.id),\n );\n if (session.toolPolicy.mode === \"workspace_default\") {\n const policy = session.effectiveToolPolicy;\n if (!policy || policy.idsTruncated)\n throw new Error(\n \"Connected, but the full session tool selection could not be read. Review the Tools picker before continuing.\",\n );\n // A workspace may choose a restricted default instead of all enabled servers.\n // Preserve that exact selection if this connection needs an explicit addition.\n // selectedIds contains only explicit additions, and is normally empty for\n // automatic selection. Compare against the resolved defaults instead.\n existing = policy.effectiveIds\n .filter((id) => !policy.mandatoryIds.includes(id))\n .map((id) => ({ kind: \"mcp\" as const, id }));\n }\n const additions = tools.filter(\n (tool) => !existing.some((current) => current.kind === tool.kind && current.id === tool.id),\n );\n const recommended = Array.isArray(item.metadata?.firstPartyMcpTools)\n ? (item.metadata.firstPartyMcpTools.filter(\n (name): name is string => typeof name === \"string\",\n ) as FirstPartyMcpToolName[])\n : [];\n const firstParty = [...new Set([...session.firstPartyMcpTools, ...recommended])];\n if (additions.length === 0 && firstParty.length === session.firstPartyMcpTools.length) return;\n await client.updateSessionToolPolicy(workspaceId, sessionId, {\n mode: \"explicit\",\n tools: [...existing, ...additions],\n firstPartyMcpTools: firstParty,\n expectedVersion: session.toolPolicyVersion,\n });\n}\n\n/** Native OAuth callbacks have already saved the provider connection. Finish\n * the separate, version-fenced session tool selection before reporting success. */\nexport async function completeSessionCapabilityOAuth(\n client: OpenGeniClient,\n workspaceId: string,\n sessionId: string,\n capabilityId: string | null,\n): Promise<void> {\n if (!capabilityId)\n throw new Error(\n \"The authorized integration could not be identified. Review its connection card to finish setup.\",\n );\n const connected = (await client.listCapabilities(workspaceId)).items.find(\n (item) => item.id === capabilityId,\n );\n if (!connected?.enabled)\n throw new Error(\"The connection was authorized, but enabling its tools could not be verified.\");\n await attachSessionCapability(client, workspaceId, sessionId, connected);\n}\n","import { CheckIcon, ShieldCheckIcon, XIcon } from \"lucide-react\";\nimport { Dialog } from \"radix-ui\";\nimport { useRef, useState, type ReactNode, type RefObject } from \"react\";\nimport { usePortalTokenStyle } from \"../lib/use-portal-token-style\";\n\nexport type SessionCapabilityFrameProps = {\n name: string;\n subtitle: string;\n logo: string | null;\n typeLabel: string;\n description: string;\n skill: boolean;\n expanded: boolean;\n complete: boolean;\n /** Completion copy must reflect the provider's actual scope of access. */\n completeLabel?: string;\n actionLabel: string;\n /** Direct navigation actions should not announce a dialog on the card. */\n opensDialog?: boolean;\n note: string;\n onOpen(): void;\n onClose(): void;\n busy?: boolean;\n opener?: RefObject<HTMLButtonElement | null>;\n cardRef?: RefObject<HTMLElement | null>;\n children?: ReactNode;\n /** A catalogue row can own the opener while reusing the same details dialog. */\n dialogOnly?: boolean;\n};\n\nfunction Mark({ src, name }: { src: string | null; name: string }) {\n const [failed, setFailed] = useState<string | null>(null);\n return (\n <span className=\"og-session-capability-logo\" aria-hidden>\n {src && failed !== src ? (\n <img src={src} alt=\"\" loading=\"lazy\" decoding=\"async\" onError={() => setFailed(src)} />\n ) : (\n name.trim().slice(0, 2).toUpperCase()\n )}\n </span>\n );\n}\n\n/** The same conversation card in the console and embedded clients. Setup\n * content owns the provider flow; this frame owns identity and protected focus. */\nexport function SessionCapabilityFrame({\n name,\n subtitle,\n logo,\n typeLabel,\n description,\n skill,\n expanded,\n complete,\n completeLabel,\n actionLabel,\n opensDialog = true,\n note,\n onOpen,\n onClose,\n busy = false,\n opener,\n cardRef,\n children,\n dialogOnly = false,\n}: SessionCapabilityFrameProps) {\n const localOpener = useRef<HTMLButtonElement>(null);\n const localCard = useRef<HTMLElement>(null);\n const buttonRef = opener ?? localOpener;\n const sectionRef = cardRef ?? localCard;\n const portalStyle = usePortalTokenStyle(sectionRef.current);\n const identity = (\n <>\n <Mark src={logo} name={name} />\n <div className=\"og-session-capability-identity\">\n <h3>{name}</h3>\n <span>{subtitle}</span>\n </div>\n <span className=\"og-session-capability-type\">{typeLabel}</span>\n </>\n );\n return (\n <div className=\"og-session-capability\">\n <section\n hidden={dialogOnly}\n style={dialogOnly ? { display: \"none\" } : undefined}\n ref={sectionRef}\n tabIndex={-1}\n aria-label={`${name} setup`}\n data-state={complete ? \"complete\" : expanded ? \"setup\" : \"suggested\"}\n className=\"og-session-capability-shell\"\n >\n <div className=\"og-session-capability-header\">\n {identity}\n {complete ? <CheckIcon size={16} aria-hidden /> : null}\n </div>\n {complete ? (\n <p role=\"status\" className=\"og-session-capability-copy\">\n {completeLabel ??\n (skill ? \"Installed · Workspace\" : \"Connected · Available in this conversation\")}\n </p>\n ) : (\n <>\n <p className=\"og-session-capability-copy\">{description}</p>\n <div className=\"og-session-capability-actions\">\n <span>\n <ShieldCheckIcon size={13} aria-hidden />\n {skill ? \"Guidance only · no account access\" : \"You choose what to authorize\"}\n </span>\n <button\n ref={buttonRef}\n type=\"button\"\n onClick={onOpen}\n disabled={busy}\n aria-haspopup={opensDialog ? \"dialog\" : undefined}\n aria-expanded={opensDialog ? expanded : undefined}\n >\n {actionLabel}\n </button>\n </div>\n </>\n )}\n </section>\n {!complete && !dialogOnly ? <p className=\"og-session-capability-note\">{note}</p> : null}\n <Dialog.Root\n open={expanded && !complete}\n onOpenChange={(open) => {\n if (!open && !busy) onClose();\n }}\n >\n <Dialog.Portal>\n <Dialog.Overlay className=\"og-root og-session-capability-overlay\" style={portalStyle} />\n <Dialog.Content\n className=\"og-root og-session-capability-dialog\"\n style={portalStyle}\n onEscapeKeyDown={(event) => {\n if (busy) event.preventDefault();\n }}\n onInteractOutside={(event) => {\n if (busy) event.preventDefault();\n }}\n onCloseAutoFocus={(event) => {\n event.preventDefault();\n const target = buttonRef.current?.isConnected\n ? buttonRef.current\n : sectionRef.current;\n if (target?.isConnected) target.focus();\n }}\n >\n <div className=\"og-session-capability-dialog-header\">\n <Mark src={logo} name={name} />\n <div>\n <Dialog.Title>{name}</Dialog.Title>\n <Dialog.Description>\n {typeLabel}\n {subtitle ? ` · ${subtitle}` : \"\"}\n </Dialog.Description>\n </div>\n {!busy ? (\n <Dialog.Close aria-label=\"Close connection setup\">\n <XIcon size={18} />\n </Dialog.Close>\n ) : null}\n </div>\n <div className=\"og-session-capability-dialog-body\">{children}</div>\n </Dialog.Content>\n </Dialog.Portal>\n </Dialog.Root>\n </div>\n );\n}\n","import {\n authorizeConnectAttempt,\n ConnectPopupClosedError,\n ConnectController,\n createBrowserConnectNavigation,\n reserveBrowserConnectNavigation,\n type ConnectAttempt,\n type ConnectNavigation,\n type ConnectOwnership,\n} from \"@opengeni/connect\";\nimport type { CapabilityCatalogItem } from \"@opengeni/sdk\";\nimport type { OpenGeniBrowserClient as OpenGeniClient } from \"@opengeni/sdk/browser\";\nimport { useEffect, useRef, useState } from \"react\";\nimport { useConnect } from \"../hooks/use-connect\";\nimport { attachSessionCapability, sessionCapabilityTools } from \"../session-capability-policy\";\nimport { SessionCapabilityFrame } from \"./session-capability-frame\";\nimport { matchingActiveMcpConnections } from \"../mcp-connection-status\";\n\nexport type SessionMcpCapabilityCardProps = {\n client: OpenGeniClient;\n workspaceId: string;\n sessionId: string;\n capabilityId: string;\n name: string;\n rationale?: string;\n /** Exact host route; no console-specific routing is assumed. */\n returnUrl: string;\n onConfigured?: (() => void | Promise<void>) | undefined;\n};\n\nexport type McpConnectionCardProps = Omit<SessionMcpCapabilityCardProps, \"sessionId\"> & {\n /** Omit for connection management: no session tool selection is written. */\n sessionId?: string;\n dialogOnly?: boolean;\n onClose?: (() => void) | undefined;\n /**\n * The provider only ever connects someone's own account (official Gmail,\n * Slack's hosted MCP): no ownership choice, always personal.\n */\n personalOnly?: boolean;\n /** The connect button's words, \"Connect Gmail\". Defaults to \"Continue to <name>\". */\n connectLabel?: string;\n /** The line under the dialog title. Defaults to the kind and provider domain. */\n dialogSubtitle?: string;\n /** The host's product copy for this connection, instead of the catalog description. */\n description?: string;\n /** The host's logo for this connection, when the catalog has no asset. */\n logoSrc?: string | null;\n /** The host's words for the ownership choice. */\n ownershipCopy?: { legend: string; workspace: string; personal: string };\n};\n\n/** Native OAuth recommendation flow. Identity and endpoint come from the live\n * catalog, never from model-authored recommendation text. */\nexport function SessionMcpCapabilityCard(props: SessionMcpCapabilityCardProps) {\n return <McpConnectionCard {...props} />;\n}\n\nexport function McpConnectionCard(props: McpConnectionCardProps) {\n const [client, setClient] = useState(props.client);\n const [generation, setGeneration] = useState(0);\n if (client !== props.client) {\n setClient(props.client);\n setGeneration(generation + 1);\n }\n return (\n <ScopedCard\n key={`${generation}:${props.workspaceId}:${props.sessionId}:${props.capabilityId}`}\n {...props}\n />\n );\n}\n\nfunction ScopedCard({\n client,\n workspaceId,\n sessionId,\n capabilityId,\n name,\n rationale = \"\",\n returnUrl,\n onConfigured,\n dialogOnly = false,\n onClose,\n personalOnly = false,\n connectLabel,\n dialogSubtitle,\n description: hostDescription,\n logoSrc,\n ownershipCopy,\n}: McpConnectionCardProps) {\n const [controller] = useState(\n () => new ConnectController(client.connectTransport(), workspaceId),\n );\n const view = useConnect(controller);\n const [item, setItem] = useState<CapabilityCatalogItem | null>(null);\n const [logo, setLogo] = useState<string | null>(null);\n const [expanded, setExpanded] = useState(dialogOnly);\n const [complete, setComplete] = useState(false);\n const [connected, setConnected] = useState(false);\n const [waiting, setWaiting] = useState(false);\n const [reconciling, setReconciling] = useState(false);\n const [busy, setBusy] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [notice, setNotice] = useState<string | null>(null);\n const [retryFresh, setRetryFresh] = useState(false);\n const [ownership, setOwnership] = useState<ConnectOwnership>(\n personalOnly ? \"personal\" : \"workspace\",\n );\n const lifetime = useRef<AbortController | null>(null);\n const authorization = useRef<AbortController | null>(null);\n const operation = useRef<AbortController | null>(null);\n const reservedPopup = useRef<(() => void) | null>(null);\n const startKey = useRef(crypto.randomUUID());\n const advanceKey = useRef(crypto.randomUUID());\n const startInput = useRef<string | null>(null);\n const storageKey = sessionId\n ? `opengeni:session-connect:${workspaceId}:${sessionId}:${capabilityId}`\n : `opengeni:workspace-connect:${workspaceId}:${capabilityId}`;\n const current = () => !!lifetime.current && !lifetime.current.signal.aborted;\n\n async function load(active: () => boolean = current) {\n const invocation = lifetime.current;\n const [catalog, session] = await Promise.all([\n client.listCapabilities(workspaceId),\n sessionId ? client.getSession(workspaceId, sessionId) : Promise.resolve(null),\n ]);\n if (!invocation || invocation.signal.aborted || lifetime.current !== invocation || !active())\n return null;\n let resolved = catalog.items.find((entry) => entry.id === capabilityId);\n // Custom catalog entries may not declare authentication. Discover it from\n // the live catalog's endpoint, never from recommendation/host copy.\n if (resolved?.kind === \"mcp\" && !resolved.enabled && !resolved.authKind) {\n const endpoint = resolved.mcpUrl ?? resolved.endpointUrl;\n if (endpoint) {\n const inspected = await client.inspectMcpAuthentication(workspaceId, endpoint);\n if (invocation.signal.aborted || lifetime.current !== invocation || !active()) return null;\n if (inspected.kind === \"oauth2\") resolved = { ...resolved, authKind: \"oauth2\" };\n }\n }\n if (!resolved || resolved.kind !== \"mcp\" || resolved.authKind !== \"oauth2\")\n throw new Error(\n \"This recommendation is not an available OAuth MCP integration. Review the current connection catalog.\",\n );\n setItem(resolved);\n if (resolved.connectionRef)\n setOwnership(resolved.connectionRef.subjectScope === \"subject\" ? \"personal\" : \"workspace\");\n else if (\n !item &&\n (personalOnly || resolved.metadata?.defaultConnectionOwnership === \"personal\")\n )\n setOwnership(\"personal\");\n let accountReady = false;\n if (resolved.enabled && resolved.connectionRef) {\n const connections =\n resolved.connectionRef.subjectScope === \"subject\"\n ? await client.listOwnConnectionAccounts(workspaceId)\n : await client.listConnections(workspaceId);\n if (invocation.signal.aborted || lifetime.current !== invocation || !active()) return null;\n const matches = matchingActiveMcpConnections(resolved, connections);\n accountReady = matches.length === 1;\n }\n if (invocation.signal.aborted || lifetime.current !== invocation || !active()) return null;\n const selected =\n session?.toolPolicy.mode === \"workspace_default\"\n ? session.effectiveToolPolicy?.selectedIds\n : session?.tools.filter((tool) => tool.kind === \"mcp\").map((tool) => tool.id);\n setConnected(accountReady);\n setComplete(\n accountReady &&\n !!selected &&\n sessionCapabilityTools(resolved).every((tool) => selected.includes(tool.id)),\n );\n return resolved;\n }\n\n useEffect(() => {\n const abort = new AbortController();\n lifetime.current = abort;\n // Mount reads are scoped to this effect, not to the user-action lock.\n // Strict Mode may cancel the first mount while its promise is still in\n // flight; that must not suppress the replacement mount's initialization.\n if (dialogOnly) setBusy(true);\n void (async () => {\n await load();\n if (abort.signal.aborted || !dialogOnly) return;\n await recoverSavedAttempt();\n })()\n .catch((failure) => {\n if (!abort.signal.aborted)\n setError(\n failure instanceof Error ? failure.message : \"Couldn't load connection details.\",\n );\n })\n .finally(() => {\n if (!abort.signal.aborted && dialogOnly) setBusy(false);\n });\n return () => {\n abort.abort();\n };\n // ScopedCard remounts for client/workspace/session/capability changes.\n // These reads initialize that scope; rerunning on action closures would\n // cancel initialization whenever its own state updates render the card.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [client, workspaceId, sessionId, capabilityId, dialogOnly]);\n\n useEffect(() => {\n const path = item?.logoAssetPath;\n setLogo(null);\n if (!path?.startsWith(\"catalog-assets/\")) return;\n const abort = new AbortController();\n let objectUrl: string | null = null;\n // Embeds can require authentication even for the API's public catalog\n // asset route. Use the client's transport instead of an unauthenticated img.\n void client\n .downloadCatalogAsset(path, { signal: abort.signal })\n .then((blob) => {\n if (abort.signal.aborted || !blob.type.startsWith(\"image/\") || blob.size > 2_000_000)\n return;\n objectUrl = URL.createObjectURL(blob);\n setLogo(objectUrl);\n })\n .catch(() => {\n /* Keep the provider's initials if its passive mark is unavailable. */\n });\n return () => {\n abort.abort();\n if (objectUrl) URL.revokeObjectURL(objectUrl);\n };\n }, [client, item?.logoAssetPath]);\n\n async function run(action: (active: () => boolean) => Promise<void>) {\n if (operation.current || !current()) return;\n const pending = new AbortController();\n operation.current = pending;\n const active = () => current() && operation.current === pending && !pending.signal.aborted;\n setBusy(true);\n setError(null);\n setNotice(null);\n setRetryFresh(false);\n try {\n await action(active);\n } catch (failure) {\n if (active()) {\n if (failure instanceof ConnectPopupClosedError) setNotice(failure.message);\n else\n setError(\n failure instanceof Error\n ? failure.message\n : \"Connection setup could not finish. Try again.\",\n );\n }\n } finally {\n if (operation.current === pending) {\n operation.current = null;\n if (current()) setBusy(false);\n }\n }\n }\n\n function remember(attemptId: string) {\n // An opaque recovery hint only. The backend rechecks the authenticated\n // owner on recover; this value never grants access or proves completion.\n try {\n sessionStorage.setItem(storageKey, attemptId);\n } catch {\n /* Storage may be disabled. */\n }\n }\n\n async function reconcile(attempt: ConnectAttempt, active: () => boolean = current) {\n if (!active()) return;\n if (attempt.state !== \"complete\" || !attempt.account || !attempt.credentialsCommitted) return;\n const resolved = await load(active);\n if (!resolved || !active()) return;\n const connection = (await client.listConnections(workspaceId)).find(\n (entry) => entry.id === attempt.account!.id,\n );\n if (!active()) return;\n if (\n !connection ||\n connection.status !== \"active\" ||\n connection.kind !== \"oauth2\" ||\n connection.metadata.mcpUrl !== (resolved.mcpUrl ?? resolved.endpointUrl) ||\n (connection.subjectId !== null) !== (attempt.ownership === \"personal\")\n )\n throw new Error(\n \"The authorized account does not match this integration. Review its connection settings.\",\n );\n const previous = resolved.connectionRef;\n if (\n resolved.enabled &&\n previous?.subjectScope !== \"subject\" &&\n previous?.connectionId &&\n previous.connectionId !== connection.id\n )\n throw new Error(\n \"This integration's selected account changed during setup. Review it before continuing.\",\n );\n await client.enableCapability(workspaceId, capabilityId, {\n connectionRef:\n attempt.ownership === \"personal\"\n ? { providerDomain: connection.providerDomain, kind: \"oauth2\", subjectScope: \"subject\" }\n : {\n providerDomain: connection.providerDomain,\n kind: \"oauth2\",\n subjectScope: \"workspace\",\n connectionId: connection.id,\n },\n });\n if (!active()) return;\n const enabled = await load(active);\n if (enabled && active()) {\n await finishConnection(enabled, active);\n }\n if (!active()) return;\n try {\n sessionStorage.removeItem(storageKey);\n } catch {\n /* Recovery hint only. */\n }\n }\n\n async function open() {\n setExpanded(true);\n await run(async (active) => {\n await load(active);\n if (!active()) return;\n await recoverSavedAttempt(active);\n });\n }\n\n async function recoverSavedAttempt(active: () => boolean = current) {\n let saved: string | null = null;\n try {\n saved = sessionStorage.getItem(storageKey);\n } catch {\n /* No persisted attempt. */\n }\n if (saved) {\n const attempt = await controller.recover(saved);\n if (!active()) return;\n if ([\"failed\", \"expired\", \"cancelled\"].includes(attempt.state)) setRetryFresh(true);\n if (attempt.state === \"complete\") setReconciling(true);\n try {\n await reconcile(attempt, active);\n } finally {\n if (active()) setReconciling(false);\n }\n }\n }\n\n async function begin(fresh = false) {\n await run(async (active) => {\n if (\n fresh ||\n (view.attempt && [\"failed\", \"expired\", \"cancelled\"].includes(view.attempt.state))\n ) {\n startKey.current = crypto.randomUUID();\n advanceKey.current = crypto.randomUUID();\n }\n const reserved = reserveBrowserConnectNavigation(window);\n reservedPopup.current = reserved.close;\n try {\n const resolved = await load(active);\n if (!resolved || !active()) return;\n if (\n resolved.connectionRef &&\n ownership !==\n (resolved.connectionRef.subjectScope === \"subject\" ? \"personal\" : \"workspace\")\n )\n throw new Error(\n \"The connection ownership changed. Review the current account before continuing.\",\n );\n const mcpUrl = resolved.mcpUrl ?? resolved.endpointUrl;\n if (!mcpUrl)\n throw new Error(\n \"The current catalog has no authorization endpoint for this integration.\",\n );\n // Never silently replace an already selected account. Reauthorization\n // stays on its exact native connection ID.\n let reconnectAccountId = resolved.connectionRef?.connectionId ?? undefined;\n if (resolved.connectionRef?.subjectScope === \"subject\") {\n const connections = (await client.listConnections(workspaceId)).filter(\n (entry) =>\n entry.subjectId !== null &&\n entry.kind === \"oauth2\" &&\n entry.metadata.mcpUrl === mcpUrl,\n );\n if (!active()) return;\n if (connections.length > 1)\n throw new Error(\n \"More than one personal account matches. Choose the account in connection settings before reconnecting.\",\n );\n reconnectAccountId = connections[0]?.id;\n }\n // A closed request can still have reached the server. Keep its key for\n // an exact replay, but never reuse it when ownership or another input\n // changes while the user reopens setup.\n const input = JSON.stringify({ ownership, returnUrl, reconnectAccountId, mcpUrl });\n if (startInput.current !== null && startInput.current !== input) {\n startKey.current = crypto.randomUUID();\n advanceKey.current = crypto.randomUUID();\n }\n startInput.current = input;\n let attempt = await controller.begin({\n providerId: \"mcp-oauth\",\n ownership,\n returnUrl,\n idempotencyKey: startKey.current,\n ...(reconnectAccountId ? { reconnectAccountId } : {}),\n });\n if (!active()) return;\n remember(attempt.id);\n if (attempt.state === \"credential_input\")\n attempt = await controller.advance(\n { type: \"credentials\", values: { mcpUrl } },\n advanceKey.current,\n );\n if (!active()) return;\n if (attempt.nextAction.type === \"authorize\")\n await performAuthorization(attempt, reserved.navigation, active);\n else await reconcile(attempt, active);\n } finally {\n if (reservedPopup.current === reserved.close) reservedPopup.current = null;\n reserved.close();\n }\n });\n }\n\n async function performAuthorization(\n attempt: ConnectAttempt,\n navigation: ConnectNavigation,\n active: () => boolean,\n ) {\n if (!active()) return;\n const pending = new AbortController();\n authorization.current = pending;\n const abortOnUnmount = () => pending.abort(lifetime.current?.signal.reason);\n lifetime.current!.signal.addEventListener(\"abort\", abortOnUnmount, { once: true });\n setWaiting(true);\n try {\n const result = await authorizeConnectAttempt(controller.transport, attempt, navigation, {\n mode: \"popup\",\n signal: pending.signal,\n });\n if (!active() || !result) return;\n setWaiting(false);\n setReconciling(true);\n await reconcile(result, active);\n if (!active()) return;\n if (result.state === \"cancelled\") {\n setNotice(\"Sign-in was cancelled. You can try connecting again.\");\n setRetryFresh(true);\n return;\n }\n if (result.state !== \"complete\") {\n if ([\"failed\", \"expired\"].includes(result.state)) setRetryFresh(true);\n throw new Error(\"Sign-in did not finish. You can try connecting again.\");\n }\n } finally {\n lifetime.current?.signal.removeEventListener(\"abort\", abortOnUnmount);\n if (authorization.current === pending) authorization.current = null;\n if (active()) {\n setReconciling(false);\n setWaiting(false);\n }\n }\n }\n\n function authorize(attempt: ConnectAttempt) {\n // run invokes action synchronously, retaining the browser click gesture.\n return run((active) =>\n performAuthorization(attempt, createBrowserConnectNavigation(window), active),\n );\n }\n\n async function finishConnection(\n capability: CapabilityCatalogItem,\n active: () => boolean = current,\n ) {\n if (!sessionId) {\n // Connection management does not select session tools.\n await onConfigured?.();\n if (!active()) return;\n setComplete(true);\n setExpanded(false);\n onClose?.();\n return;\n }\n if (capability.connectionRef && capability.connectionRef.subjectScope !== \"subject\") {\n const selected = (await client.listConnections(workspaceId)).find(\n (entry) => entry.id === capability.connectionRef?.connectionId,\n );\n if (!active()) return;\n if (!selected || selected.status !== \"active\")\n throw new Error(\"This account needs reconnection before it can be used here.\");\n }\n if (capability.connectionRef?.subjectScope === \"subject\") {\n const accounts = await client.listOwnConnectionAccounts(workspaceId);\n if (!active()) return;\n if (matchingActiveMcpConnections(capability, accounts).length === 0)\n throw new Error(\"Your account needs reconnection before it can be used here.\");\n }\n if (!active()) return;\n await attachSessionCapability(client, workspaceId, sessionId, capability, active);\n if (!active()) return;\n await onConfigured?.();\n if (!active()) return;\n setComplete(true);\n setExpanded(false);\n }\n\n async function useHere() {\n await run(async (active) => {\n if (item) await finishConnection(item, active);\n });\n }\n\n function close() {\n const pending = operation.current;\n if (pending) {\n operation.current = null;\n pending.abort(new ConnectPopupClosedError());\n authorization.current?.abort(new ConnectPopupClosedError());\n authorization.current = null;\n reservedPopup.current?.();\n setBusy(false);\n setWaiting(false);\n setReconciling(false);\n }\n setExpanded(false);\n onClose?.();\n }\n\n return (\n <SessionCapabilityFrame\n name={item?.name ?? name}\n subtitle={dialogSubtitle !== undefined ? \"\" : (item?.providerDomain ?? \"\")}\n logo={logo ?? logoSrc ?? null}\n typeLabel={dialogSubtitle ?? \"MCP server\"}\n description={hostDescription || item?.description || rationale}\n skill={false}\n expanded={expanded}\n complete={complete}\n actionLabel={\n connected\n ? sessionId\n ? \"Use in this conversation\"\n : \"Connected\"\n : `Connect ${item?.name ?? name}`\n }\n note={\n sessionId\n ? \"Review access before signing in. You'll return to this conversation after authorization.\"\n : \"Review access before signing in. You'll return here after authorization.\"\n }\n onOpen={() => void open()}\n onClose={close}\n dialogOnly={dialogOnly}\n >\n <div className=\"og-session-capability-setup\">\n {error ? <p role=\"alert\">{error}</p> : null}\n {notice ? (\n <p role=\"status\" className=\"og-session-capability-notice\">\n {notice}\n </p>\n ) : null}\n {!item ? (\n <>\n <p role=\"status\">\n {busy ? \"Loading connection details…\" : \"Connection details are unavailable.\"}\n </p>\n <button disabled={busy} onClick={() => void open()}>\n Retry\n </button>\n </>\n ) : (\n <>\n <p>{hostDescription || item.description || rationale}</p>\n {busy ? (\n <>\n <p role=\"status\" className=\"og-session-capability-progress\">\n {waiting\n ? `Finish signing in with ${item.name} in the opened window. This will close automatically when you’re connected.`\n : reconciling\n ? \"Finishing your connection…\"\n : \"Preparing your connection…\"}\n </p>\n {waiting ? (\n <button\n type=\"button\"\n className=\"og-session-capability-stop\"\n onClick={() => authorization.current?.abort(new ConnectPopupClosedError())}\n >\n Stop waiting\n </button>\n ) : null}\n </>\n ) : !connected ? (\n <>\n {!item.connectionRef && !personalOnly ? (\n <fieldset disabled={busy}>\n <legend>{ownershipCopy?.legend ?? \"Who can use this connection?\"}</legend>\n <label>\n <input\n type=\"radio\"\n name={`${capabilityId}-ownership`}\n checked={ownership === \"workspace\"}\n onChange={() => setOwnership(\"workspace\")}\n />\n Everyone in this workspace\n </label>\n <label>\n <input\n type=\"radio\"\n name={`${capabilityId}-ownership`}\n checked={ownership === \"personal\"}\n onChange={() => setOwnership(\"personal\")}\n />\n Only me\n </label>\n </fieldset>\n ) : null}\n <p className=\"og-session-capability-scope\">\n {personalOnly && ownership === \"personal\"\n ? \"Connects your own account. Only work you start can use it.\"\n : ownershipCopy\n ? ownership === \"workspace\"\n ? ownershipCopy.workspace\n : ownershipCopy.personal\n : ownership === \"workspace\"\n ? sessionId\n ? \"This connection will be available to your workspace and used in this conversation.\"\n : \"This connection will be available to your workspace.\"\n : \"This connection belongs to you. Your messages can use it; other participants use their own accounts.\"}\n </p>\n <button\n className=\"og-session-capability-primary\"\n onClick={() => {\n if (notice || retryFresh) void begin(true);\n else if (\n view.attempt?.nextAction.type === \"authorize\" &&\n view.attempt.ownership === ownership\n )\n void authorize(view.attempt);\n else void begin();\n }}\n >\n {retryFresh ||\n ((error || notice) && view.attempt?.nextAction.type === \"authorize\")\n ? \"Try signing in again\"\n : (connectLabel ?? `Continue to ${item.name}`)}\n </button>\n </>\n ) : null}\n {connected && !busy ? (\n <>\n <p className=\"og-session-capability-scope\">\n {sessionId\n ? \"Your account is connected. Enable it for this conversation to finish.\"\n : \"Your account is connected.\"}\n </p>\n <button\n className=\"og-session-capability-primary\"\n disabled={busy || view.busy}\n onClick={() => void useHere()}\n >\n {sessionId ? \"Use in this conversation\" : \"Done\"}\n </button>\n </>\n ) : null}\n </>\n )}\n </div>\n </SessionCapabilityFrame>\n );\n}\n","import type { CapabilityCatalogItem, ConnectionMetadata } from \"@opengeni/sdk\";\n\n/** Status only; runtime use is authorized for the authenticated initiating user. */\nexport function matchingActiveMcpConnections(\n item: CapabilityCatalogItem,\n connections: ConnectionMetadata[],\n) {\n return matchingMcpConnections(item, connections).filter((entry) => entry.status === \"active\");\n}\n\nfunction matchingMcpConnections(item: CapabilityCatalogItem, connections: ConnectionMetadata[]) {\n const ref = item.connectionRef;\n if (!item.enabled || !ref) return [];\n return connections.filter(\n (entry) =>\n entry.status !== \"revoked\" &&\n entry.kind === \"oauth2\" &&\n entry.metadata.mcpUrl === (item.mcpUrl ?? item.endpointUrl) &&\n (ref.subjectScope !== \"subject\"\n ? entry.id === ref.connectionId && entry.subjectId === null\n : entry.subjectId !== null),\n );\n}\n\n/** Installation availability is not account readiness. Revoked history is not actionable. */\nexport function mcpConnectionDiscoveryState(\n item: CapabilityCatalogItem,\n connections: ConnectionMetadata[],\n) {\n const matching = matchingMcpConnections(item, connections);\n const active = matching.filter((entry) => entry.status === \"active\");\n if (active.length === 1) return { status: \"added\", label: \"Connected\" } as const;\n if (matching.length > 0) return { status: \"attention\", label: \"Review connection\" } as const;\n return { status: \"available\", label: \"Connect\" } as const;\n}\n"],"mappings":";;;;;AACA,SAAS,SAAS,4BAA4B;AAGvC,SAAS,WAAW,YAA+B;AACxD,QAAM,WAAW;AAAA,IACf,WAAW;AAAA,IACX,WAAW;AAAA,IACX,WAAW;AAAA,EACb;AACA,QAAM,UAAU;AAAA,IACd,OAAO;AAAA,MACL,OAAO,WAAW,MAAM,KAAK,UAAU;AAAA,MACvC,SAAS,WAAW,QAAQ,KAAK,UAAU;AAAA,MAC3C,SAAS,WAAW,QAAQ,KAAK,UAAU;AAAA,MAC3C,eAAe,WAAW,cAAc,KAAK,UAAU;AAAA,MACvD,SAAS,WAAW,QAAQ,KAAK,UAAU;AAAA,MAC3C,QAAQ,WAAW,OAAO,KAAK,UAAU;AAAA,IAC3C;AAAA,IACA,CAAC,UAAU;AAAA,EACb;AACA,SAAO,QAAQ,OAAO,EAAE,GAAG,UAAU,GAAG,QAAQ,IAAI,CAAC,UAAU,OAAO,CAAC;AACzE;;;ACjBO,SAAS,uBAAuB,MAA6B;AAClE,QAAM,WAAW,KAAK,SAAS,QAAQ,KAAK,SAAS,cAAc;AACnE,SAAO,YAAY,CAAC,KAAK,MAAM,KAAK,CAAC,SAAS,KAAK,SAAS,SAAS,KAAK,OAAO,QAAQ,IACrF,CAAC,GAAG,KAAK,OAAO,EAAE,MAAM,OAAgB,IAAI,SAAS,CAAC,IACtD,KAAK;AACX;AAKA,eAAsB,wBACpB,QACA,aACA,WACA,MACA,eAA8B,MAAM,MACrB;AACf,QAAM,QAAQ,uBAAuB,IAAI;AACzC,MAAI,KAAK,SAAS,WAAW,MAAM,WAAW,EAAG;AACjD,MAAI,CAAC,aAAa,EAAG,OAAM,IAAI,MAAM,mCAAmC;AACxE,QAAM,UAAU,MAAM,OAAO,WAAW,aAAa,SAAS;AAC9D,MAAI,CAAC,aAAa,EAAG,OAAM,IAAI,MAAM,mCAAmC;AAGxE,MAAI,WAAW,QAAQ,MAAM;AAAA,IAC3B,CAAC,SAAS,CAAC,QAAQ,qBAAqB,cAAc,SAAS,KAAK,EAAE;AAAA,EACxE;AACA,MAAI,QAAQ,WAAW,SAAS,qBAAqB;AACnD,UAAM,SAAS,QAAQ;AACvB,QAAI,CAAC,UAAU,OAAO;AACpB,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAKF,eAAW,OAAO,aACf,OAAO,CAAC,OAAO,CAAC,OAAO,aAAa,SAAS,EAAE,CAAC,EAChD,IAAI,CAAC,QAAQ,EAAE,MAAM,OAAgB,GAAG,EAAE;AAAA,EAC/C;AACA,QAAM,YAAY,MAAM;AAAA,IACtB,CAAC,SAAS,CAAC,SAAS,KAAK,CAAC,YAAY,QAAQ,SAAS,KAAK,QAAQ,QAAQ,OAAO,KAAK,EAAE;AAAA,EAC5F;AACA,QAAM,cAAc,MAAM,QAAQ,KAAK,UAAU,kBAAkB,IAC9D,KAAK,SAAS,mBAAmB;AAAA,IAChC,CAAC,SAAyB,OAAO,SAAS;AAAA,EAC5C,IACA,CAAC;AACL,QAAM,aAAa,CAAC,GAAG,oBAAI,IAAI,CAAC,GAAG,QAAQ,oBAAoB,GAAG,WAAW,CAAC,CAAC;AAC/E,MAAI,UAAU,WAAW,KAAK,WAAW,WAAW,QAAQ,mBAAmB,OAAQ;AACvF,QAAM,OAAO,wBAAwB,aAAa,WAAW;AAAA,IAC3D,MAAM;AAAA,IACN,OAAO,CAAC,GAAG,UAAU,GAAG,SAAS;AAAA,IACjC,oBAAoB;AAAA,IACpB,iBAAiB,QAAQ;AAAA,EAC3B,CAAC;AACH;AAIA,eAAsB,+BACpB,QACA,aACA,WACA,cACe;AACf,MAAI,CAAC;AACH,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AACF,QAAM,aAAa,MAAM,OAAO,iBAAiB,WAAW,GAAG,MAAM;AAAA,IACnE,CAAC,SAAS,KAAK,OAAO;AAAA,EACxB;AACA,MAAI,CAAC,WAAW;AACd,UAAM,IAAI,MAAM,8EAA8E;AAChG,QAAM,wBAAwB,QAAQ,aAAa,WAAW,SAAS;AACzE;;;AClFA,SAAS,WAAW,iBAAiB,aAAa;AAClD,SAAS,cAAc;AACvB,SAAS,QAAQ,gBAAgD;AAiCzD,SAqCJ,UArCI,KAuCF,YAvCE;AALR,SAAS,KAAK,EAAE,KAAK,KAAK,GAAyC;AACjE,QAAM,CAAC,QAAQ,SAAS,IAAI,SAAwB,IAAI;AACxD,SACE,oBAAC,UAAK,WAAU,8BAA6B,eAAW,MACrD,iBAAO,WAAW,MACjB,oBAAC,SAAI,KAAU,KAAI,IAAG,SAAQ,QAAO,UAAS,SAAQ,SAAS,MAAM,UAAU,GAAG,GAAG,IAErF,KAAK,KAAK,EAAE,MAAM,GAAG,CAAC,EAAE,YAAY,GAExC;AAEJ;AAIO,SAAS,uBAAuB;AAAA,EACrC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,cAAc;AAAA,EACd;AAAA,EACA;AAAA,EACA;AAAA,EACA,OAAO;AAAA,EACP;AAAA,EACA;AAAA,EACA;AAAA,EACA,aAAa;AACf,GAAgC;AAC9B,QAAM,cAAc,OAA0B,IAAI;AAClD,QAAM,YAAY,OAAoB,IAAI;AAC1C,QAAM,YAAY,UAAU;AAC5B,QAAM,aAAa,WAAW;AAC9B,QAAM,cAAc,oBAAoB,WAAW,OAAO;AAC1D,QAAM,WACJ,iCACE;AAAA,wBAAC,QAAK,KAAK,MAAM,MAAY;AAAA,IAC7B,qBAAC,SAAI,WAAU,kCACb;AAAA,0BAAC,QAAI,gBAAK;AAAA,MACV,oBAAC,UAAM,oBAAS;AAAA,OAClB;AAAA,IACA,oBAAC,UAAK,WAAU,8BAA8B,qBAAU;AAAA,KAC1D;AAEF,SACE,qBAAC,SAAI,WAAU,yBACb;AAAA;AAAA,MAAC;AAAA;AAAA,QACC,QAAQ;AAAA,QACR,OAAO,aAAa,EAAE,SAAS,OAAO,IAAI;AAAA,QAC1C,KAAK;AAAA,QACL,UAAU;AAAA,QACV,cAAY,GAAG,IAAI;AAAA,QACnB,cAAY,WAAW,aAAa,WAAW,UAAU;AAAA,QACzD,WAAU;AAAA,QAEV;AAAA,+BAAC,SAAI,WAAU,gCACZ;AAAA;AAAA,YACA,WAAW,oBAAC,aAAU,MAAM,IAAI,eAAW,MAAC,IAAK;AAAA,aACpD;AAAA,UACC,WACC,oBAAC,OAAE,MAAK,UAAS,WAAU,8BACxB,4BACE,QAAQ,6BAA0B,kDACvC,IAEA,iCACE;AAAA,gCAAC,OAAE,WAAU,8BAA8B,uBAAY;AAAA,YACvD,qBAAC,SAAI,WAAU,iCACb;AAAA,mCAAC,UACC;AAAA,oCAAC,mBAAgB,MAAM,IAAI,eAAW,MAAC;AAAA,gBACtC,QAAQ,yCAAsC;AAAA,iBACjD;AAAA,cACA;AAAA,gBAAC;AAAA;AAAA,kBACC,KAAK;AAAA,kBACL,MAAK;AAAA,kBACL,SAAS;AAAA,kBACT,UAAU;AAAA,kBACV,iBAAe,cAAc,WAAW;AAAA,kBACxC,iBAAe,cAAc,WAAW;AAAA,kBAEvC;AAAA;AAAA,cACH;AAAA,eACF;AAAA,aACF;AAAA;AAAA;AAAA,IAEJ;AAAA,IACC,CAAC,YAAY,CAAC,aAAa,oBAAC,OAAE,WAAU,8BAA8B,gBAAK,IAAO;AAAA,IACnF;AAAA,MAAC,OAAO;AAAA,MAAP;AAAA,QACC,MAAM,YAAY,CAAC;AAAA,QACnB,cAAc,CAAC,SAAS;AACtB,cAAI,CAAC,QAAQ,CAAC,KAAM,SAAQ;AAAA,QAC9B;AAAA,QAEA,+BAAC,OAAO,QAAP,EACC;AAAA,8BAAC,OAAO,SAAP,EAAe,WAAU,yCAAwC,OAAO,aAAa;AAAA,UACtF;AAAA,YAAC,OAAO;AAAA,YAAP;AAAA,cACC,WAAU;AAAA,cACV,OAAO;AAAA,cACP,iBAAiB,CAAC,UAAU;AAC1B,oBAAI,KAAM,OAAM,eAAe;AAAA,cACjC;AAAA,cACA,mBAAmB,CAAC,UAAU;AAC5B,oBAAI,KAAM,OAAM,eAAe;AAAA,cACjC;AAAA,cACA,kBAAkB,CAAC,UAAU;AAC3B,sBAAM,eAAe;AACrB,sBAAM,SAAS,UAAU,SAAS,cAC9B,UAAU,UACV,WAAW;AACf,oBAAI,QAAQ,YAAa,QAAO,MAAM;AAAA,cACxC;AAAA,cAEA;AAAA,qCAAC,SAAI,WAAU,uCACb;AAAA,sCAAC,QAAK,KAAK,MAAM,MAAY;AAAA,kBAC7B,qBAAC,SACC;AAAA,wCAAC,OAAO,OAAP,EAAc,gBAAK;AAAA,oBACpB,qBAAC,OAAO,aAAP,EACE;AAAA;AAAA,sBACA,WAAW,SAAM,QAAQ,KAAK;AAAA,uBACjC;AAAA,qBACF;AAAA,kBACC,CAAC,OACA,oBAAC,OAAO,OAAP,EAAa,cAAW,0BACvB,8BAAC,SAAM,MAAM,IAAI,GACnB,IACE;AAAA,mBACN;AAAA,gBACA,oBAAC,SAAI,WAAU,qCAAqC,UAAS;AAAA;AAAA;AAAA,UAC/D;AAAA,WACF;AAAA;AAAA,IACF;AAAA,KACF;AAEJ;;;AC1KA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAIK;AAGP,SAAS,WAAW,UAAAA,SAAQ,YAAAC,iBAAgB;;;ACTrC,SAAS,6BACd,MACA,aACA;AACA,SAAO,uBAAuB,MAAM,WAAW,EAAE,OAAO,CAAC,UAAU,MAAM,WAAW,QAAQ;AAC9F;AAEA,SAAS,uBAAuB,MAA6B,aAAmC;AAC9F,QAAM,MAAM,KAAK;AACjB,MAAI,CAAC,KAAK,WAAW,CAAC,IAAK,QAAO,CAAC;AACnC,SAAO,YAAY;AAAA,IACjB,CAAC,UACC,MAAM,WAAW,aACjB,MAAM,SAAS,YACf,MAAM,SAAS,YAAY,KAAK,UAAU,KAAK,iBAC9C,IAAI,iBAAiB,YAClB,MAAM,OAAO,IAAI,gBAAgB,MAAM,cAAc,OACrD,MAAM,cAAc;AAAA,EAC5B;AACF;AAGO,SAAS,4BACd,MACA,aACA;AACA,QAAM,WAAW,uBAAuB,MAAM,WAAW;AACzD,QAAM,SAAS,SAAS,OAAO,CAAC,UAAU,MAAM,WAAW,QAAQ;AACnE,MAAI,OAAO,WAAW,EAAG,QAAO,EAAE,QAAQ,SAAS,OAAO,YAAY;AACtE,MAAI,SAAS,SAAS,EAAG,QAAO,EAAE,QAAQ,aAAa,OAAO,oBAAoB;AAClF,SAAO,EAAE,QAAQ,aAAa,OAAO,UAAU;AACjD;;;ADqBS,SAkgBC,YAAAC,WAlgBD,OAAAC,MAkgBC,QAAAC,aAlgBD;AADF,SAAS,yBAAyB,OAAsC;AAC7E,SAAO,gBAAAD,KAAC,qBAAmB,GAAG,OAAO;AACvC;AAEO,SAAS,kBAAkB,OAA+B;AAC/D,QAAM,CAAC,QAAQ,SAAS,IAAIE,UAAS,MAAM,MAAM;AACjD,QAAM,CAAC,YAAY,aAAa,IAAIA,UAAS,CAAC;AAC9C,MAAI,WAAW,MAAM,QAAQ;AAC3B,cAAU,MAAM,MAAM;AACtB,kBAAc,aAAa,CAAC;AAAA,EAC9B;AACA,SACE,gBAAAF;AAAA,IAAC;AAAA;AAAA,MAEE,GAAG;AAAA;AAAA,IADC,GAAG,UAAU,IAAI,MAAM,WAAW,IAAI,MAAM,SAAS,IAAI,MAAM,YAAY;AAAA,EAElF;AAEJ;AAEA,SAAS,WAAW;AAAA,EAClB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,YAAY;AAAA,EACZ;AAAA,EACA;AAAA,EACA,aAAa;AAAA,EACb;AAAA,EACA,eAAe;AAAA,EACf;AAAA,EACA;AAAA,EACA,aAAa;AAAA,EACb;AAAA,EACA;AACF,GAA2B;AACzB,QAAM,CAAC,UAAU,IAAIE;AAAA,IACnB,MAAM,IAAI,kBAAkB,OAAO,iBAAiB,GAAG,WAAW;AAAA,EACpE;AACA,QAAM,OAAO,WAAW,UAAU;AAClC,QAAM,CAAC,MAAM,OAAO,IAAIA,UAAuC,IAAI;AACnE,QAAM,CAAC,MAAM,OAAO,IAAIA,UAAwB,IAAI;AACpD,QAAM,CAAC,UAAU,WAAW,IAAIA,UAAS,UAAU;AACnD,QAAM,CAAC,UAAU,WAAW,IAAIA,UAAS,KAAK;AAC9C,QAAM,CAAC,WAAW,YAAY,IAAIA,UAAS,KAAK;AAChD,QAAM,CAAC,SAAS,UAAU,IAAIA,UAAS,KAAK;AAC5C,QAAM,CAAC,aAAa,cAAc,IAAIA,UAAS,KAAK;AACpD,QAAM,CAAC,MAAM,OAAO,IAAIA,UAAS,KAAK;AACtC,QAAM,CAAC,OAAO,QAAQ,IAAIA,UAAwB,IAAI;AACtD,QAAM,CAAC,QAAQ,SAAS,IAAIA,UAAwB,IAAI;AACxD,QAAM,CAAC,YAAY,aAAa,IAAIA,UAAS,KAAK;AAClD,QAAM,CAAC,WAAW,YAAY,IAAIA;AAAA,IAChC,eAAe,aAAa;AAAA,EAC9B;AACA,QAAM,WAAWC,QAA+B,IAAI;AACpD,QAAM,gBAAgBA,QAA+B,IAAI;AACzD,QAAM,YAAYA,QAA+B,IAAI;AACrD,QAAM,gBAAgBA,QAA4B,IAAI;AACtD,QAAM,WAAWA,QAAO,OAAO,WAAW,CAAC;AAC3C,QAAM,aAAaA,QAAO,OAAO,WAAW,CAAC;AAC7C,QAAM,aAAaA,QAAsB,IAAI;AAC7C,QAAM,aAAa,YACf,4BAA4B,WAAW,IAAI,SAAS,IAAI,YAAY,KACpE,8BAA8B,WAAW,IAAI,YAAY;AAC7D,QAAM,UAAU,MAAM,CAAC,CAAC,SAAS,WAAW,CAAC,SAAS,QAAQ,OAAO;AAErE,iBAAe,KAAK,SAAwB,SAAS;AACnD,UAAM,aAAa,SAAS;AAC5B,UAAM,CAAC,SAAS,OAAO,IAAI,MAAM,QAAQ,IAAI;AAAA,MAC3C,OAAO,iBAAiB,WAAW;AAAA,MACnC,YAAY,OAAO,WAAW,aAAa,SAAS,IAAI,QAAQ,QAAQ,IAAI;AAAA,IAC9E,CAAC;AACD,QAAI,CAAC,cAAc,WAAW,OAAO,WAAW,SAAS,YAAY,cAAc,CAAC,OAAO;AACzF,aAAO;AACT,QAAI,WAAW,QAAQ,MAAM,KAAK,CAAC,UAAU,MAAM,OAAO,YAAY;AAGtE,QAAI,UAAU,SAAS,SAAS,CAAC,SAAS,WAAW,CAAC,SAAS,UAAU;AACvE,YAAM,WAAW,SAAS,UAAU,SAAS;AAC7C,UAAI,UAAU;AACZ,cAAM,YAAY,MAAM,OAAO,yBAAyB,aAAa,QAAQ;AAC7E,YAAI,WAAW,OAAO,WAAW,SAAS,YAAY,cAAc,CAAC,OAAO,EAAG,QAAO;AACtF,YAAI,UAAU,SAAS,SAAU,YAAW,EAAE,GAAG,UAAU,UAAU,SAAS;AAAA,MAChF;AAAA,IACF;AACA,QAAI,CAAC,YAAY,SAAS,SAAS,SAAS,SAAS,aAAa;AAChE,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AACF,YAAQ,QAAQ;AAChB,QAAI,SAAS;AACX,mBAAa,SAAS,cAAc,iBAAiB,YAAY,aAAa,WAAW;AAAA,aAEzF,CAAC,SACA,gBAAgB,SAAS,UAAU,+BAA+B;AAEnE,mBAAa,UAAU;AACzB,QAAI,eAAe;AACnB,QAAI,SAAS,WAAW,SAAS,eAAe;AAC9C,YAAM,cACJ,SAAS,cAAc,iBAAiB,YACpC,MAAM,OAAO,0BAA0B,WAAW,IAClD,MAAM,OAAO,gBAAgB,WAAW;AAC9C,UAAI,WAAW,OAAO,WAAW,SAAS,YAAY,cAAc,CAAC,OAAO,EAAG,QAAO;AACtF,YAAM,UAAU,6BAA6B,UAAU,WAAW;AAClE,qBAAe,QAAQ,WAAW;AAAA,IACpC;AACA,QAAI,WAAW,OAAO,WAAW,SAAS,YAAY,cAAc,CAAC,OAAO,EAAG,QAAO;AACtF,UAAM,WACJ,SAAS,WAAW,SAAS,sBACzB,QAAQ,qBAAqB,cAC7B,SAAS,MAAM,OAAO,CAAC,SAAS,KAAK,SAAS,KAAK,EAAE,IAAI,CAAC,SAAS,KAAK,EAAE;AAChF,iBAAa,YAAY;AACzB;AAAA,MACE,gBACE,CAAC,CAAC,YACF,uBAAuB,QAAQ,EAAE,MAAM,CAAC,SAAS,SAAS,SAAS,KAAK,EAAE,CAAC;AAAA,IAC/E;AACA,WAAO;AAAA,EACT;AAEA,YAAU,MAAM;AACd,UAAM,QAAQ,IAAI,gBAAgB;AAClC,aAAS,UAAU;AAInB,QAAI,WAAY,SAAQ,IAAI;AAC5B,UAAM,YAAY;AAChB,YAAM,KAAK;AACX,UAAI,MAAM,OAAO,WAAW,CAAC,WAAY;AACzC,YAAM,oBAAoB;AAAA,IAC5B,GAAG,EACA,MAAM,CAAC,YAAY;AAClB,UAAI,CAAC,MAAM,OAAO;AAChB;AAAA,UACE,mBAAmB,QAAQ,QAAQ,UAAU;AAAA,QAC/C;AAAA,IACJ,CAAC,EACA,QAAQ,MAAM;AACb,UAAI,CAAC,MAAM,OAAO,WAAW,WAAY,SAAQ,KAAK;AAAA,IACxD,CAAC;AACH,WAAO,MAAM;AACX,YAAM,MAAM;AAAA,IACd;AAAA,EAKF,GAAG,CAAC,QAAQ,aAAa,WAAW,cAAc,UAAU,CAAC;AAE7D,YAAU,MAAM;AACd,UAAM,OAAO,MAAM;AACnB,YAAQ,IAAI;AACZ,QAAI,CAAC,MAAM,WAAW,iBAAiB,EAAG;AAC1C,UAAM,QAAQ,IAAI,gBAAgB;AAClC,QAAI,YAA2B;AAG/B,SAAK,OACF,qBAAqB,MAAM,EAAE,QAAQ,MAAM,OAAO,CAAC,EACnD,KAAK,CAAC,SAAS;AACd,UAAI,MAAM,OAAO,WAAW,CAAC,KAAK,KAAK,WAAW,QAAQ,KAAK,KAAK,OAAO;AACzE;AACF,kBAAY,IAAI,gBAAgB,IAAI;AACpC,cAAQ,SAAS;AAAA,IACnB,CAAC,EACA,MAAM,MAAM;AAAA,IAEb,CAAC;AACH,WAAO,MAAM;AACX,YAAM,MAAM;AACZ,UAAI,UAAW,KAAI,gBAAgB,SAAS;AAAA,IAC9C;AAAA,EACF,GAAG,CAAC,QAAQ,MAAM,aAAa,CAAC;AAEhC,iBAAe,IAAI,QAAkD;AACnE,QAAI,UAAU,WAAW,CAAC,QAAQ,EAAG;AACrC,UAAM,UAAU,IAAI,gBAAgB;AACpC,cAAU,UAAU;AACpB,UAAM,SAAS,MAAM,QAAQ,KAAK,UAAU,YAAY,WAAW,CAAC,QAAQ,OAAO;AACnF,YAAQ,IAAI;AACZ,aAAS,IAAI;AACb,cAAU,IAAI;AACd,kBAAc,KAAK;AACnB,QAAI;AACF,YAAM,OAAO,MAAM;AAAA,IACrB,SAAS,SAAS;AAChB,UAAI,OAAO,GAAG;AACZ,YAAI,mBAAmB,wBAAyB,WAAU,QAAQ,OAAO;AAAA;AAEvE;AAAA,YACE,mBAAmB,QACf,QAAQ,UACR;AAAA,UACN;AAAA,MACJ;AAAA,IACF,UAAE;AACA,UAAI,UAAU,YAAY,SAAS;AACjC,kBAAU,UAAU;AACpB,YAAI,QAAQ,EAAG,SAAQ,KAAK;AAAA,MAC9B;AAAA,IACF;AAAA,EACF;AAEA,WAAS,SAAS,WAAmB;AAGnC,QAAI;AACF,qBAAe,QAAQ,YAAY,SAAS;AAAA,IAC9C,QAAQ;AAAA,IAER;AAAA,EACF;AAEA,iBAAe,UAAU,SAAyB,SAAwB,SAAS;AACjF,QAAI,CAAC,OAAO,EAAG;AACf,QAAI,QAAQ,UAAU,cAAc,CAAC,QAAQ,WAAW,CAAC,QAAQ,qBAAsB;AACvF,UAAM,WAAW,MAAM,KAAK,MAAM;AAClC,QAAI,CAAC,YAAY,CAAC,OAAO,EAAG;AAC5B,UAAM,cAAc,MAAM,OAAO,gBAAgB,WAAW,GAAG;AAAA,MAC7D,CAAC,UAAU,MAAM,OAAO,QAAQ,QAAS;AAAA,IAC3C;AACA,QAAI,CAAC,OAAO,EAAG;AACf,QACE,CAAC,cACD,WAAW,WAAW,YACtB,WAAW,SAAS,YACpB,WAAW,SAAS,YAAY,SAAS,UAAU,SAAS,gBAC3D,WAAW,cAAc,UAAW,QAAQ,cAAc;AAE3D,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AACF,UAAM,WAAW,SAAS;AAC1B,QACE,SAAS,WACT,UAAU,iBAAiB,aAC3B,UAAU,gBACV,SAAS,iBAAiB,WAAW;AAErC,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AACF,UAAM,OAAO,iBAAiB,aAAa,cAAc;AAAA,MACvD,eACE,QAAQ,cAAc,aAClB,EAAE,gBAAgB,WAAW,gBAAgB,MAAM,UAAU,cAAc,UAAU,IACrF;AAAA,QACE,gBAAgB,WAAW;AAAA,QAC3B,MAAM;AAAA,QACN,cAAc;AAAA,QACd,cAAc,WAAW;AAAA,MAC3B;AAAA,IACR,CAAC;AACD,QAAI,CAAC,OAAO,EAAG;AACf,UAAM,UAAU,MAAM,KAAK,MAAM;AACjC,QAAI,WAAW,OAAO,GAAG;AACvB,YAAM,iBAAiB,SAAS,MAAM;AAAA,IACxC;AACA,QAAI,CAAC,OAAO,EAAG;AACf,QAAI;AACF,qBAAe,WAAW,UAAU;AAAA,IACtC,QAAQ;AAAA,IAER;AAAA,EACF;AAEA,iBAAe,OAAO;AACpB,gBAAY,IAAI;AAChB,UAAM,IAAI,OAAO,WAAW;AAC1B,YAAM,KAAK,MAAM;AACjB,UAAI,CAAC,OAAO,EAAG;AACf,YAAM,oBAAoB,MAAM;AAAA,IAClC,CAAC;AAAA,EACH;AAEA,iBAAe,oBAAoB,SAAwB,SAAS;AAClE,QAAI,QAAuB;AAC3B,QAAI;AACF,cAAQ,eAAe,QAAQ,UAAU;AAAA,IAC3C,QAAQ;AAAA,IAER;AACA,QAAI,OAAO;AACT,YAAM,UAAU,MAAM,WAAW,QAAQ,KAAK;AAC9C,UAAI,CAAC,OAAO,EAAG;AACf,UAAI,CAAC,UAAU,WAAW,WAAW,EAAE,SAAS,QAAQ,KAAK,EAAG,eAAc,IAAI;AAClF,UAAI,QAAQ,UAAU,WAAY,gBAAe,IAAI;AACrD,UAAI;AACF,cAAM,UAAU,SAAS,MAAM;AAAA,MACjC,UAAE;AACA,YAAI,OAAO,EAAG,gBAAe,KAAK;AAAA,MACpC;AAAA,IACF;AAAA,EACF;AAEA,iBAAe,MAAM,QAAQ,OAAO;AAClC,UAAM,IAAI,OAAO,WAAW;AAC1B,UACE,SACC,KAAK,WAAW,CAAC,UAAU,WAAW,WAAW,EAAE,SAAS,KAAK,QAAQ,KAAK,GAC/E;AACA,iBAAS,UAAU,OAAO,WAAW;AACrC,mBAAW,UAAU,OAAO,WAAW;AAAA,MACzC;AACA,YAAM,WAAW,gCAAgC,MAAM;AACvD,oBAAc,UAAU,SAAS;AACjC,UAAI;AACF,cAAM,WAAW,MAAM,KAAK,MAAM;AAClC,YAAI,CAAC,YAAY,CAAC,OAAO,EAAG;AAC5B,YACE,SAAS,iBACT,eACG,SAAS,cAAc,iBAAiB,YAAY,aAAa;AAEpE,gBAAM,IAAI;AAAA,YACR;AAAA,UACF;AACF,cAAM,SAAS,SAAS,UAAU,SAAS;AAC3C,YAAI,CAAC;AACH,gBAAM,IAAI;AAAA,YACR;AAAA,UACF;AAGF,YAAI,qBAAqB,SAAS,eAAe,gBAAgB;AACjE,YAAI,SAAS,eAAe,iBAAiB,WAAW;AACtD,gBAAM,eAAe,MAAM,OAAO,gBAAgB,WAAW,GAAG;AAAA,YAC9D,CAAC,UACC,MAAM,cAAc,QACpB,MAAM,SAAS,YACf,MAAM,SAAS,WAAW;AAAA,UAC9B;AACA,cAAI,CAAC,OAAO,EAAG;AACf,cAAI,YAAY,SAAS;AACvB,kBAAM,IAAI;AAAA,cACR;AAAA,YACF;AACF,+BAAqB,YAAY,CAAC,GAAG;AAAA,QACvC;AAIA,cAAM,QAAQ,KAAK,UAAU,EAAE,WAAW,WAAW,oBAAoB,OAAO,CAAC;AACjF,YAAI,WAAW,YAAY,QAAQ,WAAW,YAAY,OAAO;AAC/D,mBAAS,UAAU,OAAO,WAAW;AACrC,qBAAW,UAAU,OAAO,WAAW;AAAA,QACzC;AACA,mBAAW,UAAU;AACrB,YAAI,UAAU,MAAM,WAAW,MAAM;AAAA,UACnC,YAAY;AAAA,UACZ;AAAA,UACA;AAAA,UACA,gBAAgB,SAAS;AAAA,UACzB,GAAI,qBAAqB,EAAE,mBAAmB,IAAI,CAAC;AAAA,QACrD,CAAC;AACD,YAAI,CAAC,OAAO,EAAG;AACf,iBAAS,QAAQ,EAAE;AACnB,YAAI,QAAQ,UAAU;AACpB,oBAAU,MAAM,WAAW;AAAA,YACzB,EAAE,MAAM,eAAe,QAAQ,EAAE,OAAO,EAAE;AAAA,YAC1C,WAAW;AAAA,UACb;AACF,YAAI,CAAC,OAAO,EAAG;AACf,YAAI,QAAQ,WAAW,SAAS;AAC9B,gBAAM,qBAAqB,SAAS,SAAS,YAAY,MAAM;AAAA,YAC5D,OAAM,UAAU,SAAS,MAAM;AAAA,MACtC,UAAE;AACA,YAAI,cAAc,YAAY,SAAS,MAAO,eAAc,UAAU;AACtE,iBAAS,MAAM;AAAA,MACjB;AAAA,IACF,CAAC;AAAA,EACH;AAEA,iBAAe,qBACb,SACA,YACA,QACA;AACA,QAAI,CAAC,OAAO,EAAG;AACf,UAAM,UAAU,IAAI,gBAAgB;AACpC,kBAAc,UAAU;AACxB,UAAM,iBAAiB,MAAM,QAAQ,MAAM,SAAS,SAAS,OAAO,MAAM;AAC1E,aAAS,QAAS,OAAO,iBAAiB,SAAS,gBAAgB,EAAE,MAAM,KAAK,CAAC;AACjF,eAAW,IAAI;AACf,QAAI;AACF,YAAM,SAAS,MAAM,wBAAwB,WAAW,WAAW,SAAS,YAAY;AAAA,QACtF,MAAM;AAAA,QACN,QAAQ,QAAQ;AAAA,MAClB,CAAC;AACD,UAAI,CAAC,OAAO,KAAK,CAAC,OAAQ;AAC1B,iBAAW,KAAK;AAChB,qBAAe,IAAI;AACnB,YAAM,UAAU,QAAQ,MAAM;AAC9B,UAAI,CAAC,OAAO,EAAG;AACf,UAAI,OAAO,UAAU,aAAa;AAChC,kBAAU,sDAAsD;AAChE,sBAAc,IAAI;AAClB;AAAA,MACF;AACA,UAAI,OAAO,UAAU,YAAY;AAC/B,YAAI,CAAC,UAAU,SAAS,EAAE,SAAS,OAAO,KAAK,EAAG,eAAc,IAAI;AACpE,cAAM,IAAI,MAAM,uDAAuD;AAAA,MACzE;AAAA,IACF,UAAE;AACA,eAAS,SAAS,OAAO,oBAAoB,SAAS,cAAc;AACpE,UAAI,cAAc,YAAY,QAAS,eAAc,UAAU;AAC/D,UAAI,OAAO,GAAG;AACZ,uBAAe,KAAK;AACpB,mBAAW,KAAK;AAAA,MAClB;AAAA,IACF;AAAA,EACF;AAEA,WAAS,UAAU,SAAyB;AAE1C,WAAO;AAAA,MAAI,CAAC,WACV,qBAAqB,SAAS,+BAA+B,MAAM,GAAG,MAAM;AAAA,IAC9E;AAAA,EACF;AAEA,iBAAe,iBACb,YACA,SAAwB,SACxB;AACA,QAAI,CAAC,WAAW;AAEd,YAAM,eAAe;AACrB,UAAI,CAAC,OAAO,EAAG;AACf,kBAAY,IAAI;AAChB,kBAAY,KAAK;AACjB,gBAAU;AACV;AAAA,IACF;AACA,QAAI,WAAW,iBAAiB,WAAW,cAAc,iBAAiB,WAAW;AACnF,YAAM,YAAY,MAAM,OAAO,gBAAgB,WAAW,GAAG;AAAA,QAC3D,CAAC,UAAU,MAAM,OAAO,WAAW,eAAe;AAAA,MACpD;AACA,UAAI,CAAC,OAAO,EAAG;AACf,UAAI,CAAC,YAAY,SAAS,WAAW;AACnC,cAAM,IAAI,MAAM,6DAA6D;AAAA,IACjF;AACA,QAAI,WAAW,eAAe,iBAAiB,WAAW;AACxD,YAAM,WAAW,MAAM,OAAO,0BAA0B,WAAW;AACnE,UAAI,CAAC,OAAO,EAAG;AACf,UAAI,6BAA6B,YAAY,QAAQ,EAAE,WAAW;AAChE,cAAM,IAAI,MAAM,6DAA6D;AAAA,IACjF;AACA,QAAI,CAAC,OAAO,EAAG;AACf,UAAM,wBAAwB,QAAQ,aAAa,WAAW,YAAY,MAAM;AAChF,QAAI,CAAC,OAAO,EAAG;AACf,UAAM,eAAe;AACrB,QAAI,CAAC,OAAO,EAAG;AACf,gBAAY,IAAI;AAChB,gBAAY,KAAK;AAAA,EACnB;AAEA,iBAAe,UAAU;AACvB,UAAM,IAAI,OAAO,WAAW;AAC1B,UAAI,KAAM,OAAM,iBAAiB,MAAM,MAAM;AAAA,IAC/C,CAAC;AAAA,EACH;AAEA,WAAS,QAAQ;AACf,UAAM,UAAU,UAAU;AAC1B,QAAI,SAAS;AACX,gBAAU,UAAU;AACpB,cAAQ,MAAM,IAAI,wBAAwB,CAAC;AAC3C,oBAAc,SAAS,MAAM,IAAI,wBAAwB,CAAC;AAC1D,oBAAc,UAAU;AACxB,oBAAc,UAAU;AACxB,cAAQ,KAAK;AACb,iBAAW,KAAK;AAChB,qBAAe,KAAK;AAAA,IACtB;AACA,gBAAY,KAAK;AACjB,cAAU;AAAA,EACZ;AAEA,SACE,gBAAAH;AAAA,IAAC;AAAA;AAAA,MACC,MAAM,MAAM,QAAQ;AAAA,MACpB,UAAU,mBAAmB,SAAY,KAAM,MAAM,kBAAkB;AAAA,MACvE,MAAM,QAAQ,WAAW;AAAA,MACzB,WAAW,kBAAkB;AAAA,MAC7B,aAAa,mBAAmB,MAAM,eAAe;AAAA,MACrD,OAAO;AAAA,MACP;AAAA,MACA;AAAA,MACA,aACE,YACI,YACE,6BACA,cACF,WAAW,MAAM,QAAQ,IAAI;AAAA,MAEnC,MACE,YACI,6FACA;AAAA,MAEN,QAAQ,MAAM,KAAK,KAAK;AAAA,MACxB,SAAS;AAAA,MACT;AAAA,MAEA,0BAAAC,MAAC,SAAI,WAAU,+BACZ;AAAA,gBAAQ,gBAAAD,KAAC,OAAE,MAAK,SAAS,iBAAM,IAAO;AAAA,QACtC,SACC,gBAAAA,KAAC,OAAE,MAAK,UAAS,WAAU,gCACxB,kBACH,IACE;AAAA,QACH,CAAC,OACA,gBAAAC,MAAAF,WAAA,EACE;AAAA,0BAAAC,KAAC,OAAE,MAAK,UACL,iBAAO,qCAAgC,uCAC1C;AAAA,UACA,gBAAAA,KAAC,YAAO,UAAU,MAAM,SAAS,MAAM,KAAK,KAAK,GAAG,mBAEpD;AAAA,WACF,IAEA,gBAAAC,MAAAF,WAAA,EACE;AAAA,0BAAAC,KAAC,OAAG,6BAAmB,KAAK,eAAe,WAAU;AAAA,UACpD,OACC,gBAAAC,MAAAF,WAAA,EACE;AAAA,4BAAAC,KAAC,OAAE,MAAK,UAAS,WAAU,kCACxB,oBACG,0BAA0B,KAAK,IAAI,qFACnC,cACE,oCACA,mCACR;AAAA,YACC,UACC,gBAAAA;AAAA,cAAC;AAAA;AAAA,gBACC,MAAK;AAAA,gBACL,WAAU;AAAA,gBACV,SAAS,MAAM,cAAc,SAAS,MAAM,IAAI,wBAAwB,CAAC;AAAA,gBAC1E;AAAA;AAAA,YAED,IACE;AAAA,aACN,IACE,CAAC,YACH,gBAAAC,MAAAF,WAAA,EACG;AAAA,aAAC,KAAK,iBAAiB,CAAC,eACvB,gBAAAE,MAAC,cAAS,UAAU,MAClB;AAAA,8BAAAD,KAAC,YAAQ,yBAAe,UAAU,gCAA+B;AAAA,cACjE,gBAAAC,MAAC,WACC;AAAA,gCAAAD;AAAA,kBAAC;AAAA;AAAA,oBACC,MAAK;AAAA,oBACL,MAAM,GAAG,YAAY;AAAA,oBACrB,SAAS,cAAc;AAAA,oBACvB,UAAU,MAAM,aAAa,WAAW;AAAA;AAAA,gBAC1C;AAAA,gBAAE;AAAA,iBAEJ;AAAA,cACA,gBAAAC,MAAC,WACC;AAAA,gCAAAD;AAAA,kBAAC;AAAA;AAAA,oBACC,MAAK;AAAA,oBACL,MAAM,GAAG,YAAY;AAAA,oBACrB,SAAS,cAAc;AAAA,oBACvB,UAAU,MAAM,aAAa,UAAU;AAAA;AAAA,gBACzC;AAAA,gBAAE;AAAA,iBAEJ;AAAA,eACF,IACE;AAAA,YACJ,gBAAAA,KAAC,OAAE,WAAU,+BACV,0BAAgB,cAAc,aAC3B,+DACA,gBACE,cAAc,cACZ,cAAc,YACd,cAAc,WAChB,cAAc,cACZ,YACE,uFACA,yDACF,wGACV;AAAA,YACA,gBAAAA;AAAA,cAAC;AAAA;AAAA,gBACC,WAAU;AAAA,gBACV,SAAS,MAAM;AACb,sBAAI,UAAU,WAAY,MAAK,MAAM,IAAI;AAAA,2BAEvC,KAAK,SAAS,WAAW,SAAS,eAClC,KAAK,QAAQ,cAAc;AAE3B,yBAAK,UAAU,KAAK,OAAO;AAAA,sBACxB,MAAK,MAAM;AAAA,gBAClB;AAAA,gBAEC,yBACC,SAAS,WAAW,KAAK,SAAS,WAAW,SAAS,cACpD,yBACC,gBAAgB,eAAe,KAAK,IAAI;AAAA;AAAA,YAC/C;AAAA,aACF,IACE;AAAA,UACH,aAAa,CAAC,OACb,gBAAAC,MAAAF,WAAA,EACE;AAAA,4BAAAC,KAAC,OAAE,WAAU,+BACV,sBACG,0EACA,8BACN;AAAA,YACA,gBAAAA;AAAA,cAAC;AAAA;AAAA,gBACC,WAAU;AAAA,gBACV,UAAU,QAAQ,KAAK;AAAA,gBACvB,SAAS,MAAM,KAAK,QAAQ;AAAA,gBAE3B,sBAAY,6BAA6B;AAAA;AAAA,YAC5C;AAAA,aACF,IACE;AAAA,WACN;AAAA,SAEJ;AAAA;AAAA,EACF;AAEJ;","names":["useRef","useState","Fragment","jsx","jsxs","useState","useRef"]}
|
|
@@ -13,6 +13,9 @@ import {
|
|
|
13
13
|
annotationHasNote,
|
|
14
14
|
resolveAnnotationRevealRoot
|
|
15
15
|
} from "./chunk-447JLCBU.js";
|
|
16
|
+
import {
|
|
17
|
+
useErrorMessage
|
|
18
|
+
} from "./chunk-UGLEYEMP.js";
|
|
16
19
|
import {
|
|
17
20
|
cn
|
|
18
21
|
} from "./chunk-22KP6LR5.js";
|
|
@@ -251,6 +254,7 @@ function QueueErrorAlert({
|
|
|
251
254
|
queue,
|
|
252
255
|
dividerBefore = false
|
|
253
256
|
}) {
|
|
257
|
+
const formatError = useErrorMessage();
|
|
254
258
|
return /* @__PURE__ */ jsx2("div", { className: `${dividerBefore ? "border-t border-border" : ""} p-2`, children: /* @__PURE__ */ jsxs2(
|
|
255
259
|
"div",
|
|
256
260
|
{
|
|
@@ -266,7 +270,7 @@ function QueueErrorAlert({
|
|
|
266
270
|
className: "max-h-[min(5rem,20dvh)] min-w-0 max-w-full flex-[1_1_5rem] overflow-auto overscroll-contain whitespace-pre-wrap rounded-sm outline-hidden [overflow-wrap:anywhere] [unicode-bidi:plaintext] focus-visible:ring-2 focus-visible:ring-ring/40",
|
|
267
271
|
"data-testid": "queue-error-message",
|
|
268
272
|
dir: "auto",
|
|
269
|
-
children: (queue.mutationError ?? queue.error)
|
|
273
|
+
children: formatError(queue.mutationError ?? queue.error)
|
|
270
274
|
}
|
|
271
275
|
),
|
|
272
276
|
/* @__PURE__ */ jsx2(
|
|
@@ -305,4 +309,4 @@ export {
|
|
|
305
309
|
QUEUE_ITEM_CONTENT_UNAVAILABLE,
|
|
306
310
|
queueItemContent
|
|
307
311
|
};
|
|
308
|
-
//# sourceMappingURL=chunk-
|
|
312
|
+
//# sourceMappingURL=chunk-TOAH2BKA.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/timeline-annotations.tsx","../src/components/queue-draft-policy.ts","../src/components/queue-surface-state.tsx","../src/components/queue-item-content.ts"],"sourcesContent":["import type { TimelineAnnotationSource } from \"@opengeni/sdk\";\nimport { QuoteIcon, XIcon } from \"lucide-react\";\nimport {\n Suspense,\n lazy,\n useEffect,\n useLayoutEffect,\n useRef,\n useState,\n type ReactNode,\n type RefObject,\n} from \"react\";\nimport { cn } from \"../lib/cn\";\nimport {\n AnnotationAccentRow,\n AnnotationNotePreview,\n AnnotationQuoteSourceButton,\n type TimelineAnnotationLike,\n} from \"./timeline-annotation-chrome\";\nimport { ANNOTATION_CARD_STACK_SCROLL_AT } from \"./timeline-annotation-layout\";\nimport {\n TimelineAnnotationSourceRootContext,\n useTimelineAnnotationSourceRoot,\n} from \"./timeline-annotation-reveal-context\";\nimport {\n annotationDisplayOrdinal,\n annotationHasNote,\n resolveAnnotationRevealRoot,\n} from \"./timeline-annotation-shared\";\n\nexport type { TimelineAnnotationLike } from \"./timeline-annotation-chrome\";\n\nconst TimelineAnnotationsDialog = lazy(() => import(\"./timeline-annotations-dialog\"));\n\nfunction AnnotationSourceRootBridge({\n triggerRef,\n children,\n}: {\n triggerRef: RefObject<HTMLElement | null>;\n children: ReactNode;\n}) {\n const inheritedRoot = useTimelineAnnotationSourceRoot();\n const conversationRootRef = useRef<HTMLElement | null>(null);\n if (!inheritedRoot) {\n conversationRootRef.current = resolveAnnotationRevealRoot(triggerRef.current);\n }\n useLayoutEffect(() => {\n if (inheritedRoot) return;\n conversationRootRef.current = resolveAnnotationRevealRoot(triggerRef.current);\n });\n if (inheritedRoot) return children;\n return (\n <TimelineAnnotationSourceRootContext.Provider value={conversationRootRef}>\n {children}\n </TimelineAnnotationSourceRootContext.Provider>\n );\n}\n\nexport function TimelineAnnotationCards({\n annotations,\n onRevealSource,\n className,\n}: {\n annotations: readonly TimelineAnnotationLike[];\n onRevealSource?: ((source: TimelineAnnotationSource) => boolean) | undefined;\n className?: string | undefined;\n}) {\n const [unavailableId, setUnavailableId] = useState<string | null>(null);\n if (annotations.length === 0) return null;\n return (\n <div\n data-og-annotation-cards=\"\"\n className={cn(\n \"grid gap-2\",\n annotations.length >= ANNOTATION_CARD_STACK_SCROLL_AT &&\n \"max-h-[min(28rem,55vh)] overflow-x-hidden overflow-y-auto overscroll-contain pr-1\",\n className,\n )}\n >\n {annotations.map((annotation, index) => {\n const ordinal = annotationDisplayOrdinal(annotation, index);\n return (\n <section key={annotation.id} aria-label={`Annotation ${ordinal}`}>\n <AnnotationAccentRow>\n <p className=\"mb-0.5 text-og-xs font-medium tabular-nums text-og-fg-subtle\">\n Annotation {ordinal}\n </p>\n <AnnotationQuoteSourceButton\n annotation={annotation}\n lines={2}\n onRevealSource={onRevealSource}\n onUnavailable={setUnavailableId}\n />\n {unavailableId === annotation.id ? (\n <p role=\"status\" className=\"text-og-xs text-og-status-waiting\">\n Source is outside the loaded timeline window.\n </p>\n ) : null}\n <AnnotationNotePreview\n note={annotation.note}\n annotationId={annotation.id}\n ordinal={ordinal}\n />\n </AnnotationAccentRow>\n </section>\n );\n })}\n </div>\n );\n}\n\nexport function TimelineAnnotationsChip({\n annotations,\n editable = false,\n focusAnnotationId,\n onFocusConsumed,\n onUpdate,\n onRemove,\n onRevealSource,\n className,\n compact = false,\n onCommit,\n}: {\n annotations: readonly TimelineAnnotationLike[];\n editable?: boolean | undefined;\n focusAnnotationId?: string | null | undefined;\n onFocusConsumed?: (() => void) | undefined;\n onUpdate?: ((id: string, note: string) => void) | undefined;\n onRemove?: ((id: string) => void) | undefined;\n /** Return focus to the owning input after all notes are completed with Enter. */\n onCommit?: (() => void) | undefined;\n onRevealSource?: ((source: TimelineAnnotationSource) => boolean) | undefined;\n className?: string | undefined;\n /** Dense trigger for single-line rows such as the compact queue dock. */\n compact?: boolean | undefined;\n}) {\n const focusRequested = Boolean(\n focusAnnotationId && annotations.some((item) => item.id === focusAnnotationId),\n );\n const [open, setOpen] = useState(focusRequested);\n const triggerRef = useRef<HTMLButtonElement | null>(null);\n const openedFocusId = useRef<string | null>(focusRequested ? (focusAnnotationId ?? null) : null);\n\n useEffect(() => {\n if (!focusRequested || !focusAnnotationId) {\n if (!focusRequested) openedFocusId.current = null;\n return;\n }\n if (openedFocusId.current === focusAnnotationId) return;\n openedFocusId.current = focusAnnotationId;\n setOpen(true);\n }, [focusAnnotationId, focusRequested]);\n\n if (annotations.length === 0) return null;\n const countLabel = `${annotations.length} ${annotations.length === 1 ? \"annotation\" : \"annotations\"}`;\n const incomplete = annotations.some((annotation) => !annotationHasNote(annotation.note));\n const canClear = Boolean(editable && onRemove);\n\n const dismiss = (restoreFocus: boolean) => {\n setOpen(false);\n onFocusConsumed?.();\n if (restoreFocus) triggerRef.current?.focus();\n };\n\n return (\n <span className={cn(\"inline-flex max-w-full items-center\", className)}>\n <span\n className={cn(\n \"inline-flex max-w-full items-center rounded-full border bg-og-surface-1 text-og-fg-muted\",\n incomplete ? \"border-og-accent/45 bg-og-accent-soft/50\" : \"border-og-border\",\n )}\n >\n <button\n ref={triggerRef}\n type=\"button\"\n className={cn(\n \"inline-flex min-w-0 max-w-full items-center gap-1.5 rounded-full border-0 bg-transparent font-medium outline-hidden transition hover:text-og-fg focus-visible:ring-2 focus-visible:ring-og-accent pointer-coarse:min-h-[44px]\",\n compact ? \"min-h-6 py-0.5 text-og-xs\" : \"min-h-8 py-1 text-og-sm\",\n canClear ? \"pr-1 pl-2.5\" : \"px-2.5\",\n )}\n aria-label={`Review ${countLabel}`}\n aria-haspopup=\"dialog\"\n aria-expanded={open}\n onClick={() => setOpen((current) => !current)}\n >\n <QuoteIcon className=\"size-3.5 shrink-0 text-og-accent\" aria-hidden=\"true\" />\n <span className=\"min-w-0 truncate\">{countLabel}</span>\n </button>\n {canClear ? (\n <button\n type=\"button\"\n className=\"inline-flex size-7 shrink-0 items-center justify-center rounded-full border-0 bg-transparent text-og-fg-subtle outline-hidden hover:text-og-status-failed focus-visible:ring-2 focus-visible:ring-og-accent pointer-coarse:size-11\"\n aria-label=\"Remove all annotations\"\n onClick={() => {\n for (const annotation of annotations) onRemove?.(annotation.id);\n }}\n >\n <XIcon className=\"size-3.5\" aria-hidden=\"true\" />\n </button>\n ) : null}\n </span>\n {open ? (\n <AnnotationSourceRootBridge triggerRef={triggerRef}>\n <Suspense fallback={null}>\n <TimelineAnnotationsDialog\n annotations={annotations}\n editable={editable}\n focusAnnotationId={focusAnnotationId}\n onFocusConsumed={onFocusConsumed}\n onUpdate={onUpdate}\n onRemove={onRemove}\n onRevealSource={onRevealSource}\n triggerRef={triggerRef}\n countLabel={countLabel}\n onDismiss={dismiss}\n onCommit={() => {\n dismiss(!onCommit);\n onCommit?.();\n }}\n />\n </Suspense>\n </AnnotationSourceRootBridge>\n ) : null}\n </span>\n );\n}\n\nexport default TimelineAnnotationsChip;\n","import type { ComposerState } from \"../hooks/use-composer\";\n\n/**\n * Decide whether checking out a queued prompt would replace current composer\n * content. `hasDraftContent` reads the synchronous draft refs, so this stays\n * correct even when a controlled input update has not rendered yet.\n */\nexport function requestQueueDraftEdit(\n composer: Pick<ComposerState, \"hasDraftContent\">,\n confirmReplacement: () => void,\n editImmediately: () => void,\n): void {\n if (composer.hasDraftContent()) {\n confirmReplacement();\n } else {\n editImmediately();\n }\n}\n","import { Loader2Icon, RotateCwIcon } from \"lucide-react\";\n\nimport type { UseTurnQueueResult } from \"../hooks/use-turn-queue\";\n\ntype QueueStateProps = {\n queue: UseTurnQueueResult;\n};\n\nexport function EmptyQueueStateSurface({ queue }: QueueStateProps) {\n const hasError = Boolean(queue.error || queue.mutationError);\n return (\n <div\n className=\"og-root mx-auto mb-2 w-full max-w-3xl shrink-0 px-4 sm:px-6\"\n data-testid=\"queue-surface\"\n >\n <div className=\"overflow-hidden rounded-lg border border-border bg-surface/80 shadow-sm\">\n {queue.stoppingPreviousAttempt ? (\n <QueueStoppingStatus queue={queue} dividerAfter={hasError} />\n ) : null}\n {hasError ? <QueueErrorAlert queue={queue} /> : null}\n </div>\n </div>\n );\n}\n\nexport function QueueStoppingStatus({\n queue,\n dividerAfter = false,\n}: QueueStateProps & { dividerAfter?: boolean }) {\n return (\n <div\n role=\"status\"\n aria-live=\"polite\"\n className={`flex min-h-11 items-center gap-2.5 bg-status-waiting/[0.07] px-3 py-2 text-og-control text-fg ${\n dividerAfter ? \"border-b border-status-waiting/20\" : \"\"\n }`}\n data-testid=\"stopping-previous-attempt\"\n >\n <Loader2Icon\n aria-hidden=\"true\"\n className=\"size-3.5 shrink-0 animate-spin text-status-waiting motion-reduce:animate-none\"\n />\n <span className=\"min-w-0\">\n <span className=\"font-medium\">\n {queue.effectiveControl?.state === \"paused\"\n ? \"Stopping current attempt…\"\n : \"Stopping previous attempt…\"}\n </span>{\" \"}\n <span className=\"text-fg-muted\">\n {queue.effectiveControl?.state === \"paused\"\n ? \"Queued work stays saved until you resume.\"\n : \"Queued work is saved and starts automatically.\"}\n </span>\n </span>\n </div>\n );\n}\n\nexport function QueueErrorAlert({\n queue,\n dividerBefore = false,\n}: QueueStateProps & { dividerBefore?: boolean }) {\n return (\n <div className={`${dividerBefore ? \"border-t border-border\" : \"\"} p-2`}>\n <div\n role=\"alert\"\n className=\"flex min-w-0 max-w-full flex-wrap items-start gap-2 rounded-md bg-status-failed/10 px-2 py-1.5 text-og-control text-status-failed\"\n >\n <span\n role=\"region\"\n aria-label=\"Queue error details\"\n tabIndex={0}\n className=\"max-h-[min(5rem,20dvh)] min-w-0 max-w-full flex-[1_1_5rem] overflow-auto overscroll-contain whitespace-pre-wrap rounded-sm outline-hidden [overflow-wrap:anywhere] [unicode-bidi:plaintext] focus-visible:ring-2 focus-visible:ring-ring/40\"\n data-testid=\"queue-error-message\"\n dir=\"auto\"\n >\n {(queue.mutationError ?? queue.error)?.message}\n </span>\n <button\n type=\"button\"\n onClick={() => {\n queue.clearMutationError();\n void queue.refresh();\n }}\n aria-label=\"Dismiss queue error and retry\"\n title=\"Retry loading the queue\"\n className=\"ms-auto inline-flex size-7 shrink-0 items-center justify-center self-start rounded-md outline-hidden transition-colors hover:bg-surface-3 focus-visible:ring-2 focus-visible:ring-ring/40 motion-reduce:transition-none pointer-coarse:size-[44px]\"\n >\n <RotateCwIcon className=\"size-3.5\" />\n </button>\n </div>\n </div>\n );\n}\n","/**\n * One presentation rule for a queued item, shared by the full queue and the\n * compact session-chrome dock so the two cannot drift. A turn with an empty\n * prompt and timeline annotations is valid content; an item with neither is a\n * malformed or legacy object that gets an explicit fallback, never a blank row.\n */\nexport type QueueItemContent = \"text\" | \"annotations\" | \"unavailable\";\n\nexport const QUEUE_ITEM_CONTENT_UNAVAILABLE = \"Content unavailable\";\n\nexport function queueItemContent(text: string, annotationCount: number): QueueItemContent {\n if (text.length > 0) return \"text\";\n return annotationCount > 0 ? \"annotations\" : \"unavailable\";\n}\n"],"mappings":"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","names":["jsx","jsxs"]}
|
|
1
|
+
{"version":3,"sources":["../src/components/timeline-annotations.tsx","../src/components/queue-draft-policy.ts","../src/components/queue-surface-state.tsx","../src/components/queue-item-content.ts"],"sourcesContent":["import type { TimelineAnnotationSource } from \"@opengeni/sdk\";\nimport { QuoteIcon, XIcon } from \"lucide-react\";\nimport {\n Suspense,\n lazy,\n useEffect,\n useLayoutEffect,\n useRef,\n useState,\n type ReactNode,\n type RefObject,\n} from \"react\";\nimport { cn } from \"../lib/cn\";\nimport {\n AnnotationAccentRow,\n AnnotationNotePreview,\n AnnotationQuoteSourceButton,\n type TimelineAnnotationLike,\n} from \"./timeline-annotation-chrome\";\nimport { ANNOTATION_CARD_STACK_SCROLL_AT } from \"./timeline-annotation-layout\";\nimport {\n TimelineAnnotationSourceRootContext,\n useTimelineAnnotationSourceRoot,\n} from \"./timeline-annotation-reveal-context\";\nimport {\n annotationDisplayOrdinal,\n annotationHasNote,\n resolveAnnotationRevealRoot,\n} from \"./timeline-annotation-shared\";\n\nexport type { TimelineAnnotationLike } from \"./timeline-annotation-chrome\";\n\nconst TimelineAnnotationsDialog = lazy(() => import(\"./timeline-annotations-dialog\"));\n\nfunction AnnotationSourceRootBridge({\n triggerRef,\n children,\n}: {\n triggerRef: RefObject<HTMLElement | null>;\n children: ReactNode;\n}) {\n const inheritedRoot = useTimelineAnnotationSourceRoot();\n const conversationRootRef = useRef<HTMLElement | null>(null);\n if (!inheritedRoot) {\n conversationRootRef.current = resolveAnnotationRevealRoot(triggerRef.current);\n }\n useLayoutEffect(() => {\n if (inheritedRoot) return;\n conversationRootRef.current = resolveAnnotationRevealRoot(triggerRef.current);\n });\n if (inheritedRoot) return children;\n return (\n <TimelineAnnotationSourceRootContext.Provider value={conversationRootRef}>\n {children}\n </TimelineAnnotationSourceRootContext.Provider>\n );\n}\n\nexport function TimelineAnnotationCards({\n annotations,\n onRevealSource,\n className,\n}: {\n annotations: readonly TimelineAnnotationLike[];\n onRevealSource?: ((source: TimelineAnnotationSource) => boolean) | undefined;\n className?: string | undefined;\n}) {\n const [unavailableId, setUnavailableId] = useState<string | null>(null);\n if (annotations.length === 0) return null;\n return (\n <div\n data-og-annotation-cards=\"\"\n className={cn(\n \"grid gap-2\",\n annotations.length >= ANNOTATION_CARD_STACK_SCROLL_AT &&\n \"max-h-[min(28rem,55vh)] overflow-x-hidden overflow-y-auto overscroll-contain pr-1\",\n className,\n )}\n >\n {annotations.map((annotation, index) => {\n const ordinal = annotationDisplayOrdinal(annotation, index);\n return (\n <section key={annotation.id} aria-label={`Annotation ${ordinal}`}>\n <AnnotationAccentRow>\n <p className=\"mb-0.5 text-og-xs font-medium tabular-nums text-og-fg-subtle\">\n Annotation {ordinal}\n </p>\n <AnnotationQuoteSourceButton\n annotation={annotation}\n lines={2}\n onRevealSource={onRevealSource}\n onUnavailable={setUnavailableId}\n />\n {unavailableId === annotation.id ? (\n <p role=\"status\" className=\"text-og-xs text-og-status-waiting\">\n Source is outside the loaded timeline window.\n </p>\n ) : null}\n <AnnotationNotePreview\n note={annotation.note}\n annotationId={annotation.id}\n ordinal={ordinal}\n />\n </AnnotationAccentRow>\n </section>\n );\n })}\n </div>\n );\n}\n\nexport function TimelineAnnotationsChip({\n annotations,\n editable = false,\n focusAnnotationId,\n onFocusConsumed,\n onUpdate,\n onRemove,\n onRevealSource,\n className,\n compact = false,\n onCommit,\n}: {\n annotations: readonly TimelineAnnotationLike[];\n editable?: boolean | undefined;\n focusAnnotationId?: string | null | undefined;\n onFocusConsumed?: (() => void) | undefined;\n onUpdate?: ((id: string, note: string) => void) | undefined;\n onRemove?: ((id: string) => void) | undefined;\n /** Return focus to the owning input after all notes are completed with Enter. */\n onCommit?: (() => void) | undefined;\n onRevealSource?: ((source: TimelineAnnotationSource) => boolean) | undefined;\n className?: string | undefined;\n /** Dense trigger for single-line rows such as the compact queue dock. */\n compact?: boolean | undefined;\n}) {\n const focusRequested = Boolean(\n focusAnnotationId && annotations.some((item) => item.id === focusAnnotationId),\n );\n const [open, setOpen] = useState(focusRequested);\n const triggerRef = useRef<HTMLButtonElement | null>(null);\n const openedFocusId = useRef<string | null>(focusRequested ? (focusAnnotationId ?? null) : null);\n\n useEffect(() => {\n if (!focusRequested || !focusAnnotationId) {\n if (!focusRequested) openedFocusId.current = null;\n return;\n }\n if (openedFocusId.current === focusAnnotationId) return;\n openedFocusId.current = focusAnnotationId;\n setOpen(true);\n }, [focusAnnotationId, focusRequested]);\n\n if (annotations.length === 0) return null;\n const countLabel = `${annotations.length} ${annotations.length === 1 ? \"annotation\" : \"annotations\"}`;\n const incomplete = annotations.some((annotation) => !annotationHasNote(annotation.note));\n const canClear = Boolean(editable && onRemove);\n\n const dismiss = (restoreFocus: boolean) => {\n setOpen(false);\n onFocusConsumed?.();\n if (restoreFocus) triggerRef.current?.focus();\n };\n\n return (\n <span className={cn(\"inline-flex max-w-full items-center\", className)}>\n <span\n className={cn(\n \"inline-flex max-w-full items-center rounded-full border bg-og-surface-1 text-og-fg-muted\",\n incomplete ? \"border-og-accent/45 bg-og-accent-soft/50\" : \"border-og-border\",\n )}\n >\n <button\n ref={triggerRef}\n type=\"button\"\n className={cn(\n \"inline-flex min-w-0 max-w-full items-center gap-1.5 rounded-full border-0 bg-transparent font-medium outline-hidden transition hover:text-og-fg focus-visible:ring-2 focus-visible:ring-og-accent pointer-coarse:min-h-[44px]\",\n compact ? \"min-h-6 py-0.5 text-og-xs\" : \"min-h-8 py-1 text-og-sm\",\n canClear ? \"pr-1 pl-2.5\" : \"px-2.5\",\n )}\n aria-label={`Review ${countLabel}`}\n aria-haspopup=\"dialog\"\n aria-expanded={open}\n onClick={() => setOpen((current) => !current)}\n >\n <QuoteIcon className=\"size-3.5 shrink-0 text-og-accent\" aria-hidden=\"true\" />\n <span className=\"min-w-0 truncate\">{countLabel}</span>\n </button>\n {canClear ? (\n <button\n type=\"button\"\n className=\"inline-flex size-7 shrink-0 items-center justify-center rounded-full border-0 bg-transparent text-og-fg-subtle outline-hidden hover:text-og-status-failed focus-visible:ring-2 focus-visible:ring-og-accent pointer-coarse:size-11\"\n aria-label=\"Remove all annotations\"\n onClick={() => {\n for (const annotation of annotations) onRemove?.(annotation.id);\n }}\n >\n <XIcon className=\"size-3.5\" aria-hidden=\"true\" />\n </button>\n ) : null}\n </span>\n {open ? (\n <AnnotationSourceRootBridge triggerRef={triggerRef}>\n <Suspense fallback={null}>\n <TimelineAnnotationsDialog\n annotations={annotations}\n editable={editable}\n focusAnnotationId={focusAnnotationId}\n onFocusConsumed={onFocusConsumed}\n onUpdate={onUpdate}\n onRemove={onRemove}\n onRevealSource={onRevealSource}\n triggerRef={triggerRef}\n countLabel={countLabel}\n onDismiss={dismiss}\n onCommit={() => {\n dismiss(!onCommit);\n onCommit?.();\n }}\n />\n </Suspense>\n </AnnotationSourceRootBridge>\n ) : null}\n </span>\n );\n}\n\nexport default TimelineAnnotationsChip;\n","import type { ComposerState } from \"../hooks/use-composer\";\n\n/**\n * Decide whether checking out a queued prompt would replace current composer\n * content. `hasDraftContent` reads the synchronous draft refs, so this stays\n * correct even when a controlled input update has not rendered yet.\n */\nexport function requestQueueDraftEdit(\n composer: Pick<ComposerState, \"hasDraftContent\">,\n confirmReplacement: () => void,\n editImmediately: () => void,\n): void {\n if (composer.hasDraftContent()) {\n confirmReplacement();\n } else {\n editImmediately();\n }\n}\n","import { Loader2Icon, RotateCwIcon } from \"lucide-react\";\n\nimport type { UseTurnQueueResult } from \"../hooks/use-turn-queue\";\nimport { useErrorMessage } from \"../lib/error-message\";\n\ntype QueueStateProps = {\n queue: UseTurnQueueResult;\n};\n\nexport function EmptyQueueStateSurface({ queue }: QueueStateProps) {\n const hasError = Boolean(queue.error || queue.mutationError);\n return (\n <div\n className=\"og-root mx-auto mb-2 w-full max-w-3xl shrink-0 px-4 sm:px-6\"\n data-testid=\"queue-surface\"\n >\n <div className=\"overflow-hidden rounded-lg border border-border bg-surface/80 shadow-sm\">\n {queue.stoppingPreviousAttempt ? (\n <QueueStoppingStatus queue={queue} dividerAfter={hasError} />\n ) : null}\n {hasError ? <QueueErrorAlert queue={queue} /> : null}\n </div>\n </div>\n );\n}\n\nexport function QueueStoppingStatus({\n queue,\n dividerAfter = false,\n}: QueueStateProps & { dividerAfter?: boolean }) {\n return (\n <div\n role=\"status\"\n aria-live=\"polite\"\n className={`flex min-h-11 items-center gap-2.5 bg-status-waiting/[0.07] px-3 py-2 text-og-control text-fg ${\n dividerAfter ? \"border-b border-status-waiting/20\" : \"\"\n }`}\n data-testid=\"stopping-previous-attempt\"\n >\n <Loader2Icon\n aria-hidden=\"true\"\n className=\"size-3.5 shrink-0 animate-spin text-status-waiting motion-reduce:animate-none\"\n />\n <span className=\"min-w-0\">\n <span className=\"font-medium\">\n {queue.effectiveControl?.state === \"paused\"\n ? \"Stopping current attempt…\"\n : \"Stopping previous attempt…\"}\n </span>{\" \"}\n <span className=\"text-fg-muted\">\n {queue.effectiveControl?.state === \"paused\"\n ? \"Queued work stays saved until you resume.\"\n : \"Queued work is saved and starts automatically.\"}\n </span>\n </span>\n </div>\n );\n}\n\nexport function QueueErrorAlert({\n queue,\n dividerBefore = false,\n}: QueueStateProps & { dividerBefore?: boolean }) {\n const formatError = useErrorMessage();\n return (\n <div className={`${dividerBefore ? \"border-t border-border\" : \"\"} p-2`}>\n <div\n role=\"alert\"\n className=\"flex min-w-0 max-w-full flex-wrap items-start gap-2 rounded-md bg-status-failed/10 px-2 py-1.5 text-og-control text-status-failed\"\n >\n <span\n role=\"region\"\n aria-label=\"Queue error details\"\n tabIndex={0}\n className=\"max-h-[min(5rem,20dvh)] min-w-0 max-w-full flex-[1_1_5rem] overflow-auto overscroll-contain whitespace-pre-wrap rounded-sm outline-hidden [overflow-wrap:anywhere] [unicode-bidi:plaintext] focus-visible:ring-2 focus-visible:ring-ring/40\"\n data-testid=\"queue-error-message\"\n dir=\"auto\"\n >\n {formatError(queue.mutationError ?? queue.error)}\n </span>\n <button\n type=\"button\"\n onClick={() => {\n queue.clearMutationError();\n void queue.refresh();\n }}\n aria-label=\"Dismiss queue error and retry\"\n title=\"Retry loading the queue\"\n className=\"ms-auto inline-flex size-7 shrink-0 items-center justify-center self-start rounded-md outline-hidden transition-colors hover:bg-surface-3 focus-visible:ring-2 focus-visible:ring-ring/40 motion-reduce:transition-none pointer-coarse:size-[44px]\"\n >\n <RotateCwIcon className=\"size-3.5\" />\n </button>\n </div>\n </div>\n );\n}\n","/**\n * One presentation rule for a queued item, shared by the full queue and the\n * compact session-chrome dock so the two cannot drift. A turn with an empty\n * prompt and timeline annotations is valid content; an item with neither is a\n * malformed or legacy object that gets an explicit fallback, never a blank row.\n */\nexport type QueueItemContent = \"text\" | \"annotations\" | \"unavailable\";\n\nexport const QUEUE_ITEM_CONTENT_UNAVAILABLE = \"Content unavailable\";\n\nexport function queueItemContent(text: string, annotationCount: number): QueueItemContent {\n if (text.length > 0) return \"text\";\n return annotationCount > 0 ? \"annotations\" : \"unavailable\";\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AACA,SAAS,WAAW,aAAa;AACjC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAGK;AAyCH,cAgCU,YAhCV;AApBJ,IAAM,4BAA4B,KAAK,MAAM,OAAO,2CAA+B,CAAC;AAEpF,SAAS,2BAA2B;AAAA,EAClC;AAAA,EACA;AACF,GAGG;AACD,QAAM,gBAAgB,gCAAgC;AACtD,QAAM,sBAAsB,OAA2B,IAAI;AAC3D,MAAI,CAAC,eAAe;AAClB,wBAAoB,UAAU,4BAA4B,WAAW,OAAO;AAAA,EAC9E;AACA,kBAAgB,MAAM;AACpB,QAAI,cAAe;AACnB,wBAAoB,UAAU,4BAA4B,WAAW,OAAO;AAAA,EAC9E,CAAC;AACD,MAAI,cAAe,QAAO;AAC1B,SACE,oBAAC,oCAAoC,UAApC,EAA6C,OAAO,qBAClD,UACH;AAEJ;AAEO,SAAS,wBAAwB;AAAA,EACtC;AAAA,EACA;AAAA,EACA;AACF,GAIG;AACD,QAAM,CAAC,eAAe,gBAAgB,IAAI,SAAwB,IAAI;AACtE,MAAI,YAAY,WAAW,EAAG,QAAO;AACrC,SACE;AAAA,IAAC;AAAA;AAAA,MACC,4BAAyB;AAAA,MACzB,WAAW;AAAA,QACT;AAAA,QACA,YAAY,UAAU,mCACpB;AAAA,QACF;AAAA,MACF;AAAA,MAEC,sBAAY,IAAI,CAAC,YAAY,UAAU;AACtC,cAAM,UAAU,yBAAyB,YAAY,KAAK;AAC1D,eACE,oBAAC,aAA4B,cAAY,cAAc,OAAO,IAC5D,+BAAC,uBACC;AAAA,+BAAC,OAAE,WAAU,gEAA+D;AAAA;AAAA,YAC9D;AAAA,aACd;AAAA,UACA;AAAA,YAAC;AAAA;AAAA,cACC;AAAA,cACA,OAAO;AAAA,cACP;AAAA,cACA,eAAe;AAAA;AAAA,UACjB;AAAA,UACC,kBAAkB,WAAW,KAC5B,oBAAC,OAAE,MAAK,UAAS,WAAU,qCAAoC,2DAE/D,IACE;AAAA,UACJ;AAAA,YAAC;AAAA;AAAA,cACC,MAAM,WAAW;AAAA,cACjB,cAAc,WAAW;AAAA,cACzB;AAAA;AAAA,UACF;AAAA,WACF,KArBY,WAAW,EAsBzB;AAAA,MAEJ,CAAC;AAAA;AAAA,EACH;AAEJ;AAEO,SAAS,wBAAwB;AAAA,EACtC;AAAA,EACA,WAAW;AAAA,EACX;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,UAAU;AAAA,EACV;AACF,GAaG;AACD,QAAM,iBAAiB;AAAA,IACrB,qBAAqB,YAAY,KAAK,CAAC,SAAS,KAAK,OAAO,iBAAiB;AAAA,EAC/E;AACA,QAAM,CAAC,MAAM,OAAO,IAAI,SAAS,cAAc;AAC/C,QAAM,aAAa,OAAiC,IAAI;AACxD,QAAM,gBAAgB,OAAsB,iBAAkB,qBAAqB,OAAQ,IAAI;AAE/F,YAAU,MAAM;AACd,QAAI,CAAC,kBAAkB,CAAC,mBAAmB;AACzC,UAAI,CAAC,eAAgB,eAAc,UAAU;AAC7C;AAAA,IACF;AACA,QAAI,cAAc,YAAY,kBAAmB;AACjD,kBAAc,UAAU;AACxB,YAAQ,IAAI;AAAA,EACd,GAAG,CAAC,mBAAmB,cAAc,CAAC;AAEtC,MAAI,YAAY,WAAW,EAAG,QAAO;AACrC,QAAM,aAAa,GAAG,YAAY,MAAM,IAAI,YAAY,WAAW,IAAI,eAAe,aAAa;AACnG,QAAM,aAAa,YAAY,KAAK,CAAC,eAAe,CAAC,kBAAkB,WAAW,IAAI,CAAC;AACvF,QAAM,WAAW,QAAQ,YAAY,QAAQ;AAE7C,QAAM,UAAU,CAAC,iBAA0B;AACzC,YAAQ,KAAK;AACb,sBAAkB;AAClB,QAAI,aAAc,YAAW,SAAS,MAAM;AAAA,EAC9C;AAEA,SACE,qBAAC,UAAK,WAAW,GAAG,uCAAuC,SAAS,GAClE;AAAA;AAAA,MAAC;AAAA;AAAA,QACC,WAAW;AAAA,UACT;AAAA,UACA,aAAa,6CAA6C;AAAA,QAC5D;AAAA,QAEA;AAAA;AAAA,YAAC;AAAA;AAAA,cACC,KAAK;AAAA,cACL,MAAK;AAAA,cACL,WAAW;AAAA,gBACT;AAAA,gBACA,UAAU,8BAA8B;AAAA,gBACxC,WAAW,gBAAgB;AAAA,cAC7B;AAAA,cACA,cAAY,UAAU,UAAU;AAAA,cAChC,iBAAc;AAAA,cACd,iBAAe;AAAA,cACf,SAAS,MAAM,QAAQ,CAAC,YAAY,CAAC,OAAO;AAAA,cAE5C;AAAA,oCAAC,aAAU,WAAU,oCAAmC,eAAY,QAAO;AAAA,gBAC3E,oBAAC,UAAK,WAAU,oBAAoB,sBAAW;AAAA;AAAA;AAAA,UACjD;AAAA,UACC,WACC;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,WAAU;AAAA,cACV,cAAW;AAAA,cACX,SAAS,MAAM;AACb,2BAAW,cAAc,YAAa,YAAW,WAAW,EAAE;AAAA,cAChE;AAAA,cAEA,8BAAC,SAAM,WAAU,YAAW,eAAY,QAAO;AAAA;AAAA,UACjD,IACE;AAAA;AAAA;AAAA,IACN;AAAA,IACC,OACC,oBAAC,8BAA2B,YAC1B,8BAAC,YAAS,UAAU,MAClB;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,WAAW;AAAA,QACX,UAAU,MAAM;AACd,kBAAQ,CAAC,QAAQ;AACjB,qBAAW;AAAA,QACb;AAAA;AAAA,IACF,GACF,GACF,IACE;AAAA,KACN;AAEJ;;;AC1NO,SAAS,sBACd,UACA,oBACA,iBACM;AACN,MAAI,SAAS,gBAAgB,GAAG;AAC9B,uBAAmB;AAAA,EACrB,OAAO;AACL,oBAAgB;AAAA,EAClB;AACF;;;ACjBA,SAAS,aAAa,oBAAoB;AAgBpC,SAEI,OAAAA,MAFJ,QAAAC,aAAA;AAPC,SAAS,uBAAuB,EAAE,MAAM,GAAoB;AACjE,QAAM,WAAW,QAAQ,MAAM,SAAS,MAAM,aAAa;AAC3D,SACE,gBAAAD;AAAA,IAAC;AAAA;AAAA,MACC,WAAU;AAAA,MACV,eAAY;AAAA,MAEZ,0BAAAC,MAAC,SAAI,WAAU,2EACZ;AAAA,cAAM,0BACL,gBAAAD,KAAC,uBAAoB,OAAc,cAAc,UAAU,IACzD;AAAA,QACH,WAAW,gBAAAA,KAAC,mBAAgB,OAAc,IAAK;AAAA,SAClD;AAAA;AAAA,EACF;AAEJ;AAEO,SAAS,oBAAoB;AAAA,EAClC;AAAA,EACA,eAAe;AACjB,GAAiD;AAC/C,SACE,gBAAAC;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,aAAU;AAAA,MACV,WAAW,iGACT,eAAe,sCAAsC,EACvD;AAAA,MACA,eAAY;AAAA,MAEZ;AAAA,wBAAAD;AAAA,UAAC;AAAA;AAAA,YACC,eAAY;AAAA,YACZ,WAAU;AAAA;AAAA,QACZ;AAAA,QACA,gBAAAC,MAAC,UAAK,WAAU,WACd;AAAA,0BAAAD,KAAC,UAAK,WAAU,eACb,gBAAM,kBAAkB,UAAU,WAC/B,mCACA,mCACN;AAAA,UAAQ;AAAA,UACR,gBAAAA,KAAC,UAAK,WAAU,iBACb,gBAAM,kBAAkB,UAAU,WAC/B,8CACA,kDACN;AAAA,WACF;AAAA;AAAA;AAAA,EACF;AAEJ;AAEO,SAAS,gBAAgB;AAAA,EAC9B;AAAA,EACA,gBAAgB;AAClB,GAAkD;AAChD,QAAM,cAAc,gBAAgB;AACpC,SACE,gBAAAA,KAAC,SAAI,WAAW,GAAG,gBAAgB,2BAA2B,EAAE,QAC9D,0BAAAC;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,WAAU;AAAA,MAEV;AAAA,wBAAAD;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,cAAW;AAAA,YACX,UAAU;AAAA,YACV,WAAU;AAAA,YACV,eAAY;AAAA,YACZ,KAAI;AAAA,YAEH,sBAAY,MAAM,iBAAiB,MAAM,KAAK;AAAA;AAAA,QACjD;AAAA,QACA,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,SAAS,MAAM;AACb,oBAAM,mBAAmB;AACzB,mBAAK,MAAM,QAAQ;AAAA,YACrB;AAAA,YACA,cAAW;AAAA,YACX,OAAM;AAAA,YACN,WAAU;AAAA,YAEV,0BAAAA,KAAC,gBAAa,WAAU,YAAW;AAAA;AAAA,QACrC;AAAA;AAAA;AAAA,EACF,GACF;AAEJ;;;ACvFO,IAAM,iCAAiC;AAEvC,SAAS,iBAAiB,MAAc,iBAA2C;AACxF,MAAI,KAAK,SAAS,EAAG,QAAO;AAC5B,SAAO,kBAAkB,IAAI,gBAAgB;AAC/C;","names":["jsx","jsxs"]}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// src/lib/error-message.ts
|
|
2
|
+
import { formatErrorMessage } from "@opengeni/sdk";
|
|
3
|
+
import { createContext, useCallback, useContext, useRef } from "react";
|
|
4
|
+
var ErrorMessageContext = createContext(void 0);
|
|
5
|
+
var originalCauses = /* @__PURE__ */ new WeakMap();
|
|
6
|
+
function normalizeError(cause) {
|
|
7
|
+
if (cause instanceof Error) return cause;
|
|
8
|
+
const error = new Error(String(cause));
|
|
9
|
+
originalCauses.set(error, cause);
|
|
10
|
+
return error;
|
|
11
|
+
}
|
|
12
|
+
function useErrorMessage() {
|
|
13
|
+
const formatter = useContext(ErrorMessageContext);
|
|
14
|
+
const formatterRef = useRef(formatter);
|
|
15
|
+
formatterRef.current = formatter;
|
|
16
|
+
return useCallback((error, fallback) => {
|
|
17
|
+
const original = error instanceof Error && originalCauses.has(error) ? originalCauses.get(error) : error;
|
|
18
|
+
const defaultMessage = fallback ?? formatErrorMessage(original);
|
|
19
|
+
try {
|
|
20
|
+
const message = formatterRef.current?.(original, defaultMessage);
|
|
21
|
+
return typeof message === "string" && message ? message : defaultMessage;
|
|
22
|
+
} catch {
|
|
23
|
+
return defaultMessage;
|
|
24
|
+
}
|
|
25
|
+
}, []);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export {
|
|
29
|
+
ErrorMessageContext,
|
|
30
|
+
normalizeError,
|
|
31
|
+
useErrorMessage
|
|
32
|
+
};
|
|
33
|
+
//# sourceMappingURL=chunk-UGLEYEMP.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/lib/error-message.ts"],"sourcesContent":["import { formatErrorMessage } from \"@opengeni/sdk\";\nimport { createContext, useCallback, useContext, useRef } from \"react\";\n\n/** Original diagnostic error and the library's neutral, state-aware display copy. */\nexport type ErrorMessageFormatter = (error: unknown, defaultMessage: string) => string | undefined;\n\nexport const ErrorMessageContext = createContext<ErrorMessageFormatter | undefined>(undefined);\n\nconst originalCauses = new WeakMap<Error, unknown>();\n\n/** Keep existing Error-typed state while retaining non-Error rejections privately. */\nexport function normalizeError(cause: unknown): Error {\n if (cause instanceof Error) return cause;\n const error = new Error(String(cause));\n originalCauses.set(error, cause);\n return error;\n}\n\n/** Presentation only: callers retain their errors and delivery/retry state. */\nexport function useErrorMessage(): (error: unknown, defaultMessage?: string) => string {\n const formatter = useContext(ErrorMessageContext);\n const formatterRef = useRef(formatter);\n formatterRef.current = formatter;\n return useCallback((error, fallback) => {\n const original =\n error instanceof Error && originalCauses.has(error) ? originalCauses.get(error) : error;\n const defaultMessage = fallback ?? formatErrorMessage(original);\n try {\n const message = formatterRef.current?.(original, defaultMessage);\n return typeof message === \"string\" && message ? message : defaultMessage;\n } catch {\n // Host presentation must never interrupt delivery-state settlement or\n // cause an already-started mutation to be retried.\n return defaultMessage;\n }\n }, []);\n}\n"],"mappings":";AAAA,SAAS,0BAA0B;AACnC,SAAS,eAAe,aAAa,YAAY,cAAc;AAKxD,IAAM,sBAAsB,cAAiD,MAAS;AAE7F,IAAM,iBAAiB,oBAAI,QAAwB;AAG5C,SAAS,eAAe,OAAuB;AACpD,MAAI,iBAAiB,MAAO,QAAO;AACnC,QAAM,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC;AACrC,iBAAe,IAAI,OAAO,KAAK;AAC/B,SAAO;AACT;AAGO,SAAS,kBAAuE;AACrF,QAAM,YAAY,WAAW,mBAAmB;AAChD,QAAM,eAAe,OAAO,SAAS;AACrC,eAAa,UAAU;AACvB,SAAO,YAAY,CAAC,OAAO,aAAa;AACtC,UAAM,WACJ,iBAAiB,SAAS,eAAe,IAAI,KAAK,IAAI,eAAe,IAAI,KAAK,IAAI;AACpF,UAAM,iBAAiB,YAAY,mBAAmB,QAAQ;AAC9D,QAAI;AACF,YAAM,UAAU,aAAa,UAAU,UAAU,cAAc;AAC/D,aAAO,OAAO,YAAY,YAAY,UAAU,UAAU;AAAA,IAC5D,QAAQ;AAGN,aAAO;AAAA,IACT;AAAA,EACF,GAAG,CAAC,CAAC;AACP;","names":[]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/lib/format.ts
|
|
2
|
-
import { OpenGeniApiError } from "@opengeni/sdk";
|
|
2
|
+
import { formatErrorMessage, OpenGeniApiError } from "@opengeni/sdk";
|
|
3
3
|
var clockTimeFormatter = new Intl.DateTimeFormat(void 0, {
|
|
4
4
|
month: "short",
|
|
5
5
|
day: "numeric",
|
|
@@ -88,18 +88,53 @@ function tryParseJson(text) {
|
|
|
88
88
|
return void 0;
|
|
89
89
|
}
|
|
90
90
|
}
|
|
91
|
-
var CREDIT_EXHAUSTION_MESSAGE = "Out of
|
|
91
|
+
var CREDIT_EXHAUSTION_MESSAGE = "Out of credits \u2014 this workspace's balance is empty. Add credits to continue; the conversation is preserved.";
|
|
92
92
|
var COMPOSER_MEMBER_ALLOWANCE_MESSAGE = "Usage limit reached. A workspace admin can raise this limit. Your draft is preserved.";
|
|
93
93
|
var COMPOSER_WORKSPACE_ALLOWANCE_MESSAGE = "Workspace usage limit reached. An organization admin can raise the workspace budget. Your draft is preserved.";
|
|
94
|
-
var COMPOSER_PAYMENT_REQUIRED_MESSAGE = "Your organization doesn't have enough
|
|
94
|
+
var COMPOSER_PAYMENT_REQUIRED_MESSAGE = "Your organization doesn't have enough credits to send this message. Add credits or choose a model with another payment source. Your message and attachments are saved.";
|
|
95
|
+
var ComposerStateError = class extends Error {
|
|
96
|
+
};
|
|
97
|
+
var ComposerReconciliationRequiredError = class extends ComposerStateError {
|
|
98
|
+
constructor() {
|
|
99
|
+
super(
|
|
100
|
+
"Opengeni cannot safely retry this uncertain request after remount; reconcile the session before sending again."
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
var ComposerWorkspaceControlUnavailableError = class extends Error {
|
|
105
|
+
constructor() {
|
|
106
|
+
super("@opengeni/react: workspace-scoped resume requires setWorkspaceInferenceState.");
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
var COMPOSER_MODEL_UNAVAILABLE_MESSAGE = "This chat's model is no longer available. Choose another model to continue. Your message is saved.";
|
|
110
|
+
var MODEL_UNAVAILABLE_DETAIL_CODE = "model_unavailable";
|
|
111
|
+
function isModelUnavailableSubmissionError(error) {
|
|
112
|
+
if (error instanceof OpenGeniApiError && error.details?.code === MODEL_UNAVAILABLE_DETAIL_CODE) {
|
|
113
|
+
return true;
|
|
114
|
+
}
|
|
115
|
+
return error.message === COMPOSER_MODEL_UNAVAILABLE_MESSAGE || /^OpenGeni API 422: model is not available: /.test(error.message);
|
|
116
|
+
}
|
|
95
117
|
function composerSubmissionErrorMessage(error) {
|
|
118
|
+
if (error instanceof ComposerReconciliationRequiredError) {
|
|
119
|
+
return "This client cannot safely retry this uncertain request after remount; reconcile the session before sending again.";
|
|
120
|
+
}
|
|
121
|
+
if (error instanceof ComposerWorkspaceControlUnavailableError) {
|
|
122
|
+
return "This client cannot resume the workspace. Ask your administrator for a control-capable client.";
|
|
123
|
+
}
|
|
124
|
+
if (error instanceof ComposerStateError) return error.message;
|
|
125
|
+
if (error instanceof OpenGeniApiError && error.outcomeUnknown) return formatErrorMessage(error);
|
|
126
|
+
if (isModelUnavailableSubmissionError(error)) return COMPOSER_MODEL_UNAVAILABLE_MESSAGE;
|
|
96
127
|
if (error instanceof OpenGeniApiError && error.code === "allowance_exhausted") {
|
|
97
|
-
|
|
128
|
+
const message = error.scope === "workspace" ? COMPOSER_WORKSPACE_ALLOWANCE_MESSAGE : COMPOSER_MEMBER_ALLOWANCE_MESSAGE;
|
|
129
|
+
return composerErrorReference(error, message);
|
|
98
130
|
}
|
|
99
|
-
return isComposerCreditRefusal(error) ? COMPOSER_PAYMENT_REQUIRED_MESSAGE : error
|
|
131
|
+
return isComposerCreditRefusal(error) ? composerErrorReference(error, COMPOSER_PAYMENT_REQUIRED_MESSAGE) : formatErrorMessage(error);
|
|
132
|
+
}
|
|
133
|
+
function composerErrorReference(error, message) {
|
|
134
|
+
return error instanceof OpenGeniApiError && error.correlationId ? `${message} Reference: ${error.correlationId}.` : message;
|
|
100
135
|
}
|
|
101
136
|
function composerSubmissionCanRetry(error) {
|
|
102
|
-
return !(isComposerCreditRefusal(error) || error instanceof OpenGeniApiError && error.code === "allowance_exhausted");
|
|
137
|
+
return !(isComposerCreditRefusal(error) || isModelUnavailableSubmissionError(error) || error instanceof OpenGeniApiError && error.code === "allowance_exhausted");
|
|
103
138
|
}
|
|
104
139
|
function isComposerCreditRefusal(error) {
|
|
105
140
|
return error instanceof OpenGeniApiError && error.status === 402 && (error.code === "payment_required" || error.code === "insufficient_credits") || isCreditExhaustion(error.message);
|
|
@@ -139,7 +174,7 @@ function humanizeFailureReason(reason) {
|
|
|
139
174
|
function presentFailure(payload) {
|
|
140
175
|
const databaseFailure = payload.code === "db_deadlock" || payload.code === "db_serialization_failure" || payload.code === "db_failure" || typeof payload.sqlState === "string" && /^[0-9A-Z]{5}$/.test(payload.sqlState) && payload.database !== null && typeof payload.database === "object" && !Array.isArray(payload.database);
|
|
141
176
|
if (databaseFailure) {
|
|
142
|
-
return { reason: "
|
|
177
|
+
return { reason: "The service encountered a database error.", safetyRefusal: false };
|
|
143
178
|
}
|
|
144
179
|
const text = (key) => {
|
|
145
180
|
const value = payload[key];
|
|
@@ -171,10 +206,15 @@ export {
|
|
|
171
206
|
tryParseJson,
|
|
172
207
|
CREDIT_EXHAUSTION_MESSAGE,
|
|
173
208
|
COMPOSER_PAYMENT_REQUIRED_MESSAGE,
|
|
209
|
+
ComposerStateError,
|
|
210
|
+
ComposerReconciliationRequiredError,
|
|
211
|
+
ComposerWorkspaceControlUnavailableError,
|
|
212
|
+
COMPOSER_MODEL_UNAVAILABLE_MESSAGE,
|
|
213
|
+
isModelUnavailableSubmissionError,
|
|
174
214
|
composerSubmissionErrorMessage,
|
|
175
215
|
composerSubmissionCanRetry,
|
|
176
216
|
isCreditExhaustion,
|
|
177
217
|
humanizeFailureReason,
|
|
178
218
|
presentFailure
|
|
179
219
|
};
|
|
180
|
-
//# sourceMappingURL=chunk-
|
|
220
|
+
//# sourceMappingURL=chunk-UTRTG6LH.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/lib/format.ts"],"sourcesContent":["import { formatErrorMessage, OpenGeniApiError } from \"@opengeni/sdk\";\n\nconst clockTimeFormatter = new Intl.DateTimeFormat(undefined, {\n month: \"short\",\n day: \"numeric\",\n hour: \"numeric\",\n minute: \"2-digit\",\n});\n\n/** Local finished-at stamp for message footers: \"Aug 2, 3:42 PM\" / locale-equivalent. */\nexport function formatClockTime(iso: string): string {\n const then = new Date(iso);\n if (Number.isNaN(then.getTime())) {\n return \"\";\n }\n return clockTimeFormatter.format(then);\n}\n\n/** Compact relative time: \"now\", \"42s\", \"7m\", \"3h\", \"2d\", then a date. */\nexport function formatRelativeTime(iso: string, now: Date = new Date()): string {\n const then = new Date(iso).getTime();\n if (Number.isNaN(then)) {\n return \"\";\n }\n const seconds = Math.max(0, Math.floor((now.getTime() - then) / 1000));\n if (seconds < 10) {\n return \"now\";\n }\n if (seconds < 60) {\n return `${seconds}s`;\n }\n const minutes = Math.floor(seconds / 60);\n if (minutes < 60) {\n return `${minutes}m`;\n }\n const hours = Math.floor(minutes / 60);\n if (hours < 24) {\n return `${hours}h`;\n }\n const days = Math.floor(hours / 24);\n if (days < 14) {\n return `${days}d`;\n }\n return new Date(iso).toLocaleDateString();\n}\n\n/** Human-readable byte size: \"512 B\", \"8.0 KB\", \"1.4 MB\", \"3 GB\". */\nexport function formatBytes(bytes: number): string {\n if (bytes < 1024) {\n return `${bytes} B`;\n }\n const units = [\"KB\", \"MB\", \"GB\"] as const;\n let value = bytes / 1024;\n for (const unit of units) {\n if (value < 1024 || unit === \"GB\") {\n return `${value.toFixed(value < 10 ? 1 : 0)} ${unit}`;\n }\n value /= 1024;\n }\n return `${bytes} B`;\n}\n\n/** Single-line preview of arbitrary text, for tiles and collapsed rows. */\nexport function truncate(text: string, maxLength: number): string {\n const collapsed = text.replace(/\\s+/g, \" \").trim();\n if (collapsed.length <= maxLength) {\n return collapsed;\n }\n return `${collapsed.slice(0, Math.max(0, maxLength - 1)).trimEnd()}…`;\n}\n\n/** Render an unknown payload as readable text (pretty JSON when possible). */\nexport function stringifyPayload(value: unknown): string {\n if (value === null || value === undefined) {\n return \"\";\n }\n if (typeof value === \"string\") {\n const parsed = tryParseJson(value);\n if (parsed !== undefined && typeof parsed === \"object\") {\n return stringifyPayload(parsed);\n }\n return value;\n }\n try {\n return JSON.stringify(value, null, 2) ?? String(value);\n } catch {\n return String(value);\n }\n}\n\n/** JSON.parse that returns `undefined` instead of throwing. */\nexport function tryParseJson(text: string): unknown {\n const trimmed = text.trim();\n if (!trimmed.startsWith(\"{\") && !trimmed.startsWith(\"[\") && !trimmed.startsWith('\"')) {\n return undefined;\n }\n try {\n return JSON.parse(trimmed) as unknown;\n } catch {\n return undefined;\n }\n}\n\n/**\n * The canonical credit-death sentence. Credit exhaustion is the one failure a\n * user can fix themselves — the copy must say what happened (empty balance),\n * what to do (add credits), and what is safe (nothing was lost). Crucially it\n * must NOT say \"send a message to revive\": a revive turn burns credits the\n * workspace no longer has.\n */\nexport const CREDIT_EXHAUSTION_MESSAGE =\n \"Out of credits — this workspace's balance is empty. Add credits to continue; the conversation is preserved.\";\n\n/** A usage ceiling refused the send; the default allowance wording, plus what is safe. */\nexport const COMPOSER_MEMBER_ALLOWANCE_MESSAGE =\n \"Usage limit reached. A workspace admin can raise this limit. Your draft is preserved.\";\nexport const COMPOSER_WORKSPACE_ALLOWANCE_MESSAGE =\n \"Workspace usage limit reached. An organization admin can raise the workspace budget. Your draft is preserved.\";\n\n/**\n * Actionable composer copy for an edge rejection before a turn is accepted.\n * Unlike an in-flight credit exhaustion, this path has not consumed the\n * actor-private draft or any finalized attachment.\n */\nexport const COMPOSER_PAYMENT_REQUIRED_MESSAGE =\n \"Your organization doesn't have enough credits to send this message. Add credits or choose a model with another payment source. Your message and attachments are saved.\";\n\n/** Trusted, composer-owned guidance; unlike remote diagnostic prose, this is UI copy. */\nexport class ComposerStateError extends Error {}\n\n/** A restored request has omitted credentials and must be reconciled, not replayed. */\nexport class ComposerReconciliationRequiredError extends ComposerStateError {\n constructor() {\n super(\n \"Opengeni cannot safely retry this uncertain request after remount; reconcile the session before sending again.\",\n );\n }\n}\n\nexport class ComposerWorkspaceControlUnavailableError extends Error {\n constructor() {\n super(\"@opengeni/react: workspace-scoped resume requires setWorkspaceInferenceState.\");\n }\n}\n\n/**\n * The send named a model that is no longer in the live catalog (retired or\n * removed). Retrying the same send cannot succeed; the person must choose\n * another model, and the typed message stays recoverable via Edit message.\n */\nexport const COMPOSER_MODEL_UNAVAILABLE_MESSAGE =\n \"This chat's model is no longer available. Choose another model to continue. Your message is saved.\";\n\n/** `details.code` the API sets on a 422 for a model missing from the live catalog. */\nexport const MODEL_UNAVAILABLE_DETAIL_CODE = \"model_unavailable\";\n\n/**\n * Is this send refusal \"the model is no longer available\"? Reads the typed\n * `details.code`, and also the historical message so a refusal persisted\n * before a reload (only its text survives) or from an older API is recognized.\n */\nexport function isModelUnavailableSubmissionError(error: Error): boolean {\n if (error instanceof OpenGeniApiError && error.details?.code === MODEL_UNAVAILABLE_DETAIL_CODE) {\n return true;\n }\n return (\n error.message === COMPOSER_MODEL_UNAVAILABLE_MESSAGE ||\n /^OpenGeni API 422: model is not available: /.test(error.message)\n );\n}\n\nexport function composerSubmissionErrorMessage(error: Error): string {\n if (error instanceof ComposerReconciliationRequiredError) {\n return \"This client cannot safely retry this uncertain request after remount; reconcile the session before sending again.\";\n }\n if (error instanceof ComposerWorkspaceControlUnavailableError) {\n return \"This client cannot resume the workspace. Ask your administrator for a control-capable client.\";\n }\n if (error instanceof ComposerStateError) return error.message;\n if (error instanceof OpenGeniApiError && error.outcomeUnknown) return formatErrorMessage(error);\n if (isModelUnavailableSubmissionError(error)) return COMPOSER_MODEL_UNAVAILABLE_MESSAGE;\n if (error instanceof OpenGeniApiError && error.code === \"allowance_exhausted\") {\n // OpenGeniAllowanceExhaustedError carries the scope; read it structurally\n // so this startup-path helper adds no SDK or wording imports.\n const message =\n (error as { scope?: unknown }).scope === \"workspace\"\n ? COMPOSER_WORKSPACE_ALLOWANCE_MESSAGE\n : COMPOSER_MEMBER_ALLOWANCE_MESSAGE;\n return composerErrorReference(error, message);\n }\n return isComposerCreditRefusal(error)\n ? composerErrorReference(error, COMPOSER_PAYMENT_REQUIRED_MESSAGE)\n : formatErrorMessage(error);\n}\n\nfunction composerErrorReference(error: Error, message: string): string {\n return error instanceof OpenGeniApiError && error.correlationId\n ? `${message} Reference: ${error.correlationId}.`\n : message;\n}\n\n/** These definitive refusals need a payment, allowance or model change, not an unchanged retry. */\nexport function composerSubmissionCanRetry(error: Error): boolean {\n return !(\n isComposerCreditRefusal(error) ||\n isModelUnavailableSubmissionError(error) ||\n (error instanceof OpenGeniApiError && error.code === \"allowance_exhausted\")\n );\n}\n\nfunction isComposerCreditRefusal(error: Error): boolean {\n return (\n (error instanceof OpenGeniApiError &&\n error.status === 402 &&\n (error.code === \"payment_required\" || error.code === \"insufficient_credits\")) ||\n isCreditExhaustion(error.message)\n );\n}\n\n/**\n * Does this failure/completion payload (or raw error string) mean the\n * workspace ran out of OpenGeni credits? Matches the engine's\n * \"insufficient OpenGeni credits\" text (case-insensitive, substring — it\n * arrives both bare and wrapped in \"Activity task failed: …\") and the\n * budget-exhausted segment limit the engine stamps on a turn it ended early.\n */\nexport function isCreditExhaustion(\n input: { error?: string | null; detail?: string | null; segmentLimit?: string | null } | string,\n): boolean {\n if (typeof input === \"string\") {\n return input.toLowerCase().includes(\"insufficient opengeni credits\");\n }\n if (input.segmentLimit === \"budget_exhausted\") {\n return true;\n }\n for (const text of [input.error, input.detail]) {\n if (typeof text === \"string\" && text.toLowerCase().includes(\"insufficient opengeni credits\")) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Humanize engine/provider failure text before it reaches the timeline or a\n * failure banner. Raw provider errors leak the wrong audience's instructions —\n * \"Incorrect API key … find your API key at platform.openai.com\" tells a\n * managed-deployment USER to fix credentials only an OPERATOR controls (and is\n * flatly wrong for Azure or subscription-backed engines). Auth, quota, and\n * credit-exhaustion failures collapse to one neutral, honest sentence; every\n * other reason passes through untouched. Raw payloads stay available in the\n * debug surfaces.\n */\nexport function humanizeFailureReason(reason: string | null): string | null {\n if (!reason) {\n return reason;\n }\n if (isCreditExhaustion(reason)) {\n return CREDIT_EXHAUSTION_MESSAGE;\n }\n const normalized = reason.toLowerCase();\n const authFailure =\n normalized.includes(\"incorrect api key\") ||\n normalized.includes(\"invalid api key\") ||\n normalized.includes(\"invalid_api_key\") ||\n normalized.includes(\"platform.openai.com/account/api-keys\") ||\n (normalized.includes(\"401\") &&\n (normalized.includes(\"api key\") || normalized.includes(\"unauthorized\")));\n if (authFailure) {\n return \"The model provider rejected this deployment's engine credentials. Sending messages won't help until the deployment's engine configuration is fixed.\";\n }\n const quotaFailure =\n normalized.includes(\"insufficient_quota\") || normalized.includes(\"exceeded your current quota\");\n if (quotaFailure) {\n return \"The model provider refused the request: this deployment's provider quota is exhausted.\";\n }\n return reason;\n}\n\n/** Project failure diagnostics without changing the stored event, including legacy retry wrappers. */\nexport function presentFailure(payload: Record<string, unknown>): {\n reason: string | null;\n safetyRefusal: boolean;\n} {\n const databaseFailure =\n payload.code === \"db_deadlock\" ||\n payload.code === \"db_serialization_failure\" ||\n payload.code === \"db_failure\" ||\n (typeof payload.sqlState === \"string\" &&\n /^[0-9A-Z]{5}$/.test(payload.sqlState) &&\n payload.database !== null &&\n typeof payload.database === \"object\" &&\n !Array.isArray(payload.database));\n if (databaseFailure) {\n return { reason: \"The service encountered a database error.\", safetyRefusal: false };\n }\n const text = (key: string): string | null => {\n const value = payload[key];\n return typeof value === \"string\" && value.trim() ? value : null;\n };\n const message = text(\"error\") ?? text(\"message\");\n const detail = text(\"lastRetryableError\") ?? text(\"detail\");\n const safetyRefusal =\n payload.code === \"provider_safety_refusal\" ||\n [message, detail].some(\n (value) =>\n value !== null && /\\bthis request was blocked by our safety systems\\b/i.test(value),\n );\n if (safetyRefusal) {\n return {\n reason: `The model provider blocked this request.${detail || message ? ` ${detail ?? message}` : \"\"}`,\n safetyRefusal: true,\n };\n }\n return {\n reason:\n detail && detail !== message\n ? [humanizeFailureReason(message), humanizeFailureReason(detail)].filter(Boolean).join(\" \")\n : humanizeFailureReason(message),\n safetyRefusal: false,\n };\n}\n"],"mappings":"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","names":[]}
|
|
@@ -4,7 +4,10 @@ import {
|
|
|
4
4
|
usePageLiveActivity,
|
|
5
5
|
usePolledValue,
|
|
6
6
|
useSessionEventTrigger
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-YQEAA6LT.js";
|
|
8
|
+
import {
|
|
9
|
+
normalizeError
|
|
10
|
+
} from "./chunk-UGLEYEMP.js";
|
|
8
11
|
import {
|
|
9
12
|
useEmbeddedGoal,
|
|
10
13
|
useEmbeddedSessionLineage,
|
|
@@ -131,7 +134,7 @@ function useGoal(sessionId, options = {}) {
|
|
|
131
134
|
setGoal(null);
|
|
132
135
|
setError(null);
|
|
133
136
|
} else {
|
|
134
|
-
setError(
|
|
137
|
+
setError(normalizeError(cause));
|
|
135
138
|
}
|
|
136
139
|
setLoading(false);
|
|
137
140
|
} finally {
|
|
@@ -475,4 +478,4 @@ export {
|
|
|
475
478
|
isLineageRefreshEvent,
|
|
476
479
|
useSessionLineage
|
|
477
480
|
};
|
|
478
|
-
//# sourceMappingURL=chunk-
|
|
481
|
+
//# sourceMappingURL=chunk-XOAMRDDK.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/embedded-session-client.ts","../src/hooks/use-goal.ts","../src/hooks/use-session-mcp-approval-policy.ts","../src/hooks/use-session-lineage.ts"],"sourcesContent":["import type { ComposerDraft } from \"@opengeni/sdk\";\nimport type { EmbeddedSessionClientLike } from \"./client\";\n\nexport type ComposerDraftMapContext = {\n operation: \"read\" | \"save\" | \"submit\";\n workspaceId: string;\n sessionId: string;\n};\n\nexport type CreateEmbeddedSessionClientOptions = {\n /**\n * Explicit host-owned method replacements. Methods inherited from `base`\n * retain `base` as their receiver; overrides retain this object as theirs.\n */\n overrides?: Partial<EmbeddedSessionClientLike> | undefined;\n /** Presentation/policy projection for every draft entering the React layer. */\n mapComposerDraft?:\n | ((draft: ComposerDraft, context: ComposerDraftMapContext) => ComposerDraft)\n | undefined;\n};\n\nconst requiredMethods = [\n \"getSession\",\n \"listEvents\",\n \"streamEvents\",\n \"getComposerDraft\",\n \"saveComposerDraft\",\n \"submitComposerDraft\",\n \"sendMessage\",\n \"steerMessage\",\n \"getQueue\",\n \"moveQueueItem\",\n \"editQueueItem\",\n \"steerQueueItem\",\n \"deleteQueueItem\",\n \"pauseSession\",\n \"resumeSession\",\n \"sendApprovalDecision\",\n] as const satisfies readonly (keyof EmbeddedSessionClientLike)[];\n\n/**\n * Build the exact client required by `@opengeni/react/session` from a full SDK\n * client or host proxy. The constructor binds every delegated method once,\n * supports a host-owned atomic composer-submit override, and fails at creation\n * time instead of waiting for a missing method to be exercised by a hook.\n */\nexport function createEmbeddedSessionClient(\n base: EmbeddedSessionClientLike,\n options: CreateEmbeddedSessionClientOptions = {},\n): EmbeddedSessionClientLike {\n const overrides = options.overrides ?? {};\n const client = {} as EmbeddedSessionClientLike;\n for (const key of requiredMethods) {\n const override = overrides[key];\n const method = override ?? base[key];\n if (typeof method !== \"function\") {\n throw new TypeError(`Embedded session client method is required: ${key}`);\n }\n client[key] = method.bind(override === undefined ? base : overrides) as never;\n }\n\n const inferenceOverride = overrides.setWorkspaceInferenceState;\n const inferenceBase = base.setWorkspaceInferenceState;\n const inferenceMethod = inferenceOverride ?? inferenceBase;\n if (typeof inferenceMethod === \"function\") {\n client.setWorkspaceInferenceState = inferenceMethod.bind(\n inferenceOverride === undefined ? base : overrides,\n );\n }\n\n const mapDraft = options.mapComposerDraft;\n if (!mapDraft) return client;\n\n const getComposerDraft = client.getComposerDraft;\n client.getComposerDraft = async (workspaceId, sessionId) =>\n mapDraft(await getComposerDraft(workspaceId, sessionId), {\n operation: \"read\",\n workspaceId,\n sessionId,\n });\n\n const saveComposerDraft = client.saveComposerDraft;\n client.saveComposerDraft = async (workspaceId, sessionId, request) =>\n mapDraft(await saveComposerDraft(workspaceId, sessionId, request), {\n operation: \"save\",\n workspaceId,\n sessionId,\n });\n\n const submitComposerDraft = client.submitComposerDraft;\n client.submitComposerDraft = async (workspaceId, sessionId, request) => {\n const response = await submitComposerDraft(workspaceId, sessionId, request);\n return {\n ...response,\n draft: mapDraft(response.draft, {\n operation: \"submit\",\n workspaceId,\n sessionId,\n }),\n };\n };\n\n return client;\n}\n","import { OpenGeniApiError, type SessionEvent, type SessionGoal } from \"@opengeni/sdk\";\nimport { useCallback, useEffect, useRef, useState } from \"react\";\nimport { useEmbeddedGoal, type EmbeddedGoalClientOverride } from \"../session-context\";\nimport { normalizeError } from \"../lib/error-message\";\nimport {\n useDebouncedCallback,\n useMutationRunner,\n usePageLiveActivity,\n useSessionEventTrigger,\n type SessionEventFeedOptions,\n} from \"./internal\";\n\n/** Event types that change the session goal (set/updated/completed/paused/...). */\nexport function isGoalEvent(event: Pick<SessionEvent, \"type\">): boolean {\n return event.type.startsWith(\"goal.\");\n}\n\n/**\n * Events that can change the server-authoritative continuation projection.\n * The projection is derived from durable turn, session, control, and system\n * update state, so those events refresh the goal without model polling.\n */\nexport function isGoalRefreshEvent(event: Pick<SessionEvent, \"type\">): boolean {\n const type = event.type;\n return (\n isGoalEvent(event) ||\n type.startsWith(\"turn.\") ||\n type.startsWith(\"session.\") ||\n type.startsWith(\"system.update.\") ||\n type.startsWith(\"workspace.inference.\") ||\n type.startsWith(\"user.\")\n );\n}\n\nexport type UseGoalOptions = EmbeddedGoalClientOverride &\n SessionEventFeedOptions & {\n /** Optional safety-net polling (ms). Off by default — event refreshes drive updates. */\n pollIntervalMs?: number | undefined;\n };\n\nexport type UseGoalResult = {\n /** The session goal, or null when the session has none. */\n goal: SessionGoal | null;\n /** Convenience flags over `goal.status`. */\n isActive: boolean;\n isPaused: boolean;\n isCompleted: boolean;\n loading: boolean;\n error: Error | null;\n refresh: () => Promise<void>;\n /** Pause the goal loop (PATCH status=paused). */\n pause: (rationale?: string) => Promise<SessionGoal | null>;\n /** Resume a paused goal: resets counters and re-arms continuations. */\n resume: () => Promise<SessionGoal | null>;\n /** Clear the session goal; goal-less sessions remain a successful no-op. */\n clearGoal: () => Promise<void>;\n /** Alias for `clearGoal`. */\n deleteGoal: () => Promise<void>;\n /** True while a pause/resume/clear is in flight. */\n updating: boolean;\n mutationError: Error | null;\n clearMutationError: () => void;\n};\n\n/**\n * The session's goal: state, the autonomy counters (`autoContinuations`,\n * `noProgressStreak`), and pause/resume control. A goal-less session yields\n * `goal: null` (the 404 is absorbed). Live-updates on goal, turn, session,\n * control, and system-update events — pass `options.events` from\n * `useSessionEvents` to reuse its stream.\n */\nexport function useGoal(\n sessionId: string | null | undefined,\n options: UseGoalOptions = {},\n): UseGoalResult {\n const { client, workspaceId } = useEmbeddedGoal(options);\n const enabled = (options.enabled ?? true) && Boolean(sessionId);\n const sharedEvents = options.events;\n const sharedFeed = sharedEvents !== undefined;\n const pageLive = usePageLiveActivity();\n const [goal, setGoal] = useState<SessionGoal | null>(null);\n const [loading, setLoading] = useState(enabled);\n const [error, setError] = useState<Error | null>(null);\n const { run, mutating, mutationError, clearMutationError } = useMutationRunner();\n const generation = useRef(0);\n const targetKeyRef = useRef<string | null>(null);\n const loadAbort = useRef<AbortController | null>(null);\n\n const retireLoads = useCallback(() => {\n generation.current += 1;\n loadAbort.current?.abort();\n loadAbort.current = null;\n }, []);\n\n const load = useCallback(async (): Promise<void> => {\n if (!sessionId) {\n return;\n }\n const ticket = ++generation.current;\n loadAbort.current?.abort();\n const controller = new AbortController();\n loadAbort.current = controller;\n try {\n const fetched = await client.getGoal(workspaceId, sessionId, {\n signal: controller.signal,\n });\n if (ticket === generation.current) {\n setGoal(fetched);\n setError(null);\n setLoading(false);\n }\n } catch (cause) {\n if (ticket !== generation.current) {\n return;\n }\n if (cause instanceof OpenGeniApiError && cause.status === 404) {\n // No goal is a normal state, not an error.\n setGoal(null);\n setError(null);\n } else {\n setError(normalizeError(cause));\n }\n setLoading(false);\n } finally {\n if (loadAbort.current === controller) loadAbort.current = null;\n }\n }, [client, workspaceId, sessionId]);\n\n useEffect(() => {\n const targetKey = `${workspaceId} ${sessionId ?? \"\"}`;\n if (targetKeyRef.current !== targetKey) {\n targetKeyRef.current = targetKey;\n setGoal(null);\n setError(null);\n }\n if (!enabled || !pageLive) {\n retireLoads();\n setLoading(false);\n return;\n }\n if (sharedFeed) {\n // A shared log supplies invalidations, not the authoritative goal snapshot.\n setLoading(true);\n void load();\n return () => {\n retireLoads();\n };\n }\n setLoading(true);\n const pollIntervalMs = options.pollIntervalMs;\n if (pollIntervalMs === undefined || pollIntervalMs <= 0) {\n void load();\n return () => {\n retireLoads();\n };\n }\n let cancelled = false;\n let timer: ReturnType<typeof setTimeout> | null = null;\n const schedule = () => {\n if (cancelled) return;\n timer = setTimeout(() => {\n timer = null;\n void load().finally(schedule);\n }, pollIntervalMs);\n };\n void load().finally(schedule);\n return () => {\n cancelled = true;\n if (timer !== null) clearTimeout(timer);\n retireLoads();\n };\n }, [\n load,\n enabled,\n pageLive,\n workspaceId,\n sessionId,\n options.pollIntervalMs,\n retireLoads,\n sharedFeed,\n ]);\n\n const scheduleRefresh = useDebouncedCallback(() => void load());\n useSessionEventTrigger(client, workspaceId, sessionId, isGoalRefreshEvent, scheduleRefresh, {\n enabled,\n ...(sharedEvents !== undefined ? { events: sharedEvents } : {}),\n });\n\n const pause = useCallback(\n async (rationale?: string): Promise<SessionGoal | null> => {\n if (!sessionId) {\n return null;\n }\n const result = await run(() =>\n client.updateGoal(workspaceId, sessionId, {\n status: \"paused\",\n ...(rationale !== undefined ? { rationale } : {}),\n }),\n );\n if (result) {\n setGoal(result);\n }\n return result;\n },\n [client, workspaceId, sessionId, run],\n );\n\n const resume = useCallback(async (): Promise<SessionGoal | null> => {\n if (!sessionId) {\n return null;\n }\n const result = await run(() => client.updateGoal(workspaceId, sessionId, { status: \"active\" }));\n if (result) {\n setGoal(result);\n }\n return result;\n }, [client, workspaceId, sessionId, run]);\n\n const clearGoal = useCallback(async (): Promise<void> => {\n if (!sessionId) {\n return;\n }\n // deleteGoal resolves void, so distinguish success (a truthy sentinel) from\n // mutation.run's null-on-failure. Only a SUCCESSFUL delete hides the goal:\n // a failed one leaves the pill up so its mutationError renders. And invalidate\n // any in-flight load first so a slower getGoal started before the delete can't\n // commit and repopulate the just-cleared goal.\n const ok = await run(async () => {\n await client.deleteGoal(workspaceId, sessionId);\n return true as const;\n });\n if (ok) {\n retireLoads();\n setGoal(null);\n setError(null);\n }\n }, [client, workspaceId, sessionId, run, retireLoads]);\n\n return {\n goal,\n isActive: goal?.status === \"active\",\n isPaused: goal?.status === \"paused\",\n isCompleted: goal?.status === \"completed\",\n loading,\n error,\n refresh: load,\n pause,\n resume,\n clearGoal,\n deleteGoal: clearGoal,\n updating: mutating,\n mutationError,\n clearMutationError,\n };\n}\n","import type {\n SessionEvent,\n SessionMcpApprovalPolicy,\n SessionMcpApprovalPolicyTarget,\n UpdateSessionMcpApprovalPolicyResponse,\n} from \"@opengeni/sdk\";\nimport { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from \"react\";\nimport {\n useEmbeddedSessionMcpApprovalPolicy,\n type EmbeddedSessionMcpApprovalPolicyClientOverride,\n} from \"../session-context\";\nimport {\n useDebouncedCallback,\n useMutationRunner,\n usePolledValue,\n useSessionEventTrigger,\n type SessionEventFeedOptions,\n} from \"./internal\";\n\nexport function isSessionMcpApprovalPolicyEvent(\n event: Pick<SessionEvent, \"type\" | \"payload\">,\n serverId?: string,\n): boolean {\n if (event.type !== \"session.mcp.approval_policy.updated\") {\n return false;\n }\n if (serverId === undefined) {\n return true;\n }\n const payload = event.payload as { serverId?: unknown } | null;\n return payload?.serverId === serverId;\n}\n\nexport type UseSessionMcpApprovalPolicyOptions = EmbeddedSessionMcpApprovalPolicyClientOverride &\n SessionEventFeedOptions & {\n /** Optional safety-net polling (ms). Off by default; policy events drive refreshes. */\n pollIntervalMs?: number | undefined;\n };\n\nexport type UseSessionMcpApprovalPolicyResult = {\n server: SessionMcpApprovalPolicyTarget | null;\n policy: SessionMcpApprovalPolicy | null;\n loading: boolean;\n error: Error | null;\n refresh: () => Promise<void>;\n update: (\n policy: SessionMcpApprovalPolicy,\n ) => Promise<UpdateSessionMcpApprovalPolicyResponse | null>;\n updating: boolean;\n clearError: () => void;\n};\n\n/**\n * Read and update one existing session MCP server's approval policy. Updates\n * become effective for the next claimed attempt; current work is unchanged.\n */\nexport function useSessionMcpApprovalPolicy(\n sessionId: string | null | undefined,\n serverId: string | null | undefined,\n options: UseSessionMcpApprovalPolicyOptions = {},\n): UseSessionMcpApprovalPolicyResult {\n const { client, workspaceId } = useEmbeddedSessionMcpApprovalPolicy(options);\n const enabled = (options.enabled ?? true) && Boolean(sessionId && serverId);\n const [override, setOverride] = useState<SessionMcpApprovalPolicyTarget | null>(null);\n const targetKey = `${workspaceId}\\u0000${sessionId ?? \"\"}\\u0000${serverId ?? \"\"}`;\n const authoritativeGeneration = useRef(0);\n const mutationIdentity = useMemo(() => ({ client, targetKey }), [client, targetKey]);\n const { run, mutating, mutationError, clearMutationError } = useMutationRunner(mutationIdentity);\n\n useLayoutEffect(() => {\n authoritativeGeneration.current += 1;\n setOverride(null);\n }, [client, targetKey]);\n\n type PolicyRead = {\n targetKey: string;\n generation: number;\n server: SessionMcpApprovalPolicyTarget | null;\n };\n const load = useCallback(async (): Promise<PolicyRead> => {\n const generation = authoritativeGeneration.current;\n if (!sessionId || !serverId) {\n return { targetKey, generation, server: null };\n }\n const session = await client.getSession(workspaceId, sessionId);\n return {\n targetKey,\n generation,\n server:\n session.mcpServers.find((server) => server.id === serverId) ??\n (session.mcpApprovalPolicies && Object.hasOwn(session.mcpApprovalPolicies, serverId)\n ? {\n id: serverId,\n source: \"workspace\",\n requireApproval: session.mcpApprovalPolicies[serverId]!,\n }\n : null),\n };\n }, [client, serverId, sessionId, targetKey, workspaceId]);\n const {\n data,\n loading,\n error: loadError,\n refresh,\n } = usePolledValue(load, {\n enabled,\n pollIntervalMs: options.pollIntervalMs,\n });\n useEffect(() => {\n if (data?.targetKey === targetKey && data.generation === authoritativeGeneration.current) {\n setOverride(null);\n }\n }, [data, targetKey]);\n\n const scheduleRefresh = useDebouncedCallback(() => void refresh());\n const matchesPolicyEvent = useCallback(\n (event: Pick<SessionEvent, \"type\" | \"payload\">) =>\n isSessionMcpApprovalPolicyEvent(event, serverId ?? undefined),\n [serverId],\n );\n useSessionEventTrigger(\n client,\n workspaceId,\n sessionId,\n matchesPolicyEvent,\n scheduleRefresh,\n {\n enabled,\n ...(options.events !== undefined ? { events: options.events } : {}),\n },\n async () => await refresh(),\n );\n\n const update = useCallback(\n async (\n policy: SessionMcpApprovalPolicy,\n ): Promise<UpdateSessionMcpApprovalPolicyResponse | null> => {\n if (!sessionId || !serverId) {\n return null;\n }\n const response = await run(() =>\n client.updateSessionMcpApprovalPolicy(workspaceId, sessionId, serverId, {\n requireApproval: policy,\n }),\n );\n if (response) {\n authoritativeGeneration.current += 1;\n setOverride(response.server);\n void refresh();\n }\n return response;\n },\n [client, refresh, run, serverId, sessionId, workspaceId],\n );\n\n const server = override ?? data?.server ?? null;\n return {\n server,\n policy: server?.requireApproval ?? null,\n loading,\n error: mutationError ?? loadError,\n refresh: async () => {\n await refresh();\n },\n update,\n updating: mutating,\n clearError: clearMutationError,\n };\n}\n","import type { SessionEvent, SessionLineageResponse } from \"@opengeni/sdk\";\nimport { useCallback, useEffect, useRef } from \"react\";\nimport {\n useEmbeddedSessionLineage,\n type EmbeddedSessionLineageClientOverride,\n} from \"../session-context\";\nimport { useDebouncedCallback, usePolledValue, useSessionEventTrigger } from \"./internal\";\n\nexport type UseSessionLineageOptions = EmbeddedSessionLineageClientOverride & {\n events?: SessionEvent[] | undefined;\n /** Refresh interval (ms). Off by default. */\n pollIntervalMs?: number | undefined;\n enabled?: boolean | undefined;\n /** Optional shared causal clock invoked when each network read starts. */\n beginRead?: (() => number) | undefined;\n};\n\nexport type UseSessionLineageResult = {\n lineage: SessionLineageResponse | null;\n loading: boolean;\n error: Error | null;\n /** Causal generation captured when the accepted lineage read started. */\n readGeneration: number;\n refresh: () => Promise<void>;\n};\n\nexport function isLineageRefreshEvent(event: SessionEvent): boolean {\n if (event.type === \"session.status.changed\" || event.type === \"session.created\") {\n return true;\n }\n // A child's creation never appears on the PARENT's stream as session.created —\n // the parent sees its own spawn as an agent.toolCall.* for session_create.\n // Created events are handled separately so they can refresh immediately and\n // once after the child row has had time to commit.\n if (event.type === \"agent.toolCall.output\") {\n const payload = event.payload as { name?: unknown; toolName?: unknown } | null | undefined;\n const name =\n typeof payload?.name === \"string\"\n ? payload.name\n : typeof payload?.toolName === \"string\"\n ? payload.toolName\n : \"\";\n return name === \"session_create\" || name.endsWith(\"__session_create\");\n }\n return false;\n}\n\nfunction isSessionCreateToolCallCreated(event: SessionEvent): boolean {\n if (event.type !== \"agent.toolCall.created\") {\n return false;\n }\n const payload = event.payload as { name?: unknown; toolName?: unknown } | null | undefined;\n const name =\n typeof payload?.name === \"string\"\n ? payload.name\n : typeof payload?.toolName === \"string\"\n ? payload.toolName\n : \"\";\n return name === \"session_create\" || name.endsWith(\"__session_create\");\n}\n\n/** Read the ancestors + descendant tree for one session. Data-only; no UI state. */\nexport function useSessionLineage(\n sessionId: string | null | undefined,\n options: UseSessionLineageOptions = {},\n): UseSessionLineageResult {\n const { client, workspaceId, workspaceControlEvent, registerSessionReconciler } =\n useEmbeddedSessionLineage(options);\n const enabled = (options.enabled ?? true) && Boolean(sessionId);\n const nextReadGeneration = useRef(0);\n const beginRead = options.beginRead;\n const load = useCallback(\n async (signal?: AbortSignal) => {\n if (!sessionId) {\n return {\n lineage: { ancestors: [], children: [], truncated: false },\n readGeneration: 0,\n };\n }\n let readGeneration = 0;\n const lineage = await client.getSessionLineage(workspaceId, sessionId, {\n signal,\n onRequestStart: (sharedReadGeneration) => {\n readGeneration = sharedReadGeneration ?? beginRead?.() ?? ++nextReadGeneration.current;\n },\n });\n return {\n lineage,\n readGeneration,\n };\n },\n [beginRead, client, workspaceId, sessionId],\n );\n const state = usePolledValue(load, { pollIntervalMs: options.pollIntervalMs, enabled });\n const refresh = state.refresh;\n useEffect(() => {\n if (enabled && workspaceControlEvent) void refresh();\n }, [enabled, refresh, workspaceControlEvent]);\n useEffect(() => {\n if (!sessionId || !enabled) return;\n return registerSessionReconciler(sessionId, \"lineage\", refresh);\n }, [enabled, refresh, registerSessionReconciler, sessionId]);\n const refreshSoon = useDebouncedCallback(() => void refresh(), 150);\n const delayedChildRefreshRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n // Clear a pending delayed refresh on session/workspace SWITCH (not just\n // unmount): otherwise a timer scheduled for the previous session can fire\n // ~2.5s later and commit that session's lineage onto the new one.\n useEffect(() => {\n return () => {\n if (delayedChildRefreshRef.current !== null) {\n clearTimeout(delayedChildRefreshRef.current);\n delayedChildRefreshRef.current = null;\n }\n };\n }, [sessionId, workspaceId]);\n const refreshAfterChildCreate = useCallback(() => {\n void refresh();\n if (delayedChildRefreshRef.current !== null) {\n clearTimeout(delayedChildRefreshRef.current);\n }\n delayedChildRefreshRef.current = setTimeout(() => {\n delayedChildRefreshRef.current = null;\n void refresh();\n }, 2500);\n }, [refresh]);\n useSessionEventTrigger(\n client,\n workspaceId,\n sessionId,\n isLineageRefreshEvent,\n refreshSoon,\n // SHARED-FEED ONLY: without a caller-provided events log the trigger would\n // open its OWN streamEvents tail — a second live SSE connection next to the\n // session route's useSessionEvents. A caller with no feed opts into polling\n // (pollIntervalMs), never a duplicate stream.\n { events: options.events, enabled: enabled && options.events !== undefined },\n );\n useSessionEventTrigger(\n client,\n workspaceId,\n sessionId,\n isSessionCreateToolCallCreated,\n refreshAfterChildCreate,\n { events: options.events, enabled: enabled && options.events !== undefined },\n );\n return {\n lineage: state.data?.lineage ?? null,\n loading: state.loading,\n error: state.error,\n readGeneration: state.data?.readGeneration ?? 0,\n refresh,\n };\n}\n"],"mappings":"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","names":["useCallback","useEffect","useRef","useState","useState","useRef","useCallback","server","useEffect","useCallback","useEffect","useRef","useRef","useCallback","useEffect"]}
|