@assistant-ui/react-ag-ui 0.0.54 → 0.0.57
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 +2 -2
- package/dist/index.js +2 -2
- package/dist/runtime/AgUiThreadRuntimeCore.d.ts +25 -15
- package/dist/runtime/AgUiThreadRuntimeCore.d.ts.map +1 -1
- package/dist/runtime/AgUiThreadRuntimeCore.js +257 -210
- package/dist/runtime/AgUiThreadRuntimeCore.js.map +1 -1
- package/dist/runtime/adapter/conversions.d.ts +13 -3
- package/dist/runtime/adapter/conversions.d.ts.map +1 -1
- package/dist/runtime/adapter/conversions.js +77 -39
- package/dist/runtime/adapter/conversions.js.map +1 -1
- package/dist/runtime/adapter/run-aggregator.d.ts +23 -2
- package/dist/runtime/adapter/run-aggregator.d.ts.map +1 -1
- package/dist/runtime/adapter/run-aggregator.js +155 -41
- package/dist/runtime/adapter/run-aggregator.js.map +1 -1
- package/dist/runtime/adapter/subscriber.d.ts +3 -0
- package/dist/runtime/adapter/subscriber.d.ts.map +1 -1
- package/dist/runtime/adapter/subscriber.js +9 -0
- package/dist/runtime/adapter/subscriber.js.map +1 -1
- package/dist/runtime/adapter/tool-approval.d.ts +56 -0
- package/dist/runtime/adapter/tool-approval.d.ts.map +1 -0
- package/dist/runtime/adapter/tool-approval.js +244 -0
- package/dist/runtime/adapter/tool-approval.js.map +1 -0
- package/dist/runtime/event-parser.d.ts.map +1 -1
- package/dist/runtime/event-parser.js +2 -1
- package/dist/runtime/event-parser.js.map +1 -1
- package/dist/runtime/interrupt-internals.d.ts +8 -0
- package/dist/runtime/interrupt-internals.d.ts.map +1 -0
- package/dist/runtime/interrupt-internals.js +25 -0
- package/dist/runtime/interrupt-internals.js.map +1 -0
- package/dist/useAgUiRuntime.js +14 -9
- package/dist/useAgUiRuntime.js.map +1 -1
- package/package.json +9 -9
- package/src/index.ts +8 -2
- package/src/runtime/AgUiThreadRuntimeCore.test.ts +105 -0
- package/src/runtime/AgUiThreadRuntimeCore.ts +436 -272
- package/src/runtime/adapter/conversions.content.test.ts +61 -3
- package/src/runtime/adapter/conversions.ts +123 -57
- package/src/runtime/adapter/run-aggregator.approval.test.ts +103 -0
- package/src/runtime/adapter/run-aggregator.ts +234 -47
- package/src/runtime/adapter/subscriber.ts +14 -0
- package/src/runtime/adapter/tool-approval.test.ts +681 -0
- package/src/runtime/adapter/tool-approval.ts +366 -0
- package/src/runtime/event-parser.ts +10 -1
- package/src/runtime/interrupt-internals.ts +35 -0
- package/src/useAgUiRuntime.approval.test.tsx +885 -0
- package/src/useAgUiRuntime.history.test.tsx +76 -0
- package/src/useAgUiRuntime.ts +15 -9
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { readMcpAppResourceUri } from "./mcp-tool-result.js";
|
|
3
|
+
import { buildToolApprovalResume, projectAgUiToolApprovals, withSettledToolApprovals, withToolApprovalDecision } from "./adapter/tool-approval.js";
|
|
3
4
|
import { AG_UI_METADATA_NAMESPACE, RunAggregator, isPlainObject, tryParseJSON } from "./adapter/run-aggregator.js";
|
|
4
5
|
import { fromAgUiMessages, toAgUiMessages, toAgUiTools } from "./adapter/conversions.js";
|
|
5
6
|
import { createAgUiSubscriber } from "./adapter/subscriber.js";
|
|
6
7
|
import { fromThreadMessageLike, generateId, isMcpAppUri } from "@assistant-ui/core";
|
|
7
|
-
import {
|
|
8
|
+
import { createMessageRepositorySession, invokeUserCallback } from "@assistant-ui/core/internal";
|
|
8
9
|
import jsonpatch from "fast-json-patch";
|
|
9
10
|
//#region src/runtime/AgUiThreadRuntimeCore.ts
|
|
10
11
|
const optimisticPrefix = "__optimistic__";
|
|
@@ -16,19 +17,8 @@ const FALLBACK_USER_STATUS = {
|
|
|
16
17
|
type: "complete",
|
|
17
18
|
reason: "unknown"
|
|
18
19
|
};
|
|
19
|
-
const reportCallbackError = (name, error) => {
|
|
20
|
-
console.error(`[react-ag-ui] ${name} callback threw an error`, error);
|
|
21
|
-
};
|
|
22
20
|
const invokeRuntimeCallback = (name, callback, ...args) => {
|
|
23
|
-
|
|
24
|
-
try {
|
|
25
|
-
const result = callback(...args);
|
|
26
|
-
if (result !== null && (typeof result === "object" || typeof result === "function") && "then" in result && typeof result.then === "function") Promise.resolve(result).catch((error) => {
|
|
27
|
-
reportCallbackError(name, error);
|
|
28
|
-
});
|
|
29
|
-
} catch (error) {
|
|
30
|
-
reportCallbackError(name, error);
|
|
31
|
-
}
|
|
21
|
+
invokeUserCallback("react-ag-ui", name, callback, ...args);
|
|
32
22
|
};
|
|
33
23
|
const mergeAgUiNamespace = (current, incoming) => {
|
|
34
24
|
const merged = {
|
|
@@ -51,9 +41,22 @@ var AgUiThreadRuntimeCore = class {
|
|
|
51
41
|
onError;
|
|
52
42
|
onCancel;
|
|
53
43
|
notifyUpdate;
|
|
44
|
+
reportedErrors = /* @__PURE__ */ new WeakSet();
|
|
54
45
|
runtime;
|
|
55
|
-
|
|
56
|
-
|
|
46
|
+
session = createMessageRepositorySession({ decorateExport: (exported, repository) => {
|
|
47
|
+
let parentId = null;
|
|
48
|
+
for (const message of repository.getMessages()) {
|
|
49
|
+
if (message.metadata.isOptimistic) exported.messages.push({
|
|
50
|
+
parentId,
|
|
51
|
+
message
|
|
52
|
+
});
|
|
53
|
+
parentId = message.id;
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
...exported,
|
|
57
|
+
headId: repository.headId
|
|
58
|
+
};
|
|
59
|
+
} });
|
|
57
60
|
isRunningFlag = false;
|
|
58
61
|
abortController = null;
|
|
59
62
|
activeRunAgent = null;
|
|
@@ -62,9 +65,12 @@ var AgUiThreadRuntimeCore = class {
|
|
|
62
65
|
history;
|
|
63
66
|
lastRunConfig;
|
|
64
67
|
assistantHistoryParents = /* @__PURE__ */ new Map();
|
|
65
|
-
|
|
68
|
+
snapshotHistoryIds = /* @__PURE__ */ new Set();
|
|
69
|
+
persistedHistoryIds = /* @__PURE__ */ new Set();
|
|
70
|
+
historyWrites = /* @__PURE__ */ new Map();
|
|
66
71
|
_isLoading = false;
|
|
67
72
|
_loadPromise;
|
|
73
|
+
_loadRequested = false;
|
|
68
74
|
pendingResumeMessageId = null;
|
|
69
75
|
pendingA2uiResume = false;
|
|
70
76
|
pendingA2uiAction;
|
|
@@ -86,84 +92,25 @@ var AgUiThreadRuntimeCore = class {
|
|
|
86
92
|
this.autoCancelPendingToolCalls = options.autoCancelPendingToolCalls;
|
|
87
93
|
this.onError = options.onError;
|
|
88
94
|
this.onCancel = options.onCancel;
|
|
95
|
+
const previousHistory = this.history;
|
|
89
96
|
this.history = options.history;
|
|
90
97
|
this.installResumeShim();
|
|
98
|
+
if (this._loadRequested && !this._loadPromise && !previousHistory && options.history && this.session.getMessages().length === 0) this.__internal_load();
|
|
91
99
|
}
|
|
92
100
|
attachRuntime(runtime) {
|
|
93
101
|
this.runtime = runtime;
|
|
94
102
|
}
|
|
95
103
|
detachRuntime() {
|
|
96
104
|
this.runtime = void 0;
|
|
105
|
+
this.cancel().catch((error) => {
|
|
106
|
+
this.logger.error("[agui] failed to cancel run during teardown", error);
|
|
107
|
+
});
|
|
97
108
|
}
|
|
98
109
|
getMessages() {
|
|
99
|
-
return this.
|
|
110
|
+
return this.session.getMessages();
|
|
100
111
|
}
|
|
101
112
|
getMessageRepository() {
|
|
102
|
-
|
|
103
|
-
const exportedRepository = this.repository.export();
|
|
104
|
-
let parentId = null;
|
|
105
|
-
for (const message of this.repository.getMessages()) {
|
|
106
|
-
if (message.metadata.isOptimistic) exportedRepository.messages.push({
|
|
107
|
-
parentId,
|
|
108
|
-
message
|
|
109
|
-
});
|
|
110
|
-
parentId = message.id;
|
|
111
|
-
}
|
|
112
|
-
this.exportedRepository = {
|
|
113
|
-
...exportedRepository,
|
|
114
|
-
headId: this.repository.headId
|
|
115
|
-
};
|
|
116
|
-
return this.exportedRepository;
|
|
117
|
-
}
|
|
118
|
-
tryGetMessage(messageId) {
|
|
119
|
-
try {
|
|
120
|
-
return this.repository.getMessage(messageId);
|
|
121
|
-
} catch {
|
|
122
|
-
return;
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
tryGetMessages(messageId) {
|
|
126
|
-
try {
|
|
127
|
-
return this.repository.getMessages(messageId);
|
|
128
|
-
} catch {
|
|
129
|
-
return;
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
hasMessage(messageId) {
|
|
133
|
-
return this.tryGetMessage(messageId) !== void 0;
|
|
134
|
-
}
|
|
135
|
-
addOrUpdateMessage(parentId, message) {
|
|
136
|
-
this.repository.addOrUpdateMessage(parentId, message);
|
|
137
|
-
this.exportedRepository = void 0;
|
|
138
|
-
}
|
|
139
|
-
deleteMessage(messageId, replacementId) {
|
|
140
|
-
this.repository.deleteMessage(messageId, replacementId);
|
|
141
|
-
this.exportedRepository = void 0;
|
|
142
|
-
}
|
|
143
|
-
tryDeleteMessage(messageId) {
|
|
144
|
-
if (!this.hasMessage(messageId)) return false;
|
|
145
|
-
this.deleteMessage(messageId);
|
|
146
|
-
return true;
|
|
147
|
-
}
|
|
148
|
-
switchToBranch(messageId) {
|
|
149
|
-
this.repository.switchToBranch(messageId);
|
|
150
|
-
this.exportedRepository = void 0;
|
|
151
|
-
}
|
|
152
|
-
resetRepositoryHead(messageId) {
|
|
153
|
-
this.repository.resetHead(messageId);
|
|
154
|
-
this.exportedRepository = void 0;
|
|
155
|
-
}
|
|
156
|
-
clearRepository() {
|
|
157
|
-
this.repository.clear();
|
|
158
|
-
this.exportedRepository = void 0;
|
|
159
|
-
}
|
|
160
|
-
updateMessage(messageId, updater) {
|
|
161
|
-
const item = this.tryGetMessage(messageId);
|
|
162
|
-
if (!item) return false;
|
|
163
|
-
const message = updater(item.message);
|
|
164
|
-
if (message === item.message) return false;
|
|
165
|
-
this.addOrUpdateMessage(item.parentId, message);
|
|
166
|
-
return true;
|
|
113
|
+
return this.session.export();
|
|
167
114
|
}
|
|
168
115
|
getState() {
|
|
169
116
|
return this.stateSnapshot;
|
|
@@ -175,15 +122,22 @@ var AgUiThreadRuntimeCore = class {
|
|
|
175
122
|
return this._isLoading;
|
|
176
123
|
}
|
|
177
124
|
__internal_load() {
|
|
125
|
+
this._loadRequested = true;
|
|
178
126
|
if (this._loadPromise) return this._loadPromise;
|
|
179
|
-
|
|
127
|
+
if (!this.history) return Promise.resolve();
|
|
128
|
+
const promise = this.history.load();
|
|
180
129
|
this._isLoading = true;
|
|
181
130
|
this._loadPromise = promise.then(async (repo) => {
|
|
182
131
|
if (!repo) return;
|
|
183
|
-
this.applyExternalMessageRepository(repo);
|
|
132
|
+
this.session.applyExternalMessageRepository(repo);
|
|
133
|
+
this.assistantHistoryParents.clear();
|
|
134
|
+
this.snapshotHistoryIds.clear();
|
|
135
|
+
this.persistedHistoryIds.clear();
|
|
136
|
+
for (const { message } of repo.messages) this.persistedHistoryIds.add(message.id);
|
|
137
|
+
this.notifyUpdate();
|
|
184
138
|
if (repo.state !== void 0) this.loadExternalState(repo.state);
|
|
185
139
|
if (repo.unstable_resume) {
|
|
186
|
-
const parentId = repo.headId ?? this.
|
|
140
|
+
const parentId = repo.headId ?? this.session.headId;
|
|
187
141
|
const resumeStream = this.history?.resume?.bind(this.history);
|
|
188
142
|
await this.startRun(parentId, this.lastRunConfig, void 0, resumeStream);
|
|
189
143
|
}
|
|
@@ -215,11 +169,11 @@ var AgUiThreadRuntimeCore = class {
|
|
|
215
169
|
this.maybeCompleteAfterToolResults(pending.messageId);
|
|
216
170
|
}
|
|
217
171
|
appendEntry(message) {
|
|
218
|
-
if (message.sourceId) this.tryDeleteMessage(message.sourceId);
|
|
172
|
+
if (message.sourceId) this.session.tryDeleteMessage(message.sourceId);
|
|
219
173
|
const threadMessage = this.toThreadMessage(message);
|
|
220
|
-
const parentId = message.parentId === null ? null : message.parentId && this.hasMessage(message.parentId) ? message.parentId : this.
|
|
221
|
-
this.addOrUpdateMessage(parentId, threadMessage);
|
|
222
|
-
this.switchToBranch(threadMessage.id);
|
|
174
|
+
const parentId = message.parentId === null ? null : message.parentId && this.session.hasMessage(message.parentId) ? message.parentId : this.session.headId;
|
|
175
|
+
this.session.addOrUpdateMessage(parentId, threadMessage);
|
|
176
|
+
this.session.switchToBranch(threadMessage.id);
|
|
223
177
|
this.notifyUpdate();
|
|
224
178
|
this.recordHistoryEntry(parentId, threadMessage);
|
|
225
179
|
return threadMessage.id;
|
|
@@ -298,17 +252,65 @@ var AgUiThreadRuntimeCore = class {
|
|
|
298
252
|
const openIds = pending.interrupts.map((i) => i.id);
|
|
299
253
|
const missing = openIds.filter((id) => !responsesById.has(id));
|
|
300
254
|
if (missing.length > 0) throw new Error(`[agui] submitInterruptResponses: missing responses for open interrupts: ${missing.join(", ")}`);
|
|
255
|
+
this.assertInterruptsAnswerable("submitInterruptResponses", pending.interrupts);
|
|
256
|
+
await this.resumeWithResponses(pending.messageId, openIds.map((id) => responsesById.get(id)));
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Resumes with an already validated resume array. Answerability is checked
|
|
260
|
+
* once per submission: re-checking here would let a clock crossing reject a
|
|
261
|
+
* decision this runtime has already recorded, stranding the gate decided and
|
|
262
|
+
* unretryable.
|
|
263
|
+
*/
|
|
264
|
+
async resumeWithResponses(messageId, resume) {
|
|
265
|
+
this.clearPendingInterrupts(messageId, resume);
|
|
266
|
+
await this.startRun(messageId, this.lastRunConfig, resume);
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* The core seams that reach `respondToToolApproval` discard the promise they
|
|
270
|
+
* receive, so a rejected decision would otherwise surface only as an
|
|
271
|
+
* unhandled rejection. A failure raised by the resumed run itself was already
|
|
272
|
+
* reported by `startRun` before it rethrew, so reporting it here again would
|
|
273
|
+
* give the consumer two notifications for one failure.
|
|
274
|
+
*/
|
|
275
|
+
reportError(error) {
|
|
276
|
+
if (this.reportedErrors.has(error)) {
|
|
277
|
+
this.reportedErrors.delete(error);
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
invokeRuntimeCallback("onError", this.onError, error instanceof Error ? error : new Error(String(error)));
|
|
281
|
+
}
|
|
282
|
+
assertInterruptsAnswerable(method, interrupts) {
|
|
301
283
|
const now = Date.now();
|
|
302
|
-
for (const interrupt of
|
|
284
|
+
for (const interrupt of interrupts) {
|
|
303
285
|
if (!interrupt.expiresAt) continue;
|
|
304
286
|
const expiry = new Date(interrupt.expiresAt).getTime();
|
|
305
|
-
if (Number.isNaN(expiry)) throw new Error(`[agui]
|
|
306
|
-
if (expiry <= now) throw new Error(`[agui]
|
|
287
|
+
if (Number.isNaN(expiry)) throw new Error(`[agui] ${method}: interrupt ${interrupt.id} has malformed expiresAt "${interrupt.expiresAt}"`);
|
|
288
|
+
if (expiry <= now) throw new Error(`[agui] ${method}: interrupt ${interrupt.id} expired at ${interrupt.expiresAt}`);
|
|
307
289
|
}
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
290
|
+
if (this.isRunningFlag) throw new Error(`[agui] ${method}: a run is already in progress`);
|
|
291
|
+
}
|
|
292
|
+
async respondToToolApproval(options) {
|
|
293
|
+
const pending = this.getPendingInterrupts();
|
|
294
|
+
if (!pending) throw new Error("[agui] respondToToolApproval: no pending interrupts on this thread");
|
|
295
|
+
const gatedMessage = this.session.tryGetMessage(pending.messageId)?.message;
|
|
296
|
+
if (![...projectAgUiToolApprovals(pending.interrupts, new Set((gatedMessage?.content ?? []).filter((part) => part.type === "tool-call").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}"`);
|
|
297
|
+
this.assertInterruptsAnswerable("respondToToolApproval", pending.interrupts);
|
|
298
|
+
if (!this.session.updateMessage(pending.messageId, (message) => {
|
|
299
|
+
if (message.role !== "assistant") return message;
|
|
300
|
+
const assistant = message;
|
|
301
|
+
const content = withToolApprovalDecision(assistant.content, options);
|
|
302
|
+
if (content === assistant.content) return assistant;
|
|
303
|
+
return {
|
|
304
|
+
...assistant,
|
|
305
|
+
content
|
|
306
|
+
};
|
|
307
|
+
})) throw new Error(`[agui] respondToToolApproval: approval "${options.approvalId}" is already decided`);
|
|
308
|
+
this.notifyUpdate();
|
|
309
|
+
const assistant = this.session.tryGetMessage(pending.messageId)?.message;
|
|
310
|
+
if (!assistant) return;
|
|
311
|
+
const resume = buildToolApprovalResume(assistant.content, pending.interrupts);
|
|
312
|
+
if (!resume) return;
|
|
313
|
+
await this.resumeWithResponses(pending.messageId, resume);
|
|
312
314
|
}
|
|
313
315
|
async steerAway(message, responses) {
|
|
314
316
|
const pending = this.getPendingInterrupts();
|
|
@@ -331,7 +333,7 @@ var AgUiThreadRuntimeCore = class {
|
|
|
331
333
|
const resume = this.resolveSteerAwayResume(pending.interrupts, responses);
|
|
332
334
|
if (this.isRunningFlag) throw new Error("[agui] steerAway: a run is already in progress");
|
|
333
335
|
const normalized = this.toAppendMessage(message);
|
|
334
|
-
this.clearPendingInterrupts(pending.messageId);
|
|
336
|
+
this.clearPendingInterrupts(pending.messageId, resume);
|
|
335
337
|
const threadMessageId = this.appendEntry(normalized);
|
|
336
338
|
await this.startRun(threadMessageId, normalized.runConfig, resume);
|
|
337
339
|
}
|
|
@@ -357,7 +359,7 @@ var AgUiThreadRuntimeCore = class {
|
|
|
357
359
|
toAppendMessage(message) {
|
|
358
360
|
if (typeof message === "string") return {
|
|
359
361
|
createdAt: /* @__PURE__ */ new Date(),
|
|
360
|
-
parentId: this.
|
|
362
|
+
parentId: this.session.headId,
|
|
361
363
|
sourceId: null,
|
|
362
364
|
runConfig: {},
|
|
363
365
|
role: "user",
|
|
@@ -370,7 +372,7 @@ var AgUiThreadRuntimeCore = class {
|
|
|
370
372
|
};
|
|
371
373
|
return {
|
|
372
374
|
createdAt: message.createdAt ?? /* @__PURE__ */ new Date(),
|
|
373
|
-
parentId: message.parentId ?? this.
|
|
375
|
+
parentId: message.parentId ?? this.session.headId,
|
|
374
376
|
sourceId: message.sourceId ?? null,
|
|
375
377
|
role: message.role ?? "user",
|
|
376
378
|
content: message.content,
|
|
@@ -380,8 +382,8 @@ var AgUiThreadRuntimeCore = class {
|
|
|
380
382
|
startRun: message.startRun
|
|
381
383
|
};
|
|
382
384
|
}
|
|
383
|
-
clearPendingInterrupts(messageId) {
|
|
384
|
-
if (this.updateMessage(messageId, (message) => {
|
|
385
|
+
clearPendingInterrupts(messageId, resume) {
|
|
386
|
+
if (this.session.updateMessage(messageId, (message) => {
|
|
385
387
|
if (message.role !== "assistant") return message;
|
|
386
388
|
const assistant = message;
|
|
387
389
|
if (assistant.status?.type !== "requires-action" || assistant.status.reason !== "interrupt") return assistant;
|
|
@@ -391,6 +393,7 @@ var AgUiThreadRuntimeCore = class {
|
|
|
391
393
|
else delete newCustom["agui"];
|
|
392
394
|
return {
|
|
393
395
|
...assistant,
|
|
396
|
+
content: withSettledToolApprovals(assistant.content, resume),
|
|
394
397
|
status: {
|
|
395
398
|
type: "complete",
|
|
396
399
|
reason: "unknown"
|
|
@@ -417,7 +420,7 @@ var AgUiThreadRuntimeCore = class {
|
|
|
417
420
|
return fallbackMessageId;
|
|
418
421
|
}
|
|
419
422
|
cancelUnresolvedToolCalls(messageId) {
|
|
420
|
-
if (this.updateMessage(messageId, (message) => {
|
|
423
|
+
if (this.session.updateMessage(messageId, (message) => {
|
|
421
424
|
if (message.role !== "assistant") return message;
|
|
422
425
|
const assistant = message;
|
|
423
426
|
const content = assistant.content.map((part) => {
|
|
@@ -435,7 +438,7 @@ var AgUiThreadRuntimeCore = class {
|
|
|
435
438
|
})) this.notifyUpdate();
|
|
436
439
|
}
|
|
437
440
|
addToolResult(options) {
|
|
438
|
-
if (!this.updateMessage(options.messageId, (message) => {
|
|
441
|
+
if (!this.session.updateMessage(options.messageId, (message) => {
|
|
439
442
|
if (message.role !== "assistant") return message;
|
|
440
443
|
const assistant = message;
|
|
441
444
|
let matchedToolCall = false;
|
|
@@ -461,7 +464,7 @@ var AgUiThreadRuntimeCore = class {
|
|
|
461
464
|
sendA2uiAction(action) {
|
|
462
465
|
this.assertNoPendingInterrupts();
|
|
463
466
|
this.maybeAutoCancelPendingToolCalls();
|
|
464
|
-
const parentId = this.
|
|
467
|
+
const parentId = this.session.headId;
|
|
465
468
|
if (parentId === null) {
|
|
466
469
|
this.logger.debug("[agui] sendA2uiAction: no messages to resume, dropping action");
|
|
467
470
|
return;
|
|
@@ -485,12 +488,12 @@ var AgUiThreadRuntimeCore = class {
|
|
|
485
488
|
this.startResumeRun(messageId);
|
|
486
489
|
}
|
|
487
490
|
maybeCompleteAfterToolResults(messageId) {
|
|
488
|
-
const message = this.tryGetMessage(messageId)?.message;
|
|
491
|
+
const message = this.session.tryGetMessage(messageId)?.message;
|
|
489
492
|
if (!message || message.role !== "assistant") return false;
|
|
490
493
|
const assistant = message;
|
|
491
494
|
if (assistant.status?.type !== "requires-action" || assistant.status.reason !== "tool-calls") return false;
|
|
492
495
|
if (!assistant.content.every((part) => part.type !== "tool-call" || isResolvedToolCall(part))) return false;
|
|
493
|
-
if (!this.updateMessage(messageId, (current) => current.role === "assistant" ? {
|
|
496
|
+
if (!this.session.updateMessage(messageId, (current) => current.role === "assistant" ? {
|
|
494
497
|
...current,
|
|
495
498
|
status: {
|
|
496
499
|
type: "complete",
|
|
@@ -510,7 +513,7 @@ var AgUiThreadRuntimeCore = class {
|
|
|
510
513
|
this.pendingA2uiResume = false;
|
|
511
514
|
this.pendingA2uiAction = void 0;
|
|
512
515
|
this.assistantHistoryParents.clear();
|
|
513
|
-
if (messages.length === 0) this.
|
|
516
|
+
if (messages.length === 0) this.session.clear();
|
|
514
517
|
else {
|
|
515
518
|
let expectedParentId = null;
|
|
516
519
|
let lastAppliedId = null;
|
|
@@ -519,82 +522,32 @@ var AgUiThreadRuntimeCore = class {
|
|
|
519
522
|
for (const message of messages) {
|
|
520
523
|
if (seen.has(message.id)) continue;
|
|
521
524
|
seen.add(message.id);
|
|
522
|
-
const existing = this.tryGetMessage(message.id);
|
|
525
|
+
const existing = this.session.tryGetMessage(message.id);
|
|
523
526
|
if (existing && existing.parentId !== expectedParentId) {
|
|
524
527
|
hardReplace = true;
|
|
525
528
|
break;
|
|
526
529
|
}
|
|
527
|
-
this.addOrUpdateMessage(expectedParentId, message);
|
|
530
|
+
this.session.addOrUpdateMessage(expectedParentId, message);
|
|
528
531
|
expectedParentId = message.id;
|
|
529
532
|
lastAppliedId = message.id;
|
|
530
533
|
}
|
|
531
534
|
if (hardReplace) {
|
|
532
|
-
this.
|
|
535
|
+
this.session.clear();
|
|
533
536
|
expectedParentId = null;
|
|
534
537
|
lastAppliedId = null;
|
|
535
538
|
seen.clear();
|
|
536
539
|
for (const message of messages) {
|
|
537
540
|
if (seen.has(message.id)) continue;
|
|
538
541
|
seen.add(message.id);
|
|
539
|
-
this.addOrUpdateMessage(expectedParentId, message);
|
|
542
|
+
this.session.addOrUpdateMessage(expectedParentId, message);
|
|
540
543
|
expectedParentId = message.id;
|
|
541
544
|
lastAppliedId = message.id;
|
|
542
545
|
}
|
|
543
546
|
}
|
|
544
|
-
this.
|
|
547
|
+
this.session.resetHead(lastAppliedId);
|
|
545
548
|
}
|
|
546
|
-
this.
|
|
547
|
-
for (const { message } of this.getMessageRepository().messages) this.
|
|
548
|
-
this.notifyUpdate();
|
|
549
|
-
}
|
|
550
|
-
applyExternalMessageRepository(loaded) {
|
|
551
|
-
const headId = loaded.headId ?? loaded.messages.at(-1)?.message.id ?? null;
|
|
552
|
-
const ids = /* @__PURE__ */ new Set();
|
|
553
|
-
let degenerate = false;
|
|
554
|
-
for (const { message } of loaded.messages) {
|
|
555
|
-
if (ids.has(message.id)) {
|
|
556
|
-
degenerate = true;
|
|
557
|
-
break;
|
|
558
|
-
}
|
|
559
|
-
ids.add(message.id);
|
|
560
|
-
}
|
|
561
|
-
if (headId !== null && !ids.has(headId)) degenerate = true;
|
|
562
|
-
if (!degenerate) {
|
|
563
|
-
this.clearRepository();
|
|
564
|
-
let pending = [...loaded.messages];
|
|
565
|
-
const importedIds = /* @__PURE__ */ new Set();
|
|
566
|
-
while (pending.length > 0) {
|
|
567
|
-
const unresolved = [];
|
|
568
|
-
let progressed = false;
|
|
569
|
-
for (const item of pending) {
|
|
570
|
-
if (item.parentId !== null && !importedIds.has(item.parentId)) {
|
|
571
|
-
unresolved.push(item);
|
|
572
|
-
continue;
|
|
573
|
-
}
|
|
574
|
-
this.addOrUpdateMessage(item.parentId, item.message);
|
|
575
|
-
importedIds.add(item.message.id);
|
|
576
|
-
progressed = true;
|
|
577
|
-
}
|
|
578
|
-
if (!progressed) {
|
|
579
|
-
degenerate = true;
|
|
580
|
-
break;
|
|
581
|
-
}
|
|
582
|
-
pending = unresolved;
|
|
583
|
-
}
|
|
584
|
-
}
|
|
585
|
-
if (degenerate) {
|
|
586
|
-
this.clearRepository();
|
|
587
|
-
let previousId = null;
|
|
588
|
-
for (const { message } of loaded.messages) {
|
|
589
|
-
const existing = this.tryGetMessage(message.id);
|
|
590
|
-
this.addOrUpdateMessage(existing ? existing.parentId : previousId, message);
|
|
591
|
-
previousId = message.id;
|
|
592
|
-
}
|
|
593
|
-
this.resetRepositoryHead(previousId);
|
|
594
|
-
} else this.resetRepositoryHead(headId);
|
|
595
|
-
this.assistantHistoryParents.clear();
|
|
596
|
-
this.recordedHistoryIds.clear();
|
|
597
|
-
for (const { message } of loaded.messages) this.recordedHistoryIds.add(message.id);
|
|
549
|
+
this.snapshotHistoryIds.clear();
|
|
550
|
+
for (const { message } of this.getMessageRepository().messages) this.snapshotHistoryIds.add(message.id);
|
|
598
551
|
this.notifyUpdate();
|
|
599
552
|
}
|
|
600
553
|
loadExternalState(state) {
|
|
@@ -612,18 +565,20 @@ var AgUiThreadRuntimeCore = class {
|
|
|
612
565
|
async startRun(parentId, runConfig, resume, resumeStream) {
|
|
613
566
|
const normalizedRunConfig = runConfig ?? {};
|
|
614
567
|
this.lastRunConfig = normalizedRunConfig;
|
|
615
|
-
const parent = parentId === null ? void 0 : this.tryGetMessage(parentId);
|
|
616
|
-
const shouldEagerlyInsertAssistant = parentId !== null && parent !== void 0 && parentId !== this.
|
|
617
|
-
const historicalMessages = [...shouldEagerlyInsertAssistant ? this.tryGetMessages(parentId) ?? this.
|
|
568
|
+
const parent = parentId === null ? void 0 : this.session.tryGetMessage(parentId);
|
|
569
|
+
const shouldEagerlyInsertAssistant = parentId !== null && parent !== void 0 && parentId !== this.session.headId;
|
|
570
|
+
const historicalMessages = [...shouldEagerlyInsertAssistant ? this.session.tryGetMessages(parentId) ?? this.session.getMessages() : this.session.getMessages()];
|
|
571
|
+
const runStartMessageIds = new Set(this.getMessageRepository().messages.map(({ message }) => message.id));
|
|
618
572
|
this.pendingError = null;
|
|
619
|
-
const assistantParentId = parent ? parentId : this.
|
|
573
|
+
const assistantParentId = parent ? parentId : this.session.headId;
|
|
620
574
|
let assistantMessageId;
|
|
621
575
|
let assistantCollided = false;
|
|
622
576
|
const ensureAssistant = (allowRecreate = false) => {
|
|
623
577
|
const cached = assistantMessageId;
|
|
624
|
-
if (cached !== void 0 && this.tryGetMessage(cached)) return cached;
|
|
578
|
+
if (cached !== void 0 && this.session.tryGetMessage(cached)) return cached;
|
|
625
579
|
if (cached !== void 0 && (assistantCollided || !allowRecreate)) return cached;
|
|
626
|
-
const
|
|
580
|
+
const repositoryHeadId = this.session.headId;
|
|
581
|
+
const parentId = (cached === void 0 || shouldEagerlyInsertAssistant && (repositoryHeadId === null || runStartMessageIds.has(repositoryHeadId))) && assistantParentId && this.session.hasMessage(assistantParentId) ? assistantParentId : repositoryHeadId;
|
|
627
582
|
const created = this.insertAssistantPlaceholder(parentId, cached);
|
|
628
583
|
assistantMessageId = created;
|
|
629
584
|
this.markPendingAssistantHistory(created, parentId);
|
|
@@ -631,20 +586,44 @@ var AgUiThreadRuntimeCore = class {
|
|
|
631
586
|
};
|
|
632
587
|
if (shouldEagerlyInsertAssistant) ensureAssistant();
|
|
633
588
|
const applyUpdate = (update) => {
|
|
634
|
-
const
|
|
635
|
-
const resolved = this.updateAssistantMessage(ensureAssistant(
|
|
589
|
+
const hasStreamContent = Array.isArray(update.content) && update.content.some((part) => part.type !== "data");
|
|
590
|
+
const resolved = this.updateAssistantMessage(ensureAssistant(hasStreamContent), update);
|
|
636
591
|
if (resolved !== assistantMessageId) assistantMessageId = resolved;
|
|
637
592
|
};
|
|
593
|
+
const adoptServerMessageId = (serverId, startNewMessage) => {
|
|
594
|
+
if (startNewMessage && assistantMessageId !== void 0) {
|
|
595
|
+
applyUpdate({ status: {
|
|
596
|
+
type: "complete",
|
|
597
|
+
reason: "unknown"
|
|
598
|
+
} });
|
|
599
|
+
const previousId = assistantMessageId;
|
|
600
|
+
assistantMessageId = void 0;
|
|
601
|
+
if (this.session.tryGetMessage(previousId)) {
|
|
602
|
+
const created = this.insertAssistantPlaceholder(previousId);
|
|
603
|
+
assistantMessageId = created;
|
|
604
|
+
this.markPendingAssistantHistory(created, previousId);
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
const placeholder = ensureAssistant(true);
|
|
608
|
+
if (placeholder === serverId) return;
|
|
609
|
+
const reassigned = this.reassignAssistantId(placeholder, serverId);
|
|
610
|
+
const adoptsVisibleCollision = !reassigned && !runStartMessageIds.has(serverId) && this.session.headId === serverId;
|
|
611
|
+
if (reassigned || adoptsVisibleCollision) {
|
|
612
|
+
assistantMessageId = serverId;
|
|
613
|
+
if (adoptsVisibleCollision) {
|
|
614
|
+
const parentId = this.session.tryGetMessage(serverId)?.parentId ?? null;
|
|
615
|
+
this.markPendingAssistantHistory(serverId, parentId);
|
|
616
|
+
}
|
|
617
|
+
} else assistantCollided = true;
|
|
618
|
+
};
|
|
638
619
|
const aggregator = new RunAggregator({
|
|
639
620
|
showThinking: this.showThinking,
|
|
640
621
|
logger: this.logger,
|
|
641
622
|
emit: applyUpdate,
|
|
642
623
|
onServerMessageId: (serverId) => {
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
else assistantCollided = true;
|
|
647
|
-
}
|
|
624
|
+
adoptServerMessageId(serverId, false);
|
|
625
|
+
},
|
|
626
|
+
onTextMessageStart: (serverId) => adoptServerMessageId(serverId, true)
|
|
648
627
|
});
|
|
649
628
|
const dispatch = (event) => this.handleEvent(aggregator, event, assistantMessageId);
|
|
650
629
|
const abortController = new AbortController();
|
|
@@ -714,6 +693,7 @@ var AgUiThreadRuntimeCore = class {
|
|
|
714
693
|
}
|
|
715
694
|
if (this.pendingError) {
|
|
716
695
|
const err = this.pendingError;
|
|
696
|
+
this.reportedErrors.add(err);
|
|
717
697
|
this.pendingError = null;
|
|
718
698
|
this.pendingResumeMessageId = null;
|
|
719
699
|
this.pendingA2uiResume = false;
|
|
@@ -735,7 +715,7 @@ var AgUiThreadRuntimeCore = class {
|
|
|
735
715
|
return;
|
|
736
716
|
}
|
|
737
717
|
this.maybeAutoCancelPendingToolCalls();
|
|
738
|
-
const parentId = this.
|
|
718
|
+
const parentId = this.session.headId;
|
|
739
719
|
if (parentId !== null) this.startResumeRun(parentId);
|
|
740
720
|
else {
|
|
741
721
|
this.pendingA2uiAction = void 0;
|
|
@@ -758,7 +738,7 @@ var AgUiThreadRuntimeCore = class {
|
|
|
758
738
|
unstable_threadId: ctx.threadId,
|
|
759
739
|
unstable_parentId: ctx.parentId,
|
|
760
740
|
unstable_getMessage: () => {
|
|
761
|
-
const message = this.tryGetMessage(currentId())?.message;
|
|
741
|
+
const message = this.session.tryGetMessage(currentId())?.message;
|
|
762
742
|
if (!message) throw new Error("[agui] resume stream requested the assistant message before it existed");
|
|
763
743
|
return message;
|
|
764
744
|
}
|
|
@@ -781,7 +761,7 @@ var AgUiThreadRuntimeCore = class {
|
|
|
781
761
|
return;
|
|
782
762
|
}
|
|
783
763
|
if (ctx.abortSignal.aborted) return;
|
|
784
|
-
const current = this.tryGetMessage(currentId())?.message;
|
|
764
|
+
const current = this.session.tryGetMessage(currentId())?.message;
|
|
785
765
|
if (!current || current.status?.type === "running") ctx.applyUpdate({ status: {
|
|
786
766
|
type: "complete",
|
|
787
767
|
reason: "unknown"
|
|
@@ -789,7 +769,7 @@ var AgUiThreadRuntimeCore = class {
|
|
|
789
769
|
}
|
|
790
770
|
buildRunInput(runId, runConfig, historyMessages, resume) {
|
|
791
771
|
const threadId = this.agent.threadId || "main";
|
|
792
|
-
const messages = toAgUiMessages(historyMessages ?? this.
|
|
772
|
+
const messages = toAgUiMessages(historyMessages ?? this.session.getMessages());
|
|
793
773
|
const context = this.runtime?.thread.getModelContext();
|
|
794
774
|
const input = {
|
|
795
775
|
threadId,
|
|
@@ -854,47 +834,62 @@ var AgUiThreadRuntimeCore = class {
|
|
|
854
834
|
custom: {}
|
|
855
835
|
}
|
|
856
836
|
};
|
|
857
|
-
this.addOrUpdateMessage(parentId ?? this.
|
|
858
|
-
this.switchToBranch(id);
|
|
837
|
+
this.session.addOrUpdateMessage(parentId ?? this.session.headId, assistant);
|
|
838
|
+
this.session.switchToBranch(id);
|
|
859
839
|
this.notifyUpdate();
|
|
860
840
|
return id;
|
|
861
841
|
}
|
|
862
842
|
reassignAssistantId(oldId, newId) {
|
|
863
843
|
if (oldId === newId) return true;
|
|
864
|
-
const oldItem = this.tryGetMessage(oldId);
|
|
844
|
+
const oldItem = this.session.tryGetMessage(oldId);
|
|
865
845
|
if (!oldItem) return false;
|
|
866
|
-
const collidesWithExisting = this.hasMessage(newId);
|
|
846
|
+
const collidesWithExisting = this.session.hasMessage(newId);
|
|
867
847
|
if (collidesWithExisting) {
|
|
868
848
|
this.logger.debug?.("[agui] reassignAssistantId: server id already present in messages or repository, dropping placeholder", {
|
|
869
849
|
oldId,
|
|
870
850
|
newId
|
|
871
851
|
});
|
|
872
|
-
this.deleteMessage(oldId, oldItem.parentId ?? null);
|
|
852
|
+
this.session.deleteMessage(oldId, oldItem.parentId ?? null);
|
|
873
853
|
} else {
|
|
874
854
|
const { isOptimistic: _, ...metadata } = oldItem.message.metadata;
|
|
875
|
-
this.addOrUpdateMessage(oldItem.parentId, {
|
|
855
|
+
this.session.addOrUpdateMessage(oldItem.parentId, {
|
|
876
856
|
...oldItem.message,
|
|
877
857
|
id: newId,
|
|
878
858
|
metadata
|
|
879
859
|
});
|
|
880
|
-
this.switchToBranch(newId);
|
|
881
|
-
this.tryDeleteMessage(oldId);
|
|
860
|
+
this.session.switchToBranch(newId);
|
|
861
|
+
this.session.tryDeleteMessage(oldId);
|
|
882
862
|
}
|
|
883
863
|
const pendingParent = this.assistantHistoryParents.get(oldId);
|
|
884
864
|
if (pendingParent !== void 0) {
|
|
885
865
|
this.assistantHistoryParents.delete(oldId);
|
|
886
866
|
if (!collidesWithExisting && !this.assistantHistoryParents.has(newId)) this.assistantHistoryParents.set(newId, pendingParent);
|
|
887
867
|
}
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
if (!collidesWithExisting)
|
|
868
|
+
for (const ids of [this.snapshotHistoryIds, this.persistedHistoryIds]) if (ids.has(oldId)) {
|
|
869
|
+
ids.delete(oldId);
|
|
870
|
+
if (!collidesWithExisting) ids.add(newId);
|
|
871
|
+
}
|
|
872
|
+
const pendingWrite = this.historyWrites.get(oldId);
|
|
873
|
+
if (pendingWrite) {
|
|
874
|
+
this.historyWrites.delete(oldId);
|
|
875
|
+
if (!collidesWithExisting) {
|
|
876
|
+
this.historyWrites.set(newId, pendingWrite);
|
|
877
|
+
const settle = () => {
|
|
878
|
+
if (this.historyWrites.get(newId) === pendingWrite) this.historyWrites.delete(newId);
|
|
879
|
+
};
|
|
880
|
+
pendingWrite.then(() => {
|
|
881
|
+
this.persistedHistoryIds.delete(oldId);
|
|
882
|
+
this.persistedHistoryIds.add(newId);
|
|
883
|
+
settle();
|
|
884
|
+
}, settle);
|
|
885
|
+
}
|
|
891
886
|
}
|
|
892
887
|
this.notifyUpdate();
|
|
893
888
|
return !collidesWithExisting;
|
|
894
889
|
}
|
|
895
890
|
updateAssistantMessage(messageId, update) {
|
|
896
891
|
let latestStatus;
|
|
897
|
-
if (!this.updateMessage(messageId, (message) => {
|
|
892
|
+
if (!this.session.updateMessage(messageId, (message) => {
|
|
898
893
|
if (message.role !== "assistant") return message;
|
|
899
894
|
const assistant = message;
|
|
900
895
|
const metadata = update.metadata ? this.mergeAssistantMetadata(assistant.metadata, update.metadata) : assistant.metadata;
|
|
@@ -996,7 +991,7 @@ var AgUiThreadRuntimeCore = class {
|
|
|
996
991
|
}
|
|
997
992
|
}
|
|
998
993
|
applyCrossRunToolResult(messageId, event) {
|
|
999
|
-
if (!this.updateMessage(messageId, (message) => {
|
|
994
|
+
if (!this.session.updateMessage(messageId, (message) => {
|
|
1000
995
|
if (message.role !== "assistant") return message;
|
|
1001
996
|
const assistant = message;
|
|
1002
997
|
const mcpAppUri = readMcpAppResourceUri(event.mcpResult?._meta);
|
|
@@ -1041,7 +1036,7 @@ var AgUiThreadRuntimeCore = class {
|
|
|
1041
1036
|
const serverHash = event.content["serverHash"];
|
|
1042
1037
|
const appServerId = typeof serverId === "string" && serverId.length > 0 ? serverId : typeof serverHash === "string" && serverHash.length > 0 ? serverHash : void 0;
|
|
1043
1038
|
const result = event.content["result"];
|
|
1044
|
-
if (!this.updateMessage(messageId, (message) => {
|
|
1039
|
+
if (!this.session.updateMessage(messageId, (message) => {
|
|
1045
1040
|
if (message.role !== "assistant") return message;
|
|
1046
1041
|
const assistant = message;
|
|
1047
1042
|
let matchedToolCall = false;
|
|
@@ -1075,7 +1070,7 @@ var AgUiThreadRuntimeCore = class {
|
|
|
1075
1070
|
}
|
|
1076
1071
|
importMessagesSnapshot(rawMessages, activeAssistantId) {
|
|
1077
1072
|
try {
|
|
1078
|
-
const activeMessage = activeAssistantId ? this.tryGetMessage(activeAssistantId)?.message : void 0;
|
|
1073
|
+
const activeMessage = activeAssistantId ? this.session.tryGetMessage(activeAssistantId)?.message : void 0;
|
|
1079
1074
|
const activeAssistant = activeMessage?.role === "assistant" ? activeMessage : void 0;
|
|
1080
1075
|
const normalized = fromAgUiMessages(rawMessages, { showThinking: this.showThinking });
|
|
1081
1076
|
const converted = [];
|
|
@@ -1084,14 +1079,16 @@ var AgUiThreadRuntimeCore = class {
|
|
|
1084
1079
|
} catch (error) {
|
|
1085
1080
|
this.logger.error?.("[agui] failed to import message from snapshot", error);
|
|
1086
1081
|
}
|
|
1087
|
-
const snapshotHeadId = converted.at(-1)?.id ?? null;
|
|
1088
1082
|
const snapshotContainsActiveAssistant = converted.some((message) => message.id === activeAssistant?.id);
|
|
1089
1083
|
const preservesActiveAssistant = activeAssistant !== void 0 && !snapshotContainsActiveAssistant && (activeAssistant.metadata.isOptimistic !== true || converted.at(-1)?.role !== "assistant");
|
|
1090
1084
|
if (preservesActiveAssistant) converted.push(activeAssistant);
|
|
1091
1085
|
this.applyExternalMessages(converted);
|
|
1092
|
-
if (
|
|
1093
|
-
this.
|
|
1094
|
-
|
|
1086
|
+
if (activeAssistant !== void 0) {
|
|
1087
|
+
const activeItem = this.session.tryGetMessage(activeAssistant.id);
|
|
1088
|
+
if (activeItem) {
|
|
1089
|
+
if (preservesActiveAssistant) this.snapshotHistoryIds.delete(activeAssistant.id);
|
|
1090
|
+
this.markPendingAssistantHistory(activeAssistant.id, activeItem.parentId);
|
|
1091
|
+
}
|
|
1095
1092
|
}
|
|
1096
1093
|
} catch (error) {
|
|
1097
1094
|
this.logger.error?.("[agui] failed to import messages snapshot", error);
|
|
@@ -1108,32 +1105,82 @@ var AgUiThreadRuntimeCore = class {
|
|
|
1108
1105
|
return status?.type === "requires-action" && status.reason === "interrupt";
|
|
1109
1106
|
}
|
|
1110
1107
|
recordHistoryEntry(parentId, message) {
|
|
1111
|
-
this.appendHistoryItem(parentId, message)
|
|
1108
|
+
this.appendHistoryItem(parentId, message)?.catch((error) => {
|
|
1109
|
+
this.logger.error?.("[agui] failed to append history entry", error);
|
|
1110
|
+
});
|
|
1112
1111
|
}
|
|
1113
1112
|
markPendingAssistantHistory(messageId, parentId) {
|
|
1114
1113
|
if (!this.history) return;
|
|
1115
1114
|
this.assistantHistoryParents.set(messageId, parentId);
|
|
1116
1115
|
}
|
|
1117
1116
|
persistAssistantHistory(messageId) {
|
|
1118
|
-
|
|
1117
|
+
const history = this.history;
|
|
1118
|
+
if (!history) return;
|
|
1119
1119
|
const parentId = this.assistantHistoryParents.get(messageId);
|
|
1120
1120
|
if (parentId === void 0) return;
|
|
1121
|
-
const message = this.tryGetMessage(messageId)?.message;
|
|
1121
|
+
const message = this.session.tryGetMessage(messageId)?.message;
|
|
1122
1122
|
if (!message || message.role !== "assistant") return;
|
|
1123
1123
|
if (!this.isPersistableStatus(message.status)) return;
|
|
1124
|
+
const wasPersisted = this.persistedHistoryIds.has(messageId);
|
|
1125
|
+
const update = history.update;
|
|
1126
|
+
if (update !== void 0 && (wasPersisted || this.snapshotHistoryIds.has(messageId))) {
|
|
1127
|
+
const write = this.chainHistoryWrite(messageId, () => update.call(history, {
|
|
1128
|
+
parentId,
|
|
1129
|
+
message
|
|
1130
|
+
}));
|
|
1131
|
+
this.assistantHistoryParents.delete(messageId);
|
|
1132
|
+
write.then(() => {
|
|
1133
|
+
this.persistedHistoryIds.add(messageId);
|
|
1134
|
+
}, (error) => {
|
|
1135
|
+
const pending = this.historyWrites.get(messageId);
|
|
1136
|
+
if (pending === void 0 || pending === write) this.assistantHistoryParents.set(messageId, parentId);
|
|
1137
|
+
this.logger.error?.("[agui] failed to update history entry", error);
|
|
1138
|
+
});
|
|
1139
|
+
return;
|
|
1140
|
+
}
|
|
1141
|
+
if (wasPersisted) {
|
|
1142
|
+
this.assistantHistoryParents.delete(messageId);
|
|
1143
|
+
return;
|
|
1144
|
+
}
|
|
1145
|
+
const write = this.appendHistoryItem(parentId, message);
|
|
1146
|
+
if (!write) return;
|
|
1124
1147
|
this.assistantHistoryParents.delete(messageId);
|
|
1125
|
-
|
|
1148
|
+
write.then(() => {}, (error) => {
|
|
1149
|
+
const pending = this.historyWrites.get(messageId);
|
|
1150
|
+
if (pending === void 0 || pending === write) this.assistantHistoryParents.set(messageId, parentId);
|
|
1151
|
+
this.logger.error?.("[agui] failed to append history entry", error);
|
|
1152
|
+
});
|
|
1126
1153
|
}
|
|
1127
1154
|
appendHistoryItem(parentId, message) {
|
|
1128
|
-
if (!this.history || this.
|
|
1129
|
-
this.
|
|
1130
|
-
|
|
1155
|
+
if (!this.history || this.persistedHistoryIds.has(message.id)) return;
|
|
1156
|
+
const pending = this.historyWrites.get(message.id);
|
|
1157
|
+
if (pending) return pending;
|
|
1158
|
+
const append = this.history.append.bind(this.history);
|
|
1159
|
+
const write = this.chainHistoryWrite(message.id, () => append({
|
|
1131
1160
|
parentId,
|
|
1132
1161
|
message
|
|
1133
|
-
})
|
|
1134
|
-
|
|
1135
|
-
this.
|
|
1162
|
+
}));
|
|
1163
|
+
write.then(() => {
|
|
1164
|
+
this.persistedHistoryIds.add(message.id);
|
|
1165
|
+
}, () => {});
|
|
1166
|
+
return write;
|
|
1167
|
+
}
|
|
1168
|
+
chainHistoryWrite(id, write) {
|
|
1169
|
+
const pending = this.historyWrites.get(id);
|
|
1170
|
+
let next;
|
|
1171
|
+
if (pending) next = pending.then(write, write);
|
|
1172
|
+
else try {
|
|
1173
|
+
next = Promise.resolve(write());
|
|
1174
|
+
} catch (error) {
|
|
1175
|
+
next = Promise.reject(error);
|
|
1176
|
+
}
|
|
1177
|
+
this.historyWrites.set(id, next);
|
|
1178
|
+
next.then(() => {
|
|
1179
|
+
if (this.historyWrites.get(id) === next) this.historyWrites.delete(id);
|
|
1180
|
+
}, () => {
|
|
1181
|
+
if (this.historyWrites.get(id) === next) this.historyWrites.delete(id);
|
|
1136
1182
|
});
|
|
1183
|
+
return next;
|
|
1137
1184
|
}
|
|
1138
1185
|
};
|
|
1139
1186
|
//#endregion
|