"use client";
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { forwardRef, memo, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from "react";
import { cn } from "@stigmer/theme";
import { getUserMessage } from "@stigmer/sdk";
import { useComposer } from "./useComposer.js";
import { ComposerToolbar } from "./ComposerToolbar.js";
import { parseModelKey } from "../models/registry.js";
import { WorkspaceEditor } from "../workspace/WorkspaceEditor.js";
import { AgentPicker } from "../agent/AgentPicker.js";
import { AgentEnvForm } from "../agent/AgentEnvForm.js";
import { useAgentSetup } from "../agent/useAgentSetup.js";
import { SecretFlowErrorGuide, isSecretFlowError } from "../error/SecretFlowErrorGuide.js";
import { McpServerPicker } from "../mcp-server/McpServerPicker.js";
import { useMcpServerSetup } from "../mcp-server/useMcpServerSetup.js";
import { ApiResourceKind } from "@stigmer/protos/ai/stigmer/commons/apiresource/apiresourcekind/api_resource_kind_pb";
import { SkillPicker } from "../skill/SkillPicker.js";
import { SessionVariablesInput } from "../execution/SessionVariablesInput.js";
import { useAttachments } from "../attachment/useAttachments.js";
import { AttachmentChipList } from "../attachment/AttachmentChipList.js";
import { useFileReferences } from "../file-reference/useFileReferences.js";
import { FileReferenceChipList } from "../file-reference/FileReferenceChipList.js";
import { FILE_REF_MIME } from "../internal/file-tree/index.js";
import { useSessionEnvPool } from "../environment/useSessionEnvPool.js";
import { usePersonalEnvironment } from "../environment/usePersonalEnvironment.js";
import { useStigmer } from "../hooks.js";
import { SYSTEM_ENV_VAR_KEYS, resolveSystemEnvVarValues, } from "../environment/systemEnvVars.js";
import { useRenderTracer } from "../internal/dev/index.js";
import { AgentIcon, McpServerIcon, SkillIcon, SecretsIcon, AlertTriangleIcon, ResolveSpinner, } from "./icons.js";
/**
 * Unified message composer for Stigmer sessions.
 *
 * Combines a self-resizing textarea, model selector, and context pickers
 * (agent, workspace, MCP servers, skills) into a single input card.
 *
 * The toolbar uses a two-group layout:
 * - **Left (primary state):** Interaction Mode, Model Selector
 * - **Right (secondary actions, icon-only):** Workspace, Attach, Configure (Agent, MCP, Skills, Secrets), Send
 *
 * Selected items render as removable chips between the textarea and toolbar.
 *
 * Used for both new session creation (launcher) and follow-up messages
 * within an existing session. Layout positioning is the consumer's
 * responsibility.
 *
 * Uses `<div role="form">` instead of `<form>` so it can be embedded
 * inside host application forms without nesting violations.
 *
 * All visual properties flow through `--stgm-*` tokens.
 *
 * @example
 * ```tsx
 * // Launcher with all context types
 * <SessionComposer
 *   onSubmit={handleCreate}
 *   org={org}
 *   agentRef={agentRef}
 *   onAgentRefChange={setAgentRef}
 *   onAgentResolutionChange={setResolution}
 *   workspace={workspace}
 *   enableGitHub
 *   mcpServerUsages={mcpUsages}
 *   onMcpServerUsagesChange={setMcpUsages}
 *   skillRefs={skillRefs}
 *   onSkillRefsChange={setSkillRefs}
 *   initialRows={3}
 *   placeholder="Describe what you need help with..."
 *   autoFocus
 * />
 *
 * // Pre-filled launcher (auto-selects agent on mount)
 * <SessionComposer
 *   onSubmit={handleCreate}
 *   org={org}
 *   agentRef={agentRef}
 *   onAgentRefChange={setAgentRef}
 *   onAgentResolutionChange={setResolution}
 *   initialAgentRef={{ org: "stigmer", slug: "agent-creator" }}
 *   placeholder="Describe the agent you want to create..."
 *   initialRows={3}
 *   autoFocus
 * />
 *
 * // Follow-up (compact, workspace only)
 * <SessionComposer
 *   onSubmit={(msg, model) => conv.sendFollowUp(msg, { modelName: model })}
 *   disabled={!conv.canSendFollowUp}
 *   isSubmitting={conv.isSending}
 *   workspace={workspace}
 * />
 * ```
 */
const SessionComposerInner = forwardRef(function SessionComposer({ onSubmit, isSubmitting = false, disabled = false, onStop, isStopping = false, harness, onHarnessChange, showHarnessSelector = false, interactionMode, onInteractionModeChange, showInteractionModePicker = false, defaultModelId, onModelChange, showModelSelector = true, workspace, gitHubConnection, enableGitHub = true, enableLocal = false, onBrowseLocalFolder, org, agentRef, onAgentRefChange, onAgentResolutionChange, initialAgentRef, initialInstanceId, isDefaultAgent = false, lockAgent = false, mcpServerUsages, onMcpServerUsagesChange, skillRefs, onSkillRefsChange, sessionVariables, enableAttachments = true, enableFileReferences = true, onAttachmentValidationError, initialAttachments, placeholder = "Reply\u2026", initialRows = 1, autoFocus = false, ariaLabel = "Send message", className, }, ref) {
    useRenderTracer("SessionComposer", { disabled, isSubmitting });
    const [modelId, setModelIdRaw] = useState(defaultModelId);
    const userOverrodeModel = useRef(false);
    // Sync internal modelId when the external defaultModelId prop changes
    // (e.g., lastExecModelId resolves after executions load). Only sync if the
    // user hasn't made a local selection in this composer instance.
    useEffect(() => {
        if (!userOverrodeModel.current && defaultModelId !== undefined) {
            setModelIdRaw(defaultModelId);
        }
    }, [defaultModelId]);
    const setModelId = useCallback((id) => {
        userOverrodeModel.current = true;
        setModelIdRaw(id);
    }, []);
    const [displayNames, setDisplayNames] = useState(() => new Map());
    const isDisabled = disabled || isSubmitting;
    // Stop-mode: an in-flight, stoppable execution. The textarea/config stay
    // disabled, but the card is kept un-dimmed so the Stop button reads as live.
    const stopMode = onStop != null;
    const showAgent = onAgentRefChange != null && org != null;
    const showMcp = onMcpServerUsagesChange != null && org != null;
    const showWorkspace = workspace != null;
    const showSkills = onSkillRefsChange != null && org != null;
    const showSessionVars = sessionVariables != null;
    const showAttach = enableAttachments;
    // ---------------------------------------------------------------------------
    // Configure menu state — drives the Tier 2 drill-down popover
    // ---------------------------------------------------------------------------
    const [configOpen, setConfigOpen] = useState(false);
    const [configActivePanel, setConfigActivePanel] = useState(null);
    const configMcpInitialServerKeyRef = useRef(undefined);
    // ---------------------------------------------------------------------------
    // Session env pool — cross-references secrets across all sources
    // ---------------------------------------------------------------------------
    const personalEnv = usePersonalEnvironment((showAgent || showMcp) ? (org ?? null) : null);
    const personalEnvKeys = useMemo(() => new Set(Object.keys(personalEnv.environment?.spec?.data ?? {})), [personalEnv.environment]);
    const stigmer = useStigmer();
    const pool = useSessionEnvPool({
        personalEnvKeys,
        manualSecrets: sessionVariables?.entries,
    });
    const poolKeysWithSystem = useMemo(() => new Set([...pool.availableKeys, ...SYSTEM_ENV_VAR_KEYS]), [pool.availableKeys]);
    // ---------------------------------------------------------------------------
    // Setup hooks — instantiated before handleSubmit so it can read their state
    // ---------------------------------------------------------------------------
    const agentSetup = useAgentSetup(showAgent ? (org ?? null) : null, poolKeysWithSystem);
    const mcpSetup = useMcpServerSetup(showMcp ? (org ?? null) : null, poolKeysWithSystem);
    // ---------------------------------------------------------------------------
    // Pool-resolve notification — when POOL_RESOLVE transitions agentSetup to
    // "ready" reactively (via the useEffect inside useAgentSetup), the
    // imperative callbacks (handleAgentSelect, handleEnvSubmit) are not
    // involved, so onAgentResolutionChange would never fire. This effect
    // bridges that gap so the parent always knows the current resolution.
    // ---------------------------------------------------------------------------
    useEffect(() => {
        if (agentSetup.state.status !== "ready")
            return;
        onAgentResolutionChange?.(agentSetup.state.resolution);
    }, [agentSetup.state, onAgentResolutionChange]);
    // ---------------------------------------------------------------------------
    // Attachments — file upload state machine
    // ---------------------------------------------------------------------------
    const attachments = useAttachments(enableAttachments
        ? { onValidationError: onAttachmentValidationError }
        : undefined);
    const fileRefs = useFileReferences();
    const fileInputRef = useRef(null);
    const [isDragOver, setIsDragOver] = useState(false);
    const [isDragOverFileRef, setIsDragOverFileRef] = useState(false);
    const handleFileInputChange = useCallback((e) => {
        if (e.target.files && e.target.files.length > 0) {
            attachments.addFiles(e.target.files);
        }
        if (fileInputRef.current)
            fileInputRef.current.value = "";
    }, [attachments]);
    const handleDragOver = useCallback((e) => {
        const hasFileRef = e.dataTransfer.types.includes(FILE_REF_MIME);
        if (hasFileRef && enableFileReferences && !isDisabled) {
            e.preventDefault();
            e.stopPropagation();
            setIsDragOverFileRef(true);
            setIsDragOver(true);
            return;
        }
        if (!enableAttachments || isDisabled)
            return;
        e.preventDefault();
        e.stopPropagation();
        setIsDragOverFileRef(false);
        setIsDragOver(true);
    }, [enableAttachments, enableFileReferences, isDisabled]);
    const handleDragLeave = useCallback((e) => {
        e.preventDefault();
        e.stopPropagation();
        setIsDragOver(false);
        setIsDragOverFileRef(false);
    }, []);
    const handleDrop = useCallback((e) => {
        e.preventDefault();
        e.stopPropagation();
        setIsDragOver(false);
        setIsDragOverFileRef(false);
        if (isDisabled)
            return;
        const fileRefData = e.dataTransfer.getData(FILE_REF_MIME);
        if (fileRefData && enableFileReferences) {
            try {
                const { path } = JSON.parse(fileRefData);
                if (path)
                    fileRefs.add(path);
            }
            catch {
                // Malformed payload — ignore silently
            }
            return;
        }
        if (!enableAttachments)
            return;
        if (e.dataTransfer.files.length > 0) {
            attachments.addFiles(e.dataTransfer.files);
        }
    }, [enableAttachments, enableFileReferences, isDisabled, attachments, fileRefs]);
    // ---------------------------------------------------------------------------
    // Submit — aggregates one-time runtimeEnv from all setup flows
    // ---------------------------------------------------------------------------
    const handleSubmit = useCallback(async (message, overrides) => {
        // Persist save-for-future manual secrets before building runtimeEnv
        if (sessionVariables?.hasSaveForFutureEntries) {
            const saveVars = sessionVariables.toSaveForFutureEnv();
            if (Object.keys(saveVars).length > 0) {
                try {
                    await personalEnv.getOrCreate();
                    await personalEnv.addVariables(saveVars);
                }
                catch {
                    // Best-effort: if persistence fails, the values still flow
                    // into runtimeEnv for this execution via the one-time path.
                }
            }
        }
        const env = {};
        // System env vars first (lowest priority) — auto-resolved from
        // the Stigmer client's connection context so MCP servers and
        // agents can reach the backend without manual user input.
        const systemVars = await resolveSystemEnvVarValues(stigmer);
        Object.assign(env, systemVars);
        if (agentSetup.state.status === "ready" &&
            agentSetup.state.resolution.mode === "oneTime") {
            Object.assign(env, agentSetup.state.resolution.runtimeEnv);
        }
        const mcpEnv = mcpSetup.pendingRuntimeEnv;
        if (Object.keys(mcpEnv).length > 0) {
            Object.assign(env, mcpEnv);
        }
        if (sessionVariables && sessionVariables.hasValidEntries) {
            Object.assign(env, sessionVariables.toRuntimeEnv());
        }
        // Composer-attached files plus caller-supplied extras (e.g. the approved
        // plan.md from a Build-from-plan turn) travel as one attachment list.
        const composerAttachments = enableAttachments
            ? attachments.toAttachmentInputs()
            : [];
        const attachmentInputs = [
            ...composerAttachments,
            ...(overrides?.attachments ?? []),
        ];
        const hasEnv = Object.keys(env).length > 0;
        const hasAttachments = attachmentInputs.length > 0;
        const effectiveMode = overrides?.interactionMode ??
            (showInteractionModePicker && interactionMode
                ? interactionMode
                : undefined);
        const hasFileRefs = enableFileReferences && fileRefs.hasRefs;
        const buildFromPlan = overrides?.buildFromPlan;
        const context = hasEnv || hasAttachments || effectiveMode || hasFileRefs || buildFromPlan
            ? {
                runtimeEnv: hasEnv ? env : undefined,
                attachments: hasAttachments ? attachmentInputs : undefined,
                interactionMode: effectiveMode,
                workspaceFileRefs: hasFileRefs ? [...fileRefs.refs] : undefined,
                buildFromPlan,
            }
            : undefined;
        const resolvedModelId = modelId
            ? (parseModelKey(modelId)?.modelId ?? modelId)
            : undefined;
        onSubmit(message, resolvedModelId, context);
        if (enableAttachments) {
            attachments.clear();
        }
        if (enableFileReferences) {
            fileRefs.clear();
        }
    }, [onSubmit, modelId, stigmer, agentSetup.state, mcpSetup.pendingRuntimeEnv, sessionVariables, enableAttachments, attachments, personalEnv, showInteractionModePicker, interactionMode]);
    const composer = useComposer({
        onSubmit: handleSubmit,
        disabled: isDisabled,
    });
    useImperativeHandle(ref, () => ({
        setMessage: composer.setMessage,
        focus: () => composer.textareaRef.current?.focus(),
        submit: (message, options) => {
            const trimmed = message.trim();
            if (!trimmed || isDisabled)
                return;
            void handleSubmit(trimmed, options);
        },
    }), [composer.setMessage, composer.textareaRef, handleSubmit, isDisabled]);
    const handleModelChange = useCallback((id) => {
        setModelId(id);
        onModelChange?.(id);
    }, [onModelChange]);
    const handleHarnessChange = useCallback((h) => {
        onHarnessChange?.(h);
    }, [onHarnessChange]);
    const handleInteractionModeChange = useCallback((mode) => {
        onInteractionModeChange?.(mode);
    }, [onInteractionModeChange]);
    const handleDisplayNameResolved = useCallback((key, name) => {
        setDisplayNames((prev) => {
            const next = new Map(prev);
            next.set(key, name);
            return next;
        });
    }, []);
    // ---------------------------------------------------------------------------
    // Agent setup: state-machine-driven popover + environment resolution
    // ---------------------------------------------------------------------------
    const showEnvForm = agentSetup.state.status === "needsEnvVars";
    const isAgentBusy = agentSetup.state.status === "resolving" ||
        agentSetup.state.status === "submitting";
    const handleConfigOpenChange = useCallback((open) => {
        setConfigOpen(open);
        if (!open) {
            configMcpInitialServerKeyRef.current = undefined;
            if (configActivePanel === "agent" &&
                agentSetup.state.status !== "needsEnvVars" &&
                agentSetup.state.status !== "submitting") {
                agentSetup.reset();
            }
        }
    }, [configActivePanel, agentSetup]);
    const handleConfigActivePanelChange = useCallback((panel) => {
        if (configActivePanel === "agent" &&
            panel !== "agent" &&
            agentSetup.state.status !== "needsEnvVars" &&
            agentSetup.state.status !== "submitting") {
            agentSetup.reset();
        }
        setConfigActivePanel(panel);
    }, [configActivePanel, agentSetup]);
    const handleAgentSelect = useCallback(async (ref) => {
        if (!ref) {
            onAgentRefChange?.(null);
            onAgentResolutionChange?.(null);
            return;
        }
        try {
            const result = await agentSetup.resolveAgent(ref);
            if (result.status === "ready") {
                onAgentRefChange?.(result.agentRef);
                onAgentResolutionChange?.(result.resolution);
                handleDisplayNameResolved(`${result.agentRef.org}/${result.agentRef.slug}`, result.agentName);
                setConfigOpen(false);
            }
            // "needsEnvVars" — state machine transitions automatically,
            // panel content switches to env form via `showEnvForm`.
        }
        catch {
            // Error is captured by agentSetup.state.error — displayed inline.
        }
    }, [
        agentSetup,
        onAgentRefChange,
        onAgentResolutionChange,
        handleDisplayNameResolved,
    ]);
    const handleAgentSelectToInstance = useCallback(async (ref, instanceId) => {
        try {
            const result = await agentSetup.resolveToInstance(ref, instanceId);
            onAgentRefChange?.(result.agentRef);
            onAgentResolutionChange?.(result.resolution);
            handleDisplayNameResolved(`${result.agentRef.org}/${result.agentRef.slug}`, result.agentName);
            setConfigOpen(false);
        }
        catch {
            // Error is captured by agentSetup.state.error — displayed inline.
        }
    }, [
        agentSetup,
        onAgentRefChange,
        onAgentResolutionChange,
        handleDisplayNameResolved,
    ]);
    const handleEnvFormSubmit = useCallback(async (values, { saveForFuture }) => {
        try {
            const result = await agentSetup.submitEnvVars(values, { saveForFuture });
            onAgentRefChange?.(result.agentRef);
            onAgentResolutionChange?.(result.resolution);
            handleDisplayNameResolved(`${result.agentRef.org}/${result.agentRef.slug}`, result.agentName);
            setConfigOpen(false);
        }
        catch {
            // Error is captured by agentSetup.state.error — displayed inline.
        }
    }, [
        agentSetup,
        onAgentRefChange,
        onAgentResolutionChange,
        handleDisplayNameResolved,
    ]);
    // ---------------------------------------------------------------------------
    // Initial agent: auto-resolve on mount when initialAgentRef is provided
    // ---------------------------------------------------------------------------
    const handleAgentSelectRef = useRef(handleAgentSelect);
    handleAgentSelectRef.current = handleAgentSelect;
    const handleAgentSelectToInstanceRef = useRef(handleAgentSelectToInstance);
    handleAgentSelectToInstanceRef.current = handleAgentSelectToInstance;
    const initialAgentHandled = useRef(false);
    useEffect(() => {
        if (!initialAgentRef || !showAgent || !org || initialAgentHandled.current) {
            return;
        }
        let cancelled = false;
        initialAgentHandled.current = true;
        // When an explicit instance is provided, bind directly to it
        // (skips env collection); otherwise run the full resolution flow.
        const run = initialInstanceId
            ? handleAgentSelectToInstanceRef.current(initialAgentRef, initialInstanceId)
            : handleAgentSelectRef.current(initialAgentRef);
        run.catch(() => {
            if (!cancelled) {
                initialAgentHandled.current = false;
            }
        });
        return () => {
            cancelled = true;
        };
    }, [initialAgentRef, initialInstanceId, showAgent, org]);
    // ---------------------------------------------------------------------------
    // Initial agent: auto-open Configure > Agent when env vars are needed
    // ---------------------------------------------------------------------------
    const initialAgentConfigAutoOpened = useRef(false);
    useEffect(() => {
        if (initialAgentConfigAutoOpened.current)
            return;
        if (!initialAgentRef)
            return;
        if (agentSetup.state.status !== "needsEnvVars")
            return;
        initialAgentConfigAutoOpened.current = true;
        setConfigOpen(true);
        setConfigActivePanel("agent");
    }, [initialAgentRef, agentSetup.state.status]);
    // ---------------------------------------------------------------------------
    // Initial attachments: upload files on mount when provided
    // ---------------------------------------------------------------------------
    const initialAttachmentsHandled = useRef(false);
    useEffect(() => {
        if (initialAttachments &&
            initialAttachments.length > 0 &&
            enableAttachments &&
            !initialAttachmentsHandled.current) {
            initialAttachmentsHandled.current = true;
            attachments.addFiles(initialAttachments);
        }
    }, [initialAttachments, enableAttachments, attachments]);
    // ---------------------------------------------------------------------------
    // Initial MCP: seed useMcpServerSetup from mcpServerUsages prop on mount
    // ---------------------------------------------------------------------------
    const initialMcpSeeded = useRef(false);
    const mcpSetupAddServerRef = useRef(mcpSetup.addServer);
    mcpSetupAddServerRef.current = mcpSetup.addServer;
    const mcpSetupSetEnabledToolsRef = useRef(mcpSetup.setEnabledTools);
    mcpSetupSetEnabledToolsRef.current = mcpSetup.setEnabledTools;
    useEffect(() => {
        if (!showMcp || initialMcpSeeded.current || !mcpServerUsages?.length)
            return;
        initialMcpSeeded.current = true;
        for (const usage of mcpServerUsages) {
            const ref = usage.mcpServerRef;
            if (!ref)
                continue;
            const savedTools = usage.enabledTools;
            mcpSetupAddServerRef.current(ref).then(() => {
                if (savedTools?.length) {
                    mcpSetupSetEnabledToolsRef.current(ref, savedTools);
                }
            }).catch(() => {
                // Non-fatal: server may have been deleted or become inaccessible.
                // The user can re-add it via the MCP picker.
            });
        }
    }, [showMcp, mcpServerUsages]);
    // ---------------------------------------------------------------------------
    // MCP server setup: sync usageInputs to consumer
    // ---------------------------------------------------------------------------
    const hasLoadingMcpEntries = useMemo(() => Object.values(mcpSetup.entries).some(e => e.status === "loading"), [mcpSetup.entries]);
    useEffect(() => {
        if (!showMcp || hasLoadingMcpEntries)
            return;
        onMcpServerUsagesChange?.(mcpSetup.usageInputs);
    }, [showMcp, mcpSetup.usageInputs, onMcpServerUsagesChange, hasLoadingMcpEntries]);
    // ---------------------------------------------------------------------------
    // Submission blocking: MCP servers must be fully configured before send
    // ---------------------------------------------------------------------------
    const mcpBlocked = showMcp && !mcpSetup.allReady;
    const canSend = composer.canSubmit && !mcpBlocked;
    const handleTextareaKeyDown = useCallback((e) => {
        if (!canSend && e.key === "Enter" && !e.shiftKey) {
            e.preventDefault();
            return;
        }
        composer.textareaProps.onKeyDown(e);
    }, [canSend, composer.textareaProps]);
    const workspaceCount = workspace?.entries.length ?? 0;
    // When only one source type is enabled and no entries exist yet, bypass
    // the popover entirely (desktop) or auto-drill into the GitHub panel (web).
    const workspaceDirectAction = useMemo(() => {
        if (workspaceCount > 0)
            return undefined;
        if (enableLocal && !enableGitHub && onBrowseLocalFolder) {
            return async () => {
                const path = await onBrowseLocalFolder();
                if (path)
                    workspace?.addLocalPath(path);
            };
        }
        return undefined;
    }, [workspaceCount, enableLocal, enableGitHub, onBrowseLocalFolder, workspace]);
    const workspaceInitialPanel = useMemo(() => {
        if (workspaceCount > 0)
            return null;
        if (enableGitHub && !enableLocal)
            return "github";
        return null;
    }, [workspaceCount, enableGitHub, enableLocal]);
    const mcpCount = showMcp ? Object.keys(mcpSetup.entries).length : 0;
    const skillCount = skillRefs?.length ?? 0;
    const sessionVarCount = sessionVariables?.entries.length ?? 0;
    // ---------------------------------------------------------------------------
    // Required-by map — tracks which keys are needed by which resources
    // ---------------------------------------------------------------------------
    const requiredByMap = useMemo(() => {
        const map = {};
        if (agentSetup.state.status === "needsEnvVars") {
            const agentName = agentSetup.state.agentName;
            for (const v of agentSetup.state.missingVariables) {
                (map[v.key] ??= []).push(agentName);
            }
        }
        if (showMcp) {
            for (const entry of Object.values(mcpSetup.entries)) {
                if (entry.status !== "needsSetup")
                    continue;
                const serverName = entry.mcpServer.metadata?.name ?? "MCP Server";
                for (const v of entry.missingVariables) {
                    (map[v.key] ??= []).push(serverName);
                }
            }
        }
        return map;
    }, [agentSetup.state, showMcp, mcpSetup.entries]);
    // ---------------------------------------------------------------------------
    // Configure menu — Tier 2 items and panel renderer
    // ---------------------------------------------------------------------------
    const configureItems = useMemo(() => {
        const items = [];
        if (showAgent) {
            const agentPending = !agentRef &&
                (agentSetup.state.status === "needsEnvVars" ||
                    agentSetup.state.status === "resolving" ||
                    agentSetup.state.status === "submitting");
            // A locked agent stays out of the menu unless setup is pending:
            // the env-collection form lives in this panel and must remain
            // reachable until the agent resolves (lock ≠ unwire). Once
            // resolved, the entry disappears and the agent cannot be swapped.
            if (!lockAgent || agentPending) {
                items.push({
                    id: "agent",
                    icon: _jsx(AgentIcon, {}),
                    label: "Agent",
                    count: agentRef || agentPending ? 1 : 0,
                    hasWarning: agentPending && agentSetup.state.status === "needsEnvVars",
                });
            }
        }
        if (showMcp) {
            items.push({
                id: "mcp",
                icon: _jsx(McpServerIcon, {}),
                label: "MCP Servers",
                count: mcpCount,
                hasWarning: mcpSetup.needsSetupCount > 0,
            });
        }
        if (showSkills) {
            items.push({
                id: "skills",
                icon: _jsx(SkillIcon, {}),
                label: "Skills",
                count: skillCount,
            });
        }
        if (showSessionVars) {
            items.push({
                id: "sessionVars",
                icon: _jsx(SecretsIcon, {}),
                label: "Session Variables",
                count: sessionVarCount,
            });
        }
        return items;
    }, [showAgent, lockAgent, agentRef, agentSetup.state, showMcp, mcpCount, mcpSetup.needsSetupCount, showSkills, skillCount, showSessionVars, sessionVarCount]);
    const renderConfigPanel = useCallback((panelId) => {
        switch (panelId) {
            case "agent":
                return showEnvForm ? (_jsxs("div", { children: [_jsx(AgentEnvForm, { agentName: agentSetup.state.status === "needsEnvVars"
                                ? agentSetup.state.agentName
                                : "Agent", variables: agentSetup.state.status === "needsEnvVars"
                                ? agentSetup.state.missingVariables
                                : [], onSubmit: handleEnvFormSubmit, onCancel: () => agentSetup.reset(), isSubmitting: isAgentBusy, disabled: isDisabled, poolValues: pool.getAvailableValue }), agentSetup.state.error && (_jsx(AgentSetupError, { error: agentSetup.state.error }))] })) : (_jsxs("div", { className: "relative", children: [_jsx(AgentPicker, { org: org, value: agentRef ?? null, onChange: handleAgentSelect, onDisplayNameResolved: handleDisplayNameResolved, disabled: isDisabled || isAgentBusy }), isAgentBusy && (_jsx("div", { className: "absolute inset-0 flex items-center justify-center rounded-lg bg-popover/80", children: _jsx(ResolveSpinner, {}) })), agentSetup.state.error && (_jsx(AgentSetupError, { error: agentSetup.state.error }))] }));
            case "mcp":
                return (_jsx(McpServerPicker, { org: org, setup: {
                        entries: mcpSetup.entries,
                        onServerAdded: (ref) => mcpSetup.addServer(ref),
                        onServerRemoved: (ref) => mcpSetup.removeServer(ref),
                        onSubmitEnvVars: (ref, values, opts) => mcpSetup.submitEnvVars(ref, values, {
                            saveForFuture: opts.saveForFuture,
                        }),
                        onEnabledToolsChange: (ref, tools) => mcpSetup.setEnabledTools(ref, tools),
                    }, initialServerKey: configMcpInitialServerKeyRef.current, onDisplayNameResolved: handleDisplayNameResolved, disabled: isDisabled, poolValues: pool.getAvailableValue }));
            case "skills":
                return (_jsx(SkillPicker, { org: org, value: skillRefs ?? [], onChange: onSkillRefsChange, onDisplayNameResolved: handleDisplayNameResolved, disabled: isDisabled }));
            case "sessionVars":
                return (_jsx(SessionVariablesInput, { sessionVariables: sessionVariables, disabled: isDisabled, requiredByMap: requiredByMap }));
            default:
                return null;
        }
    }, [
        showEnvForm,
        agentSetup,
        isAgentBusy,
        isDisabled,
        org,
        agentRef,
        handleAgentSelect,
        handleEnvFormSubmit,
        handleDisplayNameResolved,
        mcpSetup,
        skillRefs,
        onSkillRefsChange,
        sessionVariables,
        pool,
        requiredByMap,
    ]);
    // ---------------------------------------------------------------------------
    // Render
    // ---------------------------------------------------------------------------
    return (_jsx("div", { role: "form", "aria-label": ariaLabel, className: cn("shrink-0", className), children: _jsxs("div", { className: cn("rounded-xl border border-border bg-card shadow-sm", "focus-within:ring-2 focus-within:ring-ring", 
            // Plan mode tints the frame so the active mode is visible at a
            // glance, without opening the picker. Themeable via --stgm-primary-muted.
            interactionMode === "plan" && "border-primary-muted", isDisabled && !stopMode && "opacity-50", isDragOver && "ring-2 ring-primary/50"), onDragOver: handleDragOver, onDragLeave: handleDragLeave, onDrop: handleDrop, children: [_jsxs("div", { className: "relative", children: [_jsx("textarea", { ...composer.textareaProps, onKeyDown: handleTextareaKeyDown, placeholder: placeholder, rows: initialRows, autoFocus: autoFocus, className: "block w-full resize-none bg-transparent px-4 pt-3 pb-2 text-sm text-foreground placeholder:text-muted-foreground focus:outline-none disabled:cursor-not-allowed" }), isDragOver && (_jsx("div", { className: "pointer-events-none absolute inset-0 flex items-center justify-center rounded-t-xl bg-primary-subtle", children: _jsx("span", { className: "text-xs font-medium text-primary", children: isDragOverFileRef ? "Reference workspace file" : "Drop files to attach" }) }))] }), showAttach && (_jsx("input", { ref: fileInputRef, type: "file", multiple: true, onChange: handleFileInputChange, className: "hidden", "aria-hidden": "true", tabIndex: -1 })), (showAttach && attachments.hasEntries) || (enableFileReferences && fileRefs.hasRefs) ? (_jsxs("div", { className: "space-y-1.5 px-3 pb-2", children: [showAttach && attachments.hasEntries && (_jsx(AttachmentChipList, { entries: attachments.entries, onRemove: attachments.removeEntry, onRetry: attachments.retryEntry, disabled: isDisabled })), enableFileReferences && fileRefs.hasRefs && (_jsx(FileReferenceChipList, { refs: fileRefs.refs, onRemove: fileRefs.remove, disabled: isDisabled }))] })) : null, showAgent &&
                    agentSetup.state.status === "needsEnvVars" &&
                    !agentRef && (_jsxs("div", { role: "status", className: "mx-3 mb-2 flex items-center gap-2 rounded-md bg-warning/10 px-2.5 py-1.5 text-xs text-warning", children: [_jsx(AlertTriangleIcon, {}), _jsx("span", { children: "Agent needs configuration before use" }), _jsx("button", { type: "button", onClick: () => {
                                setConfigOpen(true);
                                setConfigActivePanel("agent");
                            }, disabled: isDisabled, className: "ml-auto shrink-0 rounded px-1.5 py-0.5 text-[0.6rem] font-medium hover:bg-warning/20 disabled:pointer-events-none disabled:opacity-50", children: "Configure" })] })), showMcp && mcpSetup.needsSetupCount > 0 && (_jsxs("div", { role: "status", className: "mx-3 mb-2 flex items-center gap-2 rounded-md bg-warning/10 px-2.5 py-1.5 text-xs text-warning", children: [_jsx(AlertTriangleIcon, {}), _jsx("span", { children: mcpSetup.needsSetupCount === 1
                                ? "1 MCP server needs configuration"
                                : `${mcpSetup.needsSetupCount} MCP servers need configuration` }), _jsx("button", { type: "button", onClick: () => {
                                if (mcpSetup.needsSetupCount === 1) {
                                    const key = Object.entries(mcpSetup.entries).find(([, e]) => e.status === "needsSetup")?.[0];
                                    configMcpInitialServerKeyRef.current = key;
                                }
                                else {
                                    configMcpInitialServerKeyRef.current = undefined;
                                }
                                setConfigOpen(true);
                                setConfigActivePanel("mcp");
                            }, disabled: isDisabled, className: "ml-auto shrink-0 rounded px-1.5 py-0.5 text-[0.6rem] font-medium hover:bg-warning/20 disabled:pointer-events-none disabled:opacity-50", children: "Configure" })] })), _jsx(ComposerToolbar, { disabled: isDisabled, isSubmitting: isSubmitting, canSend: canSend, onSend: composer.submit, onStop: onStop, isStopping: isStopping, showWorkspace: showWorkspace, workspaceCount: workspaceCount, onWorkspaceDirectAction: workspaceDirectAction, workspaceContent: workspace
                        ? _jsx(WorkspaceEditor, { workspace: workspace, disabled: isDisabled, gitHubConnection: gitHubConnection, enableGitHub: enableGitHub, enableLocal: enableLocal, onBrowseLocalFolder: onBrowseLocalFolder, initialPanel: workspaceInitialPanel })
                        : null, showAttach: showAttach, attachmentCount: attachments.entries.length, onAttachClick: () => fileInputRef.current?.click(), configureItems: configureItems, configOpen: configOpen, onConfigOpenChange: handleConfigOpenChange, configActivePanel: configActivePanel, onConfigActivePanelChange: handleConfigActivePanelChange, renderConfigPanel: renderConfigPanel, showHarnessSelector: showHarnessSelector, harness: harness, onHarnessChange: handleHarnessChange, showInteractionModePicker: showInteractionModePicker, interactionMode: interactionMode, onInteractionModeChange: handleInteractionModeChange, showModelSelector: showModelSelector, modelId: modelId, onModelChange: handleModelChange })] }) }));
});
export const SessionComposer = memo(SessionComposerInner);
// ---------------------------------------------------------------------------
// Agent setup error — secret-flow guidance or generic fallback
// ---------------------------------------------------------------------------
function AgentSetupError({ error }) {
    if (isSecretFlowError(error)) {
        return _jsx(SecretFlowErrorGuide, { error: error, className: "mt-2" });
    }
    return (_jsx("p", { className: "mt-2 text-xs text-destructive", children: getUserMessage(error) }));
}
// ---------------------------------------------------------------------------
// MCP key-to-ref utility
// ---------------------------------------------------------------------------
function mcpRefFromKey(key) {
    const idx = key.indexOf("/");
    return {
        org: key.slice(0, idx),
        slug: key.slice(idx + 1),
        kind: ApiResourceKind.mcp_server,
    };
}
//# sourceMappingURL=SessionComposer.js.map