@open-mercato/ui 0.6.1-develop.3174.1.c3822bcdf0 → 0.6.1-develop.3187.1.4c96d17341

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.
@@ -20,12 +20,34 @@ function makeConversationId() {
20
20
  }
21
21
  const SESSION_STORAGE_PREFIX = "om-ai-chat:";
22
22
  const SESSION_STORAGE_VERSION = 1;
23
+ const SESSION_UPDATED_EVENT = "om-ai-chat-session-updated";
24
+ const SESSION_STREAM_STATE_EVENT = "om-ai-chat-session-stream-state";
25
+ const activeSessionStreams = /* @__PURE__ */ new Set();
23
26
  function getSessionStorageKey(agent, conversationId) {
24
27
  if (typeof conversationId === "string" && conversationId.length > 0) {
25
28
  return `${SESSION_STORAGE_PREFIX}${agent}:${conversationId}`;
26
29
  }
27
30
  return `${SESSION_STORAGE_PREFIX}${agent}`;
28
31
  }
32
+ function writeSessionStreamState(storageKey, active) {
33
+ if (active) {
34
+ activeSessionStreams.add(storageKey);
35
+ } else {
36
+ activeSessionStreams.delete(storageKey);
37
+ }
38
+ if (typeof window === "undefined") return;
39
+ try {
40
+ window.dispatchEvent(
41
+ new CustomEvent(SESSION_STREAM_STATE_EVENT, {
42
+ detail: { key: storageKey, active }
43
+ })
44
+ );
45
+ } catch {
46
+ }
47
+ }
48
+ function isSessionStreamActive(storageKey) {
49
+ return activeSessionStreams.has(storageKey);
50
+ }
29
51
  function readPersistedSession(agent, conversationId) {
30
52
  if (typeof window === "undefined") return null;
31
53
  try {
@@ -43,7 +65,7 @@ function readPersistedSession(agent, conversationId) {
43
65
  return null;
44
66
  }
45
67
  }
46
- function writePersistedSession(agent, session, conversationId) {
68
+ function writePersistedSession(agent, session, conversationId, options) {
47
69
  if (typeof window === "undefined") return;
48
70
  try {
49
71
  const messages = session.messages.map((message) => {
@@ -54,10 +76,15 @@ function writePersistedSession(agent, session, conversationId) {
54
76
  });
55
77
  return { ...message, files: safeFiles };
56
78
  });
57
- window.localStorage.setItem(
58
- getSessionStorageKey(agent, conversationId),
59
- JSON.stringify({ ...session, messages })
60
- );
79
+ const storageKey = getSessionStorageKey(agent, conversationId);
80
+ window.localStorage.setItem(storageKey, JSON.stringify({ ...session, messages }));
81
+ if (options?.notify) {
82
+ window.dispatchEvent(
83
+ new CustomEvent(SESSION_UPDATED_EVENT, {
84
+ detail: { key: storageKey, agent, conversationId: session.conversationId }
85
+ })
86
+ );
87
+ }
61
88
  } catch {
62
89
  }
63
90
  }
@@ -288,6 +315,8 @@ function useAiChat(input) {
288
315
  mintedConversationIdRef.current = persisted?.conversationId ?? makeConversationId();
289
316
  }
290
317
  const effectiveConversationId = typeof conversationIdInput === "string" && conversationIdInput.length > 0 ? conversationIdInput : mintedConversationIdRef.current;
318
+ const persistKey = typeof conversationIdInput === "string" && conversationIdInput.length > 0 ? conversationIdInput : null;
319
+ const sessionStorageKey = getSessionStorageKey(agent, persistKey);
291
320
  const [messages, setMessages] = React.useState(() => {
292
321
  if (persisted && persisted.messages.length > 0) {
293
322
  return persisted.messages;
@@ -298,10 +327,32 @@ function useAiChat(input) {
298
327
  content: entry.content
299
328
  }));
300
329
  });
301
- const [status, setStatusInternal] = React.useState("idle");
330
+ const [status, setStatusInternal] = React.useState(
331
+ () => isSessionStreamActive(sessionStorageKey) ? "streaming" : "idle"
332
+ );
333
+ const latestMessagesRef = React.useRef(messages);
334
+ const persistKeyRef = React.useRef(null);
335
+ const agentRef = React.useRef(agent);
336
+ const effectiveConversationIdRef = React.useRef(effectiveConversationId);
337
+ const sessionStorageKeyRef = React.useRef(sessionStorageKey);
338
+ const mountedRef = React.useRef(true);
339
+ React.useEffect(() => {
340
+ latestMessagesRef.current = messages;
341
+ }, [messages]);
342
+ React.useEffect(() => {
343
+ persistKeyRef.current = typeof conversationIdInput === "string" && conversationIdInput.length > 0 ? conversationIdInput : null;
344
+ }, [conversationIdInput]);
345
+ React.useEffect(() => {
346
+ agentRef.current = agent;
347
+ }, [agent]);
348
+ React.useEffect(() => {
349
+ effectiveConversationIdRef.current = effectiveConversationId;
350
+ }, [effectiveConversationId]);
351
+ React.useEffect(() => {
352
+ sessionStorageKeyRef.current = sessionStorageKey;
353
+ }, [sessionStorageKey]);
302
354
  React.useEffect(() => {
303
355
  if (status !== "idle") return;
304
- const persistKey = typeof conversationIdInput === "string" && conversationIdInput.length > 0 ? conversationIdInput : null;
305
356
  if (messages.length === 0) {
306
357
  clearPersistedSession(agent, persistKey);
307
358
  return;
@@ -315,8 +366,38 @@ function useAiChat(input) {
315
366
  },
316
367
  persistKey
317
368
  );
318
- }, [agent, conversationIdInput, effectiveConversationId, messages, status]);
319
- const setStatus = setStatusInternal;
369
+ }, [agent, effectiveConversationId, messages, persistKey, status]);
370
+ const persistSnapshot = React.useCallback((snapshot, notify = false) => {
371
+ if (snapshot.length === 0) return;
372
+ writePersistedSession(
373
+ agentRef.current,
374
+ {
375
+ v: SESSION_STORAGE_VERSION,
376
+ conversationId: effectiveConversationIdRef.current,
377
+ messages: snapshot
378
+ },
379
+ persistKeyRef.current,
380
+ { notify }
381
+ );
382
+ }, []);
383
+ const updateMessages = React.useCallback(
384
+ (updater, options) => {
385
+ const current = latestMessagesRef.current;
386
+ const next = typeof updater === "function" ? updater(current) : updater;
387
+ latestMessagesRef.current = next;
388
+ if (mountedRef.current) {
389
+ setMessages(next);
390
+ } else if (options?.persistWhenUnmounted) {
391
+ persistSnapshot(next, true);
392
+ }
393
+ },
394
+ [persistSnapshot]
395
+ );
396
+ const setStatus = React.useCallback((next) => {
397
+ if (mountedRef.current) {
398
+ setStatusInternal(next);
399
+ }
400
+ }, []);
320
401
  const [error, setError] = React.useState(null);
321
402
  const [lastRequestDebug, setLastRequestDebug] = React.useState(null);
322
403
  const [lastResponseDebug, setLastResponseDebug] = React.useState(null);
@@ -326,10 +407,12 @@ function useAiChat(input) {
326
407
  onErrorRef.current = onError;
327
408
  }, [onError]);
