import type { SessionSecretsView, VaultItemSummary, VaultPolicy, VaultSummary, } from "@opengeni/sdk"; import { CheckIcon, ChevronLeftIcon, ChevronRightIcon, KeyRoundIcon, LockKeyholeIcon, UsersIcon, } from "lucide-react"; import { useState, type ReactNode } from "react"; import { ConnectorAction } from "../../../ui/composer-menu-action"; import { ComposerMenuHeader, ComposerMenuRowsSkeleton, ComposerMenuSwitchIndicator, } from "../../../ui/composer-menu"; import { MENU_BACK_BUTTON_CLASS, MENU_CHECK_SLOT_CLASS, MENU_CHEVRON_CLASS, MENU_LABEL_CLASS, MENU_META_CLASS, MENU_NOTE_CLASS, MENU_SEPARATOR_CLASS, } from "../../../ui/menu-styles"; import { cn } from "../../lib/utils"; import { vaultErrorText } from "./vault-model"; /* ---------------------------------------------------------------------------- The chat's Secrets (S10): a drill-in of the composer's "+" menu. - This task: items an agent saved for this task, each with "Save to…" so a person can keep it after the task (a copy into Only me or This workspace). - Vaults: which vaults agents in this chat may use. "All vaults" includes vaults added later; picking vaults narrows the chat (and its children). A parent session's narrowing can't be widened here: those rows are disabled. -------------------------------------------------------------------------- */ export type SessionSecretsVaultRow = VaultSummary & { selected: boolean; selectable: boolean }; /** The "+" row's value: "All", "2 vaults", "None". */ export function sessionSecretsSummary( policy: VaultPolicy | null | undefined, vaults: readonly { selected: boolean }[] | null | undefined, ): string { if (!policy || policy.mode === "all") return "All"; const count = vaults ? vaults.filter((vault) => vault.selected).length : policy.vaultIds.length; return count === 0 ? "None" : count === 1 ? "1 vault" : `${count} vaults`; } /** Rows of a new chat's checklist from the workspace's vaults and a draft policy. */ export function draftSecretsRows( vaults: readonly VaultSummary[], policy: VaultPolicy, ): SessionSecretsVaultRow[] { return vaults .filter((vault) => vault.ownerKind !== "task") .map((vault) => ({ ...vault, selected: policy.mode === "all" || policy.vaultIds.includes(vault.id), selectable: true, })); } type SaveTarget = { vault: VaultSummary; label: string; icon: ReactNode }; function saveTargets(vaults: readonly VaultSummary[]): SaveTarget[] { const mine = vaults.find( (vault) => vault.ownerKind === "user" && vault.isDefault && vault.canWrite, ); const shared = vaults.find( (vault) => vault.ownerKind === "workspace" && vault.isDefault && vault.canWrite, ); return [ ...(mine ? [{ vault: mine, label: "Only me", icon: }] : []), ...(shared ? [{ vault: shared, label: "This workspace", icon: }] : []), ]; } export function SessionSecretsMenuBody({ leading, presentation = "menu", vaults, policy, narrowedByParent, taskItems, canEdit, disabled, loading, error, onRetry, onPolicyChange, onSaveTo, onManage, }: { leading?: ReactNode | undefined; presentation?: "menu" | "dialog" | undefined; vaults: SessionSecretsVaultRow[] | null; policy: VaultPolicy; narrowedByParent: boolean; /** Present for a chat that exists; a new chat has no task items yet. */ taskItems?: VaultItemSummary[] | undefined; canEdit: boolean; disabled?: boolean | undefined; loading?: boolean | undefined; error?: unknown | undefined; onRetry?: (() => void) | undefined; onPolicyChange: (policy: VaultPolicy) => Promise | void; onSaveTo?: ((item: VaultItemSummary, target: VaultSummary) => Promise) | undefined; /** Opens Settings › Secrets. */ onManage?: (() => void) | undefined; }) { const [saving, setSaving] = useState(null); const [busy, setBusy] = useState(false); const [failure, setFailure] = useState(null); const locked = !canEdit || disabled || busy; const run = async (operation: () => Promise | void) => { setBusy(true); setFailure(null); try { await operation(); } catch (caught) { setFailure(vaultErrorText((caught as { cause?: unknown })?.cause ?? caught)); } finally { setBusy(false); } }; if (saving && onSaveTo && vaults) { const targets = saveTargets(vaults); return ( <> setSaving(null)} >