import { Suspense, useCallback, useEffect, useRef, useState, type ComponentType } from "react"; import { useAtomSet } from "@effect/atom-react"; import * as Exit from "effect/Exit"; import { type Connection, type IntegrationSlug } from "@executor-js/sdk/shared"; import type { EditSheetApplyResult, EditSheetSectionProps } from "@executor-js/sdk/client"; import { toast } from "sonner"; import { updateConnection, updateIntegration } from "../api/atoms"; import { connectionWriteKeys, integrationWriteKeys } from "../api/reactivity-keys"; import { trackEvent } from "../api/analytics"; import { messageFromExit } from "../api/error-reporting"; import { Button } from "./button"; import { Input } from "./input"; import { Label } from "./label"; import { Sheet, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle, } from "./sheet"; import { Textarea } from "./textarea"; // --------------------------------------------------------------------------- // Metadata edit sheets — the user-curated, AGENT-VISIBLE metadata for an // integration and for a connection. The point of the sheet (vs. an inline // input) is the preview: it shows the record an agent reads, so editing a // field is editing what the model sees. // --------------------------------------------------------------------------- /** The `connections.list` item an agent reads for this connection, reduced to * the fields the sheet edits. The callable name is fixed at connect time and * is not editable here; only the label and description are. */ const connectionListItemPreview = (input: { readonly name: string; readonly identityLabel: string; readonly description: string; }): string => { const fields = [ `name: ${JSON.stringify(input.name)}`, ...(input.identityLabel ? [`identityLabel: ${JSON.stringify(input.identityLabel)}`] : []), ...(input.description ? [`description: ${JSON.stringify(input.description)}`] : []), ]; return `{ ${fields.join(", ")}, ... }`; }; function AgentPreview(props: { readonly label: string; readonly line: string }) { return (

{props.label}

        {props.line}
      
); } export function ConnectionEditSheet(props: { readonly connection: Connection | null; readonly onOpenChange: (open: boolean) => void; }) { const { connection } = props; const doUpdate = useAtomSet(updateConnection, { mode: "promiseExit" }); const [description, setDescription] = useState(""); const [identityLabel, setIdentityLabel] = useState(""); const [saving, setSaving] = useState(false); useEffect(() => { setDescription(connection?.description ?? ""); setIdentityLabel(connection?.identityLabel ?? ""); }, [connection]); const handleSave = async () => { if (!connection) return; setSaving(true); const exit = await doUpdate({ params: { owner: connection.owner, integration: connection.integration, name: connection.name, }, payload: { description: description.trim().length > 0 ? description.trim() : null, identityLabel: identityLabel.trim().length > 0 ? identityLabel.trim() : null, }, reactivityKeys: connectionWriteKeys, }); setSaving(false); if (Exit.isFailure(exit)) { toast.error(messageFromExit(exit, "Failed to update connection")); return; } props.onOpenChange(false); }; const previewLine = connection ? connectionListItemPreview({ name: String(connection.name), identityLabel: identityLabel.trim(), description: description.trim(), }) : ""; return ( Edit connection Both fields are agent-visible: agents read them from connections.list when they pick an account, so this is the place to say which account this is and what it is for.