@assistant-ui/react-ag-ui 0.0.59 → 0.0.61
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/agUiExtras.d.ts +2 -5
- package/dist/agUiExtras.d.ts.map +1 -1
- package/dist/hooks.d.ts +9 -12
- package/dist/hooks.d.ts.map +1 -1
- package/dist/index.d.ts +7 -5
- package/dist/index.d.ts.map +1 -0
- package/dist/runtime/AgUiThreadRuntimeCore.d.ts +153 -146
- package/dist/runtime/AgUiThreadRuntimeCore.d.ts.map +1 -1
- package/dist/runtime/AgUiThreadRuntimeCore.js +146 -26
- package/dist/runtime/AgUiThreadRuntimeCore.js.map +1 -1
- package/dist/runtime/adapter/conversions.d.ts +54 -56
- package/dist/runtime/adapter/conversions.d.ts.map +1 -1
- package/dist/runtime/adapter/conversions.js +164 -23
- package/dist/runtime/adapter/conversions.js.map +1 -1
- package/dist/runtime/adapter/run-aggregator.d.ts +98 -98
- package/dist/runtime/adapter/run-aggregator.d.ts.map +1 -1
- package/dist/runtime/adapter/run-aggregator.js +35 -2
- package/dist/runtime/adapter/run-aggregator.js.map +1 -1
- package/dist/runtime/adapter/subscriber.d.ts +109 -111
- package/dist/runtime/adapter/subscriber.d.ts.map +1 -1
- package/dist/runtime/adapter/tool-approval.d.ts +7 -9
- package/dist/runtime/adapter/tool-approval.d.ts.map +1 -1
- package/dist/runtime/event-parser.d.ts +5 -8
- package/dist/runtime/event-parser.d.ts.map +1 -1
- package/dist/runtime/interrupt-internals.d.ts +4 -7
- package/dist/runtime/interrupt-internals.d.ts.map +1 -1
- package/dist/runtime/logger.d.ts +4 -7
- package/dist/runtime/logger.d.ts.map +1 -1
- package/dist/runtime/mcp-tool-result.d.ts +7 -10
- package/dist/runtime/mcp-tool-result.d.ts.map +1 -1
- package/dist/runtime/types.d.ts +248 -250
- package/dist/runtime/types.d.ts.map +1 -1
- package/dist/useAgUiRuntime.d.ts +15 -18
- package/dist/useAgUiRuntime.d.ts.map +1 -1
- package/dist/useAgUiRuntime.js +9 -2
- package/dist/useAgUiRuntime.js.map +1 -1
- package/package.json +12 -11
- package/src/runtime/AgUiThreadRuntimeCore.ts +246 -30
- package/src/runtime/adapter/conversions.content.test.ts +56 -0
- package/src/runtime/adapter/conversions.ts +278 -36
- package/src/runtime/adapter/run-aggregator.ts +53 -2
- package/src/useAgUiRuntime.history.test.tsx +93 -2
- package/src/useAgUiRuntime.thread-switch.test.tsx +84 -4
- package/src/useAgUiRuntime.ts +12 -3
- package/src/useAgUiRuntime.voice.test.tsx +402 -0
|
@@ -5,7 +5,7 @@ import { AG_UI_METADATA_NAMESPACE, RunAggregator, isPlainObject, tryParseJSON }
|
|
|
5
5
|
import { fromAgUiMessages, toAgUiMessages, toAgUiTools } from "./adapter/conversions.js";
|
|
6
6
|
import { createAgUiSubscriber } from "./adapter/subscriber.js";
|
|
7
7
|
import { fromThreadMessageLike, generateId, isMcpAppUri } from "@assistant-ui/core";
|
|
8
|
-
import { createMessageRepositorySession, invokeUserCallback, iterateToolCallParts, mapToolCallPartsDeep } from "@assistant-ui/core/internal";
|
|
8
|
+
import { appendToolInteraction, createMessageRepositorySession, invokeUserCallback, iterateToolCallParts, mapToolCallPartsDeep } from "@assistant-ui/core/internal";
|
|
9
9
|
import jsonpatch from "fast-json-patch";
|
|
10
10
|
//#region src/runtime/AgUiThreadRuntimeCore.ts
|
|
11
11
|
const optimisticPrefix = "__optimistic__";
|
|
@@ -162,6 +162,13 @@ var AgUiThreadRuntimeCore = class {
|
|
|
162
162
|
if (!startRun || !ownsThread) return;
|
|
163
163
|
await this.startRun(threadMessageId, message.runConfig);
|
|
164
164
|
}
|
|
165
|
+
appendVoiceTranscript(message) {
|
|
166
|
+
const parentId = this.session.headId;
|
|
167
|
+
this.session.addOrUpdateMessage(parentId, message);
|
|
168
|
+
this.session.switchToBranch(message.id);
|
|
169
|
+
this.notifyUpdate();
|
|
170
|
+
this.recordHistoryEntry(parentId, message);
|
|
171
|
+
}
|
|
165
172
|
maybeAutoCancelPendingToolCalls() {
|
|
166
173
|
if (this.autoCancelPendingToolCalls === false) return;
|
|
167
174
|
const pending = this.getPendingToolCalls();
|
|
@@ -470,8 +477,9 @@ var AgUiThreadRuntimeCore = class {
|
|
|
470
477
|
return {
|
|
471
478
|
...part,
|
|
472
479
|
result: options.result,
|
|
473
|
-
|
|
474
|
-
|
|
480
|
+
isError: options.isError,
|
|
481
|
+
...options.artifact !== void 0 && { artifact: options.artifact },
|
|
482
|
+
...options.modelContent !== void 0 && { modelContent: options.modelContent }
|
|
475
483
|
};
|
|
476
484
|
});
|
|
477
485
|
if (!matchedToolCall) return message;
|
|
@@ -483,6 +491,38 @@ var AgUiThreadRuntimeCore = class {
|
|
|
483
491
|
this.notifyUpdate();
|
|
484
492
|
this.maybeResumeAfterToolResults(sessionMessageId);
|
|
485
493
|
}
|
|
494
|
+
async recordToolInteraction(options) {
|
|
495
|
+
const sessionMessageId = this.session.tryGetMessage(options.messageId) ? options.messageId : this.findMessageIdForToolCall(options.toolCallId);
|
|
496
|
+
if (sessionMessageId === void 0) throw new Error(`[agui] recordToolInteraction: message "${options.messageId}" was not found`);
|
|
497
|
+
const item = this.session.tryGetMessage(sessionMessageId);
|
|
498
|
+
if (!item) throw new Error(`[agui] recordToolInteraction: message "${sessionMessageId}" was not found`);
|
|
499
|
+
let recorded = false;
|
|
500
|
+
const updated = this.session.updateMessage(sessionMessageId, (message) => {
|
|
501
|
+
if (message.role !== "assistant") return message;
|
|
502
|
+
const assistant = message;
|
|
503
|
+
const { content } = mapToolCallPartsDeep(assistant.content, (part) => {
|
|
504
|
+
if (part.toolCallId !== options.toolCallId) return part;
|
|
505
|
+
recorded = true;
|
|
506
|
+
return {
|
|
507
|
+
...part,
|
|
508
|
+
unstable_interactions: appendToolInteraction(part.unstable_interactions, options.interaction)
|
|
509
|
+
};
|
|
510
|
+
});
|
|
511
|
+
return recorded ? {
|
|
512
|
+
...assistant,
|
|
513
|
+
content
|
|
514
|
+
} : assistant;
|
|
515
|
+
});
|
|
516
|
+
if (!recorded || !updated) throw new Error(`[agui] recordToolInteraction: tool call "${options.toolCallId}" was not found on message "${options.messageId}"`);
|
|
517
|
+
this.notifyUpdate();
|
|
518
|
+
const message = this.session.tryGetMessage(sessionMessageId)?.message;
|
|
519
|
+
const history = this.history;
|
|
520
|
+
if (!message || !history?.update || !this.isPersistableStatus(message.status)) return;
|
|
521
|
+
await this.chainHistoryWrite(sessionMessageId, () => history.update({
|
|
522
|
+
parentId: item.parentId,
|
|
523
|
+
message
|
|
524
|
+
}));
|
|
525
|
+
}
|
|
486
526
|
sendA2uiAction(action) {
|
|
487
527
|
this.assertNoPendingInterrupts();
|
|
488
528
|
this.maybeAutoCancelPendingToolCalls();
|
|
@@ -542,6 +582,18 @@ var AgUiThreadRuntimeCore = class {
|
|
|
542
582
|
});
|
|
543
583
|
}
|
|
544
584
|
applyExternalMessages(messages) {
|
|
585
|
+
messages = messages.map((message) => {
|
|
586
|
+
if (message.role === "system" || message.metadata.modality !== void 0) return message;
|
|
587
|
+
const modality = this.session.tryGetMessage(message.id)?.message.metadata.modality;
|
|
588
|
+
if (modality === void 0) return message;
|
|
589
|
+
return {
|
|
590
|
+
...message,
|
|
591
|
+
metadata: {
|
|
592
|
+
...message.metadata,
|
|
593
|
+
modality
|
|
594
|
+
}
|
|
595
|
+
};
|
|
596
|
+
});
|
|
545
597
|
this.pendingA2uiResumeOwner = null;
|
|
546
598
|
this.pendingA2uiAction = void 0;
|
|
547
599
|
this.assistantHistoryParents.clear();
|
|
@@ -596,6 +648,26 @@ var AgUiThreadRuntimeCore = class {
|
|
|
596
648
|
this.stateSnapshot = void 0;
|
|
597
649
|
this.notifyUpdate();
|
|
598
650
|
}
|
|
651
|
+
resetThreadState() {
|
|
652
|
+
const controller = this.abortController;
|
|
653
|
+
const activeRunAgent = this.activeRunAgent;
|
|
654
|
+
this.stateSnapshot = void 0;
|
|
655
|
+
this.pendingResume = null;
|
|
656
|
+
this.pendingA2uiResumeOwner = null;
|
|
657
|
+
this.pendingA2uiAction = void 0;
|
|
658
|
+
if (controller) {
|
|
659
|
+
this.abortController = null;
|
|
660
|
+
this.activeRunAgent = null;
|
|
661
|
+
this.setRunning(false);
|
|
662
|
+
try {
|
|
663
|
+
(activeRunAgent ?? this.agent).abortRun();
|
|
664
|
+
} catch (error) {
|
|
665
|
+
this.logger.error?.("[agui] agent abortRun failed", error);
|
|
666
|
+
} finally {
|
|
667
|
+
controller.abort();
|
|
668
|
+
}
|
|
669
|
+
} else this.notifyUpdate();
|
|
670
|
+
}
|
|
599
671
|
async startRun(parentId, runConfig, resume, resumeStream) {
|
|
600
672
|
if (!this.supersedeActiveRun()) return;
|
|
601
673
|
const normalizedRunConfig = runConfig ?? {};
|
|
@@ -651,6 +723,8 @@ var AgUiThreadRuntimeCore = class {
|
|
|
651
723
|
}
|
|
652
724
|
} else assistantCollided = true;
|
|
653
725
|
};
|
|
726
|
+
const abortController = new AbortController();
|
|
727
|
+
const abortSignal = abortController.signal;
|
|
654
728
|
const aggregator = new RunAggregator({
|
|
655
729
|
showThinking: this.showThinking,
|
|
656
730
|
logger: this.logger,
|
|
@@ -660,9 +734,11 @@ var AgUiThreadRuntimeCore = class {
|
|
|
660
734
|
},
|
|
661
735
|
onTextMessageStart: (serverId) => adoptServerMessageId(serverId, true)
|
|
662
736
|
});
|
|
663
|
-
const dispatch = (event) =>
|
|
664
|
-
|
|
665
|
-
|
|
737
|
+
const dispatch = (event) => {
|
|
738
|
+
if (this.abortController !== abortController) return;
|
|
739
|
+
const nextAssistantMessageId = this.handleEvent(aggregator, event, assistantMessageId);
|
|
740
|
+
if (event.type === "MESSAGES_SNAPSHOT") assistantMessageId = nextAssistantMessageId;
|
|
741
|
+
};
|
|
666
742
|
this.abortController = abortController;
|
|
667
743
|
const runAgentInstance = this.agent;
|
|
668
744
|
this.activeRunAgent = runAgentInstance;
|
|
@@ -947,21 +1023,36 @@ var AgUiThreadRuntimeCore = class {
|
|
|
947
1023
|
return resolvedMessageId;
|
|
948
1024
|
}
|
|
949
1025
|
preserveToolResults(previous, next) {
|
|
950
|
-
const
|
|
951
|
-
for (const part of iterateToolCallParts(previous)) if (isResolvedToolCall(part))
|
|
952
|
-
if (
|
|
1026
|
+
const preserved = /* @__PURE__ */ new Map();
|
|
1027
|
+
for (const part of iterateToolCallParts(previous)) if (isResolvedToolCall(part)) preserved.set(part.toolCallId, part);
|
|
1028
|
+
if (preserved.size === 0) return this.preserveToolInteractions(previous, next);
|
|
953
1029
|
const { content: merged, changed } = mapToolCallPartsDeep(next, (part) => {
|
|
954
|
-
|
|
955
|
-
const prior = resolved.get(part.toolCallId);
|
|
1030
|
+
const prior = preserved.get(part.toolCallId);
|
|
956
1031
|
if (!prior) return part;
|
|
957
|
-
return {
|
|
1032
|
+
if (!isResolvedToolCall(part) && isResolvedToolCall(prior)) return {
|
|
958
1033
|
...part,
|
|
959
1034
|
result: prior.result,
|
|
960
1035
|
...prior.artifact !== void 0 ? { artifact: prior.artifact } : {},
|
|
961
|
-
...prior.isError !== void 0 ? { isError: prior.isError } : {}
|
|
1036
|
+
...prior.isError !== void 0 ? { isError: prior.isError } : {},
|
|
1037
|
+
...prior.modelContent !== void 0 ? { modelContent: prior.modelContent } : {}
|
|
962
1038
|
};
|
|
1039
|
+
return part;
|
|
963
1040
|
});
|
|
964
|
-
return changed ? merged : next;
|
|
1041
|
+
return this.preserveToolInteractions(previous, changed ? merged : next);
|
|
1042
|
+
}
|
|
1043
|
+
preserveToolInteractions(previous, next) {
|
|
1044
|
+
const interactions = /* @__PURE__ */ new Map();
|
|
1045
|
+
for (const part of iterateToolCallParts(previous)) if (part.unstable_interactions !== void 0) interactions.set(part.toolCallId, part.unstable_interactions);
|
|
1046
|
+
if (interactions.size === 0) return next;
|
|
1047
|
+
const { content, changed } = mapToolCallPartsDeep(next, (part) => {
|
|
1048
|
+
const interaction = interactions.get(part.toolCallId);
|
|
1049
|
+
if (interaction === void 0 || part.unstable_interactions !== void 0) return part;
|
|
1050
|
+
return {
|
|
1051
|
+
...part,
|
|
1052
|
+
unstable_interactions: interaction
|
|
1053
|
+
};
|
|
1054
|
+
});
|
|
1055
|
+
return changed ? content : next;
|
|
965
1056
|
}
|
|
966
1057
|
mergeAssistantMetadata(current, incoming) {
|
|
967
1058
|
const annotations = incoming.unstable_annotations ? [...current.unstable_annotations, ...incoming.unstable_annotations] : current.unstable_annotations;
|
|
@@ -981,46 +1072,69 @@ var AgUiThreadRuntimeCore = class {
|
|
|
981
1072
|
switch (event.type) {
|
|
982
1073
|
case "STATE_SNAPSHOT":
|
|
983
1074
|
this.stateSnapshot = event.snapshot;
|
|
984
|
-
this.
|
|
985
|
-
|
|
1075
|
+
this.updateActiveAssistantState(activeAssistantId);
|
|
1076
|
+
break;
|
|
986
1077
|
case "STATE_DELTA":
|
|
987
|
-
if (event.delta.length === 0)
|
|
1078
|
+
if (event.delta.length === 0) break;
|
|
988
1079
|
try {
|
|
989
1080
|
const state = this.stateSnapshot ?? {};
|
|
990
1081
|
const result = jsonpatch.applyPatch(state, event.delta, true, false);
|
|
991
1082
|
this.stateSnapshot = result.newDocument;
|
|
992
|
-
this.
|
|
1083
|
+
this.updateActiveAssistantState(activeAssistantId);
|
|
993
1084
|
} catch (error) {
|
|
994
1085
|
this.logger.error?.("[agui] failed to apply state delta", error);
|
|
995
1086
|
}
|
|
996
|
-
|
|
997
|
-
case "MESSAGES_SNAPSHOT":
|
|
1087
|
+
break;
|
|
1088
|
+
case "MESSAGES_SNAPSHOT": {
|
|
1089
|
+
const previousHeadId = this.session.headId;
|
|
1090
|
+
const activeAssistant = activeAssistantId === void 0 ? void 0 : this.session.tryGetMessage(activeAssistantId)?.message;
|
|
1091
|
+
const hasActiveText = activeAssistant?.role === "assistant" && activeAssistant.content.some((part) => part.type === "text");
|
|
998
1092
|
this.importMessagesSnapshot(event.messages, activeAssistantId);
|
|
999
|
-
|
|
1093
|
+
const headId = this.session.headId;
|
|
1094
|
+
if (hasActiveText && headId !== previousHeadId) activeAssistantId = (headId === null ? void 0 : this.session.tryGetMessage(headId)?.message)?.role === "assistant" ? headId ?? void 0 : void 0;
|
|
1095
|
+
this.updateActiveAssistantState(activeAssistantId);
|
|
1096
|
+
break;
|
|
1097
|
+
}
|
|
1000
1098
|
case "TOOL_CALL_RESULT":
|
|
1001
1099
|
if (!aggregator.hasToolCall(event.toolCallId)) {
|
|
1002
1100
|
const messageId = this.findMessageIdForToolCall(event.toolCallId);
|
|
1003
1101
|
if (messageId !== void 0) {
|
|
1004
1102
|
this.applyCrossRunToolResult(messageId, event);
|
|
1005
|
-
|
|
1103
|
+
break;
|
|
1006
1104
|
}
|
|
1007
1105
|
}
|
|
1008
1106
|
aggregator.handle(event);
|
|
1009
|
-
|
|
1107
|
+
break;
|
|
1010
1108
|
case "ACTIVITY_SNAPSHOT": {
|
|
1011
1109
|
const toolCallId = event.content["toolCallId"];
|
|
1012
1110
|
if (event.activityType === "mcp-apps" && typeof toolCallId === "string" && !aggregator.hasToolCall(toolCallId)) {
|
|
1013
1111
|
const messageId = this.findMessageIdForToolCall(toolCallId);
|
|
1014
1112
|
if (messageId !== void 0) {
|
|
1015
1113
|
this.applyCrossRunActivitySnapshot(messageId, toolCallId, event);
|
|
1016
|
-
|
|
1114
|
+
break;
|
|
1017
1115
|
}
|
|
1018
1116
|
}
|
|
1019
1117
|
aggregator.handle(event);
|
|
1020
|
-
|
|
1118
|
+
break;
|
|
1021
1119
|
}
|
|
1022
1120
|
default: aggregator.handle(event);
|
|
1023
1121
|
}
|
|
1122
|
+
return activeAssistantId;
|
|
1123
|
+
}
|
|
1124
|
+
updateActiveAssistantState(messageId) {
|
|
1125
|
+
const activeAssistantId = (messageId === void 0 ? void 0 : this.session.tryGetMessage(messageId)?.message)?.role === "assistant" && this.session.headId === messageId ? messageId : this.session.headId;
|
|
1126
|
+
if (activeAssistantId !== null && activeAssistantId !== void 0) this.session.updateMessage(activeAssistantId, (message) => {
|
|
1127
|
+
if (message.role !== "assistant") return message;
|
|
1128
|
+
const assistant = message;
|
|
1129
|
+
return {
|
|
1130
|
+
...assistant,
|
|
1131
|
+
metadata: {
|
|
1132
|
+
...assistant.metadata,
|
|
1133
|
+
unstable_state: this.stateSnapshot ?? null
|
|
1134
|
+
}
|
|
1135
|
+
};
|
|
1136
|
+
});
|
|
1137
|
+
this.notifyUpdate();
|
|
1024
1138
|
}
|
|
1025
1139
|
applyCrossRunToolResult(messageId, event) {
|
|
1026
1140
|
if (!this.session.updateMessage(messageId, (message) => {
|
|
@@ -1107,7 +1221,13 @@ var AgUiThreadRuntimeCore = class {
|
|
|
1107
1221
|
const normalized = fromAgUiMessages(rawMessages, { showThinking: this.showThinking });
|
|
1108
1222
|
const converted = [];
|
|
1109
1223
|
for (const message of normalized) try {
|
|
1110
|
-
|
|
1224
|
+
const convertedMessage = fromThreadMessageLike(message, generateId(), FALLBACK_USER_STATUS);
|
|
1225
|
+
const existing = this.session.tryGetMessage(convertedMessage.id)?.message;
|
|
1226
|
+
if (convertedMessage.role === "assistant" && existing?.role === "assistant") converted.push({
|
|
1227
|
+
...convertedMessage,
|
|
1228
|
+
content: this.preserveToolInteractions(existing.content, convertedMessage.content)
|
|
1229
|
+
});
|
|
1230
|
+
else converted.push(convertedMessage);
|
|
1111
1231
|
} catch (error) {
|
|
1112
1232
|
this.logger.error?.("[agui] failed to import message from snapshot", error);
|
|
1113
1233
|
}
|