@assistant-ui/react-ag-ui 0.0.56 → 0.0.58
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 +16 -0
- package/dist/runtime/AgUiThreadRuntimeCore.d.ts +5 -17
- package/dist/runtime/AgUiThreadRuntimeCore.d.ts.map +1 -1
- package/dist/runtime/AgUiThreadRuntimeCore.js +133 -234
- package/dist/runtime/AgUiThreadRuntimeCore.js.map +1 -1
- package/dist/runtime/adapter/conversions.d.ts.map +1 -1
- package/dist/runtime/adapter/conversions.js +54 -25
- package/dist/runtime/adapter/conversions.js.map +1 -1
- package/dist/runtime/adapter/run-aggregator.d.ts +31 -4
- package/dist/runtime/adapter/run-aggregator.d.ts.map +1 -1
- package/dist/runtime/adapter/run-aggregator.js +419 -123
- package/dist/runtime/adapter/run-aggregator.js.map +1 -1
- package/dist/runtime/adapter/subscriber.d.ts +9 -0
- package/dist/runtime/adapter/subscriber.d.ts.map +1 -1
- package/dist/runtime/adapter/subscriber.js +4 -1
- package/dist/runtime/adapter/subscriber.js.map +1 -1
- package/dist/runtime/adapter/tool-approval.d.ts +3 -1
- package/dist/runtime/adapter/tool-approval.d.ts.map +1 -1
- package/dist/runtime/adapter/tool-approval.js +11 -12
- package/dist/runtime/adapter/tool-approval.js.map +1 -1
- package/dist/runtime/event-parser.d.ts.map +1 -1
- package/dist/runtime/event-parser.js +176 -45
- package/dist/runtime/event-parser.js.map +1 -1
- package/dist/runtime/types.d.ts +42 -1
- package/dist/runtime/types.d.ts.map +1 -1
- package/dist/useAgUiRuntime.js +12 -9
- package/dist/useAgUiRuntime.js.map +1 -1
- package/package.json +10 -10
- package/src/runtime/AgUiThreadRuntimeCore.test.ts +105 -0
- package/src/runtime/AgUiThreadRuntimeCore.ts +190 -319
- package/src/runtime/adapter/conversions.ts +128 -36
- package/src/runtime/adapter/run-aggregator.approval.test.ts +128 -0
- package/src/runtime/adapter/run-aggregator.ts +670 -148
- package/src/runtime/adapter/subscriber.ts +10 -1
- package/src/runtime/adapter/tool-approval.test.ts +75 -0
- package/src/runtime/adapter/tool-approval.ts +17 -12
- package/src/runtime/event-parser.ts +216 -36
- package/src/runtime/types.ts +70 -12
- package/src/useAgUiRuntime.approval.test.tsx +34 -24
- package/src/useAgUiRuntime.history.test.tsx +76 -0
- package/src/useAgUiRuntime.toolDeferral.test.tsx +186 -0
- package/src/useAgUiRuntime.ts +12 -9
|
@@ -5,31 +5,19 @@ 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 {
|
|
8
|
+
import { 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__";
|
|
12
12
|
const generateOptimisticId = () => `${optimisticPrefix}${generateId()}`;
|
|
13
13
|
const isOptimisticId = (id) => id.startsWith(optimisticPrefix);
|
|
14
14
|
const isResolvedToolCall = (part) => part.type === "tool-call" && "result" in part && part.result !== void 0;
|
|
15
|
-
const symbolResumeShim = Symbol("agui-resume-shim");
|
|
16
15
|
const FALLBACK_USER_STATUS = {
|
|
17
16
|
type: "complete",
|
|
18
17
|
reason: "unknown"
|
|
19
18
|
};
|
|
20
|
-
const reportCallbackError = (name, error) => {
|
|
21
|
-
console.error(`[react-ag-ui] ${name} callback threw an error`, error);
|
|
22
|
-
};
|
|
23
19
|
const invokeRuntimeCallback = (name, callback, ...args) => {
|
|
24
|
-
|
|
25
|
-
try {
|
|
26
|
-
const result = callback(...args);
|
|
27
|
-
if (result !== null && (typeof result === "object" || typeof result === "function") && "then" in result && typeof result.then === "function") Promise.resolve(result).catch((error) => {
|
|
28
|
-
reportCallbackError(name, error);
|
|
29
|
-
});
|
|
30
|
-
} catch (error) {
|
|
31
|
-
reportCallbackError(name, error);
|
|
32
|
-
}
|
|
20
|
+
invokeUserCallback("react-ag-ui", name, callback, ...args);
|
|
33
21
|
};
|
|
34
22
|
const mergeAgUiNamespace = (current, incoming) => {
|
|
35
23
|
const merged = {
|
|
@@ -54,8 +42,20 @@ var AgUiThreadRuntimeCore = class {
|
|
|
54
42
|
notifyUpdate;
|
|
55
43
|
reportedErrors = /* @__PURE__ */ new WeakSet();
|
|
56
44
|
runtime;
|
|
57
|
-
|
|
58
|
-
|
|
45
|
+
session = createMessageRepositorySession({ decorateExport: (exported, repository) => {
|
|
46
|
+
let parentId = null;
|
|
47
|
+
for (const message of repository.getMessages()) {
|
|
48
|
+
if (message.metadata.isOptimistic) exported.messages.push({
|
|
49
|
+
parentId,
|
|
50
|
+
message
|
|
51
|
+
});
|
|
52
|
+
parentId = message.id;
|
|
53
|
+
}
|
|
54
|
+
return {
|
|
55
|
+
...exported,
|
|
56
|
+
headId: repository.headId
|
|
57
|
+
};
|
|
58
|
+
} });
|
|
59
59
|
isRunningFlag = false;
|
|
60
60
|
abortController = null;
|
|
61
61
|
activeRunAgent = null;
|
|
@@ -69,6 +69,7 @@ var AgUiThreadRuntimeCore = class {
|
|
|
69
69
|
historyWrites = /* @__PURE__ */ new Map();
|
|
70
70
|
_isLoading = false;
|
|
71
71
|
_loadPromise;
|
|
72
|
+
_loadRequested = false;
|
|
72
73
|
pendingResumeMessageId = null;
|
|
73
74
|
pendingA2uiResume = false;
|
|
74
75
|
pendingA2uiAction;
|
|
@@ -81,7 +82,6 @@ var AgUiThreadRuntimeCore = class {
|
|
|
81
82
|
this.onCancel = options.onCancel;
|
|
82
83
|
this.history = options.history;
|
|
83
84
|
this.notifyUpdate = options.notifyUpdate;
|
|
84
|
-
this.installResumeShim();
|
|
85
85
|
}
|
|
86
86
|
updateOptions(options) {
|
|
87
87
|
this.agent = options.agent;
|
|
@@ -90,8 +90,9 @@ var AgUiThreadRuntimeCore = class {
|
|
|
90
90
|
this.autoCancelPendingToolCalls = options.autoCancelPendingToolCalls;
|
|
91
91
|
this.onError = options.onError;
|
|
92
92
|
this.onCancel = options.onCancel;
|
|
93
|
+
const previousHistory = this.history;
|
|
93
94
|
this.history = options.history;
|
|
94
|
-
this.
|
|
95
|
+
if (this._loadRequested && !this._loadPromise && !previousHistory && options.history && this.session.getMessages().length === 0) this.__internal_load();
|
|
95
96
|
}
|
|
96
97
|
attachRuntime(runtime) {
|
|
97
98
|
this.runtime = runtime;
|
|
@@ -103,74 +104,10 @@ var AgUiThreadRuntimeCore = class {
|
|
|
103
104
|
});
|
|
104
105
|
}
|
|
105
106
|
getMessages() {
|
|
106
|
-
return this.
|
|
107
|
+
return this.session.getMessages();
|
|
107
108
|
}
|
|
108
109
|
getMessageRepository() {
|
|
109
|
-
|
|
110
|
-
const exportedRepository = this.repository.export();
|
|
111
|
-
let parentId = null;
|
|
112
|
-
for (const message of this.repository.getMessages()) {
|
|
113
|
-
if (message.metadata.isOptimistic) exportedRepository.messages.push({
|
|
114
|
-
parentId,
|
|
115
|
-
message
|
|
116
|
-
});
|
|
117
|
-
parentId = message.id;
|
|
118
|
-
}
|
|
119
|
-
this.exportedRepository = {
|
|
120
|
-
...exportedRepository,
|
|
121
|
-
headId: this.repository.headId
|
|
122
|
-
};
|
|
123
|
-
return this.exportedRepository;
|
|
124
|
-
}
|
|
125
|
-
tryGetMessage(messageId) {
|
|
126
|
-
try {
|
|
127
|
-
return this.repository.getMessage(messageId);
|
|
128
|
-
} catch {
|
|
129
|
-
return;
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
tryGetMessages(messageId) {
|
|
133
|
-
try {
|
|
134
|
-
return this.repository.getMessages(messageId);
|
|
135
|
-
} catch {
|
|
136
|
-
return;
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
hasMessage(messageId) {
|
|
140
|
-
return this.tryGetMessage(messageId) !== void 0;
|
|
141
|
-
}
|
|
142
|
-
addOrUpdateMessage(parentId, message) {
|
|
143
|
-
this.repository.addOrUpdateMessage(parentId, message);
|
|
144
|
-
this.exportedRepository = void 0;
|
|
145
|
-
}
|
|
146
|
-
deleteMessage(messageId, replacementId) {
|
|
147
|
-
this.repository.deleteMessage(messageId, replacementId);
|
|
148
|
-
this.exportedRepository = void 0;
|
|
149
|
-
}
|
|
150
|
-
tryDeleteMessage(messageId) {
|
|
151
|
-
if (!this.hasMessage(messageId)) return false;
|
|
152
|
-
this.deleteMessage(messageId);
|
|
153
|
-
return true;
|
|
154
|
-
}
|
|
155
|
-
switchToBranch(messageId) {
|
|
156
|
-
this.repository.switchToBranch(messageId);
|
|
157
|
-
this.exportedRepository = void 0;
|
|
158
|
-
}
|
|
159
|
-
resetRepositoryHead(messageId) {
|
|
160
|
-
this.repository.resetHead(messageId);
|
|
161
|
-
this.exportedRepository = void 0;
|
|
162
|
-
}
|
|
163
|
-
clearRepository() {
|
|
164
|
-
this.repository.clear();
|
|
165
|
-
this.exportedRepository = void 0;
|
|
166
|
-
}
|
|
167
|
-
updateMessage(messageId, updater) {
|
|
168
|
-
const item = this.tryGetMessage(messageId);
|
|
169
|
-
if (!item) return false;
|
|
170
|
-
const message = updater(item.message);
|
|
171
|
-
if (message === item.message) return false;
|
|
172
|
-
this.addOrUpdateMessage(item.parentId, message);
|
|
173
|
-
return true;
|
|
110
|
+
return this.session.export();
|
|
174
111
|
}
|
|
175
112
|
getState() {
|
|
176
113
|
return this.stateSnapshot;
|
|
@@ -182,15 +119,22 @@ var AgUiThreadRuntimeCore = class {
|
|
|
182
119
|
return this._isLoading;
|
|
183
120
|
}
|
|
184
121
|
__internal_load() {
|
|
122
|
+
this._loadRequested = true;
|
|
185
123
|
if (this._loadPromise) return this._loadPromise;
|
|
186
|
-
|
|
124
|
+
if (!this.history) return Promise.resolve();
|
|
125
|
+
const promise = this.history.load();
|
|
187
126
|
this._isLoading = true;
|
|
188
127
|
this._loadPromise = promise.then(async (repo) => {
|
|
189
128
|
if (!repo) return;
|
|
190
|
-
this.applyExternalMessageRepository(repo);
|
|
129
|
+
this.session.applyExternalMessageRepository(repo);
|
|
130
|
+
this.assistantHistoryParents.clear();
|
|
131
|
+
this.snapshotHistoryIds.clear();
|
|
132
|
+
this.persistedHistoryIds.clear();
|
|
133
|
+
for (const { message } of repo.messages) this.persistedHistoryIds.add(message.id);
|
|
134
|
+
this.notifyUpdate();
|
|
191
135
|
if (repo.state !== void 0) this.loadExternalState(repo.state);
|
|
192
136
|
if (repo.unstable_resume) {
|
|
193
|
-
const parentId = repo.headId ?? this.
|
|
137
|
+
const parentId = repo.headId ?? this.session.headId;
|
|
194
138
|
const resumeStream = this.history?.resume?.bind(this.history);
|
|
195
139
|
await this.startRun(parentId, this.lastRunConfig, void 0, resumeStream);
|
|
196
140
|
}
|
|
@@ -222,11 +166,11 @@ var AgUiThreadRuntimeCore = class {
|
|
|
222
166
|
this.maybeCompleteAfterToolResults(pending.messageId);
|
|
223
167
|
}
|
|
224
168
|
appendEntry(message) {
|
|
225
|
-
if (message.sourceId) this.tryDeleteMessage(message.sourceId);
|
|
169
|
+
if (message.sourceId) this.session.tryDeleteMessage(message.sourceId);
|
|
226
170
|
const threadMessage = this.toThreadMessage(message);
|
|
227
|
-
const parentId = message.parentId === null ? null : message.parentId && this.hasMessage(message.parentId) ? message.parentId : this.
|
|
228
|
-
this.addOrUpdateMessage(parentId, threadMessage);
|
|
229
|
-
this.switchToBranch(threadMessage.id);
|
|
171
|
+
const parentId = message.parentId === null ? null : message.parentId && this.session.hasMessage(message.parentId) ? message.parentId : this.session.headId;
|
|
172
|
+
this.session.addOrUpdateMessage(parentId, threadMessage);
|
|
173
|
+
this.session.switchToBranch(threadMessage.id);
|
|
230
174
|
this.notifyUpdate();
|
|
231
175
|
this.recordHistoryEntry(parentId, threadMessage);
|
|
232
176
|
return threadMessage.id;
|
|
@@ -287,8 +231,7 @@ var AgUiThreadRuntimeCore = class {
|
|
|
287
231
|
const assistant = this.findRequiresActionAssistant("tool-calls");
|
|
288
232
|
if (!assistant) return null;
|
|
289
233
|
const toolCallIds = [];
|
|
290
|
-
for (const part of assistant.content) {
|
|
291
|
-
if (part.type !== "tool-call") continue;
|
|
234
|
+
for (const part of iterateToolCallParts(assistant.content)) {
|
|
292
235
|
if (isResolvedToolCall(part)) continue;
|
|
293
236
|
toolCallIds.push(part.toolCallId);
|
|
294
237
|
}
|
|
@@ -319,9 +262,9 @@ var AgUiThreadRuntimeCore = class {
|
|
|
319
262
|
await this.startRun(messageId, this.lastRunConfig, resume);
|
|
320
263
|
}
|
|
321
264
|
/**
|
|
322
|
-
*
|
|
323
|
-
*
|
|
324
|
-
*
|
|
265
|
+
* A rejected decision reaches the caller through the approval seam, and
|
|
266
|
+
* `onError` in addition, because a consumer watching only the runtime would
|
|
267
|
+
* otherwise miss it. A failure raised by the resumed run itself was already
|
|
325
268
|
* reported by `startRun` before it rethrew, so reporting it here again would
|
|
326
269
|
* give the consumer two notifications for one failure.
|
|
327
270
|
*/
|
|
@@ -345,10 +288,10 @@ var AgUiThreadRuntimeCore = class {
|
|
|
345
288
|
async respondToToolApproval(options) {
|
|
346
289
|
const pending = this.getPendingInterrupts();
|
|
347
290
|
if (!pending) throw new Error("[agui] respondToToolApproval: no pending interrupts on this thread");
|
|
348
|
-
const gatedMessage = this.tryGetMessage(pending.messageId)?.message;
|
|
349
|
-
if (![...projectAgUiToolApprovals(pending.interrupts, new Set((gatedMessage?.content ?? [])
|
|
291
|
+
const gatedMessage = this.session.tryGetMessage(pending.messageId)?.message;
|
|
292
|
+
if (![...projectAgUiToolApprovals(pending.interrupts, new Set(Array.from(iterateToolCallParts(gatedMessage?.content ?? [])).map((part) => part.toolCallId))).values()].some((approval) => approval.id === options.approvalId)) throw new Error(`[agui] respondToToolApproval: no pending tool-call interrupt for approval id "${options.approvalId}"`);
|
|
350
293
|
this.assertInterruptsAnswerable("respondToToolApproval", pending.interrupts);
|
|
351
|
-
if (!this.updateMessage(pending.messageId, (message) => {
|
|
294
|
+
if (!this.session.updateMessage(pending.messageId, (message) => {
|
|
352
295
|
if (message.role !== "assistant") return message;
|
|
353
296
|
const assistant = message;
|
|
354
297
|
const content = withToolApprovalDecision(assistant.content, options);
|
|
@@ -359,7 +302,7 @@ var AgUiThreadRuntimeCore = class {
|
|
|
359
302
|
};
|
|
360
303
|
})) throw new Error(`[agui] respondToToolApproval: approval "${options.approvalId}" is already decided`);
|
|
361
304
|
this.notifyUpdate();
|
|
362
|
-
const assistant = this.tryGetMessage(pending.messageId)?.message;
|
|
305
|
+
const assistant = this.session.tryGetMessage(pending.messageId)?.message;
|
|
363
306
|
if (!assistant) return;
|
|
364
307
|
const resume = buildToolApprovalResume(assistant.content, pending.interrupts);
|
|
365
308
|
if (!resume) return;
|
|
@@ -412,7 +355,7 @@ var AgUiThreadRuntimeCore = class {
|
|
|
412
355
|
toAppendMessage(message) {
|
|
413
356
|
if (typeof message === "string") return {
|
|
414
357
|
createdAt: /* @__PURE__ */ new Date(),
|
|
415
|
-
parentId: this.
|
|
358
|
+
parentId: this.session.headId,
|
|
416
359
|
sourceId: null,
|
|
417
360
|
runConfig: {},
|
|
418
361
|
role: "user",
|
|
@@ -425,7 +368,7 @@ var AgUiThreadRuntimeCore = class {
|
|
|
425
368
|
};
|
|
426
369
|
return {
|
|
427
370
|
createdAt: message.createdAt ?? /* @__PURE__ */ new Date(),
|
|
428
|
-
parentId: message.parentId ?? this.
|
|
371
|
+
parentId: message.parentId ?? this.session.headId,
|
|
429
372
|
sourceId: message.sourceId ?? null,
|
|
430
373
|
role: message.role ?? "user",
|
|
431
374
|
content: message.content,
|
|
@@ -436,7 +379,7 @@ var AgUiThreadRuntimeCore = class {
|
|
|
436
379
|
};
|
|
437
380
|
}
|
|
438
381
|
clearPendingInterrupts(messageId, resume) {
|
|
439
|
-
if (this.updateMessage(messageId, (message) => {
|
|
382
|
+
if (this.session.updateMessage(messageId, (message) => {
|
|
440
383
|
if (message.role !== "assistant") return message;
|
|
441
384
|
const assistant = message;
|
|
442
385
|
if (assistant.status?.type !== "requires-action" || assistant.status.reason !== "interrupt") return assistant;
|
|
@@ -464,8 +407,8 @@ var AgUiThreadRuntimeCore = class {
|
|
|
464
407
|
for (let index = messages.length - 1; index >= 0; index--) {
|
|
465
408
|
const message = messages[index];
|
|
466
409
|
if (!message || message.role !== "assistant") continue;
|
|
467
|
-
for (const part of message.content) {
|
|
468
|
-
if (part.
|
|
410
|
+
for (const part of iterateToolCallParts(message.content)) {
|
|
411
|
+
if (part.toolCallId !== toolCallId) continue;
|
|
469
412
|
if (!isResolvedToolCall(part)) return message.id;
|
|
470
413
|
fallbackMessageId ??= message.id;
|
|
471
414
|
}
|
|
@@ -473,11 +416,11 @@ var AgUiThreadRuntimeCore = class {
|
|
|
473
416
|
return fallbackMessageId;
|
|
474
417
|
}
|
|
475
418
|
cancelUnresolvedToolCalls(messageId) {
|
|
476
|
-
if (this.updateMessage(messageId, (message) => {
|
|
419
|
+
if (this.session.updateMessage(messageId, (message) => {
|
|
477
420
|
if (message.role !== "assistant") return message;
|
|
478
421
|
const assistant = message;
|
|
479
|
-
const content = assistant.content
|
|
480
|
-
if (
|
|
422
|
+
const { content } = mapToolCallPartsDeep(assistant.content, (part) => {
|
|
423
|
+
if (isResolvedToolCall(part)) return part;
|
|
481
424
|
return {
|
|
482
425
|
...part,
|
|
483
426
|
result: { error: "Tool call cancelled by user" },
|
|
@@ -491,12 +434,14 @@ var AgUiThreadRuntimeCore = class {
|
|
|
491
434
|
})) this.notifyUpdate();
|
|
492
435
|
}
|
|
493
436
|
addToolResult(options) {
|
|
494
|
-
|
|
437
|
+
const sessionMessageId = this.session.tryGetMessage(options.messageId) ? options.messageId : this.findMessageIdForToolCall(options.toolCallId);
|
|
438
|
+
if (sessionMessageId === void 0) return;
|
|
439
|
+
if (!this.session.updateMessage(sessionMessageId, (message) => {
|
|
495
440
|
if (message.role !== "assistant") return message;
|
|
496
441
|
const assistant = message;
|
|
497
442
|
let matchedToolCall = false;
|
|
498
|
-
const content = assistant.content
|
|
499
|
-
if (part.
|
|
443
|
+
const { content } = mapToolCallPartsDeep(assistant.content, (part) => {
|
|
444
|
+
if (part.toolCallId !== options.toolCallId) return part;
|
|
500
445
|
matchedToolCall = true;
|
|
501
446
|
return {
|
|
502
447
|
...part,
|
|
@@ -512,12 +457,12 @@ var AgUiThreadRuntimeCore = class {
|
|
|
512
457
|
};
|
|
513
458
|
})) return;
|
|
514
459
|
this.notifyUpdate();
|
|
515
|
-
this.maybeResumeAfterToolResults(
|
|
460
|
+
this.maybeResumeAfterToolResults(sessionMessageId);
|
|
516
461
|
}
|
|
517
462
|
sendA2uiAction(action) {
|
|
518
463
|
this.assertNoPendingInterrupts();
|
|
519
464
|
this.maybeAutoCancelPendingToolCalls();
|
|
520
|
-
const parentId = this.
|
|
465
|
+
const parentId = this.session.headId;
|
|
521
466
|
if (parentId === null) {
|
|
522
467
|
this.logger.debug("[agui] sendA2uiAction: no messages to resume, dropping action");
|
|
523
468
|
return;
|
|
@@ -541,12 +486,17 @@ var AgUiThreadRuntimeCore = class {
|
|
|
541
486
|
this.startResumeRun(messageId);
|
|
542
487
|
}
|
|
543
488
|
maybeCompleteAfterToolResults(messageId) {
|
|
544
|
-
const message = this.tryGetMessage(messageId)?.message;
|
|
489
|
+
const message = this.session.tryGetMessage(messageId)?.message;
|
|
545
490
|
if (!message || message.role !== "assistant") return false;
|
|
546
491
|
const assistant = message;
|
|
547
492
|
if (assistant.status?.type !== "requires-action" || assistant.status.reason !== "tool-calls") return false;
|
|
548
|
-
|
|
549
|
-
|
|
493
|
+
let allResolved = true;
|
|
494
|
+
for (const part of iterateToolCallParts(assistant.content)) if (!isResolvedToolCall(part)) {
|
|
495
|
+
allResolved = false;
|
|
496
|
+
break;
|
|
497
|
+
}
|
|
498
|
+
if (!allResolved) return false;
|
|
499
|
+
if (!this.session.updateMessage(messageId, (current) => current.role === "assistant" ? {
|
|
550
500
|
...current,
|
|
551
501
|
status: {
|
|
552
502
|
type: "complete",
|
|
@@ -566,7 +516,7 @@ var AgUiThreadRuntimeCore = class {
|
|
|
566
516
|
this.pendingA2uiResume = false;
|
|
567
517
|
this.pendingA2uiAction = void 0;
|
|
568
518
|
this.assistantHistoryParents.clear();
|
|
569
|
-
if (messages.length === 0) this.
|
|
519
|
+
if (messages.length === 0) this.session.clear();
|
|
570
520
|
else {
|
|
571
521
|
let expectedParentId = null;
|
|
572
522
|
let lastAppliedId = null;
|
|
@@ -575,85 +525,34 @@ var AgUiThreadRuntimeCore = class {
|
|
|
575
525
|
for (const message of messages) {
|
|
576
526
|
if (seen.has(message.id)) continue;
|
|
577
527
|
seen.add(message.id);
|
|
578
|
-
const existing = this.tryGetMessage(message.id);
|
|
528
|
+
const existing = this.session.tryGetMessage(message.id);
|
|
579
529
|
if (existing && existing.parentId !== expectedParentId) {
|
|
580
530
|
hardReplace = true;
|
|
581
531
|
break;
|
|
582
532
|
}
|
|
583
|
-
this.addOrUpdateMessage(expectedParentId, message);
|
|
533
|
+
this.session.addOrUpdateMessage(expectedParentId, message);
|
|
584
534
|
expectedParentId = message.id;
|
|
585
535
|
lastAppliedId = message.id;
|
|
586
536
|
}
|
|
587
537
|
if (hardReplace) {
|
|
588
|
-
this.
|
|
538
|
+
this.session.clear();
|
|
589
539
|
expectedParentId = null;
|
|
590
540
|
lastAppliedId = null;
|
|
591
541
|
seen.clear();
|
|
592
542
|
for (const message of messages) {
|
|
593
543
|
if (seen.has(message.id)) continue;
|
|
594
544
|
seen.add(message.id);
|
|
595
|
-
this.addOrUpdateMessage(expectedParentId, message);
|
|
545
|
+
this.session.addOrUpdateMessage(expectedParentId, message);
|
|
596
546
|
expectedParentId = message.id;
|
|
597
547
|
lastAppliedId = message.id;
|
|
598
548
|
}
|
|
599
549
|
}
|
|
600
|
-
this.
|
|
550
|
+
this.session.resetHead(lastAppliedId);
|
|
601
551
|
}
|
|
602
552
|
this.snapshotHistoryIds.clear();
|
|
603
553
|
for (const { message } of this.getMessageRepository().messages) this.snapshotHistoryIds.add(message.id);
|
|
604
554
|
this.notifyUpdate();
|
|
605
555
|
}
|
|
606
|
-
applyExternalMessageRepository(loaded) {
|
|
607
|
-
const headId = loaded.headId ?? loaded.messages.at(-1)?.message.id ?? null;
|
|
608
|
-
const ids = /* @__PURE__ */ new Set();
|
|
609
|
-
let degenerate = false;
|
|
610
|
-
for (const { message } of loaded.messages) {
|
|
611
|
-
if (ids.has(message.id)) {
|
|
612
|
-
degenerate = true;
|
|
613
|
-
break;
|
|
614
|
-
}
|
|
615
|
-
ids.add(message.id);
|
|
616
|
-
}
|
|
617
|
-
if (headId !== null && !ids.has(headId)) degenerate = true;
|
|
618
|
-
if (!degenerate) {
|
|
619
|
-
this.clearRepository();
|
|
620
|
-
let pending = [...loaded.messages];
|
|
621
|
-
const importedIds = /* @__PURE__ */ new Set();
|
|
622
|
-
while (pending.length > 0) {
|
|
623
|
-
const unresolved = [];
|
|
624
|
-
let progressed = false;
|
|
625
|
-
for (const item of pending) {
|
|
626
|
-
if (item.parentId !== null && !importedIds.has(item.parentId)) {
|
|
627
|
-
unresolved.push(item);
|
|
628
|
-
continue;
|
|
629
|
-
}
|
|
630
|
-
this.addOrUpdateMessage(item.parentId, item.message);
|
|
631
|
-
importedIds.add(item.message.id);
|
|
632
|
-
progressed = true;
|
|
633
|
-
}
|
|
634
|
-
if (!progressed) {
|
|
635
|
-
degenerate = true;
|
|
636
|
-
break;
|
|
637
|
-
}
|
|
638
|
-
pending = unresolved;
|
|
639
|
-
}
|
|
640
|
-
}
|
|
641
|
-
if (degenerate) {
|
|
642
|
-
this.clearRepository();
|
|
643
|
-
let previousId = null;
|
|
644
|
-
for (const { message } of loaded.messages) {
|
|
645
|
-
const existing = this.tryGetMessage(message.id);
|
|
646
|
-
this.addOrUpdateMessage(existing ? existing.parentId : previousId, message);
|
|
647
|
-
previousId = message.id;
|
|
648
|
-
}
|
|
649
|
-
this.resetRepositoryHead(previousId);
|
|
650
|
-
} else this.resetRepositoryHead(headId);
|
|
651
|
-
this.assistantHistoryParents.clear();
|
|
652
|
-
this.snapshotHistoryIds.clear();
|
|
653
|
-
this.persistedHistoryIds.clear();
|
|
654
|
-
for (const { message } of loaded.messages) this.persistedHistoryIds.add(message.id);
|
|
655
|
-
this.notifyUpdate();
|
|
656
|
-
}
|
|
657
556
|
loadExternalState(state) {
|
|
658
557
|
this.stateSnapshot = state;
|
|
659
558
|
this.notifyUpdate();
|
|
@@ -669,20 +568,20 @@ var AgUiThreadRuntimeCore = class {
|
|
|
669
568
|
async startRun(parentId, runConfig, resume, resumeStream) {
|
|
670
569
|
const normalizedRunConfig = runConfig ?? {};
|
|
671
570
|
this.lastRunConfig = normalizedRunConfig;
|
|
672
|
-
const parent = parentId === null ? void 0 : this.tryGetMessage(parentId);
|
|
673
|
-
const shouldEagerlyInsertAssistant = parentId !== null && parent !== void 0 && parentId !== this.
|
|
674
|
-
const historicalMessages = [...shouldEagerlyInsertAssistant ? this.tryGetMessages(parentId) ?? this.
|
|
571
|
+
const parent = parentId === null ? void 0 : this.session.tryGetMessage(parentId);
|
|
572
|
+
const shouldEagerlyInsertAssistant = parentId !== null && parent !== void 0 && parentId !== this.session.headId;
|
|
573
|
+
const historicalMessages = [...shouldEagerlyInsertAssistant ? this.session.tryGetMessages(parentId) ?? this.session.getMessages() : this.session.getMessages()];
|
|
675
574
|
const runStartMessageIds = new Set(this.getMessageRepository().messages.map(({ message }) => message.id));
|
|
676
575
|
this.pendingError = null;
|
|
677
|
-
const assistantParentId = parent ? parentId : this.
|
|
576
|
+
const assistantParentId = parent ? parentId : this.session.headId;
|
|
678
577
|
let assistantMessageId;
|
|
679
578
|
let assistantCollided = false;
|
|
680
579
|
const ensureAssistant = (allowRecreate = false) => {
|
|
681
580
|
const cached = assistantMessageId;
|
|
682
|
-
if (cached !== void 0 && this.tryGetMessage(cached)) return cached;
|
|
581
|
+
if (cached !== void 0 && this.session.tryGetMessage(cached)) return cached;
|
|
683
582
|
if (cached !== void 0 && (assistantCollided || !allowRecreate)) return cached;
|
|
684
|
-
const repositoryHeadId = this.
|
|
685
|
-
const parentId = (cached === void 0 || shouldEagerlyInsertAssistant && (repositoryHeadId === null || runStartMessageIds.has(repositoryHeadId))) && assistantParentId && this.hasMessage(assistantParentId) ? assistantParentId : repositoryHeadId;
|
|
583
|
+
const repositoryHeadId = this.session.headId;
|
|
584
|
+
const parentId = (cached === void 0 || shouldEagerlyInsertAssistant && (repositoryHeadId === null || runStartMessageIds.has(repositoryHeadId))) && assistantParentId && this.session.hasMessage(assistantParentId) ? assistantParentId : repositoryHeadId;
|
|
686
585
|
const created = this.insertAssistantPlaceholder(parentId, cached);
|
|
687
586
|
assistantMessageId = created;
|
|
688
587
|
this.markPendingAssistantHistory(created, parentId);
|
|
@@ -694,23 +593,40 @@ var AgUiThreadRuntimeCore = class {
|
|
|
694
593
|
const resolved = this.updateAssistantMessage(ensureAssistant(hasStreamContent), update);
|
|
695
594
|
if (resolved !== assistantMessageId) assistantMessageId = resolved;
|
|
696
595
|
};
|
|
596
|
+
const adoptServerMessageId = (serverId, startNewMessage) => {
|
|
597
|
+
if (startNewMessage && assistantMessageId !== void 0) {
|
|
598
|
+
applyUpdate({ status: {
|
|
599
|
+
type: "complete",
|
|
600
|
+
reason: "unknown"
|
|
601
|
+
} });
|
|
602
|
+
const previousId = assistantMessageId;
|
|
603
|
+
assistantMessageId = void 0;
|
|
604
|
+
if (this.session.tryGetMessage(previousId)) {
|
|
605
|
+
const created = this.insertAssistantPlaceholder(previousId);
|
|
606
|
+
assistantMessageId = created;
|
|
607
|
+
this.markPendingAssistantHistory(created, previousId);
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
const placeholder = ensureAssistant(true);
|
|
611
|
+
if (placeholder === serverId) return;
|
|
612
|
+
const reassigned = this.reassignAssistantId(placeholder, serverId);
|
|
613
|
+
const adoptsVisibleCollision = !reassigned && !runStartMessageIds.has(serverId) && this.session.headId === serverId;
|
|
614
|
+
if (reassigned || adoptsVisibleCollision) {
|
|
615
|
+
assistantMessageId = serverId;
|
|
616
|
+
if (adoptsVisibleCollision) {
|
|
617
|
+
const parentId = this.session.tryGetMessage(serverId)?.parentId ?? null;
|
|
618
|
+
this.markPendingAssistantHistory(serverId, parentId);
|
|
619
|
+
}
|
|
620
|
+
} else assistantCollided = true;
|
|
621
|
+
};
|
|
697
622
|
const aggregator = new RunAggregator({
|
|
698
623
|
showThinking: this.showThinking,
|
|
699
624
|
logger: this.logger,
|
|
700
625
|
emit: applyUpdate,
|
|
701
626
|
onServerMessageId: (serverId) => {
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
const adoptsVisibleCollision = !reassigned && !runStartMessageIds.has(serverId) && this.repository.headId === serverId;
|
|
706
|
-
if (reassigned || adoptsVisibleCollision) {
|
|
707
|
-
assistantMessageId = serverId;
|
|
708
|
-
if (adoptsVisibleCollision) {
|
|
709
|
-
const parentId = this.tryGetMessage(serverId)?.parentId ?? null;
|
|
710
|
-
this.markPendingAssistantHistory(serverId, parentId);
|
|
711
|
-
}
|
|
712
|
-
} else assistantCollided = true;
|
|
713
|
-
}
|
|
627
|
+
adoptServerMessageId(serverId, false);
|
|
628
|
+
},
|
|
629
|
+
onTextMessageStart: (serverId) => adoptServerMessageId(serverId, true)
|
|
714
630
|
});
|
|
715
631
|
const dispatch = (event) => this.handleEvent(aggregator, event, assistantMessageId);
|
|
716
632
|
const abortController = new AbortController();
|
|
@@ -802,7 +718,7 @@ var AgUiThreadRuntimeCore = class {
|
|
|
802
718
|
return;
|
|
803
719
|
}
|
|
804
720
|
this.maybeAutoCancelPendingToolCalls();
|
|
805
|
-
const parentId = this.
|
|
721
|
+
const parentId = this.session.headId;
|
|
806
722
|
if (parentId !== null) this.startResumeRun(parentId);
|
|
807
723
|
else {
|
|
808
724
|
this.pendingA2uiAction = void 0;
|
|
@@ -825,7 +741,7 @@ var AgUiThreadRuntimeCore = class {
|
|
|
825
741
|
unstable_threadId: ctx.threadId,
|
|
826
742
|
unstable_parentId: ctx.parentId,
|
|
827
743
|
unstable_getMessage: () => {
|
|
828
|
-
const message = this.tryGetMessage(currentId())?.message;
|
|
744
|
+
const message = this.session.tryGetMessage(currentId())?.message;
|
|
829
745
|
if (!message) throw new Error("[agui] resume stream requested the assistant message before it existed");
|
|
830
746
|
return message;
|
|
831
747
|
}
|
|
@@ -848,7 +764,7 @@ var AgUiThreadRuntimeCore = class {
|
|
|
848
764
|
return;
|
|
849
765
|
}
|
|
850
766
|
if (ctx.abortSignal.aborted) return;
|
|
851
|
-
const current = this.tryGetMessage(currentId())?.message;
|
|
767
|
+
const current = this.session.tryGetMessage(currentId())?.message;
|
|
852
768
|
if (!current || current.status?.type === "running") ctx.applyUpdate({ status: {
|
|
853
769
|
type: "complete",
|
|
854
770
|
reason: "unknown"
|
|
@@ -856,7 +772,7 @@ var AgUiThreadRuntimeCore = class {
|
|
|
856
772
|
}
|
|
857
773
|
buildRunInput(runId, runConfig, historyMessages, resume) {
|
|
858
774
|
const threadId = this.agent.threadId || "main";
|
|
859
|
-
const messages = toAgUiMessages(historyMessages ?? this.
|
|
775
|
+
const messages = toAgUiMessages(historyMessages ?? this.session.getMessages());
|
|
860
776
|
const context = this.runtime?.thread.getModelContext();
|
|
861
777
|
const input = {
|
|
862
778
|
threadId,
|
|
@@ -879,21 +795,6 @@ var AgUiThreadRuntimeCore = class {
|
|
|
879
795
|
this.pendingA2uiAction = void 0;
|
|
880
796
|
return input;
|
|
881
797
|
}
|
|
882
|
-
installResumeShim() {
|
|
883
|
-
const agent = this.agent;
|
|
884
|
-
if (agent[symbolResumeShim]) return;
|
|
885
|
-
agent[symbolResumeShim] = true;
|
|
886
|
-
const original = Object.hasOwn(agent, "prepareRunAgentInput") ? agent.prepareRunAgentInput : Object.getPrototypeOf(agent)?.prepareRunAgentInput;
|
|
887
|
-
if (typeof original !== "function") return;
|
|
888
|
-
agent.prepareRunAgentInput = function(params) {
|
|
889
|
-
const input = original.call(this, params);
|
|
890
|
-
if (params?.resume !== void 0 && input && typeof input === "object") return {
|
|
891
|
-
...input,
|
|
892
|
-
resume: params.resume
|
|
893
|
-
};
|
|
894
|
-
return input;
|
|
895
|
-
};
|
|
896
|
-
}
|
|
897
798
|
setRunning(running) {
|
|
898
799
|
this.isRunningFlag = running;
|
|
899
800
|
this.notifyUpdate();
|
|
@@ -921,31 +822,31 @@ var AgUiThreadRuntimeCore = class {
|
|
|
921
822
|
custom: {}
|
|
922
823
|
}
|
|
923
824
|
};
|
|
924
|
-
this.addOrUpdateMessage(parentId ?? this.
|
|
925
|
-
this.switchToBranch(id);
|
|
825
|
+
this.session.addOrUpdateMessage(parentId ?? this.session.headId, assistant);
|
|
826
|
+
this.session.switchToBranch(id);
|
|
926
827
|
this.notifyUpdate();
|
|
927
828
|
return id;
|
|
928
829
|
}
|
|
929
830
|
reassignAssistantId(oldId, newId) {
|
|
930
831
|
if (oldId === newId) return true;
|
|
931
|
-
const oldItem = this.tryGetMessage(oldId);
|
|
832
|
+
const oldItem = this.session.tryGetMessage(oldId);
|
|
932
833
|
if (!oldItem) return false;
|
|
933
|
-
const collidesWithExisting = this.hasMessage(newId);
|
|
834
|
+
const collidesWithExisting = this.session.hasMessage(newId);
|
|
934
835
|
if (collidesWithExisting) {
|
|
935
836
|
this.logger.debug?.("[agui] reassignAssistantId: server id already present in messages or repository, dropping placeholder", {
|
|
936
837
|
oldId,
|
|
937
838
|
newId
|
|
938
839
|
});
|
|
939
|
-
this.deleteMessage(oldId, oldItem.parentId ?? null);
|
|
840
|
+
this.session.deleteMessage(oldId, oldItem.parentId ?? null);
|
|
940
841
|
} else {
|
|
941
842
|
const { isOptimistic: _, ...metadata } = oldItem.message.metadata;
|
|
942
|
-
this.addOrUpdateMessage(oldItem.parentId, {
|
|
843
|
+
this.session.addOrUpdateMessage(oldItem.parentId, {
|
|
943
844
|
...oldItem.message,
|
|
944
845
|
id: newId,
|
|
945
846
|
metadata
|
|
946
847
|
});
|
|
947
|
-
this.switchToBranch(newId);
|
|
948
|
-
this.tryDeleteMessage(oldId);
|
|
848
|
+
this.session.switchToBranch(newId);
|
|
849
|
+
this.session.tryDeleteMessage(oldId);
|
|
949
850
|
}
|
|
950
851
|
const pendingParent = this.assistantHistoryParents.get(oldId);
|
|
951
852
|
if (pendingParent !== void 0) {
|
|
@@ -976,7 +877,7 @@ var AgUiThreadRuntimeCore = class {
|
|
|
976
877
|
}
|
|
977
878
|
updateAssistantMessage(messageId, update) {
|
|
978
879
|
let latestStatus;
|
|
979
|
-
if (!this.updateMessage(messageId, (message) => {
|
|
880
|
+
if (!this.session.updateMessage(messageId, (message) => {
|
|
980
881
|
if (message.role !== "assistant") return message;
|
|
981
882
|
const assistant = message;
|
|
982
883
|
const metadata = update.metadata ? this.mergeAssistantMetadata(assistant.metadata, update.metadata) : assistant.metadata;
|
|
@@ -1001,14 +902,12 @@ var AgUiThreadRuntimeCore = class {
|
|
|
1001
902
|
}
|
|
1002
903
|
preserveToolResults(previous, next) {
|
|
1003
904
|
const resolved = /* @__PURE__ */ new Map();
|
|
1004
|
-
for (const part of previous) if (
|
|
905
|
+
for (const part of iterateToolCallParts(previous)) if (isResolvedToolCall(part)) resolved.set(part.toolCallId, part);
|
|
1005
906
|
if (resolved.size === 0) return next;
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
if (part.type !== "tool-call" || isResolvedToolCall(part)) return part;
|
|
907
|
+
const { content: merged, changed } = mapToolCallPartsDeep(next, (part) => {
|
|
908
|
+
if (isResolvedToolCall(part)) return part;
|
|
1009
909
|
const prior = resolved.get(part.toolCallId);
|
|
1010
910
|
if (!prior) return part;
|
|
1011
|
-
changed = true;
|
|
1012
911
|
return {
|
|
1013
912
|
...part,
|
|
1014
913
|
result: prior.result,
|
|
@@ -1078,13 +977,13 @@ var AgUiThreadRuntimeCore = class {
|
|
|
1078
977
|
}
|
|
1079
978
|
}
|
|
1080
979
|
applyCrossRunToolResult(messageId, event) {
|
|
1081
|
-
if (!this.updateMessage(messageId, (message) => {
|
|
980
|
+
if (!this.session.updateMessage(messageId, (message) => {
|
|
1082
981
|
if (message.role !== "assistant") return message;
|
|
1083
982
|
const assistant = message;
|
|
1084
983
|
const mcpAppUri = readMcpAppResourceUri(event.mcpResult?._meta);
|
|
1085
984
|
let matchedToolCall = false;
|
|
1086
|
-
const content = assistant.content
|
|
1087
|
-
if (part.
|
|
985
|
+
const { content } = mapToolCallPartsDeep(assistant.content, (part) => {
|
|
986
|
+
if (part.toolCallId !== event.toolCallId) return part;
|
|
1088
987
|
matchedToolCall = true;
|
|
1089
988
|
if (part.mcp?.app !== void 0 && isPlainObject(part.result) && Array.isArray(part.result["content"])) return {
|
|
1090
989
|
...part,
|
|
@@ -1123,12 +1022,12 @@ var AgUiThreadRuntimeCore = class {
|
|
|
1123
1022
|
const serverHash = event.content["serverHash"];
|
|
1124
1023
|
const appServerId = typeof serverId === "string" && serverId.length > 0 ? serverId : typeof serverHash === "string" && serverHash.length > 0 ? serverHash : void 0;
|
|
1125
1024
|
const result = event.content["result"];
|
|
1126
|
-
if (!this.updateMessage(messageId, (message) => {
|
|
1025
|
+
if (!this.session.updateMessage(messageId, (message) => {
|
|
1127
1026
|
if (message.role !== "assistant") return message;
|
|
1128
1027
|
const assistant = message;
|
|
1129
1028
|
let matchedToolCall = false;
|
|
1130
|
-
const content = assistant.content
|
|
1131
|
-
if (part.
|
|
1029
|
+
const { content } = mapToolCallPartsDeep(assistant.content, (part) => {
|
|
1030
|
+
if (part.toolCallId !== toolCallId) return part;
|
|
1132
1031
|
matchedToolCall = true;
|
|
1133
1032
|
return {
|
|
1134
1033
|
...part,
|
|
@@ -1157,7 +1056,7 @@ var AgUiThreadRuntimeCore = class {
|
|
|
1157
1056
|
}
|
|
1158
1057
|
importMessagesSnapshot(rawMessages, activeAssistantId) {
|
|
1159
1058
|
try {
|
|
1160
|
-
const activeMessage = activeAssistantId ? this.tryGetMessage(activeAssistantId)?.message : void 0;
|
|
1059
|
+
const activeMessage = activeAssistantId ? this.session.tryGetMessage(activeAssistantId)?.message : void 0;
|
|
1161
1060
|
const activeAssistant = activeMessage?.role === "assistant" ? activeMessage : void 0;
|
|
1162
1061
|
const normalized = fromAgUiMessages(rawMessages, { showThinking: this.showThinking });
|
|
1163
1062
|
const converted = [];
|
|
@@ -1171,7 +1070,7 @@ var AgUiThreadRuntimeCore = class {
|
|
|
1171
1070
|
if (preservesActiveAssistant) converted.push(activeAssistant);
|
|
1172
1071
|
this.applyExternalMessages(converted);
|
|
1173
1072
|
if (activeAssistant !== void 0) {
|
|
1174
|
-
const activeItem = this.tryGetMessage(activeAssistant.id);
|
|
1073
|
+
const activeItem = this.session.tryGetMessage(activeAssistant.id);
|
|
1175
1074
|
if (activeItem) {
|
|
1176
1075
|
if (preservesActiveAssistant) this.snapshotHistoryIds.delete(activeAssistant.id);
|
|
1177
1076
|
this.markPendingAssistantHistory(activeAssistant.id, activeItem.parentId);
|
|
@@ -1205,7 +1104,7 @@ var AgUiThreadRuntimeCore = class {
|
|
|
1205
1104
|
if (!history) return;
|
|
1206
1105
|
const parentId = this.assistantHistoryParents.get(messageId);
|
|
1207
1106
|
if (parentId === void 0) return;
|
|
1208
|
-
const message = this.tryGetMessage(messageId)?.message;
|
|
1107
|
+
const message = this.session.tryGetMessage(messageId)?.message;
|
|
1209
1108
|
if (!message || message.role !== "assistant") return;
|
|
1210
1109
|
if (!this.isPersistableStatus(message.status)) return;
|
|
1211
1110
|
const wasPersisted = this.persistedHistoryIds.has(messageId);
|