@copilotz/chat-adapter 0.3.9 → 0.4.0
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/README.md +13 -0
- package/dist/index.js +205 -232
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -107,6 +107,19 @@ Messages stream token-by-token with a thinking indicator while waiting for the f
|
|
|
107
107
|
<CopilotzChat userId="user-123" />
|
|
108
108
|
```
|
|
109
109
|
|
|
110
|
+
### Live Stream Contract
|
|
111
|
+
|
|
112
|
+
From `0.4.0` onward, the adapter treats the Copilotz live stream as a lifecycle-native contract:
|
|
113
|
+
|
|
114
|
+
- `TOKEN` streams partial assistant text and reasoning
|
|
115
|
+
- `TOOL_CALL` starts or updates the tool execution UI
|
|
116
|
+
- `TOOL_RESULT` completes the tool execution UI
|
|
117
|
+
- `LLM_RESULT` finalizes the active assistant turn
|
|
118
|
+
- `ASSET_CREATED` remains an optional live artifact event
|
|
119
|
+
- `NEW_MESSAGE` is treated as a history/artifact event, not the primary live completion signal
|
|
120
|
+
|
|
121
|
+
If you stream Copilotz events through a custom bridge, keep those event names intact.
|
|
122
|
+
|
|
110
123
|
### Tool Calls with Live Status
|
|
111
124
|
|
|
112
125
|
When your agent calls tools, the UI shows real-time status updates:
|
package/dist/index.js
CHANGED
|
@@ -475,15 +475,11 @@ async function runCopilotzStream(options) {
|
|
|
475
475
|
}
|
|
476
476
|
break;
|
|
477
477
|
}
|
|
478
|
-
case "
|
|
478
|
+
case "NEW_MESSAGE": {
|
|
479
479
|
hadNonReasoningContent = true;
|
|
480
480
|
lastTokenWasReasoning = false;
|
|
481
481
|
collectedMessages.push(payload2);
|
|
482
482
|
onMessageEvent?.(payload2);
|
|
483
|
-
const senderType = payload2?.payload?.senderType ?? payload2?.payload?.sender?.type;
|
|
484
|
-
if (senderType === "agent" && typeof payload2?.payload?.content === "string") {
|
|
485
|
-
aggregatedText = payload2.payload.content;
|
|
486
|
-
}
|
|
487
483
|
break;
|
|
488
484
|
}
|
|
489
485
|
case "TOOL_CALL": {
|
|
@@ -492,6 +488,13 @@ async function runCopilotzStream(options) {
|
|
|
492
488
|
onMessageEvent?.(payload2);
|
|
493
489
|
break;
|
|
494
490
|
}
|
|
491
|
+
case "TOOL_RESULT":
|
|
492
|
+
case "LLM_RESULT": {
|
|
493
|
+
hadNonReasoningContent = true;
|
|
494
|
+
lastTokenWasReasoning = false;
|
|
495
|
+
onMessageEvent?.(payload2);
|
|
496
|
+
break;
|
|
497
|
+
}
|
|
495
498
|
case "ASSET_CREATED": {
|
|
496
499
|
const assetPayload = payload2 && typeof payload2 === "object" && "payload" in payload2 ? payload2.payload : payload2;
|
|
497
500
|
if (assetPayload?.dataUrl) {
|
|
@@ -504,8 +507,15 @@ async function runCopilotzStream(options) {
|
|
|
504
507
|
}
|
|
505
508
|
case "ERROR":
|
|
506
509
|
throw new Error(payload2?.error || "Copilotz stream error");
|
|
507
|
-
default:
|
|
508
|
-
|
|
510
|
+
default: {
|
|
511
|
+
const hasEnvelope = payload2 && typeof payload2 === "object" && "type" in payload2;
|
|
512
|
+
if (hasEnvelope) {
|
|
513
|
+
onMessageEvent?.(payload2);
|
|
514
|
+
} else {
|
|
515
|
+
onMessageEvent?.({ type: eventType, payload: payload2 });
|
|
516
|
+
}
|
|
517
|
+
break;
|
|
518
|
+
}
|
|
509
519
|
}
|
|
510
520
|
};
|
|
511
521
|
while (true) {
|
|
@@ -661,7 +671,8 @@ async function getAssetDataUrl(refOrId) {
|
|
|
661
671
|
const text = await res.text().catch(() => res.statusText);
|
|
662
672
|
throw new Error(text || `Failed to fetch asset ${refOrId}`);
|
|
663
673
|
}
|
|
664
|
-
const
|
|
674
|
+
const body = await res.json();
|
|
675
|
+
const data = body?.data ?? body;
|
|
665
676
|
if (!data?.dataUrl) {
|
|
666
677
|
throw new Error(data?.error || `Asset ${refOrId} has no dataUrl`);
|
|
667
678
|
}
|
|
@@ -858,6 +869,67 @@ var parseToolArguments = (value) => {
|
|
|
858
869
|
}
|
|
859
870
|
return {};
|
|
860
871
|
};
|
|
872
|
+
var matchesToolResultUpdate = (target, update) => {
|
|
873
|
+
if (update.id && target.id) {
|
|
874
|
+
return update.id === target.id;
|
|
875
|
+
}
|
|
876
|
+
return Boolean(update.name && target.name && update.name === target.name);
|
|
877
|
+
};
|
|
878
|
+
var findMatchingToolCallIndex = (toolCalls, update) => toolCalls.findIndex((toolCall) => matchesToolResultUpdate(
|
|
879
|
+
{ id: toolCall.id, name: toolCall.name },
|
|
880
|
+
update
|
|
881
|
+
) && (toolCall.status === "pending" || toolCall.status === "running" || typeof toolCall.result === "undefined"));
|
|
882
|
+
var applyToolResultUpdateToMessages = (messages, update, assistantPatch) => {
|
|
883
|
+
const nextMessages = [...messages];
|
|
884
|
+
for (let i = nextMessages.length - 1; i >= 0; i--) {
|
|
885
|
+
const message = nextMessages[i];
|
|
886
|
+
if (message.role !== "assistant" || !Array.isArray(message.toolCalls) || message.toolCalls.length === 0) {
|
|
887
|
+
continue;
|
|
888
|
+
}
|
|
889
|
+
const toolCallIndex = findMatchingToolCallIndex(message.toolCalls, update);
|
|
890
|
+
if (toolCallIndex === -1) continue;
|
|
891
|
+
const updatedToolCalls = [...message.toolCalls];
|
|
892
|
+
const current = updatedToolCalls[toolCallIndex];
|
|
893
|
+
updatedToolCalls[toolCallIndex] = {
|
|
894
|
+
...current,
|
|
895
|
+
status: update.status,
|
|
896
|
+
...update.result !== void 0 ? { result: update.result } : {},
|
|
897
|
+
endTime: update.endTime
|
|
898
|
+
};
|
|
899
|
+
nextMessages[i] = {
|
|
900
|
+
...message,
|
|
901
|
+
toolCalls: updatedToolCalls,
|
|
902
|
+
...assistantPatch ?? {}
|
|
903
|
+
};
|
|
904
|
+
return { messages: nextMessages, matched: true };
|
|
905
|
+
}
|
|
906
|
+
return { messages, matched: false };
|
|
907
|
+
};
|
|
908
|
+
var extractLiveToolCall = (payload) => {
|
|
909
|
+
const toolCall = payload?.toolCall;
|
|
910
|
+
if (!toolCall) return null;
|
|
911
|
+
const tool = toolCall.tool;
|
|
912
|
+
const name = typeof tool?.name === "string" ? tool.name : typeof tool?.id === "string" ? tool.id : "tool";
|
|
913
|
+
const result = toolCall.output !== void 0 ? toolCall.output : void 0;
|
|
914
|
+
return {
|
|
915
|
+
...typeof toolCall.id === "string" ? { id: toolCall.id } : {},
|
|
916
|
+
name,
|
|
917
|
+
arguments: parseToolArguments(toolCall.args),
|
|
918
|
+
status: normalizeToolStatus(toolCall.status ?? payload?.status ?? "running"),
|
|
919
|
+
...result !== void 0 ? { result } : {}
|
|
920
|
+
};
|
|
921
|
+
};
|
|
922
|
+
var extractLiveToolResultUpdate = (payload) => {
|
|
923
|
+
const tool = payload?.tool;
|
|
924
|
+
const result = payload?.projectedOutput !== void 0 ? payload.projectedOutput : payload?.output !== void 0 ? payload.output : payload?.content;
|
|
925
|
+
return {
|
|
926
|
+
...typeof payload?.toolCallId === "string" ? { id: payload.toolCallId } : {},
|
|
927
|
+
...typeof tool?.name === "string" ? { name: tool.name } : typeof tool?.id === "string" ? { name: tool.id } : {},
|
|
928
|
+
status: normalizeToolStatus(payload?.status),
|
|
929
|
+
...result !== void 0 ? { result } : {},
|
|
930
|
+
endTime: nowTs()
|
|
931
|
+
};
|
|
932
|
+
};
|
|
861
933
|
var extractToolCallsFromServerMessage = (msg) => {
|
|
862
934
|
const metadata = msg.metadata ?? void 0;
|
|
863
935
|
const topLevelToolCalls = Array.isArray(msg.toolCalls) ? msg.toolCalls || [] : [];
|
|
@@ -924,33 +996,9 @@ var extractToolResultUpdateFromMessage = (msg) => {
|
|
|
924
996
|
};
|
|
925
997
|
var mergePersistedToolResults = (messages, updates) => {
|
|
926
998
|
if (updates.length === 0) return messages;
|
|
927
|
-
|
|
999
|
+
let nextMessages = messages;
|
|
928
1000
|
for (const update of updates) {
|
|
929
|
-
|
|
930
|
-
const message = nextMessages[i];
|
|
931
|
-
if (message.role !== "assistant" || !Array.isArray(message.toolCalls) || message.toolCalls.length === 0) {
|
|
932
|
-
continue;
|
|
933
|
-
}
|
|
934
|
-
const toolCalls = message.toolCalls;
|
|
935
|
-
let toolCallIndex = update.id ? toolCalls.findIndex((toolCall) => toolCall.id === update.id) : -1;
|
|
936
|
-
if (toolCallIndex === -1 && update.name) {
|
|
937
|
-
toolCallIndex = toolCalls.findIndex((toolCall) => toolCall.name === update.name && (toolCall.status === "pending" || toolCall.status === "running" || typeof toolCall.result === "undefined"));
|
|
938
|
-
}
|
|
939
|
-
if (toolCallIndex === -1) continue;
|
|
940
|
-
const updatedToolCalls = [...toolCalls];
|
|
941
|
-
const current = updatedToolCalls[toolCallIndex];
|
|
942
|
-
updatedToolCalls[toolCallIndex] = {
|
|
943
|
-
...current,
|
|
944
|
-
status: update.status,
|
|
945
|
-
...update.result !== void 0 ? { result: update.result } : {},
|
|
946
|
-
endTime: update.endTime
|
|
947
|
-
};
|
|
948
|
-
nextMessages[i] = {
|
|
949
|
-
...message,
|
|
950
|
-
toolCalls: updatedToolCalls
|
|
951
|
-
};
|
|
952
|
-
break;
|
|
953
|
-
}
|
|
1001
|
+
nextMessages = applyToolResultUpdateToMessages(nextMessages, update).messages;
|
|
954
1002
|
}
|
|
955
1003
|
return nextMessages;
|
|
956
1004
|
};
|
|
@@ -1070,6 +1118,7 @@ function useCopilotz({
|
|
|
1070
1118
|
const messagePageInfoRef = useRef2(messagePageInfo);
|
|
1071
1119
|
const isLoadingOlderMessagesRef = useRef2(isLoadingOlderMessages);
|
|
1072
1120
|
const persistedToolUpdatesRef = useRef2([]);
|
|
1121
|
+
const liveToolUpdatesRef = useRef2([]);
|
|
1073
1122
|
threadsRef.current = threads;
|
|
1074
1123
|
threadMetadataMapRef.current = threadMetadataMap;
|
|
1075
1124
|
threadExternalIdMapRef.current = threadExternalIdMap;
|
|
@@ -1133,87 +1182,46 @@ function useCopilotz({
|
|
|
1133
1182
|
return;
|
|
1134
1183
|
}
|
|
1135
1184
|
const senderType = getEventSenderType(payload);
|
|
1136
|
-
if (senderType
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
const
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
const resultObj = metadata?.output;
|
|
1148
|
-
const callId = payload.toolCallId || generateId();
|
|
1149
|
-
setMessages((prev) => {
|
|
1150
|
-
const next = [...prev];
|
|
1151
|
-
for (let i = next.length - 1; i >= 0; i--) {
|
|
1152
|
-
const m = next[i];
|
|
1153
|
-
if (m.role === "assistant") {
|
|
1154
|
-
const existing = Array.isArray(m.toolCalls) ? m.toolCalls : [];
|
|
1155
|
-
next[i] = {
|
|
1156
|
-
...m,
|
|
1157
|
-
toolCalls: [
|
|
1158
|
-
...existing,
|
|
1159
|
-
{
|
|
1160
|
-
id: callId,
|
|
1161
|
-
name: toolName,
|
|
1162
|
-
arguments: argsObj,
|
|
1163
|
-
result: resultObj,
|
|
1164
|
-
status: "completed",
|
|
1165
|
-
endTime: Date.now()
|
|
1166
|
-
}
|
|
1167
|
-
]
|
|
1168
|
-
};
|
|
1169
|
-
break;
|
|
1170
|
-
}
|
|
1171
|
-
}
|
|
1172
|
-
return next;
|
|
1173
|
-
});
|
|
1174
|
-
return;
|
|
1175
|
-
}
|
|
1176
|
-
if (senderType === "agent" && typeof payload.content === "string") {
|
|
1177
|
-
const agentSenderId = payload.senderId ?? payload.sender?.id ?? payload.sender?.name ?? void 0;
|
|
1178
|
-
const agentSenderName = payload.sender?.name ?? payload.senderId ?? void 0;
|
|
1179
|
-
const incomingAgentKey = agentSenderId ?? agentSenderName ?? null;
|
|
1180
|
-
setMessages((prev) => {
|
|
1181
|
-
const next = [...prev];
|
|
1182
|
-
for (let i = next.length - 1; i >= 0; i--) {
|
|
1183
|
-
const m = next[i];
|
|
1184
|
-
if (m.role === "assistant" && m.isStreaming && (!incomingAgentKey || messageAgentKey(m) === incomingAgentKey)) {
|
|
1185
|
-
next[i] = {
|
|
1186
|
-
...m,
|
|
1187
|
-
content: payload.content,
|
|
1188
|
-
isStreaming: false,
|
|
1189
|
-
isComplete: true,
|
|
1190
|
-
...agentSenderId ? { senderAgentId: agentSenderId } : {},
|
|
1191
|
-
...agentSenderName ? { senderName: agentSenderName } : {}
|
|
1192
|
-
};
|
|
1193
|
-
return next;
|
|
1194
|
-
}
|
|
1195
|
-
}
|
|
1196
|
-
const trimmedContent = payload.content.trim();
|
|
1197
|
-
if (!trimmedContent) {
|
|
1198
|
-
return prev;
|
|
1199
|
-
}
|
|
1200
|
-
return [
|
|
1201
|
-
...next,
|
|
1202
|
-
{
|
|
1203
|
-
id: generateId(),
|
|
1204
|
-
role: "assistant",
|
|
1185
|
+
if (senderType !== "agent" || typeof payload.content !== "string") return;
|
|
1186
|
+
const agentSenderId = payload.senderId ?? payload.sender?.id ?? payload.sender?.name ?? void 0;
|
|
1187
|
+
const agentSenderName = payload.sender?.name ?? payload.senderId ?? void 0;
|
|
1188
|
+
const incomingAgentKey = agentSenderId ?? agentSenderName ?? null;
|
|
1189
|
+
setMessages((prev) => {
|
|
1190
|
+
const next = [...prev];
|
|
1191
|
+
for (let i = next.length - 1; i >= 0; i--) {
|
|
1192
|
+
const m = next[i];
|
|
1193
|
+
if (m.role === "assistant" && m.isStreaming && (!incomingAgentKey || messageAgentKey(m) === incomingAgentKey)) {
|
|
1194
|
+
next[i] = {
|
|
1195
|
+
...m,
|
|
1205
1196
|
content: payload.content,
|
|
1206
|
-
timestamp: nowTs(),
|
|
1207
1197
|
isStreaming: false,
|
|
1208
1198
|
isComplete: true,
|
|
1209
|
-
metadata: liveMetadata,
|
|
1210
1199
|
...agentSenderId ? { senderAgentId: agentSenderId } : {},
|
|
1211
1200
|
...agentSenderName ? { senderName: agentSenderName } : {}
|
|
1212
|
-
}
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1201
|
+
};
|
|
1202
|
+
return next;
|
|
1203
|
+
}
|
|
1204
|
+
}
|
|
1205
|
+
const trimmedContent = payload.content.trim();
|
|
1206
|
+
if (!trimmedContent) {
|
|
1207
|
+
return prev;
|
|
1208
|
+
}
|
|
1209
|
+
return [
|
|
1210
|
+
...next,
|
|
1211
|
+
{
|
|
1212
|
+
id: generateId(),
|
|
1213
|
+
role: "assistant",
|
|
1214
|
+
content: payload.content,
|
|
1215
|
+
timestamp: nowTs(),
|
|
1216
|
+
isStreaming: false,
|
|
1217
|
+
isComplete: true,
|
|
1218
|
+
metadata: liveMetadata,
|
|
1219
|
+
...agentSenderId ? { senderAgentId: agentSenderId } : {},
|
|
1220
|
+
...agentSenderName ? { senderName: agentSenderName } : {}
|
|
1221
|
+
}
|
|
1222
|
+
];
|
|
1223
|
+
});
|
|
1224
|
+
}, []);
|
|
1217
1225
|
const updateThreadsState = useCallback2((rawThreads, preferredExternalId) => {
|
|
1218
1226
|
const metadataMap = {};
|
|
1219
1227
|
const externalMap = {};
|
|
@@ -1302,6 +1310,7 @@ function useCopilotz({
|
|
|
1302
1310
|
setIsLoadingOlderMessages(false);
|
|
1303
1311
|
setMessagePageInfo(createEmptyMessagePageInfo());
|
|
1304
1312
|
persistedToolUpdatesRef.current = [];
|
|
1313
|
+
liveToolUpdatesRef.current = [];
|
|
1305
1314
|
try {
|
|
1306
1315
|
const page = await fetchThreadMessagesPage(
|
|
1307
1316
|
threadId,
|
|
@@ -1597,78 +1606,70 @@ function useCopilotz({
|
|
|
1597
1606
|
});
|
|
1598
1607
|
};
|
|
1599
1608
|
const curThreadId = currentThreadIdRef.current;
|
|
1609
|
+
const applyLiveToolResultUpdate = (update) => {
|
|
1610
|
+
let matched = false;
|
|
1611
|
+
setMessages((prev) => {
|
|
1612
|
+
const next = applyToolResultUpdateToMessages(prev, update, {
|
|
1613
|
+
isStreaming: true,
|
|
1614
|
+
isComplete: false
|
|
1615
|
+
});
|
|
1616
|
+
matched = next.matched;
|
|
1617
|
+
return next.matched ? next.messages : prev;
|
|
1618
|
+
});
|
|
1619
|
+
if (!matched) {
|
|
1620
|
+
liveToolUpdatesRef.current.push(update);
|
|
1621
|
+
}
|
|
1622
|
+
};
|
|
1623
|
+
const finalizeActiveAssistantTurn = (finalAnswer) => {
|
|
1624
|
+
setMessages((prev) => {
|
|
1625
|
+
const currentIdx = prev.findIndex((message2) => message2.id === currentAssistantId && message2.role === "assistant");
|
|
1626
|
+
const fallbackIdx = currentIdx >= 0 ? currentIdx : (() => {
|
|
1627
|
+
for (let i = prev.length - 1; i >= 0; i--) {
|
|
1628
|
+
if (prev[i].role === "assistant" && prev[i].isStreaming) {
|
|
1629
|
+
return i;
|
|
1630
|
+
}
|
|
1631
|
+
}
|
|
1632
|
+
return -1;
|
|
1633
|
+
})();
|
|
1634
|
+
if (fallbackIdx < 0) return prev;
|
|
1635
|
+
const message = prev[fallbackIdx];
|
|
1636
|
+
const nextMessage = {
|
|
1637
|
+
...message,
|
|
1638
|
+
...typeof finalAnswer === "string" && finalAnswer.length > 0 ? { content: finalAnswer } : {},
|
|
1639
|
+
isStreaming: false,
|
|
1640
|
+
isComplete: true,
|
|
1641
|
+
...message.reasoning ? { isReasoningStreaming: false } : {}
|
|
1642
|
+
};
|
|
1643
|
+
if (message.content === nextMessage.content && message.isStreaming === nextMessage.isStreaming && message.isComplete === nextMessage.isComplete && message.isReasoningStreaming === nextMessage.isReasoningStreaming) {
|
|
1644
|
+
return prev;
|
|
1645
|
+
}
|
|
1646
|
+
const updated = [...prev];
|
|
1647
|
+
updated[fallbackIdx] = nextMessage;
|
|
1648
|
+
currentAssistantId = nextMessage.id;
|
|
1649
|
+
return updated;
|
|
1650
|
+
});
|
|
1651
|
+
};
|
|
1600
1652
|
const toServerMessageFromEvent = async (event) => {
|
|
1601
1653
|
if (!event) return null;
|
|
1602
1654
|
const type = event?.type || "";
|
|
1603
1655
|
const payload = event?.payload ?? event;
|
|
1604
1656
|
if (type === "TOOL_CALL") {
|
|
1605
|
-
const
|
|
1606
|
-
|
|
1607
|
-
const tcTool = tc?.tool ?? void 0;
|
|
1608
|
-
const call = payload?.call ?? metadata?.call;
|
|
1609
|
-
const func = call?.function ?? payload?.function;
|
|
1610
|
-
const toolName = tcTool?.name || func?.name || payload?.name || call?.name || metadata.toolName || metadata.tool || "tool";
|
|
1611
|
-
let argsObj = {};
|
|
1612
|
-
const possibleArgs = [
|
|
1613
|
-
tc?.args,
|
|
1614
|
-
func?.arguments,
|
|
1615
|
-
payload?.args,
|
|
1616
|
-
call?.arguments,
|
|
1617
|
-
metadata?.args,
|
|
1618
|
-
metadata?.arguments
|
|
1619
|
-
];
|
|
1620
|
-
for (const candidate of possibleArgs) {
|
|
1621
|
-
if (candidate === void 0 || candidate === null) continue;
|
|
1622
|
-
try {
|
|
1623
|
-
if (typeof candidate === "string") {
|
|
1624
|
-
argsObj = JSON.parse(candidate);
|
|
1625
|
-
break;
|
|
1626
|
-
}
|
|
1627
|
-
if (typeof candidate === "object") {
|
|
1628
|
-
argsObj = candidate;
|
|
1629
|
-
break;
|
|
1630
|
-
}
|
|
1631
|
-
} catch {
|
|
1632
|
-
}
|
|
1633
|
-
}
|
|
1634
|
-
const output = (tc?.output !== void 0 ? tc.output : void 0) ?? (metadata?.output !== void 0 ? metadata.output : void 0) ?? (payload?.output !== void 0 ? payload.output : void 0);
|
|
1635
|
-
const callId = tc?.id || call?.id || func?.id || payload?.id || generateId();
|
|
1636
|
-
const statusVal = tc?.status || payload?.status || event?.status || "pending";
|
|
1657
|
+
const parsedToolCall = extractLiveToolCall(payload);
|
|
1658
|
+
if (!parsedToolCall) return null;
|
|
1637
1659
|
return {
|
|
1638
1660
|
id: generateId(),
|
|
1639
1661
|
threadId: curThreadId ?? "",
|
|
1640
1662
|
senderType: "tool",
|
|
1641
1663
|
content: "",
|
|
1642
1664
|
toolCalls: [{
|
|
1643
|
-
id:
|
|
1644
|
-
name:
|
|
1645
|
-
args:
|
|
1646
|
-
output,
|
|
1647
|
-
status:
|
|
1665
|
+
id: parsedToolCall.id ?? generateId(),
|
|
1666
|
+
name: parsedToolCall.name,
|
|
1667
|
+
args: parsedToolCall.arguments,
|
|
1668
|
+
...parsedToolCall.result !== void 0 ? { output: parsedToolCall.result } : {},
|
|
1669
|
+
status: parsedToolCall.status
|
|
1648
1670
|
}]
|
|
1649
1671
|
};
|
|
1650
1672
|
}
|
|
1651
|
-
if (type === "MESSAGE" || type === "NEW_MESSAGE") {
|
|
1652
|
-
const senderType = payload?.senderType || payload?.sender?.type;
|
|
1653
|
-
const liveMetadata = event?.metadata && typeof event.metadata === "object" ? event.metadata : payload?.metadata;
|
|
1654
|
-
if (isInternalMessageMetadata(liveMetadata)) {
|
|
1655
|
-
return null;
|
|
1656
|
-
}
|
|
1657
|
-
if (senderType !== "agent") {
|
|
1658
|
-
return null;
|
|
1659
|
-
}
|
|
1660
|
-
const content = typeof payload?.content === "string" ? payload.content : "";
|
|
1661
|
-
if (!content.trim()) {
|
|
1662
|
-
return null;
|
|
1663
|
-
}
|
|
1664
|
-
return {
|
|
1665
|
-
id: generateId(),
|
|
1666
|
-
threadId: curThreadId ?? "",
|
|
1667
|
-
senderType: "agent",
|
|
1668
|
-
content,
|
|
1669
|
-
metadata: liveMetadata ?? {}
|
|
1670
|
-
};
|
|
1671
|
-
}
|
|
1672
1673
|
if (type === "ASSET_CREATED") {
|
|
1673
1674
|
const by = payload?.by || "";
|
|
1674
1675
|
if (by && by !== "tool") return null;
|
|
@@ -1694,6 +1695,7 @@ function useCopilotz({
|
|
|
1694
1695
|
abortControllerRef.current?.abort();
|
|
1695
1696
|
abortControllerRef.current = abortController;
|
|
1696
1697
|
setIsStreaming(true);
|
|
1698
|
+
liveToolUpdatesRef.current = [];
|
|
1697
1699
|
try {
|
|
1698
1700
|
const normalizedUserMetadata = params.userMetadata ? JSON.parse(JSON.stringify(params.userMetadata)) : void 0;
|
|
1699
1701
|
const contextSeed = userContextSeedRef.current;
|
|
@@ -1739,68 +1741,38 @@ function useCopilotz({
|
|
|
1739
1741
|
}
|
|
1740
1742
|
const type = event?.type || "";
|
|
1741
1743
|
const payload = getEventPayload(event);
|
|
1744
|
+
if (type === "TOOL_RESULT") {
|
|
1745
|
+
processToolOutput(payload ?? {});
|
|
1746
|
+
applyLiveToolResultUpdate(extractLiveToolResultUpdate(
|
|
1747
|
+
payload ?? {}
|
|
1748
|
+
));
|
|
1749
|
+
return;
|
|
1750
|
+
}
|
|
1751
|
+
if (type === "LLM_RESULT") {
|
|
1752
|
+
const finalAnswer = typeof payload?.answer === "string" ? payload.answer : void 0;
|
|
1753
|
+
finalizeActiveAssistantTurn(finalAnswer);
|
|
1754
|
+
pendingStartNewAssistantBubble = true;
|
|
1755
|
+
return;
|
|
1756
|
+
}
|
|
1742
1757
|
if (type === "MESSAGE" || type === "NEW_MESSAGE") {
|
|
1743
|
-
const senderType = getEventSenderType(payload);
|
|
1744
|
-
if (senderType === "tool") {
|
|
1745
|
-
const metadata = payload?.metadata ?? {};
|
|
1746
|
-
const toolCallsArray = metadata?.toolCalls;
|
|
1747
|
-
const toolCallData = toolCallsArray && toolCallsArray.length > 0 ? toolCallsArray[0] : void 0;
|
|
1748
|
-
if (!toolCallData) {
|
|
1749
|
-
return;
|
|
1750
|
-
}
|
|
1751
|
-
processToolOutput(metadata);
|
|
1752
|
-
const toolCallId = toolCallData.id;
|
|
1753
|
-
const toolCallName = toolCallData.name;
|
|
1754
|
-
const toolResult = toolCallData.output || payload?.content;
|
|
1755
|
-
const toolStatus = toolCallData.status || "completed";
|
|
1756
|
-
const isFailed = toolStatus === "failed" || toolCallData?.error;
|
|
1757
|
-
setMessages((prev) => {
|
|
1758
|
-
const updated = [...prev];
|
|
1759
|
-
for (let i = updated.length - 1; i >= 0; i--) {
|
|
1760
|
-
if (updated[i].role === "assistant" && updated[i].toolCalls) {
|
|
1761
|
-
const toolCalls = updated[i].toolCalls;
|
|
1762
|
-
if (toolCalls) {
|
|
1763
|
-
let toolCallIndex = toolCallId ? toolCalls.findIndex((tc) => tc.id === toolCallId) : -1;
|
|
1764
|
-
if (toolCallIndex === -1 && toolCallName) {
|
|
1765
|
-
toolCallIndex = toolCalls.findIndex(
|
|
1766
|
-
(tc) => tc.name === toolCallName && (tc.status === "pending" || tc.status === "running")
|
|
1767
|
-
);
|
|
1768
|
-
}
|
|
1769
|
-
if (toolCallIndex !== -1) {
|
|
1770
|
-
const updatedToolCalls = [...toolCalls];
|
|
1771
|
-
updatedToolCalls[toolCallIndex] = {
|
|
1772
|
-
...updatedToolCalls[toolCallIndex],
|
|
1773
|
-
status: isFailed ? "failed" : "completed",
|
|
1774
|
-
result: toolResult,
|
|
1775
|
-
endTime: Date.now()
|
|
1776
|
-
};
|
|
1777
|
-
updated[i] = {
|
|
1778
|
-
...updated[i],
|
|
1779
|
-
toolCalls: updatedToolCalls,
|
|
1780
|
-
isStreaming: true,
|
|
1781
|
-
isComplete: false
|
|
1782
|
-
};
|
|
1783
|
-
break;
|
|
1784
|
-
}
|
|
1785
|
-
}
|
|
1786
|
-
}
|
|
1787
|
-
}
|
|
1788
|
-
return updated;
|
|
1789
|
-
});
|
|
1790
|
-
return;
|
|
1791
|
-
}
|
|
1792
|
-
handleStreamMessageEvent(event);
|
|
1793
|
-
if (senderType === "agent") {
|
|
1794
|
-
currentAssistantId = generateId();
|
|
1795
|
-
pendingStartNewAssistantBubble = true;
|
|
1796
|
-
}
|
|
1797
1758
|
return;
|
|
1798
1759
|
}
|
|
1799
1760
|
if (type === "TOOL_CALL") {
|
|
1800
|
-
const
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
if (!
|
|
1761
|
+
const parsedToolCall = extractLiveToolCall(
|
|
1762
|
+
payload ?? {}
|
|
1763
|
+
);
|
|
1764
|
+
if (!parsedToolCall) return;
|
|
1765
|
+
const callId = parsedToolCall.id ?? generateId();
|
|
1766
|
+
const toolName = parsedToolCall.name;
|
|
1767
|
+
const bufferedUpdates = liveToolUpdatesRef.current;
|
|
1768
|
+
const matchingUpdateIndex = bufferedUpdates.findIndex((upd) => matchesToolResultUpdate({ id: callId, name: toolName }, upd));
|
|
1769
|
+
const bufferedUpdate = matchingUpdateIndex >= 0 ? bufferedUpdates[matchingUpdateIndex] : void 0;
|
|
1770
|
+
if (matchingUpdateIndex >= 0) {
|
|
1771
|
+
bufferedUpdates.splice(matchingUpdateIndex, 1);
|
|
1772
|
+
}
|
|
1773
|
+
const initialStatus = bufferedUpdate ? bufferedUpdate.status : parsedToolCall.status;
|
|
1774
|
+
const initialResult = bufferedUpdate && bufferedUpdate.result !== void 0 ? bufferedUpdate.result : parsedToolCall.result;
|
|
1775
|
+
const endTime = bufferedUpdate?.endTime;
|
|
1804
1776
|
setMessages(
|
|
1805
1777
|
(prev) => (() => {
|
|
1806
1778
|
const appendToolCall = (msg) => ({
|
|
@@ -1808,12 +1780,13 @@ function useCopilotz({
|
|
|
1808
1780
|
toolCalls: [
|
|
1809
1781
|
...Array.isArray(msg.toolCalls) ? msg.toolCalls : [],
|
|
1810
1782
|
{
|
|
1811
|
-
id:
|
|
1812
|
-
name:
|
|
1813
|
-
arguments:
|
|
1814
|
-
result:
|
|
1815
|
-
status:
|
|
1816
|
-
startTime: Date.now()
|
|
1783
|
+
id: callId,
|
|
1784
|
+
name: toolName,
|
|
1785
|
+
arguments: parsedToolCall.arguments,
|
|
1786
|
+
...initialResult !== void 0 ? { result: initialResult } : {},
|
|
1787
|
+
status: initialStatus,
|
|
1788
|
+
startTime: Date.now(),
|
|
1789
|
+
...endTime !== void 0 ? { endTime } : {}
|
|
1817
1790
|
}
|
|
1818
1791
|
]
|
|
1819
1792
|
});
|