@copilotz/chat-adapter 0.1.11 → 0.1.13

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/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import React from 'react';
1
+ import React, { ReactNode } from 'react';
2
2
  import { ChatUserContext, ChatConfig, ChatCallbacks, MemoryItem, AgentOption, ChatMessage, ChatThread, MediaAttachment } from '@copilotz/chat-ui';
3
3
  export { ChatCallbacks, ChatConfig, ChatMessage, ChatThread, ChatUserContext, MediaAttachment, MemoryItem } from '@copilotz/chat-ui';
4
4
 
@@ -88,6 +88,23 @@ interface UseUrlStateReturn {
88
88
  */
89
89
  declare function useUrlState(config?: UrlSyncConfig): UseUrlStateReturn;
90
90
 
91
+ type SpecialChatState = {
92
+ kind: string;
93
+ title?: string;
94
+ message?: string;
95
+ payload?: Record<string, unknown>;
96
+ };
97
+ type SpecialStateControls = {
98
+ clear: () => void;
99
+ };
100
+ type RenderSpecialState = (state: SpecialChatState, controls: SpecialStateControls) => ReactNode | null;
101
+ type EventInterceptorResult = void | {
102
+ handled?: boolean;
103
+ specialState?: SpecialChatState | null;
104
+ };
105
+ type EventInterceptor = (event: unknown) => EventInterceptorResult;
106
+ type RunErrorInterceptor = (error: unknown) => SpecialChatState | null | undefined;
107
+
91
108
  interface CopilotzChatProps {
92
109
  userId: string;
93
110
  userName?: string;
@@ -133,6 +150,9 @@ interface CopilotzChatProps {
133
150
  selectedAgentId?: string | null;
134
151
  onSelectAgent?: (agentId: string) => void;
135
152
  className?: string;
153
+ eventInterceptor?: EventInterceptor;
154
+ runErrorInterceptor?: RunErrorInterceptor;
155
+ renderSpecialState?: RenderSpecialState;
136
156
  /**
137
157
  * URL state synchronization configuration.
138
158
  * When enabled, syncs thread ID, agent, and prompt to/from URL parameters.
@@ -177,6 +197,8 @@ interface UseCopilotzOptions {
177
197
  defaultThreadName?: string;
178
198
  onToolOutput?: (output: Record<string, unknown>) => void;
179
199
  preferredAgentName?: string | null;
200
+ eventInterceptor?: EventInterceptor;
201
+ runErrorInterceptor?: RunErrorInterceptor;
180
202
  /**
181
203
  * URL state synchronization configuration.
182
204
  * When enabled, thread ID and agent are synced to/from URL parameters.
@@ -195,12 +217,14 @@ interface UseCopilotzOptions {
195
217
  */
196
218
  urlSync?: UrlSyncConfig;
197
219
  }
198
- declare function useCopilotz({ userId, initialContext, bootstrap, defaultThreadName, onToolOutput, preferredAgentName, urlSync }: UseCopilotzOptions): {
220
+ declare function useCopilotz({ userId, initialContext, bootstrap, defaultThreadName, onToolOutput, preferredAgentName, eventInterceptor, runErrorInterceptor, urlSync, }: UseCopilotzOptions): {
199
221
  messages: ChatMessage[];
200
222
  isMessagesLoading: boolean;
201
223
  threads: ChatThread[];
202
224
  currentThreadId: string | null;
203
225
  isStreaming: boolean;
226
+ specialState: SpecialChatState | null;
227
+ clearSpecialState: () => void;
204
228
  userContextSeed: Partial<ChatUserContext>;
205
229
  sendMessage: (content: string, attachments?: MediaAttachment[]) => Promise<void>;
206
230
  createThread: (title?: string) => void;
@@ -276,6 +300,16 @@ type CopilotzStreamResult = {
276
300
  messages: any[];
277
301
  media: Record<string, string> | null;
278
302
  };
303
+ declare class CopilotzRequestError extends Error {
304
+ status: number;
305
+ code?: string;
306
+ details?: unknown;
307
+ constructor(message: string, options: {
308
+ status: number;
309
+ code?: string;
310
+ details?: unknown;
311
+ });
312
+ }
279
313
  declare function runCopilotzStream(options: RunOptions): Promise<CopilotzStreamResult>;
280
314
  declare function fetchThreads(userId: string): Promise<RestThread[]>;
281
315
  declare function fetchThreadMessages(threadId: string): Promise<RestMessage[]>;
@@ -300,4 +334,4 @@ type WithMetadata = {
300
334
  };
301
335
  declare function resolveAssetsInMessages<T extends WithMetadata>(messages: T[]): Promise<T[]>;
302
336
 
303
- export { CopilotzChat, type CopilotzStreamResult, type UrlParamsConfig, type UrlState, type UrlSyncConfig, type UseUrlStateReturn, copilotzService, deleteMessagesByThreadId, deleteThread, fetchThreadMessages, fetchThreads, getAssetDataUrl, resolveAssetsInMessages, runCopilotzStream, updateThread, useCopilotz, useUrlState };
337
+ export { CopilotzChat, CopilotzRequestError, type CopilotzStreamResult, type EventInterceptor, type EventInterceptorResult, type RenderSpecialState, type RunErrorInterceptor, type SpecialChatState, type SpecialStateControls, type UrlParamsConfig, type UrlState, type UrlSyncConfig, type UseUrlStateReturn, copilotzService, deleteMessagesByThreadId, deleteThread, fetchThreadMessages, fetchThreads, getAssetDataUrl, resolveAssetsInMessages, runCopilotzStream, updateThread, useCopilotz, useUrlState };
package/dist/index.js CHANGED
@@ -124,6 +124,18 @@ var withAuthHeaders = (headers = {}) => {
124
124
  }
125
125
  return headers;
126
126
  };
127
+ var CopilotzRequestError = class extends Error {
128
+ status;
129
+ code;
130
+ details;
131
+ constructor(message, options) {
132
+ super(message);
133
+ this.name = "CopilotzRequestError";
134
+ this.status = options.status;
135
+ this.code = options.code;
136
+ this.details = options.details;
137
+ }
138
+ };
127
139
  var SSE_LINE_BREAK = "\n\n";
128
140
  var appendChunk = (buffer, chunk) => {
129
141
  if (!buffer) return chunk;
@@ -138,6 +150,14 @@ var appendChunk = (buffer, chunk) => {
138
150
  }
139
151
  return buffer + chunk;
140
152
  };
153
+ var parseErrorText = (rawText) => {
154
+ if (!rawText) return null;
155
+ try {
156
+ return JSON.parse(rawText);
157
+ } catch {
158
+ return null;
159
+ }
160
+ };
141
161
  var toAttachmentPayload = (attachments) => {
142
162
  if (!attachments || attachments.length === 0) return void 0;
143
163
  return attachments.map((att) => {
@@ -365,7 +385,16 @@ async function runCopilotzStream(options) {
365
385
  });
366
386
  if (!response.ok || !response.body) {
367
387
  const errorText = await response.text().catch(() => response.statusText);
368
- throw new Error(errorText || "Failed to run Copilotz agent");
388
+ const parsed = parseErrorText(errorText);
389
+ const details = parsed && typeof parsed === "object" ? parsed : void 0;
390
+ const detailsRecord = details;
391
+ const message = typeof detailsRecord?.message === "string" && detailsRecord.message || typeof detailsRecord?.error === "string" && detailsRecord.error || errorText || response.statusText || "Failed to run Copilotz agent";
392
+ const code = typeof detailsRecord?.code === "string" ? detailsRecord.code : typeof detailsRecord?.error === "string" && detailsRecord.error !== message ? detailsRecord.error : void 0;
393
+ throw new CopilotzRequestError(message, {
394
+ status: response.status,
395
+ code,
396
+ details
397
+ });
369
398
  }
370
399
  const reader = response.body.getReader();
371
400
  const decoder = new TextDecoder("utf-8");
@@ -478,6 +507,33 @@ async function fetchThreads(userId) {
478
507
  return data;
479
508
  }
480
509
  async function fetchThreadMessages(threadId) {
510
+ const graphParams = new URLSearchParams();
511
+ graphParams.set("threadId", threadId);
512
+ graphParams.set("limit", "500");
513
+ try {
514
+ const graphRes = await fetch(apiUrl(`/v1/messages?${graphParams.toString()}`), {
515
+ headers: withAuthHeaders({ Accept: "application/json" })
516
+ });
517
+ if (graphRes.ok) {
518
+ const graphData = await graphRes.json();
519
+ if (Array.isArray(graphData)) {
520
+ return graphData;
521
+ }
522
+ if (Array.isArray(graphData?.data)) {
523
+ return graphData.data;
524
+ }
525
+ return [];
526
+ }
527
+ if (![404, 405, 501, 502, 503, 504].includes(graphRes.status)) {
528
+ const errorText = await graphRes.text().catch(() => graphRes.statusText);
529
+ throw new Error(errorText || `Failed to load graph thread messages (${graphRes.status})`);
530
+ }
531
+ } catch (error) {
532
+ const message = error instanceof Error ? error.message : "";
533
+ if (!/Failed to fetch/i.test(message)) {
534
+ throw error;
535
+ }
536
+ }
481
537
  const params = new URLSearchParams();
482
538
  params.set("filters", JSON.stringify({ threadId }));
483
539
  params.set("sort", "createdAt:asc");
@@ -508,6 +564,23 @@ async function updateThread(threadId, updates) {
508
564
  return data?.body ?? data;
509
565
  }
510
566
  async function deleteMessagesByThreadId(threadId) {
567
+ const graphParams = new URLSearchParams();
568
+ graphParams.set("threadId", threadId);
569
+ try {
570
+ const graphRes = await fetch(apiUrl(`/v1/messages?${graphParams.toString()}`), {
571
+ method: "DELETE",
572
+ headers: withAuthHeaders({ Accept: "application/json" })
573
+ });
574
+ if (!graphRes.ok && ![404, 405, 501].includes(graphRes.status)) {
575
+ const errorText = await graphRes.text().catch(() => graphRes.statusText);
576
+ throw new Error(errorText || `Failed to delete graph messages (${graphRes.status})`);
577
+ }
578
+ } catch (error) {
579
+ const message = error instanceof Error ? error.message : "";
580
+ if (!/Failed to fetch/i.test(message)) {
581
+ throw error;
582
+ }
583
+ }
511
584
  const params = new URLSearchParams();
512
585
  params.set("filters", JSON.stringify({ threadId }));
513
586
  const res = await fetch(apiUrl(`/v1/rest/messages?${params.toString()}`), {
@@ -718,6 +791,114 @@ function useUrlState(config = {}) {
718
791
  var nowTs = () => Date.now();
719
792
  var generateId = () => globalThis.crypto?.randomUUID?.() ?? `id-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
720
793
  var isAbortError = (error) => error instanceof DOMException && error.name === "AbortError" || typeof error === "object" && error !== null && "name" in error && error.name === "AbortError";
794
+ var normalizeToolStatus = (status) => {
795
+ if (status === "pending") return "pending";
796
+ if (status === "running" || status === "processing") return "running";
797
+ if (status === "failed") return "failed";
798
+ return "completed";
799
+ };
800
+ var parseToolArguments = (value) => {
801
+ if (value && typeof value === "object" && !Array.isArray(value)) {
802
+ return value;
803
+ }
804
+ if (typeof value === "string") {
805
+ try {
806
+ const parsed = JSON.parse(value);
807
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
808
+ return parsed;
809
+ }
810
+ } catch {
811
+ }
812
+ }
813
+ return {};
814
+ };
815
+ var extractToolCallsFromServerMessage = (msg) => {
816
+ const metadata = msg.metadata ?? void 0;
817
+ const topLevelToolCalls = Array.isArray(msg.toolCalls) ? msg.toolCalls || [] : [];
818
+ const metadataToolCalls = Array.isArray(metadata?.toolCalls) ? metadata.toolCalls : [];
819
+ const usedMetadataIndexes = /* @__PURE__ */ new Set();
820
+ const parsed = [];
821
+ const findMatchingMetadataIndex = (toolCall) => {
822
+ const id = typeof toolCall.id === "string" ? toolCall.id : void 0;
823
+ const name = typeof toolCall.name === "string" ? toolCall.name : void 0;
824
+ const byId = id ? metadataToolCalls.findIndex((candidate, idx) => !usedMetadataIndexes.has(idx) && candidate?.id === id) : -1;
825
+ if (byId >= 0) return byId;
826
+ return name ? metadataToolCalls.findIndex((candidate, idx) => !usedMetadataIndexes.has(idx) && candidate?.name === name) : -1;
827
+ };
828
+ const parseToolCall = (primary, secondary) => {
829
+ const id = typeof primary.id === "string" ? primary.id : typeof secondary?.id === "string" ? secondary.id : void 0;
830
+ const name = typeof primary.name === "string" ? primary.name : typeof secondary?.name === "string" ? secondary.name : "tool";
831
+ const argsRaw = primary.args ?? primary.arguments ?? secondary?.args ?? secondary?.arguments;
832
+ const result = primary.output !== void 0 ? primary.output : primary.result !== void 0 ? primary.result : secondary?.output !== void 0 ? secondary.output : secondary?.result;
833
+ const status = normalizeToolStatus(primary.status ?? secondary?.status);
834
+ return {
835
+ ...id ? { id } : {},
836
+ name,
837
+ arguments: parseToolArguments(argsRaw),
838
+ ...result !== void 0 ? { result } : {},
839
+ status
840
+ };
841
+ };
842
+ topLevelToolCalls.forEach((toolCall) => {
843
+ const metadataIndex = findMatchingMetadataIndex(toolCall);
844
+ const metadataCall = metadataIndex >= 0 ? metadataToolCalls[metadataIndex] : void 0;
845
+ if (metadataIndex >= 0) usedMetadataIndexes.add(metadataIndex);
846
+ parsed.push(parseToolCall(toolCall, metadataCall));
847
+ });
848
+ metadataToolCalls.forEach((toolCall, index) => {
849
+ if (usedMetadataIndexes.has(index)) return;
850
+ parsed.push(parseToolCall(toolCall));
851
+ });
852
+ return parsed;
853
+ };
854
+ var extractToolResultUpdateFromMessage = (msg) => {
855
+ if (msg.senderType !== "tool") return null;
856
+ const toolCalls = extractToolCallsFromServerMessage(msg);
857
+ if (!Array.isArray(toolCalls) || toolCalls.length === 0) return null;
858
+ const firstToolCall = toolCalls[0];
859
+ const metadata = msg.metadata ?? void 0;
860
+ const fallbackResult = metadata?.output;
861
+ const result = firstToolCall.result !== void 0 ? firstToolCall.result : fallbackResult;
862
+ return {
863
+ ...firstToolCall.id ? { id: firstToolCall.id } : {},
864
+ ...firstToolCall.name ? { name: firstToolCall.name } : {},
865
+ ...result !== void 0 ? { result } : {},
866
+ status: firstToolCall.status,
867
+ endTime: msg.createdAt ? new Date(msg.createdAt).getTime() : nowTs()
868
+ };
869
+ };
870
+ var mergePersistedToolResults = (messages, updates) => {
871
+ if (updates.length === 0) return messages;
872
+ const nextMessages = [...messages];
873
+ for (const update of updates) {
874
+ for (let i = nextMessages.length - 1; i >= 0; i--) {
875
+ const message = nextMessages[i];
876
+ if (message.role !== "assistant" || !Array.isArray(message.toolCalls) || message.toolCalls.length === 0) {
877
+ continue;
878
+ }
879
+ const toolCalls = message.toolCalls;
880
+ let toolCallIndex = update.id ? toolCalls.findIndex((toolCall) => toolCall.id === update.id) : -1;
881
+ if (toolCallIndex === -1 && update.name) {
882
+ toolCallIndex = toolCalls.findIndex((toolCall) => toolCall.name === update.name && (toolCall.status === "pending" || toolCall.status === "running" || typeof toolCall.result === "undefined"));
883
+ }
884
+ if (toolCallIndex === -1) continue;
885
+ const updatedToolCalls = [...toolCalls];
886
+ const current = updatedToolCalls[toolCallIndex];
887
+ updatedToolCalls[toolCallIndex] = {
888
+ ...current,
889
+ status: update.status,
890
+ ...update.result !== void 0 ? { result: update.result } : {},
891
+ endTime: update.endTime
892
+ };
893
+ nextMessages[i] = {
894
+ ...message,
895
+ toolCalls: updatedToolCalls
896
+ };
897
+ break;
898
+ }
899
+ }
900
+ return nextMessages;
901
+ };
721
902
  var convertServerMessage = (msg) => {
722
903
  const timestamp = msg.createdAt ? new Date(msg.createdAt).getTime() : nowTs();
723
904
  const metadata = msg.metadata ?? void 0;
@@ -750,13 +931,15 @@ var convertServerMessage = (msg) => {
750
931
  return [];
751
932
  });
752
933
  const role = msg.senderType === "agent" ? "assistant" : msg.senderType === "user" ? "user" : "assistant";
753
- const mappedToolCalls = Array.isArray(msg.toolCalls) ? (msg.toolCalls || []).map((tc) => ({
754
- id: typeof tc?.id === "string" ? tc.id : generateId(),
755
- name: typeof tc?.name === "string" ? tc.name : "tool",
756
- arguments: tc?.args || {},
757
- status: "completed"
758
- })) : void 0;
759
- const hasToolCalls = Array.isArray(mappedToolCalls) && mappedToolCalls.length > 0;
934
+ const parsedToolCalls = extractToolCallsFromServerMessage(msg);
935
+ const mappedToolCalls = parsedToolCalls.map((toolCall) => ({
936
+ id: toolCall.id ?? generateId(),
937
+ name: toolCall.name,
938
+ arguments: toolCall.arguments,
939
+ status: toolCall.status,
940
+ ...toolCall.result !== void 0 ? { result: toolCall.result } : {}
941
+ }));
942
+ const hasToolCalls = mappedToolCalls.length > 0;
760
943
  const isToolSender = msg.senderType === "tool";
761
944
  const content = isToolSender ? "" : (msg.content ?? "") || (hasToolCalls ? "" : "");
762
945
  return {
@@ -771,7 +954,17 @@ var convertServerMessage = (msg) => {
771
954
  toolCalls: hasToolCalls ? mappedToolCalls : void 0
772
955
  };
773
956
  };
774
- function useCopilotz({ userId, initialContext, bootstrap, defaultThreadName, onToolOutput, preferredAgentName, urlSync }) {
957
+ function useCopilotz({
958
+ userId,
959
+ initialContext,
960
+ bootstrap,
961
+ defaultThreadName,
962
+ onToolOutput,
963
+ preferredAgentName,
964
+ eventInterceptor,
965
+ runErrorInterceptor,
966
+ urlSync
967
+ }) {
775
968
  const {
776
969
  state: urlState,
777
970
  setThreadId: setUrlThreadId,
@@ -787,6 +980,7 @@ function useCopilotz({ userId, initialContext, bootstrap, defaultThreadName, onT
787
980
  const [messages, setMessages] = useState2([]);
788
981
  const [isMessagesLoading, setIsMessagesLoading] = useState2(false);
789
982
  const [isStreaming, setIsStreaming] = useState2(false);
983
+ const [specialState, setSpecialState] = useState2(null);
790
984
  const [userContextSeed, setUserContextSeed] = useState2(initialContext || {});
791
985
  const preferredAgentRef = useRef2(preferredAgentName ?? null);
792
986
  const threadsRef = useRef2(threads);
@@ -821,6 +1015,31 @@ function useCopilotz({ userId, initialContext, bootstrap, defaultThreadName, onT
821
1015
  }
822
1016
  onToolOutput?.(output);
823
1017
  }, [onToolOutput]);
1018
+ const clearSpecialState = useCallback2(() => {
1019
+ setSpecialState(null);
1020
+ }, []);
1021
+ const applyEventInterceptor = useCallback2((event) => {
1022
+ if (!eventInterceptor) return void 0;
1023
+ try {
1024
+ const result = eventInterceptor(event);
1025
+ if (result?.specialState) {
1026
+ setSpecialState(result.specialState);
1027
+ }
1028
+ return result;
1029
+ } catch (error) {
1030
+ console.error("Error in Copilotz event interceptor", error);
1031
+ return void 0;
1032
+ }
1033
+ }, [eventInterceptor]);
1034
+ const getSpecialStateFromError = useCallback2((error) => {
1035
+ if (!runErrorInterceptor) return null;
1036
+ try {
1037
+ return runErrorInterceptor(error) ?? null;
1038
+ } catch (interceptorError) {
1039
+ console.error("Error in Copilotz run error interceptor", interceptorError);
1040
+ return null;
1041
+ }
1042
+ }, [runErrorInterceptor]);
824
1043
  const handleStreamMessageEvent = useCallback2((event) => {
825
1044
  const payload = event?.payload;
826
1045
  if (!payload) return;
@@ -945,10 +1164,11 @@ function useCopilotz({ userId, initialContext, bootstrap, defaultThreadName, onT
945
1164
  if (output) processToolOutput(output);
946
1165
  }
947
1166
  });
1167
+ const toolResultUpdates = resolvedMessages.map((msg) => extractToolResultUpdateFromMessage(msg)).filter((update) => update !== null);
948
1168
  const viewMessages = resolvedMessages.filter((msg) => {
949
1169
  const text = (typeof msg.content === "string" ? msg.content : "").trim();
950
1170
  const hasText = text.length > 0;
951
- const hasToolCalls = Array.isArray(msg.toolCalls) && msg.toolCalls.length > 0;
1171
+ const hasToolCalls = extractToolCallsFromServerMessage(msg).length > 0;
952
1172
  const meta = msg.metadata ?? {};
953
1173
  const hasAttachments = Array.isArray(meta.attachments) && meta.attachments.length > 0;
954
1174
  if (msg.senderType === "tool") {
@@ -956,7 +1176,8 @@ function useCopilotz({ userId, initialContext, bootstrap, defaultThreadName, onT
956
1176
  }
957
1177
  return hasText || hasToolCalls || hasAttachments;
958
1178
  }).map(convertServerMessage);
959
- setMessages(viewMessages);
1179
+ const hydratedMessages = mergePersistedToolResults(viewMessages, toolResultUpdates);
1180
+ setMessages(hydratedMessages);
960
1181
  } catch (error) {
961
1182
  if (isAbortError(error)) return;
962
1183
  console.error(`Error loading messages for thread ${threadId}`, error);
@@ -1292,6 +1513,10 @@ function useCopilotz({ userId, initialContext, bootstrap, defaultThreadName, onT
1292
1513
  selectedAgent: params.agentName ?? preferredAgentRef.current ?? null,
1293
1514
  onToken: (token, isComplete) => updateStreamingMessage(token, isComplete),
1294
1515
  onMessageEvent: async (event) => {
1516
+ const intercepted = applyEventInterceptor(event);
1517
+ if (intercepted?.handled) {
1518
+ return;
1519
+ }
1295
1520
  const type = event?.type || "";
1296
1521
  const payload = event?.payload ?? event;
1297
1522
  if (type === "MESSAGE" || type === "NEW_MESSAGE") {
@@ -1404,6 +1629,10 @@ function useCopilotz({ userId, initialContext, bootstrap, defaultThreadName, onT
1404
1629
  handleStreamMessageEvent(event);
1405
1630
  },
1406
1631
  onAssetEvent: async (payload) => {
1632
+ const intercepted = applyEventInterceptor({ type: "ASSET_CREATED", payload });
1633
+ if (intercepted?.handled) {
1634
+ return;
1635
+ }
1407
1636
  await (async () => {
1408
1637
  if (!hasStreamProgress) return;
1409
1638
  finalizeCurrentAssistantBubble();
@@ -1423,7 +1652,7 @@ function useCopilotz({ userId, initialContext, bootstrap, defaultThreadName, onT
1423
1652
  abortControllerRef.current = null;
1424
1653
  }
1425
1654
  return currentAssistantId;
1426
- }, [handleStreamMessageEvent, handleStreamAssetEvent]);
1655
+ }, [applyEventInterceptor, handleStreamMessageEvent, handleStreamAssetEvent]);
1427
1656
  const handleSendMessage = useCallback2(async (content, attachments = []) => {
1428
1657
  if (!content.trim() && attachments.length === 0) return;
1429
1658
  if (!userId) return;
@@ -1463,6 +1692,7 @@ function useCopilotz({ userId, initialContext, bootstrap, defaultThreadName, onT
1463
1692
  isComplete: false
1464
1693
  };
1465
1694
  setMessages((prev) => [...prev, userMessage, assistantPlaceholder]);
1695
+ setSpecialState(null);
1466
1696
  if (!threadsRef.current.some((t) => t.id === conversationKey)) {
1467
1697
  const newThread = {
1468
1698
  id: conversationKey,
@@ -1493,6 +1723,12 @@ function useCopilotz({ userId, initialContext, bootstrap, defaultThreadName, onT
1493
1723
  } catch (error) {
1494
1724
  if (isAbortError(error)) return;
1495
1725
  console.error("Error sending Copilotz message", error);
1726
+ const nextSpecialState = getSpecialStateFromError(error);
1727
+ if (nextSpecialState) {
1728
+ setSpecialState(nextSpecialState);
1729
+ setMessages((prev) => prev.filter((msg) => !msg.isStreaming));
1730
+ return;
1731
+ }
1496
1732
  setMessages((prev) => prev.map((msg) => msg.isStreaming ? {
1497
1733
  ...msg,
1498
1734
  isStreaming: false,
@@ -1500,7 +1736,7 @@ function useCopilotz({ userId, initialContext, bootstrap, defaultThreadName, onT
1500
1736
  content: "Desculpe, ocorreu um erro ao gerar a resposta. Por favor, tente novamente."
1501
1737
  } : msg));
1502
1738
  }
1503
- }, [userId, fetchAndSetThreadsState, loadThreadMessages, sendCopilotzMessage]);
1739
+ }, [userId, fetchAndSetThreadsState, loadThreadMessages, sendCopilotzMessage, getSpecialStateFromError]);
1504
1740
  const bootstrapConversation = useCallback2(async (uid) => {
1505
1741
  if (!bootstrap?.initialToolCalls && !bootstrap?.initialMessage) return;
1506
1742
  const bootstrapThreadExternalId = generateId();
@@ -1509,6 +1745,7 @@ function useCopilotz({ userId, initialContext, bootstrap, defaultThreadName, onT
1509
1745
  setThreadExternalIdMap((prev) => ({ ...prev, [bootstrapThreadExternalId]: bootstrapThreadExternalId }));
1510
1746
  setThreadMetadataMap((prev) => ({ ...prev, [bootstrapThreadExternalId]: {} }));
1511
1747
  setMessages([]);
1748
+ setSpecialState(null);
1512
1749
  try {
1513
1750
  await sendCopilotzMessage({
1514
1751
  threadExternalId: bootstrapThreadExternalId,
@@ -1525,6 +1762,12 @@ function useCopilotz({ userId, initialContext, bootstrap, defaultThreadName, onT
1525
1762
  } catch (error) {
1526
1763
  if (isAbortError(error)) return;
1527
1764
  console.error("Error bootstrapping conversation", error);
1765
+ const nextSpecialState = getSpecialStateFromError(error);
1766
+ if (nextSpecialState) {
1767
+ setSpecialState(nextSpecialState);
1768
+ setMessages([]);
1769
+ return;
1770
+ }
1528
1771
  setMessages([
1529
1772
  {
1530
1773
  id: generateId(),
@@ -1536,7 +1779,7 @@ function useCopilotz({ userId, initialContext, bootstrap, defaultThreadName, onT
1536
1779
  }
1537
1780
  ]);
1538
1781
  }
1539
- }, [fetchAndSetThreadsState, loadThreadMessages, sendCopilotzMessage, bootstrap, defaultThreadName]);
1782
+ }, [fetchAndSetThreadsState, loadThreadMessages, sendCopilotzMessage, bootstrap, defaultThreadName, getSpecialStateFromError]);
1540
1783
  const reset = useCallback2(() => {
1541
1784
  messagesRequestRef.current += 1;
1542
1785
  setThreads([]);
@@ -1548,6 +1791,7 @@ function useCopilotz({ userId, initialContext, bootstrap, defaultThreadName, onT
1548
1791
  setUserContextSeed({});
1549
1792
  setIsMessagesLoading(false);
1550
1793
  setIsStreaming(false);
1794
+ setSpecialState(null);
1551
1795
  abortControllerRef.current?.abort();
1552
1796
  }, []);
1553
1797
  useEffect2(() => {
@@ -1590,6 +1834,8 @@ function useCopilotz({ userId, initialContext, bootstrap, defaultThreadName, onT
1590
1834
  threads,
1591
1835
  currentThreadId,
1592
1836
  isStreaming,
1837
+ specialState,
1838
+ clearSpecialState,
1593
1839
  userContextSeed,
1594
1840
  sendMessage: handleSendMessage,
1595
1841
  createThread: handleCreateThread,
@@ -1636,6 +1882,9 @@ var CopilotzChat = ({
1636
1882
  selectedAgentId = null,
1637
1883
  onSelectAgent,
1638
1884
  className,
1885
+ eventInterceptor,
1886
+ runErrorInterceptor,
1887
+ renderSpecialState,
1639
1888
  urlSync
1640
1889
  }) => {
1641
1890
  const selectedAgent = agentOptions.find((agent) => agent.id === selectedAgentId) || null;
@@ -1645,6 +1894,8 @@ var CopilotzChat = ({
1645
1894
  threads,
1646
1895
  currentThreadId,
1647
1896
  isStreaming,
1897
+ specialState,
1898
+ clearSpecialState,
1648
1899
  userContextSeed,
1649
1900
  sendMessage,
1650
1901
  createThread,
@@ -1664,6 +1915,8 @@ var CopilotzChat = ({
1664
1915
  defaultThreadName: userConfig?.labels?.defaultThreadName,
1665
1916
  onToolOutput,
1666
1917
  preferredAgentName: selectedAgent?.name ?? null,
1918
+ eventInterceptor,
1919
+ runErrorInterceptor,
1667
1920
  urlSync
1668
1921
  });
1669
1922
  const [promptHandled, setPromptHandled] = useState3(false);
@@ -1748,7 +2001,8 @@ var CopilotzChat = ({
1748
2001
  }, [userConfig, customComponent]);
1749
2002
  const effectiveUserName = userName || userId;
1750
2003
  const effectiveUserAvatar = userAvatar;
1751
- return /* @__PURE__ */ jsx(ChatUserContextProvider, { initial: userContextSeed, children: /* @__PURE__ */ jsx(
2004
+ const specialStateContent = specialState ? renderSpecialState?.(specialState, { clear: clearSpecialState }) : null;
2005
+ return /* @__PURE__ */ jsx(ChatUserContextProvider, { initial: userContextSeed, children: specialStateContent ?? /* @__PURE__ */ jsx(
1752
2006
  ChatUI,
1753
2007
  {
1754
2008
  messages,
@@ -1787,6 +2041,7 @@ var CopilotzChat = ({
1787
2041
  };
1788
2042
  export {
1789
2043
  CopilotzChat,
2044
+ CopilotzRequestError,
1790
2045
  copilotzService,
1791
2046
  deleteMessagesByThreadId,
1792
2047
  deleteThread,