@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 +37 -3
- package/dist/index.js +270 -15
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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
|
-
|
|
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
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
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({
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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,
|