import { t } from "../../lib/console-text"; import { useEffect, useRef, useState } from "react"; import { ConnectController } from "@opengeni/connect"; import { ConnectSetup, useConnect } from "../../../connect"; import { attachSessionCapability } from "../../../session-capability-policy"; import type { AuthNeededItem } from "../../../timeline"; import type { OpenGeniBrowserClient } from "@opengeni/sdk/browser"; import { Button } from "../../../ui/button"; import { SessionCapabilityFrame } from "../../../components/session-capability-frame"; import "../../../../styles/connect.css"; import { usePage } from "../../lib/navigation"; type Props = { item: AuthNeededItem; workspaceId: string; sessionId: string; client: OpenGeniBrowserClient; actorId: string; canConfigure: boolean; onConfigured?: (() => Promise) | undefined; }; /** Client/actor changes discard unsent secret DOM. No credential is placed in * component state, an event, local storage, or an OAuth redirect. */ export function PreparedMcpSetupCard(props: Props) { const [client, setClient] = useState(props.client); const [generation, setGeneration] = useState(0); if (client !== props.client) { setClient(props.client); setGeneration(generation + 1); } return ( ); } function ScopedCard({ item, client, workspaceId, sessionId, canConfigure, onConfigured }: Props) { const page = usePage(); const [proposal] = useState(() => item.setupRequest!); const [controller, setController] = useState(null); const [failed, setFailed] = useState(false); const [retry, setRetry] = useState(0); const [beginKey, setBeginKey] = useState(`mcp-card:${item.id}`); const configured = useRef(onConfigured); configured.current = onConfigured; useEffect(() => { if (!canConfigure || !proposal.mcpSetup || !proposal.ownership) return; let active = true; const next = new ConnectController(client.connectTransport(), workspaceId); setController(next); setFailed(false); // The card identity is a stable operation key, including across reload and // Strict Mode. Never recover an unrelated pending setup by provider alone. void next .begin({ providerId: "mcp-headers", ownership: proposal.ownership, mcpSetup: proposal.mcpSetup, idempotencyKey: beginKey, returnUrl: new URL(new URL(page.href()).pathname, page.origin()).href, }) .catch(() => { if (active) setFailed(true); }); return () => { active = false; next.dispose(); }; }, [page, client, workspaceId, canConfigure, proposal, beginKey, retry]); const contents = !canConfigure ? (

Someone with connection and integration management access needs to connect this server.

) : failed ? (

Could not load this connection setup. Your key has not been submitted.

) : controller ? ( configured.current?.()} onRestart={(previousId) => setBeginKey(`mcp-restart:${previousId}`)} /> ) : (

Preparing the key form…

); return ( {}} onClose={() => {}} inlineSetup={contents} /> ); } function PreparedSetup({ controller, client, workspaceId, sessionId, onConfigured, onRestart, }: { controller: ConnectController; client: OpenGeniBrowserClient; workspaceId: string; sessionId: string; onConfigured(): void | Promise; onRestart(previousId: string): void; }) { const view = useConnect(controller); const [access, setAccess] = useState<"pending" | "ready" | "failed">("pending"); const [retry, setRetry] = useState(0); const notify = useRef(onConfigured); notify.current = onConfigured; const id = view.attempt?.state === "complete" && view.attempt.integrationInstalled ? view.attempt.mcpCapabilityId : undefined; useEffect(() => { if (!id) return; let active = true; setAccess("pending"); void (async () => { const item = (await client.listCapabilities(workspaceId)).items.find( (entry) => entry.id === id, ); if (!active) return; if (!item?.enabled) throw new Error("Connection not yet visible"); // Existing CAS-fenced selection preserves all other choices and cannot // widen a parent's ceiling or the currently accepted attempt's tools. await attachSessionCapability(t, client, workspaceId, sessionId, item, () => active); if (!active) return; setAccess("ready"); // A host refresh failure cannot undo a verified connection or turn it // into a reason to resubmit credentials. void Promise.resolve(notify.current()).catch(() => {}); })().catch(() => { if (active) setAccess("failed"); }); return () => { active = false; }; }, [client, workspaceId, sessionId, id, retry]); return ( <> { throw new Error("Prepared key setup does not use OAuth"); }} /> {view.attempt && ["cancelled", "expired"].includes(view.attempt.state) ? ( ) : null} {id ? ( access === "ready" ? (

Connected. Its tools are available from your next message.

) : access === "failed" ? (

Connected, but chat access could not be confirmed. Your key is saved; do not submit it again.

) : (

Connected. Checking this chat's tool access…

) ) : null} ); }