@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 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 "MESSAGE": {
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
- onMessageEvent?.({ type: eventType, payload: payload2 });
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 data = await res.json();
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
- const nextMessages = [...messages];
999
+ let nextMessages = messages;
928
1000
  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
- }
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 === "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",
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
- }, [processToolOutput]);
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 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";
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: callId,
1644
- name: toolName,
1645
- args: argsObj,
1646
- output,
1647
- status: statusVal
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 sm2 = await toServerMessageFromEvent(event);
1801
- const toolCalls = sm2?.toolCalls;
1802
- const toolCall = toolCalls && toolCalls[0];
1803
- if (!toolCall) return;
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: 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()
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
  });