@copilotz/chat-adapter 0.9.44 → 0.9.45
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 +7 -3
- package/dist/index.js +501 -386
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -101,6 +101,38 @@ var User = createLucideIcon("user", __iconNode);
|
|
|
101
101
|
// src/useCopilotzChat.ts
|
|
102
102
|
import { useState as useState2, useCallback as useCallback2, useRef as useRef2, useEffect as useEffect2 } from "react";
|
|
103
103
|
|
|
104
|
+
// src/streamEvents.ts
|
|
105
|
+
var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
106
|
+
var hasValues = (value) => Array.isArray(value) && value.length > 0;
|
|
107
|
+
var getStreamEventPayload = (event) => {
|
|
108
|
+
if (!isRecord(event)) return event;
|
|
109
|
+
return "payload" in event ? event.payload : event;
|
|
110
|
+
};
|
|
111
|
+
var getLlmAttemptId = (event) => {
|
|
112
|
+
if (!isRecord(event)) return null;
|
|
113
|
+
const metadata = isRecord(event.metadata) ? event.metadata : {};
|
|
114
|
+
if (typeof metadata.llmAttemptId === "string" && metadata.llmAttemptId.trim().length > 0) {
|
|
115
|
+
return metadata.llmAttemptId.trim();
|
|
116
|
+
}
|
|
117
|
+
if (event.subjectType === "llm_attempt" && typeof event.subjectId === "string" && event.subjectId.trim().length > 0) {
|
|
118
|
+
return event.subjectId.trim();
|
|
119
|
+
}
|
|
120
|
+
return null;
|
|
121
|
+
};
|
|
122
|
+
var isTerminalEmptyLlmResultEvent = (event) => {
|
|
123
|
+
if (!isRecord(event) || event.type !== "LLM_RESULT") return false;
|
|
124
|
+
const payload = getStreamEventPayload(event);
|
|
125
|
+
if (!isRecord(payload)) return false;
|
|
126
|
+
if (payload.answer !== "") return false;
|
|
127
|
+
if (payload.finishReason === "tool_calls") return false;
|
|
128
|
+
if (hasValues(payload.toolCalls)) return false;
|
|
129
|
+
const metadata = isRecord(event.metadata) ? event.metadata : {};
|
|
130
|
+
const routing = isRecord(metadata.routing) ? metadata.routing : {};
|
|
131
|
+
if ((routing.action === "ask" || routing.action === "handoff") && typeof routing.targetId === "string" && routing.targetId.trim().length > 0) return false;
|
|
132
|
+
if (hasValues(metadata.targetQueue)) return false;
|
|
133
|
+
return true;
|
|
134
|
+
};
|
|
135
|
+
|
|
104
136
|
// src/copilotzService.ts
|
|
105
137
|
var rawBaseValue = import.meta.env?.VITE_API_URL;
|
|
106
138
|
var rawBase = typeof rawBaseValue === "string" && rawBaseValue.length > 0 ? rawBaseValue : "/api";
|
|
@@ -428,6 +460,11 @@ async function runCopilotzStream(options) {
|
|
|
428
460
|
let lastCompletedText = "";
|
|
429
461
|
let lastTokenWasReasoning = false;
|
|
430
462
|
let hadNonReasoningContent = false;
|
|
463
|
+
let activeLlmAttemptId = null;
|
|
464
|
+
let activePhaseKind = null;
|
|
465
|
+
let activePhaseId = null;
|
|
466
|
+
let phaseOrdinal = -1;
|
|
467
|
+
let fallbackAttemptOrdinal = 0;
|
|
431
468
|
const collectedMessages = [];
|
|
432
469
|
let collectedMedia = null;
|
|
433
470
|
const resetTokenAggregation = () => {
|
|
@@ -436,6 +473,30 @@ async function runCopilotzStream(options) {
|
|
|
436
473
|
lastTokenWasReasoning = false;
|
|
437
474
|
hadNonReasoningContent = false;
|
|
438
475
|
};
|
|
476
|
+
const startAttempt = (event) => {
|
|
477
|
+
activeLlmAttemptId = getLlmAttemptId(event) ?? `stream-attempt:${fallbackAttemptOrdinal++}`;
|
|
478
|
+
activePhaseKind = null;
|
|
479
|
+
activePhaseId = null;
|
|
480
|
+
phaseOrdinal = -1;
|
|
481
|
+
resetTokenAggregation();
|
|
482
|
+
};
|
|
483
|
+
const getTokenContext = (isReasoning) => {
|
|
484
|
+
if (!activeLlmAttemptId) {
|
|
485
|
+
startAttempt(null);
|
|
486
|
+
}
|
|
487
|
+
const kind = isReasoning ? "reasoning" : "answer";
|
|
488
|
+
if (activePhaseKind !== kind || !activePhaseId) {
|
|
489
|
+
phaseOrdinal += 1;
|
|
490
|
+
activePhaseKind = kind;
|
|
491
|
+
activePhaseId = `${activeLlmAttemptId}:${kind}:${phaseOrdinal}`;
|
|
492
|
+
}
|
|
493
|
+
return {
|
|
494
|
+
isReasoning,
|
|
495
|
+
llmAttemptId: activeLlmAttemptId,
|
|
496
|
+
phaseId: activePhaseId,
|
|
497
|
+
phaseOrdinal
|
|
498
|
+
};
|
|
499
|
+
};
|
|
439
500
|
const processEvent = async (eventChunk) => {
|
|
440
501
|
if (!eventChunk.trim()) return;
|
|
441
502
|
const lines = eventChunk.split("\n");
|
|
@@ -461,10 +522,16 @@ async function runCopilotzStream(options) {
|
|
|
461
522
|
return;
|
|
462
523
|
}
|
|
463
524
|
switch (eventType) {
|
|
525
|
+
case "LLM_CALL": {
|
|
526
|
+
startAttempt(payload2);
|
|
527
|
+
await onMessageEvent?.(payload2);
|
|
528
|
+
break;
|
|
529
|
+
}
|
|
464
530
|
case "TOKEN": {
|
|
465
531
|
const inner = payload2?.payload ?? payload2;
|
|
466
532
|
const chunk = typeof inner?.token === "string" ? inner.token : "";
|
|
467
|
-
const isReasoning =
|
|
533
|
+
const isReasoning = typeof inner?.isReasoning === "boolean" ? inner.isReasoning : activePhaseKind === "reasoning" || lastTokenWasReasoning;
|
|
534
|
+
const tokenContext = getTokenContext(isReasoning);
|
|
468
535
|
if (isReasoning && !lastTokenWasReasoning && hadNonReasoningContent) {
|
|
469
536
|
aggregatedReasoning = "";
|
|
470
537
|
aggregatedText = "";
|
|
@@ -482,12 +549,14 @@ async function runCopilotzStream(options) {
|
|
|
482
549
|
const isComplete = Boolean(inner?.isComplete);
|
|
483
550
|
if (chunk || isComplete) {
|
|
484
551
|
const tokenText = isReasoning ? aggregatedReasoning : aggregatedText;
|
|
485
|
-
onToken?.(tokenText, isComplete, payload2,
|
|
552
|
+
onToken?.(tokenText, isComplete, payload2, tokenContext);
|
|
486
553
|
if (isComplete) {
|
|
487
554
|
if (!isReasoning && tokenText) {
|
|
488
555
|
lastCompletedText = tokenText;
|
|
489
556
|
}
|
|
490
557
|
resetTokenAggregation();
|
|
558
|
+
activePhaseKind = null;
|
|
559
|
+
activePhaseId = null;
|
|
491
560
|
}
|
|
492
561
|
}
|
|
493
562
|
break;
|
|
@@ -833,8 +902,6 @@ function useUrlState(config = {}) {
|
|
|
833
902
|
}
|
|
834
903
|
|
|
835
904
|
// src/activity.ts
|
|
836
|
-
var thinkingId = "thinking";
|
|
837
|
-
var answeringId = "answering";
|
|
838
905
|
var getItems = (message) => Array.isArray(message.activity?.items) ? message.activity.items : [];
|
|
839
906
|
var setItems = (message, items) => ({
|
|
840
907
|
...message,
|
|
@@ -860,7 +927,8 @@ var upsertItem = (message, item) => {
|
|
|
860
927
|
};
|
|
861
928
|
return setItems(message, next);
|
|
862
929
|
};
|
|
863
|
-
var completeItems = (message, shouldComplete) => setItems(message, getItems(message).map((item) => item.status === "active" && shouldComplete(item) ? { ...item, status: "complete", completedAt
|
|
930
|
+
var completeItems = (message, shouldComplete, completedAt = Date.now()) => setItems(message, getItems(message).map((item) => item.status === "active" && shouldComplete(item) ? { ...item, status: "complete", completedAt } : item));
|
|
931
|
+
var removeItems = (message, shouldRemove) => setItems(message, getItems(message).filter((item) => !shouldRemove(item)));
|
|
864
932
|
var hasVisibleAssistantOutput = (message) => {
|
|
865
933
|
if (message.role !== "assistant") return false;
|
|
866
934
|
if (typeof message.content === "string" && message.content.trim().length > 0) return true;
|
|
@@ -875,38 +943,40 @@ var toPublicChatMessage = (message) => {
|
|
|
875
943
|
var updateAssistantMessageToken = (message, params) => {
|
|
876
944
|
if (message.role !== "assistant") return message;
|
|
877
945
|
if (params.isReasoning) {
|
|
878
|
-
|
|
946
|
+
const activityId2 = params.activityId ?? `${message.id}:thinking`;
|
|
947
|
+
return upsertItem(completeItems(removeItems({
|
|
879
948
|
...message,
|
|
880
949
|
isStreaming: true,
|
|
881
950
|
isComplete: false
|
|
882
|
-
}, {
|
|
883
|
-
id:
|
|
951
|
+
}, (item) => item.kind === "answering"), (item) => item.id !== activityId2 && (item.kind === "thinking" || item.kind === "answering"), params.at), {
|
|
952
|
+
id: activityId2,
|
|
884
953
|
kind: "thinking",
|
|
885
954
|
status: "active",
|
|
886
|
-
startedAt: getItems(message).find((item) => item.id ===
|
|
955
|
+
startedAt: getItems(message).find((item) => item.id === activityId2)?.startedAt ?? params.at ?? Date.now(),
|
|
887
956
|
details: { reasoning: params.partial }
|
|
888
957
|
});
|
|
889
958
|
}
|
|
890
|
-
|
|
959
|
+
const activityId = params.activityId ?? `${message.id}:answering`;
|
|
960
|
+
return upsertItem(completeItems(removeItems({
|
|
891
961
|
...message,
|
|
892
962
|
content: params.partial,
|
|
893
963
|
isStreaming: true,
|
|
894
964
|
isComplete: false
|
|
895
|
-
}, (item) => item.kind === "thinking" || item.kind === "
|
|
896
|
-
id:
|
|
965
|
+
}, (item) => item.kind === "answering" && item.id !== activityId), (item) => item.kind === "thinking" || item.kind === "answering", params.at), {
|
|
966
|
+
id: activityId,
|
|
897
967
|
kind: "answering",
|
|
898
968
|
status: "active",
|
|
899
|
-
startedAt: getItems(message).find((item) => item.id ===
|
|
969
|
+
startedAt: getItems(message).find((item) => item.id === activityId)?.startedAt ?? params.at ?? Date.now()
|
|
900
970
|
});
|
|
901
971
|
};
|
|
902
972
|
var appendAssistantToolCall = (message, toolCall) => {
|
|
903
973
|
if (message.role !== "assistant") return message;
|
|
904
974
|
const status = toolStatusToActivityStatus(toolCall.status);
|
|
905
|
-
return upsertItem({
|
|
975
|
+
return upsertItem(completeItems(removeItems({
|
|
906
976
|
...message,
|
|
907
977
|
isStreaming: true,
|
|
908
978
|
isComplete: false
|
|
909
|
-
}, {
|
|
979
|
+
}, (item) => item.kind === "answering"), (item) => item.kind === "thinking" || item.kind === "answering"), {
|
|
910
980
|
id: toolCall.id,
|
|
911
981
|
kind: "tool",
|
|
912
982
|
status,
|
|
@@ -955,23 +1025,23 @@ var applyAssistantToolResult = (message, update) => {
|
|
|
955
1025
|
};
|
|
956
1026
|
return setItems(message, next);
|
|
957
1027
|
};
|
|
958
|
-
var finalizeAssistantMessage = (message, finalAnswer) => {
|
|
1028
|
+
var finalizeAssistantMessage = (message, finalAnswer, completedAt = Date.now()) => {
|
|
959
1029
|
if (message.role !== "assistant") return message;
|
|
960
1030
|
const completed = completeItems({
|
|
961
1031
|
...message,
|
|
962
1032
|
...typeof finalAnswer === "string" && finalAnswer.length > 0 ? { content: finalAnswer } : {},
|
|
963
1033
|
isStreaming: false,
|
|
964
1034
|
isComplete: true
|
|
965
|
-
}, (item) => item.kind === "thinking" || item.kind === "answering");
|
|
1035
|
+
}, (item) => item.kind === "thinking" || item.kind === "answering", completedAt);
|
|
966
1036
|
return setItems(completed, getItems(completed).filter((item) => item.kind !== "answering"));
|
|
967
1037
|
};
|
|
968
|
-
var closeAssistantMessage = (message) => {
|
|
1038
|
+
var closeAssistantMessage = (message, completedAt = Date.now()) => {
|
|
969
1039
|
if (message.role !== "assistant") return message;
|
|
970
1040
|
return completeItems({
|
|
971
1041
|
...message,
|
|
972
1042
|
isStreaming: false,
|
|
973
1043
|
isComplete: true
|
|
974
|
-
}, () => true);
|
|
1044
|
+
}, () => true, completedAt);
|
|
975
1045
|
};
|
|
976
1046
|
|
|
977
1047
|
// src/contract.ts
|
|
@@ -981,9 +1051,9 @@ var ContractViolation = class extends Error {
|
|
|
981
1051
|
this.name = "ContractViolation";
|
|
982
1052
|
}
|
|
983
1053
|
};
|
|
984
|
-
var
|
|
1054
|
+
var isRecord2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
985
1055
|
var expectRecord = (value, path) => {
|
|
986
|
-
if (!
|
|
1056
|
+
if (!isRecord2(value)) throw new ContractViolation(`${path} must be an object`);
|
|
987
1057
|
return value;
|
|
988
1058
|
};
|
|
989
1059
|
var expectString = (value, path) => {
|
|
@@ -1104,14 +1174,14 @@ var resolveLiveEventSender = (event, options = {}) => {
|
|
|
1104
1174
|
const raw = expectRecord(event, "stream event");
|
|
1105
1175
|
const payload = raw.payload === void 0 ? raw : expectRecord(raw.payload, "stream event.payload");
|
|
1106
1176
|
const agent = payload.agent ?? raw.agent;
|
|
1107
|
-
if (
|
|
1177
|
+
if (isRecord2(agent)) {
|
|
1108
1178
|
return resolveAgentSender({
|
|
1109
1179
|
id: expectString(agent.id, "stream event.payload.agent.id"),
|
|
1110
1180
|
name: expectString(agent.name, "stream event.payload.agent.name")
|
|
1111
1181
|
}, options);
|
|
1112
1182
|
}
|
|
1113
1183
|
const sender = payload.sender ?? raw.sender;
|
|
1114
|
-
if (!
|
|
1184
|
+
if (!isRecord2(sender)) {
|
|
1115
1185
|
throw new ContractViolation("stream event sender contract requires payload.agent or payload.sender");
|
|
1116
1186
|
}
|
|
1117
1187
|
const type = expectSenderType(sender.type ?? payload.senderType, "stream event.payload.sender.type");
|
|
@@ -1241,6 +1311,98 @@ async function resolveAssetsInMessages(messages, getRequestHeaders) {
|
|
|
1241
1311
|
}));
|
|
1242
1312
|
}
|
|
1243
1313
|
|
|
1314
|
+
// src/messageReconciliation.ts
|
|
1315
|
+
var CLIENT_MESSAGE_ID_METADATA_KEY = "clientMessageId";
|
|
1316
|
+
var LLM_ATTEMPT_ID_METADATA_KEY = "llmAttemptId";
|
|
1317
|
+
var stringifyForCompare = (value) => {
|
|
1318
|
+
if (value === void 0) return "";
|
|
1319
|
+
try {
|
|
1320
|
+
return JSON.stringify(value) ?? "";
|
|
1321
|
+
} catch {
|
|
1322
|
+
return String(value);
|
|
1323
|
+
}
|
|
1324
|
+
};
|
|
1325
|
+
var getMessageSignature = (message) => JSON.stringify({
|
|
1326
|
+
id: message.id,
|
|
1327
|
+
role: message.role,
|
|
1328
|
+
content: message.content ?? "",
|
|
1329
|
+
isStreaming: message.isStreaming === true,
|
|
1330
|
+
isComplete: message.isComplete === true,
|
|
1331
|
+
attachments: stringifyForCompare(message.attachments),
|
|
1332
|
+
activity: stringifyForCompare(message.activity),
|
|
1333
|
+
metadata: stringifyForCompare(message.metadata),
|
|
1334
|
+
sender: message.sender ? {
|
|
1335
|
+
id: message.sender.id,
|
|
1336
|
+
type: message.sender.type,
|
|
1337
|
+
name: message.sender.name,
|
|
1338
|
+
agentId: message.sender.agentId
|
|
1339
|
+
} : null
|
|
1340
|
+
});
|
|
1341
|
+
var getMetadataString = (message, key) => {
|
|
1342
|
+
const value = message.metadata?.[key];
|
|
1343
|
+
return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
|
|
1344
|
+
};
|
|
1345
|
+
var getCorrelationKeys = (message) => {
|
|
1346
|
+
const clientMessageId = getMetadataString(
|
|
1347
|
+
message,
|
|
1348
|
+
CLIENT_MESSAGE_ID_METADATA_KEY
|
|
1349
|
+
);
|
|
1350
|
+
const llmAttemptId = getMetadataString(
|
|
1351
|
+
message,
|
|
1352
|
+
LLM_ATTEMPT_ID_METADATA_KEY
|
|
1353
|
+
);
|
|
1354
|
+
return [
|
|
1355
|
+
...clientMessageId ? [`client-message:${message.role}:${clientMessageId}`] : [],
|
|
1356
|
+
...llmAttemptId ? [`llm-attempt:${message.role}:${llmAttemptId}`] : []
|
|
1357
|
+
];
|
|
1358
|
+
};
|
|
1359
|
+
var indexUniqueFreshCorrelations = (freshMessages) => {
|
|
1360
|
+
const messagesByCorrelation = /* @__PURE__ */ new Map();
|
|
1361
|
+
for (const message of freshMessages) {
|
|
1362
|
+
for (const key of getCorrelationKeys(message)) {
|
|
1363
|
+
messagesByCorrelation.set(
|
|
1364
|
+
key,
|
|
1365
|
+
messagesByCorrelation.has(key) ? null : message
|
|
1366
|
+
);
|
|
1367
|
+
}
|
|
1368
|
+
}
|
|
1369
|
+
return messagesByCorrelation;
|
|
1370
|
+
};
|
|
1371
|
+
var reconcileThreadMessages = (currentMessages, freshMessages) => {
|
|
1372
|
+
if (freshMessages.length === 0) {
|
|
1373
|
+
return { messages: currentMessages, changed: false };
|
|
1374
|
+
}
|
|
1375
|
+
if (currentMessages.length === 0) {
|
|
1376
|
+
return { messages: freshMessages, changed: true };
|
|
1377
|
+
}
|
|
1378
|
+
const freshById = new Map(freshMessages.map((message) => [message.id, message]));
|
|
1379
|
+
const freshByCorrelation = indexUniqueFreshCorrelations(freshMessages);
|
|
1380
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1381
|
+
let changed = false;
|
|
1382
|
+
const nextMessages = currentMessages.map((message) => {
|
|
1383
|
+
const exactMatch = freshById.get(message.id);
|
|
1384
|
+
const correlatedMatch = exactMatch ? null : getCorrelationKeys(message).map((key) => freshByCorrelation.get(key)).find((candidate) => candidate !== null && candidate !== void 0 && !seen.has(candidate.id));
|
|
1385
|
+
const fresh = exactMatch ?? correlatedMatch;
|
|
1386
|
+
if (!fresh) return message;
|
|
1387
|
+
seen.add(fresh.id);
|
|
1388
|
+
if (getMessageSignature(message) === getMessageSignature(fresh)) {
|
|
1389
|
+
return message;
|
|
1390
|
+
}
|
|
1391
|
+
changed = true;
|
|
1392
|
+
return fresh;
|
|
1393
|
+
});
|
|
1394
|
+
const appended = freshMessages.filter((message) => !seen.has(message.id));
|
|
1395
|
+
if (appended.length > 0) {
|
|
1396
|
+
changed = true;
|
|
1397
|
+
nextMessages.push(...appended);
|
|
1398
|
+
nextMessages.sort((a, b) => {
|
|
1399
|
+
const timestampDelta = (a.timestamp ?? 0) - (b.timestamp ?? 0);
|
|
1400
|
+
return timestampDelta !== 0 ? timestampDelta : a.id.localeCompare(b.id);
|
|
1401
|
+
});
|
|
1402
|
+
}
|
|
1403
|
+
return changed ? { messages: nextMessages, changed } : { messages: currentMessages, changed: false };
|
|
1404
|
+
};
|
|
1405
|
+
|
|
1244
1406
|
// src/toolActivity.ts
|
|
1245
1407
|
var fail = (message) => {
|
|
1246
1408
|
throw new ContractViolation(message);
|
|
@@ -1263,7 +1425,7 @@ var formatToolError = (error) => {
|
|
|
1263
1425
|
}
|
|
1264
1426
|
};
|
|
1265
1427
|
var expectToolArguments = (value, path) => {
|
|
1266
|
-
if (
|
|
1428
|
+
if (isRecord2(value)) return value;
|
|
1267
1429
|
if (typeof value === "string") {
|
|
1268
1430
|
try {
|
|
1269
1431
|
const parsed = JSON.parse(value);
|
|
@@ -1439,9 +1601,6 @@ var canAttachToStreamingAssistant = (message, incomingAgentKey) => {
|
|
|
1439
1601
|
const currentAgentKey = messageAgentKey(message);
|
|
1440
1602
|
return !incomingAgentKey || !currentAgentKey || currentAgentKey === incomingAgentKey;
|
|
1441
1603
|
};
|
|
1442
|
-
var canAttachToCurrentStreamingAssistant = (message) => {
|
|
1443
|
-
return !!message && message.role === "assistant" && message.isStreaming === true;
|
|
1444
|
-
};
|
|
1445
1604
|
|
|
1446
1605
|
// src/messageContract.ts
|
|
1447
1606
|
var isInternalMessageMetadata = (metadata) => metadata?.visibility === "internal";
|
|
@@ -1548,9 +1707,10 @@ var convertServerMessage = (msg, options = {}) => {
|
|
|
1548
1707
|
const isToolSender = msg.senderType === "tool";
|
|
1549
1708
|
const content = isToolSender ? "" : messageContent;
|
|
1550
1709
|
const reasoning = typeof msg.reasoning === "string" && msg.reasoning.length > 0 ? msg.reasoning : void 0;
|
|
1710
|
+
const llmAttemptId = typeof metadata?.[LLM_ATTEMPT_ID_METADATA_KEY] === "string" ? metadata[LLM_ATTEMPT_ID_METADATA_KEY].trim() : "";
|
|
1551
1711
|
const activityItems = [
|
|
1552
1712
|
...reasoning ? [{
|
|
1553
|
-
id: `${msg.id}:
|
|
1713
|
+
id: `${llmAttemptId || msg.id}:reasoning:0`,
|
|
1554
1714
|
kind: "thinking",
|
|
1555
1715
|
status: "complete",
|
|
1556
1716
|
completedAt: timestamp,
|
|
@@ -1631,38 +1791,6 @@ var prepareHydratedMessages = async (rawMessages, options = {}) => {
|
|
|
1631
1791
|
};
|
|
1632
1792
|
};
|
|
1633
1793
|
|
|
1634
|
-
// src/streamEvents.ts
|
|
1635
|
-
var isRecord2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1636
|
-
var hasValues = (value) => Array.isArray(value) && value.length > 0;
|
|
1637
|
-
var getStreamEventPayload = (event) => {
|
|
1638
|
-
if (!isRecord2(event)) return event;
|
|
1639
|
-
return "payload" in event ? event.payload : event;
|
|
1640
|
-
};
|
|
1641
|
-
var getLlmAttemptId = (event) => {
|
|
1642
|
-
if (!isRecord2(event)) return null;
|
|
1643
|
-
const metadata = isRecord2(event.metadata) ? event.metadata : {};
|
|
1644
|
-
if (typeof metadata.llmAttemptId === "string" && metadata.llmAttemptId.trim().length > 0) {
|
|
1645
|
-
return metadata.llmAttemptId.trim();
|
|
1646
|
-
}
|
|
1647
|
-
if (event.subjectType === "llm_attempt" && typeof event.subjectId === "string" && event.subjectId.trim().length > 0) {
|
|
1648
|
-
return event.subjectId.trim();
|
|
1649
|
-
}
|
|
1650
|
-
return null;
|
|
1651
|
-
};
|
|
1652
|
-
var isTerminalEmptyLlmResultEvent = (event) => {
|
|
1653
|
-
if (!isRecord2(event) || event.type !== "LLM_RESULT") return false;
|
|
1654
|
-
const payload = getStreamEventPayload(event);
|
|
1655
|
-
if (!isRecord2(payload)) return false;
|
|
1656
|
-
if (payload.answer !== "") return false;
|
|
1657
|
-
if (payload.finishReason === "tool_calls") return false;
|
|
1658
|
-
if (hasValues(payload.toolCalls)) return false;
|
|
1659
|
-
const metadata = isRecord2(event.metadata) ? event.metadata : {};
|
|
1660
|
-
const routing = isRecord2(metadata.routing) ? metadata.routing : {};
|
|
1661
|
-
if ((routing.action === "ask" || routing.action === "handoff") && typeof routing.targetId === "string" && routing.targetId.trim().length > 0) return false;
|
|
1662
|
-
if (hasValues(metadata.targetQueue)) return false;
|
|
1663
|
-
return true;
|
|
1664
|
-
};
|
|
1665
|
-
|
|
1666
1794
|
// src/threadIdentity.ts
|
|
1667
1795
|
var identityValues = ({ id, externalId }) => [id, externalId].filter((value) => typeof value === "string" && value.length > 0);
|
|
1668
1796
|
var isSameThreadIdentity = (owner, current) => {
|
|
@@ -1672,96 +1800,244 @@ var isSameThreadIdentity = (owner, current) => {
|
|
|
1672
1800
|
return ownerValues.some((value) => currentValues.has(value));
|
|
1673
1801
|
};
|
|
1674
1802
|
|
|
1675
|
-
// src/
|
|
1676
|
-
var
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
return JSON.stringify(value) ?? "";
|
|
1682
|
-
} catch {
|
|
1683
|
-
return String(value);
|
|
1684
|
-
}
|
|
1685
|
-
};
|
|
1686
|
-
var getMessageSignature = (message) => JSON.stringify({
|
|
1687
|
-
id: message.id,
|
|
1688
|
-
role: message.role,
|
|
1689
|
-
content: message.content ?? "",
|
|
1690
|
-
isStreaming: message.isStreaming === true,
|
|
1691
|
-
isComplete: message.isComplete === true,
|
|
1692
|
-
attachments: stringifyForCompare(message.attachments),
|
|
1693
|
-
activity: stringifyForCompare(message.activity),
|
|
1694
|
-
metadata: stringifyForCompare(message.metadata),
|
|
1695
|
-
sender: message.sender ? {
|
|
1696
|
-
id: message.sender.id,
|
|
1697
|
-
type: message.sender.type,
|
|
1698
|
-
name: message.sender.name,
|
|
1699
|
-
agentId: message.sender.agentId
|
|
1700
|
-
} : null
|
|
1803
|
+
// src/liveRun.ts
|
|
1804
|
+
var copyState = (state) => ({
|
|
1805
|
+
...state,
|
|
1806
|
+
attemptsById: new Map(state.attemptsById),
|
|
1807
|
+
toolMessageByCallId: new Map(state.toolMessageByCallId),
|
|
1808
|
+
pendingToolResultsByCallId: new Map(state.pendingToolResultsByCallId)
|
|
1701
1809
|
});
|
|
1702
|
-
var
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1810
|
+
var createLiveRunState = (initialMessageId) => ({
|
|
1811
|
+
initialMessageId,
|
|
1812
|
+
initialMessageClaimed: false,
|
|
1813
|
+
activeAttemptId: null,
|
|
1814
|
+
lastAttemptId: null,
|
|
1815
|
+
attemptsById: /* @__PURE__ */ new Map(),
|
|
1816
|
+
toolMessageByCallId: /* @__PURE__ */ new Map(),
|
|
1817
|
+
pendingToolResultsByCallId: /* @__PURE__ */ new Map()
|
|
1818
|
+
});
|
|
1819
|
+
var ensureAttempt = (state, attemptId, sender, at, options) => {
|
|
1820
|
+
const existing = state.attemptsById.get(attemptId);
|
|
1821
|
+
if (existing) {
|
|
1822
|
+
const next2 = copyState(state);
|
|
1823
|
+
const cursor2 = sender && existing.sender !== sender ? { ...existing, sender } : existing;
|
|
1824
|
+
next2.attemptsById.set(attemptId, cursor2);
|
|
1825
|
+
next2.activeAttemptId = attemptId;
|
|
1826
|
+
next2.lastAttemptId = attemptId;
|
|
1827
|
+
return { state: next2, cursor: cursor2, operations: [] };
|
|
1828
|
+
}
|
|
1829
|
+
const next = copyState(state);
|
|
1830
|
+
const messageId = next.initialMessageClaimed ? options.createId() : next.initialMessageId;
|
|
1831
|
+
const cursor = { messageId, sender };
|
|
1832
|
+
next.initialMessageClaimed = true;
|
|
1833
|
+
next.activeAttemptId = attemptId;
|
|
1834
|
+
next.lastAttemptId = attemptId;
|
|
1835
|
+
next.attemptsById.set(attemptId, cursor);
|
|
1836
|
+
return {
|
|
1837
|
+
state: next,
|
|
1838
|
+
cursor,
|
|
1839
|
+
operations: [{
|
|
1840
|
+
type: "ensure-attempt",
|
|
1841
|
+
messageId,
|
|
1842
|
+
attemptId,
|
|
1843
|
+
sender,
|
|
1844
|
+
at
|
|
1845
|
+
}]
|
|
1846
|
+
};
|
|
1719
1847
|
};
|
|
1720
|
-
var
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1848
|
+
var transitionLiveRun = (state, action, options) => {
|
|
1849
|
+
if (action.type === "attempt-start") {
|
|
1850
|
+
const ensured = ensureAttempt(
|
|
1851
|
+
state,
|
|
1852
|
+
action.attemptId,
|
|
1853
|
+
action.sender,
|
|
1854
|
+
action.at,
|
|
1855
|
+
options
|
|
1856
|
+
);
|
|
1857
|
+
return { state: ensured.state, operations: ensured.operations };
|
|
1858
|
+
}
|
|
1859
|
+
if (action.type === "token") {
|
|
1860
|
+
const ensured = ensureAttempt(
|
|
1861
|
+
state,
|
|
1862
|
+
action.attemptId,
|
|
1863
|
+
action.sender,
|
|
1864
|
+
action.at,
|
|
1865
|
+
options
|
|
1866
|
+
);
|
|
1867
|
+
return {
|
|
1868
|
+
state: ensured.state,
|
|
1869
|
+
operations: [
|
|
1870
|
+
...ensured.operations,
|
|
1871
|
+
{
|
|
1872
|
+
type: "update-token",
|
|
1873
|
+
messageId: ensured.cursor.messageId,
|
|
1874
|
+
phaseId: action.phaseId,
|
|
1875
|
+
partial: action.partial,
|
|
1876
|
+
isReasoning: action.isReasoning,
|
|
1877
|
+
sender: action.sender ?? ensured.cursor.sender,
|
|
1878
|
+
at: action.at
|
|
1879
|
+
}
|
|
1880
|
+
]
|
|
1881
|
+
};
|
|
1882
|
+
}
|
|
1883
|
+
if (action.type === "attempt-result") {
|
|
1884
|
+
const cursor = state.attemptsById.get(action.attemptId);
|
|
1885
|
+
if (!cursor) return { state, operations: [] };
|
|
1886
|
+
const next = copyState(state);
|
|
1887
|
+
if (next.activeAttemptId === action.attemptId) {
|
|
1888
|
+
next.activeAttemptId = null;
|
|
1728
1889
|
}
|
|
1890
|
+
next.lastAttemptId = action.attemptId;
|
|
1891
|
+
return {
|
|
1892
|
+
state: next,
|
|
1893
|
+
operations: [{
|
|
1894
|
+
type: "finalize-attempt",
|
|
1895
|
+
messageId: cursor.messageId,
|
|
1896
|
+
answer: action.answer,
|
|
1897
|
+
at: action.at
|
|
1898
|
+
}]
|
|
1899
|
+
};
|
|
1729
1900
|
}
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1901
|
+
if (action.type === "tool-call") {
|
|
1902
|
+
if (state.toolMessageByCallId.has(action.toolCall.id)) {
|
|
1903
|
+
return { state, operations: [] };
|
|
1904
|
+
}
|
|
1905
|
+
const attemptId = action.attemptId ?? state.activeAttemptId ?? state.lastAttemptId ?? `tool-attempt:${action.toolCall.id}`;
|
|
1906
|
+
const ensured = ensureAttempt(
|
|
1907
|
+
state,
|
|
1908
|
+
attemptId,
|
|
1909
|
+
action.sender,
|
|
1910
|
+
action.at,
|
|
1911
|
+
options
|
|
1912
|
+
);
|
|
1913
|
+
const next = copyState(ensured.state);
|
|
1914
|
+
next.toolMessageByCallId.set(action.toolCall.id, ensured.cursor.messageId);
|
|
1915
|
+
const pendingResult = next.pendingToolResultsByCallId.get(action.toolCall.id);
|
|
1916
|
+
if (pendingResult) {
|
|
1917
|
+
next.pendingToolResultsByCallId.delete(action.toolCall.id);
|
|
1918
|
+
}
|
|
1919
|
+
return {
|
|
1920
|
+
state: next,
|
|
1921
|
+
operations: [
|
|
1922
|
+
...ensured.operations,
|
|
1923
|
+
{
|
|
1924
|
+
type: "append-tool",
|
|
1925
|
+
messageId: ensured.cursor.messageId,
|
|
1926
|
+
toolCall: action.toolCall,
|
|
1927
|
+
sender: action.sender ?? ensured.cursor.sender,
|
|
1928
|
+
at: action.at
|
|
1929
|
+
},
|
|
1930
|
+
...pendingResult ? [{
|
|
1931
|
+
type: "resolve-tool",
|
|
1932
|
+
messageId: ensured.cursor.messageId,
|
|
1933
|
+
update: pendingResult
|
|
1934
|
+
}] : []
|
|
1935
|
+
]
|
|
1936
|
+
};
|
|
1735
1937
|
}
|
|
1736
|
-
|
|
1737
|
-
|
|
1938
|
+
const messageId = action.update.id ? state.toolMessageByCallId.get(action.update.id) : void 0;
|
|
1939
|
+
if (!messageId || !action.update.id) {
|
|
1940
|
+
if (!action.update.id) return { state, operations: [] };
|
|
1941
|
+
const next = copyState(state);
|
|
1942
|
+
next.pendingToolResultsByCallId.set(action.update.id, action.update);
|
|
1943
|
+
return { state: next, operations: [] };
|
|
1738
1944
|
}
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
if (
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1945
|
+
return {
|
|
1946
|
+
state,
|
|
1947
|
+
operations: [{ type: "resolve-tool", messageId, update: action.update }]
|
|
1948
|
+
};
|
|
1949
|
+
};
|
|
1950
|
+
var applyOperation = (messages, operation) => {
|
|
1951
|
+
const index = messages.findIndex((message) => message.id === operation.messageId);
|
|
1952
|
+
if (operation.type === "ensure-attempt") {
|
|
1953
|
+
if (index >= 0) {
|
|
1954
|
+
const current2 = messages[index];
|
|
1955
|
+
const nextMessage = {
|
|
1956
|
+
...current2,
|
|
1957
|
+
metadata: {
|
|
1958
|
+
...current2.metadata ?? {},
|
|
1959
|
+
[LLM_ATTEMPT_ID_METADATA_KEY]: operation.attemptId
|
|
1960
|
+
},
|
|
1961
|
+
...operation.sender ? { sender: operation.sender } : {}
|
|
1962
|
+
};
|
|
1963
|
+
if (current2.metadata?.[LLM_ATTEMPT_ID_METADATA_KEY] === operation.attemptId && (!operation.sender || current2.sender === operation.sender)) return messages;
|
|
1964
|
+
const next2 = [...messages];
|
|
1965
|
+
next2[index] = nextMessage;
|
|
1966
|
+
return next2;
|
|
1751
1967
|
}
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1968
|
+
return [
|
|
1969
|
+
...messages,
|
|
1970
|
+
{
|
|
1971
|
+
id: operation.messageId,
|
|
1972
|
+
role: "assistant",
|
|
1973
|
+
content: "",
|
|
1974
|
+
timestamp: operation.at,
|
|
1975
|
+
isStreaming: true,
|
|
1976
|
+
isComplete: false,
|
|
1977
|
+
metadata: { [LLM_ATTEMPT_ID_METADATA_KEY]: operation.attemptId },
|
|
1978
|
+
activity: {
|
|
1979
|
+
items: [{
|
|
1980
|
+
id: `${operation.messageId}:pending`,
|
|
1981
|
+
kind: "answering",
|
|
1982
|
+
status: "active",
|
|
1983
|
+
startedAt: operation.at
|
|
1984
|
+
}]
|
|
1985
|
+
},
|
|
1986
|
+
...operation.sender ? { sender: operation.sender } : {}
|
|
1987
|
+
}
|
|
1988
|
+
];
|
|
1989
|
+
}
|
|
1990
|
+
if (index < 0) return messages;
|
|
1991
|
+
const current = messages[index];
|
|
1992
|
+
let updated;
|
|
1993
|
+
if (operation.type === "update-token") {
|
|
1994
|
+
updated = {
|
|
1995
|
+
...updateAssistantMessageToken(current, {
|
|
1996
|
+
partial: operation.partial,
|
|
1997
|
+
isReasoning: operation.isReasoning,
|
|
1998
|
+
activityId: operation.phaseId,
|
|
1999
|
+
at: operation.at
|
|
2000
|
+
}),
|
|
2001
|
+
...operation.sender ? { sender: operation.sender } : {}
|
|
2002
|
+
};
|
|
2003
|
+
} else if (operation.type === "finalize-attempt") {
|
|
2004
|
+
updated = finalizeAssistantMessage(current, operation.answer, operation.at);
|
|
2005
|
+
} else if (operation.type === "append-tool") {
|
|
2006
|
+
updated = {
|
|
2007
|
+
...appendAssistantToolCall(current, {
|
|
2008
|
+
...operation.toolCall,
|
|
2009
|
+
startTime: operation.toolCall.startTime ?? operation.at
|
|
2010
|
+
}),
|
|
2011
|
+
...operation.sender ? { sender: operation.sender } : {}
|
|
2012
|
+
};
|
|
2013
|
+
} else {
|
|
2014
|
+
const toolItem = current.activity?.items.find((item) => item.kind === "tool" && item.id === operation.update.id);
|
|
2015
|
+
if (!toolItem) return messages;
|
|
2016
|
+
updated = applyAssistantToolResult(current, {
|
|
2017
|
+
...operation.update.id ? { id: operation.update.id } : {},
|
|
2018
|
+
...operation.update.toolExecutionId ? { toolExecutionId: operation.update.toolExecutionId } : {},
|
|
2019
|
+
name: operation.update.name ?? toolItem.toolName ?? toolItem.id,
|
|
2020
|
+
status: operation.update.status,
|
|
2021
|
+
...operation.update.result !== void 0 ? { result: operation.update.result } : {},
|
|
2022
|
+
...operation.update.error !== void 0 ? { error: operation.update.error } : {},
|
|
2023
|
+
endTime: operation.update.endTime
|
|
1762
2024
|
});
|
|
2025
|
+
const hasActiveTool = updated.activity?.items.some((item) => item.kind === "tool" && item.status === "active") ?? false;
|
|
2026
|
+
updated = {
|
|
2027
|
+
...updated,
|
|
2028
|
+
isStreaming: hasActiveTool,
|
|
2029
|
+
isComplete: !hasActiveTool
|
|
2030
|
+
};
|
|
1763
2031
|
}
|
|
1764
|
-
|
|
2032
|
+
if (updated === current) return messages;
|
|
2033
|
+
const next = [...messages];
|
|
2034
|
+
next[index] = updated;
|
|
2035
|
+
return next;
|
|
2036
|
+
};
|
|
2037
|
+
var applyLiveRunOperations = (messages, operations) => operations.reduce(applyOperation, messages);
|
|
2038
|
+
var getLatestLiveRunMessageId = (state) => {
|
|
2039
|
+
const attemptId = state.activeAttemptId ?? state.lastAttemptId;
|
|
2040
|
+
return attemptId ? state.attemptsById.get(attemptId)?.messageId ?? state.initialMessageId : state.initialMessageId;
|
|
1765
2041
|
};
|
|
1766
2042
|
|
|
1767
2043
|
// src/useCopilotzChat.ts
|
|
@@ -1770,6 +2046,10 @@ var generateId = () => globalThis.crypto?.randomUUID?.() ?? `id-${Date.now()}-${
|
|
|
1770
2046
|
var isAbortError = (error) => error instanceof DOMException && error.name === "AbortError" || typeof error === "object" && error !== null && "name" in error && error.name === "AbortError";
|
|
1771
2047
|
var getEventPayload = (event) => getStreamEventPayload(event);
|
|
1772
2048
|
var getEventSenderType = (payload) => payload?.senderType || payload?.sender?.type;
|
|
2049
|
+
var getEventTimestamp = (event) => {
|
|
2050
|
+
const timestamp = typeof event?.createdAt === "string" ? new Date(event.createdAt).getTime() : NaN;
|
|
2051
|
+
return Number.isFinite(timestamp) ? timestamp : nowTs();
|
|
2052
|
+
};
|
|
1773
2053
|
var isRecord3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1774
2054
|
var normalizeThreadTag = (value) => {
|
|
1775
2055
|
if (!isRecord3(value)) return null;
|
|
@@ -1798,11 +2078,11 @@ var createEmptyMessagePageInfo = () => ({
|
|
|
1798
2078
|
oldestMessageId: null,
|
|
1799
2079
|
newestMessageId: null
|
|
1800
2080
|
});
|
|
1801
|
-
var createPendingAssistantActivity = () => ({
|
|
2081
|
+
var createPendingAssistantActivity = (messageId) => ({
|
|
1802
2082
|
items: [
|
|
1803
2083
|
{
|
|
1804
|
-
id:
|
|
1805
|
-
kind: "
|
|
2084
|
+
id: `${messageId}:pending`,
|
|
2085
|
+
kind: "answering",
|
|
1806
2086
|
status: "active",
|
|
1807
2087
|
startedAt: nowTs()
|
|
1808
2088
|
}
|
|
@@ -1845,7 +2125,6 @@ function useCopilotz({ userId, userName, userAvatar, assistantName, agentOptions
|
|
|
1845
2125
|
assistantName
|
|
1846
2126
|
});
|
|
1847
2127
|
const persistedToolUpdatesRef = useRef2([]);
|
|
1848
|
-
const liveToolUpdatesRef = useRef2([]);
|
|
1849
2128
|
const stopRequestedRef = useRef2(false);
|
|
1850
2129
|
const recoveryPollGenerationRef = useRef2(0);
|
|
1851
2130
|
threadsRef.current = threads;
|
|
@@ -2041,7 +2320,6 @@ function useCopilotz({ userId, userName, userAvatar, assistantName, agentOptions
|
|
|
2041
2320
|
setIsLoadingOlderMessages(false);
|
|
2042
2321
|
setMessagePageInfo(createEmptyMessagePageInfo());
|
|
2043
2322
|
persistedToolUpdatesRef.current = [];
|
|
2044
|
-
liveToolUpdatesRef.current = [];
|
|
2045
2323
|
try {
|
|
2046
2324
|
const page = await fetchThreadMessagesPage(threadId, { limit: THREAD_MESSAGES_PAGE_SIZE }, getRequestHeaders);
|
|
2047
2325
|
const { viewMessages, toolResultUpdates } = await prepareThreadMessages(page.data);
|
|
@@ -2401,161 +2679,26 @@ function useCopilotz({ userId, userName, userAvatar, assistantName, agentOptions
|
|
|
2401
2679
|
}, []);
|
|
2402
2680
|
const sendCopilotzMessage = useCallback2(
|
|
2403
2681
|
async (params) => {
|
|
2404
|
-
|
|
2405
|
-
let
|
|
2682
|
+
const initialAssistantMessageId = params.assistantMessageId ?? generateId();
|
|
2683
|
+
let liveRunState = createLiveRunState(initialAssistantMessageId);
|
|
2406
2684
|
const streamOwnerThreadId = currentThreadIdRef.current;
|
|
2407
2685
|
const streamOwnerThreadExternalId = params.threadExternalId ?? currentThreadExternalIdRef.current;
|
|
2408
2686
|
const streamStillOwnsVisibleThread = () => isSameThreadIdentity(
|
|
2409
2687
|
{ id: streamOwnerThreadId, externalId: streamOwnerThreadExternalId },
|
|
2410
2688
|
{ id: currentThreadIdRef.current, externalId: currentThreadExternalIdRef.current }
|
|
2411
2689
|
);
|
|
2412
|
-
params.onBeforeStart?.(
|
|
2690
|
+
params.onBeforeStart?.(initialAssistantMessageId);
|
|
2413
2691
|
let hasStreamProgress = false;
|
|
2414
|
-
const
|
|
2415
|
-
if (!streamStillOwnsVisibleThread()) return;
|
|
2416
|
-
if (partial && partial.length > 0) {
|
|
2417
|
-
hasStreamProgress = true;
|
|
2418
|
-
setIsRecoveringStream(false);
|
|
2419
|
-
}
|
|
2420
|
-
const isReasoning = opts?.isReasoning ?? false;
|
|
2421
|
-
const nextSender = opts?.agent ? resolveAgentSender(opts.agent, senderOptionsRef.current) : currentAssistantSender;
|
|
2422
|
-
if (nextSender) {
|
|
2423
|
-
currentAssistantSender = nextSender;
|
|
2424
|
-
}
|
|
2425
|
-
const nextAgentKey = currentAssistantSender?.agentId ?? currentAssistantSender?.id ?? null;
|
|
2426
|
-
const applyUpdate = (msg) => {
|
|
2427
|
-
return {
|
|
2428
|
-
...updateAssistantMessageToken(msg, {
|
|
2429
|
-
partial,
|
|
2430
|
-
isReasoning
|
|
2431
|
-
}),
|
|
2432
|
-
...currentAssistantSender ? { sender: currentAssistantSender } : {}
|
|
2433
|
-
};
|
|
2434
|
-
};
|
|
2435
|
-
setMessages((prev) => {
|
|
2436
|
-
const idx = prev.findIndex((m) => m.id === currentAssistantId);
|
|
2437
|
-
if (idx >= 0 && canAttachToCurrentStreamingAssistant(prev[idx])) {
|
|
2438
|
-
const msg = prev[idx];
|
|
2439
|
-
const next = applyUpdate(msg);
|
|
2440
|
-
if (msg.content === next.content && msg.activity === next.activity && msg.isStreaming === next.isStreaming && msg.isComplete === next.isComplete) {
|
|
2441
|
-
return prev;
|
|
2442
|
-
}
|
|
2443
|
-
const updated = [...prev];
|
|
2444
|
-
updated[idx] = next;
|
|
2445
|
-
return updated;
|
|
2446
|
-
}
|
|
2447
|
-
const last = prev[prev.length - 1];
|
|
2448
|
-
if (canAttachToStreamingAssistant(last, nextAgentKey)) {
|
|
2449
|
-
currentAssistantId = last.id;
|
|
2450
|
-
const next = applyUpdate(last);
|
|
2451
|
-
if (last.content === next.content && last.activity === next.activity && last.isStreaming === next.isStreaming && last.isComplete === next.isComplete) {
|
|
2452
|
-
return prev;
|
|
2453
|
-
}
|
|
2454
|
-
const updated = [...prev];
|
|
2455
|
-
updated[prev.length - 1] = next;
|
|
2456
|
-
return updated;
|
|
2457
|
-
}
|
|
2458
|
-
const lastStreamingBelongsToDifferentAgent = Boolean(nextAgentKey) && last?.role === "assistant" && last.isStreaming && Boolean(messageAgentKey(last)) && messageAgentKey(last) !== nextAgentKey;
|
|
2459
|
-
if (!prev.length || prev[prev.length - 1].role !== "assistant" || !prev[prev.length - 1].isStreaming || lastStreamingBelongsToDifferentAgent) {
|
|
2460
|
-
const newId = generateId();
|
|
2461
|
-
currentAssistantId = newId;
|
|
2462
|
-
const base = {
|
|
2463
|
-
id: newId,
|
|
2464
|
-
role: "assistant",
|
|
2465
|
-
content: "",
|
|
2466
|
-
timestamp: nowTs(),
|
|
2467
|
-
isStreaming: true,
|
|
2468
|
-
isComplete: false,
|
|
2469
|
-
...currentAssistantSender ? { sender: currentAssistantSender } : {}
|
|
2470
|
-
};
|
|
2471
|
-
return [...prev, applyUpdate(base)];
|
|
2472
|
-
}
|
|
2473
|
-
return prev;
|
|
2474
|
-
});
|
|
2475
|
-
};
|
|
2476
|
-
const finalizeCurrentAssistantBubble = () => {
|
|
2692
|
+
const dispatchLiveRunAction = (action) => {
|
|
2477
2693
|
if (!streamStillOwnsVisibleThread()) return;
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
if (idx < 0) return prev;
|
|
2481
|
-
const msg = prev[idx];
|
|
2482
|
-
if (!msg.isStreaming && msg.isComplete) return prev;
|
|
2483
|
-
const updated = [...prev];
|
|
2484
|
-
updated[idx] = closeAssistantMessage(msg);
|
|
2485
|
-
return updated;
|
|
2694
|
+
const transition = transitionLiveRun(liveRunState, action, {
|
|
2695
|
+
createId: generateId
|
|
2486
2696
|
});
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
if (!streamStillOwnsVisibleThread()) return;
|
|
2491
|
-
let matched = false;
|
|
2492
|
-
setMessages((prev) => {
|
|
2493
|
-
const next = applyToolResultUpdateToMessages(prev, update, {
|
|
2494
|
-
isStreaming: true,
|
|
2495
|
-
isComplete: false
|
|
2496
|
-
});
|
|
2497
|
-
matched = next.matched;
|
|
2498
|
-
return next.matched ? next.messages : prev;
|
|
2499
|
-
});
|
|
2500
|
-
if (!matched) {
|
|
2501
|
-
liveToolUpdatesRef.current.push(update);
|
|
2697
|
+
liveRunState = transition.state;
|
|
2698
|
+
if (transition.operations.length > 0) {
|
|
2699
|
+
setMessages((prev) => applyLiveRunOperations(prev, transition.operations));
|
|
2502
2700
|
}
|
|
2503
2701
|
};
|
|
2504
|
-
const finalizeActiveAssistantTurn = (finalAnswer, llmAttemptId) => {
|
|
2505
|
-
if (!streamStillOwnsVisibleThread()) return;
|
|
2506
|
-
setMessages((prev) => {
|
|
2507
|
-
const currentIdx = prev.findIndex((message2) => message2.id === currentAssistantId && message2.role === "assistant");
|
|
2508
|
-
const fallbackIdx = currentIdx >= 0 ? currentIdx : (() => {
|
|
2509
|
-
for (let i = prev.length - 1; i >= 0; i--) {
|
|
2510
|
-
if (prev[i].role === "assistant" && prev[i].isStreaming) {
|
|
2511
|
-
return i;
|
|
2512
|
-
}
|
|
2513
|
-
}
|
|
2514
|
-
return -1;
|
|
2515
|
-
})();
|
|
2516
|
-
if (fallbackIdx < 0) return prev;
|
|
2517
|
-
const message = prev[fallbackIdx];
|
|
2518
|
-
const correlatedMessage = llmAttemptId ? {
|
|
2519
|
-
...message,
|
|
2520
|
-
metadata: {
|
|
2521
|
-
...message.metadata ?? {},
|
|
2522
|
-
[LLM_ATTEMPT_ID_METADATA_KEY]: llmAttemptId
|
|
2523
|
-
}
|
|
2524
|
-
} : message;
|
|
2525
|
-
const nextMessage = finalizeAssistantMessage(correlatedMessage, finalAnswer);
|
|
2526
|
-
if (message.content === nextMessage.content && message.isStreaming === nextMessage.isStreaming && message.isComplete === nextMessage.isComplete && message.activity === nextMessage.activity && message.metadata === nextMessage.metadata) {
|
|
2527
|
-
return prev;
|
|
2528
|
-
}
|
|
2529
|
-
const updated = [...prev];
|
|
2530
|
-
updated[fallbackIdx] = nextMessage;
|
|
2531
|
-
currentAssistantId = nextMessage.id;
|
|
2532
|
-
return updated;
|
|
2533
|
-
});
|
|
2534
|
-
};
|
|
2535
|
-
const toServerMessageFromEvent = async (event) => {
|
|
2536
|
-
if (!event) return null;
|
|
2537
|
-
const type = event?.type || "";
|
|
2538
|
-
const payload = event?.payload ?? event;
|
|
2539
|
-
if (type === "TOOL_CALL") {
|
|
2540
|
-
const parsedToolCall = extractLiveToolCall(payload);
|
|
2541
|
-
return {
|
|
2542
|
-
id: generateId(),
|
|
2543
|
-
threadId: curThreadId ?? "",
|
|
2544
|
-
senderType: "tool",
|
|
2545
|
-
content: "",
|
|
2546
|
-
toolCalls: [
|
|
2547
|
-
{
|
|
2548
|
-
id: parsedToolCall.id ?? generateId(),
|
|
2549
|
-
name: parsedToolCall.name,
|
|
2550
|
-
args: parsedToolCall.arguments,
|
|
2551
|
-
...parsedToolCall.result !== void 0 ? { output: parsedToolCall.result } : {},
|
|
2552
|
-
status: parsedToolCall.status
|
|
2553
|
-
}
|
|
2554
|
-
]
|
|
2555
|
-
};
|
|
2556
|
-
}
|
|
2557
|
-
return null;
|
|
2558
|
-
};
|
|
2559
2702
|
const abortController = new AbortController();
|
|
2560
2703
|
if (!params.preserveActiveRun) {
|
|
2561
2704
|
abortControllerRef.current?.abort();
|
|
@@ -2566,7 +2709,6 @@ function useCopilotz({ userId, userName, userAvatar, assistantName, agentOptions
|
|
|
2566
2709
|
stopRequestedRef.current = false;
|
|
2567
2710
|
setThreadActivityStatus("running");
|
|
2568
2711
|
setIsStreaming(true);
|
|
2569
|
-
liveToolUpdatesRef.current = [];
|
|
2570
2712
|
let streamError = null;
|
|
2571
2713
|
const resolveActivityThreadId = async () => {
|
|
2572
2714
|
if (params.threadId) return params.threadId;
|
|
@@ -2613,10 +2755,26 @@ function useCopilotz({ userId, userName, userAvatar, assistantName, agentOptions
|
|
|
2613
2755
|
participants: participantsRef.current,
|
|
2614
2756
|
targetAgent: targetAgentNameRef.current,
|
|
2615
2757
|
getRequestHeaders,
|
|
2616
|
-
onToken: (token, _isComplete, raw, opts) =>
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2758
|
+
onToken: (token, _isComplete, raw, opts) => {
|
|
2759
|
+
const rawAgent = raw?.payload?.agent ?? raw?.agent ?? null;
|
|
2760
|
+
const sender = rawAgent ? resolveAgentSender(rawAgent, senderOptionsRef.current) : params.assistantSender;
|
|
2761
|
+
const attemptId = opts?.llmAttemptId ?? liveRunState.activeAttemptId ?? liveRunState.lastAttemptId ?? `stream-attempt:${initialAssistantMessageId}`;
|
|
2762
|
+
const isReasoning = opts?.isReasoning ?? false;
|
|
2763
|
+
const phaseId = opts?.phaseId ?? `${attemptId}:${isReasoning ? "reasoning" : "answer"}:0`;
|
|
2764
|
+
if (token.length > 0) {
|
|
2765
|
+
hasStreamProgress = true;
|
|
2766
|
+
setIsRecoveringStream(false);
|
|
2767
|
+
}
|
|
2768
|
+
dispatchLiveRunAction({
|
|
2769
|
+
type: "token",
|
|
2770
|
+
attemptId,
|
|
2771
|
+
phaseId,
|
|
2772
|
+
partial: token,
|
|
2773
|
+
isReasoning,
|
|
2774
|
+
sender,
|
|
2775
|
+
at: getEventTimestamp(raw)
|
|
2776
|
+
});
|
|
2777
|
+
},
|
|
2620
2778
|
onMessageEvent: async (event) => {
|
|
2621
2779
|
if (!streamStillOwnsVisibleThread()) return;
|
|
2622
2780
|
const intercepted = applyEventInterceptor(event);
|
|
@@ -2625,14 +2783,37 @@ function useCopilotz({ userId, userName, userAvatar, assistantName, agentOptions
|
|
|
2625
2783
|
}
|
|
2626
2784
|
const type = event?.type || "";
|
|
2627
2785
|
const payload = getEventPayload(event);
|
|
2786
|
+
if (type === "LLM_CALL") {
|
|
2787
|
+
const attemptId = getLlmAttemptId(event);
|
|
2788
|
+
if (attemptId) {
|
|
2789
|
+
dispatchLiveRunAction({
|
|
2790
|
+
type: "attempt-start",
|
|
2791
|
+
attemptId,
|
|
2792
|
+
sender: resolveLiveEventSender(event, senderOptionsRef.current),
|
|
2793
|
+
at: getEventTimestamp(event)
|
|
2794
|
+
});
|
|
2795
|
+
}
|
|
2796
|
+
return;
|
|
2797
|
+
}
|
|
2628
2798
|
if (type === "TOOL_RESULT") {
|
|
2629
2799
|
processToolOutput(payload ?? {});
|
|
2630
|
-
|
|
2800
|
+
dispatchLiveRunAction({
|
|
2801
|
+
type: "tool-result",
|
|
2802
|
+
update: extractLiveToolResultUpdate(payload ?? {})
|
|
2803
|
+
});
|
|
2631
2804
|
return;
|
|
2632
2805
|
}
|
|
2633
2806
|
if (type === "LLM_RESULT") {
|
|
2634
2807
|
const finalAnswer = typeof payload?.answer === "string" ? payload.answer : void 0;
|
|
2635
|
-
|
|
2808
|
+
const attemptId = getLlmAttemptId(event) ?? liveRunState.activeAttemptId ?? liveRunState.lastAttemptId;
|
|
2809
|
+
if (attemptId) {
|
|
2810
|
+
dispatchLiveRunAction({
|
|
2811
|
+
type: "attempt-result",
|
|
2812
|
+
attemptId,
|
|
2813
|
+
answer: finalAnswer,
|
|
2814
|
+
at: getEventTimestamp(event)
|
|
2815
|
+
});
|
|
2816
|
+
}
|
|
2636
2817
|
if (isTerminalEmptyLlmResultEvent(event)) {
|
|
2637
2818
|
finalizeStreamingPlaceholders();
|
|
2638
2819
|
}
|
|
@@ -2644,87 +2825,22 @@ function useCopilotz({ userId, userName, userAvatar, assistantName, agentOptions
|
|
|
2644
2825
|
if (type === "TOOL_CALL") {
|
|
2645
2826
|
const parsedToolCall = extractLiveToolCall(payload ?? {});
|
|
2646
2827
|
const eventSender = resolveLiveEventSender(event, senderOptionsRef.current);
|
|
2647
|
-
currentAssistantSender = eventSender;
|
|
2648
|
-
const eventAgentKey = currentAssistantSender.agentId ?? currentAssistantSender.id;
|
|
2649
2828
|
const callId = parsedToolCall.id ?? generateId();
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
if (!message) return false;
|
|
2664
|
-
return message.role === "assistant" && message.isStreaming && message.content.trim().length === 0 && !message.attachments?.length;
|
|
2665
|
-
};
|
|
2666
|
-
const appendToolCall = (msg) => ({
|
|
2667
|
-
...appendAssistantToolCall(msg, {
|
|
2668
|
-
id: callId,
|
|
2669
|
-
name: toolName,
|
|
2670
|
-
arguments: parsedToolCall.arguments,
|
|
2671
|
-
...initialResult !== void 0 ? { result: initialResult } : {},
|
|
2672
|
-
status: initialStatus,
|
|
2673
|
-
startTime: Date.now(),
|
|
2674
|
-
...endTime !== void 0 ? { endTime } : {}
|
|
2675
|
-
})
|
|
2676
|
-
});
|
|
2677
|
-
const currentIdx = prev.findIndex((message) => message.id === currentAssistantId && message.role === "assistant" && message.isStreaming && canHostActivity(message));
|
|
2678
|
-
if (currentIdx >= 0) {
|
|
2679
|
-
const next = [...prev];
|
|
2680
|
-
next[currentIdx] = appendToolCall({
|
|
2681
|
-
...next[currentIdx],
|
|
2682
|
-
isStreaming: true,
|
|
2683
|
-
isComplete: false,
|
|
2684
|
-
...currentAssistantSender ? { sender: currentAssistantSender } : {}
|
|
2685
|
-
});
|
|
2686
|
-
return next;
|
|
2687
|
-
}
|
|
2688
|
-
const last = prev[prev.length - 1];
|
|
2689
|
-
if (canHostActivity(last) && canAttachToStreamingAssistant(last, eventAgentKey)) {
|
|
2690
|
-
currentAssistantId = last.id;
|
|
2691
|
-
const next = [...prev];
|
|
2692
|
-
next[prev.length - 1] = appendToolCall({
|
|
2693
|
-
...last,
|
|
2694
|
-
isStreaming: true,
|
|
2695
|
-
isComplete: false,
|
|
2696
|
-
...currentAssistantSender ? { sender: currentAssistantSender } : {}
|
|
2697
|
-
});
|
|
2698
|
-
return next;
|
|
2699
|
-
}
|
|
2700
|
-
const newId = generateId();
|
|
2701
|
-
currentAssistantId = newId;
|
|
2702
|
-
return [
|
|
2703
|
-
...prev,
|
|
2704
|
-
appendToolCall({
|
|
2705
|
-
id: newId,
|
|
2706
|
-
role: "assistant",
|
|
2707
|
-
content: "",
|
|
2708
|
-
timestamp: nowTs(),
|
|
2709
|
-
isStreaming: true,
|
|
2710
|
-
isComplete: false,
|
|
2711
|
-
...currentAssistantSender ? { sender: currentAssistantSender } : {}
|
|
2712
|
-
})
|
|
2713
|
-
];
|
|
2714
|
-
})()
|
|
2715
|
-
);
|
|
2716
|
-
hasStreamProgress = true;
|
|
2717
|
-
return;
|
|
2718
|
-
}
|
|
2719
|
-
const sm = await toServerMessageFromEvent(event);
|
|
2720
|
-
if (sm) {
|
|
2721
|
-
const viewMsg = convertServerMessage(sm, {
|
|
2722
|
-
senderOptions: senderOptionsRef.current,
|
|
2723
|
-
createId: generateId,
|
|
2724
|
-
now: nowTs
|
|
2829
|
+
dispatchLiveRunAction({
|
|
2830
|
+
type: "tool-call",
|
|
2831
|
+
attemptId: getLlmAttemptId(event) ?? liveRunState.activeAttemptId ?? liveRunState.lastAttemptId,
|
|
2832
|
+
sender: eventSender,
|
|
2833
|
+
at: getEventTimestamp(event),
|
|
2834
|
+
toolCall: {
|
|
2835
|
+
id: callId,
|
|
2836
|
+
name: parsedToolCall.name,
|
|
2837
|
+
arguments: parsedToolCall.arguments,
|
|
2838
|
+
status: parsedToolCall.status,
|
|
2839
|
+
...parsedToolCall.toolExecutionId ? { toolExecutionId: parsedToolCall.toolExecutionId } : {},
|
|
2840
|
+
...parsedToolCall.result !== void 0 ? { result: parsedToolCall.result } : {}
|
|
2841
|
+
}
|
|
2725
2842
|
});
|
|
2726
|
-
|
|
2727
|
-
setMessages((prev) => [...prev, viewMsg]);
|
|
2843
|
+
hasStreamProgress = true;
|
|
2728
2844
|
return;
|
|
2729
2845
|
}
|
|
2730
2846
|
handleStreamMessageEvent(event);
|
|
@@ -2738,10 +2854,8 @@ function useCopilotz({ userId, userName, userAvatar, assistantName, agentOptions
|
|
|
2738
2854
|
if (intercepted?.handled) {
|
|
2739
2855
|
return;
|
|
2740
2856
|
}
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
handleStreamAssetEvent(payload, currentAssistantId);
|
|
2744
|
-
})();
|
|
2857
|
+
if (!hasStreamProgress) return;
|
|
2858
|
+
handleStreamAssetEvent(payload, getLatestLiveRunMessageId(liveRunState));
|
|
2745
2859
|
},
|
|
2746
2860
|
signal: abortController.signal
|
|
2747
2861
|
});
|
|
@@ -2774,16 +2888,16 @@ function useCopilotz({ userId, userName, userAvatar, assistantName, agentOptions
|
|
|
2774
2888
|
abortControllerRef.current = null;
|
|
2775
2889
|
}
|
|
2776
2890
|
if (!streamStillOwnsVisibleThread()) {
|
|
2777
|
-
return
|
|
2891
|
+
return getLatestLiveRunMessageId(liveRunState);
|
|
2778
2892
|
}
|
|
2779
2893
|
if (recoveryStarted) {
|
|
2780
|
-
return
|
|
2894
|
+
return getLatestLiveRunMessageId(liveRunState);
|
|
2781
2895
|
}
|
|
2782
2896
|
finalizeStreamingPlaceholders();
|
|
2783
2897
|
if (streamError) {
|
|
2784
2898
|
throw streamError;
|
|
2785
2899
|
}
|
|
2786
|
-
return
|
|
2900
|
+
return getLatestLiveRunMessageId(liveRunState);
|
|
2787
2901
|
},
|
|
2788
2902
|
[applyEventInterceptor, handleStreamMessageEvent, handleStreamAssetEvent, fetchAndSetThreadsState, finalizeStreamingPlaceholders, getRequestHeaders, refreshThreadMessages, startThreadActivityRecovery]
|
|
2789
2903
|
);
|
|
@@ -2840,15 +2954,16 @@ function useCopilotz({ userId, userName, userAvatar, assistantName, agentOptions
|
|
|
2840
2954
|
},
|
|
2841
2955
|
senderOptionsRef.current
|
|
2842
2956
|
) : preferredAgentRef.current ? resolveAgentSender({ id: preferredAgentRef.current, name: preferredAgentRef.current }, senderOptionsRef.current) : resolveAssistantFallbackSender(senderOptionsRef.current);
|
|
2957
|
+
const assistantMessageId = generateId();
|
|
2843
2958
|
const assistantPlaceholder = {
|
|
2844
|
-
id:
|
|
2959
|
+
id: assistantMessageId,
|
|
2845
2960
|
role: "assistant",
|
|
2846
2961
|
content: "",
|
|
2847
2962
|
timestamp: timestamp + 1,
|
|
2848
2963
|
isStreaming: true,
|
|
2849
2964
|
isComplete: false,
|
|
2850
2965
|
sender: assistantSender,
|
|
2851
|
-
activity: createPendingAssistantActivity()
|
|
2966
|
+
activity: createPendingAssistantActivity(assistantMessageId)
|
|
2852
2967
|
};
|
|
2853
2968
|
setMessages((prev) => [...prev, userMessage, assistantPlaceholder]);
|
|
2854
2969
|
setSpecialState(null);
|
|
@@ -2956,7 +3071,7 @@ function useCopilotz({ userId, userName, userAvatar, assistantName, agentOptions
|
|
|
2956
3071
|
isStreaming: true,
|
|
2957
3072
|
isComplete: false,
|
|
2958
3073
|
sender: assistantSender,
|
|
2959
|
-
activity: createPendingAssistantActivity()
|
|
3074
|
+
activity: createPendingAssistantActivity(assistantMessageId)
|
|
2960
3075
|
}
|
|
2961
3076
|
]);
|
|
2962
3077
|
setMessagePageInfo(createEmptyMessagePageInfo());
|