@open-mercato/ui 0.6.6-develop.6184.1.b7e55f8d61 → 0.6.6-develop.6201.1.8ceb502c4b
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +1 -1
- package/dist/ai/AiChat.js +1 -28
- package/dist/ai/AiChat.js.map +2 -2
- package/dist/ai/AiChatSessions.js +35 -32
- package/dist/ai/AiChatSessions.js.map +2 -2
- package/dist/ai/modelPickerStorage.js +29 -0
- package/dist/ai/modelPickerStorage.js.map +7 -0
- package/dist/ai/useAiChat.js.map +2 -2
- package/dist/backend/AppShell.js +24 -19
- package/dist/backend/AppShell.js.map +2 -2
- package/dist/backend/DataTable.js +26 -23
- package/dist/backend/DataTable.js.map +2 -2
- package/dist/backend/notifications/useNotificationActions.js +5 -5
- package/dist/backend/notifications/useNotificationActions.js.map +2 -2
- package/dist/backend/progress/ProgressTopBar.js.map +2 -2
- package/package.json +3 -3
- package/src/ai/AiChat.tsx +1 -37
- package/src/ai/AiChatSessions.tsx +54 -48
- package/src/ai/__tests__/AiChat.modelPickerStorage.test.ts +40 -0
- package/src/ai/__tests__/AiChatSessions.storage.test.ts +42 -0
- package/src/ai/__tests__/AiChatSessions.test.tsx +10 -6
- package/src/ai/modelPickerStorage.ts +40 -0
- package/src/ai/useAiChat.ts +7 -0
- package/src/backend/AppShell.tsx +41 -17
- package/src/backend/DataTable.tsx +34 -29
- package/src/backend/__tests__/DataTable.perspectiveStorage.test.ts +44 -0
- package/src/backend/notifications/__tests__/useNotificationActions.failedWrites.test.tsx +119 -0
- package/src/backend/notifications/useNotificationActions.ts +5 -5
- package/src/backend/progress/ProgressTopBar.tsx +4 -0
package/.turbo/turbo-build.log
CHANGED
package/dist/ai/AiChat.js
CHANGED
|
@@ -67,36 +67,9 @@ import { useAiChatUpload } from "./useAiChatUpload.js";
|
|
|
67
67
|
import { useAiShortcuts } from "./useAiShortcuts.js";
|
|
68
68
|
import { LoopTracePanel } from "./LoopTracePanel.js";
|
|
69
69
|
import { AiIcon } from "./AiIcon.js";
|
|
70
|
+
import { readModelPickerValue, writeModelPickerValue } from "./modelPickerStorage.js";
|
|
70
71
|
const PREVIEW_DATA_URL_MAX_BYTES = 2 * 1024 * 1024;
|
|
71
72
|
const COMPACT_FOOTER_MAX_WIDTH = 640;
|
|
72
|
-
const MODEL_PICKER_STORAGE_PREFIX = "om-ai-model-picker:";
|
|
73
|
-
function readModelPickerValue(agentId) {
|
|
74
|
-
if (typeof window === "undefined") return null;
|
|
75
|
-
try {
|
|
76
|
-
const raw = window.localStorage.getItem(`${MODEL_PICKER_STORAGE_PREFIX}${agentId}`);
|
|
77
|
-
if (!raw) return null;
|
|
78
|
-
const parsed = JSON.parse(raw);
|
|
79
|
-
if (parsed && typeof parsed === "object" && typeof parsed.providerId === "string" && typeof parsed.modelId === "string") {
|
|
80
|
-
const value = parsed;
|
|
81
|
-
return { providerId: value.providerId, modelId: value.modelId };
|
|
82
|
-
}
|
|
83
|
-
return null;
|
|
84
|
-
} catch {
|
|
85
|
-
return null;
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
function writeModelPickerValue(agentId, value) {
|
|
89
|
-
if (typeof window === "undefined") return;
|
|
90
|
-
try {
|
|
91
|
-
const key = `${MODEL_PICKER_STORAGE_PREFIX}${agentId}`;
|
|
92
|
-
if (value === null) {
|
|
93
|
-
window.localStorage.removeItem(key);
|
|
94
|
-
} else {
|
|
95
|
-
window.localStorage.setItem(key, JSON.stringify({ providerId: value.providerId, modelId: value.modelId }));
|
|
96
|
-
}
|
|
97
|
-
} catch {
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
73
|
function useAgentModels(agent) {
|
|
101
74
|
const [providers, setProviders] = React.useState([]);
|
|
102
75
|
const [allowRuntimeOverride, setAllowRuntimeOverride] = React.useState(false);
|
package/dist/ai/AiChat.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/ai/AiChat.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport {\n Activity,\n AlertTriangle,\n BadgeQuestionMark,\n Bot,\n ChartNoAxesCombined,\n Check,\n ChevronDown,\n ChevronRight,\n Circle,\n CircleAlert,\n CircleCheck,\n CircleDashed,\n Copy,\n Database,\n Download,\n FileQuestion,\n GitBranch,\n Handshake,\n Lightbulb,\n ListChecks,\n Loader2,\n Maximize2,\n Paperclip,\n Plus,\n Search,\n Send,\n Square,\n TrendingDown,\n User,\n Wrench,\n X,\n} from 'lucide-react'\nimport ReactMarkdown from 'react-markdown'\nimport remarkGfm from 'remark-gfm'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { Alert, AlertDescription, AlertTitle } from '../primitives/alert'\nimport { Button } from '../primitives/button'\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogHeader,\n DialogTitle,\n} from '../primitives/dialog'\nimport { IconButton } from '../primitives/icon-button'\nimport { Label } from '../primitives/label'\nimport { Textarea } from '../primitives/textarea'\nimport { apiCall } from '../backend/utils/apiCall'\nimport { parseAiContentSegments } from './AiMessageContent'\nimport {\n ModelPicker,\n type ModelPickerProvider,\n type ModelPickerValue,\n} from './ModelPicker'\nimport { RecordCard } from './records/RecordCard'\nimport {\n defaultAiUiPartRegistry,\n isReservedAiUiPartId,\n type AiUiPartComponentId,\n type AiUiPartRegistry,\n} from './ui-part-registry'\nimport {\n useAiChat,\n type AiAgentTaskSnapshot,\n type AiChatMessage,\n type AiChatMessageFile,\n type AiChatMessageUiPart,\n type AiChatToolCallSnapshot,\n} from './useAiChat'\nimport { useAiChatUpload } from './useAiChatUpload'\nimport { useAiShortcuts } from './useAiShortcuts'\nimport { LoopTracePanel, type LoopTracePanelTrace } from './LoopTracePanel'\nimport { AiIcon } from './AiIcon'\n\n// Cap inline previews so we do not blow past localStorage quota (~5MB on most\n// browsers). Images larger than this still upload + send to the LLM as inline\n// base64 server-side; only the in-chat preview is dropped on reload.\nconst PREVIEW_DATA_URL_MAX_BYTES = 2 * 1024 * 1024\nconst COMPACT_FOOTER_MAX_WIDTH = 640\n\nconst MODEL_PICKER_STORAGE_PREFIX = 'om-ai-model-picker:'\n\nfunction readModelPickerValue(agentId: string): ModelPickerValue | null {\n if (typeof window === 'undefined') return null\n try {\n const raw = window.localStorage.getItem(`${MODEL_PICKER_STORAGE_PREFIX}${agentId}`)\n if (!raw) return null\n const parsed = JSON.parse(raw) as unknown\n if (\n parsed &&\n typeof parsed === 'object' &&\n typeof (parsed as Record<string, unknown>).providerId === 'string' &&\n typeof (parsed as Record<string, unknown>).modelId === 'string'\n ) {\n const value = parsed as ModelPickerValue\n return { providerId: value.providerId, modelId: value.modelId }\n }\n return null\n } catch {\n return null\n }\n}\n\nfunction writeModelPickerValue(agentId: string, value: ModelPickerValue | null): void {\n if (typeof window === 'undefined') return\n try {\n const key = `${MODEL_PICKER_STORAGE_PREFIX}${agentId}`\n if (value === null) {\n window.localStorage.removeItem(key)\n } else {\n window.localStorage.setItem(key, JSON.stringify({ providerId: value.providerId, modelId: value.modelId }))\n }\n } catch {\n // Quota exceeded / privacy mode \u2014 silently ignore.\n }\n}\n\ninterface ModelsApiResponse {\n agentId: string\n allowRuntimeOverride: boolean\n allowRuntimeModelOverride?: boolean\n defaultProviderId: string | null\n defaultModelId: string | null\n defaultProviderName?: string | null\n defaultModelName?: string | null\n providers: ModelPickerProvider[]\n}\n\nfunction useAgentModels(agent: string): {\n providers: ModelPickerProvider[]\n allowRuntimeOverride: boolean\n allowRuntimeModelOverride: boolean\n defaultLabel: string | null\n loaded: boolean\n} {\n const [providers, setProviders] = React.useState<ModelPickerProvider[]>([])\n const [allowRuntimeOverride, setAllowRuntimeOverride] = React.useState(false)\n const [defaultLabel, setDefaultLabel] = React.useState<string | null>(null)\n const [loaded, setLoaded] = React.useState(false)\n\n React.useEffect(() => {\n const modelsUrl = `/api/ai_assistant/ai/agents/${encodeURIComponent(agent)}/models`\n setLoaded(false)\n setDefaultLabel(null)\n void apiCall<ModelsApiResponse>(modelsUrl).then((result) => {\n if (!result.ok || !result.result) {\n setLoaded(true)\n return\n }\n const effectiveAllowRuntimeOverride =\n result.result.allowRuntimeOverride ?? result.result.allowRuntimeModelOverride ?? false\n setAllowRuntimeOverride(effectiveAllowRuntimeOverride)\n setProviders(result.result.providers)\n setDefaultLabel(\n result.result.defaultProviderName && result.result.defaultModelName\n ? `${result.result.defaultProviderName} / ${result.result.defaultModelName}`\n : result.result.defaultProviderId && result.result.defaultModelId\n ? `${result.result.defaultProviderId} / ${result.result.defaultModelId}`\n : null,\n )\n setLoaded(true)\n })\n }, [agent])\n\n return {\n providers,\n allowRuntimeOverride,\n allowRuntimeModelOverride: allowRuntimeOverride,\n defaultLabel,\n loaded,\n }\n}\n\nfunction isModelPickerValueAvailable(\n value: ModelPickerValue,\n providers: ModelPickerProvider[],\n): boolean {\n return providers.some(\n (provider) =>\n provider.id === value.providerId &&\n provider.models.some((model) => model.id === value.modelId),\n )\n}\n\nasync function readFileAsDataUrl(file: File): Promise<string | undefined> {\n if (!file.type.startsWith('image/')) return undefined\n if (file.size > PREVIEW_DATA_URL_MAX_BYTES) return undefined\n return new Promise<string | undefined>((resolve) => {\n const reader = new FileReader()\n reader.onload = () =>\n resolve(typeof reader.result === 'string' ? reader.result : undefined)\n reader.onerror = () => resolve(undefined)\n try {\n reader.readAsDataURL(file)\n } catch {\n resolve(undefined)\n }\n })\n}\n\n/**\n * Optional resolved-tool snapshot the host can feed into the debug panel.\n * Step 4.6 wires this from the `GET /api/ai_assistant/ai/agents` response\n * (`tools[]`). Step 5.3+ will replace the manual wiring with a streamed\n * `debug` part once the dispatcher emits one.\n */\nexport interface AiChatDebugTool {\n name: string\n displayName?: string\n isMutation?: boolean\n requiredFeatures?: string[]\n}\n\n/**\n * Resolved prompt-section snapshot for the debug panel. Until Phase 3 Step\n * 5.3 lands structured `PromptTemplate.sections`, hosts synthesise this\n * from the agent's `systemPrompt` + additive overrides.\n */\nexport interface AiChatDebugPromptSection {\n id: string\n source?: 'default' | 'override' | 'placeholder'\n text?: string\n}\n\n/** Quick-action suggestion shown in the welcome state. */\nexport interface AiChatSuggestion {\n label: string\n prompt: string\n icon?: React.ReactNode\n}\n\n/** Context item displayed as a chip/pill in the chat header. */\nexport interface AiChatContextItem {\n label: string\n detail?: string\n}\n\nexport interface AiChatProps {\n agent: string\n apiPath?: string\n pageContext?: Record<string, unknown>\n attachmentIds?: string[]\n initialMessages?: Array<{ role: 'user' | 'assistant'; content: string }>\n debug?: boolean\n className?: string\n placeholder?: string\n onMutationRequested?: (pendingActionId: string) => void\n onError?: (err: { code?: string; message: string }) => void\n /**\n * Optional stable conversation id. Forwarded verbatim to\n * `POST /api/ai_assistant/ai/chat` request bodies and reused across\n * turns so the Step 5.6 `prepareMutation` idempotency hash stays\n * stable across retries within the same chat. When omitted, the hook\n * mints a fresh random id once on mount \u2014 remounting the component\n * resets the conversation.\n */\n conversationId?: string\n /**\n * Optional UI-part registry. Defaults to the module-global\n * {@link defaultAiUiPartRegistry}. Pass a scoped registry from\n * {@link createAiUiPartRegistry} when embedding multiple `<AiChat>`\n * instances that should not share registrations (playground, tests).\n */\n registry?: AiUiPartRegistry\n /**\n * Optional list of server-emitted UI parts to render inside the chat\n * transcript. The registry resolves each part via `componentId`. Phase 3\n * will populate this from the streamed dispatcher response; Phase 2 WS-A\n * leaves the wiring exposed so hosts can preview the registry path.\n */\n uiParts?: Array<{\n componentId: AiUiPartComponentId\n payload?: unknown\n pendingActionId?: string\n }>\n /**\n * Optional resolved-tool map for the debug panel. Ignored when\n * `debug` is falsy.\n */\n debugTools?: AiChatDebugTool[]\n /**\n * Optional resolved prompt sections for the debug panel. Ignored when\n * `debug` is falsy.\n */\n debugPromptSections?: AiChatDebugPromptSection[]\n /**\n * Optional loop trace from the last completed turn. When present and\n * `debug` is truthy, renders a `LoopTracePanel` inside the debug panel.\n * Populated by the host from the dispatcher SSE `loop-finish` event\n * (wired in l4.4). Ignored when `debug` is falsy.\n */\n loopTrace?: LoopTracePanelTrace\n /** Suggested prompts shown in the empty / welcome state. */\n suggestions?: AiChatSuggestion[]\n /** Context items shown as pills above the transcript (e.g. selected products). */\n contextItems?: AiChatContextItem[]\n /** Welcome heading shown when there are no messages yet. */\n welcomeTitle?: string\n /** Welcome description shown below the heading. */\n welcomeDescription?: string\n /** Initial compact composer state used before the footer has been measured. */\n defaultCompactFooter?: boolean\n /** Optional extra action nodes rendered in the conversation header bar. */\n headerActions?: React.ReactNode\n /** Called once on mount (and on reset) with the effective conversation id. */\n onConversationIdChange?: (conversationId: string) => void\n /**\n * Called when the server returns 404 for the active `conversationId`,\n * e.g. when the id was carried over from a previous tenant/org scope\n * and no longer exists. The host (typically the AI dock) should remove\n * the corresponding session entry so the stale tab disappears instead\n * of staying empty.\n */\n onConversationNotFound?: () => void\n}\n\ninterface ServerEmittedUiPartRef {\n componentId: AiUiPartComponentId\n payload?: unknown\n pendingActionId?: string\n}\n\nfunction mapErrorCodeToVariant(\n code: string | undefined,\n): 'destructive' | 'warning' {\n if (!code) return 'destructive'\n // Policy denies that describe a filtered tool or attachment surface a\n // warning alert; caller can still continue. Hard denials (agent_unknown,\n // agent_features_denied, unauthenticated, execution_mode_not_supported,\n // mutation_blocked_by_*, validation_error) surface destructive alerts.\n const warningCodes = new Set<string>([\n 'tool_not_whitelisted',\n 'tool_features_denied',\n 'attachment_type_not_accepted',\n ])\n return warningCodes.has(code) ? 'warning' : 'destructive'\n}\n\nconst MARKDOWN_TYPOGRAPHY_CLASS = cn(\n '[&_p]:my-1 [&_p:first-child]:mt-0 [&_p:last-child]:mb-0',\n '[&_ul]:my-2 [&_ol]:my-2 [&_ul]:ml-4 [&_ol]:ml-4 [&_ul]:list-disc [&_ol]:list-decimal',\n '[&_li]:my-0.5',\n '[&_h1]:mt-3 [&_h1]:mb-2 [&_h1]:text-base [&_h1]:font-semibold',\n '[&_h2]:mt-3 [&_h2]:mb-2 [&_h2]:text-sm [&_h2]:font-semibold',\n '[&_h3]:mt-2 [&_h3]:mb-1 [&_h3]:text-sm [&_h3]:font-semibold',\n '[&_a]:text-primary [&_a]:underline [&_a]:underline-offset-2',\n '[&_code]:rounded [&_code]:bg-muted [&_code]:px-1 [&_code]:py-0.5 [&_code]:font-mono [&_code]:text-xs',\n '[&_pre]:my-2 [&_pre]:overflow-x-auto [&_pre]:rounded-md [&_pre]:border [&_pre]:border-border [&_pre]:bg-muted [&_pre]:p-3',\n '[&_pre_code]:bg-transparent [&_pre_code]:p-0',\n '[&_blockquote]:my-2 [&_blockquote]:border-l-2 [&_blockquote]:border-border [&_blockquote]:pl-3 [&_blockquote]:text-muted-foreground',\n '[&_table]:my-2 [&_table]:w-full [&_table]:border-collapse [&_table]:text-xs',\n '[&_th]:border [&_th]:border-border [&_th]:px-2 [&_th]:py-1 [&_th]:text-left [&_th]:font-medium',\n '[&_td]:border [&_td]:border-border [&_td]:px-2 [&_td]:py-1',\n)\n\nconst MARKDOWN_COMPONENTS = {\n a: ({ node, ...props }: React.AnchorHTMLAttributes<HTMLAnchorElement> & { node?: unknown }) => (\n <a {...props} target=\"_blank\" rel=\"noreferrer\" />\n ),\n}\n\nfunction MarkdownChunk({ text }: { text: string }) {\n if (!text.trim()) return null\n return (\n <div className={cn('text-sm', MARKDOWN_TYPOGRAPHY_CLASS)}>\n <ReactMarkdown remarkPlugins={[remarkGfm]} components={MARKDOWN_COMPONENTS}>\n {text}\n </ReactMarkdown>\n </div>\n )\n}\n\nfunction MessageContent({ content, isAssistant }: { content: string; isAssistant: boolean }) {\n if (!isAssistant) {\n return <div className=\"whitespace-pre-wrap text-sm\">{content}</div>\n }\n if (!content) {\n return null\n }\n const segments = parseAiContentSegments(content)\n if (segments.length === 0) {\n return null\n }\n return (\n <div className=\"space-y-1\" data-ai-message-content=\"\">\n {segments.map((segment, index) => {\n if (segment.kind === 'record-card') {\n return <RecordCard key={`card-${index}`} data={segment.payload} />\n }\n if (segment.kind === 'invalid-card') {\n return (\n <pre\n key={`raw-${index}`}\n className=\"my-2 max-h-60 overflow-auto rounded-md border border-dashed border-border bg-muted p-2 text-xs\"\n data-ai-record-card-invalid={segment.info}\n >\n {segment.raw}\n </pre>\n )\n }\n return <MarkdownChunk key={`md-${index}`} text={segment.text} />\n })}\n </div>\n )\n}\n\nfunction safeStringify(value: unknown): string {\n if (value === null || value === undefined) return ''\n if (typeof value === 'string') return value\n try {\n return JSON.stringify(value, null, 2)\n } catch {\n return String(value)\n }\n}\n\nfunction titleWords(value: string): string {\n return value.replace(/\\b[a-z]/g, (char) => char.toUpperCase())\n}\n\nfunction formatToolCaption(call: AiChatToolCallSnapshot): string {\n const rawCaption = call.caption?.trim()\n if (!rawCaption) return call.toolName\n const caption = titleWords(rawCaption.replace(/\\s*\u00B7\\s*/g, ' - '))\n return `${caption} (${call.toolName})`\n}\n\n/**\n * Visible agent task plan rendered above raw tool-call rows. Phase 1\n * implementation of spec `.ai/specs/implemented/2026-05-13-ai-chat-visible-task-plan.md`:\n * compact, fixed-height rows with icon + label + status badge so the\n * panel does not jump while streaming. Source of truth is the\n * client-local `taskPlan` array on the assistant message.\n */\nfunction AiChatTaskPlan({ tasks }: { tasks: AiAgentTaskSnapshot[] }) {\n const t = useT()\n if (!tasks || tasks.length === 0) return null\n return (\n <div\n className=\"rounded-md border border-border bg-muted/30\"\n data-ai-chat-task-plan=\"\"\n data-ai-chat-task-plan-count={tasks.length}\n >\n <div className=\"flex items-center gap-2 border-b border-border/60 px-2 py-1.5 text-[11px] font-medium uppercase tracking-wide text-muted-foreground\">\n <ListChecks className=\"size-3.5 text-muted-foreground\" aria-hidden />\n <span>{t('ai_assistant.chat.taskPlanTitle', 'Plan')}</span>\n <span className=\"ml-auto font-mono\">{tasks.length}</span>\n </div>\n <ul className=\"divide-y divide-border/60\" data-ai-chat-task-plan-list=\"\">\n {tasks.map((task) => (\n <AiChatTaskRow key={task.id} task={task} />\n ))}\n </ul>\n </div>\n )\n}\n\nfunction AiChatTaskRow({ task }: { task: AiAgentTaskSnapshot }) {\n const t = useT()\n const statusLabel =\n task.state === 'running'\n ? t('ai_assistant.chat.taskRunning', 'running\u2026')\n : task.state === 'done'\n ? t('ai_assistant.chat.taskDone', 'done')\n : task.state === 'failed'\n ? t('ai_assistant.chat.taskFailed', 'failed')\n : task.state === 'skipped'\n ? t('ai_assistant.chat.taskSkipped', 'skipped')\n : t('ai_assistant.chat.taskPending', 'pending')\n const Icon =\n task.state === 'running'\n ? Loader2\n : task.state === 'done'\n ? CircleCheck\n : task.state === 'failed'\n ? CircleAlert\n : task.state === 'skipped'\n ? CircleDashed\n : Circle\n const iconClassName = cn(\n 'size-3.5 shrink-0',\n task.state === 'running' && 'animate-spin text-muted-foreground',\n task.state === 'done' && 'text-status-success-icon',\n task.state === 'failed' && 'text-destructive',\n task.state === 'skipped' && 'text-muted-foreground',\n task.state === 'pending' && 'text-muted-foreground',\n )\n const statusBadgeClassName = cn(\n 'ml-auto text-[10px] uppercase tracking-wide',\n task.state === 'done' && 'text-status-success-text',\n task.state === 'failed' && 'text-destructive',\n task.state !== 'done' && task.state !== 'failed' && 'text-muted-foreground',\n )\n return (\n <li\n className=\"flex h-7 items-center gap-2 px-2 text-xs\"\n data-ai-chat-task-id={task.id}\n data-ai-chat-task-state={task.state}\n data-ai-chat-task-source={task.source}\n data-ai-chat-task-tool-call-id={task.toolCallId}\n >\n <Icon className={iconClassName} aria-hidden />\n <span className=\"truncate\" title={task.detail ?? task.label}>\n {task.label}\n </span>\n <span className={statusBadgeClassName}>{statusLabel}</span>\n </li>\n )\n}\n\nfunction ToolCallList({ toolCalls }: { toolCalls: AiChatToolCallSnapshot[] }) {\n const t = useT()\n const [openId, setOpenId] = React.useState<string | null>(null)\n if (!toolCalls || toolCalls.length === 0) return null\n return (\n <div\n className=\"rounded-md border border-border bg-muted/30\"\n data-ai-chat-tool-calls=\"\"\n data-ai-chat-tool-call-count={toolCalls.length}\n >\n <div className=\"flex items-center gap-2 border-b border-border/60 px-2 py-1.5 text-[11px] font-medium uppercase tracking-wide text-muted-foreground\">\n <Wrench className=\"size-3.5 text-muted-foreground\" aria-hidden />\n <span>{t('ai_assistant.chat.agentTasksTitle', 'Tool calls')}</span>\n <span className=\"ml-auto font-mono\">{toolCalls.length}</span>\n </div>\n <ul className=\"divide-y divide-border/60\">\n {toolCalls.map((call) => {\n const isOpen = openId === call.id\n const isError = call.state === 'error'\n const isPending = call.state === 'pending'\n const isComplete = call.state === 'complete'\n const statusLabel = isError\n ? t('ai_assistant.chat.toolError', 'failed')\n : isPending\n ? t('ai_assistant.chat.toolRunning', 'running\u2026')\n : t('ai_assistant.chat.toolDone', 'done')\n return (\n <li\n key={call.id}\n className={cn(\n isError ? 'bg-destructive/5' : '',\n )}\n data-ai-chat-tool-call={call.toolName}\n data-ai-chat-tool-state={call.state}\n >\n <Button\n variant=\"ghost\"\n size=\"2xs\"\n type=\"button\"\n onClick={() => setOpenId(isOpen ? null : call.id)}\n className=\"w-full justify-start rounded-none px-2 text-left hover:bg-muted/60\"\n >\n {isOpen ? (\n <ChevronDown className=\"size-3.5 text-muted-foreground\" aria-hidden />\n ) : (\n <ChevronRight className=\"size-3.5 text-muted-foreground\" aria-hidden />\n )}\n {isPending ? (\n <Loader2 className=\"size-3.5 animate-spin text-muted-foreground\" aria-hidden />\n ) : (\n <Wrench\n className={cn(\n 'size-3.5',\n isError ? 'text-destructive' : 'text-muted-foreground',\n )}\n aria-hidden\n />\n )}\n <span className=\"truncate\" title={formatToolCaption(call)}>\n {formatToolCaption(call)}\n </span>\n <span\n className={cn(\n 'ml-auto text-[10px] uppercase tracking-wide',\n isError\n ? 'text-destructive'\n : isComplete\n ? 'text-status-success-text'\n : 'text-muted-foreground',\n )}\n >\n {statusLabel}\n </span>\n </Button>\n {isOpen ? (\n <div className=\"space-y-1 border-t border-border/60 px-2 py-1.5 text-xs\">\n {call.input !== undefined ? (\n <div>\n <div className=\"text-[10px] uppercase tracking-wide text-muted-foreground\">\n {t('ai_assistant.chat.toolInput', 'Input')}\n </div>\n <pre className=\"mt-0.5 max-h-32 overflow-auto rounded bg-background p-1.5 font-mono text-[11px]\">\n {safeStringify(call.input)}\n </pre>\n </div>\n ) : null}\n {call.output !== undefined && !isError ? (\n <div>\n <div className=\"text-[10px] uppercase tracking-wide text-muted-foreground\">\n {t('ai_assistant.chat.toolOutput', 'Output')}\n </div>\n <pre className=\"mt-0.5 max-h-32 overflow-auto rounded bg-background p-1.5 font-mono text-[11px]\">\n {safeStringify(call.output)}\n </pre>\n </div>\n ) : null}\n {call.errorMessage ? (\n <div className=\"text-destructive\">{call.errorMessage}</div>\n ) : null}\n </div>\n ) : null}\n </li>\n )\n })}\n </ul>\n </div>\n )\n}\n\nfunction ReasoningPanel({ text, streaming }: { text: string; streaming: boolean }) {\n const t = useT()\n const [open, setOpen] = React.useState(false)\n if (!text) return null\n return (\n <div\n className=\"rounded-md border border-border bg-muted/30\"\n data-ai-chat-reasoning={streaming ? 'streaming' : 'complete'}\n >\n <button\n type=\"button\"\n onClick={() => setOpen((o) => !o)}\n className=\"flex w-full items-center gap-2 px-2 py-1.5 text-left text-xs font-medium hover:bg-muted/60\"\n >\n {open ? (\n <ChevronDown className=\"size-3.5 text-muted-foreground\" aria-hidden />\n ) : (\n <ChevronRight className=\"size-3.5 text-muted-foreground\" aria-hidden />\n )}\n <Lightbulb className=\"size-3.5 text-muted-foreground\" aria-hidden />\n <span>{t('ai_assistant.chat.reasoning', 'Reasoning')}</span>\n {streaming ? (\n <Loader2\n className=\"ml-1 size-3 animate-spin text-muted-foreground\"\n aria-hidden\n />\n ) : null}\n </button>\n {open ? (\n <pre className=\"max-h-48 overflow-auto whitespace-pre-wrap border-t border-border/60 px-2 py-1.5 text-xs text-muted-foreground\">\n {text}\n </pre>\n ) : null}\n </div>\n )\n}\n\nfunction getAttachmentImageUrl(file: AiChatMessageFile): string | null {\n if (file.id) return `/api/attachments/image/${encodeURIComponent(file.id)}`\n return file.previewUrl ?? null\n}\n\nfunction getAttachmentDownloadUrl(file: AiChatMessageFile): string | null {\n if (file.id) return `/api/attachments/file/${encodeURIComponent(file.id)}?download=1`\n return file.previewUrl ?? null\n}\n\nfunction MessageFileAttachment({ file }: { file: AiChatMessageFile }) {\n const t = useT()\n const [zoomOpen, setZoomOpen] = React.useState(false)\n const imageUrl = file.previewUrl ? getAttachmentImageUrl(file) : null\n const downloadUrl = getAttachmentDownloadUrl(file)\n const downloadLabel = t('ai_assistant.chat.downloadFile', 'Download {name}').replace(\n '{name}',\n file.name,\n )\n const zoomLabel = t('ai_assistant.chat.zoomImage', 'Open {name} preview').replace(\n '{name}',\n file.name,\n )\n\n if (!file.previewUrl) {\n return (\n <span className=\"inline-flex max-w-full items-center gap-1 rounded-full border border-border bg-muted px-2 py-0.5 text-xs\">\n <Paperclip className=\"size-3 shrink-0\" aria-hidden />\n <span className=\"max-w-[180px] truncate\">{file.name}</span>\n {downloadUrl ? (\n <IconButton\n asChild\n variant=\"ghost\"\n size=\"xs\"\n className=\"-mr-1 size-5 shrink-0\"\n >\n <a href={downloadUrl} download={file.name} aria-label={downloadLabel}>\n <Download className=\"size-3\" aria-hidden />\n </a>\n </IconButton>\n ) : null}\n </span>\n )\n }\n\n return (\n <>\n <div className=\"group/file relative max-w-full\">\n <button\n type=\"button\"\n className=\"block max-w-full rounded-md border border-border bg-muted/30 focus-visible:outline-none focus-visible:shadow-focus\"\n onClick={() => setZoomOpen(true)}\n aria-label={zoomLabel}\n data-ai-chat-image-preview=\"\"\n >\n <img\n src={file.previewUrl}\n alt={file.name}\n className=\"max-h-32 max-w-[200px] rounded-md object-cover\"\n />\n <span className=\"pointer-events-none absolute right-1 top-1 inline-flex size-6 items-center justify-center rounded-md bg-background/90 text-muted-foreground opacity-0 shadow-xs transition-opacity group-hover/file:opacity-100 group-focus-within/file:opacity-100\">\n <Maximize2 className=\"size-3.5\" aria-hidden />\n </span>\n </button>\n {downloadUrl ? (\n <IconButton\n asChild\n variant=\"white\"\n size=\"xs\"\n className=\"absolute bottom-1 right-1 opacity-0 shadow-xs transition-opacity group-hover/file:opacity-100 group-focus-within/file:opacity-100\"\n >\n <a\n href={downloadUrl}\n download={file.name}\n aria-label={downloadLabel}\n onClick={(event) => event.stopPropagation()}\n data-ai-chat-file-download=\"\"\n >\n <Download className=\"size-3.5\" aria-hidden />\n </a>\n </IconButton>\n ) : null}\n </div>\n\n <Dialog open={zoomOpen} onOpenChange={setZoomOpen}>\n <DialogContent className=\"gap-3 p-4 sm:max-w-[min(96vw,960px)]\">\n <DialogHeader className=\"min-w-0 pr-16\">\n <DialogTitle className=\"truncate text-sm\">{file.name}</DialogTitle>\n <DialogDescription className=\"sr-only\">\n {t('ai_assistant.chat.imagePreviewDialogDescription', 'Image preview')}\n </DialogDescription>\n </DialogHeader>\n <div className=\"relative flex min-h-0 items-center justify-center overflow-auto rounded-md bg-muted/30 p-2\">\n <img\n src={imageUrl ?? file.previewUrl}\n alt={file.name}\n className=\"max-h-[75vh] max-w-full object-contain\"\n />\n {downloadUrl ? (\n <IconButton\n asChild\n variant=\"white\"\n size=\"sm\"\n className=\"absolute bottom-3 right-3 shadow-xs\"\n >\n <a\n href={downloadUrl}\n download={file.name}\n aria-label={downloadLabel}\n data-ai-chat-file-download=\"\"\n >\n <Download className=\"size-4\" aria-hidden />\n </a>\n </IconButton>\n ) : null}\n </div>\n </DialogContent>\n </Dialog>\n </>\n )\n}\n\nfunction MessageRow({\n message,\n registry,\n onMutationRequested,\n isOwner,\n}: {\n message: AiChatMessage\n registry?: AiUiPartRegistry\n onMutationRequested?: (pendingActionId: string) => void\n /** Whether the current viewer owns the conversation. Used to label foreign user messages. */\n isOwner?: boolean | null\n}) {\n const t = useT()\n const isAssistant = message.role === 'assistant'\n const isOtherUsersMessage = !isAssistant && isOwner === false\n const label = isAssistant\n ? t('ai_assistant.chat.assistantRoleLabel', 'Assistant')\n : isOtherUsersMessage\n ? t('ai_assistant.chat.ownerRoleLabel', 'Owner')\n : t('ai_assistant.chat.userRoleLabel', 'You')\n const Icon = isAssistant ? Bot : User\n const [copied, setCopied] = React.useState(false)\n const copyTimerRef = React.useRef<ReturnType<typeof setTimeout> | null>(null)\n const visibleTaskPlan =\n isAssistant && message.taskPlan\n ? message.taskPlan.filter((task) => task.source === 'agent')\n : []\n\n React.useEffect(() => {\n return () => {\n if (copyTimerRef.current) clearTimeout(copyTimerRef.current)\n }\n }, [])\n\n const handleCopy = React.useCallback(async () => {\n const text = message.content\n if (!text) return\n try {\n if (typeof navigator !== 'undefined' && navigator.clipboard?.writeText) {\n await navigator.clipboard.writeText(text)\n } else {\n const textarea = document.createElement('textarea')\n textarea.value = text\n textarea.setAttribute('readonly', '')\n textarea.style.position = 'absolute'\n textarea.style.left = '-9999px'\n document.body.appendChild(textarea)\n textarea.select()\n document.execCommand('copy')\n document.body.removeChild(textarea)\n }\n setCopied(true)\n if (copyTimerRef.current) clearTimeout(copyTimerRef.current)\n copyTimerRef.current = setTimeout(() => setCopied(false), 1500)\n } catch {\n // Clipboard API blocked (no permission, http context) \u2014 silently fail.\n }\n }, [message.content])\n\n return (\n <div\n className={cn(\n 'group/message flex gap-3 px-3 py-2',\n isAssistant ? 'bg-muted/40 rounded-md' : '',\n )}\n data-role={message.role}\n >\n <div\n className={cn(\n 'flex size-6 shrink-0 items-center justify-center rounded-full',\n isAssistant\n ? 'bg-primary/10 text-primary'\n : 'bg-secondary text-secondary-foreground',\n )}\n aria-hidden\n >\n <Icon className=\"size-4\" />\n </div>\n <div className=\"flex-1 space-y-1\">\n <div className=\"flex items-center justify-between gap-2\">\n <div className=\"text-xs font-medium text-muted-foreground\">{label}</div>\n {message.content ? (\n <IconButton\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n onClick={handleCopy}\n aria-label={\n copied\n ? t('ai_assistant.chat.copied', 'Copied')\n : t('ai_assistant.chat.copyMessage', 'Copy message')\n }\n data-ai-chat-copy-button=\"\"\n className=\"opacity-0 transition-opacity group-hover/message:opacity-100 focus-visible:opacity-100\"\n >\n {copied ? (\n <Check className=\"size-3.5 text-status-success-icon\" aria-hidden />\n ) : (\n <Copy className=\"size-3.5\" aria-hidden />\n )}\n </IconButton>\n ) : null}\n </div>\n {message.files && message.files.length > 0 ? (\n <div className=\"flex flex-wrap gap-2 py-1\">\n {message.files.map((file, i) => (\n <MessageFileAttachment key={`${file.id ?? file.name}:${i}`} file={file} />\n ))}\n </div>\n ) : null}\n {isAssistant && message.reasoning ? (\n <ReasoningPanel\n text={message.reasoning}\n streaming={message.reasoningStreaming === true}\n />\n ) : null}\n {visibleTaskPlan.length > 0 ? (\n <AiChatTaskPlan tasks={visibleTaskPlan} />\n ) : null}\n {isAssistant && message.toolCalls && message.toolCalls.length > 0 ? (\n <ToolCallList toolCalls={message.toolCalls} />\n ) : null}\n <MessageContent content={message.content} isAssistant={isAssistant} />\n {isAssistant && registry && message.uiParts && message.uiParts.length > 0 ? (\n <MessageUiParts\n parts={message.uiParts}\n registry={registry}\n onMutationRequested={onMutationRequested}\n />\n ) : null}\n </div>\n </div>\n )\n}\n\nfunction MessageUiParts({\n parts,\n registry,\n onMutationRequested,\n}: {\n parts: AiChatMessageUiPart[]\n registry: AiUiPartRegistry\n onMutationRequested?: (pendingActionId: string) => void\n}) {\n return (\n <div className=\"mt-2 flex flex-col gap-2\" data-ai-message-ui-parts=\"\">\n {parts.map((part) => (\n <AiUiPartRenderer\n key={part.key}\n part={{\n componentId: part.componentId as AiUiPartComponentId,\n payload: part.payload,\n pendingActionId: part.pendingActionId,\n }}\n registry={registry}\n onMutationRequested={onMutationRequested}\n />\n ))}\n </div>\n )\n}\n\nfunction UnknownUiPartPlaceholder({ componentId }: { componentId: AiUiPartComponentId }) {\n const t = useT()\n return (\n <div\n className=\"inline-flex items-center gap-2 rounded-full border border-dashed border-border bg-muted px-3 py-1 text-xs text-muted-foreground\"\n data-ai-ui-part-placeholder={componentId}\n >\n <span>\n {t('ai_assistant.chat.uiPartPending', 'Pending UI part:')} {componentId}\n </span>\n </div>\n )\n}\n\nfunction AiUiPartRenderer({\n part,\n registry,\n onMutationRequested,\n}: {\n part: ServerEmittedUiPartRef\n registry: AiUiPartRegistry\n onMutationRequested?: (pendingActionId: string) => void\n}) {\n const Component = registry.resolve(part.componentId)\n const isReserved = isReservedAiUiPartId(part.componentId)\n React.useEffect(() => {\n if (Component) return\n if (isReserved) return\n try {\n // eslint-disable-next-line no-console\n console.warn(\n `[AiChat] No component registered for UI part \"${part.componentId}\".`,\n )\n } catch {\n // noop\n }\n }, [Component, part.componentId, isReserved])\n React.useEffect(() => {\n if (part.pendingActionId) {\n onMutationRequested?.(part.pendingActionId)\n }\n }, [part.pendingActionId, onMutationRequested])\n if (!Component) {\n return <UnknownUiPartPlaceholder componentId={part.componentId} />\n }\n return (\n <Component\n componentId={part.componentId}\n payload={part.payload}\n pendingActionId={part.pendingActionId}\n />\n )\n}\n\nfunction getSuggestionIcon(suggestion: AiChatSuggestion): {\n icon: React.ReactNode\n name: string\n} {\n if (suggestion.icon) return { icon: suggestion.icon, name: 'custom' }\n const text = `${suggestion.label} ${suggestion.prompt}`.toLowerCase()\n const iconClassName = 'size-4 text-foreground'\n if (/\\b(analy[sz]e|analysis|health|score|summary|summari[sz]e)\\b/.test(text)) {\n return { icon: <ChartNoAxesCombined className={iconClassName} />, name: 'analysis' }\n }\n if (/\\b(at[-\\s]?risk|risk|stalled|stale|overdue|danger)\\b/.test(text)) {\n return { icon: <AlertTriangle className={iconClassName} />, name: 'risk' }\n }\n if (/\\bpipeline|stage|funnel\\b/.test(text)) {\n return { icon: <GitBranch className={iconClassName} />, name: 'pipeline' }\n }\n if (/\\bdeal|opportunit(?:y|ies)\\b/.test(text)) {\n return { icon: <Handshake className={iconClassName} />, name: 'deal' }\n }\n if (/\\bdata|access|record|records|schema\\b/.test(text)) {\n return { icon: <Database className={iconClassName} />, name: 'data' }\n }\n if (/\\bwhat can you help|help me|capabilit(?:y|ies)\\b/.test(text)) {\n return { icon: <BadgeQuestionMark className={iconClassName} />, name: 'capabilities' }\n }\n if (/\\bsuggest|try|idea|ideas|recommend\\b/.test(text)) {\n return { icon: <Lightbulb className={iconClassName} />, name: 'ideas' }\n }\n if (/\\bhow do i|how to|use this|instructions?|guide\\b/.test(text)) {\n return { icon: <FileQuestion className={iconClassName} />, name: 'guide' }\n }\n if (/\\bfind|search|show|list|lookup\\b/.test(text)) {\n return { icon: <Search className={iconClassName} />, name: 'search' }\n }\n if (/\\bactivity|activities|timeline|recent\\b/.test(text)) {\n return { icon: <Activity className={iconClassName} />, name: 'activity' }\n }\n if (/\\bdown|drop|declin|loss|lost\\b/.test(text)) {\n return { icon: <TrendingDown className={iconClassName} />, name: 'trend-down' }\n }\n return { icon: <AiIcon className={iconClassName} />, name: 'ai' }\n}\n\nfunction WelcomeState({\n title,\n description,\n suggestions,\n onSuggestionClick,\n}: {\n title?: string\n description?: string\n suggestions?: AiChatSuggestion[]\n onSuggestionClick: (prompt: string) => void\n}) {\n const t = useT()\n const heading = title ?? t('ai_assistant.chat.welcomeTitle', 'How can I help?')\n const desc =\n description ??\n t(\n 'ai_assistant.chat.welcomeDescription',\n 'Ask me anything about your data. Here are some things I can do:',\n )\n return (\n <div className=\"flex flex-1 flex-col items-center justify-center gap-4 px-4 py-8\">\n <div\n className=\"flex size-12 items-center justify-center rounded-full bg-primary/10 text-primary\"\n aria-hidden\n >\n <Bot className=\"size-6\" />\n </div>\n <div className=\"space-y-1 text-center\">\n <h3 className=\"text-sm font-semibold\">{heading}</h3>\n <p className=\"text-xs text-muted-foreground\">{desc}</p>\n </div>\n {suggestions && suggestions.length > 0 ? (\n <div className=\"flex w-full max-w-md flex-col gap-2\" data-ai-chat-suggestions=\"\">\n {suggestions.map((suggestion, index) => {\n const suggestionIcon = getSuggestionIcon(suggestion)\n return (\n <Button\n variant=\"outline\"\n key={index}\n type=\"button\"\n className=\"h-auto w-full justify-start whitespace-normal rounded-lg bg-card px-3 py-2.5 text-left text-sm\"\n onClick={() => onSuggestionClick(suggestion.prompt)}\n data-ai-chat-suggestion={index}\n data-ai-chat-suggestion-icon={suggestionIcon.name}\n >\n <span className=\"shrink-0 text-muted-foreground\" aria-hidden>\n {suggestionIcon.icon}\n </span>\n <span>{suggestion.label}</span>\n </Button>\n )\n })}\n </div>\n ) : null}\n </div>\n )\n}\n\nfunction ContextItemsPill({ items }: { items: AiChatContextItem[] }) {\n if (items.length === 0) return null\n return (\n <div\n className=\"flex flex-wrap gap-1.5 border-b border-border px-3 py-2\"\n data-ai-chat-context-items=\"\"\n >\n {items.map((item, index) => (\n <span\n key={index}\n className=\"inline-flex items-center gap-1 rounded-full border border-border bg-secondary px-2 py-0.5 text-xs text-secondary-foreground\"\n data-ai-chat-context-item={index}\n title={item.detail}\n >\n {item.label}\n </span>\n ))}\n </div>\n )\n}\n\n/**\n * Embeddable AI chat component. Binds to the dispatcher route\n * `POST /api/ai_assistant/ai/chat?agent=<module>.<agent>` via\n * {@link createAiAgentTransport}. Phase 2 WS-A deliverable (Step 4.1).\n *\n * - Keyboard: `Enter` submits; `Shift+Enter` inserts a newline; `Escape`\n * aborts streaming (or blurs the composer when idle).\n * - Error envelopes from the dispatcher surface as `Alert` + `onError`.\n * - UI parts render via the client-side registry; unknown parts render a\n * neutral placeholder chip so mutation-card slots reserved for Phase 3\n * never throw before their implementations land.\n */\nexport function AiChat({\n agent,\n apiPath,\n pageContext,\n attachmentIds,\n initialMessages,\n debug,\n className,\n placeholder,\n onMutationRequested,\n onError,\n registry,\n uiParts: uiPartsProp,\n debugTools,\n debugPromptSections,\n loopTrace,\n conversationId,\n suggestions,\n contextItems,\n welcomeTitle,\n welcomeDescription,\n defaultCompactFooter = false,\n headerActions,\n onConversationIdChange,\n onConversationNotFound,\n}: AiChatProps) {\n const t = useT()\n const onConversationIdChangeRef = React.useRef(onConversationIdChange)\n React.useLayoutEffect(() => {\n onConversationIdChangeRef.current = onConversationIdChange\n })\n const textareaRef = React.useRef<HTMLTextAreaElement | null>(null)\n const transcriptRef = React.useRef<HTMLDivElement | null>(null)\n const fileInputRef = React.useRef<HTMLInputElement | null>(null)\n const [input, setInput] = React.useState('')\n type PendingAttachment = {\n file: File\n attachmentId?: string\n /**\n * Base64 data URL of the image (capped by PREVIEW_DATA_URL_MAX_BYTES).\n * Stored on the local message for immediate previews; once the transcript\n * is loaded from server storage the attachment id is used to rebuild a\n * same-origin thumbnail URL.\n */\n previewDataUrl?: string\n error?: string\n }\n const [pendingFiles, setPendingFiles] = React.useState<PendingAttachment[]>([])\n const upload = useAiChatUpload()\n const isUploading = upload.busy\n const footerRef = React.useRef<HTMLDivElement | null>(null)\n const [isCompactFooter, setIsCompactFooter] = React.useState(defaultCompactFooter)\n\n React.useEffect(() => {\n const element = footerRef.current\n if (!element || typeof ResizeObserver === 'undefined') return\n\n const updateCompactState = (width: number) => {\n setIsCompactFooter(width < COMPACT_FOOTER_MAX_WIDTH)\n }\n\n updateCompactState(element.getBoundingClientRect().width)\n const observer = new ResizeObserver((entries) => {\n const entry = entries[0]\n if (!entry) return\n updateCompactState(entry.contentRect.width)\n })\n observer.observe(element)\n return () => observer.disconnect()\n }, [])\n\n const uploadedAttachmentIds = React.useMemo(\n () =>\n pendingFiles\n .map((entry) => entry.attachmentId)\n .filter((id): id is string => typeof id === 'string' && id.length > 0),\n [pendingFiles],\n )\n\n const allAttachmentIds = React.useMemo(\n () => [...(attachmentIds ?? []), ...uploadedAttachmentIds],\n [attachmentIds, uploadedAttachmentIds],\n )\n\n const {\n providers: modelProviders,\n allowRuntimeOverride,\n allowRuntimeModelOverride,\n defaultLabel: modelDefaultLabel,\n loaded: modelProvidersLoaded,\n } = useAgentModels(agent)\n\n const [modelPickerValue, setModelPickerValue] = React.useState<ModelPickerValue | null>(() =>\n readModelPickerValue(agent),\n )\n\n const effectiveModelPickerValue = React.useMemo(() => {\n if (!modelProvidersLoaded || !allowRuntimeOverride || modelProviders.length === 0) {\n return null\n }\n if (modelPickerValue && isModelPickerValueAvailable(modelPickerValue, modelProviders)) {\n return modelPickerValue\n }\n return null\n }, [\n allowRuntimeOverride,\n modelPickerValue,\n modelProviders,\n modelProvidersLoaded,\n ])\n\n React.useEffect(() => {\n setModelPickerValue(readModelPickerValue(agent))\n }, [agent])\n\n React.useEffect(() => {\n if (!modelProvidersLoaded) return\n if (!allowRuntimeModelOverride || modelProviders.length === 0) {\n if (modelPickerValue !== null) {\n setModelPickerValue(null)\n writeModelPickerValue(agent, null)\n }\n return\n }\n if (modelPickerValue && !isModelPickerValueAvailable(modelPickerValue, modelProviders)) {\n setModelPickerValue(null)\n writeModelPickerValue(agent, null)\n }\n }, [\n agent,\n allowRuntimeModelOverride,\n modelPickerValue,\n modelProviders,\n modelProvidersLoaded,\n ])\n\n const handleModelPickerChange = React.useCallback(\n (value: ModelPickerValue | null) => {\n setModelPickerValue(value)\n writeModelPickerValue(agent, value)\n },\n [agent],\n )\n\n const chat = useAiChat({\n agent,\n apiPath,\n pageContext,\n attachmentIds: allAttachmentIds.length > 0 ? allAttachmentIds : undefined,\n debug,\n initialMessages,\n onError,\n conversationId,\n providerOverride: effectiveModelPickerValue?.providerId ?? null,\n modelOverride: effectiveModelPickerValue?.modelId ?? null,\n onConversationNotFound,\n })\n\n const isStreaming = chat.status === 'streaming'\n const isSubmitting = chat.status === 'submitting'\n const isBusy = isStreaming || isSubmitting\n\n React.useEffect(() => {\n onConversationIdChangeRef.current?.(chat.conversationId)\n }, [chat.conversationId])\n\n // Surface a \"Thinking...\" placeholder so the chat does not look frozen.\n // Visible whenever ANY of the following is true while a turn is in flight:\n // (a) we're still in the submit phase before the first stream chunk\n // (b) streaming, but no content / reasoning / tool calls have arrived yet\n // (c) streaming, and at least one tool call is still in `pending` state\n // (the model is waiting on a tool result \u2014 the previous version\n // treated `toolCalls.length > 0` as \"has content\" and hid the\n // indicator the moment the first tool started, even though the\n // model had not produced any user-visible output yet)\n // (d) streaming, and the last visible event was a finished tool call\n // \u2014 the model is reasoning about the result before emitting more\n // text or kicking off the next tool\n // Once visible text/reasoning exists and no tool is pending, hide the\n // placeholder even if the network stream stays open for a tail event. That\n // avoids showing \"Thinking...\" under an already usable answer.\n const lastAssistant = React.useMemo(() => {\n for (let index = chat.messages.length - 1; index >= 0; index -= 1) {\n const candidate = chat.messages[index]\n if (candidate?.role === 'assistant') return candidate\n }\n return null\n }, [chat.messages])\n\n const trimmedContent = lastAssistant?.content?.trim() ?? ''\n const hasReasoning = !!(lastAssistant?.reasoning && lastAssistant.reasoning.length > 0)\n const toolCalls = lastAssistant?.toolCalls ?? []\n const hasPendingToolCall = toolCalls.some((call) => call.state === 'pending')\n const hasCompletedToolCall = toolCalls.some(\n (call) => call.state === 'complete' || call.state === 'error',\n )\n const hasAnyVisibleSignal = !!(\n trimmedContent || hasReasoning || toolCalls.length > 0\n )\n\n const assistantStreamSnapshot = React.useMemo(() => {\n if (!lastAssistant) return ''\n const toolSig = toolCalls\n .map((call) => `${call.id}:${call.state}:${call.output != null ? 1 : 0}`)\n .join('|')\n return [\n lastAssistant.id,\n lastAssistant.content?.length ?? 0,\n lastAssistant.reasoning?.length ?? 0,\n lastAssistant.reasoningStreaming ? 1 : 0,\n toolSig,\n ].join('#')\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [lastAssistant])\n\n const lastSnapshotRef = React.useRef<string>('')\n const [, setStreamTick] = React.useState(0)\n\n React.useEffect(() => {\n if (assistantStreamSnapshot !== lastSnapshotRef.current) {\n lastSnapshotRef.current = assistantStreamSnapshot\n setStreamTick((value) => value + 1)\n }\n }, [assistantStreamSnapshot])\n\n const showThinkingIndicator =\n isSubmitting ||\n (isStreaming &&\n (\n !hasAnyVisibleSignal ||\n hasPendingToolCall ||\n // Tool just returned and the model hasn't started speaking yet.\n (hasCompletedToolCall && !trimmedContent)\n ))\n\n const activeRegistry = registry ?? defaultAiUiPartRegistry\n\n // Reserved UI parts. Phase 3 will populate this from the streamed response;\n // for Phase 2 WS-A it stays empty unless the host surfaces test/debug parts\n // via the optional `uiParts` prop so the registry resolution path can be\n // exercised without waiting for the runtime emitter.\n const uiParts: ServerEmittedUiPartRef[] = React.useMemo(\n () => (uiPartsProp ?? []).map((part) => ({\n componentId: part.componentId,\n payload: part.payload,\n pendingActionId: part.pendingActionId,\n })),\n [uiPartsProp],\n )\n\n const hasUploadingFiles = React.useMemo(\n () => pendingFiles.some((entry) => !entry.attachmentId && !entry.error),\n [pendingFiles],\n )\n\n const handleSendMessage = React.useCallback(\n (text: string) => {\n if (!text.trim() || isBusy) return\n // Block send while any attachment is still uploading. Without this guard\n // the message would ship with an empty attachmentIds list (the chip is\n // visible but the server hasn't returned an id yet), the model would\n // never see the file, and `setPendingFiles([])` below would erase the\n // chip \u2014 so the upload finishes into the void. Surface the wait via the\n // disabled Send button + composer hint instead.\n if (hasUploadingFiles || isUploading) return\n const filesToAttach = pendingFiles.map((entry): AiChatMessageFile => {\n const isImage = entry.file.type.startsWith('image/')\n const fallback = isImage ? URL.createObjectURL(entry.file) : undefined\n return {\n id: entry.attachmentId,\n name: entry.file.name,\n type: entry.file.type,\n previewUrl: isImage ? (entry.previewDataUrl ?? fallback) : undefined,\n }\n })\n setInput('')\n setPendingFiles([])\n void chat.sendMessage(text, filesToAttach.length > 0 ? filesToAttach : undefined)\n },\n [chat, hasUploadingFiles, isBusy, isUploading, pendingFiles],\n )\n\n const handleSubmit = React.useCallback(() => {\n handleSendMessage(input)\n }, [handleSendMessage, input])\n\n // Listen for \"Fix with AI\" requests dispatched by the failure variant\n // of `MutationResultCard`. Any rendered failure card can fire a custom\n // DOM event with the prompt \u2014 this side-steps having to thread a\n // sendMessage callback through the UI-part registry while keeping the\n // chat the single owner of message creation. Idempotency is handled\n // server-side: `prepareMutation` only dedupes against active `pending`\n // rows, so a retry after a terminal failure always produces a fresh\n // pending action.\n React.useEffect(() => {\n if (typeof window === 'undefined') return\n const handler = (event: Event) => {\n const detail = (event as CustomEvent<{ message?: string }>).detail\n const message = detail?.message\n if (typeof message !== 'string' || message.trim().length === 0) return\n handleSendMessage(message)\n }\n window.addEventListener('om-ai-chat-fix-request', handler as EventListener)\n return () => {\n window.removeEventListener('om-ai-chat-fix-request', handler as EventListener)\n }\n }, [handleSendMessage])\n\n const cancelOrBlur = React.useCallback(() => {\n if (isBusy) {\n chat.cancel()\n return\n }\n textareaRef.current?.blur()\n }, [chat, isBusy])\n\n const handleFileSelect = React.useCallback(\n async (event: React.ChangeEvent<HTMLInputElement>) => {\n const files = Array.from(event.target.files ?? [])\n if (files.length === 0) return\n const queued: PendingAttachment[] = files.map((file) => ({ file }))\n setPendingFiles((prev) => [...prev, ...queued])\n const [previewResults, result] = await Promise.all([\n Promise.all(files.map((file) => readFileAsDataUrl(file))),\n upload.upload(files),\n ])\n // Pair upload outcomes back to chips by input INDEX, not by filename.\n // The server may sanitize the uploaded name (whitespace, unicode,\n // dangerous characters), and two files in the same batch can share a\n // name \u2014 both cases broke the previous Map-by-fileName matching and\n // left the chip stuck on the spinner forever.\n const idByIndex = new Map<number, string>()\n for (const item of result.items) {\n if (typeof item.inputIndex === 'number') idByIndex.set(item.inputIndex, item.attachmentId)\n }\n const errorByIndex = new Map<number, string>()\n for (const failure of result.failed) {\n if (typeof failure.inputIndex === 'number') errorByIndex.set(failure.inputIndex, failure.message)\n }\n setPendingFiles((prev) => {\n const next = prev.slice()\n const baseIndex = next.length - files.length\n for (let offset = 0; offset < files.length; offset += 1) {\n const index = baseIndex + offset\n if (index < 0) continue\n const entry = next[index]\n if (!entry) continue\n const dataUrl = previewResults[offset]\n const patch: PendingAttachment = {\n ...entry,\n previewDataUrl: dataUrl ?? entry.previewDataUrl,\n }\n if (!patch.attachmentId) {\n const id = idByIndex.get(offset)\n if (id) {\n patch.attachmentId = id\n patch.error = undefined\n } else {\n // Defensive fallback: if neither success nor failure carried an\n // index for this slot (older transports, partial outcome), the\n // chip would otherwise stay on the spinner. Mark it as a\n // generic error so the user can remove it and retry instead of\n // staring at a dead spinner that also blocks the Send button.\n const explicitError = errorByIndex.get(offset)\n patch.error =\n explicitError ??\n 'Upload finished without a server response. Remove the file and try again.'\n }\n }\n next[index] = patch\n }\n return next\n })\n if (fileInputRef.current) fileInputRef.current.value = ''\n },\n [upload],\n )\n\n const removePendingFile = React.useCallback((index: number) => {\n setPendingFiles((prev) => prev.filter((_, i) => i !== index))\n }, [])\n\n const { handleKeyDown } = useAiShortcuts({\n onSubmit: handleSubmit,\n onCancel: cancelOrBlur,\n })\n\n React.useEffect(() => {\n textareaRef.current?.focus()\n }, [])\n\n // Sticky-bottom autoscroll: only re-pin to the bottom when the user is\n // already there (or within a small tolerance). If they have scrolled up to\n // read an earlier part of a long response, every streaming delta would\n // otherwise yank them back to the tail and make the message look truncated.\n // Tolerance is generous enough to absorb sub-pixel rounding, but tight\n // enough that an intentional scroll-up keeps the user where they want.\n const stickToBottomRef = React.useRef(true)\n const SCROLL_STICK_TOLERANCE_PX = 64\n\n React.useEffect(() => {\n const node = transcriptRef.current\n if (!node) return\n const handleScroll = () => {\n const distanceFromBottom = node.scrollHeight - node.scrollTop - node.clientHeight\n stickToBottomRef.current = distanceFromBottom <= SCROLL_STICK_TOLERANCE_PX\n }\n node.addEventListener('scroll', handleScroll, { passive: true })\n return () => node.removeEventListener('scroll', handleScroll)\n }, [])\n\n React.useEffect(() => {\n const node = transcriptRef.current\n if (!node) return\n if (!stickToBottomRef.current) return\n node.scrollTop = node.scrollHeight\n }, [chat.messages])\n\n // Mark the body so floating UI surfaces (e.g. the demo feedback FAB) can\n // hide themselves while the chat is open and would otherwise overlay the\n // composer's send button. CSS lives in `apps/mercato/src/app/globals.css`\n // alongside the column-chooser precedent.\n React.useEffect(() => {\n if (typeof document === 'undefined') return\n const previous = document.body.getAttribute('data-ai-chat-open')\n document.body.setAttribute('data-ai-chat-open', 'true')\n return () => {\n if (previous === null) {\n document.body.removeAttribute('data-ai-chat-open')\n } else {\n document.body.setAttribute('data-ai-chat-open', previous)\n }\n }\n }, [])\n\n const handleNewConversation = React.useCallback(() => {\n chat.reset()\n setInput('')\n setPendingFiles([])\n upload.reset()\n setTimeout(() => textareaRef.current?.focus(), 0)\n }, [chat, upload])\n\n const resolvedPlaceholder =\n placeholder ?? t('ai_assistant.chat.composerPlaceholder', 'Message the AI agent...')\n\n const errorVariant = mapErrorCodeToVariant(chat.error?.code)\n\n return (\n <section\n className={cn(\n 'flex h-full min-h-[320px] min-w-0 flex-col gap-3 overflow-hidden rounded-lg border border-border bg-background p-3',\n className,\n )}\n aria-label={t('ai_assistant.chat.regionLabel', 'AI chat')}\n data-ai-chat-agent={agent}\n data-ai-chat-conversation-id={chat.conversationId}\n >\n <div className=\"flex min-w-0 items-center justify-between gap-2 border-b border-border pb-2\">\n <div className=\"flex min-w-0 flex-1 items-center gap-2 text-xs text-muted-foreground\">\n {contextItems && contextItems.length > 0 ? (\n <ContextItemsPill items={contextItems} />\n ) : (\n <span className=\"font-mono opacity-70\" aria-hidden>\n {chat.conversationId.slice(0, 8)}\n </span>\n )}\n </div>\n {headerActions ?? null}\n <IconButton\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n onClick={handleNewConversation}\n disabled={isBusy}\n aria-label={t('ai_assistant.chat.newConversation', 'Start new conversation')}\n title={t('ai_assistant.chat.newConversation', 'Start new conversation')}\n data-ai-chat-new-conversation=\"\"\n >\n <Plus className=\"size-4\" aria-hidden />\n </IconButton>\n </div>\n <div\n ref={transcriptRef}\n role=\"log\"\n aria-live=\"polite\"\n aria-label={t('ai_assistant.chat.transcriptLabel', 'Chat transcript')}\n className=\"min-w-0 flex-1 space-y-2 overflow-y-auto overflow-x-hidden pr-1\"\n >\n {chat.messages.length === 0 ? (\n <WelcomeState\n title={welcomeTitle}\n description={welcomeDescription}\n suggestions={suggestions}\n onSuggestionClick={handleSendMessage}\n />\n ) : (\n chat.messages.map((message) => (\n <MessageRow\n key={message.id}\n message={message}\n registry={activeRegistry}\n onMutationRequested={onMutationRequested}\n isOwner={chat.isOwner}\n />\n ))\n )}\n {uiParts.map((part, index) => (\n <AiUiPartRenderer\n key={`${part.componentId}-${index}`}\n part={part}\n registry={activeRegistry}\n onMutationRequested={onMutationRequested}\n />\n ))}\n {showThinkingIndicator ? (\n <div\n className=\"flex items-center gap-2 px-3 py-2 text-xs text-muted-foreground\"\n data-ai-chat-state=\"thinking\"\n >\n <Loader2 className=\"size-4 animate-spin\" aria-hidden />\n <span>{t('ai_assistant.chat.thinking', 'Thinking...')}</span>\n </div>\n ) : null}\n </div>\n\n {chat.error ? (\n <Alert variant={errorVariant} data-ai-chat-error={chat.error.code ?? 'unknown'}>\n <AlertTitle>\n {t('ai_assistant.chat.errorTitle', 'Agent dispatch failed')}\n </AlertTitle>\n <AlertDescription>\n {chat.error.code ? (\n <span className=\"mr-2 font-mono text-xs\">{chat.error.code}</span>\n ) : null}\n {chat.error.message}\n </AlertDescription>\n </Alert>\n ) : null}\n\n {chat.isOwner === false ? (\n <div\n className=\"flex items-center justify-center rounded-md border border-border bg-muted/30 px-3 py-3 text-xs text-muted-foreground\"\n data-ai-chat-read-only-notice=\"\"\n >\n {t('ai_assistant.chat.readOnlyNotice', 'This is a shared conversation. You can read but not reply.')}\n </div>\n ) : null}\n <form\n className={cn('flex min-w-0 flex-col gap-2', chat.isOwner === false && 'hidden')}\n onSubmit={(event) => {\n event.preventDefault()\n handleSubmit()\n }}\n >\n <Label\n htmlFor=\"ai-chat-composer\"\n className=\"sr-only\"\n >\n {t('ai_assistant.chat.composerLabel', 'Message composer')}\n </Label>\n {pendingFiles.length > 0 ? (\n <div className=\"flex flex-wrap gap-1.5 rounded-md border border-border bg-muted/30 px-2 py-1.5\" data-ai-chat-attachments=\"\">\n {pendingFiles.map((entry, index) => (\n <span\n key={index}\n className=\"inline-flex items-center gap-1 rounded-full border border-border bg-background px-2 py-0.5 text-xs\"\n title={entry.error ? entry.error : undefined}\n data-ai-chat-attachment-state={\n entry.error ? 'error' : entry.attachmentId ? 'ready' : 'uploading'\n }\n >\n <Paperclip className=\"size-3 text-muted-foreground\" aria-hidden />\n <span className=\"max-w-[120px] truncate\">{entry.file.name}</span>\n {!entry.attachmentId && !entry.error ? (\n <Loader2 className=\"size-3 animate-spin text-muted-foreground\" aria-hidden />\n ) : null}\n <button\n type=\"button\"\n className=\"ml-0.5 rounded-full p-0.5 hover:bg-muted\"\n onClick={() => removePendingFile(index)}\n aria-label={t('ai_assistant.chat.removeFile', 'Remove file')}\n >\n <X className=\"size-3\" />\n </button>\n </span>\n ))}\n {isUploading ? <Loader2 className=\"size-3 animate-spin text-muted-foreground\" aria-hidden /> : null}\n </div>\n ) : null}\n <Textarea\n id=\"ai-chat-composer\"\n ref={textareaRef}\n value={input}\n placeholder={resolvedPlaceholder}\n onChange={(event) => setInput(event.target.value)}\n onKeyDown={handleKeyDown}\n rows={3}\n aria-label={t('ai_assistant.chat.composerLabel', 'Message composer')}\n className=\"min-w-0 resize-none\"\n />\n <input\n ref={fileInputRef}\n type=\"file\"\n multiple\n accept=\"image/*,.pdf,.doc,.docx,.txt,.csv\"\n className=\"hidden\"\n onChange={handleFileSelect}\n data-ai-chat-file-input=\"\"\n />\n <div\n ref={footerRef}\n className=\"flex min-w-0 items-center justify-between gap-2\"\n data-ai-chat-footer=\"\"\n data-ai-chat-footer-compact={isCompactFooter ? 'true' : 'false'}\n >\n <div className=\"flex min-w-0 items-center gap-2\">\n <IconButton\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => fileInputRef.current?.click()}\n disabled={isBusy || isUploading}\n aria-label={t('ai_assistant.chat.attachFile', 'Attach file')}\n >\n <Paperclip className=\"size-4\" aria-hidden />\n </IconButton>\n {allowRuntimeOverride && modelProviders.length > 0 ? (\n <ModelPicker\n agentId={agent}\n value={effectiveModelPickerValue}\n onChange={handleModelPickerChange}\n availableProviders={modelProviders}\n disabled={isBusy}\n compact={isCompactFooter}\n defaultLabel={modelDefaultLabel}\n className=\"shrink-0\"\n />\n ) : null}\n <p className={cn('text-xs text-muted-foreground', isCompactFooter && 'hidden')}>\n {hasUploadingFiles || isUploading\n ? t(\n 'ai_assistant.chat.uploadingHint',\n 'Uploading attachments\u2026 Send is disabled until they finish.',\n )\n : t(\n 'ai_assistant.chat.shortcutHint',\n 'Press Enter to send, Shift+Enter for new line.',\n )}\n </p>\n </div>\n <div className=\"flex shrink-0 items-center gap-2\">\n {isStreaming ? (\n <IconButton\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => chat.cancel()}\n aria-label={t('ai_assistant.chat.cancel', 'Cancel streaming response')}\n >\n <Square className=\"size-4\" aria-hidden />\n </IconButton>\n ) : null}\n <Button\n type=\"submit\"\n size=\"sm\"\n disabled={\n isBusy ||\n isUploading ||\n hasUploadingFiles ||\n input.trim().length === 0\n }\n aria-label={\n hasUploadingFiles || isUploading\n ? t('ai_assistant.chat.sendWaitingForUpload', 'Waiting for upload to finish\u2026')\n : t('ai_assistant.chat.send', 'Send message')\n }\n title={\n hasUploadingFiles || isUploading\n ? t('ai_assistant.chat.sendWaitingForUpload', 'Waiting for upload to finish\u2026')\n : undefined\n }\n className={cn(isCompactFooter && 'w-8 px-0')}\n >\n <Send className=\"size-4\" aria-hidden />\n {!isCompactFooter ? (\n <span>{t('ai_assistant.chat.send', 'Send message')}</span>\n ) : null}\n </Button>\n </div>\n </div>\n </form>\n\n {debug ? (\n <AiChatDebugPanel\n tools={debugTools}\n promptSections={debugPromptSections}\n loopTrace={loopTrace ?? chat.lastLoopTrace ?? undefined}\n lastRequestDebug={chat.lastRequestDebug}\n lastResponseDebug={chat.lastResponseDebug}\n status={chat.status}\n errorCode={chat.error?.code}\n />\n ) : null}\n </section>\n )\n}\n\ninterface DebugPanelProps {\n tools?: AiChatDebugTool[]\n promptSections?: AiChatDebugPromptSection[]\n loopTrace?: LoopTracePanelTrace\n lastRequestDebug: { url: string; body: unknown } | null\n lastResponseDebug: { status: number; text: string } | null\n status: 'idle' | 'submitting' | 'streaming'\n errorCode?: string\n}\n\nfunction AiChatDebugPanel({\n tools,\n promptSections,\n loopTrace,\n lastRequestDebug,\n lastResponseDebug,\n status,\n errorCode,\n}: DebugPanelProps) {\n const t = useT()\n return (\n <div\n className=\"flex flex-col gap-2 rounded-md border border-border bg-muted/60 p-2 text-xs\"\n data-ai-chat-debug=\"true\"\n >\n <div className=\"font-semibold\">\n {t('ai_assistant.chat.debug.panelTitle', 'Debug panel')}\n </div>\n\n {loopTrace ? (\n <LoopTracePanel trace={loopTrace} />\n ) : null}\n\n <details className=\"rounded border border-border bg-background\" data-ai-chat-debug-section=\"tools\" open>\n <summary className=\"cursor-pointer px-2 py-1 font-semibold\">\n {t('ai_assistant.chat.debug.toolsSection', 'Resolved tools')}\n {tools ? (\n <span className=\"ml-2 font-mono text-muted-foreground\">({tools.length})</span>\n ) : null}\n </summary>\n <div className=\"px-2 pb-2\">\n {tools && tools.length > 0 ? (\n <ul className=\"flex flex-col gap-1\" data-ai-chat-debug-tools>\n {tools.map((tool) => (\n <li\n key={tool.name}\n className=\"flex flex-col rounded border border-border bg-muted/40 px-2 py-1\"\n data-ai-chat-debug-tool={tool.name}\n >\n <span className=\"font-mono\">{tool.name}</span>\n {tool.displayName ? (\n <span className=\"text-muted-foreground\">{tool.displayName}</span>\n ) : null}\n <span className=\"mt-1 flex flex-wrap gap-2 text-muted-foreground\">\n <span>\n {tool.isMutation\n ? t('ai_assistant.chat.debug.toolMutation', 'mutation')\n : t('ai_assistant.chat.debug.toolRead', 'read')}\n </span>\n {tool.requiredFeatures && tool.requiredFeatures.length > 0 ? (\n <span className=\"font-mono\">\n [{tool.requiredFeatures.join(', ')}]\n </span>\n ) : (\n <span>\n {t('ai_assistant.chat.debug.toolNoFeatures', 'no required features')}\n </span>\n )}\n </span>\n </li>\n ))}\n </ul>\n ) : (\n <p className=\"text-muted-foreground\">\n {t(\n 'ai_assistant.chat.debug.toolsEmpty',\n 'No tools resolved for this agent yet.',\n )}\n </p>\n )}\n </div>\n </details>\n\n <details\n className=\"rounded border border-border bg-background\"\n data-ai-chat-debug-section=\"promptSections\"\n >\n <summary className=\"cursor-pointer px-2 py-1 font-semibold\">\n {t('ai_assistant.chat.debug.promptSection', 'Prompt sections')}\n {promptSections ? (\n <span className=\"ml-2 font-mono text-muted-foreground\">({promptSections.length})</span>\n ) : null}\n </summary>\n <div className=\"px-2 pb-2\">\n {promptSections && promptSections.length > 0 ? (\n <ul className=\"flex flex-col gap-1\" data-ai-chat-debug-prompt-sections>\n {promptSections.map((section) => (\n <li\n key={section.id}\n className=\"rounded border border-border bg-muted/40 px-2 py-1\"\n data-ai-chat-debug-prompt-section-id={section.id}\n >\n <div className=\"flex items-center justify-between gap-2\">\n <span className=\"font-mono\">{section.id}</span>\n <span className=\"text-muted-foreground\">\n {section.source === 'override'\n ? t('ai_assistant.chat.debug.promptOverride', 'override')\n : section.source === 'placeholder'\n ? t('ai_assistant.chat.debug.promptPlaceholder', 'placeholder')\n : t('ai_assistant.chat.debug.promptDefault', 'default')}\n </span>\n </div>\n {section.text ? (\n <pre className=\"mt-1 max-h-24 overflow-auto whitespace-pre-wrap font-mono text-muted-foreground\">\n {section.text}\n </pre>\n ) : null}\n </li>\n ))}\n </ul>\n ) : (\n <p className=\"text-muted-foreground\">\n {t(\n 'ai_assistant.chat.debug.promptEmpty',\n 'No prompt sections resolved for this agent.',\n )}\n </p>\n )}\n </div>\n </details>\n\n <details\n className=\"rounded border border-border bg-background\"\n data-ai-chat-debug-section=\"lastRequest\"\n >\n <summary className=\"cursor-pointer px-2 py-1 font-semibold\">\n {t('ai_assistant.chat.debug.lastRequestSection', 'Last request')}\n </summary>\n <div className=\"px-2 pb-2\">\n {lastRequestDebug ? (\n <pre\n className=\"max-h-40 overflow-auto whitespace-pre-wrap font-mono\"\n data-ai-chat-debug-last-request\n >\n {JSON.stringify(lastRequestDebug, null, 2)}\n </pre>\n ) : (\n <p className=\"text-muted-foreground\">\n {t(\n 'ai_assistant.chat.debug.lastRequestEmpty',\n 'No request has been sent yet.',\n )}\n </p>\n )}\n </div>\n </details>\n\n <details\n className=\"rounded border border-border bg-background\"\n data-ai-chat-debug-section=\"lastResponse\"\n >\n <summary className=\"cursor-pointer px-2 py-1 font-semibold\">\n {t('ai_assistant.chat.debug.lastResponseSection', 'Last response')}\n </summary>\n <div className=\"px-2 pb-2\">\n {lastResponseDebug ? (\n <pre\n className=\"max-h-40 overflow-auto whitespace-pre-wrap font-mono\"\n data-ai-chat-debug-last-response\n >\n {JSON.stringify(\n { status: lastResponseDebug.status, text: lastResponseDebug.text, errorCode },\n null,\n 2,\n )}\n </pre>\n ) : (\n <p className=\"text-muted-foreground\">\n {t(\n 'ai_assistant.chat.debug.lastResponseEmpty',\n 'No response received yet.',\n )}\n </p>\n )}\n </div>\n </details>\n\n <div className=\"text-muted-foreground\" data-ai-chat-debug-status={status}>\n {t('ai_assistant.chat.debug.statusLabel', 'Status:')}{' '}\n <span className=\"font-mono\">{status}</span>\n </div>\n </div>\n )\n}\n\nexport default AiChat\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport {\n Activity,\n AlertTriangle,\n BadgeQuestionMark,\n Bot,\n ChartNoAxesCombined,\n Check,\n ChevronDown,\n ChevronRight,\n Circle,\n CircleAlert,\n CircleCheck,\n CircleDashed,\n Copy,\n Database,\n Download,\n FileQuestion,\n GitBranch,\n Handshake,\n Lightbulb,\n ListChecks,\n Loader2,\n Maximize2,\n Paperclip,\n Plus,\n Search,\n Send,\n Square,\n TrendingDown,\n User,\n Wrench,\n X,\n} from 'lucide-react'\nimport ReactMarkdown from 'react-markdown'\nimport remarkGfm from 'remark-gfm'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { Alert, AlertDescription, AlertTitle } from '../primitives/alert'\nimport { Button } from '../primitives/button'\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogHeader,\n DialogTitle,\n} from '../primitives/dialog'\nimport { IconButton } from '../primitives/icon-button'\nimport { Label } from '../primitives/label'\nimport { Textarea } from '../primitives/textarea'\nimport { apiCall } from '../backend/utils/apiCall'\nimport { parseAiContentSegments } from './AiMessageContent'\nimport {\n ModelPicker,\n type ModelPickerProvider,\n type ModelPickerValue,\n} from './ModelPicker'\nimport { RecordCard } from './records/RecordCard'\nimport {\n defaultAiUiPartRegistry,\n isReservedAiUiPartId,\n type AiUiPartComponentId,\n type AiUiPartRegistry,\n} from './ui-part-registry'\nimport {\n useAiChat,\n type AiAgentTaskSnapshot,\n type AiChatMessage,\n type AiChatMessageFile,\n type AiChatMessageUiPart,\n type AiChatToolCallSnapshot,\n} from './useAiChat'\nimport { useAiChatUpload } from './useAiChatUpload'\nimport { useAiShortcuts } from './useAiShortcuts'\nimport { LoopTracePanel, type LoopTracePanelTrace } from './LoopTracePanel'\nimport { AiIcon } from './AiIcon'\nimport { readModelPickerValue, writeModelPickerValue } from './modelPickerStorage'\n\n// Cap inline previews so we do not blow past localStorage quota (~5MB on most\n// browsers). Images larger than this still upload + send to the LLM as inline\n// base64 server-side; only the in-chat preview is dropped on reload.\nconst PREVIEW_DATA_URL_MAX_BYTES = 2 * 1024 * 1024\nconst COMPACT_FOOTER_MAX_WIDTH = 640\n\ninterface ModelsApiResponse {\n agentId: string\n allowRuntimeOverride: boolean\n allowRuntimeModelOverride?: boolean\n defaultProviderId: string | null\n defaultModelId: string | null\n defaultProviderName?: string | null\n defaultModelName?: string | null\n providers: ModelPickerProvider[]\n}\n\nfunction useAgentModels(agent: string): {\n providers: ModelPickerProvider[]\n allowRuntimeOverride: boolean\n allowRuntimeModelOverride: boolean\n defaultLabel: string | null\n loaded: boolean\n} {\n const [providers, setProviders] = React.useState<ModelPickerProvider[]>([])\n const [allowRuntimeOverride, setAllowRuntimeOverride] = React.useState(false)\n const [defaultLabel, setDefaultLabel] = React.useState<string | null>(null)\n const [loaded, setLoaded] = React.useState(false)\n\n React.useEffect(() => {\n const modelsUrl = `/api/ai_assistant/ai/agents/${encodeURIComponent(agent)}/models`\n setLoaded(false)\n setDefaultLabel(null)\n void apiCall<ModelsApiResponse>(modelsUrl).then((result) => {\n if (!result.ok || !result.result) {\n setLoaded(true)\n return\n }\n const effectiveAllowRuntimeOverride =\n result.result.allowRuntimeOverride ?? result.result.allowRuntimeModelOverride ?? false\n setAllowRuntimeOverride(effectiveAllowRuntimeOverride)\n setProviders(result.result.providers)\n setDefaultLabel(\n result.result.defaultProviderName && result.result.defaultModelName\n ? `${result.result.defaultProviderName} / ${result.result.defaultModelName}`\n : result.result.defaultProviderId && result.result.defaultModelId\n ? `${result.result.defaultProviderId} / ${result.result.defaultModelId}`\n : null,\n )\n setLoaded(true)\n })\n }, [agent])\n\n return {\n providers,\n allowRuntimeOverride,\n allowRuntimeModelOverride: allowRuntimeOverride,\n defaultLabel,\n loaded,\n }\n}\n\nfunction isModelPickerValueAvailable(\n value: ModelPickerValue,\n providers: ModelPickerProvider[],\n): boolean {\n return providers.some(\n (provider) =>\n provider.id === value.providerId &&\n provider.models.some((model) => model.id === value.modelId),\n )\n}\n\nasync function readFileAsDataUrl(file: File): Promise<string | undefined> {\n if (!file.type.startsWith('image/')) return undefined\n if (file.size > PREVIEW_DATA_URL_MAX_BYTES) return undefined\n return new Promise<string | undefined>((resolve) => {\n const reader = new FileReader()\n reader.onload = () =>\n resolve(typeof reader.result === 'string' ? reader.result : undefined)\n reader.onerror = () => resolve(undefined)\n try {\n reader.readAsDataURL(file)\n } catch {\n resolve(undefined)\n }\n })\n}\n\n/**\n * Optional resolved-tool snapshot the host can feed into the debug panel.\n * Step 4.6 wires this from the `GET /api/ai_assistant/ai/agents` response\n * (`tools[]`). Step 5.3+ will replace the manual wiring with a streamed\n * `debug` part once the dispatcher emits one.\n */\nexport interface AiChatDebugTool {\n name: string\n displayName?: string\n isMutation?: boolean\n requiredFeatures?: string[]\n}\n\n/**\n * Resolved prompt-section snapshot for the debug panel. Until Phase 3 Step\n * 5.3 lands structured `PromptTemplate.sections`, hosts synthesise this\n * from the agent's `systemPrompt` + additive overrides.\n */\nexport interface AiChatDebugPromptSection {\n id: string\n source?: 'default' | 'override' | 'placeholder'\n text?: string\n}\n\n/** Quick-action suggestion shown in the welcome state. */\nexport interface AiChatSuggestion {\n label: string\n prompt: string\n icon?: React.ReactNode\n}\n\n/** Context item displayed as a chip/pill in the chat header. */\nexport interface AiChatContextItem {\n label: string\n detail?: string\n}\n\nexport interface AiChatProps {\n agent: string\n apiPath?: string\n pageContext?: Record<string, unknown>\n attachmentIds?: string[]\n initialMessages?: Array<{ role: 'user' | 'assistant'; content: string }>\n debug?: boolean\n className?: string\n placeholder?: string\n onMutationRequested?: (pendingActionId: string) => void\n onError?: (err: { code?: string; message: string }) => void\n /**\n * Optional stable conversation id. Forwarded verbatim to\n * `POST /api/ai_assistant/ai/chat` request bodies and reused across\n * turns so the Step 5.6 `prepareMutation` idempotency hash stays\n * stable across retries within the same chat. When omitted, the hook\n * mints a fresh random id once on mount \u2014 remounting the component\n * resets the conversation.\n */\n conversationId?: string\n /**\n * Optional UI-part registry. Defaults to the module-global\n * {@link defaultAiUiPartRegistry}. Pass a scoped registry from\n * {@link createAiUiPartRegistry} when embedding multiple `<AiChat>`\n * instances that should not share registrations (playground, tests).\n */\n registry?: AiUiPartRegistry\n /**\n * Optional list of server-emitted UI parts to render inside the chat\n * transcript. The registry resolves each part via `componentId`. Phase 3\n * will populate this from the streamed dispatcher response; Phase 2 WS-A\n * leaves the wiring exposed so hosts can preview the registry path.\n */\n uiParts?: Array<{\n componentId: AiUiPartComponentId\n payload?: unknown\n pendingActionId?: string\n }>\n /**\n * Optional resolved-tool map for the debug panel. Ignored when\n * `debug` is falsy.\n */\n debugTools?: AiChatDebugTool[]\n /**\n * Optional resolved prompt sections for the debug panel. Ignored when\n * `debug` is falsy.\n */\n debugPromptSections?: AiChatDebugPromptSection[]\n /**\n * Optional loop trace from the last completed turn. When present and\n * `debug` is truthy, renders a `LoopTracePanel` inside the debug panel.\n * Populated by the host from the dispatcher SSE `loop-finish` event\n * (wired in l4.4). Ignored when `debug` is falsy.\n */\n loopTrace?: LoopTracePanelTrace\n /** Suggested prompts shown in the empty / welcome state. */\n suggestions?: AiChatSuggestion[]\n /** Context items shown as pills above the transcript (e.g. selected products). */\n contextItems?: AiChatContextItem[]\n /** Welcome heading shown when there are no messages yet. */\n welcomeTitle?: string\n /** Welcome description shown below the heading. */\n welcomeDescription?: string\n /** Initial compact composer state used before the footer has been measured. */\n defaultCompactFooter?: boolean\n /** Optional extra action nodes rendered in the conversation header bar. */\n headerActions?: React.ReactNode\n /** Called once on mount (and on reset) with the effective conversation id. */\n onConversationIdChange?: (conversationId: string) => void\n /**\n * Called when the server returns 404 for the active `conversationId`,\n * e.g. when the id was carried over from a previous tenant/org scope\n * and no longer exists. The host (typically the AI dock) should remove\n * the corresponding session entry so the stale tab disappears instead\n * of staying empty.\n */\n onConversationNotFound?: () => void\n}\n\ninterface ServerEmittedUiPartRef {\n componentId: AiUiPartComponentId\n payload?: unknown\n pendingActionId?: string\n}\n\nfunction mapErrorCodeToVariant(\n code: string | undefined,\n): 'destructive' | 'warning' {\n if (!code) return 'destructive'\n // Policy denies that describe a filtered tool or attachment surface a\n // warning alert; caller can still continue. Hard denials (agent_unknown,\n // agent_features_denied, unauthenticated, execution_mode_not_supported,\n // mutation_blocked_by_*, validation_error) surface destructive alerts.\n const warningCodes = new Set<string>([\n 'tool_not_whitelisted',\n 'tool_features_denied',\n 'attachment_type_not_accepted',\n ])\n return warningCodes.has(code) ? 'warning' : 'destructive'\n}\n\nconst MARKDOWN_TYPOGRAPHY_CLASS = cn(\n '[&_p]:my-1 [&_p:first-child]:mt-0 [&_p:last-child]:mb-0',\n '[&_ul]:my-2 [&_ol]:my-2 [&_ul]:ml-4 [&_ol]:ml-4 [&_ul]:list-disc [&_ol]:list-decimal',\n '[&_li]:my-0.5',\n '[&_h1]:mt-3 [&_h1]:mb-2 [&_h1]:text-base [&_h1]:font-semibold',\n '[&_h2]:mt-3 [&_h2]:mb-2 [&_h2]:text-sm [&_h2]:font-semibold',\n '[&_h3]:mt-2 [&_h3]:mb-1 [&_h3]:text-sm [&_h3]:font-semibold',\n '[&_a]:text-primary [&_a]:underline [&_a]:underline-offset-2',\n '[&_code]:rounded [&_code]:bg-muted [&_code]:px-1 [&_code]:py-0.5 [&_code]:font-mono [&_code]:text-xs',\n '[&_pre]:my-2 [&_pre]:overflow-x-auto [&_pre]:rounded-md [&_pre]:border [&_pre]:border-border [&_pre]:bg-muted [&_pre]:p-3',\n '[&_pre_code]:bg-transparent [&_pre_code]:p-0',\n '[&_blockquote]:my-2 [&_blockquote]:border-l-2 [&_blockquote]:border-border [&_blockquote]:pl-3 [&_blockquote]:text-muted-foreground',\n '[&_table]:my-2 [&_table]:w-full [&_table]:border-collapse [&_table]:text-xs',\n '[&_th]:border [&_th]:border-border [&_th]:px-2 [&_th]:py-1 [&_th]:text-left [&_th]:font-medium',\n '[&_td]:border [&_td]:border-border [&_td]:px-2 [&_td]:py-1',\n)\n\nconst MARKDOWN_COMPONENTS = {\n a: ({ node, ...props }: React.AnchorHTMLAttributes<HTMLAnchorElement> & { node?: unknown }) => (\n <a {...props} target=\"_blank\" rel=\"noreferrer\" />\n ),\n}\n\nfunction MarkdownChunk({ text }: { text: string }) {\n if (!text.trim()) return null\n return (\n <div className={cn('text-sm', MARKDOWN_TYPOGRAPHY_CLASS)}>\n <ReactMarkdown remarkPlugins={[remarkGfm]} components={MARKDOWN_COMPONENTS}>\n {text}\n </ReactMarkdown>\n </div>\n )\n}\n\nfunction MessageContent({ content, isAssistant }: { content: string; isAssistant: boolean }) {\n if (!isAssistant) {\n return <div className=\"whitespace-pre-wrap text-sm\">{content}</div>\n }\n if (!content) {\n return null\n }\n const segments = parseAiContentSegments(content)\n if (segments.length === 0) {\n return null\n }\n return (\n <div className=\"space-y-1\" data-ai-message-content=\"\">\n {segments.map((segment, index) => {\n if (segment.kind === 'record-card') {\n return <RecordCard key={`card-${index}`} data={segment.payload} />\n }\n if (segment.kind === 'invalid-card') {\n return (\n <pre\n key={`raw-${index}`}\n className=\"my-2 max-h-60 overflow-auto rounded-md border border-dashed border-border bg-muted p-2 text-xs\"\n data-ai-record-card-invalid={segment.info}\n >\n {segment.raw}\n </pre>\n )\n }\n return <MarkdownChunk key={`md-${index}`} text={segment.text} />\n })}\n </div>\n )\n}\n\nfunction safeStringify(value: unknown): string {\n if (value === null || value === undefined) return ''\n if (typeof value === 'string') return value\n try {\n return JSON.stringify(value, null, 2)\n } catch {\n return String(value)\n }\n}\n\nfunction titleWords(value: string): string {\n return value.replace(/\\b[a-z]/g, (char) => char.toUpperCase())\n}\n\nfunction formatToolCaption(call: AiChatToolCallSnapshot): string {\n const rawCaption = call.caption?.trim()\n if (!rawCaption) return call.toolName\n const caption = titleWords(rawCaption.replace(/\\s*\u00B7\\s*/g, ' - '))\n return `${caption} (${call.toolName})`\n}\n\n/**\n * Visible agent task plan rendered above raw tool-call rows. Phase 1\n * implementation of spec `.ai/specs/implemented/2026-05-13-ai-chat-visible-task-plan.md`:\n * compact, fixed-height rows with icon + label + status badge so the\n * panel does not jump while streaming. Source of truth is the\n * client-local `taskPlan` array on the assistant message.\n */\nfunction AiChatTaskPlan({ tasks }: { tasks: AiAgentTaskSnapshot[] }) {\n const t = useT()\n if (!tasks || tasks.length === 0) return null\n return (\n <div\n className=\"rounded-md border border-border bg-muted/30\"\n data-ai-chat-task-plan=\"\"\n data-ai-chat-task-plan-count={tasks.length}\n >\n <div className=\"flex items-center gap-2 border-b border-border/60 px-2 py-1.5 text-[11px] font-medium uppercase tracking-wide text-muted-foreground\">\n <ListChecks className=\"size-3.5 text-muted-foreground\" aria-hidden />\n <span>{t('ai_assistant.chat.taskPlanTitle', 'Plan')}</span>\n <span className=\"ml-auto font-mono\">{tasks.length}</span>\n </div>\n <ul className=\"divide-y divide-border/60\" data-ai-chat-task-plan-list=\"\">\n {tasks.map((task) => (\n <AiChatTaskRow key={task.id} task={task} />\n ))}\n </ul>\n </div>\n )\n}\n\nfunction AiChatTaskRow({ task }: { task: AiAgentTaskSnapshot }) {\n const t = useT()\n const statusLabel =\n task.state === 'running'\n ? t('ai_assistant.chat.taskRunning', 'running\u2026')\n : task.state === 'done'\n ? t('ai_assistant.chat.taskDone', 'done')\n : task.state === 'failed'\n ? t('ai_assistant.chat.taskFailed', 'failed')\n : task.state === 'skipped'\n ? t('ai_assistant.chat.taskSkipped', 'skipped')\n : t('ai_assistant.chat.taskPending', 'pending')\n const Icon =\n task.state === 'running'\n ? Loader2\n : task.state === 'done'\n ? CircleCheck\n : task.state === 'failed'\n ? CircleAlert\n : task.state === 'skipped'\n ? CircleDashed\n : Circle\n const iconClassName = cn(\n 'size-3.5 shrink-0',\n task.state === 'running' && 'animate-spin text-muted-foreground',\n task.state === 'done' && 'text-status-success-icon',\n task.state === 'failed' && 'text-destructive',\n task.state === 'skipped' && 'text-muted-foreground',\n task.state === 'pending' && 'text-muted-foreground',\n )\n const statusBadgeClassName = cn(\n 'ml-auto text-[10px] uppercase tracking-wide',\n task.state === 'done' && 'text-status-success-text',\n task.state === 'failed' && 'text-destructive',\n task.state !== 'done' && task.state !== 'failed' && 'text-muted-foreground',\n )\n return (\n <li\n className=\"flex h-7 items-center gap-2 px-2 text-xs\"\n data-ai-chat-task-id={task.id}\n data-ai-chat-task-state={task.state}\n data-ai-chat-task-source={task.source}\n data-ai-chat-task-tool-call-id={task.toolCallId}\n >\n <Icon className={iconClassName} aria-hidden />\n <span className=\"truncate\" title={task.detail ?? task.label}>\n {task.label}\n </span>\n <span className={statusBadgeClassName}>{statusLabel}</span>\n </li>\n )\n}\n\nfunction ToolCallList({ toolCalls }: { toolCalls: AiChatToolCallSnapshot[] }) {\n const t = useT()\n const [openId, setOpenId] = React.useState<string | null>(null)\n if (!toolCalls || toolCalls.length === 0) return null\n return (\n <div\n className=\"rounded-md border border-border bg-muted/30\"\n data-ai-chat-tool-calls=\"\"\n data-ai-chat-tool-call-count={toolCalls.length}\n >\n <div className=\"flex items-center gap-2 border-b border-border/60 px-2 py-1.5 text-[11px] font-medium uppercase tracking-wide text-muted-foreground\">\n <Wrench className=\"size-3.5 text-muted-foreground\" aria-hidden />\n <span>{t('ai_assistant.chat.agentTasksTitle', 'Tool calls')}</span>\n <span className=\"ml-auto font-mono\">{toolCalls.length}</span>\n </div>\n <ul className=\"divide-y divide-border/60\">\n {toolCalls.map((call) => {\n const isOpen = openId === call.id\n const isError = call.state === 'error'\n const isPending = call.state === 'pending'\n const isComplete = call.state === 'complete'\n const statusLabel = isError\n ? t('ai_assistant.chat.toolError', 'failed')\n : isPending\n ? t('ai_assistant.chat.toolRunning', 'running\u2026')\n : t('ai_assistant.chat.toolDone', 'done')\n return (\n <li\n key={call.id}\n className={cn(\n isError ? 'bg-destructive/5' : '',\n )}\n data-ai-chat-tool-call={call.toolName}\n data-ai-chat-tool-state={call.state}\n >\n <Button\n variant=\"ghost\"\n size=\"2xs\"\n type=\"button\"\n onClick={() => setOpenId(isOpen ? null : call.id)}\n className=\"w-full justify-start rounded-none px-2 text-left hover:bg-muted/60\"\n >\n {isOpen ? (\n <ChevronDown className=\"size-3.5 text-muted-foreground\" aria-hidden />\n ) : (\n <ChevronRight className=\"size-3.5 text-muted-foreground\" aria-hidden />\n )}\n {isPending ? (\n <Loader2 className=\"size-3.5 animate-spin text-muted-foreground\" aria-hidden />\n ) : (\n <Wrench\n className={cn(\n 'size-3.5',\n isError ? 'text-destructive' : 'text-muted-foreground',\n )}\n aria-hidden\n />\n )}\n <span className=\"truncate\" title={formatToolCaption(call)}>\n {formatToolCaption(call)}\n </span>\n <span\n className={cn(\n 'ml-auto text-[10px] uppercase tracking-wide',\n isError\n ? 'text-destructive'\n : isComplete\n ? 'text-status-success-text'\n : 'text-muted-foreground',\n )}\n >\n {statusLabel}\n </span>\n </Button>\n {isOpen ? (\n <div className=\"space-y-1 border-t border-border/60 px-2 py-1.5 text-xs\">\n {call.input !== undefined ? (\n <div>\n <div className=\"text-[10px] uppercase tracking-wide text-muted-foreground\">\n {t('ai_assistant.chat.toolInput', 'Input')}\n </div>\n <pre className=\"mt-0.5 max-h-32 overflow-auto rounded bg-background p-1.5 font-mono text-[11px]\">\n {safeStringify(call.input)}\n </pre>\n </div>\n ) : null}\n {call.output !== undefined && !isError ? (\n <div>\n <div className=\"text-[10px] uppercase tracking-wide text-muted-foreground\">\n {t('ai_assistant.chat.toolOutput', 'Output')}\n </div>\n <pre className=\"mt-0.5 max-h-32 overflow-auto rounded bg-background p-1.5 font-mono text-[11px]\">\n {safeStringify(call.output)}\n </pre>\n </div>\n ) : null}\n {call.errorMessage ? (\n <div className=\"text-destructive\">{call.errorMessage}</div>\n ) : null}\n </div>\n ) : null}\n </li>\n )\n })}\n </ul>\n </div>\n )\n}\n\nfunction ReasoningPanel({ text, streaming }: { text: string; streaming: boolean }) {\n const t = useT()\n const [open, setOpen] = React.useState(false)\n if (!text) return null\n return (\n <div\n className=\"rounded-md border border-border bg-muted/30\"\n data-ai-chat-reasoning={streaming ? 'streaming' : 'complete'}\n >\n <button\n type=\"button\"\n onClick={() => setOpen((o) => !o)}\n className=\"flex w-full items-center gap-2 px-2 py-1.5 text-left text-xs font-medium hover:bg-muted/60\"\n >\n {open ? (\n <ChevronDown className=\"size-3.5 text-muted-foreground\" aria-hidden />\n ) : (\n <ChevronRight className=\"size-3.5 text-muted-foreground\" aria-hidden />\n )}\n <Lightbulb className=\"size-3.5 text-muted-foreground\" aria-hidden />\n <span>{t('ai_assistant.chat.reasoning', 'Reasoning')}</span>\n {streaming ? (\n <Loader2\n className=\"ml-1 size-3 animate-spin text-muted-foreground\"\n aria-hidden\n />\n ) : null}\n </button>\n {open ? (\n <pre className=\"max-h-48 overflow-auto whitespace-pre-wrap border-t border-border/60 px-2 py-1.5 text-xs text-muted-foreground\">\n {text}\n </pre>\n ) : null}\n </div>\n )\n}\n\nfunction getAttachmentImageUrl(file: AiChatMessageFile): string | null {\n if (file.id) return `/api/attachments/image/${encodeURIComponent(file.id)}`\n return file.previewUrl ?? null\n}\n\nfunction getAttachmentDownloadUrl(file: AiChatMessageFile): string | null {\n if (file.id) return `/api/attachments/file/${encodeURIComponent(file.id)}?download=1`\n return file.previewUrl ?? null\n}\n\nfunction MessageFileAttachment({ file }: { file: AiChatMessageFile }) {\n const t = useT()\n const [zoomOpen, setZoomOpen] = React.useState(false)\n const imageUrl = file.previewUrl ? getAttachmentImageUrl(file) : null\n const downloadUrl = getAttachmentDownloadUrl(file)\n const downloadLabel = t('ai_assistant.chat.downloadFile', 'Download {name}').replace(\n '{name}',\n file.name,\n )\n const zoomLabel = t('ai_assistant.chat.zoomImage', 'Open {name} preview').replace(\n '{name}',\n file.name,\n )\n\n if (!file.previewUrl) {\n return (\n <span className=\"inline-flex max-w-full items-center gap-1 rounded-full border border-border bg-muted px-2 py-0.5 text-xs\">\n <Paperclip className=\"size-3 shrink-0\" aria-hidden />\n <span className=\"max-w-[180px] truncate\">{file.name}</span>\n {downloadUrl ? (\n <IconButton\n asChild\n variant=\"ghost\"\n size=\"xs\"\n className=\"-mr-1 size-5 shrink-0\"\n >\n <a href={downloadUrl} download={file.name} aria-label={downloadLabel}>\n <Download className=\"size-3\" aria-hidden />\n </a>\n </IconButton>\n ) : null}\n </span>\n )\n }\n\n return (\n <>\n <div className=\"group/file relative max-w-full\">\n <button\n type=\"button\"\n className=\"block max-w-full rounded-md border border-border bg-muted/30 focus-visible:outline-none focus-visible:shadow-focus\"\n onClick={() => setZoomOpen(true)}\n aria-label={zoomLabel}\n data-ai-chat-image-preview=\"\"\n >\n <img\n src={file.previewUrl}\n alt={file.name}\n className=\"max-h-32 max-w-[200px] rounded-md object-cover\"\n />\n <span className=\"pointer-events-none absolute right-1 top-1 inline-flex size-6 items-center justify-center rounded-md bg-background/90 text-muted-foreground opacity-0 shadow-xs transition-opacity group-hover/file:opacity-100 group-focus-within/file:opacity-100\">\n <Maximize2 className=\"size-3.5\" aria-hidden />\n </span>\n </button>\n {downloadUrl ? (\n <IconButton\n asChild\n variant=\"white\"\n size=\"xs\"\n className=\"absolute bottom-1 right-1 opacity-0 shadow-xs transition-opacity group-hover/file:opacity-100 group-focus-within/file:opacity-100\"\n >\n <a\n href={downloadUrl}\n download={file.name}\n aria-label={downloadLabel}\n onClick={(event) => event.stopPropagation()}\n data-ai-chat-file-download=\"\"\n >\n <Download className=\"size-3.5\" aria-hidden />\n </a>\n </IconButton>\n ) : null}\n </div>\n\n <Dialog open={zoomOpen} onOpenChange={setZoomOpen}>\n <DialogContent className=\"gap-3 p-4 sm:max-w-[min(96vw,960px)]\">\n <DialogHeader className=\"min-w-0 pr-16\">\n <DialogTitle className=\"truncate text-sm\">{file.name}</DialogTitle>\n <DialogDescription className=\"sr-only\">\n {t('ai_assistant.chat.imagePreviewDialogDescription', 'Image preview')}\n </DialogDescription>\n </DialogHeader>\n <div className=\"relative flex min-h-0 items-center justify-center overflow-auto rounded-md bg-muted/30 p-2\">\n <img\n src={imageUrl ?? file.previewUrl}\n alt={file.name}\n className=\"max-h-[75vh] max-w-full object-contain\"\n />\n {downloadUrl ? (\n <IconButton\n asChild\n variant=\"white\"\n size=\"sm\"\n className=\"absolute bottom-3 right-3 shadow-xs\"\n >\n <a\n href={downloadUrl}\n download={file.name}\n aria-label={downloadLabel}\n data-ai-chat-file-download=\"\"\n >\n <Download className=\"size-4\" aria-hidden />\n </a>\n </IconButton>\n ) : null}\n </div>\n </DialogContent>\n </Dialog>\n </>\n )\n}\n\nfunction MessageRow({\n message,\n registry,\n onMutationRequested,\n isOwner,\n}: {\n message: AiChatMessage\n registry?: AiUiPartRegistry\n onMutationRequested?: (pendingActionId: string) => void\n /** Whether the current viewer owns the conversation. Used to label foreign user messages. */\n isOwner?: boolean | null\n}) {\n const t = useT()\n const isAssistant = message.role === 'assistant'\n const isOtherUsersMessage = !isAssistant && isOwner === false\n const label = isAssistant\n ? t('ai_assistant.chat.assistantRoleLabel', 'Assistant')\n : isOtherUsersMessage\n ? t('ai_assistant.chat.ownerRoleLabel', 'Owner')\n : t('ai_assistant.chat.userRoleLabel', 'You')\n const Icon = isAssistant ? Bot : User\n const [copied, setCopied] = React.useState(false)\n const copyTimerRef = React.useRef<ReturnType<typeof setTimeout> | null>(null)\n const visibleTaskPlan =\n isAssistant && message.taskPlan\n ? message.taskPlan.filter((task) => task.source === 'agent')\n : []\n\n React.useEffect(() => {\n return () => {\n if (copyTimerRef.current) clearTimeout(copyTimerRef.current)\n }\n }, [])\n\n const handleCopy = React.useCallback(async () => {\n const text = message.content\n if (!text) return\n try {\n if (typeof navigator !== 'undefined' && navigator.clipboard?.writeText) {\n await navigator.clipboard.writeText(text)\n } else {\n const textarea = document.createElement('textarea')\n textarea.value = text\n textarea.setAttribute('readonly', '')\n textarea.style.position = 'absolute'\n textarea.style.left = '-9999px'\n document.body.appendChild(textarea)\n textarea.select()\n document.execCommand('copy')\n document.body.removeChild(textarea)\n }\n setCopied(true)\n if (copyTimerRef.current) clearTimeout(copyTimerRef.current)\n copyTimerRef.current = setTimeout(() => setCopied(false), 1500)\n } catch {\n // Clipboard API blocked (no permission, http context) \u2014 silently fail.\n }\n }, [message.content])\n\n return (\n <div\n className={cn(\n 'group/message flex gap-3 px-3 py-2',\n isAssistant ? 'bg-muted/40 rounded-md' : '',\n )}\n data-role={message.role}\n >\n <div\n className={cn(\n 'flex size-6 shrink-0 items-center justify-center rounded-full',\n isAssistant\n ? 'bg-primary/10 text-primary'\n : 'bg-secondary text-secondary-foreground',\n )}\n aria-hidden\n >\n <Icon className=\"size-4\" />\n </div>\n <div className=\"flex-1 space-y-1\">\n <div className=\"flex items-center justify-between gap-2\">\n <div className=\"text-xs font-medium text-muted-foreground\">{label}</div>\n {message.content ? (\n <IconButton\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n onClick={handleCopy}\n aria-label={\n copied\n ? t('ai_assistant.chat.copied', 'Copied')\n : t('ai_assistant.chat.copyMessage', 'Copy message')\n }\n data-ai-chat-copy-button=\"\"\n className=\"opacity-0 transition-opacity group-hover/message:opacity-100 focus-visible:opacity-100\"\n >\n {copied ? (\n <Check className=\"size-3.5 text-status-success-icon\" aria-hidden />\n ) : (\n <Copy className=\"size-3.5\" aria-hidden />\n )}\n </IconButton>\n ) : null}\n </div>\n {message.files && message.files.length > 0 ? (\n <div className=\"flex flex-wrap gap-2 py-1\">\n {message.files.map((file, i) => (\n <MessageFileAttachment key={`${file.id ?? file.name}:${i}`} file={file} />\n ))}\n </div>\n ) : null}\n {isAssistant && message.reasoning ? (\n <ReasoningPanel\n text={message.reasoning}\n streaming={message.reasoningStreaming === true}\n />\n ) : null}\n {visibleTaskPlan.length > 0 ? (\n <AiChatTaskPlan tasks={visibleTaskPlan} />\n ) : null}\n {isAssistant && message.toolCalls && message.toolCalls.length > 0 ? (\n <ToolCallList toolCalls={message.toolCalls} />\n ) : null}\n <MessageContent content={message.content} isAssistant={isAssistant} />\n {isAssistant && registry && message.uiParts && message.uiParts.length > 0 ? (\n <MessageUiParts\n parts={message.uiParts}\n registry={registry}\n onMutationRequested={onMutationRequested}\n />\n ) : null}\n </div>\n </div>\n )\n}\n\nfunction MessageUiParts({\n parts,\n registry,\n onMutationRequested,\n}: {\n parts: AiChatMessageUiPart[]\n registry: AiUiPartRegistry\n onMutationRequested?: (pendingActionId: string) => void\n}) {\n return (\n <div className=\"mt-2 flex flex-col gap-2\" data-ai-message-ui-parts=\"\">\n {parts.map((part) => (\n <AiUiPartRenderer\n key={part.key}\n part={{\n componentId: part.componentId as AiUiPartComponentId,\n payload: part.payload,\n pendingActionId: part.pendingActionId,\n }}\n registry={registry}\n onMutationRequested={onMutationRequested}\n />\n ))}\n </div>\n )\n}\n\nfunction UnknownUiPartPlaceholder({ componentId }: { componentId: AiUiPartComponentId }) {\n const t = useT()\n return (\n <div\n className=\"inline-flex items-center gap-2 rounded-full border border-dashed border-border bg-muted px-3 py-1 text-xs text-muted-foreground\"\n data-ai-ui-part-placeholder={componentId}\n >\n <span>\n {t('ai_assistant.chat.uiPartPending', 'Pending UI part:')} {componentId}\n </span>\n </div>\n )\n}\n\nfunction AiUiPartRenderer({\n part,\n registry,\n onMutationRequested,\n}: {\n part: ServerEmittedUiPartRef\n registry: AiUiPartRegistry\n onMutationRequested?: (pendingActionId: string) => void\n}) {\n const Component = registry.resolve(part.componentId)\n const isReserved = isReservedAiUiPartId(part.componentId)\n React.useEffect(() => {\n if (Component) return\n if (isReserved) return\n try {\n // eslint-disable-next-line no-console\n console.warn(\n `[AiChat] No component registered for UI part \"${part.componentId}\".`,\n )\n } catch {\n // noop\n }\n }, [Component, part.componentId, isReserved])\n React.useEffect(() => {\n if (part.pendingActionId) {\n onMutationRequested?.(part.pendingActionId)\n }\n }, [part.pendingActionId, onMutationRequested])\n if (!Component) {\n return <UnknownUiPartPlaceholder componentId={part.componentId} />\n }\n return (\n <Component\n componentId={part.componentId}\n payload={part.payload}\n pendingActionId={part.pendingActionId}\n />\n )\n}\n\nfunction getSuggestionIcon(suggestion: AiChatSuggestion): {\n icon: React.ReactNode\n name: string\n} {\n if (suggestion.icon) return { icon: suggestion.icon, name: 'custom' }\n const text = `${suggestion.label} ${suggestion.prompt}`.toLowerCase()\n const iconClassName = 'size-4 text-foreground'\n if (/\\b(analy[sz]e|analysis|health|score|summary|summari[sz]e)\\b/.test(text)) {\n return { icon: <ChartNoAxesCombined className={iconClassName} />, name: 'analysis' }\n }\n if (/\\b(at[-\\s]?risk|risk|stalled|stale|overdue|danger)\\b/.test(text)) {\n return { icon: <AlertTriangle className={iconClassName} />, name: 'risk' }\n }\n if (/\\bpipeline|stage|funnel\\b/.test(text)) {\n return { icon: <GitBranch className={iconClassName} />, name: 'pipeline' }\n }\n if (/\\bdeal|opportunit(?:y|ies)\\b/.test(text)) {\n return { icon: <Handshake className={iconClassName} />, name: 'deal' }\n }\n if (/\\bdata|access|record|records|schema\\b/.test(text)) {\n return { icon: <Database className={iconClassName} />, name: 'data' }\n }\n if (/\\bwhat can you help|help me|capabilit(?:y|ies)\\b/.test(text)) {\n return { icon: <BadgeQuestionMark className={iconClassName} />, name: 'capabilities' }\n }\n if (/\\bsuggest|try|idea|ideas|recommend\\b/.test(text)) {\n return { icon: <Lightbulb className={iconClassName} />, name: 'ideas' }\n }\n if (/\\bhow do i|how to|use this|instructions?|guide\\b/.test(text)) {\n return { icon: <FileQuestion className={iconClassName} />, name: 'guide' }\n }\n if (/\\bfind|search|show|list|lookup\\b/.test(text)) {\n return { icon: <Search className={iconClassName} />, name: 'search' }\n }\n if (/\\bactivity|activities|timeline|recent\\b/.test(text)) {\n return { icon: <Activity className={iconClassName} />, name: 'activity' }\n }\n if (/\\bdown|drop|declin|loss|lost\\b/.test(text)) {\n return { icon: <TrendingDown className={iconClassName} />, name: 'trend-down' }\n }\n return { icon: <AiIcon className={iconClassName} />, name: 'ai' }\n}\n\nfunction WelcomeState({\n title,\n description,\n suggestions,\n onSuggestionClick,\n}: {\n title?: string\n description?: string\n suggestions?: AiChatSuggestion[]\n onSuggestionClick: (prompt: string) => void\n}) {\n const t = useT()\n const heading = title ?? t('ai_assistant.chat.welcomeTitle', 'How can I help?')\n const desc =\n description ??\n t(\n 'ai_assistant.chat.welcomeDescription',\n 'Ask me anything about your data. Here are some things I can do:',\n )\n return (\n <div className=\"flex flex-1 flex-col items-center justify-center gap-4 px-4 py-8\">\n <div\n className=\"flex size-12 items-center justify-center rounded-full bg-primary/10 text-primary\"\n aria-hidden\n >\n <Bot className=\"size-6\" />\n </div>\n <div className=\"space-y-1 text-center\">\n <h3 className=\"text-sm font-semibold\">{heading}</h3>\n <p className=\"text-xs text-muted-foreground\">{desc}</p>\n </div>\n {suggestions && suggestions.length > 0 ? (\n <div className=\"flex w-full max-w-md flex-col gap-2\" data-ai-chat-suggestions=\"\">\n {suggestions.map((suggestion, index) => {\n const suggestionIcon = getSuggestionIcon(suggestion)\n return (\n <Button\n variant=\"outline\"\n key={index}\n type=\"button\"\n className=\"h-auto w-full justify-start whitespace-normal rounded-lg bg-card px-3 py-2.5 text-left text-sm\"\n onClick={() => onSuggestionClick(suggestion.prompt)}\n data-ai-chat-suggestion={index}\n data-ai-chat-suggestion-icon={suggestionIcon.name}\n >\n <span className=\"shrink-0 text-muted-foreground\" aria-hidden>\n {suggestionIcon.icon}\n </span>\n <span>{suggestion.label}</span>\n </Button>\n )\n })}\n </div>\n ) : null}\n </div>\n )\n}\n\nfunction ContextItemsPill({ items }: { items: AiChatContextItem[] }) {\n if (items.length === 0) return null\n return (\n <div\n className=\"flex flex-wrap gap-1.5 border-b border-border px-3 py-2\"\n data-ai-chat-context-items=\"\"\n >\n {items.map((item, index) => (\n <span\n key={index}\n className=\"inline-flex items-center gap-1 rounded-full border border-border bg-secondary px-2 py-0.5 text-xs text-secondary-foreground\"\n data-ai-chat-context-item={index}\n title={item.detail}\n >\n {item.label}\n </span>\n ))}\n </div>\n )\n}\n\n/**\n * Embeddable AI chat component. Binds to the dispatcher route\n * `POST /api/ai_assistant/ai/chat?agent=<module>.<agent>` via\n * {@link createAiAgentTransport}. Phase 2 WS-A deliverable (Step 4.1).\n *\n * - Keyboard: `Enter` submits; `Shift+Enter` inserts a newline; `Escape`\n * aborts streaming (or blurs the composer when idle).\n * - Error envelopes from the dispatcher surface as `Alert` + `onError`.\n * - UI parts render via the client-side registry; unknown parts render a\n * neutral placeholder chip so mutation-card slots reserved for Phase 3\n * never throw before their implementations land.\n */\nexport function AiChat({\n agent,\n apiPath,\n pageContext,\n attachmentIds,\n initialMessages,\n debug,\n className,\n placeholder,\n onMutationRequested,\n onError,\n registry,\n uiParts: uiPartsProp,\n debugTools,\n debugPromptSections,\n loopTrace,\n conversationId,\n suggestions,\n contextItems,\n welcomeTitle,\n welcomeDescription,\n defaultCompactFooter = false,\n headerActions,\n onConversationIdChange,\n onConversationNotFound,\n}: AiChatProps) {\n const t = useT()\n const onConversationIdChangeRef = React.useRef(onConversationIdChange)\n React.useLayoutEffect(() => {\n onConversationIdChangeRef.current = onConversationIdChange\n })\n const textareaRef = React.useRef<HTMLTextAreaElement | null>(null)\n const transcriptRef = React.useRef<HTMLDivElement | null>(null)\n const fileInputRef = React.useRef<HTMLInputElement | null>(null)\n const [input, setInput] = React.useState('')\n type PendingAttachment = {\n file: File\n attachmentId?: string\n /**\n * Base64 data URL of the image (capped by PREVIEW_DATA_URL_MAX_BYTES).\n * Stored on the local message for immediate previews; once the transcript\n * is loaded from server storage the attachment id is used to rebuild a\n * same-origin thumbnail URL.\n */\n previewDataUrl?: string\n error?: string\n }\n const [pendingFiles, setPendingFiles] = React.useState<PendingAttachment[]>([])\n const upload = useAiChatUpload()\n const isUploading = upload.busy\n const footerRef = React.useRef<HTMLDivElement | null>(null)\n const [isCompactFooter, setIsCompactFooter] = React.useState(defaultCompactFooter)\n\n React.useEffect(() => {\n const element = footerRef.current\n if (!element || typeof ResizeObserver === 'undefined') return\n\n const updateCompactState = (width: number) => {\n setIsCompactFooter(width < COMPACT_FOOTER_MAX_WIDTH)\n }\n\n updateCompactState(element.getBoundingClientRect().width)\n const observer = new ResizeObserver((entries) => {\n const entry = entries[0]\n if (!entry) return\n updateCompactState(entry.contentRect.width)\n })\n observer.observe(element)\n return () => observer.disconnect()\n }, [])\n\n const uploadedAttachmentIds = React.useMemo(\n () =>\n pendingFiles\n .map((entry) => entry.attachmentId)\n .filter((id): id is string => typeof id === 'string' && id.length > 0),\n [pendingFiles],\n )\n\n const allAttachmentIds = React.useMemo(\n () => [...(attachmentIds ?? []), ...uploadedAttachmentIds],\n [attachmentIds, uploadedAttachmentIds],\n )\n\n const {\n providers: modelProviders,\n allowRuntimeOverride,\n allowRuntimeModelOverride,\n defaultLabel: modelDefaultLabel,\n loaded: modelProvidersLoaded,\n } = useAgentModels(agent)\n\n const [modelPickerValue, setModelPickerValue] = React.useState<ModelPickerValue | null>(() =>\n readModelPickerValue(agent),\n )\n\n const effectiveModelPickerValue = React.useMemo(() => {\n if (!modelProvidersLoaded || !allowRuntimeOverride || modelProviders.length === 0) {\n return null\n }\n if (modelPickerValue && isModelPickerValueAvailable(modelPickerValue, modelProviders)) {\n return modelPickerValue\n }\n return null\n }, [\n allowRuntimeOverride,\n modelPickerValue,\n modelProviders,\n modelProvidersLoaded,\n ])\n\n React.useEffect(() => {\n setModelPickerValue(readModelPickerValue(agent))\n }, [agent])\n\n React.useEffect(() => {\n if (!modelProvidersLoaded) return\n if (!allowRuntimeModelOverride || modelProviders.length === 0) {\n if (modelPickerValue !== null) {\n setModelPickerValue(null)\n writeModelPickerValue(agent, null)\n }\n return\n }\n if (modelPickerValue && !isModelPickerValueAvailable(modelPickerValue, modelProviders)) {\n setModelPickerValue(null)\n writeModelPickerValue(agent, null)\n }\n }, [\n agent,\n allowRuntimeModelOverride,\n modelPickerValue,\n modelProviders,\n modelProvidersLoaded,\n ])\n\n const handleModelPickerChange = React.useCallback(\n (value: ModelPickerValue | null) => {\n setModelPickerValue(value)\n writeModelPickerValue(agent, value)\n },\n [agent],\n )\n\n const chat = useAiChat({\n agent,\n apiPath,\n pageContext,\n attachmentIds: allAttachmentIds.length > 0 ? allAttachmentIds : undefined,\n debug,\n initialMessages,\n onError,\n conversationId,\n providerOverride: effectiveModelPickerValue?.providerId ?? null,\n modelOverride: effectiveModelPickerValue?.modelId ?? null,\n onConversationNotFound,\n })\n\n const isStreaming = chat.status === 'streaming'\n const isSubmitting = chat.status === 'submitting'\n const isBusy = isStreaming || isSubmitting\n\n React.useEffect(() => {\n onConversationIdChangeRef.current?.(chat.conversationId)\n }, [chat.conversationId])\n\n // Surface a \"Thinking...\" placeholder so the chat does not look frozen.\n // Visible whenever ANY of the following is true while a turn is in flight:\n // (a) we're still in the submit phase before the first stream chunk\n // (b) streaming, but no content / reasoning / tool calls have arrived yet\n // (c) streaming, and at least one tool call is still in `pending` state\n // (the model is waiting on a tool result \u2014 the previous version\n // treated `toolCalls.length > 0` as \"has content\" and hid the\n // indicator the moment the first tool started, even though the\n // model had not produced any user-visible output yet)\n // (d) streaming, and the last visible event was a finished tool call\n // \u2014 the model is reasoning about the result before emitting more\n // text or kicking off the next tool\n // Once visible text/reasoning exists and no tool is pending, hide the\n // placeholder even if the network stream stays open for a tail event. That\n // avoids showing \"Thinking...\" under an already usable answer.\n const lastAssistant = React.useMemo(() => {\n for (let index = chat.messages.length - 1; index >= 0; index -= 1) {\n const candidate = chat.messages[index]\n if (candidate?.role === 'assistant') return candidate\n }\n return null\n }, [chat.messages])\n\n const trimmedContent = lastAssistant?.content?.trim() ?? ''\n const hasReasoning = !!(lastAssistant?.reasoning && lastAssistant.reasoning.length > 0)\n const toolCalls = lastAssistant?.toolCalls ?? []\n const hasPendingToolCall = toolCalls.some((call) => call.state === 'pending')\n const hasCompletedToolCall = toolCalls.some(\n (call) => call.state === 'complete' || call.state === 'error',\n )\n const hasAnyVisibleSignal = !!(\n trimmedContent || hasReasoning || toolCalls.length > 0\n )\n\n const assistantStreamSnapshot = React.useMemo(() => {\n if (!lastAssistant) return ''\n const toolSig = toolCalls\n .map((call) => `${call.id}:${call.state}:${call.output != null ? 1 : 0}`)\n .join('|')\n return [\n lastAssistant.id,\n lastAssistant.content?.length ?? 0,\n lastAssistant.reasoning?.length ?? 0,\n lastAssistant.reasoningStreaming ? 1 : 0,\n toolSig,\n ].join('#')\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [lastAssistant])\n\n const lastSnapshotRef = React.useRef<string>('')\n const [, setStreamTick] = React.useState(0)\n\n React.useEffect(() => {\n if (assistantStreamSnapshot !== lastSnapshotRef.current) {\n lastSnapshotRef.current = assistantStreamSnapshot\n setStreamTick((value) => value + 1)\n }\n }, [assistantStreamSnapshot])\n\n const showThinkingIndicator =\n isSubmitting ||\n (isStreaming &&\n (\n !hasAnyVisibleSignal ||\n hasPendingToolCall ||\n // Tool just returned and the model hasn't started speaking yet.\n (hasCompletedToolCall && !trimmedContent)\n ))\n\n const activeRegistry = registry ?? defaultAiUiPartRegistry\n\n // Reserved UI parts. Phase 3 will populate this from the streamed response;\n // for Phase 2 WS-A it stays empty unless the host surfaces test/debug parts\n // via the optional `uiParts` prop so the registry resolution path can be\n // exercised without waiting for the runtime emitter.\n const uiParts: ServerEmittedUiPartRef[] = React.useMemo(\n () => (uiPartsProp ?? []).map((part) => ({\n componentId: part.componentId,\n payload: part.payload,\n pendingActionId: part.pendingActionId,\n })),\n [uiPartsProp],\n )\n\n const hasUploadingFiles = React.useMemo(\n () => pendingFiles.some((entry) => !entry.attachmentId && !entry.error),\n [pendingFiles],\n )\n\n const handleSendMessage = React.useCallback(\n (text: string) => {\n if (!text.trim() || isBusy) return\n // Block send while any attachment is still uploading. Without this guard\n // the message would ship with an empty attachmentIds list (the chip is\n // visible but the server hasn't returned an id yet), the model would\n // never see the file, and `setPendingFiles([])` below would erase the\n // chip \u2014 so the upload finishes into the void. Surface the wait via the\n // disabled Send button + composer hint instead.\n if (hasUploadingFiles || isUploading) return\n const filesToAttach = pendingFiles.map((entry): AiChatMessageFile => {\n const isImage = entry.file.type.startsWith('image/')\n const fallback = isImage ? URL.createObjectURL(entry.file) : undefined\n return {\n id: entry.attachmentId,\n name: entry.file.name,\n type: entry.file.type,\n previewUrl: isImage ? (entry.previewDataUrl ?? fallback) : undefined,\n }\n })\n setInput('')\n setPendingFiles([])\n void chat.sendMessage(text, filesToAttach.length > 0 ? filesToAttach : undefined)\n },\n [chat, hasUploadingFiles, isBusy, isUploading, pendingFiles],\n )\n\n const handleSubmit = React.useCallback(() => {\n handleSendMessage(input)\n }, [handleSendMessage, input])\n\n // Listen for \"Fix with AI\" requests dispatched by the failure variant\n // of `MutationResultCard`. Any rendered failure card can fire a custom\n // DOM event with the prompt \u2014 this side-steps having to thread a\n // sendMessage callback through the UI-part registry while keeping the\n // chat the single owner of message creation. Idempotency is handled\n // server-side: `prepareMutation` only dedupes against active `pending`\n // rows, so a retry after a terminal failure always produces a fresh\n // pending action.\n React.useEffect(() => {\n if (typeof window === 'undefined') return\n const handler = (event: Event) => {\n const detail = (event as CustomEvent<{ message?: string }>).detail\n const message = detail?.message\n if (typeof message !== 'string' || message.trim().length === 0) return\n handleSendMessage(message)\n }\n window.addEventListener('om-ai-chat-fix-request', handler as EventListener)\n return () => {\n window.removeEventListener('om-ai-chat-fix-request', handler as EventListener)\n }\n }, [handleSendMessage])\n\n const cancelOrBlur = React.useCallback(() => {\n if (isBusy) {\n chat.cancel()\n return\n }\n textareaRef.current?.blur()\n }, [chat, isBusy])\n\n const handleFileSelect = React.useCallback(\n async (event: React.ChangeEvent<HTMLInputElement>) => {\n const files = Array.from(event.target.files ?? [])\n if (files.length === 0) return\n const queued: PendingAttachment[] = files.map((file) => ({ file }))\n setPendingFiles((prev) => [...prev, ...queued])\n const [previewResults, result] = await Promise.all([\n Promise.all(files.map((file) => readFileAsDataUrl(file))),\n upload.upload(files),\n ])\n // Pair upload outcomes back to chips by input INDEX, not by filename.\n // The server may sanitize the uploaded name (whitespace, unicode,\n // dangerous characters), and two files in the same batch can share a\n // name \u2014 both cases broke the previous Map-by-fileName matching and\n // left the chip stuck on the spinner forever.\n const idByIndex = new Map<number, string>()\n for (const item of result.items) {\n if (typeof item.inputIndex === 'number') idByIndex.set(item.inputIndex, item.attachmentId)\n }\n const errorByIndex = new Map<number, string>()\n for (const failure of result.failed) {\n if (typeof failure.inputIndex === 'number') errorByIndex.set(failure.inputIndex, failure.message)\n }\n setPendingFiles((prev) => {\n const next = prev.slice()\n const baseIndex = next.length - files.length\n for (let offset = 0; offset < files.length; offset += 1) {\n const index = baseIndex + offset\n if (index < 0) continue\n const entry = next[index]\n if (!entry) continue\n const dataUrl = previewResults[offset]\n const patch: PendingAttachment = {\n ...entry,\n previewDataUrl: dataUrl ?? entry.previewDataUrl,\n }\n if (!patch.attachmentId) {\n const id = idByIndex.get(offset)\n if (id) {\n patch.attachmentId = id\n patch.error = undefined\n } else {\n // Defensive fallback: if neither success nor failure carried an\n // index for this slot (older transports, partial outcome), the\n // chip would otherwise stay on the spinner. Mark it as a\n // generic error so the user can remove it and retry instead of\n // staring at a dead spinner that also blocks the Send button.\n const explicitError = errorByIndex.get(offset)\n patch.error =\n explicitError ??\n 'Upload finished without a server response. Remove the file and try again.'\n }\n }\n next[index] = patch\n }\n return next\n })\n if (fileInputRef.current) fileInputRef.current.value = ''\n },\n [upload],\n )\n\n const removePendingFile = React.useCallback((index: number) => {\n setPendingFiles((prev) => prev.filter((_, i) => i !== index))\n }, [])\n\n const { handleKeyDown } = useAiShortcuts({\n onSubmit: handleSubmit,\n onCancel: cancelOrBlur,\n })\n\n React.useEffect(() => {\n textareaRef.current?.focus()\n }, [])\n\n // Sticky-bottom autoscroll: only re-pin to the bottom when the user is\n // already there (or within a small tolerance). If they have scrolled up to\n // read an earlier part of a long response, every streaming delta would\n // otherwise yank them back to the tail and make the message look truncated.\n // Tolerance is generous enough to absorb sub-pixel rounding, but tight\n // enough that an intentional scroll-up keeps the user where they want.\n const stickToBottomRef = React.useRef(true)\n const SCROLL_STICK_TOLERANCE_PX = 64\n\n React.useEffect(() => {\n const node = transcriptRef.current\n if (!node) return\n const handleScroll = () => {\n const distanceFromBottom = node.scrollHeight - node.scrollTop - node.clientHeight\n stickToBottomRef.current = distanceFromBottom <= SCROLL_STICK_TOLERANCE_PX\n }\n node.addEventListener('scroll', handleScroll, { passive: true })\n return () => node.removeEventListener('scroll', handleScroll)\n }, [])\n\n React.useEffect(() => {\n const node = transcriptRef.current\n if (!node) return\n if (!stickToBottomRef.current) return\n node.scrollTop = node.scrollHeight\n }, [chat.messages])\n\n // Mark the body so floating UI surfaces (e.g. the demo feedback FAB) can\n // hide themselves while the chat is open and would otherwise overlay the\n // composer's send button. CSS lives in `apps/mercato/src/app/globals.css`\n // alongside the column-chooser precedent.\n React.useEffect(() => {\n if (typeof document === 'undefined') return\n const previous = document.body.getAttribute('data-ai-chat-open')\n document.body.setAttribute('data-ai-chat-open', 'true')\n return () => {\n if (previous === null) {\n document.body.removeAttribute('data-ai-chat-open')\n } else {\n document.body.setAttribute('data-ai-chat-open', previous)\n }\n }\n }, [])\n\n const handleNewConversation = React.useCallback(() => {\n chat.reset()\n setInput('')\n setPendingFiles([])\n upload.reset()\n setTimeout(() => textareaRef.current?.focus(), 0)\n }, [chat, upload])\n\n const resolvedPlaceholder =\n placeholder ?? t('ai_assistant.chat.composerPlaceholder', 'Message the AI agent...')\n\n const errorVariant = mapErrorCodeToVariant(chat.error?.code)\n\n return (\n <section\n className={cn(\n 'flex h-full min-h-[320px] min-w-0 flex-col gap-3 overflow-hidden rounded-lg border border-border bg-background p-3',\n className,\n )}\n aria-label={t('ai_assistant.chat.regionLabel', 'AI chat')}\n data-ai-chat-agent={agent}\n data-ai-chat-conversation-id={chat.conversationId}\n >\n <div className=\"flex min-w-0 items-center justify-between gap-2 border-b border-border pb-2\">\n <div className=\"flex min-w-0 flex-1 items-center gap-2 text-xs text-muted-foreground\">\n {contextItems && contextItems.length > 0 ? (\n <ContextItemsPill items={contextItems} />\n ) : (\n <span className=\"font-mono opacity-70\" aria-hidden>\n {chat.conversationId.slice(0, 8)}\n </span>\n )}\n </div>\n {headerActions ?? null}\n <IconButton\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n onClick={handleNewConversation}\n disabled={isBusy}\n aria-label={t('ai_assistant.chat.newConversation', 'Start new conversation')}\n title={t('ai_assistant.chat.newConversation', 'Start new conversation')}\n data-ai-chat-new-conversation=\"\"\n >\n <Plus className=\"size-4\" aria-hidden />\n </IconButton>\n </div>\n <div\n ref={transcriptRef}\n role=\"log\"\n aria-live=\"polite\"\n aria-label={t('ai_assistant.chat.transcriptLabel', 'Chat transcript')}\n className=\"min-w-0 flex-1 space-y-2 overflow-y-auto overflow-x-hidden pr-1\"\n >\n {chat.messages.length === 0 ? (\n <WelcomeState\n title={welcomeTitle}\n description={welcomeDescription}\n suggestions={suggestions}\n onSuggestionClick={handleSendMessage}\n />\n ) : (\n chat.messages.map((message) => (\n <MessageRow\n key={message.id}\n message={message}\n registry={activeRegistry}\n onMutationRequested={onMutationRequested}\n isOwner={chat.isOwner}\n />\n ))\n )}\n {uiParts.map((part, index) => (\n <AiUiPartRenderer\n key={`${part.componentId}-${index}`}\n part={part}\n registry={activeRegistry}\n onMutationRequested={onMutationRequested}\n />\n ))}\n {showThinkingIndicator ? (\n <div\n className=\"flex items-center gap-2 px-3 py-2 text-xs text-muted-foreground\"\n data-ai-chat-state=\"thinking\"\n >\n <Loader2 className=\"size-4 animate-spin\" aria-hidden />\n <span>{t('ai_assistant.chat.thinking', 'Thinking...')}</span>\n </div>\n ) : null}\n </div>\n\n {chat.error ? (\n <Alert variant={errorVariant} data-ai-chat-error={chat.error.code ?? 'unknown'}>\n <AlertTitle>\n {t('ai_assistant.chat.errorTitle', 'Agent dispatch failed')}\n </AlertTitle>\n <AlertDescription>\n {chat.error.code ? (\n <span className=\"mr-2 font-mono text-xs\">{chat.error.code}</span>\n ) : null}\n {chat.error.message}\n </AlertDescription>\n </Alert>\n ) : null}\n\n {chat.isOwner === false ? (\n <div\n className=\"flex items-center justify-center rounded-md border border-border bg-muted/30 px-3 py-3 text-xs text-muted-foreground\"\n data-ai-chat-read-only-notice=\"\"\n >\n {t('ai_assistant.chat.readOnlyNotice', 'This is a shared conversation. You can read but not reply.')}\n </div>\n ) : null}\n <form\n className={cn('flex min-w-0 flex-col gap-2', chat.isOwner === false && 'hidden')}\n onSubmit={(event) => {\n event.preventDefault()\n handleSubmit()\n }}\n >\n <Label\n htmlFor=\"ai-chat-composer\"\n className=\"sr-only\"\n >\n {t('ai_assistant.chat.composerLabel', 'Message composer')}\n </Label>\n {pendingFiles.length > 0 ? (\n <div className=\"flex flex-wrap gap-1.5 rounded-md border border-border bg-muted/30 px-2 py-1.5\" data-ai-chat-attachments=\"\">\n {pendingFiles.map((entry, index) => (\n <span\n key={index}\n className=\"inline-flex items-center gap-1 rounded-full border border-border bg-background px-2 py-0.5 text-xs\"\n title={entry.error ? entry.error : undefined}\n data-ai-chat-attachment-state={\n entry.error ? 'error' : entry.attachmentId ? 'ready' : 'uploading'\n }\n >\n <Paperclip className=\"size-3 text-muted-foreground\" aria-hidden />\n <span className=\"max-w-[120px] truncate\">{entry.file.name}</span>\n {!entry.attachmentId && !entry.error ? (\n <Loader2 className=\"size-3 animate-spin text-muted-foreground\" aria-hidden />\n ) : null}\n <button\n type=\"button\"\n className=\"ml-0.5 rounded-full p-0.5 hover:bg-muted\"\n onClick={() => removePendingFile(index)}\n aria-label={t('ai_assistant.chat.removeFile', 'Remove file')}\n >\n <X className=\"size-3\" />\n </button>\n </span>\n ))}\n {isUploading ? <Loader2 className=\"size-3 animate-spin text-muted-foreground\" aria-hidden /> : null}\n </div>\n ) : null}\n <Textarea\n id=\"ai-chat-composer\"\n ref={textareaRef}\n value={input}\n placeholder={resolvedPlaceholder}\n onChange={(event) => setInput(event.target.value)}\n onKeyDown={handleKeyDown}\n rows={3}\n aria-label={t('ai_assistant.chat.composerLabel', 'Message composer')}\n className=\"min-w-0 resize-none\"\n />\n <input\n ref={fileInputRef}\n type=\"file\"\n multiple\n accept=\"image/*,.pdf,.doc,.docx,.txt,.csv\"\n className=\"hidden\"\n onChange={handleFileSelect}\n data-ai-chat-file-input=\"\"\n />\n <div\n ref={footerRef}\n className=\"flex min-w-0 items-center justify-between gap-2\"\n data-ai-chat-footer=\"\"\n data-ai-chat-footer-compact={isCompactFooter ? 'true' : 'false'}\n >\n <div className=\"flex min-w-0 items-center gap-2\">\n <IconButton\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => fileInputRef.current?.click()}\n disabled={isBusy || isUploading}\n aria-label={t('ai_assistant.chat.attachFile', 'Attach file')}\n >\n <Paperclip className=\"size-4\" aria-hidden />\n </IconButton>\n {allowRuntimeOverride && modelProviders.length > 0 ? (\n <ModelPicker\n agentId={agent}\n value={effectiveModelPickerValue}\n onChange={handleModelPickerChange}\n availableProviders={modelProviders}\n disabled={isBusy}\n compact={isCompactFooter}\n defaultLabel={modelDefaultLabel}\n className=\"shrink-0\"\n />\n ) : null}\n <p className={cn('text-xs text-muted-foreground', isCompactFooter && 'hidden')}>\n {hasUploadingFiles || isUploading\n ? t(\n 'ai_assistant.chat.uploadingHint',\n 'Uploading attachments\u2026 Send is disabled until they finish.',\n )\n : t(\n 'ai_assistant.chat.shortcutHint',\n 'Press Enter to send, Shift+Enter for new line.',\n )}\n </p>\n </div>\n <div className=\"flex shrink-0 items-center gap-2\">\n {isStreaming ? (\n <IconButton\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => chat.cancel()}\n aria-label={t('ai_assistant.chat.cancel', 'Cancel streaming response')}\n >\n <Square className=\"size-4\" aria-hidden />\n </IconButton>\n ) : null}\n <Button\n type=\"submit\"\n size=\"sm\"\n disabled={\n isBusy ||\n isUploading ||\n hasUploadingFiles ||\n input.trim().length === 0\n }\n aria-label={\n hasUploadingFiles || isUploading\n ? t('ai_assistant.chat.sendWaitingForUpload', 'Waiting for upload to finish\u2026')\n : t('ai_assistant.chat.send', 'Send message')\n }\n title={\n hasUploadingFiles || isUploading\n ? t('ai_assistant.chat.sendWaitingForUpload', 'Waiting for upload to finish\u2026')\n : undefined\n }\n className={cn(isCompactFooter && 'w-8 px-0')}\n >\n <Send className=\"size-4\" aria-hidden />\n {!isCompactFooter ? (\n <span>{t('ai_assistant.chat.send', 'Send message')}</span>\n ) : null}\n </Button>\n </div>\n </div>\n </form>\n\n {debug ? (\n <AiChatDebugPanel\n tools={debugTools}\n promptSections={debugPromptSections}\n loopTrace={loopTrace ?? chat.lastLoopTrace ?? undefined}\n lastRequestDebug={chat.lastRequestDebug}\n lastResponseDebug={chat.lastResponseDebug}\n status={chat.status}\n errorCode={chat.error?.code}\n />\n ) : null}\n </section>\n )\n}\n\ninterface DebugPanelProps {\n tools?: AiChatDebugTool[]\n promptSections?: AiChatDebugPromptSection[]\n loopTrace?: LoopTracePanelTrace\n lastRequestDebug: { url: string; body: unknown } | null\n lastResponseDebug: { status: number; text: string } | null\n status: 'idle' | 'submitting' | 'streaming'\n errorCode?: string\n}\n\nfunction AiChatDebugPanel({\n tools,\n promptSections,\n loopTrace,\n lastRequestDebug,\n lastResponseDebug,\n status,\n errorCode,\n}: DebugPanelProps) {\n const t = useT()\n return (\n <div\n className=\"flex flex-col gap-2 rounded-md border border-border bg-muted/60 p-2 text-xs\"\n data-ai-chat-debug=\"true\"\n >\n <div className=\"font-semibold\">\n {t('ai_assistant.chat.debug.panelTitle', 'Debug panel')}\n </div>\n\n {loopTrace ? (\n <LoopTracePanel trace={loopTrace} />\n ) : null}\n\n <details className=\"rounded border border-border bg-background\" data-ai-chat-debug-section=\"tools\" open>\n <summary className=\"cursor-pointer px-2 py-1 font-semibold\">\n {t('ai_assistant.chat.debug.toolsSection', 'Resolved tools')}\n {tools ? (\n <span className=\"ml-2 font-mono text-muted-foreground\">({tools.length})</span>\n ) : null}\n </summary>\n <div className=\"px-2 pb-2\">\n {tools && tools.length > 0 ? (\n <ul className=\"flex flex-col gap-1\" data-ai-chat-debug-tools>\n {tools.map((tool) => (\n <li\n key={tool.name}\n className=\"flex flex-col rounded border border-border bg-muted/40 px-2 py-1\"\n data-ai-chat-debug-tool={tool.name}\n >\n <span className=\"font-mono\">{tool.name}</span>\n {tool.displayName ? (\n <span className=\"text-muted-foreground\">{tool.displayName}</span>\n ) : null}\n <span className=\"mt-1 flex flex-wrap gap-2 text-muted-foreground\">\n <span>\n {tool.isMutation\n ? t('ai_assistant.chat.debug.toolMutation', 'mutation')\n : t('ai_assistant.chat.debug.toolRead', 'read')}\n </span>\n {tool.requiredFeatures && tool.requiredFeatures.length > 0 ? (\n <span className=\"font-mono\">\n [{tool.requiredFeatures.join(', ')}]\n </span>\n ) : (\n <span>\n {t('ai_assistant.chat.debug.toolNoFeatures', 'no required features')}\n </span>\n )}\n </span>\n </li>\n ))}\n </ul>\n ) : (\n <p className=\"text-muted-foreground\">\n {t(\n 'ai_assistant.chat.debug.toolsEmpty',\n 'No tools resolved for this agent yet.',\n )}\n </p>\n )}\n </div>\n </details>\n\n <details\n className=\"rounded border border-border bg-background\"\n data-ai-chat-debug-section=\"promptSections\"\n >\n <summary className=\"cursor-pointer px-2 py-1 font-semibold\">\n {t('ai_assistant.chat.debug.promptSection', 'Prompt sections')}\n {promptSections ? (\n <span className=\"ml-2 font-mono text-muted-foreground\">({promptSections.length})</span>\n ) : null}\n </summary>\n <div className=\"px-2 pb-2\">\n {promptSections && promptSections.length > 0 ? (\n <ul className=\"flex flex-col gap-1\" data-ai-chat-debug-prompt-sections>\n {promptSections.map((section) => (\n <li\n key={section.id}\n className=\"rounded border border-border bg-muted/40 px-2 py-1\"\n data-ai-chat-debug-prompt-section-id={section.id}\n >\n <div className=\"flex items-center justify-between gap-2\">\n <span className=\"font-mono\">{section.id}</span>\n <span className=\"text-muted-foreground\">\n {section.source === 'override'\n ? t('ai_assistant.chat.debug.promptOverride', 'override')\n : section.source === 'placeholder'\n ? t('ai_assistant.chat.debug.promptPlaceholder', 'placeholder')\n : t('ai_assistant.chat.debug.promptDefault', 'default')}\n </span>\n </div>\n {section.text ? (\n <pre className=\"mt-1 max-h-24 overflow-auto whitespace-pre-wrap font-mono text-muted-foreground\">\n {section.text}\n </pre>\n ) : null}\n </li>\n ))}\n </ul>\n ) : (\n <p className=\"text-muted-foreground\">\n {t(\n 'ai_assistant.chat.debug.promptEmpty',\n 'No prompt sections resolved for this agent.',\n )}\n </p>\n )}\n </div>\n </details>\n\n <details\n className=\"rounded border border-border bg-background\"\n data-ai-chat-debug-section=\"lastRequest\"\n >\n <summary className=\"cursor-pointer px-2 py-1 font-semibold\">\n {t('ai_assistant.chat.debug.lastRequestSection', 'Last request')}\n </summary>\n <div className=\"px-2 pb-2\">\n {lastRequestDebug ? (\n <pre\n className=\"max-h-40 overflow-auto whitespace-pre-wrap font-mono\"\n data-ai-chat-debug-last-request\n >\n {JSON.stringify(lastRequestDebug, null, 2)}\n </pre>\n ) : (\n <p className=\"text-muted-foreground\">\n {t(\n 'ai_assistant.chat.debug.lastRequestEmpty',\n 'No request has been sent yet.',\n )}\n </p>\n )}\n </div>\n </details>\n\n <details\n className=\"rounded border border-border bg-background\"\n data-ai-chat-debug-section=\"lastResponse\"\n >\n <summary className=\"cursor-pointer px-2 py-1 font-semibold\">\n {t('ai_assistant.chat.debug.lastResponseSection', 'Last response')}\n </summary>\n <div className=\"px-2 pb-2\">\n {lastResponseDebug ? (\n <pre\n className=\"max-h-40 overflow-auto whitespace-pre-wrap font-mono\"\n data-ai-chat-debug-last-response\n >\n {JSON.stringify(\n { status: lastResponseDebug.status, text: lastResponseDebug.text, errorCode },\n null,\n 2,\n )}\n </pre>\n ) : (\n <p className=\"text-muted-foreground\">\n {t(\n 'ai_assistant.chat.debug.lastResponseEmpty',\n 'No response received yet.',\n )}\n </p>\n )}\n </div>\n </details>\n\n <div className=\"text-muted-foreground\" data-ai-chat-debug-status={status}>\n {t('ai_assistant.chat.debug.statusLabel', 'Status:')}{' '}\n <span className=\"font-mono\">{status}</span>\n </div>\n </div>\n )\n}\n\nexport default AiChat\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|