@assistant-ui/react-ag-ui 0.0.53 → 0.0.56
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 +23 -2
- package/dist/runtime/AgUiThreadRuntimeCore.d.ts.map +1 -1
- package/dist/runtime/AgUiThreadRuntimeCore.js +183 -39
- package/dist/runtime/AgUiThreadRuntimeCore.js.map +1 -1
- package/dist/runtime/adapter/conversions.d.ts +19 -3
- package/dist/runtime/adapter/conversions.d.ts.map +1 -1
- package/dist/runtime/adapter/conversions.js +197 -36
- package/dist/runtime/adapter/conversions.js.map +1 -1
- package/dist/runtime/adapter/run-aggregator.d.ts +15 -2
- package/dist/runtime/adapter/run-aggregator.d.ts.map +1 -1
- package/dist/runtime/adapter/run-aggregator.js +62 -10
- package/dist/runtime/adapter/run-aggregator.js.map +1 -1
- package/dist/runtime/adapter/subscriber.d.ts +6 -0
- package/dist/runtime/adapter/subscriber.d.ts.map +1 -1
- package/dist/runtime/adapter/subscriber.js +10 -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 +15 -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/runtime/types.d.ts +5 -0
- package/dist/runtime/types.d.ts.map +1 -1
- package/dist/useAgUiRuntime.js +4 -1
- package/dist/useAgUiRuntime.js.map +1 -1
- package/package.json +7 -7
- package/src/index.ts +8 -2
- package/src/runtime/AgUiThreadRuntimeCore.ts +335 -42
- package/src/runtime/adapter/conversions.content.test.ts +278 -0
- package/src/runtime/adapter/conversions.ts +298 -50
- package/src/runtime/adapter/run-aggregator.approval.test.ts +103 -0
- package/src/runtime/adapter/run-aggregator.ts +96 -5
- package/src/runtime/adapter/subscriber.ts +17 -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 +23 -1
- package/src/runtime/interrupt-internals.ts +35 -0
- package/src/runtime/types.ts +6 -0
- package/src/useAgUiRuntime.approval.test.tsx +885 -0
- package/src/useAgUiRuntime.queue.test.tsx +68 -1
- package/src/useAgUiRuntime.ts +5 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AgUiInterrupt, AgUiInterruptReason, AgUiResumeEntry, AgUiRunFinishedOutcome, UseAgUiRuntimeAdapters, UseAgUiRuntimeOptions, UseAgUiThreadListAdapter } from "./runtime/types.js";
|
|
2
2
|
import { useAgUiInterrupts, useAgUiSendA2uiAction, useAgUiSetState, useAgUiState, useAgUiSteerAway, useAgUiSubmitInterruptResponses } from "./hooks.js";
|
|
3
3
|
import { AgUiAssistantRuntime, useAgUiRuntime } from "./useAgUiRuntime.js";
|
|
4
|
-
import { FromAgUiMessagesOptions, fromAgUiMessages } from "./runtime/adapter/conversions.js";
|
|
5
|
-
export { type AgUiAssistantRuntime, type AgUiInterrupt, type AgUiInterruptReason, type AgUiResumeEntry, type AgUiRunFinishedOutcome, type FromAgUiMessagesOptions, type UseAgUiRuntimeAdapters, type UseAgUiRuntimeOptions, type UseAgUiThreadListAdapter, fromAgUiMessages, useAgUiInterrupts, useAgUiRuntime, useAgUiSendA2uiAction, useAgUiSetState, useAgUiState, useAgUiSteerAway, useAgUiSubmitInterruptResponses };
|
|
4
|
+
import { AgUiMessage, FromAgUiMessagesOptions, fromAgUiMessages, toAgUiMessages } from "./runtime/adapter/conversions.js";
|
|
5
|
+
export { type AgUiAssistantRuntime, type AgUiInterrupt, type AgUiInterruptReason, type AgUiMessage, type AgUiResumeEntry, type AgUiRunFinishedOutcome, type FromAgUiMessagesOptions, type UseAgUiRuntimeAdapters, type UseAgUiRuntimeOptions, type UseAgUiThreadListAdapter, fromAgUiMessages, toAgUiMessages, useAgUiInterrupts, useAgUiRuntime, useAgUiSendA2uiAction, useAgUiSetState, useAgUiState, useAgUiSteerAway, useAgUiSubmitInterruptResponses };
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { useAgUiInterrupts, useAgUiSendA2uiAction, useAgUiSetState, useAgUiState, useAgUiSteerAway, useAgUiSubmitInterruptResponses } from "./hooks.js";
|
|
2
|
-
import { fromAgUiMessages } from "./runtime/adapter/conversions.js";
|
|
2
|
+
import { fromAgUiMessages, toAgUiMessages } from "./runtime/adapter/conversions.js";
|
|
3
3
|
import { useAgUiRuntime } from "./useAgUiRuntime.js";
|
|
4
|
-
export { fromAgUiMessages, useAgUiInterrupts, useAgUiRuntime, useAgUiSendA2uiAction, useAgUiSetState, useAgUiState, useAgUiSteerAway, useAgUiSubmitInterruptResponses };
|
|
4
|
+
export { fromAgUiMessages, toAgUiMessages, useAgUiInterrupts, useAgUiRuntime, useAgUiSendA2uiAction, useAgUiSetState, useAgUiState, useAgUiSteerAway, useAgUiSubmitInterruptResponses };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Logger } from "./logger.js";
|
|
2
2
|
import { AgUiInterrupt, AgUiResumeEntry } from "./types.js";
|
|
3
|
-
import { AddToolResultOptions, AppendMessage, AssistantRuntime, ChatModelRunOptions, ChatModelRunResult, CreateAppendMessage, ExportedMessageRepository, ThreadHistoryAdapter, ThreadMessage } from "@assistant-ui/core";
|
|
3
|
+
import { AddToolResultOptions, AppendMessage, AssistantRuntime, ChatModelRunOptions, ChatModelRunResult, CreateAppendMessage, ExportedMessageRepository, RespondToToolApprovalOptions, ThreadHistoryAdapter, ThreadMessage } from "@assistant-ui/core";
|
|
4
4
|
import { AbstractAgent } from "@ag-ui/client";
|
|
5
5
|
import { ReadonlyJSONValue } from "assistant-stream/utils";
|
|
6
6
|
//#region src/runtime/AgUiThreadRuntimeCore.d.ts
|
|
@@ -30,6 +30,7 @@ declare class AgUiThreadRuntimeCore {
|
|
|
30
30
|
private onError;
|
|
31
31
|
private onCancel;
|
|
32
32
|
private readonly notifyUpdate;
|
|
33
|
+
private readonly reportedErrors;
|
|
33
34
|
private runtime;
|
|
34
35
|
private readonly repository;
|
|
35
36
|
private exportedRepository;
|
|
@@ -41,7 +42,9 @@ declare class AgUiThreadRuntimeCore {
|
|
|
41
42
|
private history;
|
|
42
43
|
private lastRunConfig;
|
|
43
44
|
private readonly assistantHistoryParents;
|
|
44
|
-
private readonly
|
|
45
|
+
private readonly snapshotHistoryIds;
|
|
46
|
+
private readonly persistedHistoryIds;
|
|
47
|
+
private readonly historyWrites;
|
|
45
48
|
private _isLoading;
|
|
46
49
|
private _loadPromise;
|
|
47
50
|
private pendingResumeMessageId;
|
|
@@ -88,6 +91,23 @@ declare class AgUiThreadRuntimeCore {
|
|
|
88
91
|
toolCallIds: string[];
|
|
89
92
|
} | null;
|
|
90
93
|
submitInterruptResponses(responses: readonly AgUiResumeEntry[]): Promise<void>;
|
|
94
|
+
/**
|
|
95
|
+
* Resumes with an already validated resume array. Answerability is checked
|
|
96
|
+
* once per submission: re-checking here would let a clock crossing reject a
|
|
97
|
+
* decision this runtime has already recorded, stranding the gate decided and
|
|
98
|
+
* unretryable.
|
|
99
|
+
*/
|
|
100
|
+
private resumeWithResponses;
|
|
101
|
+
/**
|
|
102
|
+
* The core seams that reach `respondToToolApproval` discard the promise they
|
|
103
|
+
* receive, so a rejected decision would otherwise surface only as an
|
|
104
|
+
* unhandled rejection. A failure raised by the resumed run itself was already
|
|
105
|
+
* reported by `startRun` before it rethrew, so reporting it here again would
|
|
106
|
+
* give the consumer two notifications for one failure.
|
|
107
|
+
*/
|
|
108
|
+
reportError(error: unknown): void;
|
|
109
|
+
private assertInterruptsAnswerable;
|
|
110
|
+
respondToToolApproval(options: RespondToToolApprovalOptions): Promise<void>;
|
|
91
111
|
steerAway(message: CreateAppendMessage, responses?: readonly AgUiResumeEntry[]): Promise<void>;
|
|
92
112
|
private resolveSteerAwayResume;
|
|
93
113
|
private collectInterruptResponses;
|
|
@@ -127,6 +147,7 @@ declare class AgUiThreadRuntimeCore {
|
|
|
127
147
|
private markPendingAssistantHistory;
|
|
128
148
|
private persistAssistantHistory;
|
|
129
149
|
private appendHistoryItem;
|
|
150
|
+
private chainHistoryWrite;
|
|
130
151
|
}
|
|
131
152
|
//#endregion
|
|
132
153
|
export { AgUiThreadRuntimeCore };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AgUiThreadRuntimeCore.d.ts","names":[],"sources":["../../src/runtime/AgUiThreadRuntimeCore.ts"],"mappings":";;;;;;
|
|
1
|
+
{"version":3,"file":"AgUiThreadRuntimeCore.d.ts","names":[],"sources":["../../src/runtime/AgUiThreadRuntimeCore.ts"],"mappings":";;;;;;KA2EK,YAAY,YAAY;KACxB,gBACH,SAAS,wBACN,eAAe;KACf;EACH;EACA;EACA,WAAW;EACX,SAAS;;KAGN;EACH,OAAO;EACP,QAAQ;EACR;EACA;EACA,WAAW,OAAO;EAClB;EACA,UAAU;EACV;;cAuDW;UACH;UACA;UACA;UACA;UACA;UACA;mBACS;mBACA;UAET;mBACS;UACT;UACA;UACA;UAGA;UACA;UACA;UACA;UACA;mBACS;mBACA;mBACA;mBACA;UACT;UACA;UACA;UACA;UACA;EAEI,YAAA,SAAS;EAYrB,cAAc,SAAS,KAAK;EAW5B,cAAc,SAAS;EAIvB;EAOA,wBAAwB;EAIxB,wBAAwB;UAmBhB;UAQA;UAUA;UAIA;UAQA;UAQA;UAMA;UAKA;UAKA;UAKA;EAYR,YAAY;EAIZ;MAII;EAIJ,mBAAmB;EA6Cb,OAAO,SAAS,gBAAgB;UAW9B;UAQA;EAiBF,KAAK,SAAS,gBAAgB;EAI9B,OACJ,yBACA;IAAU,YAAY;MACrB;EAMG,UAAU;EAcV,OAAO,QAAQ,kBAAkB;EAUjC,kBAAkB,mBAAmB,kBAAkB;UAwBrD;UAOA;EAgBR;IACE;IACA,qBAAqB;;EAavB;IACE;IACA;;EAcI,yBACJ,oBAAoB,oBACnB;;;;;;;UAuCW;;;;;;;;EAed,YAAY;UAYJ;EAwBF,sBACJ,SAAS,+BACR;EAoEG,UACJ,SAAS,qBACT,qBAAqB,oBACpB;UAyCK;UAkBA;UAiCA;UA0BA;EAmCR,yBAAyB;UAkBjB;EAiBR,cAAc,SAAS;EAyBvB,eAAe,QAAQ;UA4Bf;UAYA;UA6BA;EAUR,sBAAsB,mBAAmB;UAkDjC;EAiER,kBAAkB,OAAO;EAKzB,SACE,MACI,sBACE,MAAM,kCAAkC;EAOhD;UAKc;UAoOA;UA2DN;UAkCA;UAqBA;UAKA;UAQA;UAyBA;UAkEA;UAiDA;UA4BA;UA6BA;UAgEA;UA2EA;UA8DA;UAuDA;UAQA;UAIA;UAKA;UAMA;UAQA;UAsDA;UAqBA"}
|
|
@@ -1,5 +1,6 @@
|
|
|
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";
|
|
@@ -30,6 +31,19 @@ const invokeRuntimeCallback = (name, callback, ...args) => {
|
|
|
30
31
|
reportCallbackError(name, error);
|
|
31
32
|
}
|
|
32
33
|
};
|
|
34
|
+
const mergeAgUiNamespace = (current, incoming) => {
|
|
35
|
+
const merged = {
|
|
36
|
+
...current,
|
|
37
|
+
...incoming
|
|
38
|
+
};
|
|
39
|
+
const currentNs = current?.[AG_UI_METADATA_NAMESPACE];
|
|
40
|
+
const incomingNs = incoming[AG_UI_METADATA_NAMESPACE];
|
|
41
|
+
if (typeof currentNs === "object" && currentNs !== null && typeof incomingNs === "object" && incomingNs !== null) merged[AG_UI_METADATA_NAMESPACE] = {
|
|
42
|
+
...currentNs,
|
|
43
|
+
...incomingNs
|
|
44
|
+
};
|
|
45
|
+
return merged;
|
|
46
|
+
};
|
|
33
47
|
var AgUiThreadRuntimeCore = class {
|
|
34
48
|
agent;
|
|
35
49
|
logger;
|
|
@@ -38,6 +52,7 @@ var AgUiThreadRuntimeCore = class {
|
|
|
38
52
|
onError;
|
|
39
53
|
onCancel;
|
|
40
54
|
notifyUpdate;
|
|
55
|
+
reportedErrors = /* @__PURE__ */ new WeakSet();
|
|
41
56
|
runtime;
|
|
42
57
|
repository = new MessageRepository();
|
|
43
58
|
exportedRepository;
|
|
@@ -49,7 +64,9 @@ var AgUiThreadRuntimeCore = class {
|
|
|
49
64
|
history;
|
|
50
65
|
lastRunConfig;
|
|
51
66
|
assistantHistoryParents = /* @__PURE__ */ new Map();
|
|
52
|
-
|
|
67
|
+
snapshotHistoryIds = /* @__PURE__ */ new Set();
|
|
68
|
+
persistedHistoryIds = /* @__PURE__ */ new Set();
|
|
69
|
+
historyWrites = /* @__PURE__ */ new Map();
|
|
53
70
|
_isLoading = false;
|
|
54
71
|
_loadPromise;
|
|
55
72
|
pendingResumeMessageId = null;
|
|
@@ -81,6 +98,9 @@ var AgUiThreadRuntimeCore = class {
|
|
|
81
98
|
}
|
|
82
99
|
detachRuntime() {
|
|
83
100
|
this.runtime = void 0;
|
|
101
|
+
this.cancel().catch((error) => {
|
|
102
|
+
this.logger.error("[agui] failed to cancel run during teardown", error);
|
|
103
|
+
});
|
|
84
104
|
}
|
|
85
105
|
getMessages() {
|
|
86
106
|
return this.repository.getMessages();
|
|
@@ -285,17 +305,65 @@ var AgUiThreadRuntimeCore = class {
|
|
|
285
305
|
const openIds = pending.interrupts.map((i) => i.id);
|
|
286
306
|
const missing = openIds.filter((id) => !responsesById.has(id));
|
|
287
307
|
if (missing.length > 0) throw new Error(`[agui] submitInterruptResponses: missing responses for open interrupts: ${missing.join(", ")}`);
|
|
308
|
+
this.assertInterruptsAnswerable("submitInterruptResponses", pending.interrupts);
|
|
309
|
+
await this.resumeWithResponses(pending.messageId, openIds.map((id) => responsesById.get(id)));
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* Resumes with an already validated resume array. Answerability is checked
|
|
313
|
+
* once per submission: re-checking here would let a clock crossing reject a
|
|
314
|
+
* decision this runtime has already recorded, stranding the gate decided and
|
|
315
|
+
* unretryable.
|
|
316
|
+
*/
|
|
317
|
+
async resumeWithResponses(messageId, resume) {
|
|
318
|
+
this.clearPendingInterrupts(messageId, resume);
|
|
319
|
+
await this.startRun(messageId, this.lastRunConfig, resume);
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* The core seams that reach `respondToToolApproval` discard the promise they
|
|
323
|
+
* receive, so a rejected decision would otherwise surface only as an
|
|
324
|
+
* unhandled rejection. A failure raised by the resumed run itself was already
|
|
325
|
+
* reported by `startRun` before it rethrew, so reporting it here again would
|
|
326
|
+
* give the consumer two notifications for one failure.
|
|
327
|
+
*/
|
|
328
|
+
reportError(error) {
|
|
329
|
+
if (this.reportedErrors.has(error)) {
|
|
330
|
+
this.reportedErrors.delete(error);
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
invokeRuntimeCallback("onError", this.onError, error instanceof Error ? error : new Error(String(error)));
|
|
334
|
+
}
|
|
335
|
+
assertInterruptsAnswerable(method, interrupts) {
|
|
288
336
|
const now = Date.now();
|
|
289
|
-
for (const interrupt of
|
|
337
|
+
for (const interrupt of interrupts) {
|
|
290
338
|
if (!interrupt.expiresAt) continue;
|
|
291
339
|
const expiry = new Date(interrupt.expiresAt).getTime();
|
|
292
|
-
if (Number.isNaN(expiry)) throw new Error(`[agui]
|
|
293
|
-
if (expiry <= now) throw new Error(`[agui]
|
|
340
|
+
if (Number.isNaN(expiry)) throw new Error(`[agui] ${method}: interrupt ${interrupt.id} has malformed expiresAt "${interrupt.expiresAt}"`);
|
|
341
|
+
if (expiry <= now) throw new Error(`[agui] ${method}: interrupt ${interrupt.id} expired at ${interrupt.expiresAt}`);
|
|
294
342
|
}
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
343
|
+
if (this.isRunningFlag) throw new Error(`[agui] ${method}: a run is already in progress`);
|
|
344
|
+
}
|
|
345
|
+
async respondToToolApproval(options) {
|
|
346
|
+
const pending = this.getPendingInterrupts();
|
|
347
|
+
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 ?? []).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}"`);
|
|
350
|
+
this.assertInterruptsAnswerable("respondToToolApproval", pending.interrupts);
|
|
351
|
+
if (!this.updateMessage(pending.messageId, (message) => {
|
|
352
|
+
if (message.role !== "assistant") return message;
|
|
353
|
+
const assistant = message;
|
|
354
|
+
const content = withToolApprovalDecision(assistant.content, options);
|
|
355
|
+
if (content === assistant.content) return assistant;
|
|
356
|
+
return {
|
|
357
|
+
...assistant,
|
|
358
|
+
content
|
|
359
|
+
};
|
|
360
|
+
})) throw new Error(`[agui] respondToToolApproval: approval "${options.approvalId}" is already decided`);
|
|
361
|
+
this.notifyUpdate();
|
|
362
|
+
const assistant = this.tryGetMessage(pending.messageId)?.message;
|
|
363
|
+
if (!assistant) return;
|
|
364
|
+
const resume = buildToolApprovalResume(assistant.content, pending.interrupts);
|
|
365
|
+
if (!resume) return;
|
|
366
|
+
await this.resumeWithResponses(pending.messageId, resume);
|
|
299
367
|
}
|
|
300
368
|
async steerAway(message, responses) {
|
|
301
369
|
const pending = this.getPendingInterrupts();
|
|
@@ -318,7 +386,7 @@ var AgUiThreadRuntimeCore = class {
|
|
|
318
386
|
const resume = this.resolveSteerAwayResume(pending.interrupts, responses);
|
|
319
387
|
if (this.isRunningFlag) throw new Error("[agui] steerAway: a run is already in progress");
|
|
320
388
|
const normalized = this.toAppendMessage(message);
|
|
321
|
-
this.clearPendingInterrupts(pending.messageId);
|
|
389
|
+
this.clearPendingInterrupts(pending.messageId, resume);
|
|
322
390
|
const threadMessageId = this.appendEntry(normalized);
|
|
323
391
|
await this.startRun(threadMessageId, normalized.runConfig, resume);
|
|
324
392
|
}
|
|
@@ -367,7 +435,7 @@ var AgUiThreadRuntimeCore = class {
|
|
|
367
435
|
startRun: message.startRun
|
|
368
436
|
};
|
|
369
437
|
}
|
|
370
|
-
clearPendingInterrupts(messageId) {
|
|
438
|
+
clearPendingInterrupts(messageId, resume) {
|
|
371
439
|
if (this.updateMessage(messageId, (message) => {
|
|
372
440
|
if (message.role !== "assistant") return message;
|
|
373
441
|
const assistant = message;
|
|
@@ -378,6 +446,7 @@ var AgUiThreadRuntimeCore = class {
|
|
|
378
446
|
else delete newCustom["agui"];
|
|
379
447
|
return {
|
|
380
448
|
...assistant,
|
|
449
|
+
content: withSettledToolApprovals(assistant.content, resume),
|
|
381
450
|
status: {
|
|
382
451
|
type: "complete",
|
|
383
452
|
reason: "unknown"
|
|
@@ -530,8 +599,8 @@ var AgUiThreadRuntimeCore = class {
|
|
|
530
599
|
}
|
|
531
600
|
this.resetRepositoryHead(lastAppliedId);
|
|
532
601
|
}
|
|
533
|
-
this.
|
|
534
|
-
for (const { message } of this.getMessageRepository().messages) this.
|
|
602
|
+
this.snapshotHistoryIds.clear();
|
|
603
|
+
for (const { message } of this.getMessageRepository().messages) this.snapshotHistoryIds.add(message.id);
|
|
535
604
|
this.notifyUpdate();
|
|
536
605
|
}
|
|
537
606
|
applyExternalMessageRepository(loaded) {
|
|
@@ -580,8 +649,9 @@ var AgUiThreadRuntimeCore = class {
|
|
|
580
649
|
this.resetRepositoryHead(previousId);
|
|
581
650
|
} else this.resetRepositoryHead(headId);
|
|
582
651
|
this.assistantHistoryParents.clear();
|
|
583
|
-
this.
|
|
584
|
-
|
|
652
|
+
this.snapshotHistoryIds.clear();
|
|
653
|
+
this.persistedHistoryIds.clear();
|
|
654
|
+
for (const { message } of loaded.messages) this.persistedHistoryIds.add(message.id);
|
|
585
655
|
this.notifyUpdate();
|
|
586
656
|
}
|
|
587
657
|
loadExternalState(state) {
|
|
@@ -602,6 +672,7 @@ var AgUiThreadRuntimeCore = class {
|
|
|
602
672
|
const parent = parentId === null ? void 0 : this.tryGetMessage(parentId);
|
|
603
673
|
const shouldEagerlyInsertAssistant = parentId !== null && parent !== void 0 && parentId !== this.repository.headId;
|
|
604
674
|
const historicalMessages = [...shouldEagerlyInsertAssistant ? this.tryGetMessages(parentId) ?? this.repository.getMessages() : this.repository.getMessages()];
|
|
675
|
+
const runStartMessageIds = new Set(this.getMessageRepository().messages.map(({ message }) => message.id));
|
|
605
676
|
this.pendingError = null;
|
|
606
677
|
const assistantParentId = parent ? parentId : this.repository.headId;
|
|
607
678
|
let assistantMessageId;
|
|
@@ -610,7 +681,8 @@ var AgUiThreadRuntimeCore = class {
|
|
|
610
681
|
const cached = assistantMessageId;
|
|
611
682
|
if (cached !== void 0 && this.tryGetMessage(cached)) return cached;
|
|
612
683
|
if (cached !== void 0 && (assistantCollided || !allowRecreate)) return cached;
|
|
613
|
-
const
|
|
684
|
+
const repositoryHeadId = this.repository.headId;
|
|
685
|
+
const parentId = (cached === void 0 || shouldEagerlyInsertAssistant && (repositoryHeadId === null || runStartMessageIds.has(repositoryHeadId))) && assistantParentId && this.hasMessage(assistantParentId) ? assistantParentId : repositoryHeadId;
|
|
614
686
|
const created = this.insertAssistantPlaceholder(parentId, cached);
|
|
615
687
|
assistantMessageId = created;
|
|
616
688
|
this.markPendingAssistantHistory(created, parentId);
|
|
@@ -618,8 +690,8 @@ var AgUiThreadRuntimeCore = class {
|
|
|
618
690
|
};
|
|
619
691
|
if (shouldEagerlyInsertAssistant) ensureAssistant();
|
|
620
692
|
const applyUpdate = (update) => {
|
|
621
|
-
const
|
|
622
|
-
const resolved = this.updateAssistantMessage(ensureAssistant(
|
|
693
|
+
const hasStreamContent = Array.isArray(update.content) && update.content.some((part) => part.type !== "data");
|
|
694
|
+
const resolved = this.updateAssistantMessage(ensureAssistant(hasStreamContent), update);
|
|
623
695
|
if (resolved !== assistantMessageId) assistantMessageId = resolved;
|
|
624
696
|
};
|
|
625
697
|
const aggregator = new RunAggregator({
|
|
@@ -629,8 +701,15 @@ var AgUiThreadRuntimeCore = class {
|
|
|
629
701
|
onServerMessageId: (serverId) => {
|
|
630
702
|
const placeholder = ensureAssistant(true);
|
|
631
703
|
if (placeholder === serverId) return;
|
|
632
|
-
|
|
633
|
-
|
|
704
|
+
const reassigned = this.reassignAssistantId(placeholder, serverId);
|
|
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;
|
|
634
713
|
}
|
|
635
714
|
});
|
|
636
715
|
const dispatch = (event) => this.handleEvent(aggregator, event, assistantMessageId);
|
|
@@ -701,6 +780,7 @@ var AgUiThreadRuntimeCore = class {
|
|
|
701
780
|
}
|
|
702
781
|
if (this.pendingError) {
|
|
703
782
|
const err = this.pendingError;
|
|
783
|
+
this.reportedErrors.add(err);
|
|
704
784
|
this.pendingError = null;
|
|
705
785
|
this.pendingResumeMessageId = null;
|
|
706
786
|
this.pendingA2uiResume = false;
|
|
@@ -872,9 +952,24 @@ var AgUiThreadRuntimeCore = class {
|
|
|
872
952
|
this.assistantHistoryParents.delete(oldId);
|
|
873
953
|
if (!collidesWithExisting && !this.assistantHistoryParents.has(newId)) this.assistantHistoryParents.set(newId, pendingParent);
|
|
874
954
|
}
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
if (!collidesWithExisting)
|
|
955
|
+
for (const ids of [this.snapshotHistoryIds, this.persistedHistoryIds]) if (ids.has(oldId)) {
|
|
956
|
+
ids.delete(oldId);
|
|
957
|
+
if (!collidesWithExisting) ids.add(newId);
|
|
958
|
+
}
|
|
959
|
+
const pendingWrite = this.historyWrites.get(oldId);
|
|
960
|
+
if (pendingWrite) {
|
|
961
|
+
this.historyWrites.delete(oldId);
|
|
962
|
+
if (!collidesWithExisting) {
|
|
963
|
+
this.historyWrites.set(newId, pendingWrite);
|
|
964
|
+
const settle = () => {
|
|
965
|
+
if (this.historyWrites.get(newId) === pendingWrite) this.historyWrites.delete(newId);
|
|
966
|
+
};
|
|
967
|
+
pendingWrite.then(() => {
|
|
968
|
+
this.persistedHistoryIds.delete(oldId);
|
|
969
|
+
this.persistedHistoryIds.add(newId);
|
|
970
|
+
settle();
|
|
971
|
+
}, settle);
|
|
972
|
+
}
|
|
878
973
|
}
|
|
879
974
|
this.notifyUpdate();
|
|
880
975
|
return !collidesWithExisting;
|
|
@@ -934,10 +1029,7 @@ var AgUiThreadRuntimeCore = class {
|
|
|
934
1029
|
steps,
|
|
935
1030
|
...current.isOptimistic ? { isOptimistic: true } : {},
|
|
936
1031
|
...incoming.timing ? { timing: incoming.timing } : {},
|
|
937
|
-
custom: incoming.custom ?
|
|
938
|
-
...current.custom,
|
|
939
|
-
...incoming.custom
|
|
940
|
-
} : current.custom
|
|
1032
|
+
custom: incoming.custom ? mergeAgUiNamespace(current.custom, incoming.custom) : current.custom
|
|
941
1033
|
};
|
|
942
1034
|
}
|
|
943
1035
|
handleEvent(aggregator, event, activeAssistantId) {
|
|
@@ -1074,14 +1166,16 @@ var AgUiThreadRuntimeCore = class {
|
|
|
1074
1166
|
} catch (error) {
|
|
1075
1167
|
this.logger.error?.("[agui] failed to import message from snapshot", error);
|
|
1076
1168
|
}
|
|
1077
|
-
const snapshotHeadId = converted.at(-1)?.id ?? null;
|
|
1078
1169
|
const snapshotContainsActiveAssistant = converted.some((message) => message.id === activeAssistant?.id);
|
|
1079
1170
|
const preservesActiveAssistant = activeAssistant !== void 0 && !snapshotContainsActiveAssistant && (activeAssistant.metadata.isOptimistic !== true || converted.at(-1)?.role !== "assistant");
|
|
1080
1171
|
if (preservesActiveAssistant) converted.push(activeAssistant);
|
|
1081
1172
|
this.applyExternalMessages(converted);
|
|
1082
|
-
if (
|
|
1083
|
-
this.
|
|
1084
|
-
|
|
1173
|
+
if (activeAssistant !== void 0) {
|
|
1174
|
+
const activeItem = this.tryGetMessage(activeAssistant.id);
|
|
1175
|
+
if (activeItem) {
|
|
1176
|
+
if (preservesActiveAssistant) this.snapshotHistoryIds.delete(activeAssistant.id);
|
|
1177
|
+
this.markPendingAssistantHistory(activeAssistant.id, activeItem.parentId);
|
|
1178
|
+
}
|
|
1085
1179
|
}
|
|
1086
1180
|
} catch (error) {
|
|
1087
1181
|
this.logger.error?.("[agui] failed to import messages snapshot", error);
|
|
@@ -1098,32 +1192,82 @@ var AgUiThreadRuntimeCore = class {
|
|
|
1098
1192
|
return status?.type === "requires-action" && status.reason === "interrupt";
|
|
1099
1193
|
}
|
|
1100
1194
|
recordHistoryEntry(parentId, message) {
|
|
1101
|
-
this.appendHistoryItem(parentId, message)
|
|
1195
|
+
this.appendHistoryItem(parentId, message)?.catch((error) => {
|
|
1196
|
+
this.logger.error?.("[agui] failed to append history entry", error);
|
|
1197
|
+
});
|
|
1102
1198
|
}
|
|
1103
1199
|
markPendingAssistantHistory(messageId, parentId) {
|
|
1104
1200
|
if (!this.history) return;
|
|
1105
1201
|
this.assistantHistoryParents.set(messageId, parentId);
|
|
1106
1202
|
}
|
|
1107
1203
|
persistAssistantHistory(messageId) {
|
|
1108
|
-
|
|
1204
|
+
const history = this.history;
|
|
1205
|
+
if (!history) return;
|
|
1109
1206
|
const parentId = this.assistantHistoryParents.get(messageId);
|
|
1110
1207
|
if (parentId === void 0) return;
|
|
1111
1208
|
const message = this.tryGetMessage(messageId)?.message;
|
|
1112
1209
|
if (!message || message.role !== "assistant") return;
|
|
1113
1210
|
if (!this.isPersistableStatus(message.status)) return;
|
|
1211
|
+
const wasPersisted = this.persistedHistoryIds.has(messageId);
|
|
1212
|
+
const update = history.update;
|
|
1213
|
+
if (update !== void 0 && (wasPersisted || this.snapshotHistoryIds.has(messageId))) {
|
|
1214
|
+
const write = this.chainHistoryWrite(messageId, () => update.call(history, {
|
|
1215
|
+
parentId,
|
|
1216
|
+
message
|
|
1217
|
+
}));
|
|
1218
|
+
this.assistantHistoryParents.delete(messageId);
|
|
1219
|
+
write.then(() => {
|
|
1220
|
+
this.persistedHistoryIds.add(messageId);
|
|
1221
|
+
}, (error) => {
|
|
1222
|
+
const pending = this.historyWrites.get(messageId);
|
|
1223
|
+
if (pending === void 0 || pending === write) this.assistantHistoryParents.set(messageId, parentId);
|
|
1224
|
+
this.logger.error?.("[agui] failed to update history entry", error);
|
|
1225
|
+
});
|
|
1226
|
+
return;
|
|
1227
|
+
}
|
|
1228
|
+
if (wasPersisted) {
|
|
1229
|
+
this.assistantHistoryParents.delete(messageId);
|
|
1230
|
+
return;
|
|
1231
|
+
}
|
|
1232
|
+
const write = this.appendHistoryItem(parentId, message);
|
|
1233
|
+
if (!write) return;
|
|
1114
1234
|
this.assistantHistoryParents.delete(messageId);
|
|
1115
|
-
|
|
1235
|
+
write.then(() => {}, (error) => {
|
|
1236
|
+
const pending = this.historyWrites.get(messageId);
|
|
1237
|
+
if (pending === void 0 || pending === write) this.assistantHistoryParents.set(messageId, parentId);
|
|
1238
|
+
this.logger.error?.("[agui] failed to append history entry", error);
|
|
1239
|
+
});
|
|
1116
1240
|
}
|
|
1117
1241
|
appendHistoryItem(parentId, message) {
|
|
1118
|
-
if (!this.history || this.
|
|
1119
|
-
this.
|
|
1120
|
-
|
|
1242
|
+
if (!this.history || this.persistedHistoryIds.has(message.id)) return;
|
|
1243
|
+
const pending = this.historyWrites.get(message.id);
|
|
1244
|
+
if (pending) return pending;
|
|
1245
|
+
const append = this.history.append.bind(this.history);
|
|
1246
|
+
const write = this.chainHistoryWrite(message.id, () => append({
|
|
1121
1247
|
parentId,
|
|
1122
1248
|
message
|
|
1123
|
-
})
|
|
1124
|
-
|
|
1125
|
-
this.
|
|
1249
|
+
}));
|
|
1250
|
+
write.then(() => {
|
|
1251
|
+
this.persistedHistoryIds.add(message.id);
|
|
1252
|
+
}, () => {});
|
|
1253
|
+
return write;
|
|
1254
|
+
}
|
|
1255
|
+
chainHistoryWrite(id, write) {
|
|
1256
|
+
const pending = this.historyWrites.get(id);
|
|
1257
|
+
let next;
|
|
1258
|
+
if (pending) next = pending.then(write, write);
|
|
1259
|
+
else try {
|
|
1260
|
+
next = Promise.resolve(write());
|
|
1261
|
+
} catch (error) {
|
|
1262
|
+
next = Promise.reject(error);
|
|
1263
|
+
}
|
|
1264
|
+
this.historyWrites.set(id, next);
|
|
1265
|
+
next.then(() => {
|
|
1266
|
+
if (this.historyWrites.get(id) === next) this.historyWrites.delete(id);
|
|
1267
|
+
}, () => {
|
|
1268
|
+
if (this.historyWrites.get(id) === next) this.historyWrites.delete(id);
|
|
1126
1269
|
});
|
|
1270
|
+
return next;
|
|
1127
1271
|
}
|
|
1128
1272
|
};
|
|
1129
1273
|
//#endregion
|