import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
import { useState, useEffect, useMemo, useRef, useLayoutEffect, useCallback, } from "react";
import { useEditContext } from "../../../client/editContext";
import { ToolCallDisplay, InlineToolResultPreview, extractInlineToolResultImage, isFailedToolCall, isRejectedToolCall, } from "./ToolCallDisplay";
import { QuestionnaireRecap } from "./QuestionnaireRecap";
import { parseQuestionnaireRecap } from "../agentQuestionnaireRecap";
import { cleanToolFunctionName, selectGroupPendingApprovals, } from "./toolCallApprovalGate";
import { ApprovalCard } from "./ApprovalCard";
import { parseToolResultValue } from "../../agentMessageConversion";
import { MediaImage } from "../../MediaImage";
import { X, ChevronDown, ChevronRight, Loader2, Undo2, Check, CircleSlash2, } from "lucide-react";
import { Button } from "../../../../components/ui/button";
import { Checkbox } from "../../../../components/ui/checkbox";
import { Tooltip, TooltipTrigger, TooltipContent, } from "../../../../components/ui/tooltip";
import { CopyButton } from "../../../../components/ui/copy-button";
import { PlaceholderInput } from "../../../../components/ui/PlaceholderInput";
import { sanitizeHtml } from "../../../../lib/sanitize";
import { cn } from "../../../../lib/utils";
import { hasPromptPlaceholders } from "../agentPromptPlaceholders";
const hasQuickActionSubmission = (action) => [action?.prompt, action?.value].some((value) => value != null && String(value).trim().length > 0);
// Helper to extract partial quick actions from potentially incomplete JSON during streaming
const extractPartialQuickActions = (jsonText) => {
    const actions = [];
    let headline;
    // First try to parse complete JSON
    try {
        const parsed = JSON.parse(jsonText);
        headline =
            !Array.isArray(parsed) && typeof parsed?.headline === "string"
                ? parsed.headline.trim() || undefined
                : undefined;
        const rawActions = Array.isArray(parsed)
            ? parsed
            : parsed?.actions || parsed?.buttons || parsed?.choices || [];
        if (Array.isArray(rawActions)) {
            rawActions.forEach((a) => {
                if (!a || !hasQuickActionSubmission(a))
                    return;
                const label = a.label || a.text || String(a.value || a.prompt || "");
                if (label) {
                    actions.push({
                        id: a.id,
                        label,
                        prompt: a.prompt,
                        value: a.value,
                        style: a.style,
                        behavior: a.behavior &&
                            (a.behavior === "compose" || a.behavior === "submit")
                            ? a.behavior
                            : a.submit === false ||
                                a.compose === true ||
                                a.draft === true ||
                                a.mode === "compose"
                                ? "compose"
                                : undefined,
                        placeholder: a.placeholder,
                    });
                }
            });
        }
        return { headline, actions };
    }
    catch (e) {
        // JSON is incomplete, try to extract whatever complete action objects we can find
    }
    const headlineMatch = jsonText.match(/"(?:headline|title)"\s*:\s*"([^"\\]*(\\.[^"\\]*)*)"/);
    if (headlineMatch?.[1]) {
        try {
            headline = JSON.parse(`"${headlineMatch[1]}"`).trim() || undefined;
        }
        catch {
            headline = headlineMatch[1].trim() || undefined;
        }
    }
    // Look for individual action objects in the partial JSON
    // Match patterns like: { "label": "...", ... }
    const labelPattern = /"(?:label|text)"\s*:\s*"([^"\\]*(\\.[^"\\]*)*)"/g;
    const labelMatches = [];
    let labelMatch;
    while ((labelMatch = labelPattern.exec(jsonText)) !== null) {
        if (labelMatch[1]) {
            labelMatches.push({
                label: labelMatch[1],
                startIdx: labelMatch.index,
            });
        }
    }
    // For each label field found, try to find and parse the enclosing object
    for (const { label, startIdx } of labelMatches) {
        // Find the opening brace before this label field
        let openBrace = -1;
        for (let i = startIdx - 1; i >= 0; i--) {
            if (jsonText[i] === "{") {
                openBrace = i;
                break;
            }
            if (jsonText[i] === "}")
                break; // Hit another object's end
        }
        if (openBrace === -1)
            continue;
        // Find the closing brace after this label field
        let closeBrace = -1;
        let depth = 0;
        for (let i = openBrace; i < jsonText.length; i++) {
            if (jsonText[i] === "{")
                depth++;
            if (jsonText[i] === "}") {
                depth--;
                if (depth === 0) {
                    closeBrace = i;
                    break;
                }
            }
        }
        // Extract the object and try to parse it
        const objStr = closeBrace !== -1
            ? jsonText.substring(openBrace, closeBrace + 1)
            : jsonText.substring(openBrace) + "}"; // Try to close incomplete object
        try {
            const obj = JSON.parse(objStr);
            if (!hasQuickActionSubmission(obj))
                continue;
            const actionLabel = obj.label || obj.text || String(obj.value || obj.prompt || "");
            if (actionLabel) {
                actions.push({
                    id: obj.id,
                    label: actionLabel,
                    prompt: obj.prompt,
                    value: obj.value,
                    style: obj.style,
                    behavior: obj.behavior &&
                        (obj.behavior === "compose" || obj.behavior === "submit")
                        ? obj.behavior
                        : obj.submit === false ||
                            obj.compose === true ||
                            obj.draft === true ||
                            obj.mode === "compose"
                            ? "compose"
                            : undefined,
                    placeholder: obj.placeholder,
                });
            }
        }
        catch (e) {
            // Skip malformed objects
        }
    }
    return { headline, actions };
};
function parseContentSegments(content, hideIncomplete) {
    if (!content)
        return [];
    const segments = [];
    const fencedQuickToken = "```quick_action_buttons";
    const plainQuickToken = "quick_action_buttons";
    const fencedTodoToken = "```todo_list";
    const plainTodoToken = "todo_list";
    const fencedMediaToken = "```media_item";
    const fencedJsonToken = "```json";
    // Helper: find next valid plain token occurrence (at line start or after newline)
    const findNextPlainHeader = (token, fromIndex) => {
        let idx = content.indexOf(token, fromIndex);
        while (idx !== -1) {
            const before = idx > 0 ? content[idx - 1] : "\n";
            const after = content[idx + token.length] || "\n";
            const isBoundaryBefore = before === "\n" || before === "\r";
            const isBoundaryAfter = after === "\n" || after === "\r" || after === ":" || /\s/.test(after);
            // Ensure it's not the fenced token which would have ``` right before
            const isFencedPrefix = content.slice(Math.max(0, idx - 3), idx) === "```";
            if (isBoundaryBefore && isBoundaryAfter && !isFencedPrefix)
                return idx;
            idx = content.indexOf(token, idx + 1);
        }
        return -1;
    };
    // Helper: match JSON object braces starting at first '{'
    const findMatchingBraceEnd = (startPos) => {
        let i = startPos;
        let depth = 0;
        let inString = false;
        let stringQuote = null;
        let escaped = false;
        for (; i < content.length; i++) {
            const ch = content[i];
            if (inString) {
                if (escaped) {
                    escaped = false;
                }
                else if (ch === "\\") {
                    escaped = true;
                }
                else if (ch === stringQuote) {
                    inString = false;
                    stringQuote = null;
                }
                continue;
            }
            else {
                if (ch === '"' || ch === "'") {
                    inString = true;
                    stringQuote = ch;
                    continue;
                }
                if (ch === "{")
                    depth++;
                if (ch === "}") {
                    depth--;
                    if (depth === 0)
                        return i;
                }
            }
        }
        return -1;
    };
    let cursor = 0;
    while (cursor < content.length) {
        const nextFencedQuick = content.indexOf(fencedQuickToken, cursor);
        const nextPlainQuick = findNextPlainHeader(plainQuickToken, cursor);
        const nextFencedTodo = content.indexOf(fencedTodoToken, cursor);
        const nextPlainTodo = findNextPlainHeader(plainTodoToken, cursor);
        const nextFencedMedia = content.indexOf(fencedMediaToken, cursor);
        const nextFencedJson = content.indexOf(fencedJsonToken, cursor);
        const candidates = [];
        if (nextFencedQuick !== -1)
            candidates.push({
                start: nextFencedQuick,
                kind: "fenced",
                type: "quick",
            });
        if (nextPlainQuick !== -1)
            candidates.push({ start: nextPlainQuick, kind: "plain", type: "quick" });
        if (nextFencedTodo !== -1)
            candidates.push({ start: nextFencedTodo, kind: "fenced", type: "todo" });
        if (nextPlainTodo !== -1)
            candidates.push({ start: nextPlainTodo, kind: "plain", type: "todo" });
        if (nextFencedMedia !== -1)
            candidates.push({
                start: nextFencedMedia,
                kind: "fenced",
                type: "media",
            });
        if (nextFencedJson !== -1)
            candidates.push({ start: nextFencedJson, kind: "fenced", type: "json" });
        let pick = null;
        if (candidates.length > 0) {
            pick = candidates.reduce((min, cur) => cur.start < min.start ? cur : min);
        }
        if (pick === null) {
            const tail = content.slice(cursor);
            if (tail)
                segments.push({ kind: "text", text: tail });
            break;
        }
        // Push any text before the token
        const textBefore = content.slice(cursor, pick.start);
        if (textBefore)
            segments.push({ kind: "text", text: textBefore });
        if (pick.kind === "fenced") {
            const openTokenLength = pick.type === "quick"
                ? fencedQuickToken.length
                : pick.type === "todo"
                    ? fencedTodoToken.length
                    : pick.type === "json"
                        ? fencedJsonToken.length
                        : fencedMediaToken.length;
            const afterOpenLineStart = pick.start + openTokenLength;
            const openLineMatch = content
                .slice(afterOpenLineStart)
                .match(/^[^\n\r]*\r?\n?/);
            const openLineLength = openLineMatch ? openLineMatch[0].length : 0;
            const jsonStart = afterOpenLineStart + openLineLength;
            const close = content.indexOf("```", jsonStart);
            const jsonText = (close === -1
                ? content.slice(jsonStart)
                : content.slice(jsonStart, close)).trim();
            if (close === -1) {
                // Incomplete fenced block during streaming
                if (hideIncomplete && pick.type === "quick") {
                    // Try to parse partial quick actions and show what we have so far
                    const partialActionGroup = extractPartialQuickActions(jsonText);
                    if (partialActionGroup.actions.length > 0) {
                        segments.push({ kind: "actions", ...partialActionGroup });
                    }
                    break;
                }
                else if (hideIncomplete && pick.type === "json") {
                    // Try to show partial JSON during streaming
                    if (jsonText.trim()) {
                        try {
                            // Try to parse complete JSON
                            const parsed = JSON.parse(jsonText);
                            segments.push({ kind: "json", data: parsed, raw: jsonText });
                        }
                        catch {
                            // Show raw partial JSON during streaming
                            segments.push({
                                kind: "json",
                                data: null,
                                raw: jsonText + "...",
                            });
                        }
                    }
                    break;
                }
                else if (hideIncomplete) {
                    break;
                }
                else {
                    segments.push({ kind: "text", text: content.slice(pick.start) });
                    break;
                }
            }
            // Handle media_item blocks separately (pipe-delimited format, not JSON)
            if (pick.type === "media") {
                const items = [];
                // Parse pipe-delimited format: {GUID}|Label or just {GUID}
                const lines = jsonText
                    .split(/\r?\n/)
                    .map((l) => l.trim())
                    .filter((l) => l);
                for (const line of lines) {
                    // Only parse complete lines (must have closing brace)
                    if (line.includes("}")) {
                        const pipeIndex = line.indexOf("|");
                        if (pipeIndex !== -1) {
                            // Has label
                            const itemId = line.substring(0, pipeIndex).trim();
                            const label = line.substring(pipeIndex + 1).trim();
                            if (itemId && /\{[0-9a-fA-F-]+\}/.test(itemId)) {
                                items.push({ itemId, label: label || undefined });
                            }
                        }
                        else {
                            // No label
                            const itemId = line.trim();
                            if (itemId && /\{[0-9a-fA-F-]+\}/.test(itemId)) {
                                items.push({ itemId });
                            }
                        }
                    }
                }
                if (items.length > 0) {
                    segments.push({
                        kind: "media",
                        items: items,
                    });
                }
                cursor = close + 3;
                continue;
            }
            // Handle json blocks - display as formatted JSON
            if (pick.type === "json") {
                try {
                    const parsed = JSON.parse(jsonText);
                    segments.push({
                        kind: "json",
                        data: parsed,
                        raw: jsonText,
                    });
                }
                catch {
                    // If JSON is invalid, show as raw text
                    segments.push({
                        kind: "json",
                        data: null,
                        raw: jsonText,
                    });
                }
                cursor = close + 3;
                continue;
            }
            try {
                const parsed = JSON.parse(jsonText);
                if (pick.type === "quick") {
                    const rawActions = Array.isArray(parsed)
                        ? parsed
                        : parsed?.actions || parsed?.buttons || parsed?.choices || [];
                    const actions = [];
                    if (Array.isArray(rawActions)) {
                        rawActions.forEach((a) => {
                            if (!a || !hasQuickActionSubmission(a))
                                return;
                            const action = {
                                id: a.id,
                                label: a.label || a.text || String(a.value || a.prompt || ""),
                                prompt: a.prompt,
                                value: a.value,
                                style: a.style,
                                behavior: a.behavior &&
                                    (a.behavior === "compose" || a.behavior === "submit")
                                    ? a.behavior
                                    : a.submit === false ||
                                        a.compose === true ||
                                        a.draft === true ||
                                        a.mode === "compose"
                                        ? "compose"
                                        : undefined,
                                placeholder: a.placeholder,
                            };
                            if (action.label)
                                actions.push(action);
                        });
                    }
                    if (actions.length > 0) {
                        const headline = typeof parsed?.headline === "string"
                            ? parsed.headline.trim() || undefined
                            : undefined;
                        segments.push({ kind: "actions", headline, actions });
                    }
                }
                else if (pick.type === "todo") {
                    // todo_list
                    const todoItems = Array.isArray(parsed)
                        ? parsed
                        : parsed?.items || [];
                    const title = Array.isArray(parsed) ? undefined : parsed?.title;
                    const cleaned = (Array.isArray(todoItems) ? todoItems : [])
                        .map((i) => {
                        if (!i)
                            return null;
                        const text = i.text || i.label || String(i.task || i.title || "");
                        if (!text)
                            return null;
                        return {
                            id: i.id,
                            title: text,
                            status: i.status || "pending",
                            instructions: i.note || i.description,
                        };
                    })
                        .filter(Boolean);
                    if (cleaned.length > 0) {
                        segments.push({ kind: "todo", title, items: cleaned });
                    }
                }
            }
            catch {
                if (!hideIncomplete) {
                    segments.push({
                        kind: "text",
                        text: content.slice(pick.start, close + 3),
                    });
                }
            }
            cursor = close + 3;
            continue;
        }
        // Plain header without backticks
        const headerTokenLength = pick.type === "quick" ? plainQuickToken.length : plainTodoToken.length;
        const afterHeaderStart = pick.start + headerTokenLength;
        const headerRestMatch = content
            .slice(afterHeaderStart)
            .match(/^[^\n\r]*\r?\n?/);
        const headerRestLen = headerRestMatch ? headerRestMatch[0].length : 0;
        const afterHeader = afterHeaderStart + headerRestLen;
        // Find first '{' after header (could be same line or following lines)
        const braceStart = content.indexOf("{", afterHeader);
        if (braceStart === -1) {
            if (hideIncomplete) {
                break;
            }
            else {
                // Treat as plain text if no JSON object is present
                cursor = afterHeader;
                continue;
            }
        }
        const braceEnd = findMatchingBraceEnd(braceStart);
        const jsonText = (braceEnd === -1
            ? content.slice(braceStart)
            : content.slice(braceStart, braceEnd + 1)).trim();
        if (braceEnd === -1) {
            // Incomplete plain JSON block during streaming
            if (hideIncomplete && pick.type === "quick") {
                // Try to parse partial quick actions and show what we have so far
                const partialActionGroup = extractPartialQuickActions(jsonText);
                if (partialActionGroup.actions.length > 0) {
                    segments.push({ kind: "actions", ...partialActionGroup });
                }
                break;
            }
            else if (hideIncomplete) {
                break;
            }
            else {
                // Incomplete JSON; include the rest as text
                const remaining = content.slice(pick.start);
                if (remaining)
                    segments.push({ kind: "text", text: remaining });
                break;
            }
        }
        try {
            const parsed = JSON.parse(jsonText);
            if (pick.type === "quick") {
                const rawActions = Array.isArray(parsed)
                    ? parsed
                    : parsed?.actions || parsed?.buttons || parsed?.choices || [];
                const actions = [];
                if (Array.isArray(rawActions)) {
                    rawActions.forEach((a) => {
                        if (!a || !hasQuickActionSubmission(a))
                            return;
                        const action = {
                            id: a.id,
                            label: a.label || a.text || String(a.value || a.prompt || ""),
                            prompt: a.prompt,
                            value: a.value,
                            style: a.style,
                            behavior: a.behavior &&
                                (a.behavior === "compose" || a.behavior === "submit")
                                ? a.behavior
                                : a.submit === false ||
                                    a.compose === true ||
                                    a.draft === true ||
                                    a.mode === "compose"
                                    ? "compose"
                                    : undefined,
                            placeholder: a.placeholder,
                        };
                        if (action.label)
                            actions.push(action);
                    });
                }
                if (actions.length > 0) {
                    const headline = typeof parsed?.headline === "string"
                        ? parsed.headline.trim() || undefined
                        : undefined;
                    segments.push({ kind: "actions", headline, actions });
                }
            }
            else {
                const todoItems = Array.isArray(parsed) ? parsed : parsed?.items || [];
                const title = Array.isArray(parsed) ? undefined : parsed?.title;
                const cleaned = (Array.isArray(todoItems) ? todoItems : [])
                    .map((i) => {
                    if (!i)
                        return null;
                    const text = i.text || i.label || String(i.task || i.title || "");
                    if (!text)
                        return null;
                    return {
                        id: i.id,
                        title: text,
                        status: i.status || "pending",
                        instructions: i.note || i.description,
                    };
                })
                    .filter(Boolean);
                if (cleaned.length > 0) {
                    segments.push({ kind: "todo", title, items: cleaned });
                }
            }
        }
        catch {
            // On parse failure, keep original as text (excluding already-pushed prefix)
            const fallbackText = content.slice(pick.start, braceEnd + 1);
            if (!hideIncomplete && fallbackText) {
                segments.push({ kind: "text", text: fallbackText });
            }
        }
        cursor = braceEnd + 1;
    }
    return segments;
}
// Helper to extract command links from markdown
function extractCommandLinks(text) {
    const links = [];
    const commandLinkPattern = /\[([^\]]+)\]\(command:([^)]+)\)/g;
    let match;
    while ((match = commandLinkPattern.exec(text)) !== null) {
        if (match[1] && match[2]) {
            links.push({
                label: match[1],
                command: match[2],
                index: match.index,
            });
        }
    }
    return { text, links };
}
// JSON Block Display Component
function JsonBlockDisplay({ data, raw, className, defaultCollapsed = false, }) {
    const [isExpanded, setIsExpanded] = useState(!defaultCollapsed);
    const [isCopied, setIsCopied] = useState(false);
    const handleCopy = async () => {
        try {
            const textToCopy = data ? JSON.stringify(data, null, 2) : raw;
            await navigator.clipboard.writeText(textToCopy);
            setIsCopied(true);
            setTimeout(() => setIsCopied(false), 2000);
        }
        catch (err) {
            console.error("Failed to copy:", err);
        }
    };
    // Try to extract a meaningful title from the JSON data
    const getTitle = () => {
        if (!data)
            return "JSON";
        if (data.__typename)
            return data.__typename;
        if (data.name)
            return data.name;
        if (data.type)
            return data.type;
        if (data.title)
            return data.title;
        if (Array.isArray(data))
            return `Array (${data.length} items)`;
        return "JSON";
    };
    // Render JSON with syntax highlighting
    const renderJsonValue = (value, indent = 0) => {
        const indentStr = "  ".repeat(indent);
        const nextIndent = indent + 1;
        if (value === null) {
            return _jsx("span", { className: "text-neutral-grey-50", children: "null" });
        }
        if (typeof value === "boolean") {
            return _jsx("span", { className: "text-highlight-100", children: value.toString() });
        }
        if (typeof value === "number") {
            return _jsx("span", { className: "text-feedback-blue", children: value });
        }
        if (typeof value === "string") {
            // Check if it looks like a GUID
            const isGuid = /^\{?[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}\}?$/.test(value);
            return (_jsxs("span", { className: isGuid ? "text-feedback-orange" : "text-feedback-green", children: ["\"", value, "\""] }));
        }
        if (Array.isArray(value)) {
            if (value.length === 0) {
                return _jsx("span", { className: "text-neutral-grey-50", children: "[]" });
            }
            return (_jsxs(_Fragment, { children: [_jsx("span", { className: "text-neutral-grey-50", children: "[" }), value.map((item, idx) => (_jsxs("div", { className: "pl-4", children: [renderJsonValue(item, nextIndent), idx < value.length - 1 && (_jsx("span", { className: "text-neutral-grey-50", children: "," }))] }, idx))), _jsx("span", { className: "text-neutral-grey-50", children: "]" })] }));
        }
        if (typeof value === "object") {
            const entries = Object.entries(value);
            if (entries.length === 0) {
                return _jsx("span", { className: "text-neutral-grey-50", children: "{}" });
            }
            return (_jsxs(_Fragment, { children: [_jsx("span", { className: "text-neutral-grey-50", children: "{" }), entries.map(([key, val], idx) => (_jsxs("div", { className: "pl-4", children: [_jsxs("span", { className: "text-feedback-red", children: ["\"", key, "\""] }), _jsx("span", { className: "text-neutral-grey-50", children: ": " }), renderJsonValue(val, nextIndent), idx < entries.length - 1 && (_jsx("span", { className: "text-neutral-grey-50", children: "," }))] }, key))), _jsx("span", { className: "text-neutral-grey-50", children: "}" })] }));
        }
        return _jsx("span", { children: String(value) });
    };
    return (_jsxs("div", { className: `border-border-default bg-neutral-grey-5 my-2 overflow-hidden rounded-lg border ${className || ""}`, children: [_jsxs("div", { className: "border-border-default bg-neutral-grey-5 flex items-center justify-between border-b px-3 py-2", children: [_jsxs("button", { onClick: () => setIsExpanded(!isExpanded), className: "text-neutral-grey-100 hover:text-neutral-grey-100 flex items-center gap-2 text-xs font-medium", children: [isExpanded ? (_jsx(ChevronDown, { strokeWidth: 1, size: 14 })) : (_jsx(ChevronRight, { strokeWidth: 1, size: 14 })), _jsx("span", { children: getTitle() })] }), _jsx("button", { onClick: handleCopy, className: "badge-pad text-neutral-grey-50 hover:bg-neutral-grey-10 hover:text-neutral-grey-100 rounded text-xs transition-colors", children: isCopied ? "Copied!" : "Copy" })] }), isExpanded && (_jsx("div", { className: "overflow-x-auto p-3", children: data ? (_jsx("pre", { className: "font-mono text-[11px] leading-relaxed", children: renderJsonValue(data) })) : (_jsx("pre", { className: "text-neutral-grey-50 font-mono text-[11px] leading-relaxed whitespace-pre-wrap", children: raw })) }))] }));
}
function simpleFormatToHtml(text) {
    const stripLegacyFontTags = (value) => value
        .replace(/<\/?font\b[^>]*>/gi, "")
        .replace(/&lt;\/?font\b(?:(?!&gt;)[\s\S])*?&gt;/gi, "");
    const normalizedText = stripLegacyFontTags(text);
    //Inline formatting
    const format = (s) => s
        .replace(/\[([^\]]+)\]\(([^)]+)\)/g, (_, text, url) => `<a href="${url}" target="_blank" rel="noopener noreferrer">${text.replace(/\*\*(.*?)\*\*/g, "<strong>$1</strong>")}</a>`)
        .replace(/^######\s+(.+)$/, "<h6>$1</h6>")
        .replace(/^#####\s+(.+)$/, "<h5>$1</h5>")
        .replace(/^####\s+(.+)$/, "<h4>$1</h4>")
        .replace(/^###\s+(.+)$/, "<h3>$1</h3>")
        .replace(/^##\s+(.+)$/, "<h2>$1</h2>")
        .replace(/^#\s+(.+)$/, "<h1>$1</h1>")
        .replace(/\*\*(.*?)\*\*/g, "<strong>$1</strong>");
    const lines = normalizedText.split(/\r?\n/);
    const blocks = [];
    let i = 0;
    while (i < lines.length) {
        const line = lines[i];
        if (!line) {
            i++;
            continue;
        }
        const t = line.trim();
        // Sepperator Lines
        if (/^[\-\*\_]{3,}$/.test(t)) {
            blocks.push("<hr />");
            i++;
            continue;
        }
        // Table
        if (t.startsWith("|") && t.endsWith("|") && t.split("|").length >= 3) {
            const rows = [];
            let j = i;
            while (j < lines.length) {
                const l = lines[j];
                if (!l)
                    break;
                const lt = l.trim();
                if (lt.startsWith("|") && lt.endsWith("|")) {
                    if (/^[\|\s:\-]+$/.test(lt)) {
                        j++; // Skip separator
                        continue;
                    }
                    rows.push(lt
                        .split("|")
                        .slice(1, -1)
                        .map((c) => c.trim()));
                    j++;
                }
                else
                    break;
            }
            if (rows.length > 0 && rows[0]) {
                let html = "<table><thead><tr>";
                rows[0].forEach((c) => (html += `<th>${format(c)}</th>`));
                html += "</tr></thead>";
                if (rows.length > 1) {
                    html += "<tbody>";
                    for (let k = 1; k < rows.length; k++) {
                        html += "<tr>";
                        rows[k]?.forEach((c) => (html += `<td>${format(c)}</td>`));
                        html += "</tr>";
                    }
                    html += "</tbody>";
                }
                blocks.push(html + "</table>");
                i = j;
                continue;
            }
        }
        // List (supports nested mixed ul/ol without recursion)
        const listLineMatch = line.match(/^(\s*)([\-\*]|\d+\.)\s+(.+)$/);
        if (listLineMatch && listLineMatch[2] && listLineMatch[3]) {
            const listLines = [];
            let j = i;
            while (j < lines.length) {
                const currentLine = lines[j];
                if (!currentLine)
                    break;
                const m = currentLine.match(/^(\s*)([\-\*]|\d+\.)\s+(.+)$/);
                if (!m || !m[2] || !m[3])
                    break;
                listLines.push({
                    indent: m[1]?.length || 0,
                    marker: m[2],
                    content: m[3],
                });
                j++;
            }
            if (listLines.length > 0) {
                const indentStack = [];
                const tagStack = [];
                let html = "";
                let itemOpen = false;
                const openList = (tag, indent) => {
                    html += `<${tag}>`;
                    tagStack.push(tag);
                    indentStack.push(indent);
                };
                const closeItem = () => {
                    if (!itemOpen)
                        return;
                    html += "</li>";
                    itemOpen = false;
                };
                const closeList = () => {
                    closeItem();
                    const tag = tagStack.pop();
                    indentStack.pop();
                    if (tag)
                        html += `</${tag}>`;
                };
                for (const entry of listLines) {
                    const tag = /\d/.test(entry.marker) ? "ol" : "ul";
                    if (tagStack.length === 0) {
                        openList(tag, entry.indent);
                    }
                    else {
                        let currentIndent = indentStack[indentStack.length - 1];
                        while (tagStack.length > 0 &&
                            currentIndent !== undefined &&
                            entry.indent < currentIndent) {
                            closeList();
                            currentIndent = indentStack[indentStack.length - 1];
                        }
                        if (currentIndent === undefined ||
                            (entry.indent !== currentIndent && entry.indent > currentIndent)) {
                            if (!itemOpen) {
                                html += "<li>";
                                itemOpen = true;
                            }
                            openList(tag, entry.indent);
                        }
                        else if (entry.indent === currentIndent) {
                            const currentTag = tagStack[tagStack.length - 1];
                            if (currentTag && currentTag !== tag) {
                                closeList();
                                openList(tag, entry.indent);
                            }
                        }
                        else if (currentIndent === undefined) {
                            openList(tag, entry.indent);
                        }
                    }
                    closeItem();
                    html += `<li>${format(entry.content)}`;
                    itemOpen = true;
                }
                while (tagStack.length > 0)
                    closeList();
                if (html) {
                    blocks.push(html);
                    i = j;
                    continue;
                }
            }
        }
        // Regular text
        blocks.push(format(line));
        i++;
    }
    // Join blocks with <br/> between text, but not between block elements
    let result = "";
    for (let k = 0; k < blocks.length; k++) {
        const block = blocks[k];
        if (!block)
            continue;
        result += block;
        if (k < blocks.length - 1) {
            const next = blocks[k + 1];
            if (!next)
                continue;
            const isBlock = /^<(table|ul|ol|hr|h[1-6])/i.test(block);
            const nextIsBlock = /^<(table|ul|ol|hr|h[1-6])/i.test(next);
            if (!isBlock && !nextIsBlock)
                result += "<br/>";
        }
    }
    return sanitizeHtml(result.replace(/<\/(h[1-6])><br\/>/g, "</$1>"));
}
const assistantMessageTextClassName = [
    "max-w-none text-xs leading-5 text-neutral-grey-100 select-text",
    "[&_a]:text-neutral-grey-100 [&_a]:underline [&_a]:underline-offset-2",
    "[&_strong]:font-medium [&_strong]:text-neutral-grey-100",
    "[&_h1]:mt-0 [&_h1]:mb-3 [&_h1]:text-[16px] [&_h1]:leading-6 [&_h1]:font-medium [&_h1]:text-neutral-grey-100",
    "[&_h2]:mt-3 [&_h2]:mb-1.5 [&_h2]:text-[15px] [&_h2]:leading-5 [&_h2]:font-medium [&_h2]:text-neutral-grey-100",
    "[&_h3]:mt-2.5 [&_h3]:mb-1 [&_h3]:text-[13px] [&_h3]:leading-5 [&_h3]:font-medium [&_h3]:text-neutral-grey-100",
    "[&_h4]:mt-2 [&_h4]:mb-1 [&_h4]:text-[12px] [&_h4]:leading-5 [&_h4]:font-medium [&_h4]:text-neutral-grey-100",
    "[&_h5]:mt-2 [&_h5]:mb-1 [&_h5]:text-[12px] [&_h5]:leading-5 [&_h5]:font-medium [&_h5]:text-neutral-grey-100",
    "[&_h6]:mt-2 [&_h6]:mb-1 [&_h6]:text-[12px] [&_h6]:leading-5 [&_h6]:font-medium [&_h6]:text-neutral-grey-100",
    "[&_ul]:my-1.5 [&_ul]:list-disc [&_ul]:pl-5",
    "[&_ol]:my-1.5 [&_ol]:list-decimal [&_ol]:pl-5",
    "[&_li]:my-0.5 [&_li]:pl-1 [&_li]:leading-5 [&_li]:text-neutral-grey-100",
    "[&_li::marker]:text-neutral-grey-100",
    "[&_table]:my-2 [&_table]:overflow-hidden [&_table]:rounded-[4px] [&_table]:border [&_table]:border-separate [&_table]:border-spacing-0 [&_table]:border-neutral-grey-10 [&_table]:text-[12px] [&_table]:text-neutral-grey-100",
    "[&_thead]:bg-neutral-grey-5",
    "[&_th]:border-r [&_th]:border-b [&_th]:border-neutral-grey-10 [&_th]:bg-neutral-grey-5 [&_th]:px-3 [&_th]:py-2 [&_th]:text-[12px] [&_th]:font-medium [&_th]:text-neutral-grey-100 [&_th:last-child]:border-r-0",
    "[&_td]:border-r [&_td]:border-b [&_td]:border-neutral-grey-10 [&_td]:px-3 [&_td]:py-2 [&_td]:text-[12px] [&_td]:text-neutral-grey-100 [&_td:last-child]:border-r-0",
    "[&_tbody_tr:last-child_td]:border-b-0",
].join(" ");
const blockLevelAssistantHtmlPattern = /<(?:table|ul|ol|hr|h[1-6])(?:\s|>|\/)/i;
function FormattedAssistantHtml({ html }) {
    const hasBlockMarkup = blockLevelAssistantHtmlPattern.test(html);
    const Component = hasBlockMarkup ? "div" : "span";
    return (_jsx(Component, { className: hasBlockMarkup ? "contents" : undefined, dangerouslySetInnerHTML: { __html: html } }));
}
const getToolFunctionName = (toolCall) => {
    return cleanToolFunctionName(toolCall.function?.name || toolCall.displayName || "");
};
// Must agree with what QuestionnaireRecap actually renders — emitting a recap
// item for an unrenderable payload would split the activity group around an
// invisible hole.
const isQuestionnaireRecapToolCall = (toolCall) => {
    if (getToolFunctionName(toolCall).toLowerCase() !== "ask-questionnaire") {
        return false;
    }
    if (!toolCall.function?.result) {
        return false;
    }
    return (parseQuestionnaireRecap({
        functionName: toolCall.function.name,
        functionArguments: toolCall.function.arguments,
        functionResult: toolCall.function.result,
        functionError: toolCall.function.error,
    }) !== null);
};
const isScreenshotCaptureToolCall = (toolCall) => {
    const name = getToolFunctionName(toolCall).toLowerCase();
    return (name === "capture-page-screenshot" ||
        name === "capture-parhelia-ui-screenshot");
};
const getScreenshotToolResultPreview = (toolCall) => {
    if (!isScreenshotCaptureToolCall(toolCall)) {
        return null;
    }
    if (!toolCall.isCompleted || toolCall.function?.error) {
        return null;
    }
    const result = toolCall.function?.result;
    if (!result) {
        return null;
    }
    const parsedResult = parseToolResultValue(result);
    return extractInlineToolResultImage(parsedResult ?? result);
};
const isActivityItem = (item) => {
    if (item.type === "toolcalls") {
        return item.toolCalls.length > 0;
    }
    if (item.type !== "segment") {
        return false;
    }
    return (item.segment.kind === "reasoning" && item.segment.text.trim().length > 0);
};
const isVisibleUserDirectedItem = (item) => {
    if (item.type === "questionnaire-recap") {
        return true;
    }
    if (item.type === "screenshot-preview") {
        return true;
    }
    if (item.type !== "segment" || item.segment.kind === "reasoning") {
        return false;
    }
    const segment = item.segment;
    if (segment.kind === "text") {
        return segment.text.trim().length > 0;
    }
    if (segment.kind === "actions") {
        return segment.actions.length > 0;
    }
    if (segment.kind === "todo") {
        return segment.items.length > 0 || !!segment.title?.trim();
    }
    if (segment.kind === "media") {
        return segment.items.length > 0;
    }
    if (segment.kind === "json") {
        return (!!segment.raw.trim() ||
            (segment.data !== null && segment.data !== undefined));
    }
    return false;
};
const groupActivityItems = (flatItems) => {
    const displayItems = [];
    let activityEntries = [];
    const flushActivityEntries = () => {
        const entries = activityEntries;
        if (entries.length === 0) {
            return;
        }
        const first = entries[0];
        if (entries.length === 1) {
            displayItems.push({
                type: "single",
                entry: first,
                key: `item-${first.index}`,
            });
        }
        else {
            const last = entries[entries.length - 1];
            displayItems.push({
                type: "activity-group",
                entries,
                key: `activity-${first.index}-${last.index}`,
            });
        }
        activityEntries = [];
    };
    flatItems.forEach((item, index) => {
        if (isActivityItem(item)) {
            activityEntries.push({ item, index });
            return;
        }
        if (isVisibleUserDirectedItem(item)) {
            flushActivityEntries();
            displayItems.push({
                type: "single",
                entry: { item, index },
                key: `item-${index}`,
            });
        }
    });
    flushActivityEntries();
    return displayItems;
};
function ActivityBlockGroup({ entries, isLive, renderEntry, }) {
    const toolCalls = entries.flatMap((entry) => entry.item.type === "toolcalls" ? entry.item.toolCalls : []);
    const thinkingCount = entries.filter((entry) => entry.item.type === "segment" && entry.item.segment.kind === "reasoning").length;
    const toolCallCount = toolCalls.length;
    const toolNames = [
        ...new Set(toolCalls.map(getToolFunctionName).filter(Boolean)),
    ];
    const errorCount = toolCalls.filter(isFailedToolCall).length;
    const rejectedCount = toolCalls.filter(isRejectedToolCall).length;
    const hasIncompleteToolCall = toolCalls.some((toolCall) => {
        return (!toolCall.isCompleted &&
            !toolCall.function?.result &&
            !toolCall.function?.error);
    });
    // Always start collapsed — a pending approval is surfaced by the dedicated
    // ApprovalCard after the group, not by force-expanding the diagnostics.
    const [isExpanded, setIsExpanded] = useState(false);
    const summaryParts = [
        toolCallCount > 0
            ? `${toolCallCount} tool call${toolCallCount !== 1 ? "s" : ""}`
            : null,
        thinkingCount > 0
            ? `${thinkingCount} thinking block${thinkingCount !== 1 ? "s" : ""}`
            : null,
    ].filter(Boolean);
    const detailsText = toolNames.length <= 3
        ? toolNames.join(", ")
        : `${toolNames.slice(0, 2).join(", ")} +${toolNames.length - 2} more`;
    const isActive = !errorCount && !rejectedCount && (isLive || hasIncompleteToolCall);
    return (_jsxs("div", { className: "my-3 overflow-hidden rounded-lg", "data-testid": "agent-activity-group", children: [_jsxs("button", { type: "button", className: "group text-neutral-grey-50 hover:bg-neutral-grey-5/60 hover:text-neutral-grey-100 flex w-full cursor-pointer items-center gap-1.5 px-2 py-1.5 text-left text-xs transition-all", onClick: () => {
                    setIsExpanded(!isExpanded);
                }, "aria-expanded": isExpanded, "data-testid": "agent-activity-group-toggle", children: [_jsx("div", { className: "flex h-4 w-4 shrink-0 items-center justify-center transition-transform group-hover:scale-110", children: isExpanded ? (_jsx(ChevronDown, { size: 14, strokeWidth: 1, className: "text-neutral-grey-50" })) : (_jsx(ChevronRight, { size: 14, strokeWidth: 1, className: "text-neutral-grey-50" })) }), _jsx("span", { className: `shrink-0 leading-4 font-medium ${isActive ? "animate-text-shimmer" : ""}`, children: summaryParts.join(", ") }), detailsText && (_jsx("span", { className: "text-neutral-grey-50 min-w-0 flex-1 truncate text-[10px] leading-4", children: detailsText })), errorCount > 0 && (_jsxs("span", { className: "text-feedback-red shrink-0 text-[10px] leading-4", children: ["(", errorCount, " error", errorCount !== 1 ? "s" : "", ")"] })), rejectedCount > 0 && (_jsxs("span", { className: "text-feedback-orange shrink-0 text-[10px] leading-4", children: ["(", rejectedCount, " rejected)"] })), _jsx("div", { className: "ml-auto flex h-4 w-4 shrink-0 items-center justify-center", children: errorCount > 0 ? (_jsx(XCircleLike, {})) : rejectedCount > 0 ? (_jsx(CircleSlash2, { "aria-label": "Rejected tool call", className: "text-feedback-orange h-3.5 w-3.5", strokeWidth: 1 })) : isActive ? (_jsx(Loader2, { className: "text-neutral-grey-50 h-3.5 w-3.5 animate-spin", strokeWidth: 1 })) : (_jsx(Check, { className: "text-feedback-green h-3.5 w-3.5", strokeWidth: 1 })) })] }), _jsx("div", { className: "grid transition-[grid-template-rows] duration-200 ease-out motion-reduce:transition-none", style: {
                    gridTemplateRows: isExpanded ? "1fr" : "0fr",
                }, "aria-hidden": !isExpanded, "data-testid": "agent-activity-group-content", children: _jsx("div", { className: "min-h-0 overflow-hidden", children: _jsx("div", { className: "border-border-default/60 border-t px-2 py-1", children: entries.map((entry) => renderEntry(entry)) }) }) })] }));
}
function XCircleLike() {
    return (_jsx("span", { className: "border-feedback-red text-feedback-red flex h-3.5 w-3.5 items-center justify-center rounded-full border", children: _jsx(X, { className: "h-2.5 w-2.5", strokeWidth: 1 }) }));
}
// Thinking block component with auto-scroll
function ThinkingBlock({ text, isStreaming, isCollapsed, thinkingKey, onToggleCollapse, }) {
    const contentRef = useRef(null);
    // Auto-scroll to bottom when content changes during streaming
    useLayoutEffect(() => {
        if (isStreaming && !isCollapsed && contentRef.current) {
            contentRef.current.scrollTop = contentRef.current.scrollHeight;
        }
    }, [text, isStreaming, isCollapsed]);
    return (_jsxs("div", { className: `my-1 flex flex-col gap-1 text-[11px] transition-colors duration-200 ${isStreaming ? "text-neutral-grey-100" : "text-neutral-grey-50"}`, children: [_jsxs("div", { className: "text-neutral-grey-50 hover:text-neutral-grey-100 flex cursor-pointer items-center gap-1.5 p-2 text-[10px] font-medium tracking-wider transition-colors", onClick: onToggleCollapse, children: [isCollapsed ? (_jsx(ChevronRight, { strokeWidth: 1, size: 12 })) : (_jsx(ChevronDown, { strokeWidth: 1, size: 12 })), _jsx("span", { children: "Thinking" }), isStreaming && (_jsx(Loader2, { className: "text-neutral-grey-50 ml-1 h-3 w-3 animate-spin", strokeWidth: 1 }))] }), !isCollapsed && (_jsxs("div", { ref: contentRef, className: `prose-sm max-h-[6rem] overflow-y-auto px-2 pb-2 leading-relaxed whitespace-pre-wrap italic ${isStreaming ? "text-neutral-grey-50" : ""}`, children: [text, isStreaming && (_jsx("span", { className: "bg-neutral-grey-50 ml-0.5 inline-block h-3 w-0.5 animate-pulse" }))] }))] }));
}
const getQuickActionSubmittableText = (action) => (action.prompt || action.value || "").trim();
// Renders a "Recommended actions" card. Actions whose prompt carries
// {{placeholder}} / <<placeholder>> tokens expand an inline template editor
// directly beneath the button (animated open/close) instead of routing the
// template down to the bottom composer.
function QuickActionsCard({ headline, actions, message, className, onQuickAction, }) {
    const [expandedIndex, setExpandedIndex] = useState(null);
    // Lags behind expandedIndex on close so the template stays mounted through
    // the collapse transition (matching the open animation).
    const [renderedIndex, setRenderedIndex] = useState(null);
    useEffect(() => {
        if (expandedIndex !== null) {
            setRenderedIndex(expandedIndex);
        }
    }, [expandedIndex]);
    const renderedAction = renderedIndex !== null ? actions[renderedIndex] : undefined;
    const isOpen = expandedIndex !== null;
    return (_jsxs("div", { className: cn("border-neutral-grey-10 my-3 rounded-2xl border bg-white px-3 py-2.5", className), children: [headline && (_jsx("div", { className: "text-neutral-grey-100 mb-2.5 text-[13px] leading-4 font-medium", children: headline })), _jsx("div", { className: "flex flex-wrap gap-2", children: actions.map((a, aIdx) => {
                    const buttonLabel = a.label;
                    const variant = a.style === "destructive"
                        ? "destructive"
                        : a.style === "outline"
                            ? "outline"
                            : a.style === "secondary"
                                ? "secondary"
                                : "default"; // primary
                    const buttonClassName = variant === "outline"
                        ? "h-7 rounded-full border-neutral-grey-10 bg-white px-3 text-xs font-medium text-neutral-grey-100 shadow-none hover:bg-neutral-grey-5"
                        : "h-7 rounded-full border border-transparent bg-neutral-grey-10 px-3 text-xs font-medium text-neutral-grey-100 shadow-none hover:bg-neutral-grey-5";
                    const submittedText = getQuickActionSubmittableText(a);
                    const hasTemplate = hasPromptPlaceholders(submittedText);
                    const isExpanded = expandedIndex === aIdx;
                    return (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(Button, { size: "xs", variant: variant, className: cn(buttonClassName, isExpanded &&
                                        "bg-feedback-blue-light/60 ring-feedback-blue/40 ring-1"), "aria-expanded": hasTemplate ? isExpanded : undefined, onClick: () => {
                                        // Templated actions reveal the inline editor; only when a
                                        // handler exists to receive the filled result.
                                        if (hasTemplate && onQuickAction) {
                                            setExpandedIndex((current) => current === aIdx ? null : aIdx);
                                            return;
                                        }
                                        onQuickAction?.({
                                            label: buttonLabel,
                                            prompt: a.prompt,
                                            value: a.value,
                                            behavior: a.behavior,
                                            placeholder: a.placeholder,
                                        }, message);
                                    }, children: buttonLabel }) }), submittedText && !hasTemplate && (_jsx(TooltipContent, { children: _jsx("p", { className: "max-w-xs break-words", children: submittedText }) }))] }, (a.id || buttonLabel || "btn") + "-" + aIdx));
                }) }), _jsx("div", { className: "grid transition-[grid-template-rows] duration-200 ease-out motion-reduce:transition-none", style: { gridTemplateRows: isOpen ? "1fr" : "0fr" }, "aria-hidden": !isOpen, onTransitionEnd: (event) => {
                    if (event.propertyName === "grid-template-rows" &&
                        expandedIndex === null) {
                        setRenderedIndex(null);
                    }
                }, "data-testid": "agent-quick-action-template", children: _jsx("div", { className: "min-h-0 overflow-hidden", children: renderedAction && (_jsx("div", { className: "pt-2.5", children: _jsx(PlaceholderInput, { text: getQuickActionSubmittableText(renderedAction), variant: "quickAction", showButtons: true, onComplete: (filledText) => {
                                // Submitting the filled template sends the prompt directly,
                                // regardless of the action's original behavior.
                                onQuickAction?.({
                                    label: renderedAction.label,
                                    prompt: filledText,
                                    behavior: "submit",
                                }, message);
                                setExpandedIndex(null);
                            }, onCancel: () => setExpandedIndex(null) }, renderedIndex ?? "template") })) }) })] }));
}
export function AiResponseMessage({ messages, finished, editOperations, error, onQuickAction, defaultCollapseJson = false, agentId, allPendingApprovals, onSwitchToAutonomous, readOnly = false, followsUserMessage = false, }) {
    const editContext = useEditContext();
    // const [showFunctions, setShowFunctions] = useState(false);
    const [changesRejected, setChangesRejected] = useState(undefined);
    const [confirmingUndo, setConfirmingUndo] = useState(false);
    // Store tool calls to preserve them during streaming
    const [preservedToolCalls, setPreservedToolCalls] = useState({});
    // Track popover open state for each tool call
    const [openPopovers, setOpenPopovers] = useState({});
    // Track collapsed state for thinking sections (default to collapsed)
    const [collapsedThinking, setCollapsedThinking] = useState({});
    const normalizeToolArguments = useCallback((value) => {
        if (value === undefined || value === null)
            return "";
        if (typeof value === "string")
            return value;
        try {
            return JSON.stringify(value);
        }
        catch {
            return String(value);
        }
    }, []);
    const getToolArgumentsScore = useCallback((value) => {
        const normalized = normalizeToolArguments(value).trim();
        if (!normalized)
            return 0;
        // Treat placeholders as low-value argument payloads.
        if (normalized === "{}" || normalized === "[]" || normalized === "null") {
            return 1;
        }
        // Prefer richer JSON payloads over placeholders.
        try {
            const parsed = JSON.parse(normalized);
            if (Array.isArray(parsed)) {
                return parsed.length > 0 ? 1000 + normalized.length : 1;
            }
            if (parsed && typeof parsed === "object") {
                return Object.keys(parsed).length > 0 ? 1000 + normalized.length : 1;
            }
        }
        catch {
            // Non-JSON string payload; compare by length.
        }
        return normalized.length;
    }, [normalizeToolArguments]);
    // Helper to avoid unnecessary updates when tool calls are unchanged
    const areToolCallsEqual = (a, b) => {
        if (a === b)
            return true;
        if (!a || !b)
            return false;
        if (a.length !== b.length)
            return false;
        for (let i = 0; i < a.length; i++) {
            const ai = a[i];
            const bi = b[i];
            if (ai?.id !== bi?.id ||
                ai?.displayName !== bi?.displayName ||
                ai?.function?.name !== bi?.function?.name ||
                ai?.function?.result !== bi?.function?.result ||
                ai?.function?.error !== bi?.function?.error ||
                ai?.isCompleted !== bi?.isCompleted ||
                ai?.responseTimeMs !== bi?.responseTimeMs ||
                ai?.createdDate !== bi?.createdDate ||
                normalizeToolArguments(ai?.function?.arguments) !==
                    normalizeToolArguments(bi?.function?.arguments)) {
                return false;
            }
        }
        return true;
    };
    // Update preserved tool calls when messages change, but only if values actually change
    useEffect(() => {
        setPreservedToolCalls((prev) => {
            let changed = false;
            const next = { ...prev };
            for (const message of messages) {
                const hasToolCalls = !!(message.tool_calls && message.tool_calls.length > 0);
                if (!hasToolCalls)
                    continue;
                if (!areToolCallsEqual(next[message.id], message.tool_calls)) {
                    next[message.id] = message.tool_calls;
                    changed = true;
                }
            }
            return changed ? next : prev;
        });
    }, [messages]);
    // Filter out operations that are already undone
    const undoableOperations = editOperations.filter((op) => !op.undone);
    // Check if all operations have been undone (for persistent "undone" state after reload)
    const allOperationsUndone = editOperations.length > 0 && undoableOperations.length === 0;
    // Show undo button if there are any operations that aren't undone
    const canUndo = undoableOperations.length > 0;
    // Calculate if we're currently streaming (for loading indicator)
    const isStreaming = !finished && messages.length > 0;
    // Memoize filtered messages and their parsed content segments to avoid hooks inside loops
    const nonToolAssistantMessages = useMemo(() => messages.filter((x) => x.role !== "tool" && x.role !== "user"), [messages]);
    // Create a flat structure of content items: text, tool calls, text, tool calls, etc.
    const flatItems = useMemo(() => {
        const items = [];
        // preservedToolCalls only bridges transient gaps while the run is still
        // streaming. Once finished, the merged transcript is authoritative —
        // falling back here would resurrect stale stream-time copies (whose ids
        // may differ from the persisted ones) next to the merged-in calls and
        // render the same batch twice.
        const getEffectiveToolCalls = (message) => {
            if (message.tool_calls && message.tool_calls.length > 0) {
                return message.tool_calls;
            }
            if (!finished) {
                return preservedToolCalls[message.id] || [];
            }
            return [];
        };
        // Collect all unique tool call IDs and their latest versions across all messages in this group
        const latestToolCalls = new Map();
        nonToolAssistantMessages.forEach((message) => {
            const tcs = getEffectiveToolCalls(message);
            tcs.forEach((tc) => {
                if (tc.id) {
                    // If we already have this tool call, only update if the new one is more "complete"
                    const existing = latestToolCalls.get(tc.id);
                    const newArgScore = getToolArgumentsScore(tc.function?.arguments);
                    const existingArgScore = getToolArgumentsScore(existing?.function?.arguments);
                    const isNewer = !existing ||
                        (!existing.function?.result && tc.function?.result) ||
                        (!existing.function?.error && tc.function?.error) ||
                        (!existing.requiresApproval &&
                            tc.requiresApproval) ||
                        newArgScore > existingArgScore;
                    if (isNewer) {
                        latestToolCalls.set(tc.id, tc);
                    }
                }
            });
        });
        const emittedToolCallIds = new Set();
        nonToolAssistantMessages.forEach((message, msgIndex) => {
            const isLastMessage = msgIndex === nonToolAssistantMessages.length - 1;
            // Add reasoning segment if present
            if (message.reasoning) {
                items.push({
                    type: "segment",
                    segment: { kind: "reasoning", text: message.reasoning },
                    messageId: message.id,
                    message,
                    isLastMessage,
                });
            }
            const contentString = (message.content ||
                message.formattedContent ||
                "");
            const segments = parseContentSegments(contentString, isStreaming);
            // Add content segments
            segments.forEach((segment) => {
                items.push({
                    type: "segment",
                    segment,
                    messageId: message.id,
                    message,
                    isLastMessage,
                });
            });
            // Add tool calls if present, but only if they haven't been emitted yet
            const tcs = getEffectiveToolCalls(message);
            if (tcs.length > 0) {
                const uniqueToolCallsInThisMessage = [];
                tcs.forEach((tc) => {
                    if (!tc.id || !emittedToolCallIds.has(tc.id)) {
                        if (tc.id) {
                            emittedToolCallIds.add(tc.id);
                            // Use the latest version we found earlier
                            const latestToolCall = latestToolCalls.get(tc.id) || tc;
                            uniqueToolCallsInThisMessage.push(latestToolCall.messageId
                                ? latestToolCall
                                : {
                                    ...latestToolCall,
                                    messageId: message.id,
                                });
                        }
                        else {
                            uniqueToolCallsInThisMessage.push(tc.messageId
                                ? tc
                                : {
                                    ...tc,
                                    messageId: message.id,
                                });
                        }
                    }
                });
                if (uniqueToolCallsInThisMessage.length > 0) {
                    items.push({
                        type: "toolcalls",
                        toolCalls: uniqueToolCallsInThisMessage,
                        messageId: message.id,
                        message,
                        isLastMessage,
                    });
                    // Some completed tool calls get a visible recap right after their
                    // raw tool-call batch so useful UI is not hidden in collapsed output.
                    uniqueToolCallsInThisMessage.forEach((toolCall) => {
                        if (isQuestionnaireRecapToolCall(toolCall)) {
                            items.push({
                                type: "questionnaire-recap",
                                toolCall,
                                messageId: message.id,
                                message,
                                isLastMessage,
                            });
                        }
                        const screenshotPreview = getScreenshotToolResultPreview(toolCall);
                        if (screenshotPreview) {
                            items.push({
                                type: "screenshot-preview",
                                toolCall,
                                preview: screenshotPreview,
                                messageId: message.id,
                                message,
                                isLastMessage,
                            });
                        }
                    });
                }
            }
        });
        const mergedItems = [];
        items.forEach((item) => {
            const previousItem = mergedItems[mergedItems.length - 1];
            if (item.type === "toolcalls" && previousItem?.type === "toolcalls") {
                mergedItems[mergedItems.length - 1] = {
                    ...previousItem,
                    toolCalls: [...previousItem.toolCalls, ...item.toolCalls],
                    messageId: `${previousItem.messageId}__${item.messageId}`,
                    message: item.message,
                    isLastMessage: item.isLastMessage,
                };
                return;
            }
            mergedItems.push(item);
        });
        return mergedItems;
    }, [
        nonToolAssistantMessages,
        isStreaming,
        finished,
        preservedToolCalls,
        getToolArgumentsScore,
    ]);
    const displayItems = useMemo(() => groupActivityItems(flatItems), [flatItems]);
    // Pending approvals that belong to THIS assistant group, enriched with the
    // matching tool call's allowableScope. allPendingApprovals is session-wide
    // and handed to every group; intersecting against the same deduped
    // flatItems tool calls that ToolCallDisplay renders keeps the card off
    // historical groups and immune to stream-time id remaps.
    const groupApprovalItems = useMemo(() => {
        if (!allPendingApprovals || allPendingApprovals.length === 0)
            return [];
        const groupToolCalls = [];
        flatItems.forEach((item) => {
            if (item.type === "toolcalls") {
                groupToolCalls.push(...item.toolCalls);
            }
        });
        const pending = selectGroupPendingApprovals(groupToolCalls, allPendingApprovals);
        if (pending.length === 0)
            return [];
        const scopeByToolCallId = new Map();
        groupToolCalls.forEach((toolCall) => {
            if (toolCall.id && toolCall.requiresApproval?.allowableScope) {
                scopeByToolCallId.set(toolCall.id, toolCall.requiresApproval.allowableScope);
            }
        });
        return pending.map((item) => ({
            ...item,
            allowableScope: scopeByToolCallId.get(item.toolCallId),
        }));
    }, [flatItems, allPendingApprovals]);
    // Extract copyable text content from all text and reasoning segments
    const copyableText = useMemo(() => {
        const textParts = [];
        flatItems.forEach((item) => {
            if (item.type === "segment") {
                const segment = item.segment;
                if (segment.kind === "text") {
                    // Extract plain text from HTML if needed, or use raw text
                    const text = segment.text.trim();
                    if (text) {
                        textParts.push(text);
                    }
                }
                else if (segment.kind === "reasoning") {
                    const text = segment.text.trim();
                    if (text) {
                        textParts.push(text);
                    }
                }
            }
        });
        return textParts.join("\n\n");
    }, [flatItems]);
    const renderFlatItem = (item, itemIndex, options) => {
        const prevItem = itemIndex > 0 ? flatItems[itemIndex - 1] : null;
        const isLastItem = itemIndex === flatItems.length - 1;
        const shouldAddSpacing = prevItem?.type === "toolcalls" && item.type === "segment";
        if (item.type === "questionnaire-recap") {
            return (_jsx(QuestionnaireRecap, { toolCall: item.toolCall }, `item-${itemIndex}`));
        }
        if (item.type === "screenshot-preview") {
            return (_jsx("div", { className: "my-2", "data-testid": "screenshot-tool-result-preview", children: _jsx(InlineToolResultPreview, { preview: item.preview, testId: "screenshot-tool-result-image", className: "border-border-default hover:border-border-default block overflow-hidden rounded border bg-white transition-colors", imageClassName: "bg-neutral-grey-5 max-h-80 w-auto max-w-full object-contain" }) }, `item-${itemIndex}`));
        }
        if (item.type === "segment") {
            const segment = item.segment;
            const message = item.message;
            if (segment.kind === "reasoning") {
                const thinkingKey = `thinking-${message.id}-${itemIndex}`;
                // Only show the "Thinking" spinner while we're still in the pre-output phase.
                // Once any actual response content starts appearing, thinking has effectively finished,
                // even if the overall response is still streaming.
                const contentString = (message.content ||
                    message.formattedContent ||
                    "");
                const hasToolCalls = (message.tool_calls && message.tool_calls.length > 0) ||
                    (preservedToolCalls[message.id] &&
                        preservedToolCalls[message.id].length > 0);
                const hasStartedOutput = contentString.trim().length > 0 || hasToolCalls;
                // Check if this reasoning block is still actively streaming
                const isReasoningStreaming = !!(isStreaming &&
                    item.isLastMessage &&
                    !hasStartedOutput &&
                    !allPendingApprovals?.length);
                // Thinking details stay collapsed by default, including while streaming.
                // An explicit user toggle remains sticky for the lifetime of this message.
                const isCollapsed = collapsedThinking[thinkingKey] !== undefined
                    ? collapsedThinking[thinkingKey]
                    : true;
                return (_jsx(ThinkingBlock, { text: segment.text, isStreaming: isReasoningStreaming, isCollapsed: isCollapsed, thinkingKey: thinkingKey, onToggleCollapse: () => {
                        setCollapsedThinking((prev) => ({
                            ...prev,
                            [thinkingKey]: !isCollapsed,
                        }));
                    } }, `item-${itemIndex}`));
            }
            if (segment.kind === "text") {
                const trimmedText = segment.text.trim();
                if (!trimmedText)
                    return null;
                // Check for command links in the text
                const { text, links } = extractCommandLinks(trimmedText);
                if (links.length > 0) {
                    // Split text around command links and render buttons
                    const parts = [];
                    let lastIndex = 0;
                    links.forEach((link, linkIdx) => {
                        // Add text before the link
                        if (link.index > lastIndex) {
                            const textBefore = text.substring(lastIndex, link.index);
                            const html = simpleFormatToHtml(textBefore);
                            if (html) {
                                parts.push(_jsx(FormattedAssistantHtml, { html: html }, `text-${itemIndex}-${linkIdx}`));
                            }
                        }
                        // Add the button
                        // Calculate the actual text that will be submitted (same logic as in AgentTerminal.tsx)
                        // For command links: prompt=command, value=command, so submitted text is link.command
                        const submittedText = (link.command || link.label || "").trim();
                        parts.push(_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(Button, { size: "sm", variant: "outline", className: "mx-1 inline-flex", onClick: () => onQuickAction?.({
                                            label: link.label,
                                            value: link.command,
                                            prompt: link.command,
                                        }, message), children: link.label }) }), submittedText && (_jsx(TooltipContent, { children: _jsx("p", { className: "max-w-xs break-words", children: submittedText }) }))] }, `btn-${itemIndex}-${linkIdx}`));
                        // Find the end of this match in the original text
                        const matchEnd = link.index + `[${link.label}](command:${link.command})`.length;
                        lastIndex = matchEnd;
                    });
                    // Add any remaining text after the last link
                    if (lastIndex < text.length) {
                        const textAfter = text.substring(lastIndex);
                        const html = simpleFormatToHtml(textAfter);
                        if (html) {
                            parts.push(_jsx(FormattedAssistantHtml, { html: html }, `text-${itemIndex}-end`));
                        }
                    }
                    return (_jsxs("div", { className: `${assistantMessageTextClassName} ${shouldAddSpacing ? "mt-2" : ""}`, children: [parts, isStreaming && isLastItem && (_jsx("span", { className: "bg-neutral-grey-50 ml-0.5 inline-block h-3 w-0.5 animate-pulse" }))] }, `item-${itemIndex}`));
                }
                const html = simpleFormatToHtml(trimmedText);
                if (!html)
                    return null;
                return (_jsxs("div", { className: `${assistantMessageTextClassName} ${shouldAddSpacing ? "mt-2" : ""}`, children: [_jsx(FormattedAssistantHtml, { html: html }), isStreaming && isLastItem && (_jsx("span", { className: "bg-neutral-grey-50 ml-0.5 inline-block h-3 w-0.5 animate-pulse" }))] }, `item-${itemIndex}`));
            }
            if (segment.kind === "todo") {
                const todo = segment;
                return (_jsxs("div", { className: `my-1 ${shouldAddSpacing ? "mt-2" : ""}`, children: [todo.title && (_jsx("div", { className: "text-neutral-grey-100 mb-1 text-xs font-medium", children: todo.title })), _jsx("div", { className: "flex flex-col gap-1", children: todo.items.map((todoItem, iIdx) => (_jsxs("label", { className: "text-neutral-grey-100 flex items-start gap-2 text-xs", children: [_jsx(Checkbox, { checked: todoItem.status === "completed", onCheckedChange: () => { }, "aria-readonly": true, className: "mt-0.5" }), _jsxs("div", { children: [_jsx("div", { className: todoItem.status === "completed" ||
                                                    todoItem.status === "cancelled"
                                                    ? "line-through opacity-70"
                                                    : todoItem.status === "in_progress"
                                                        ? "text-feedback-blue font-medium"
                                                        : "", children: todoItem.title }), todoItem.instructions && (_jsx("div", { className: "text-neutral-grey-100 text-xs", children: todoItem.instructions }))] })] }, (todoItem.id || todoItem.title || "todo") + "-" + iIdx))) })] }, `item-${itemIndex}`));
            }
            if (segment.kind === "media") {
                const media = segment;
                return (_jsx("div", { className: `my-2 flex flex-col gap-3 ${shouldAddSpacing ? "mt-2" : ""}`, children: media.items.map((mediaItem, mIdx) => (_jsx(MediaImage, { itemId: mediaItem.itemId, label: mediaItem.label }, mediaItem.itemId || `media-${itemIndex}-${mIdx}`))) }, `item-${itemIndex}`));
            }
            if (segment.kind === "json") {
                return (_jsx(JsonBlockDisplay, { data: segment.data, raw: segment.raw, defaultCollapsed: defaultCollapseJson, className: shouldAddSpacing ? "mt-2" : "" }, `item-${itemIndex}`));
            }
            // segment.kind === "actions"
            const headline = segment.headline?.trim() || "Recommended actions:";
            return (_jsx(QuickActionsCard, { headline: headline, actions: segment.actions, message: message, className: shouldAddSpacing ? "mt-2" : undefined, onQuickAction: onQuickAction }, `item-${itemIndex}`));
        }
        // item.type === "toolcalls"
        return (_jsx("div", { className: "my-3", children: _jsx(ToolCallDisplay, { toolCalls: item.toolCalls, finished: finished, isLive: options?.isLive ?? false, openPopovers: openPopovers, setOpenPopovers: setOpenPopovers, messageId: item.messageId }) }, `item-${itemIndex}`));
    };
    return (_jsx("div", { className: `px-4 pb-3 ${followsUserMessage ? "pt-5" : "pt-3"}`, "data-testid": "agent-message", children: _jsxs("div", { className: "group/message min-w-0 flex-1 select-text", children: [error && (_jsx("div", { className: "border-feedback-red bg-feedback-red-light mb-2 rounded-lg border-l-4 p-2.5", "data-testid": "agent-error", children: _jsxs("div", { className: "flex items-start", children: [_jsx("div", { className: "flex-shrink-0", children: _jsx(X, { className: "text-feedback-red h-5 w-5", strokeWidth: 1 }) }), _jsxs("div", { className: "ml-3", children: [_jsx("p", { className: "text-feedback-red text-sm font-medium", children: "Agent Error" }), _jsx("p", { className: "text-feedback-red mt-1 text-sm", children: error })] })] }) })), displayItems.map((displayItem, displayItemIndex) => {
                    if (displayItem.type === "activity-group") {
                        // Live while nothing visible renders after the group — a trailing
                        // still-empty streaming message must not steal liveness from it.
                        const isLive = !finished && displayItemIndex === displayItems.length - 1;
                        return (_jsx(ActivityBlockGroup, { entries: displayItem.entries, isLive: isLive, renderEntry: (entry) => renderFlatItem(entry.item, entry.index) }, displayItem.key));
                    }
                    return renderFlatItem(displayItem.entry.item, displayItem.entry.index, 
                    // Same liveness rule as the activity group: animated while nothing
                    // visible renders after this item and the response is running.
                    {
                        isLive: !finished && displayItemIndex === displayItems.length - 1,
                    });
                }), finished && copyableText && groupApprovalItems.length === 0 && (_jsx("div", { className: "mt-1 flex h-5 items-center justify-end", children: _jsx("div", { className: "pointer-events-none opacity-0 transition-opacity group-focus-within/message:pointer-events-auto group-focus-within/message:opacity-100 group-hover/message:pointer-events-auto group-hover/message:opacity-100", children: _jsx(CopyButton, { textToCopy: copyableText, iconOnly: false, "data-testid": "agent-message-copy-button", className: "text-neutral-grey-50 hover:text-neutral-grey-100 flex cursor-pointer items-center gap-1 px-1.5 py-1 text-xs" }) }) })), finished && editOperations.length > 0 && (_jsx("div", { className: "mt-3 flex items-center justify-end gap-2", children: finished && editOperations.length > 0 && (_jsxs(_Fragment, { children: [canUndo && !changesRejected && !confirmingUndo && (_jsxs("button", { type: "button", title: `Undo ${editOperations.length} change${editOperations.length !== 1 ? "s" : ""}`, className: "tour-ai-response-message-changes text-neutral-grey-50 hover:text-neutral-grey-50 flex cursor-pointer items-center gap-1 text-xs", onClick: (e) => {
                                    e.stopPropagation();
                                    setConfirmingUndo(true);
                                }, children: [_jsx(Undo2, { strokeWidth: 1, size: 12 }), " Undo"] })), canUndo && !changesRejected && confirmingUndo && (_jsxs("div", { className: "flex items-center gap-2 text-xs", children: [_jsxs("span", { className: "text-neutral-grey-50", children: ["Undo ", editOperations.length, " change", editOperations.length !== 1 ? "s" : "", "?"] }), _jsx("button", { type: "button", className: "text-feedback-red hover:text-feedback-red cursor-pointer", onClick: async (e) => {
                                            e.stopPropagation();
                                            const operationIds = undoableOperations.map((op) => op.id);
                                            if (!editContext?.operations?.undoOperations) {
                                                setConfirmingUndo(false);
                                                return;
                                            }
                                            if (operationIds.length === 0) {
                                                setConfirmingUndo(false);
                                                return;
                                            }
                                            try {
                                                const success = await editContext.operations.undoOperations(operationIds);
                                                if (success) {
                                                    setChangesRejected(true);
                                                }
                                            }
                                            catch (error) {
                                                // Undo operation failed
                                            }
                                            setConfirmingUndo(false);
                                        }, children: "Yes" }), _jsx("button", { type: "button", className: "text-neutral-grey-50 hover:text-neutral-grey-50 cursor-pointer", onClick: (e) => {
                                            e.stopPropagation();
                                            setConfirmingUndo(false);
                                        }, children: "No" })] })), (changesRejected || allOperationsUndone) && (_jsxs("span", { className: "text-neutral-grey-50 inline-flex items-center gap-1 text-xs", children: [_jsx(Check, { strokeWidth: 1, size: 12 }), " Undone"] }))] })) })), groupApprovalItems.length > 0 && (_jsx(ApprovalCard, { agentId: agentId, items: groupApprovalItems, readOnly: readOnly, onSwitchToAutonomous: onSwitchToAutonomous }))] }) }));
}
//# sourceMappingURL=AiResponseMessage.js.map