@copilotz/chat-adapter 0.3.9 → 0.4.1

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 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
@@ -337,6 +337,8 @@ async function runCopilotzStream(options) {
337
337
  const threadName = mergedThreadMetadata.name ?? null;
338
338
  const { name: _threadName, ...restThreadMetadata } = mergedThreadMetadata;
339
339
  const resolvedParticipants = Array.isArray(participants) && participants.length > 0 ? participants : [selectedAgent || "assistant"];
340
+ const resolvedTarget = targetAgent?.trim() || null;
341
+ const toolCallSenderId = selectedAgent || resolvedParticipants[0] || "assistant";
340
342
  const threadPayload = threadId || threadExternalId || threadName || Object.keys(restThreadMetadata).length > 0 ? {
341
343
  id: threadId ?? null,
342
344
  externalId: threadExternalId ?? null,
@@ -379,14 +381,13 @@ async function runCopilotzStream(options) {
379
381
  if (parts.length === 1 && parts[0].type === "text") return parts[0].text;
380
382
  return parts;
381
383
  })();
382
- const resolvedTarget = targetAgent?.trim() || null;
383
384
  const payload = {
384
385
  content: contentParts,
385
386
  sender: {
386
387
  type: normalizedToolCalls.length > 0 ? "agent" : "user",
387
- externalId: user.externalId,
388
- id: normalizedToolCalls.length > 0 ? "assistant" : void 0,
389
- name: normalizedToolCalls.length > 0 ? "assistant" : user.name ?? null,
388
+ externalId: normalizedToolCalls.length > 0 ? toolCallSenderId : user.externalId,
389
+ id: normalizedToolCalls.length > 0 ? toolCallSenderId : void 0,
390
+ name: normalizedToolCalls.length > 0 ? toolCallSenderId : user.name ?? null,
390
391
  metadata: Object.keys(senderMetadata).length > 0 ? senderMetadata : null
391
392
  },
392
393
  metadata: messageMetadata ?? null,
@@ -475,15 +476,11 @@ async function runCopilotzStream(options) {
475
476
  }
476
477
  break;
477
478
  }
478
- case "MESSAGE": {
479
+ case "NEW_MESSAGE": {
479
480
  hadNonReasoningContent = true;
480
481
  lastTokenWasReasoning = false;
481
482
  collectedMessages.push(payload2);
482
483
  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
484
  break;
488
485
  }
489
486
  case "TOOL_CALL": {
@@ -492,6 +489,13 @@ async function runCopilotzStream(options) {
492
489
  onMessageEvent?.(payload2);
493
490
  break;
494
491
  }
492
+ case "TOOL_RESULT":
493
+ case "LLM_RESULT": {
494
+ hadNonReasoningContent = true;
495
+ lastTokenWasReasoning = false;
496
+ onMessageEvent?.(payload2);
497
+ break;
498
+ }
495
499
  case "ASSET_CREATED": {
496
500
  const assetPayload = payload2 && typeof payload2 === "object" && "payload" in payload2 ? payload2.payload : payload2;
497
501
  if (assetPayload?.dataUrl) {
@@ -504,8 +508,15 @@ async function runCopilotzStream(options) {
504
508
  }
505
509
  case "ERROR":
506
510
  throw new Error(payload2?.error || "Copilotz stream error");
507
- default:
508
- onMessageEvent?.({ type: eventType, payload: payload2 });
511
+ default: {
512
+ const hasEnvelope = payload2 && typeof payload2 === "object" && "type" in payload2;
513
+ if (hasEnvelope) {
514
+ onMessageEvent?.(payload2);
515
+ } else {
516
+ onMessageEvent?.({ type: eventType, payload: payload2 });
517
+ }
518
+ break;
519
+ }
509
520
  }
510
521
  };
511
522
  while (true) {
@@ -661,7 +672,8 @@ async function getAssetDataUrl(refOrId) {
661
672
  const text = await res.text().catch(() => res.statusText);
662
673
  throw new Error(text || `Failed to fetch asset ${refOrId}`);
663
674
  }
664
- const data = await res.json();
675
+ const body = await res.json();
676
+ const data = body?.data ?? body;
665
677
  if (!data?.dataUrl) {
666
678
  throw new Error(data?.error || `Asset ${refOrId} has no dataUrl`);
667
679
  }
@@ -858,6 +870,67 @@ var parseToolArguments = (value) => {
858
870
  }
859
871
  return {};
860
872
  };
873
+ var matchesToolResultUpdate = (target, update) => {
874
+ if (update.id && target.id) {
875
+ return update.id === target.id;
876
+ }
877
+ return Boolean(update.name && target.name && update.name === target.name);
878
+ };
879
+ var findMatchingToolCallIndex = (toolCalls, update) => toolCalls.findIndex((toolCall) => matchesToolResultUpdate(
880
+ { id: toolCall.id, name: toolCall.name },
881
+ update
882
+ ) && (toolCall.status === "pending" || toolCall.status === "running" || typeof toolCall.result === "undefined"));
883
+ var applyToolResultUpdateToMessages = (messages, update, assistantPatch) => {
884
+ const nextMessages = [...messages];
885
+ for (let i = nextMessages.length - 1; i >= 0; i--) {
886
+ const message = nextMessages[i];
887
+ if (message.role !== "assistant" || !Array.isArray(message.toolCalls) || message.toolCalls.length === 0) {
888
+ continue;
889
+ }
890
+ const toolCallIndex = findMatchingToolCallIndex(message.toolCalls, update);
891
+ if (toolCallIndex === -1) continue;
892
+ const updatedToolCalls = [...message.toolCalls];
893
+ const current = updatedToolCalls[toolCallIndex];
894
+ updatedToolCalls[toolCallIndex] = {
895
+ ...current,
896
+ status: update.status,
897
+ ...update.result !== void 0 ? { result: update.result } : {},
898
+ endTime: update.endTime
899
+ };
900
+ nextMessages[i] = {
901
+ ...message,
902
+ toolCalls: updatedToolCalls,
903
+ ...assistantPatch ?? {}
904
+ };
905
+ return { messages: nextMessages, matched: true };
906
+ }
907
+ return { messages, matched: false };
908
+ };
909
+ var extractLiveToolCall = (payload) => {
910
+ const toolCall = payload?.toolCall;
911
+ if (!toolCall) return null;
912
+ const tool = toolCall.tool;
913
+ const name = typeof tool?.name === "string" ? tool.name : typeof tool?.id === "string" ? tool.id : "tool";
914
+ const result = toolCall.output !== void 0 ? toolCall.output : void 0;
915
+ return {
916
+ ...typeof toolCall.id === "string" ? { id: toolCall.id } : {},
917
+ name,
918
+ arguments: parseToolArguments(toolCall.args),
919
+ status: normalizeToolStatus(toolCall.status ?? payload?.status ?? "running"),
920
+ ...result !== void 0 ? { result } : {}
921
+ };
922
+ };
923
+ var extractLiveToolResultUpdate = (payload) => {
924
+ const tool = payload?.tool;
925
+ const result = payload?.projectedOutput !== void 0 ? payload.projectedOutput : payload?.output !== void 0 ? payload.output : payload?.content;
926
+ return {
927
+ ...typeof payload?.toolCallId === "string" ? { id: payload.toolCallId } : {},
928
+ ...typeof tool?.name === "string" ? { name: tool.name } : typeof tool?.id === "string" ? { name: tool.id } : {},
929
+ status: normalizeToolStatus(payload?.status),
930
+ ...result !== void 0 ? { result } : {},
931
+ endTime: nowTs()
932
+ };
933
+ };
861
934
  var extractToolCallsFromServerMessage = (msg) => {
862
935
  const metadata = msg.metadata ?? void 0;
863
936
  const topLevelToolCalls = Array.isArray(msg.toolCalls) ? msg.toolCalls || [] : [];
@@ -924,33 +997,9 @@ var extractToolResultUpdateFromMessage = (msg) => {
924
997
  };
925
998
  var mergePersistedToolResults = (messages, updates) => {
926
999
  if (updates.length === 0) return messages;
927
- const nextMessages = [...messages];
1000
+ let nextMessages = messages;
928
1001
  for (const update of updates) {
929
- for (let i = nextMessages.length - 1; i >= 0; i--) {
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
- }
1002
+ nextMessages = applyToolResultUpdateToMessages(nextMessages, update).messages;
954
1003
  }
955
1004
  return nextMessages;
956
1005
  };
@@ -1070,6 +1119,7 @@ function useCopilotz({
1070
1119
  const messagePageInfoRef = useRef2(messagePageInfo);
1071
1120
  const isLoadingOlderMessagesRef = useRef2(isLoadingOlderMessages);
1072
1121
  const persistedToolUpdatesRef = useRef2([]);
1122
+ const liveToolUpdatesRef = useRef2([]);
1073
1123
  threadsRef.current = threads;
1074
1124
  threadMetadataMapRef.current = threadMetadataMap;
1075
1125
  threadExternalIdMapRef.current = threadExternalIdMap;
@@ -1133,87 +1183,46 @@ function useCopilotz({
1133
1183
  return;
1134
1184
  }
1135
1185
  const senderType = getEventSenderType(payload);
1136
- if (senderType === "tool") {
1137
- const metadata = payload.metadata ?? event.metadata ?? {};
1138
- const output = metadata?.output ?? metadata;
1139
- if (output) processToolOutput(output);
1140
- const toolName = metadata?.toolName || metadata?.tool || "tool";
1141
- let argsObj = {};
1142
- try {
1143
- const argStr = metadata?.arguments ?? "{}";
1144
- argsObj = typeof argStr === "string" ? JSON.parse(argStr) : argStr;
1145
- } catch (_) {
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",
1186
+ if (senderType !== "agent" || typeof payload.content !== "string") return;
1187
+ const agentSenderId = payload.senderId ?? payload.sender?.id ?? payload.sender?.name ?? void 0;
1188
+ const agentSenderName = payload.sender?.name ?? payload.senderId ?? void 0;
1189
+ const incomingAgentKey = agentSenderId ?? agentSenderName ?? null;
1190
+ setMessages((prev) => {
1191
+ const next = [...prev];
1192
+ for (let i = next.length - 1; i >= 0; i--) {
1193
+ const m = next[i];
1194
+ if (m.role === "assistant" && m.isStreaming && (!incomingAgentKey || messageAgentKey(m) === incomingAgentKey)) {
1195
+ next[i] = {
1196
+ ...m,
1205
1197
  content: payload.content,
1206
- timestamp: nowTs(),
1207
1198
  isStreaming: false,
1208
1199
  isComplete: true,
1209
- metadata: liveMetadata,
1210
1200
  ...agentSenderId ? { senderAgentId: agentSenderId } : {},
1211
1201
  ...agentSenderName ? { senderName: agentSenderName } : {}
1212
- }
1213
- ];
1214
- });
1215
- }
1216
- }, [processToolOutput]);
1202
+ };
1203
+ return next;
1204
+ }
1205
+ }
1206
+ const trimmedContent = payload.content.trim();
1207
+ if (!trimmedContent) {
1208
+ return prev;
1209
+ }
1210
+ return [
1211
+ ...next,
1212
+ {
1213
+ id: generateId(),
1214
+ role: "assistant",
1215
+ content: payload.content,
1216
+ timestamp: nowTs(),
1217
+ isStreaming: false,
1218
+ isComplete: true,
1219
+ metadata: liveMetadata,
1220
+ ...agentSenderId ? { senderAgentId: agentSenderId } : {},
1221
+ ...agentSenderName ? { senderName: agentSenderName } : {}
1222
+ }
1223
+ ];
1224
+ });
1225
+ }, []);
1217
1226
  const updateThreadsState = useCallback2((rawThreads, preferredExternalId) => {
1218
1227
  const metadataMap = {};
1219
1228
  const externalMap = {};
@@ -1302,6 +1311,7 @@ function useCopilotz({
1302
1311
  setIsLoadingOlderMessages(false);
1303
1312
  setMessagePageInfo(createEmptyMessagePageInfo());
1304
1313
  persistedToolUpdatesRef.current = [];
1314
+ liveToolUpdatesRef.current = [];
1305
1315
  try {
1306
1316
  const page = await fetchThreadMessagesPage(
1307
1317
  threadId,
@@ -1597,78 +1607,70 @@ function useCopilotz({
1597
1607
  });
1598
1608
  };
1599
1609
  const curThreadId = currentThreadIdRef.current;
1610
+ const applyLiveToolResultUpdate = (update) => {
1611
+ let matched = false;
1612
+ setMessages((prev) => {
1613
+ const next = applyToolResultUpdateToMessages(prev, update, {
1614
+ isStreaming: true,
1615
+ isComplete: false
1616
+ });
1617
+ matched = next.matched;
1618
+ return next.matched ? next.messages : prev;
1619
+ });
1620
+ if (!matched) {
1621
+ liveToolUpdatesRef.current.push(update);
1622
+ }
1623
+ };
1624
+ const finalizeActiveAssistantTurn = (finalAnswer) => {
1625
+ setMessages((prev) => {
1626
+ const currentIdx = prev.findIndex((message2) => message2.id === currentAssistantId && message2.role === "assistant");
1627
+ const fallbackIdx = currentIdx >= 0 ? currentIdx : (() => {
1628
+ for (let i = prev.length - 1; i >= 0; i--) {
1629
+ if (prev[i].role === "assistant" && prev[i].isStreaming) {
1630
+ return i;
1631
+ }
1632
+ }
1633
+ return -1;
1634
+ })();
1635
+ if (fallbackIdx < 0) return prev;
1636
+ const message = prev[fallbackIdx];
1637
+ const nextMessage = {
1638
+ ...message,
1639
+ ...typeof finalAnswer === "string" && finalAnswer.length > 0 ? { content: finalAnswer } : {},
1640
+ isStreaming: false,
1641
+ isComplete: true,
1642
+ ...message.reasoning ? { isReasoningStreaming: false } : {}
1643
+ };
1644
+ if (message.content === nextMessage.content && message.isStreaming === nextMessage.isStreaming && message.isComplete === nextMessage.isComplete && message.isReasoningStreaming === nextMessage.isReasoningStreaming) {
1645
+ return prev;
1646
+ }
1647
+ const updated = [...prev];
1648
+ updated[fallbackIdx] = nextMessage;
1649
+ currentAssistantId = nextMessage.id;
1650
+ return updated;
1651
+ });
1652
+ };
1600
1653
  const toServerMessageFromEvent = async (event) => {
1601
1654
  if (!event) return null;
1602
1655
  const type = event?.type || "";
1603
1656
  const payload = event?.payload ?? event;
1604
1657
  if (type === "TOOL_CALL") {
1605
- const metadata = payload?.metadata ?? {};
1606
- const tc = payload?.toolCall ?? void 0;
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";
1658
+ const parsedToolCall = extractLiveToolCall(payload);
1659
+ if (!parsedToolCall) return null;
1637
1660
  return {
1638
1661
  id: generateId(),
1639
1662
  threadId: curThreadId ?? "",
1640
1663
  senderType: "tool",
1641
1664
  content: "",
1642
1665
  toolCalls: [{
1643
- id: callId,
1644
- name: toolName,
1645
- args: argsObj,
1646
- output,
1647
- status: statusVal
1666
+ id: parsedToolCall.id ?? generateId(),
1667
+ name: parsedToolCall.name,
1668
+ args: parsedToolCall.arguments,
1669
+ ...parsedToolCall.result !== void 0 ? { output: parsedToolCall.result } : {},
1670
+ status: parsedToolCall.status
1648
1671
  }]
1649
1672
  };
1650
1673
  }
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
1674
  if (type === "ASSET_CREATED") {
1673
1675
  const by = payload?.by || "";
1674
1676
  if (by && by !== "tool") return null;
@@ -1694,6 +1696,7 @@ function useCopilotz({
1694
1696
  abortControllerRef.current?.abort();
1695
1697
  abortControllerRef.current = abortController;
1696
1698
  setIsStreaming(true);
1699
+ liveToolUpdatesRef.current = [];
1697
1700
  try {
1698
1701
  const normalizedUserMetadata = params.userMetadata ? JSON.parse(JSON.stringify(params.userMetadata)) : void 0;
1699
1702
  const contextSeed = userContextSeedRef.current;
@@ -1739,68 +1742,38 @@ function useCopilotz({
1739
1742
  }
1740
1743
  const type = event?.type || "";
1741
1744
  const payload = getEventPayload(event);
1745
+ if (type === "TOOL_RESULT") {
1746
+ processToolOutput(payload ?? {});
1747
+ applyLiveToolResultUpdate(extractLiveToolResultUpdate(
1748
+ payload ?? {}
1749
+ ));
1750
+ return;
1751
+ }
1752
+ if (type === "LLM_RESULT") {
1753
+ const finalAnswer = typeof payload?.answer === "string" ? payload.answer : void 0;
1754
+ finalizeActiveAssistantTurn(finalAnswer);
1755
+ pendingStartNewAssistantBubble = true;
1756
+ return;
1757
+ }
1742
1758
  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
1759
  return;
1798
1760
  }
1799
1761
  if (type === "TOOL_CALL") {
1800
- const sm2 = await toServerMessageFromEvent(event);
1801
- const toolCalls = sm2?.toolCalls;
1802
- const toolCall = toolCalls && toolCalls[0];
1803
- if (!toolCall) return;
1762
+ const parsedToolCall = extractLiveToolCall(
1763
+ payload ?? {}
1764
+ );
1765
+ if (!parsedToolCall) return;
1766
+ const callId = parsedToolCall.id ?? generateId();
1767
+ const toolName = parsedToolCall.name;
1768
+ const bufferedUpdates = liveToolUpdatesRef.current;
1769
+ const matchingUpdateIndex = bufferedUpdates.findIndex((upd) => matchesToolResultUpdate({ id: callId, name: toolName }, upd));
1770
+ const bufferedUpdate = matchingUpdateIndex >= 0 ? bufferedUpdates[matchingUpdateIndex] : void 0;
1771
+ if (matchingUpdateIndex >= 0) {
1772
+ bufferedUpdates.splice(matchingUpdateIndex, 1);
1773
+ }
1774
+ const initialStatus = bufferedUpdate ? bufferedUpdate.status : parsedToolCall.status;
1775
+ const initialResult = bufferedUpdate && bufferedUpdate.result !== void 0 ? bufferedUpdate.result : parsedToolCall.result;
1776
+ const endTime = bufferedUpdate?.endTime;
1804
1777
  setMessages(
1805
1778
  (prev) => (() => {
1806
1779
  const appendToolCall = (msg) => ({
@@ -1808,12 +1781,13 @@ function useCopilotz({
1808
1781
  toolCalls: [
1809
1782
  ...Array.isArray(msg.toolCalls) ? msg.toolCalls : [],
1810
1783
  {
1811
- id: toolCall.id ?? generateId(),
1812
- name: toolCall.name ?? "tool",
1813
- arguments: toolCall.args ?? toolCall.arguments ?? {},
1814
- result: toolCall.output,
1815
- status: toolCall.status ?? "running",
1816
- startTime: Date.now()
1784
+ id: callId,
1785
+ name: toolName,
1786
+ arguments: parsedToolCall.arguments,
1787
+ ...initialResult !== void 0 ? { result: initialResult } : {},
1788
+ status: initialStatus,
1789
+ startTime: Date.now(),
1790
+ ...endTime !== void 0 ? { endTime } : {}
1817
1791
  }
1818
1792
  ]
1819
1793
  });