@assistant-ui/react-ag-ui 0.0.43 → 0.0.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/agUiExtras.d.ts +0 -1
- package/dist/agUiExtras.d.ts.map +1 -1
- package/dist/hooks.d.ts +15 -9
- package/dist/hooks.d.ts.map +1 -1
- package/dist/hooks.js +17 -8
- package/dist/hooks.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/runtime/AgUiThreadRuntimeCore.d.ts +26 -4
- package/dist/runtime/AgUiThreadRuntimeCore.d.ts.map +1 -1
- package/dist/runtime/AgUiThreadRuntimeCore.js +296 -89
- package/dist/runtime/AgUiThreadRuntimeCore.js.map +1 -1
- package/dist/runtime/adapter/conversions.d.ts +0 -1
- package/dist/runtime/adapter/conversions.d.ts.map +1 -1
- package/dist/runtime/adapter/conversions.js +24 -5
- package/dist/runtime/adapter/conversions.js.map +1 -1
- package/dist/runtime/adapter/run-aggregator.d.ts +0 -1
- package/dist/runtime/adapter/run-aggregator.d.ts.map +1 -1
- package/dist/runtime/adapter/run-aggregator.js +36 -7
- package/dist/runtime/adapter/run-aggregator.js.map +1 -1
- package/dist/runtime/adapter/subscriber.d.ts +0 -1
- package/dist/runtime/adapter/subscriber.d.ts.map +1 -1
- package/dist/runtime/event-parser.d.ts +0 -1
- package/dist/runtime/event-parser.d.ts.map +1 -1
- package/dist/runtime/event-parser.js +18 -9
- package/dist/runtime/event-parser.js.map +1 -1
- package/dist/runtime/logger.d.ts.map +1 -1
- package/dist/runtime/types.d.ts +13 -1
- package/dist/runtime/types.d.ts.map +1 -1
- package/dist/useAgUiRuntime.d.ts +0 -1
- package/dist/useAgUiRuntime.d.ts.map +1 -1
- package/dist/useAgUiRuntime.js +8 -2
- package/dist/useAgUiRuntime.js.map +1 -1
- package/package.json +6 -6
- package/src/hooks.ts +29 -7
- package/src/index.ts +2 -0
- package/src/runtime/AgUiThreadRuntimeCore.ts +390 -108
- package/src/runtime/adapter/conversions.ts +71 -15
- package/src/runtime/adapter/run-aggregator.ts +69 -5
- package/src/runtime/types.ts +17 -0
- package/src/useAgUiRuntime.ts +8 -1
|
@@ -2,12 +2,14 @@
|
|
|
2
2
|
import { AG_UI_METADATA_NAMESPACE, RunAggregator, tryParseJSON } from "./adapter/run-aggregator.js";
|
|
3
3
|
import { fromAgUiMessages, toAgUiMessages, toAgUiTools } from "./adapter/conversions.js";
|
|
4
4
|
import { createAgUiSubscriber } from "./adapter/subscriber.js";
|
|
5
|
+
import { MessageRepository } from "@assistant-ui/core/internal";
|
|
5
6
|
import { fromThreadMessageLike, generateId } from "@assistant-ui/core";
|
|
6
7
|
import jsonpatch from "fast-json-patch";
|
|
7
8
|
//#region src/runtime/AgUiThreadRuntimeCore.ts
|
|
8
9
|
const optimisticPrefix = "__optimistic__";
|
|
9
10
|
const generateOptimisticId = () => `${optimisticPrefix}${generateId()}`;
|
|
10
11
|
const isOptimisticId = (id) => id.startsWith(optimisticPrefix);
|
|
12
|
+
const isResolvedToolCall = (part) => part.type === "tool-call" && "result" in part && part.result !== void 0;
|
|
11
13
|
const symbolResumeShim = Symbol("agui-resume-shim");
|
|
12
14
|
const FALLBACK_USER_STATUS = {
|
|
13
15
|
type: "complete",
|
|
@@ -17,11 +19,13 @@ var AgUiThreadRuntimeCore = class {
|
|
|
17
19
|
agent;
|
|
18
20
|
logger;
|
|
19
21
|
showThinking;
|
|
22
|
+
autoCancelPendingToolCalls;
|
|
20
23
|
onError;
|
|
21
24
|
onCancel;
|
|
22
25
|
notifyUpdate;
|
|
23
26
|
runtime;
|
|
24
|
-
|
|
27
|
+
repository = new MessageRepository();
|
|
28
|
+
exportedRepository;
|
|
25
29
|
isRunningFlag = false;
|
|
26
30
|
abortController = null;
|
|
27
31
|
stateSnapshot;
|
|
@@ -37,6 +41,7 @@ var AgUiThreadRuntimeCore = class {
|
|
|
37
41
|
this.agent = options.agent;
|
|
38
42
|
this.logger = options.logger;
|
|
39
43
|
this.showThinking = options.showThinking;
|
|
44
|
+
this.autoCancelPendingToolCalls = options.autoCancelPendingToolCalls;
|
|
40
45
|
this.onError = options.onError;
|
|
41
46
|
this.onCancel = options.onCancel;
|
|
42
47
|
this.history = options.history;
|
|
@@ -47,6 +52,7 @@ var AgUiThreadRuntimeCore = class {
|
|
|
47
52
|
this.agent = options.agent;
|
|
48
53
|
this.logger = options.logger;
|
|
49
54
|
this.showThinking = options.showThinking;
|
|
55
|
+
this.autoCancelPendingToolCalls = options.autoCancelPendingToolCalls;
|
|
50
56
|
this.onError = options.onError;
|
|
51
57
|
this.onCancel = options.onCancel;
|
|
52
58
|
this.history = options.history;
|
|
@@ -59,7 +65,74 @@ var AgUiThreadRuntimeCore = class {
|
|
|
59
65
|
this.runtime = void 0;
|
|
60
66
|
}
|
|
61
67
|
getMessages() {
|
|
62
|
-
return this.
|
|
68
|
+
return this.repository.getMessages();
|
|
69
|
+
}
|
|
70
|
+
getMessageRepository() {
|
|
71
|
+
if (this.exportedRepository) return this.exportedRepository;
|
|
72
|
+
const exportedRepository = this.repository.export();
|
|
73
|
+
let parentId = null;
|
|
74
|
+
for (const message of this.repository.getMessages()) {
|
|
75
|
+
if (message.metadata.isOptimistic) exportedRepository.messages.push({
|
|
76
|
+
parentId,
|
|
77
|
+
message
|
|
78
|
+
});
|
|
79
|
+
parentId = message.id;
|
|
80
|
+
}
|
|
81
|
+
this.exportedRepository = {
|
|
82
|
+
...exportedRepository,
|
|
83
|
+
headId: this.repository.headId
|
|
84
|
+
};
|
|
85
|
+
return this.exportedRepository;
|
|
86
|
+
}
|
|
87
|
+
tryGetMessage(messageId) {
|
|
88
|
+
try {
|
|
89
|
+
return this.repository.getMessage(messageId);
|
|
90
|
+
} catch {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
tryGetMessages(messageId) {
|
|
95
|
+
try {
|
|
96
|
+
return this.repository.getMessages(messageId);
|
|
97
|
+
} catch {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
hasMessage(messageId) {
|
|
102
|
+
return this.tryGetMessage(messageId) !== void 0;
|
|
103
|
+
}
|
|
104
|
+
addOrUpdateMessage(parentId, message) {
|
|
105
|
+
this.repository.addOrUpdateMessage(parentId, message);
|
|
106
|
+
this.exportedRepository = void 0;
|
|
107
|
+
}
|
|
108
|
+
deleteMessage(messageId, replacementId) {
|
|
109
|
+
this.repository.deleteMessage(messageId, replacementId);
|
|
110
|
+
this.exportedRepository = void 0;
|
|
111
|
+
}
|
|
112
|
+
tryDeleteMessage(messageId) {
|
|
113
|
+
if (!this.hasMessage(messageId)) return false;
|
|
114
|
+
this.deleteMessage(messageId);
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
117
|
+
switchToBranch(messageId) {
|
|
118
|
+
this.repository.switchToBranch(messageId);
|
|
119
|
+
this.exportedRepository = void 0;
|
|
120
|
+
}
|
|
121
|
+
resetRepositoryHead(messageId) {
|
|
122
|
+
this.repository.resetHead(messageId);
|
|
123
|
+
this.exportedRepository = void 0;
|
|
124
|
+
}
|
|
125
|
+
clearRepository() {
|
|
126
|
+
this.repository.clear();
|
|
127
|
+
this.exportedRepository = void 0;
|
|
128
|
+
}
|
|
129
|
+
updateMessage(messageId, updater) {
|
|
130
|
+
const item = this.tryGetMessage(messageId);
|
|
131
|
+
if (!item) return false;
|
|
132
|
+
const message = updater(item.message);
|
|
133
|
+
if (message === item.message) return false;
|
|
134
|
+
this.addOrUpdateMessage(item.parentId, message);
|
|
135
|
+
return true;
|
|
63
136
|
}
|
|
64
137
|
getState() {
|
|
65
138
|
return this.stateSnapshot;
|
|
@@ -76,11 +149,10 @@ var AgUiThreadRuntimeCore = class {
|
|
|
76
149
|
this._isLoading = true;
|
|
77
150
|
this._loadPromise = promise.then(async (repo) => {
|
|
78
151
|
if (!repo) return;
|
|
79
|
-
|
|
80
|
-
this.applyExternalMessages(messages);
|
|
152
|
+
this.applyExternalMessageRepository(repo);
|
|
81
153
|
if (repo.state !== void 0) this.loadExternalState(repo.state);
|
|
82
154
|
if (repo.unstable_resume) {
|
|
83
|
-
const parentId = repo.headId ??
|
|
155
|
+
const parentId = repo.headId ?? this.repository.headId;
|
|
84
156
|
const resumeStream = this.history?.resume?.bind(this.history);
|
|
85
157
|
await this.startRun(parentId, this.lastRunConfig, void 0, resumeStream);
|
|
86
158
|
}
|
|
@@ -96,18 +168,29 @@ var AgUiThreadRuntimeCore = class {
|
|
|
96
168
|
}
|
|
97
169
|
async append(message) {
|
|
98
170
|
const startRun = message.startRun ?? message.role === "user";
|
|
99
|
-
if (startRun)
|
|
171
|
+
if (startRun) {
|
|
172
|
+
this.assertNoPendingInterrupts();
|
|
173
|
+
this.maybeAutoCancelPendingToolCalls();
|
|
174
|
+
}
|
|
100
175
|
const threadMessageId = this.appendEntry(message);
|
|
101
176
|
if (!startRun) return;
|
|
102
177
|
await this.startRun(threadMessageId, message.runConfig);
|
|
103
178
|
}
|
|
179
|
+
maybeAutoCancelPendingToolCalls() {
|
|
180
|
+
if (this.autoCancelPendingToolCalls === false) return;
|
|
181
|
+
const pending = this.getPendingToolCalls();
|
|
182
|
+
if (!pending) return;
|
|
183
|
+
this.cancelUnresolvedToolCalls(pending.messageId);
|
|
184
|
+
this.maybeCompleteAfterToolResults(pending.messageId);
|
|
185
|
+
}
|
|
104
186
|
appendEntry(message) {
|
|
105
|
-
if (message.sourceId) this.
|
|
106
|
-
this.resetHead(message.parentId);
|
|
187
|
+
if (message.sourceId) this.tryDeleteMessage(message.sourceId);
|
|
107
188
|
const threadMessage = this.toThreadMessage(message);
|
|
108
|
-
this.
|
|
189
|
+
const parentId = message.parentId === null ? null : message.parentId && this.hasMessage(message.parentId) ? message.parentId : this.repository.headId;
|
|
190
|
+
this.addOrUpdateMessage(parentId, threadMessage);
|
|
191
|
+
this.switchToBranch(threadMessage.id);
|
|
109
192
|
this.notifyUpdate();
|
|
110
|
-
this.recordHistoryEntry(
|
|
193
|
+
this.recordHistoryEntry(parentId, threadMessage);
|
|
111
194
|
return threadMessage.id;
|
|
112
195
|
}
|
|
113
196
|
async edit(message) {
|
|
@@ -115,8 +198,7 @@ var AgUiThreadRuntimeCore = class {
|
|
|
115
198
|
}
|
|
116
199
|
async reload(parentId, config = {}) {
|
|
117
200
|
this.assertNoPendingInterrupts();
|
|
118
|
-
this.
|
|
119
|
-
this.notifyUpdate();
|
|
201
|
+
this.maybeAutoCancelPendingToolCalls();
|
|
120
202
|
await this.startRun(parentId, config.runConfig);
|
|
121
203
|
}
|
|
122
204
|
async cancel() {
|
|
@@ -144,9 +226,14 @@ var AgUiThreadRuntimeCore = class {
|
|
|
144
226
|
if (!this.getPendingInterrupts()) return;
|
|
145
227
|
throw new Error("[agui] cannot start a new run while interrupts are pending; resolve them with submitInterruptResponses()");
|
|
146
228
|
}
|
|
229
|
+
findRequiresActionAssistant(reason) {
|
|
230
|
+
const assistant = this.getMessages().findLast((message) => message.role === "assistant");
|
|
231
|
+
if (!assistant || assistant.status?.type !== "requires-action" || assistant.status.reason !== reason) return null;
|
|
232
|
+
return assistant;
|
|
233
|
+
}
|
|
147
234
|
getPendingInterrupts() {
|
|
148
|
-
const assistant = this.
|
|
149
|
-
if (!assistant
|
|
235
|
+
const assistant = this.findRequiresActionAssistant("interrupt");
|
|
236
|
+
if (!assistant) return null;
|
|
150
237
|
const stored = assistant.metadata.custom[AG_UI_METADATA_NAMESPACE]?.interrupts;
|
|
151
238
|
if (!stored?.length) return null;
|
|
152
239
|
return {
|
|
@@ -154,6 +241,21 @@ var AgUiThreadRuntimeCore = class {
|
|
|
154
241
|
interrupts: stored
|
|
155
242
|
};
|
|
156
243
|
}
|
|
244
|
+
getPendingToolCalls() {
|
|
245
|
+
const assistant = this.findRequiresActionAssistant("tool-calls");
|
|
246
|
+
if (!assistant) return null;
|
|
247
|
+
const toolCallIds = [];
|
|
248
|
+
for (const part of assistant.content) {
|
|
249
|
+
if (part.type !== "tool-call") continue;
|
|
250
|
+
if (isResolvedToolCall(part)) continue;
|
|
251
|
+
toolCallIds.push(part.toolCallId);
|
|
252
|
+
}
|
|
253
|
+
if (toolCallIds.length === 0) return null;
|
|
254
|
+
return {
|
|
255
|
+
messageId: assistant.id,
|
|
256
|
+
toolCallIds
|
|
257
|
+
};
|
|
258
|
+
}
|
|
157
259
|
async submitInterruptResponses(responses) {
|
|
158
260
|
const pending = this.getPendingInterrupts();
|
|
159
261
|
if (!pending) throw new Error("[agui] submitInterruptResponses: no pending interrupts on this thread");
|
|
@@ -185,6 +287,17 @@ var AgUiThreadRuntimeCore = class {
|
|
|
185
287
|
async steerAway(message, responses) {
|
|
186
288
|
const pending = this.getPendingInterrupts();
|
|
187
289
|
if (!pending) {
|
|
290
|
+
const pendingTools = this.getPendingToolCalls();
|
|
291
|
+
if (pendingTools) {
|
|
292
|
+
if (responses?.length) throw new Error("[agui] steerAway: responses are only valid for pending interrupts");
|
|
293
|
+
if (this.isRunningFlag) throw new Error("[agui] steerAway: a run is already in progress");
|
|
294
|
+
this.cancelUnresolvedToolCalls(pendingTools.messageId);
|
|
295
|
+
this.maybeCompleteAfterToolResults(pendingTools.messageId);
|
|
296
|
+
const normalized = this.toAppendMessage(message);
|
|
297
|
+
const threadMessageId = this.appendEntry(normalized);
|
|
298
|
+
await this.startRun(threadMessageId, normalized.runConfig);
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
188
301
|
if (responses?.length) throw new Error("[agui] steerAway: no pending interrupts on this thread");
|
|
189
302
|
await this.append(this.toAppendMessage(message));
|
|
190
303
|
return;
|
|
@@ -215,7 +328,7 @@ var AgUiThreadRuntimeCore = class {
|
|
|
215
328
|
toAppendMessage(message) {
|
|
216
329
|
if (typeof message === "string") return {
|
|
217
330
|
createdAt: /* @__PURE__ */ new Date(),
|
|
218
|
-
parentId: this.
|
|
331
|
+
parentId: this.repository.headId,
|
|
219
332
|
sourceId: null,
|
|
220
333
|
runConfig: {},
|
|
221
334
|
role: "user",
|
|
@@ -228,7 +341,7 @@ var AgUiThreadRuntimeCore = class {
|
|
|
228
341
|
};
|
|
229
342
|
return {
|
|
230
343
|
createdAt: message.createdAt ?? /* @__PURE__ */ new Date(),
|
|
231
|
-
parentId: message.parentId ?? this.
|
|
344
|
+
parentId: message.parentId ?? this.repository.headId,
|
|
232
345
|
sourceId: message.sourceId ?? null,
|
|
233
346
|
role: message.role ?? "user",
|
|
234
347
|
content: message.content,
|
|
@@ -239,16 +352,14 @@ var AgUiThreadRuntimeCore = class {
|
|
|
239
352
|
};
|
|
240
353
|
}
|
|
241
354
|
clearPendingInterrupts(messageId) {
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
if (message.id !== messageId || message.role !== "assistant") return message;
|
|
355
|
+
if (this.updateMessage(messageId, (message) => {
|
|
356
|
+
if (message.role !== "assistant") return message;
|
|
245
357
|
const assistant = message;
|
|
246
358
|
if (assistant.status?.type !== "requires-action" || assistant.status.reason !== "interrupt") return assistant;
|
|
247
|
-
touched = true;
|
|
248
359
|
const { interrupts: _drop, ...restAgui } = assistant.metadata.custom["agui"] ?? {};
|
|
249
360
|
const newCustom = { ...assistant.metadata.custom };
|
|
250
|
-
if (Object.keys(restAgui).length > 0) newCustom[
|
|
251
|
-
else delete newCustom[
|
|
361
|
+
if (Object.keys(restAgui).length > 0) newCustom["agui"] = restAgui;
|
|
362
|
+
else delete newCustom["agui"];
|
|
252
363
|
return {
|
|
253
364
|
...assistant,
|
|
254
365
|
status: {
|
|
@@ -260,26 +371,43 @@ var AgUiThreadRuntimeCore = class {
|
|
|
260
371
|
custom: newCustom
|
|
261
372
|
}
|
|
262
373
|
};
|
|
263
|
-
});
|
|
264
|
-
if (touched) this.notifyUpdate();
|
|
374
|
+
})) this.notifyUpdate();
|
|
265
375
|
}
|
|
266
376
|
findMessageIdForToolCall(toolCallId) {
|
|
267
377
|
let fallbackMessageId;
|
|
268
|
-
|
|
269
|
-
|
|
378
|
+
const messages = this.getMessages();
|
|
379
|
+
for (let index = messages.length - 1; index >= 0; index--) {
|
|
380
|
+
const message = messages[index];
|
|
270
381
|
if (!message || message.role !== "assistant") continue;
|
|
271
382
|
for (const part of message.content) {
|
|
272
383
|
if (part.type !== "tool-call" || part.toolCallId !== toolCallId) continue;
|
|
273
|
-
if (!(
|
|
384
|
+
if (!isResolvedToolCall(part)) return message.id;
|
|
274
385
|
fallbackMessageId ??= message.id;
|
|
275
386
|
}
|
|
276
387
|
}
|
|
277
388
|
return fallbackMessageId;
|
|
278
389
|
}
|
|
390
|
+
cancelUnresolvedToolCalls(messageId) {
|
|
391
|
+
if (this.updateMessage(messageId, (message) => {
|
|
392
|
+
if (message.role !== "assistant") return message;
|
|
393
|
+
const assistant = message;
|
|
394
|
+
const content = assistant.content.map((part) => {
|
|
395
|
+
if (part.type !== "tool-call" || isResolvedToolCall(part)) return part;
|
|
396
|
+
return {
|
|
397
|
+
...part,
|
|
398
|
+
result: { error: "Tool call cancelled by user" },
|
|
399
|
+
isError: true
|
|
400
|
+
};
|
|
401
|
+
});
|
|
402
|
+
return {
|
|
403
|
+
...assistant,
|
|
404
|
+
content
|
|
405
|
+
};
|
|
406
|
+
})) this.notifyUpdate();
|
|
407
|
+
}
|
|
279
408
|
addToolResult(options) {
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
if (message.id !== options.messageId || message.role !== "assistant") return message;
|
|
409
|
+
if (!this.updateMessage(options.messageId, (message) => {
|
|
410
|
+
if (message.role !== "assistant") return message;
|
|
283
411
|
const assistant = message;
|
|
284
412
|
let matchedToolCall = false;
|
|
285
413
|
const content = assistant.content.map((part) => {
|
|
@@ -293,13 +421,11 @@ var AgUiThreadRuntimeCore = class {
|
|
|
293
421
|
};
|
|
294
422
|
});
|
|
295
423
|
if (!matchedToolCall) return message;
|
|
296
|
-
updated = true;
|
|
297
424
|
return {
|
|
298
425
|
...assistant,
|
|
299
426
|
content
|
|
300
427
|
};
|
|
301
|
-
});
|
|
302
|
-
if (!updated) return;
|
|
428
|
+
})) return;
|
|
303
429
|
this.notifyUpdate();
|
|
304
430
|
this.maybeResumeAfterToolResults(options.messageId);
|
|
305
431
|
}
|
|
@@ -312,18 +438,18 @@ var AgUiThreadRuntimeCore = class {
|
|
|
312
438
|
this.startResumeRun(messageId);
|
|
313
439
|
}
|
|
314
440
|
maybeCompleteAfterToolResults(messageId) {
|
|
315
|
-
const message = this.
|
|
441
|
+
const message = this.tryGetMessage(messageId)?.message;
|
|
316
442
|
if (!message || message.role !== "assistant") return false;
|
|
317
443
|
const assistant = message;
|
|
318
444
|
if (assistant.status?.type !== "requires-action" || assistant.status.reason !== "tool-calls") return false;
|
|
319
|
-
if (!assistant.content.every((part) => part.type !== "tool-call" ||
|
|
320
|
-
|
|
321
|
-
...
|
|
445
|
+
if (!assistant.content.every((part) => part.type !== "tool-call" || isResolvedToolCall(part))) return false;
|
|
446
|
+
if (!this.updateMessage(messageId, (current) => current.role === "assistant" ? {
|
|
447
|
+
...current,
|
|
322
448
|
status: {
|
|
323
449
|
type: "complete",
|
|
324
450
|
reason: "unknown"
|
|
325
451
|
}
|
|
326
|
-
} :
|
|
452
|
+
} : current)) return false;
|
|
327
453
|
this.notifyUpdate();
|
|
328
454
|
this.persistAssistantHistory(messageId);
|
|
329
455
|
return true;
|
|
@@ -335,30 +461,122 @@ var AgUiThreadRuntimeCore = class {
|
|
|
335
461
|
}
|
|
336
462
|
applyExternalMessages(messages) {
|
|
337
463
|
this.assistantHistoryParents.clear();
|
|
338
|
-
|
|
464
|
+
if (messages.length === 0) this.clearRepository();
|
|
465
|
+
else {
|
|
466
|
+
let expectedParentId = null;
|
|
467
|
+
let lastAppliedId = null;
|
|
468
|
+
let hardReplace = false;
|
|
469
|
+
const seen = /* @__PURE__ */ new Set();
|
|
470
|
+
for (const message of messages) {
|
|
471
|
+
if (seen.has(message.id)) continue;
|
|
472
|
+
seen.add(message.id);
|
|
473
|
+
const existing = this.tryGetMessage(message.id);
|
|
474
|
+
if (existing && existing.parentId !== expectedParentId) {
|
|
475
|
+
hardReplace = true;
|
|
476
|
+
break;
|
|
477
|
+
}
|
|
478
|
+
this.addOrUpdateMessage(expectedParentId, message);
|
|
479
|
+
expectedParentId = message.id;
|
|
480
|
+
lastAppliedId = message.id;
|
|
481
|
+
}
|
|
482
|
+
if (hardReplace) {
|
|
483
|
+
this.clearRepository();
|
|
484
|
+
expectedParentId = null;
|
|
485
|
+
lastAppliedId = null;
|
|
486
|
+
seen.clear();
|
|
487
|
+
for (const message of messages) {
|
|
488
|
+
if (seen.has(message.id)) continue;
|
|
489
|
+
seen.add(message.id);
|
|
490
|
+
this.addOrUpdateMessage(expectedParentId, message);
|
|
491
|
+
expectedParentId = message.id;
|
|
492
|
+
lastAppliedId = message.id;
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
this.resetRepositoryHead(lastAppliedId);
|
|
496
|
+
}
|
|
497
|
+
this.recordedHistoryIds.clear();
|
|
498
|
+
for (const { message } of this.getMessageRepository().messages) this.recordedHistoryIds.add(message.id);
|
|
499
|
+
this.notifyUpdate();
|
|
500
|
+
}
|
|
501
|
+
applyExternalMessageRepository(loaded) {
|
|
502
|
+
const headId = loaded.headId ?? loaded.messages.at(-1)?.message.id ?? null;
|
|
503
|
+
const ids = /* @__PURE__ */ new Set();
|
|
504
|
+
let degenerate = false;
|
|
505
|
+
for (const { message } of loaded.messages) {
|
|
506
|
+
if (ids.has(message.id)) {
|
|
507
|
+
degenerate = true;
|
|
508
|
+
break;
|
|
509
|
+
}
|
|
510
|
+
ids.add(message.id);
|
|
511
|
+
}
|
|
512
|
+
if (headId !== null && !ids.has(headId)) degenerate = true;
|
|
513
|
+
if (!degenerate) {
|
|
514
|
+
this.clearRepository();
|
|
515
|
+
let pending = [...loaded.messages];
|
|
516
|
+
const importedIds = /* @__PURE__ */ new Set();
|
|
517
|
+
while (pending.length > 0) {
|
|
518
|
+
const unresolved = [];
|
|
519
|
+
let progressed = false;
|
|
520
|
+
for (const item of pending) {
|
|
521
|
+
if (item.parentId !== null && !importedIds.has(item.parentId)) {
|
|
522
|
+
unresolved.push(item);
|
|
523
|
+
continue;
|
|
524
|
+
}
|
|
525
|
+
this.addOrUpdateMessage(item.parentId, item.message);
|
|
526
|
+
importedIds.add(item.message.id);
|
|
527
|
+
progressed = true;
|
|
528
|
+
}
|
|
529
|
+
if (!progressed) {
|
|
530
|
+
degenerate = true;
|
|
531
|
+
break;
|
|
532
|
+
}
|
|
533
|
+
pending = unresolved;
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
if (degenerate) {
|
|
537
|
+
this.clearRepository();
|
|
538
|
+
let previousId = null;
|
|
539
|
+
for (const { message } of loaded.messages) {
|
|
540
|
+
const existing = this.tryGetMessage(message.id);
|
|
541
|
+
this.addOrUpdateMessage(existing ? existing.parentId : previousId, message);
|
|
542
|
+
previousId = message.id;
|
|
543
|
+
}
|
|
544
|
+
this.resetRepositoryHead(previousId);
|
|
545
|
+
} else this.resetRepositoryHead(headId);
|
|
546
|
+
this.assistantHistoryParents.clear();
|
|
339
547
|
this.recordedHistoryIds.clear();
|
|
340
|
-
for (const message of
|
|
548
|
+
for (const { message } of loaded.messages) this.recordedHistoryIds.add(message.id);
|
|
341
549
|
this.notifyUpdate();
|
|
342
550
|
}
|
|
343
551
|
loadExternalState(state) {
|
|
344
552
|
this.stateSnapshot = state;
|
|
345
553
|
this.notifyUpdate();
|
|
346
554
|
}
|
|
555
|
+
setState(next) {
|
|
556
|
+
this.stateSnapshot = typeof next === "function" ? next(this.stateSnapshot) : next;
|
|
557
|
+
this.notifyUpdate();
|
|
558
|
+
}
|
|
559
|
+
resetState() {
|
|
560
|
+
this.stateSnapshot = void 0;
|
|
561
|
+
this.notifyUpdate();
|
|
562
|
+
}
|
|
347
563
|
async startRun(parentId, runConfig, resume, resumeStream) {
|
|
348
564
|
const normalizedRunConfig = runConfig ?? {};
|
|
349
565
|
this.lastRunConfig = normalizedRunConfig;
|
|
350
|
-
this.
|
|
351
|
-
const
|
|
566
|
+
const parent = parentId === null ? void 0 : this.tryGetMessage(parentId);
|
|
567
|
+
const shouldEagerlyInsertAssistant = parentId !== null && parent !== void 0 && parentId !== this.repository.headId;
|
|
568
|
+
const historicalMessages = [...shouldEagerlyInsertAssistant ? this.tryGetMessages(parentId) ?? this.repository.getMessages() : this.repository.getMessages()];
|
|
352
569
|
this.pendingError = null;
|
|
353
|
-
const assistantParentId = parentId
|
|
570
|
+
const assistantParentId = parent ? parentId : this.repository.headId;
|
|
354
571
|
let assistantMessageId;
|
|
355
572
|
const ensureAssistant = () => {
|
|
356
573
|
if (assistantMessageId) return assistantMessageId;
|
|
357
|
-
const created = this.insertAssistantPlaceholder();
|
|
574
|
+
const created = this.insertAssistantPlaceholder(assistantParentId ?? null);
|
|
358
575
|
assistantMessageId = created;
|
|
359
576
|
this.markPendingAssistantHistory(created, assistantParentId ?? null);
|
|
360
577
|
return created;
|
|
361
578
|
};
|
|
579
|
+
if (shouldEagerlyInsertAssistant) ensureAssistant();
|
|
362
580
|
const applyUpdate = (update) => {
|
|
363
581
|
const resolved = this.updateAssistantMessage(ensureAssistant(), update);
|
|
364
582
|
if (resolved !== assistantMessageId) assistantMessageId = resolved;
|
|
@@ -370,8 +588,7 @@ var AgUiThreadRuntimeCore = class {
|
|
|
370
588
|
onServerMessageId: (serverId) => {
|
|
371
589
|
const placeholder = ensureAssistant();
|
|
372
590
|
if (placeholder === serverId) return;
|
|
373
|
-
this.reassignAssistantId(placeholder, serverId);
|
|
374
|
-
assistantMessageId = serverId;
|
|
591
|
+
if (this.reassignAssistantId(placeholder, serverId)) assistantMessageId = serverId;
|
|
375
592
|
}
|
|
376
593
|
});
|
|
377
594
|
const dispatch = (event) => this.handleEvent(aggregator, event);
|
|
@@ -461,7 +678,7 @@ var AgUiThreadRuntimeCore = class {
|
|
|
461
678
|
unstable_threadId: ctx.threadId,
|
|
462
679
|
unstable_parentId: ctx.parentId,
|
|
463
680
|
unstable_getMessage: () => {
|
|
464
|
-
const message = this.
|
|
681
|
+
const message = this.tryGetMessage(currentId())?.message;
|
|
465
682
|
if (!message) throw new Error("[agui] resume stream requested the assistant message before it existed");
|
|
466
683
|
return message;
|
|
467
684
|
}
|
|
@@ -484,7 +701,7 @@ var AgUiThreadRuntimeCore = class {
|
|
|
484
701
|
return;
|
|
485
702
|
}
|
|
486
703
|
if (ctx.abortSignal.aborted) return;
|
|
487
|
-
const current = this.
|
|
704
|
+
const current = this.tryGetMessage(currentId())?.message;
|
|
488
705
|
if (!current || current.status?.type === "running") ctx.applyUpdate({ status: {
|
|
489
706
|
type: "complete",
|
|
490
707
|
reason: "unknown"
|
|
@@ -492,7 +709,7 @@ var AgUiThreadRuntimeCore = class {
|
|
|
492
709
|
}
|
|
493
710
|
buildRunInput(runId, runConfig, historyMessages, resume) {
|
|
494
711
|
const threadId = this.agent.threadId || "main";
|
|
495
|
-
const messages = toAgUiMessages(historyMessages ?? this.
|
|
712
|
+
const messages = toAgUiMessages(historyMessages ?? this.repository.getMessages());
|
|
496
713
|
const context = this.runtime?.thread.getModelContext();
|
|
497
714
|
return {
|
|
498
715
|
threadId,
|
|
@@ -535,7 +752,7 @@ var AgUiThreadRuntimeCore = class {
|
|
|
535
752
|
if (this.abortController === controller) this.abortController = null;
|
|
536
753
|
this.setRunning(false);
|
|
537
754
|
}
|
|
538
|
-
insertAssistantPlaceholder() {
|
|
755
|
+
insertAssistantPlaceholder(parentId) {
|
|
539
756
|
const id = generateOptimisticId();
|
|
540
757
|
const assistant = {
|
|
541
758
|
id,
|
|
@@ -552,44 +769,48 @@ var AgUiThreadRuntimeCore = class {
|
|
|
552
769
|
custom: {}
|
|
553
770
|
}
|
|
554
771
|
};
|
|
555
|
-
this.
|
|
772
|
+
this.addOrUpdateMessage(parentId ?? this.repository.headId, assistant);
|
|
773
|
+
this.switchToBranch(id);
|
|
556
774
|
this.notifyUpdate();
|
|
557
775
|
return id;
|
|
558
776
|
}
|
|
559
777
|
reassignAssistantId(oldId, newId) {
|
|
560
|
-
if (oldId === newId) return;
|
|
561
|
-
|
|
562
|
-
|
|
778
|
+
if (oldId === newId) return true;
|
|
779
|
+
const oldItem = this.tryGetMessage(oldId);
|
|
780
|
+
if (!oldItem) return false;
|
|
781
|
+
const collidesWithExisting = this.hasMessage(newId);
|
|
782
|
+
if (collidesWithExisting) {
|
|
783
|
+
this.logger.debug?.("[agui] reassignAssistantId: server id already present in messages or repository, dropping placeholder", {
|
|
563
784
|
oldId,
|
|
564
785
|
newId
|
|
565
786
|
});
|
|
566
|
-
this.
|
|
567
|
-
} else
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
...m,
|
|
787
|
+
this.deleteMessage(oldId, oldItem.parentId ?? null);
|
|
788
|
+
} else {
|
|
789
|
+
const { isOptimistic: _, ...metadata } = oldItem.message.metadata;
|
|
790
|
+
this.addOrUpdateMessage(oldItem.parentId, {
|
|
791
|
+
...oldItem.message,
|
|
572
792
|
id: newId,
|
|
573
793
|
metadata
|
|
574
|
-
};
|
|
575
|
-
|
|
794
|
+
});
|
|
795
|
+
this.switchToBranch(newId);
|
|
796
|
+
this.tryDeleteMessage(oldId);
|
|
797
|
+
}
|
|
576
798
|
const pendingParent = this.assistantHistoryParents.get(oldId);
|
|
577
799
|
if (pendingParent !== void 0) {
|
|
578
800
|
this.assistantHistoryParents.delete(oldId);
|
|
579
|
-
if (!this.assistantHistoryParents.has(newId)) this.assistantHistoryParents.set(newId, pendingParent);
|
|
801
|
+
if (!collidesWithExisting && !this.assistantHistoryParents.has(newId)) this.assistantHistoryParents.set(newId, pendingParent);
|
|
580
802
|
}
|
|
581
803
|
if (this.recordedHistoryIds.has(oldId)) {
|
|
582
804
|
this.recordedHistoryIds.delete(oldId);
|
|
583
|
-
this.recordedHistoryIds.add(newId);
|
|
805
|
+
if (!collidesWithExisting) this.recordedHistoryIds.add(newId);
|
|
584
806
|
}
|
|
585
807
|
this.notifyUpdate();
|
|
808
|
+
return !collidesWithExisting;
|
|
586
809
|
}
|
|
587
810
|
updateAssistantMessage(messageId, update) {
|
|
588
|
-
let touched = false;
|
|
589
811
|
let latestStatus;
|
|
590
|
-
|
|
591
|
-
if (message.
|
|
592
|
-
touched = true;
|
|
812
|
+
if (!this.updateMessage(messageId, (message) => {
|
|
813
|
+
if (message.role !== "assistant") return message;
|
|
593
814
|
const assistant = message;
|
|
594
815
|
const metadata = update.metadata ? this.mergeAssistantMetadata(assistant.metadata, update.metadata) : assistant.metadata;
|
|
595
816
|
latestStatus = update.status ?? assistant.status;
|
|
@@ -600,8 +821,7 @@ var AgUiThreadRuntimeCore = class {
|
|
|
600
821
|
status: latestStatus,
|
|
601
822
|
metadata
|
|
602
823
|
};
|
|
603
|
-
});
|
|
604
|
-
if (!touched) return messageId;
|
|
824
|
+
})) return messageId;
|
|
605
825
|
let resolvedMessageId = messageId;
|
|
606
826
|
if (latestStatus !== void 0 && latestStatus.type !== "running" && isOptimisticId(messageId)) {
|
|
607
827
|
const stableId = generateId();
|
|
@@ -614,12 +834,11 @@ var AgUiThreadRuntimeCore = class {
|
|
|
614
834
|
}
|
|
615
835
|
preserveToolResults(previous, next) {
|
|
616
836
|
const resolved = /* @__PURE__ */ new Map();
|
|
617
|
-
for (const part of previous) if (part.type === "tool-call" &&
|
|
837
|
+
for (const part of previous) if (part.type === "tool-call" && isResolvedToolCall(part)) resolved.set(part.toolCallId, part);
|
|
618
838
|
if (resolved.size === 0) return next;
|
|
619
839
|
let changed = false;
|
|
620
840
|
const merged = next.map((part) => {
|
|
621
|
-
if (part.type !== "tool-call") return part;
|
|
622
|
-
if ("result" in part && part.result !== void 0) return part;
|
|
841
|
+
if (part.type !== "tool-call" || isResolvedToolCall(part)) return part;
|
|
623
842
|
const prior = resolved.get(part.toolCallId);
|
|
624
843
|
if (!prior) return part;
|
|
625
844
|
changed = true;
|
|
@@ -683,9 +902,8 @@ var AgUiThreadRuntimeCore = class {
|
|
|
683
902
|
}
|
|
684
903
|
}
|
|
685
904
|
applyCrossRunToolResult(messageId, event) {
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
if (message.id !== messageId || message.role !== "assistant") return message;
|
|
905
|
+
if (!this.updateMessage(messageId, (message) => {
|
|
906
|
+
if (message.role !== "assistant") return message;
|
|
689
907
|
const assistant = message;
|
|
690
908
|
let matchedToolCall = false;
|
|
691
909
|
const content = assistant.content.map((part) => {
|
|
@@ -699,13 +917,11 @@ var AgUiThreadRuntimeCore = class {
|
|
|
699
917
|
};
|
|
700
918
|
});
|
|
701
919
|
if (!matchedToolCall) return message;
|
|
702
|
-
updated = true;
|
|
703
920
|
return {
|
|
704
921
|
...assistant,
|
|
705
922
|
content
|
|
706
923
|
};
|
|
707
|
-
});
|
|
708
|
-
if (!updated) return;
|
|
924
|
+
})) return;
|
|
709
925
|
this.notifyUpdate();
|
|
710
926
|
this.maybeCompleteAfterToolResults(messageId);
|
|
711
927
|
}
|
|
@@ -726,15 +942,6 @@ var AgUiThreadRuntimeCore = class {
|
|
|
726
942
|
toThreadMessage(message) {
|
|
727
943
|
return fromThreadMessageLike(message, generateId(), FALLBACK_USER_STATUS);
|
|
728
944
|
}
|
|
729
|
-
resetHead(parentId) {
|
|
730
|
-
if (!parentId) {
|
|
731
|
-
if (this.messages.length) this.messages = [];
|
|
732
|
-
return;
|
|
733
|
-
}
|
|
734
|
-
const idx = this.messages.findIndex((message) => message.id === parentId);
|
|
735
|
-
if (idx === -1) return;
|
|
736
|
-
this.messages = this.messages.slice(0, idx + 1);
|
|
737
|
-
}
|
|
738
945
|
isTerminalStatus(status) {
|
|
739
946
|
return status?.type === "complete" || status?.type === "incomplete";
|
|
740
947
|
}
|
|
@@ -753,7 +960,7 @@ var AgUiThreadRuntimeCore = class {
|
|
|
753
960
|
if (!this.history) return;
|
|
754
961
|
const parentId = this.assistantHistoryParents.get(messageId);
|
|
755
962
|
if (parentId === void 0) return;
|
|
756
|
-
const message = this.
|
|
963
|
+
const message = this.tryGetMessage(messageId)?.message;
|
|
757
964
|
if (!message || message.role !== "assistant") return;
|
|
758
965
|
if (!this.isPersistableStatus(message.status)) return;
|
|
759
966
|
this.assistantHistoryParents.delete(messageId);
|