328
409
  const emitError = React.useCallback((envelope) => {
329
- setError(envelope);
330
- try {
331
- onErrorRef.current?.(envelope);
332
- } catch {
410
+ if (mountedRef.current) {
411
+ setError(envelope);
412
+ try {
413
+ onErrorRef.current?.(envelope);
414
+ } catch {
415
+ }
333
416
  }
334
417
  }, []);
335
418
  const cancel = React.useCallback(() => {
@@ -337,17 +420,18 @@ function useAiChat(input) {
337
420
  abortRef.current.abort();
338
421
  abortRef.current = null;
339
422
  }
423
+ writeSessionStreamState(sessionStorageKeyRef.current, false);
340
424
  setStatus("idle");
341
- }, []);
425
+ }, [setStatus]);
342
426
  const reset = React.useCallback(() => {
343
427
  cancel();
344
- setMessages([]);
428
+ updateMessages([]);
345
429
  setError(null);
346
430
  setLastRequestDebug(null);
347
431
  setLastResponseDebug(null);
348
- clearPersistedSession(agent);
432
+ clearPersistedSession(agent, persistKey);
349
433
  mintedConversationIdRef.current = makeConversationId();
350
- }, [agent, cancel]);
434
+ }, [agent, cancel, persistKey, updateMessages]);
351
435
  const sendMessage = React.useCallback(
352
436
  async (textInput, files) => {
353
437
  const trimmed = textInput.trim();
@@ -368,9 +452,10 @@ function useAiChat(input) {
368
452
  content: ""
369
453
  };
370
454
  const assistantId = assistantMessage.id;
371
- const outgoingHistory = [...messages, userMessage];
372
- setMessages([...outgoingHistory, assistantMessage]);
455
+ const outgoingHistory = [...latestMessagesRef.current, userMessage];
456
+ updateMessages([...outgoingHistory, assistantMessage], { persistWhenUnmounted: true });
373
457
  setStatus("submitting");
458
+ writeSessionStreamState(sessionStorageKey, true);
374
459
  const controller = new AbortController();
375
460
  abortRef.current = controller;
376
461
  const url = getTransportEndpoint(agent, apiPath);
@@ -384,7 +469,9 @@ function useAiChat(input) {
384
469
  debug,
385
470
  conversationId: effectiveConversationId
386
471
  };
387
- setLastRequestDebug({ url, body });
472
+ if (mountedRef.current) {
473
+ setLastRequestDebug({ url, body });
474
+ }
388
475
  let response;
389
476
  try {
390
477
  response = await apiFetch(url, {
@@ -398,28 +485,38 @@ function useAiChat(input) {
398
485
  });
399
486
  } catch (requestError) {
400
487
  if (requestError?.name === "AbortError") {
488
+ writeSessionStreamState(sessionStorageKey, false);
401
489
  setStatus("idle");
402
490
  abortRef.current = null;
403
491
  return;
404
492
  }
405
493
  const message = requestError instanceof Error ? requestError.message : "Network request failed.";
406
494
  emitError({ message });
495
+ writeSessionStreamState(sessionStorageKey, false);
407
496
  setStatus("idle");
408
497
  abortRef.current = null;
409
498
  return;
410
499
  }
411
500
  if (!response.ok) {
412
501
  const envelope = await readErrorEnvelope(response);
413
- setLastResponseDebug({ status: response.status, text: envelope.message });
502
+ if (mountedRef.current) {
503
+ setLastResponseDebug({ status: response.status, text: envelope.message });
504
+ }
414
505
  emitError(envelope);
506
+ writeSessionStreamState(sessionStorageKey, false);
415
507
  setStatus("idle");
416
- setMessages((current) => current.filter((entry) => entry.id !== assistantId));
508
+ updateMessages((current) => current.filter((entry) => entry.id !== assistantId), {
509
+ persistWhenUnmounted: true
510
+ });
417
511
  abortRef.current = null;
418
512
  return;
419
513
  }
420
514
  const bodyStream = response.body;
421
515
  if (!bodyStream) {
422
- setLastResponseDebug({ status: response.status, text: "" });
516
+ if (mountedRef.current) {
517
+ setLastResponseDebug({ status: response.status, text: "" });
518
+ }
519
+ writeSessionStreamState(sessionStorageKey, false);
423
520
  setStatus("idle");
424
521
  abortRef.current = null;
425
522
  return;
@@ -471,10 +568,11 @@ function useAiChat(input) {
471
568
  builder = { ...builder, text: streamedRaw };
472
569
  }
473
570
  const snapshotBuilder = builder;
474
- setMessages(
571
+ updateMessages(
475
572
  (current) => current.map(
476
573
  (entry) => entry.id === assistantId ? mergeAssistantMessage(entry, snapshotBuilder) : entry
477
- )
574
+ ),
575
+ { persistWhenUnmounted: true }
478
576
  );
479
577
  }
480
578
  const tail = decoder.decode();
@@ -491,19 +589,24 @@ function useAiChat(input) {
491
589
  }
492
590
  builder = { ...builder, reasoningStreaming: false };
493
591
  const finalSnapshot = builder;
494
- setMessages(
592
+ updateMessages(
495
593
  (current) => current.map(
496
594
  (entry) => entry.id === assistantId ? mergeAssistantMessage(entry, finalSnapshot) : entry
497
- )
595
+ ),
596
+ { persistWhenUnmounted: true }
498
597
  );
499
- setLastResponseDebug({ status: response.status, text: streamedRaw });
598
+ if (mountedRef.current) {
599
+ setLastResponseDebug({ status: response.status, text: streamedRaw });
600
+ }
500
601
  const isEmpty = !builder.text.trim() && builder.toolCalls.length === 0 && !builder.reasoning;
501
602
  if (isEmpty) {
502
603
  emitError({
503
604
  code: "empty_response",
504
605
  message: "The AI agent returned an empty response. This usually means the LLM provider rejected the request (invalid API key, rate limit, or model error). Check your server logs for details."
505
606
  });
506
- setMessages((current) => current.filter((entry) => entry.id !== assistantId));
607
+ updateMessages((current) => current.filter((entry) => entry.id !== assistantId), {
608
+ persistWhenUnmounted: true
609
+ });
507
610
  }
508
611
  } catch (streamError) {
509
612
  if (streamError?.name === "AbortError") {
@@ -511,26 +614,72 @@ function useAiChat(input) {
511
614
  const rawMessage = streamError instanceof Error ? streamError.message : "Stream interrupted.";
512
615
  const message = rawMessage.includes("API") ? rawMessage : `${rawMessage} \u2014 check server logs for LLM provider details.`;
513
616
  emitError({ code: "stream_error", message });
514
- setMessages((current) => current.filter((entry) => entry.id !== assistantId));
617
+ updateMessages((current) => current.filter((entry) => entry.id !== assistantId), {
618
+ persistWhenUnmounted: true
619
+ });
515
620
  }
516
621
  } finally {
517
622
  reader.releaseLock();
518
623
  if (abortRef.current === controller) {
519
624
  abortRef.current = null;
520
625
  }
626
+ writeSessionStreamState(sessionStorageKey, false);
521
627
  setStatus("idle");
522
628
  }
523
629
  },
524
- [agent, apiPath, attachmentIds, debug, effectiveConversationId, emitError, messages, pageContext]
630
+ [
631
+ agent,
632
+ apiPath,
633
+ attachmentIds,
634
+ debug,
635
+ effectiveConversationId,
636
+ emitError,
637
+ pageContext,
638
+ sessionStorageKey,
639
+ setStatus,
640
+ updateMessages
641
+ ]
525
642
  );
526
643
  React.useEffect(() => {
644
+ if (typeof window === "undefined") return;
645
+ const handleSessionUpdate = (event) => {
646
+ const detail = event.detail;
647
+ if (!detail || detail.key !== sessionStorageKey) return;
648
+ const next = readPersistedSession(agent, persistKey);
649
+ if (!next) return;
650
+ if (next.conversationId !== effectiveConversationId) return;
651
+ updateMessages(next.messages);
652
+ };
653
+ window.addEventListener(SESSION_UPDATED_EVENT, handleSessionUpdate);
527
654
  return () => {
528
- if (abortRef.current) {
529
- abortRef.current.abort();
530
- abortRef.current = null;
655
+ window.removeEventListener(SESSION_UPDATED_EVENT, handleSessionUpdate);
656
+ };
657
+ }, [agent, effectiveConversationId, persistKey, sessionStorageKey, updateMessages]);
658
+ React.useEffect(() => {
659
+ if (isSessionStreamActive(sessionStorageKey)) {
660
+ setStatusInternal("streaming");
661
+ }
662
+ if (typeof window === "undefined") return;
663
+ const handleStreamState = (event) => {
664
+ const detail = event.detail;
665
+ if (!detail || detail.key !== sessionStorageKey) return;
666
+ setStatusInternal(detail.active ? "streaming" : "idle");
667
+ };
668
+ window.addEventListener(SESSION_STREAM_STATE_EVENT, handleStreamState);
669
+ return () => {
670
+ window.removeEventListener(SESSION_STREAM_STATE_EVENT, handleStreamState);
671
+ };
672
+ }, [sessionStorageKey]);
673
+ React.useEffect(() => {
674
+ mountedRef.current = true;
675
+ return () => {
676
+ const finalMessages = latestMessagesRef.current;
677
+ if (finalMessages.length > 0) {
678
+ persistSnapshot(finalMessages, true);
531
679
  }
680
+ mountedRef.current = false;
532
681
  };
533
- }, []);
682
+ }, [persistSnapshot]);
534
683
  return {
535
684
  messages,
536
685
  status,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/ai/useAiChat.ts"],
4
- "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { createAiAgentTransport } from '@open-mercato/ai-assistant/modules/ai_assistant/lib/agent-transport'\nimport { apiFetch } from '../backend/utils/api'\n\n/**\n * Chat message shape used by {@link AiChat}. Kept intentionally minimal so the\n * component stays independent of the AI SDK's evolving `UIMessage` type. The\n * dispatcher route (`POST /api/ai_assistant/ai/chat`) accepts exactly this\n * shape for `messages`.\n */\nexport interface AiChatMessageFile {\n name: string\n type: string\n previewUrl?: string\n}\n\nexport interface AiChatToolCallSnapshot {\n id: string\n toolName: string\n state: 'pending' | 'complete' | 'error'\n input?: unknown\n output?: unknown\n errorMessage?: string\n}\n\nexport interface AiChatMessageUiPart {\n componentId: string\n payload?: unknown\n pendingActionId?: string\n /** Stable id used as React key when rendering. */\n key: string\n}\n\nexport interface AiChatMessage {\n id: string\n role: 'user' | 'assistant'\n content: string\n files?: AiChatMessageFile[]\n reasoning?: string\n reasoningStreaming?: boolean\n toolCalls?: AiChatToolCallSnapshot[]\n /**\n * UI parts emitted by the agent during this message's lifecycle. Today\n * the only producer is `prepareMutation` (mutation approval flow):\n * the dispatcher's mutation tool returns an `awaiting-confirmation`\n * envelope, useAiChat parses it and attaches a `mutation-preview-card`\n * part here so AiChat can render the approval card inline. Phase 3\n * WS-C wiring \u2014 without this, the `MutationPreviewCard` registered in\n * the UI-part registry never surfaces.\n */\n uiParts?: AiChatMessageUiPart[]\n}\n\nexport interface UseAiChatInput {\n agent: string\n apiPath?: string\n pageContext?: Record<string, unknown>\n attachmentIds?: string[]\n debug?: boolean\n initialMessages?: Array<Pick<AiChatMessage, 'role' | 'content'>>\n onError?: (err: { code?: string; message: string }) => void\n /**\n * Optional stable conversation id. When provided, the same id is forwarded\n * to the dispatcher on every turn so `prepareMutation`'s idempotency hash\n * (Step 5.6) stays stable across mutation preview / confirm / retry cycles.\n * When omitted, the hook mints a fresh random id once on mount and reuses\n * it for the lifetime of the component \u2014 callers can still override via\n * props at any time to reset the conversation.\n */\n conversationId?: string\n}\n\nexport interface AiChatErrorEnvelope {\n code?: string\n message: string\n}\n\nexport interface UseAiChatResult {\n messages: AiChatMessage[]\n status: 'idle' | 'submitting' | 'streaming'\n error: AiChatErrorEnvelope | null\n lastRequestDebug: { url: string; body: unknown } | null\n lastResponseDebug: { status: number; text: string } | null\n /**\n * The conversation id currently in use for this chat instance. Equal to\n * the caller-provided `conversationId` input when one is supplied;\n * otherwise the random id minted on mount. Stable across re-renders for a\n * given mount (Phase 3 WS-D contract with `prepareMutation`).\n */\n conversationId: string\n sendMessage: (input: string, files?: AiChatMessageFile[]) => Promise<void>\n cancel: () => void\n reset: () => void\n}\n\nfunction makeMessageId(): string {\n const random = Math.random().toString(36).slice(2, 10)\n const time = Date.now().toString(36)\n return `msg_${time}_${random}`\n}\n\nfunction makeConversationId(): string {\n // Use crypto.randomUUID() when the browser exposes it (all evergreen\n // runtimes do), otherwise fall back to a low-entropy token that is still\n // unique enough for the idempotency-hash use case.\n const g = globalThis as unknown as { crypto?: { randomUUID?: () => string } }\n if (g.crypto && typeof g.crypto.randomUUID === 'function') {\n try {\n return g.crypto.randomUUID()\n } catch {\n // fall through to the random fallback\n }\n }\n const rand = () => Math.random().toString(16).slice(2, 10)\n return `conv_${Date.now().toString(16)}_${rand()}${rand()}`\n}\n\nconst SESSION_STORAGE_PREFIX = 'om-ai-chat:'\nconst SESSION_STORAGE_VERSION = 1\n\ninterface PersistedAiChatSession {\n v: number\n conversationId: string\n messages: AiChatMessage[]\n}\n\nfunction getSessionStorageKey(agent: string, conversationId?: string | null): string {\n // When the caller pins a `conversationId` (e.g. via the AiChatSessions\n // provider's tabs), namespace the persisted slot per session so multiple\n // open conversations for the same agent don't overwrite each other. The\n // legacy single-session-per-agent layout (no externally-supplied id) is\n // kept for backward compatibility with code that still relies on it.\n if (typeof conversationId === 'string' && conversationId.length > 0) {\n return `${SESSION_STORAGE_PREFIX}${agent}:${conversationId}`\n }\n return `${SESSION_STORAGE_PREFIX}${agent}`\n}\n\nfunction readPersistedSession(\n agent: string,\n conversationId?: string | null,\n): PersistedAiChatSession | null {\n if (typeof window === 'undefined') return null\n try {\n const raw = window.localStorage.getItem(getSessionStorageKey(agent, conversationId))\n if (!raw) return null\n const parsed = JSON.parse(raw) as PersistedAiChatSession | null\n if (!parsed || parsed.v !== SESSION_STORAGE_VERSION) return null\n if (typeof parsed.conversationId !== 'string') return null\n if (!Array.isArray(parsed.messages)) return null\n const messages = parsed.messages.filter((entry): entry is AiChatMessage => {\n return (\n !!entry &&\n typeof entry === 'object' &&\n typeof (entry as AiChatMessage).id === 'string' &&\n typeof (entry as AiChatMessage).content === 'string' &&\n ((entry as AiChatMessage).role === 'user' || (entry as AiChatMessage).role === 'assistant')\n )\n })\n return { v: SESSION_STORAGE_VERSION, conversationId: parsed.conversationId, messages }\n } catch {\n return null\n }\n}\n\nfunction writePersistedSession(\n agent: string,\n session: PersistedAiChatSession,\n conversationId?: string | null,\n): void {\n if (typeof window === 'undefined') return\n try {\n // Strip transient blob/object preview URLs before persisting (they would\n // not survive a reload). Self-contained `data:` URLs are kept so image\n // previews come back unchanged after the chat is reopened \u2014 public\n // attachment URLs are intentionally not used because the LLM provider\n // cannot reach a localhost origin and we want a single durable shape\n // that works for both transport and reload.\n const messages = session.messages.map((message) => {\n if (!message.files || message.files.length === 0) return message\n const safeFiles = message.files.map(({ name, type, previewUrl }) => {\n const durable =\n typeof previewUrl === 'string' && previewUrl.startsWith('data:')\n ? previewUrl\n : undefined\n return durable ? { name, type, previewUrl: durable } : { name, type }\n })\n return { ...message, files: safeFiles }\n })\n window.localStorage.setItem(\n getSessionStorageKey(agent, conversationId),\n JSON.stringify({ ...session, messages }),\n )\n } catch {\n // Quota exceeded / privacy mode \u2014 silently drop persistence.\n }\n}\n\nfunction clearPersistedSession(agent: string, conversationId?: string | null): void {\n if (typeof window === 'undefined') return\n try {\n window.localStorage.removeItem(getSessionStorageKey(agent, conversationId))\n } catch {\n // ignore\n }\n}\n\nfunction getTransportEndpoint(agent: string, apiPath?: string): string {\n // Reuse the transport factory so UI consumers share the dispatcher URL\n // convention with server-side callers (e.g. runAiAgentText / Playwright\n // fixtures). The factory returns a ChatTransport<UI_MESSAGE> whose internal\n // endpoint we do not directly read \u2014 instead we reconstruct the same URL\n // shape here so downstream error handling stays deterministic.\n //\n // When the AI SDK exposes a public endpoint getter (or the stream format\n // switches from plain text to UIMessageChunk) we can call\n // transport.sendMessages(...) directly.\n const transport = createAiAgentTransport({ agentId: agent, endpoint: apiPath })\n void transport\n const base = apiPath && apiPath.length > 0 ? apiPath : '/api/ai_assistant/ai/chat'\n const separator = base.includes('?') ? '&' : '?'\n return `${base}${separator}agent=${encodeURIComponent(agent)}`\n}\n\ninterface AssistantBuilderState {\n text: string\n reasoning: string\n reasoningStreaming: boolean\n toolCalls: AiChatToolCallSnapshot[]\n uiParts: AiChatMessageUiPart[]\n}\n\nfunction createBuilder(): AssistantBuilderState {\n return { text: '', reasoning: '', reasoningStreaming: false, toolCalls: [], uiParts: [] }\n}\n\n/**\n * Generic extractor for UI parts emitted by tool outputs. A tool can\n * surface inline UI to the chat by returning JSON in any of these\n * shapes \u2014 each tool call produces zero or more UI parts:\n *\n * 1. The dispatcher's mutation envelope:\n * `{ status: 'awaiting-confirmation', pendingActionId, expiresAt,\n * agent, toolName, message }`\n * \u2192 synthesizes a `mutation-preview-card` part (the registered\n * card fetches the live diff via `useAiPendingActionPolling`).\n *\n * 2. A single explicit UI part:\n * `{ uiPart: { componentId, payload?, pendingActionId? } }`\n *\n * 3. Multiple explicit UI parts:\n * `{ uiParts: [{ componentId, payload? }, ...] }`\n *\n * Tool authors only need to JSON-encode an object whose `uiPart` /\n * `uiParts` reference component ids that the host has registered on\n * `defaultAiUiPartRegistry` (or a scoped registry passed through\n * `<AiChat registry={...}/>`). Unknown component ids fall back to the\n * `UnknownUiPartPlaceholder` so an unregistered id never blows up the\n * transcript.\n */\nfunction extractUiPartsFromOutput(\n output: unknown,\n toolCallId: string,\n): AiChatMessageUiPart[] {\n let parsed: unknown = output\n if (typeof output === 'string') {\n const trimmed = output.trim()\n if (!trimmed.startsWith('{') && !trimmed.startsWith('[')) return []\n try {\n parsed = JSON.parse(trimmed)\n } catch {\n return []\n }\n }\n if (!parsed || typeof parsed !== 'object') return []\n const value = parsed as Record<string, unknown>\n const parts: AiChatMessageUiPart[] = []\n\n // (1) Mutation approval envelope. The dispatcher's `prepareMutation`\n // interceptor in `agent-tools.ts` formats the result via\n // `formatPendingActionToolResult` as\n // { status: 'pending-confirmation', agentId, toolName, pendingActionId,\n // expiresAt, message }\n // (NOTE: status is `pending-confirmation` and the field is `agentId`,\n // not `agent`). We also accept `awaiting-confirmation` / `agent` for\n // forward compat with older / alternative dispatchers.\n if (value.status === 'pending-confirmation' || value.status === 'awaiting-confirmation') {\n const pendingActionId =\n typeof value.pendingActionId === 'string' && value.pendingActionId.length > 0\n ? value.pendingActionId\n : null\n if (pendingActionId) {\n const agentId =\n typeof value.agentId === 'string'\n ? value.agentId\n : typeof value.agent === 'string'\n ? value.agent\n : undefined\n parts.push({\n componentId: 'mutation-preview-card',\n pendingActionId,\n payload: {\n pendingActionId,\n expiresAt: typeof value.expiresAt === 'string' ? value.expiresAt : undefined,\n agentId,\n toolName: typeof value.toolName === 'string' ? value.toolName : undefined,\n },\n key: `${toolCallId}:mutation-preview-card`,\n })\n }\n }\n\n // (2) Explicit single UI part.\n if (value.uiPart && typeof value.uiPart === 'object') {\n const part = value.uiPart as Record<string, unknown>\n if (typeof part.componentId === 'string' && part.componentId.length > 0) {\n parts.push({\n componentId: part.componentId,\n payload: part.payload,\n pendingActionId:\n typeof part.pendingActionId === 'string' ? part.pendingActionId : undefined,\n key: `${toolCallId}:${part.componentId}`,\n })\n }\n }\n\n // (3) Explicit list of UI parts.\n if (Array.isArray(value.uiParts)) {\n value.uiParts.forEach((entry, index) => {\n if (!entry || typeof entry !== 'object') return\n const part = entry as Record<string, unknown>\n if (typeof part.componentId !== 'string' || part.componentId.length === 0) return\n parts.push({\n componentId: part.componentId,\n payload: part.payload,\n pendingActionId:\n typeof part.pendingActionId === 'string' ? part.pendingActionId : undefined,\n key: `${toolCallId}:${index}:${part.componentId}`,\n })\n })\n }\n\n return parts\n}\n\nfunction updateToolCall(\n state: AssistantBuilderState,\n id: string,\n patch: Partial<AiChatToolCallSnapshot> & { toolName?: string },\n): AssistantBuilderState {\n if (!id) return state\n const idx = state.toolCalls.findIndex((entry) => entry.id === id)\n if (idx === -1) {\n const next: AiChatToolCallSnapshot = {\n id,\n toolName: patch.toolName ?? 'tool',\n state: patch.state ?? 'pending',\n input: patch.input,\n output: patch.output,\n errorMessage: patch.errorMessage,\n }\n return { ...state, toolCalls: [...state.toolCalls, next] }\n }\n const current = state.toolCalls[idx]\n const merged: AiChatToolCallSnapshot = {\n ...current,\n toolName: patch.toolName ?? current.toolName,\n state: patch.state ?? current.state,\n input: patch.input !== undefined ? patch.input : current.input,\n output: patch.output !== undefined ? patch.output : current.output,\n errorMessage: patch.errorMessage ?? current.errorMessage,\n }\n const nextCalls = state.toolCalls.slice()\n nextCalls[idx] = merged\n return { ...state, toolCalls: nextCalls }\n}\n\nfunction applyChunk(\n state: AssistantBuilderState,\n chunk: { type: string; [key: string]: unknown },\n): AssistantBuilderState {\n switch (chunk.type) {\n case 'text-delta':\n return {\n ...state,\n text: state.text + (typeof chunk.delta === 'string' ? chunk.delta : ''),\n }\n case 'reasoning-start':\n return { ...state, reasoningStreaming: true }\n case 'reasoning-delta':\n return {\n ...state,\n reasoning:\n state.reasoning + (typeof chunk.delta === 'string' ? chunk.delta : ''),\n reasoningStreaming: true,\n }\n case 'reasoning-end':\n return { ...state, reasoningStreaming: false }\n case 'tool-input-start':\n return updateToolCall(state, String(chunk.toolCallId ?? ''), {\n toolName: typeof chunk.toolName === 'string' ? chunk.toolName : undefined,\n state: 'pending',\n })\n case 'tool-input-available':\n return updateToolCall(state, String(chunk.toolCallId ?? ''), {\n toolName: typeof chunk.toolName === 'string' ? chunk.toolName : undefined,\n input: chunk.input,\n state: 'pending',\n })\n case 'tool-output-available': {\n const toolCallId = String(chunk.toolCallId ?? '')\n const next = updateToolCall(state, toolCallId, {\n output: chunk.output,\n state: 'complete',\n })\n // Phase 3 WS-C \u2014 surface ANY UI parts the tool output advertises:\n // the legacy `awaiting-confirmation` mutation envelope plus the\n // generic `{ uiPart }` / `{ uiParts: [...] }` shapes. This lets\n // module authors define their own dynamic cards (stats panels,\n // record summaries, charts\u2026) without touching the dispatcher or\n // the chat client.\n const newParts = extractUiPartsFromOutput(chunk.output, toolCallId)\n if (newParts.length === 0) return next\n const seen = new Set(next.uiParts.map((entry) => entry.key))\n const merged = [...next.uiParts]\n for (const part of newParts) {\n if (seen.has(part.key)) continue\n seen.add(part.key)\n merged.push(part)\n }\n if (merged.length === next.uiParts.length) return next\n return { ...next, uiParts: merged }\n }\n case 'tool-output-error':\n return updateToolCall(state, String(chunk.toolCallId ?? ''), {\n state: 'error',\n errorMessage:\n typeof chunk.errorText === 'string' ? chunk.errorText : 'Tool error',\n })\n case 'tool-input-error':\n return updateToolCall(state, String(chunk.toolCallId ?? ''), {\n toolName: typeof chunk.toolName === 'string' ? chunk.toolName : undefined,\n input: chunk.input,\n state: 'error',\n errorMessage:\n typeof chunk.errorText === 'string' ? chunk.errorText : 'Tool error',\n })\n default:\n return state\n }\n}\n\nfunction mergeAssistantMessage(\n current: AiChatMessage,\n state: AssistantBuilderState,\n): AiChatMessage {\n return {\n ...current,\n content: state.text,\n reasoning: state.reasoning ? state.reasoning : undefined,\n reasoningStreaming: state.reasoning ? state.reasoningStreaming : undefined,\n toolCalls: state.toolCalls.length > 0 ? state.toolCalls : undefined,\n uiParts: state.uiParts.length > 0 ? state.uiParts : undefined,\n }\n}\n\nfunction parseSseLines(buffer: string): { events: string[]; rest: string } {\n const events: string[] = []\n let rest = buffer\n for (;;) {\n const idx = rest.indexOf('\\n\\n')\n if (idx === -1) break\n events.push(rest.slice(0, idx))\n rest = rest.slice(idx + 2)\n }\n return { events, rest }\n}\n\nfunction extractDataPayload(eventBlock: string): string | null {\n const lines = eventBlock.split('\\n')\n const dataLines: string[] = []\n for (const line of lines) {\n if (line.startsWith('data: ')) {\n dataLines.push(line.slice(6))\n } else if (line.startsWith('data:')) {\n dataLines.push(line.slice(5))\n }\n }\n if (dataLines.length === 0) return null\n return dataLines.join('\\n')\n}\n\nasync function readErrorEnvelope(response: Response): Promise<AiChatErrorEnvelope> {\n try {\n const data = (await response.clone().json()) as\n | { error?: unknown; code?: unknown; message?: unknown }\n | null\n if (data && typeof data === 'object') {\n const rawMessage =\n (typeof data.error === 'string' && data.error) ||\n (typeof data.message === 'string' && data.message) ||\n ''\n const rawCode = typeof data.code === 'string' ? data.code : undefined\n if (rawMessage || rawCode) {\n return {\n code: rawCode,\n message: rawMessage || 'Agent dispatch failed.',\n }\n }\n }\n } catch {\n // Fall through to text fallback\n }\n const text = await response.text().catch(() => '')\n return { message: text || `Agent dispatch failed (${response.status}).` }\n}\n\nexport function useAiChat(input: UseAiChatInput): UseAiChatResult {\n const { agent, apiPath, pageContext, attachmentIds, debug, initialMessages, onError, conversationId: conversationIdInput } = input\n\n // Minted once on mount when the caller does not supply a conversationId.\n // The ref keeps the id stable across re-renders and is reused for every\n // turn so the Phase 3 WS-C `prepareMutation` idempotency hash stays\n // stable within the same chat. When the agent has a persisted session in\n // localStorage we re-hydrate the conversationId from it so re-opening the\n // chat continues the previous turn instead of starting fresh.\n const persistedRef = React.useRef<PersistedAiChatSession | null | 'unread'>('unread')\n if (persistedRef.current === 'unread') {\n // When the caller pins a `conversationId` (multi-tab session mode) we\n // read ONLY from that per-conversation slot. Falling back to the\n // legacy agent-only slot here would make every brand-new tab inherit\n // the previous tab's messages \u2014 the \"+ shows the same chat\" bug \u2014 so\n // unknown conversationIds always start clean. Without a pinned id we\n // keep the legacy single-session-per-agent layout for backward\n // compatibility.\n persistedRef.current =\n typeof conversationIdInput === 'string' && conversationIdInput.length > 0\n ? readPersistedSession(agent, conversationIdInput)\n : readPersistedSession(agent)\n }\n const persisted = persistedRef.current\n\n const mintedConversationIdRef = React.useRef<string | null>(null)\n if (mintedConversationIdRef.current === null) {\n mintedConversationIdRef.current = persisted?.conversationId ?? makeConversationId()\n }\n const effectiveConversationId =\n typeof conversationIdInput === 'string' && conversationIdInput.length > 0\n ? conversationIdInput\n : mintedConversationIdRef.current\n\n const [messages, setMessages] = React.useState<AiChatMessage[]>(() => {\n if (persisted && persisted.messages.length > 0) {\n return persisted.messages\n }\n return (initialMessages ?? []).map((entry) => ({\n id: makeMessageId(),\n role: entry.role,\n content: entry.content,\n }))\n })\n\n // Persist messages + conversationId on every change. Skip during in-flight\n // streaming so we do not write the same growing string on every chunk \u2014\n // the next idle tick captures the final assistant content.\n const [status, setStatusInternal] = React.useState<'idle' | 'submitting' | 'streaming'>('idle')\n React.useEffect(() => {\n if (status !== 'idle') return\n const persistKey =\n typeof conversationIdInput === 'string' && conversationIdInput.length > 0\n ? conversationIdInput\n : null\n if (messages.length === 0) {\n clearPersistedSession(agent, persistKey)\n return\n }\n writePersistedSession(\n agent,\n {\n v: SESSION_STORAGE_VERSION,\n conversationId: effectiveConversationId,\n messages,\n },\n persistKey,\n )\n }, [agent, conversationIdInput, effectiveConversationId, messages, status])\n const setStatus = setStatusInternal\n const [error, setError] = React.useState<AiChatErrorEnvelope | null>(null)\n const [lastRequestDebug, setLastRequestDebug] = React.useState<\n { url: string; body: unknown } | null\n >(null)\n const [lastResponseDebug, setLastResponseDebug] = React.useState<\n { status: number; text: string } | null\n >(null)\n\n const abortRef = React.useRef<AbortController | null>(null)\n const onErrorRef = React.useRef(onError)\n React.useEffect(() => {\n onErrorRef.current = onError\n }, [onError])\n\n const emitError = React.useCallback((envelope: AiChatErrorEnvelope) => {\n setError(envelope)\n try {\n onErrorRef.current?.(envelope)\n } catch {\n // UI layer must never throw because a caller-supplied error handler\n // misbehaved.\n }\n }, [])\n\n const cancel = React.useCallback(() => {\n if (abortRef.current) {\n abortRef.current.abort()\n abortRef.current = null\n }\n setStatus('idle')\n }, [])\n\n const reset = React.useCallback(() => {\n cancel()\n setMessages([])\n setError(null)\n setLastRequestDebug(null)\n setLastResponseDebug(null)\n clearPersistedSession(agent)\n mintedConversationIdRef.current = makeConversationId()\n }, [agent, cancel])\n\n const sendMessage = React.useCallback(\n async (textInput: string, files?: AiChatMessageFile[]) => {\n const trimmed = textInput.trim()\n if (!trimmed) return\n if (abortRef.current) {\n abortRef.current.abort()\n }\n\n setError(null)\n const userMessage: AiChatMessage = {\n id: makeMessageId(),\n role: 'user',\n content: trimmed,\n files: files && files.length > 0 ? files : undefined,\n }\n const assistantMessage: AiChatMessage = {\n id: makeMessageId(),\n role: 'assistant',\n content: '',\n }\n const assistantId = assistantMessage.id\n // Snapshot prior messages for request payload so the dispatcher sees the\n // full turn history including the just-added user message.\n const outgoingHistory = [...messages, userMessage]\n setMessages([...outgoingHistory, assistantMessage])\n setStatus('submitting')\n\n const controller = new AbortController()\n abortRef.current = controller\n\n const url = getTransportEndpoint(agent, apiPath)\n const body = {\n messages: outgoingHistory.map((message) => ({\n role: message.role,\n content: message.content,\n })),\n pageContext,\n attachmentIds,\n debug,\n conversationId: effectiveConversationId,\n }\n setLastRequestDebug({ url, body })\n\n let response: Response\n try {\n response = await apiFetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Accept: 'text/event-stream, text/plain, application/json',\n },\n body: JSON.stringify(body),\n signal: controller.signal,\n })\n } catch (requestError) {\n if ((requestError as { name?: string })?.name === 'AbortError') {\n setStatus('idle')\n abortRef.current = null\n return\n }\n const message =\n requestError instanceof Error\n ? requestError.message\n : 'Network request failed.'\n emitError({ message })\n setStatus('idle')\n abortRef.current = null\n return\n }\n\n if (!response.ok) {\n const envelope = await readErrorEnvelope(response)\n setLastResponseDebug({ status: response.status, text: envelope.message })\n emitError(envelope)\n setStatus('idle')\n setMessages((current) => current.filter((entry) => entry.id !== assistantId))\n abortRef.current = null\n return\n }\n\n const bodyStream = response.body\n if (!bodyStream) {\n setLastResponseDebug({ status: response.status, text: '' })\n setStatus('idle')\n abortRef.current = null\n return\n }\n\n const headerGet = (name: string): string | null => {\n const headers = (response as { headers?: { get?: (k: string) => string | null } })\n .headers\n if (!headers || typeof headers.get !== 'function') return null\n try {\n return headers.get(name)\n } catch {\n return null\n }\n }\n const isUiMessageStream =\n headerGet('x-vercel-ai-ui-message-stream') !== null ||\n (headerGet('content-type') ?? '').includes('event-stream')\n\n setStatus('streaming')\n const reader = bodyStream.getReader()\n const decoder = new TextDecoder()\n let streamedRaw = ''\n let builder = createBuilder()\n let sseBuffer = ''\n const flushUiMessageBuffer = (extra?: string) => {\n if (extra) sseBuffer += extra\n const { events, rest } = parseSseLines(sseBuffer)\n sseBuffer = rest\n for (const block of events) {\n const data = extractDataPayload(block)\n if (!data) continue\n if (data === '[DONE]') continue\n try {\n const parsed = JSON.parse(data) as { type?: string }\n if (parsed && typeof parsed.type === 'string') {\n builder = applyChunk(builder, parsed as { type: string })\n }\n } catch {\n // Tolerate malformed events / SSE comments.\n }\n }\n }\n try {\n while (true) {\n const { value, done } = await reader.read()\n if (done) break\n if (!value) continue\n const piece = decoder.decode(value, { stream: true })\n if (!piece) continue\n streamedRaw += piece\n\n if (isUiMessageStream) {\n flushUiMessageBuffer(piece)\n } else {\n // Plain text fallback (legacy `toTextStreamResponse`).\n builder = { ...builder, text: streamedRaw }\n }\n const snapshotBuilder = builder\n setMessages((current) =>\n current.map((entry) =>\n entry.id === assistantId\n ? mergeAssistantMessage(entry, snapshotBuilder)\n : entry,\n ),\n )\n }\n const tail = decoder.decode()\n if (tail) {\n streamedRaw += tail\n if (isUiMessageStream) {\n flushUiMessageBuffer(tail)\n } else {\n builder = { ...builder, text: streamedRaw }\n }\n }\n if (isUiMessageStream && sseBuffer.length > 0) {\n flushUiMessageBuffer('\\n\\n')\n }\n builder = { ...builder, reasoningStreaming: false }\n const finalSnapshot = builder\n setMessages((current) =>\n current.map((entry) =>\n entry.id === assistantId\n ? mergeAssistantMessage(entry, finalSnapshot)\n : entry,\n ),\n )\n setLastResponseDebug({ status: response.status, text: streamedRaw })\n const isEmpty =\n !builder.text.trim() && builder.toolCalls.length === 0 && !builder.reasoning\n if (isEmpty) {\n emitError({\n code: 'empty_response',\n message:\n 'The AI agent returned an empty response. This usually means the LLM provider rejected the request (invalid API key, rate limit, or model error). Check your server logs for details.',\n })\n setMessages((current) => current.filter((entry) => entry.id !== assistantId))\n }\n } catch (streamError) {\n if ((streamError as { name?: string })?.name === 'AbortError') {\n // Cancelled by the user \u2014 keep whatever we have so far and exit\n // quietly.\n } else {\n const rawMessage =\n streamError instanceof Error\n ? streamError.message\n : 'Stream interrupted.'\n // LLM provider errors (auth failures, rate limits, invalid tool\n // schemas) surface as stream read errors. Include a hint so the\n // operator can check server logs for the full stack trace.\n const message = rawMessage.includes('API')\n ? rawMessage\n : `${rawMessage} \u2014 check server logs for LLM provider details.`\n emitError({ code: 'stream_error', message })\n // Remove the empty assistant placeholder so the error alert is\n // the only visible feedback.\n setMessages((current) => current.filter((entry) => entry.id !== assistantId))\n }\n } finally {\n reader.releaseLock()\n if (abortRef.current === controller) {\n abortRef.current = null\n }\n setStatus('idle')\n }\n },\n [agent, apiPath, attachmentIds, debug, effectiveConversationId, emitError, messages, pageContext],\n )\n\n React.useEffect(() => {\n return () => {\n if (abortRef.current) {\n abortRef.current.abort()\n abortRef.current = null\n }\n }\n }, [])\n\n return {\n messages,\n status,\n error,\n lastRequestDebug,\n lastResponseDebug,\n conversationId: effectiveConversationId,\n sendMessage,\n cancel,\n reset,\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { createAiAgentTransport } from '@open-mercato/ai-assistant/modules/ai_assistant/lib/agent-transport'\nimport { apiFetch } from '../backend/utils/api'\n\n/**\n * Chat message shape used by {@link AiChat}. Kept intentionally minimal so the\n * component stays independent of the AI SDK's evolving `UIMessage` type. The\n * dispatcher route (`POST /api/ai_assistant/ai/chat`) accepts exactly this\n * shape for `messages`.\n */\nexport interface AiChatMessageFile {\n name: string\n type: string\n previewUrl?: string\n}\n\nexport interface AiChatToolCallSnapshot {\n id: string\n toolName: string\n state: 'pending' | 'complete' | 'error'\n input?: unknown\n output?: unknown\n errorMessage?: string\n}\n\nexport interface AiChatMessageUiPart {\n componentId: string\n payload?: unknown\n pendingActionId?: string\n /** Stable id used as React key when rendering. */\n key: string\n}\n\nexport interface AiChatMessage {\n id: string\n role: 'user' | 'assistant'\n content: string\n files?: AiChatMessageFile[]\n reasoning?: string\n reasoningStreaming?: boolean\n toolCalls?: AiChatToolCallSnapshot[]\n /**\n * UI parts emitted by the agent during this message's lifecycle. Today\n * the only producer is `prepareMutation` (mutation approval flow):\n * the dispatcher's mutation tool returns an `awaiting-confirmation`\n * envelope, useAiChat parses it and attaches a `mutation-preview-card`\n * part here so AiChat can render the approval card inline. Phase 3\n * WS-C wiring \u2014 without this, the `MutationPreviewCard` registered in\n * the UI-part registry never surfaces.\n */\n uiParts?: AiChatMessageUiPart[]\n}\n\nexport interface UseAiChatInput {\n agent: string\n apiPath?: string\n pageContext?: Record<string, unknown>\n attachmentIds?: string[]\n debug?: boolean\n initialMessages?: Array<Pick<AiChatMessage, 'role' | 'content'>>\n onError?: (err: { code?: string; message: string }) => void\n /**\n * Optional stable conversation id. When provided, the same id is forwarded\n * to the dispatcher on every turn so `prepareMutation`'s idempotency hash\n * (Step 5.6) stays stable across mutation preview / confirm / retry cycles.\n * When omitted, the hook mints a fresh random id once on mount and reuses\n * it for the lifetime of the component \u2014 callers can still override via\n * props at any time to reset the conversation.\n */\n conversationId?: string\n}\n\nexport interface AiChatErrorEnvelope {\n code?: string\n message: string\n}\n\nexport interface UseAiChatResult {\n messages: AiChatMessage[]\n status: 'idle' | 'submitting' | 'streaming'\n error: AiChatErrorEnvelope | null\n lastRequestDebug: { url: string; body: unknown } | null\n lastResponseDebug: { status: number; text: string } | null\n /**\n * The conversation id currently in use for this chat instance. Equal to\n * the caller-provided `conversationId` input when one is supplied;\n * otherwise the random id minted on mount. Stable across re-renders for a\n * given mount (Phase 3 WS-D contract with `prepareMutation`).\n */\n conversationId: string\n sendMessage: (input: string, files?: AiChatMessageFile[]) => Promise<void>\n cancel: () => void\n reset: () => void\n}\n\nfunction makeMessageId(): string {\n const random = Math.random().toString(36).slice(2, 10)\n const time = Date.now().toString(36)\n return `msg_${time}_${random}`\n}\n\nfunction makeConversationId(): string {\n // Use crypto.randomUUID() when the browser exposes it (all evergreen\n // runtimes do), otherwise fall back to a low-entropy token that is still\n // unique enough for the idempotency-hash use case.\n const g = globalThis as unknown as { crypto?: { randomUUID?: () => string } }\n if (g.crypto && typeof g.crypto.randomUUID === 'function') {\n try {\n return g.crypto.randomUUID()\n } catch {\n // fall through to the random fallback\n }\n }\n const rand = () => Math.random().toString(16).slice(2, 10)\n return `conv_${Date.now().toString(16)}_${rand()}${rand()}`\n}\n\nconst SESSION_STORAGE_PREFIX = 'om-ai-chat:'\nconst SESSION_STORAGE_VERSION = 1\nconst SESSION_UPDATED_EVENT = 'om-ai-chat-session-updated'\nconst SESSION_STREAM_STATE_EVENT = 'om-ai-chat-session-stream-state'\nconst activeSessionStreams = new Set<string>()\n\ninterface PersistedAiChatSession {\n v: number\n conversationId: string\n messages: AiChatMessage[]\n}\n\nfunction getSessionStorageKey(agent: string, conversationId?: string | null): string {\n // When the caller pins a `conversationId` (e.g. via the AiChatSessions\n // provider's tabs), namespace the persisted slot per session so multiple\n // open conversations for the same agent don't overwrite each other. The\n // legacy single-session-per-agent layout (no externally-supplied id) is\n // kept for backward compatibility with code that still relies on it.\n if (typeof conversationId === 'string' && conversationId.length > 0) {\n return `${SESSION_STORAGE_PREFIX}${agent}:${conversationId}`\n }\n return `${SESSION_STORAGE_PREFIX}${agent}`\n}\n\nfunction writeSessionStreamState(storageKey: string, active: boolean): void {\n if (active) {\n activeSessionStreams.add(storageKey)\n } else {\n activeSessionStreams.delete(storageKey)\n }\n if (typeof window === 'undefined') return\n try {\n window.dispatchEvent(\n new CustomEvent(SESSION_STREAM_STATE_EVENT, {\n detail: { key: storageKey, active },\n }),\n )\n } catch {\n // ignore event dispatch failures in non-browser test environments\n }\n}\n\nfunction isSessionStreamActive(storageKey: string): boolean {\n return activeSessionStreams.has(storageKey)\n}\n\nfunction readPersistedSession(\n agent: string,\n conversationId?: string | null,\n): PersistedAiChatSession | null {\n if (typeof window === 'undefined') return null\n try {\n const raw = window.localStorage.getItem(getSessionStorageKey(agent, conversationId))\n if (!raw) return null\n const parsed = JSON.parse(raw) as PersistedAiChatSession | null\n if (!parsed || parsed.v !== SESSION_STORAGE_VERSION) return null\n if (typeof parsed.conversationId !== 'string') return null\n if (!Array.isArray(parsed.messages)) return null\n const messages = parsed.messages.filter((entry): entry is AiChatMessage => {\n return (\n !!entry &&\n typeof entry === 'object' &&\n typeof (entry as AiChatMessage).id === 'string' &&\n typeof (entry as AiChatMessage).content === 'string' &&\n ((entry as AiChatMessage).role === 'user' || (entry as AiChatMessage).role === 'assistant')\n )\n })\n return { v: SESSION_STORAGE_VERSION, conversationId: parsed.conversationId, messages }\n } catch {\n return null\n }\n}\n\nfunction writePersistedSession(\n agent: string,\n session: PersistedAiChatSession,\n conversationId?: string | null,\n options?: { notify?: boolean },\n): void {\n if (typeof window === 'undefined') return\n try {\n // Strip transient blob/object preview URLs before persisting (they would\n // not survive a reload). Self-contained `data:` URLs are kept so image\n // previews come back unchanged after the chat is reopened \u2014 public\n // attachment URLs are intentionally not used because the LLM provider\n // cannot reach a localhost origin and we want a single durable shape\n // that works for both transport and reload.\n const messages = session.messages.map((message) => {\n if (!message.files || message.files.length === 0) return message\n const safeFiles = message.files.map(({ name, type, previewUrl }) => {\n const durable =\n typeof previewUrl === 'string' && previewUrl.startsWith('data:')\n ? previewUrl\n : undefined\n return durable ? { name, type, previewUrl: durable } : { name, type }\n })\n return { ...message, files: safeFiles }\n })\n const storageKey = getSessionStorageKey(agent, conversationId)\n window.localStorage.setItem(storageKey, JSON.stringify({ ...session, messages }))\n if (options?.notify) {\n window.dispatchEvent(\n new CustomEvent(SESSION_UPDATED_EVENT, {\n detail: { key: storageKey, agent, conversationId: session.conversationId },\n }),\n )\n }\n } catch {\n // Quota exceeded / privacy mode \u2014 silently drop persistence.\n }\n}\n\nfunction clearPersistedSession(agent: string, conversationId?: string | null): void {\n if (typeof window === 'undefined') return\n try {\n window.localStorage.removeItem(getSessionStorageKey(agent, conversationId))\n } catch {\n // ignore\n }\n}\n\nfunction getTransportEndpoint(agent: string, apiPath?: string): string {\n // Reuse the transport factory so UI consumers share the dispatcher URL\n // convention with server-side callers (e.g. runAiAgentText / Playwright\n // fixtures). The factory returns a ChatTransport<UI_MESSAGE> whose internal\n // endpoint we do not directly read \u2014 instead we reconstruct the same URL\n // shape here so downstream error handling stays deterministic.\n //\n // When the AI SDK exposes a public endpoint getter (or the stream format\n // switches from plain text to UIMessageChunk) we can call\n // transport.sendMessages(...) directly.\n const transport = createAiAgentTransport({ agentId: agent, endpoint: apiPath })\n void transport\n const base = apiPath && apiPath.length > 0 ? apiPath : '/api/ai_assistant/ai/chat'\n const separator = base.includes('?') ? '&' : '?'\n return `${base}${separator}agent=${encodeURIComponent(agent)}`\n}\n\ninterface AssistantBuilderState {\n text: string\n reasoning: string\n reasoningStreaming: boolean\n toolCalls: AiChatToolCallSnapshot[]\n uiParts: AiChatMessageUiPart[]\n}\n\nfunction createBuilder(): AssistantBuilderState {\n return { text: '', reasoning: '', reasoningStreaming: false, toolCalls: [], uiParts: [] }\n}\n\n/**\n * Generic extractor for UI parts emitted by tool outputs. A tool can\n * surface inline UI to the chat by returning JSON in any of these\n * shapes \u2014 each tool call produces zero or more UI parts:\n *\n * 1. The dispatcher's mutation envelope:\n * `{ status: 'awaiting-confirmation', pendingActionId, expiresAt,\n * agent, toolName, message }`\n * \u2192 synthesizes a `mutation-preview-card` part (the registered\n * card fetches the live diff via `useAiPendingActionPolling`).\n *\n * 2. A single explicit UI part:\n * `{ uiPart: { componentId, payload?, pendingActionId? } }`\n *\n * 3. Multiple explicit UI parts:\n * `{ uiParts: [{ componentId, payload? }, ...] }`\n *\n * Tool authors only need to JSON-encode an object whose `uiPart` /\n * `uiParts` reference component ids that the host has registered on\n * `defaultAiUiPartRegistry` (or a scoped registry passed through\n * `<AiChat registry={...}/>`). Unknown component ids fall back to the\n * `UnknownUiPartPlaceholder` so an unregistered id never blows up the\n * transcript.\n */\nfunction extractUiPartsFromOutput(\n output: unknown,\n toolCallId: string,\n): AiChatMessageUiPart[] {\n let parsed: unknown = output\n if (typeof output === 'string') {\n const trimmed = output.trim()\n if (!trimmed.startsWith('{') && !trimmed.startsWith('[')) return []\n try {\n parsed = JSON.parse(trimmed)\n } catch {\n return []\n }\n }\n if (!parsed || typeof parsed !== 'object') return []\n const value = parsed as Record<string, unknown>\n const parts: AiChatMessageUiPart[] = []\n\n // (1) Mutation approval envelope. The dispatcher's `prepareMutation`\n // interceptor in `agent-tools.ts` formats the result via\n // `formatPendingActionToolResult` as\n // { status: 'pending-confirmation', agentId, toolName, pendingActionId,\n // expiresAt, message }\n // (NOTE: status is `pending-confirmation` and the field is `agentId`,\n // not `agent`). We also accept `awaiting-confirmation` / `agent` for\n // forward compat with older / alternative dispatchers.\n if (value.status === 'pending-confirmation' || value.status === 'awaiting-confirmation') {\n const pendingActionId =\n typeof value.pendingActionId === 'string' && value.pendingActionId.length > 0\n ? value.pendingActionId\n : null\n if (pendingActionId) {\n const agentId =\n typeof value.agentId === 'string'\n ? value.agentId\n : typeof value.agent === 'string'\n ? value.agent\n : undefined\n parts.push({\n componentId: 'mutation-preview-card',\n pendingActionId,\n payload: {\n pendingActionId,\n expiresAt: typeof value.expiresAt === 'string' ? value.expiresAt : undefined,\n agentId,\n toolName: typeof value.toolName === 'string' ? value.toolName : undefined,\n },\n key: `${toolCallId}:mutation-preview-card`,\n })\n }\n }\n\n // (2) Explicit single UI part.\n if (value.uiPart && typeof value.uiPart === 'object') {\n const part = value.uiPart as Record<string, unknown>\n if (typeof part.componentId === 'string' && part.componentId.length > 0) {\n parts.push({\n componentId: part.componentId,\n payload: part.payload,\n pendingActionId:\n typeof part.pendingActionId === 'string' ? part.pendingActionId : undefined,\n key: `${toolCallId}:${part.componentId}`,\n })\n }\n }\n\n // (3) Explicit list of UI parts.\n if (Array.isArray(value.uiParts)) {\n value.uiParts.forEach((entry, index) => {\n if (!entry || typeof entry !== 'object') return\n const part = entry as Record<string, unknown>\n if (typeof part.componentId !== 'string' || part.componentId.length === 0) return\n parts.push({\n componentId: part.componentId,\n payload: part.payload,\n pendingActionId:\n typeof part.pendingActionId === 'string' ? part.pendingActionId : undefined,\n key: `${toolCallId}:${index}:${part.componentId}`,\n })\n })\n }\n\n return parts\n}\n\nfunction updateToolCall(\n state: AssistantBuilderState,\n id: string,\n patch: Partial<AiChatToolCallSnapshot> & { toolName?: string },\n): AssistantBuilderState {\n if (!id) return state\n const idx = state.toolCalls.findIndex((entry) => entry.id === id)\n if (idx === -1) {\n const next: AiChatToolCallSnapshot = {\n id,\n toolName: patch.toolName ?? 'tool',\n state: patch.state ?? 'pending',\n input: patch.input,\n output: patch.output,\n errorMessage: patch.errorMessage,\n }\n return { ...state, toolCalls: [...state.toolCalls, next] }\n }\n const current = state.toolCalls[idx]\n const merged: AiChatToolCallSnapshot = {\n ...current,\n toolName: patch.toolName ?? current.toolName,\n state: patch.state ?? current.state,\n input: patch.input !== undefined ? patch.input : current.input,\n output: patch.output !== undefined ? patch.output : current.output,\n errorMessage: patch.errorMessage ?? current.errorMessage,\n }\n const nextCalls = state.toolCalls.slice()\n nextCalls[idx] = merged\n return { ...state, toolCalls: nextCalls }\n}\n\nfunction applyChunk(\n state: AssistantBuilderState,\n chunk: { type: string; [key: string]: unknown },\n): AssistantBuilderState {\n switch (chunk.type) {\n case 'text-delta':\n return {\n ...state,\n text: state.text + (typeof chunk.delta === 'string' ? chunk.delta : ''),\n }\n case 'reasoning-start':\n return { ...state, reasoningStreaming: true }\n case 'reasoning-delta':\n return {\n ...state,\n reasoning:\n state.reasoning + (typeof chunk.delta === 'string' ? chunk.delta : ''),\n reasoningStreaming: true,\n }\n case 'reasoning-end':\n return { ...state, reasoningStreaming: false }\n case 'tool-input-start':\n return updateToolCall(state, String(chunk.toolCallId ?? ''), {\n toolName: typeof chunk.toolName === 'string' ? chunk.toolName : undefined,\n state: 'pending',\n })\n case 'tool-input-available':\n return updateToolCall(state, String(chunk.toolCallId ?? ''), {\n toolName: typeof chunk.toolName === 'string' ? chunk.toolName : undefined,\n input: chunk.input,\n state: 'pending',\n })\n case 'tool-output-available': {\n const toolCallId = String(chunk.toolCallId ?? '')\n const next = updateToolCall(state, toolCallId, {\n output: chunk.output,\n state: 'complete',\n })\n // Phase 3 WS-C \u2014 surface ANY UI parts the tool output advertises:\n // the legacy `awaiting-confirmation` mutation envelope plus the\n // generic `{ uiPart }` / `{ uiParts: [...] }` shapes. This lets\n // module authors define their own dynamic cards (stats panels,\n // record summaries, charts\u2026) without touching the dispatcher or\n // the chat client.\n const newParts = extractUiPartsFromOutput(chunk.output, toolCallId)\n if (newParts.length === 0) return next\n const seen = new Set(next.uiParts.map((entry) => entry.key))\n const merged = [...next.uiParts]\n for (const part of newParts) {\n if (seen.has(part.key)) continue\n seen.add(part.key)\n merged.push(part)\n }\n if (merged.length === next.uiParts.length) return next\n return { ...next, uiParts: merged }\n }\n case 'tool-output-error':\n return updateToolCall(state, String(chunk.toolCallId ?? ''), {\n state: 'error',\n errorMessage:\n typeof chunk.errorText === 'string' ? chunk.errorText : 'Tool error',\n })\n case 'tool-input-error':\n return updateToolCall(state, String(chunk.toolCallId ?? ''), {\n toolName: typeof chunk.toolName === 'string' ? chunk.toolName : undefined,\n input: chunk.input,\n state: 'error',\n errorMessage:\n typeof chunk.errorText === 'string' ? chunk.errorText : 'Tool error',\n })\n default:\n return state\n }\n}\n\nfunction mergeAssistantMessage(\n current: AiChatMessage,\n state: AssistantBuilderState,\n): AiChatMessage {\n return {\n ...current,\n content: state.text,\n reasoning: state.reasoning ? state.reasoning : undefined,\n reasoningStreaming: state.reasoning ? state.reasoningStreaming : undefined,\n toolCalls: state.toolCalls.length > 0 ? state.toolCalls : undefined,\n uiParts: state.uiParts.length > 0 ? state.uiParts : undefined,\n }\n}\n\nfunction parseSseLines(buffer: string): { events: string[]; rest: string } {\n const events: string[] = []\n let rest = buffer\n for (;;) {\n const idx = rest.indexOf('\\n\\n')\n if (idx === -1) break\n events.push(rest.slice(0, idx))\n rest = rest.slice(idx + 2)\n }\n return { events, rest }\n}\n\nfunction extractDataPayload(eventBlock: string): string | null {\n const lines = eventBlock.split('\\n')\n const dataLines: string[] = []\n for (const line of lines) {\n if (line.startsWith('data: ')) {\n dataLines.push(line.slice(6))\n } else if (line.startsWith('data:')) {\n dataLines.push(line.slice(5))\n }\n }\n if (dataLines.length === 0) return null\n return dataLines.join('\\n')\n}\n\nasync function readErrorEnvelope(response: Response): Promise<AiChatErrorEnvelope> {\n try {\n const data = (await response.clone().json()) as\n | { error?: unknown; code?: unknown; message?: unknown }\n | null\n if (data && typeof data === 'object') {\n const rawMessage =\n (typeof data.error === 'string' && data.error) ||\n (typeof data.message === 'string' && data.message) ||\n ''\n const rawCode = typeof data.code === 'string' ? data.code : undefined\n if (rawMessage || rawCode) {\n return {\n code: rawCode,\n message: rawMessage || 'Agent dispatch failed.',\n }\n }\n }\n } catch {\n // Fall through to text fallback\n }\n const text = await response.text().catch(() => '')\n return { message: text || `Agent dispatch failed (${response.status}).` }\n}\n\nexport function useAiChat(input: UseAiChatInput): UseAiChatResult {\n const { agent, apiPath, pageContext, attachmentIds, debug, initialMessages, onError, conversationId: conversationIdInput } = input\n\n // Minted once on mount when the caller does not supply a conversationId.\n // The ref keeps the id stable across re-renders and is reused for every\n // turn so the Phase 3 WS-C `prepareMutation` idempotency hash stays\n // stable within the same chat. When the agent has a persisted session in\n // localStorage we re-hydrate the conversationId from it so re-opening the\n // chat continues the previous turn instead of starting fresh.\n const persistedRef = React.useRef<PersistedAiChatSession | null | 'unread'>('unread')\n if (persistedRef.current === 'unread') {\n // When the caller pins a `conversationId` (multi-tab session mode) we\n // read ONLY from that per-conversation slot. Falling back to the\n // legacy agent-only slot here would make every brand-new tab inherit\n // the previous tab's messages \u2014 the \"+ shows the same chat\" bug \u2014 so\n // unknown conversationIds always start clean. Without a pinned id we\n // keep the legacy single-session-per-agent layout for backward\n // compatibility.\n persistedRef.current =\n typeof conversationIdInput === 'string' && conversationIdInput.length > 0\n ? readPersistedSession(agent, conversationIdInput)\n : readPersistedSession(agent)\n }\n const persisted = persistedRef.current\n\n const mintedConversationIdRef = React.useRef<string | null>(null)\n if (mintedConversationIdRef.current === null) {\n mintedConversationIdRef.current = persisted?.conversationId ?? makeConversationId()\n }\n const effectiveConversationId =\n typeof conversationIdInput === 'string' && conversationIdInput.length > 0\n ? conversationIdInput\n : mintedConversationIdRef.current\n const persistKey =\n typeof conversationIdInput === 'string' && conversationIdInput.length > 0\n ? conversationIdInput\n : null\n const sessionStorageKey = getSessionStorageKey(agent, persistKey)\n\n const [messages, setMessages] = React.useState<AiChatMessage[]>(() => {\n if (persisted && persisted.messages.length > 0) {\n return persisted.messages\n }\n return (initialMessages ?? []).map((entry) => ({\n id: makeMessageId(),\n role: entry.role,\n content: entry.content,\n }))\n })\n\n // Persist messages + conversationId on every change. Skip during in-flight\n // streaming so we do not write the same growing string on every chunk \u2014\n // the next idle tick captures the final assistant content.\n const [status, setStatusInternal] = React.useState<'idle' | 'submitting' | 'streaming'>(() =>\n isSessionStreamActive(sessionStorageKey) ? 'streaming' : 'idle',\n )\n // Refs mirror the latest persist state so the unmount cleanup can flush\n // an in-flight assistant message to localStorage even though the streaming\n // skip above (and React's stale-closure semantics) would otherwise lose it.\n // Without this, closing the dock or switching agents while the assistant\n // is \"Thinking\" abandons the partial reply (issue #1816).\n const latestMessagesRef = React.useRef<AiChatMessage[]>(messages)\n const persistKeyRef = React.useRef<string | null>(null)\n const agentRef = React.useRef<string>(agent)\n const effectiveConversationIdRef = React.useRef<string>(effectiveConversationId)\n const sessionStorageKeyRef = React.useRef<string>(sessionStorageKey)\n const mountedRef = React.useRef(true)\n React.useEffect(() => {\n latestMessagesRef.current = messages\n }, [messages])\n React.useEffect(() => {\n persistKeyRef.current =\n typeof conversationIdInput === 'string' && conversationIdInput.length > 0\n ? conversationIdInput\n : null\n }, [conversationIdInput])\n React.useEffect(() => {\n agentRef.current = agent\n }, [agent])\n React.useEffect(() => {\n effectiveConversationIdRef.current = effectiveConversationId\n }, [effectiveConversationId])\n React.useEffect(() => {\n sessionStorageKeyRef.current = sessionStorageKey\n }, [sessionStorageKey])\n React.useEffect(() => {\n if (status !== 'idle') return\n if (messages.length === 0) {\n clearPersistedSession(agent, persistKey)\n return\n }\n writePersistedSession(\n agent,\n {\n v: SESSION_STORAGE_VERSION,\n conversationId: effectiveConversationId,\n messages,\n },\n persistKey,\n )\n }, [agent, effectiveConversationId, messages, persistKey, status])\n const persistSnapshot = React.useCallback((snapshot: AiChatMessage[], notify = false) => {\n if (snapshot.length === 0) return\n writePersistedSession(\n agentRef.current,\n {\n v: SESSION_STORAGE_VERSION,\n conversationId: effectiveConversationIdRef.current,\n messages: snapshot,\n },\n persistKeyRef.current,\n { notify },\n )\n }, [])\n\n const updateMessages = React.useCallback(\n (\n updater:\n | AiChatMessage[]\n | ((current: AiChatMessage[]) => AiChatMessage[]),\n options?: { persistWhenUnmounted?: boolean },\n ) => {\n const current = latestMessagesRef.current\n const next = typeof updater === 'function' ? updater(current) : updater\n latestMessagesRef.current = next\n if (mountedRef.current) {\n setMessages(next)\n } else if (options?.persistWhenUnmounted) {\n persistSnapshot(next, true)\n }\n },\n [persistSnapshot],\n )\n\n const setStatus = React.useCallback((next: 'idle' | 'submitting' | 'streaming') => {\n if (mountedRef.current) {\n setStatusInternal(next)\n }\n }, [])\n const [error, setError] = React.useState<AiChatErrorEnvelope | null>(null)\n const [lastRequestDebug, setLastRequestDebug] = React.useState<\n { url: string; body: unknown } | null\n >(null)\n const [lastResponseDebug, setLastResponseDebug] = React.useState<\n { status: number; text: string } | null\n >(null)\n\n const abortRef = React.useRef<AbortController | null>(null)\n const onErrorRef = React.useRef(onError)\n React.useEffect(() => {\n onErrorRef.current = onError\n }, [onError])\n\n const emitError = React.useCallback((envelope: AiChatErrorEnvelope) => {\n if (mountedRef.current) {\n setError(envelope)\n try {\n onErrorRef.current?.(envelope)\n } catch {\n // UI layer must never throw because a caller-supplied error handler\n // misbehaved.\n }\n }\n }, [])\n\n const cancel = React.useCallback(() => {\n if (abortRef.current) {\n abortRef.current.abort()\n abortRef.current = null\n }\n writeSessionStreamState(sessionStorageKeyRef.current, false)\n setStatus('idle')\n }, [setStatus])\n\n const reset = React.useCallback(() => {\n cancel()\n updateMessages([])\n setError(null)\n setLastRequestDebug(null)\n setLastResponseDebug(null)\n clearPersistedSession(agent, persistKey)\n mintedConversationIdRef.current = makeConversationId()\n }, [agent, cancel, persistKey, updateMessages])\n\n const sendMessage = React.useCallback(\n async (textInput: string, files?: AiChatMessageFile[]) => {\n const trimmed = textInput.trim()\n if (!trimmed) return\n if (abortRef.current) {\n abortRef.current.abort()\n }\n\n setError(null)\n const userMessage: AiChatMessage = {\n id: makeMessageId(),\n role: 'user',\n content: trimmed,\n files: files && files.length > 0 ? files : undefined,\n }\n const assistantMessage: AiChatMessage = {\n id: makeMessageId(),\n role: 'assistant',\n content: '',\n }\n const assistantId = assistantMessage.id\n // Snapshot prior messages for request payload so the dispatcher sees the\n // full turn history including the just-added user message.\n const outgoingHistory = [...latestMessagesRef.current, userMessage]\n updateMessages([...outgoingHistory, assistantMessage], { persistWhenUnmounted: true })\n setStatus('submitting')\n writeSessionStreamState(sessionStorageKey, true)\n\n const controller = new AbortController()\n abortRef.current = controller\n\n const url = getTransportEndpoint(agent, apiPath)\n const body = {\n messages: outgoingHistory.map((message) => ({\n role: message.role,\n content: message.content,\n })),\n pageContext,\n attachmentIds,\n debug,\n conversationId: effectiveConversationId,\n }\n if (mountedRef.current) {\n setLastRequestDebug({ url, body })\n }\n\n let response: Response\n try {\n response = await apiFetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Accept: 'text/event-stream, text/plain, application/json',\n },\n body: JSON.stringify(body),\n signal: controller.signal,\n })\n } catch (requestError) {\n if ((requestError as { name?: string })?.name === 'AbortError') {\n writeSessionStreamState(sessionStorageKey, false)\n setStatus('idle')\n abortRef.current = null\n return\n }\n const message =\n requestError instanceof Error\n ? requestError.message\n : 'Network request failed.'\n emitError({ message })\n writeSessionStreamState(sessionStorageKey, false)\n setStatus('idle')\n abortRef.current = null\n return\n }\n\n if (!response.ok) {\n const envelope = await readErrorEnvelope(response)\n if (mountedRef.current) {\n setLastResponseDebug({ status: response.status, text: envelope.message })\n }\n emitError(envelope)\n writeSessionStreamState(sessionStorageKey, false)\n setStatus('idle')\n updateMessages((current) => current.filter((entry) => entry.id !== assistantId), {\n persistWhenUnmounted: true,\n })\n abortRef.current = null\n return\n }\n\n const bodyStream = response.body\n if (!bodyStream) {\n if (mountedRef.current) {\n setLastResponseDebug({ status: response.status, text: '' })\n }\n writeSessionStreamState(sessionStorageKey, false)\n setStatus('idle')\n abortRef.current = null\n return\n }\n\n const headerGet = (name: string): string | null => {\n const headers = (response as { headers?: { get?: (k: string) => string | null } })\n .headers\n if (!headers || typeof headers.get !== 'function') return null\n try {\n return headers.get(name)\n } catch {\n return null\n }\n }\n const isUiMessageStream =\n headerGet('x-vercel-ai-ui-message-stream') !== null ||\n (headerGet('content-type') ?? '').includes('event-stream')\n\n setStatus('streaming')\n const reader = bodyStream.getReader()\n const decoder = new TextDecoder()\n let streamedRaw = ''\n let builder = createBuilder()\n let sseBuffer = ''\n const flushUiMessageBuffer = (extra?: string) => {\n if (extra) sseBuffer += extra\n const { events, rest } = parseSseLines(sseBuffer)\n sseBuffer = rest\n for (const block of events) {\n const data = extractDataPayload(block)\n if (!data) continue\n if (data === '[DONE]') continue\n try {\n const parsed = JSON.parse(data) as { type?: string }\n if (parsed && typeof parsed.type === 'string') {\n builder = applyChunk(builder, parsed as { type: string })\n }\n } catch {\n // Tolerate malformed events / SSE comments.\n }\n }\n }\n try {\n while (true) {\n const { value, done } = await reader.read()\n if (done) break\n if (!value) continue\n const piece = decoder.decode(value, { stream: true })\n if (!piece) continue\n streamedRaw += piece\n\n if (isUiMessageStream) {\n flushUiMessageBuffer(piece)\n } else {\n // Plain text fallback (legacy `toTextStreamResponse`).\n builder = { ...builder, text: streamedRaw }\n }\n const snapshotBuilder = builder\n updateMessages(\n (current) =>\n current.map((entry) =>\n entry.id === assistantId\n ? mergeAssistantMessage(entry, snapshotBuilder)\n : entry,\n ),\n { persistWhenUnmounted: true },\n )\n }\n const tail = decoder.decode()\n if (tail) {\n streamedRaw += tail\n if (isUiMessageStream) {\n flushUiMessageBuffer(tail)\n } else {\n builder = { ...builder, text: streamedRaw }\n }\n }\n if (isUiMessageStream && sseBuffer.length > 0) {\n flushUiMessageBuffer('\\n\\n')\n }\n builder = { ...builder, reasoningStreaming: false }\n const finalSnapshot = builder\n updateMessages(\n (current) =>\n current.map((entry) =>\n entry.id === assistantId\n ? mergeAssistantMessage(entry, finalSnapshot)\n : entry,\n ),\n { persistWhenUnmounted: true },\n )\n if (mountedRef.current) {\n setLastResponseDebug({ status: response.status, text: streamedRaw })\n }\n const isEmpty =\n !builder.text.trim() && builder.toolCalls.length === 0 && !builder.reasoning\n if (isEmpty) {\n emitError({\n code: 'empty_response',\n message:\n 'The AI agent returned an empty response. This usually means the LLM provider rejected the request (invalid API key, rate limit, or model error). Check your server logs for details.',\n })\n updateMessages((current) => current.filter((entry) => entry.id !== assistantId), {\n persistWhenUnmounted: true,\n })\n }\n } catch (streamError) {\n if ((streamError as { name?: string })?.name === 'AbortError') {\n // Cancelled by the user \u2014 keep whatever we have so far and exit\n // quietly.\n } else {\n const rawMessage =\n streamError instanceof Error\n ? streamError.message\n : 'Stream interrupted.'\n // LLM provider errors (auth failures, rate limits, invalid tool\n // schemas) surface as stream read errors. Include a hint so the\n // operator can check server logs for the full stack trace.\n const message = rawMessage.includes('API')\n ? rawMessage\n : `${rawMessage} \u2014 check server logs for LLM provider details.`\n emitError({ code: 'stream_error', message })\n // Remove the empty assistant placeholder so the error alert is\n // the only visible feedback.\n updateMessages((current) => current.filter((entry) => entry.id !== assistantId), {\n persistWhenUnmounted: true,\n })\n }\n } finally {\n reader.releaseLock()\n if (abortRef.current === controller) {\n abortRef.current = null\n }\n writeSessionStreamState(sessionStorageKey, false)\n setStatus('idle')\n }\n },\n [\n agent,\n apiPath,\n attachmentIds,\n debug,\n effectiveConversationId,\n emitError,\n pageContext,\n sessionStorageKey,\n setStatus,\n updateMessages,\n ],\n )\n\n React.useEffect(() => {\n if (typeof window === 'undefined') return\n const handleSessionUpdate = (event: Event) => {\n const detail = (event as CustomEvent<{ key?: string }>).detail\n if (!detail || detail.key !== sessionStorageKey) return\n const next = readPersistedSession(agent, persistKey)\n if (!next) return\n if (next.conversationId !== effectiveConversationId) return\n updateMessages(next.messages)\n }\n window.addEventListener(SESSION_UPDATED_EVENT, handleSessionUpdate)\n return () => {\n window.removeEventListener(SESSION_UPDATED_EVENT, handleSessionUpdate)\n }\n }, [agent, effectiveConversationId, persistKey, sessionStorageKey, updateMessages])\n\n React.useEffect(() => {\n if (isSessionStreamActive(sessionStorageKey)) {\n setStatusInternal('streaming')\n }\n if (typeof window === 'undefined') return\n const handleStreamState = (event: Event) => {\n const detail = (event as CustomEvent<{ key?: string; active?: boolean }>).detail\n if (!detail || detail.key !== sessionStorageKey) return\n setStatusInternal(detail.active ? 'streaming' : 'idle')\n }\n window.addEventListener(SESSION_STREAM_STATE_EVENT, handleStreamState)\n return () => {\n window.removeEventListener(SESSION_STREAM_STATE_EVENT, handleStreamState)\n }\n }, [sessionStorageKey])\n\n React.useEffect(() => {\n mountedRef.current = true\n return () => {\n // Flush the latest snapshot \u2014 including any in-flight assistant\n // content \u2014 and let the request keep running in the background.\n // Background stream updates continue writing to this same storage slot\n // and notify any reopened chat for the conversation (issue #1816).\n const finalMessages = latestMessagesRef.current\n if (finalMessages.length > 0) {\n persistSnapshot(finalMessages, true)\n }\n mountedRef.current = false\n }\n }, [persistSnapshot])\n\n return {\n messages,\n status,\n error,\n lastRequestDebug,\n lastResponseDebug,\n conversationId: effectiveConversationId,\n sendMessage,\n cancel,\n reset,\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-mercato/ui",
3
- "version": "0.6.1-develop.3174.1.c3822bcdf0",
3
+ "version": "0.6.1-develop.3187.1.4c96d17341",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "scripts": {
@@ -140,12 +140,12 @@
140
140
  "remark-gfm": "^4.0.1"
141
141
  },
142
142
  "peerDependencies": {
143
- "@open-mercato/shared": "0.6.1-develop.3174.1.c3822bcdf0",
143
+ "@open-mercato/shared": "0.6.1-develop.3187.1.4c96d17341",
144
144
  "react": ">=18.0.0",
145
145
  "react-dom": ">=18.0.0"
146
146
  },
147
147
  "devDependencies": {
148
- "@open-mercato/shared": "0.6.1-develop.3174.1.c3822bcdf0",
148
+ "@open-mercato/shared": "0.6.1-develop.3187.1.4c96d17341",
149
149
  "@testing-library/dom": "^10.4.1",
150
150
  "@testing-library/jest-dom": "^6.9.1",
151
151
  "@testing-library/react": "^16.3.1",