@assistant-ui/react-ag-ui 0.0.54 → 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 +169 -35
- 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 +58 -26
- package/dist/runtime/adapter/conversions.js.map +1 -1
- package/dist/runtime/adapter/run-aggregator.d.ts +5 -1
- package/dist/runtime/adapter/run-aggregator.d.ts.map +1 -1
- package/dist/runtime/adapter/run-aggregator.js +26 -1
- 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 +3 -0
- package/dist/useAgUiRuntime.js.map +1 -1
- package/package.json +7 -7
- package/src/index.ts +8 -2
- package/src/runtime/AgUiThreadRuntimeCore.ts +314 -41
- package/src/runtime/adapter/conversions.content.test.ts +61 -3
- package/src/runtime/adapter/conversions.ts +83 -39
- package/src/runtime/adapter/run-aggregator.approval.test.ts +103 -0
- package/src/runtime/adapter/run-aggregator.ts +37 -1
- 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.ts +4 -0
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";
|
|
@@ -51,6 +52,7 @@ var AgUiThreadRuntimeCore = class {
|
|
|
51
52
|
onError;
|
|
52
53
|
onCancel;
|
|
53
54
|
notifyUpdate;
|
|
55
|
+
reportedErrors = /* @__PURE__ */ new WeakSet();
|
|
54
56
|
runtime;
|
|
55
57
|
repository = new MessageRepository();
|
|
56
58
|
exportedRepository;
|
|
@@ -62,7 +64,9 @@ var AgUiThreadRuntimeCore = class {
|
|
|
62
64
|
history;
|
|
63
65
|
lastRunConfig;
|
|
64
66
|
assistantHistoryParents = /* @__PURE__ */ new Map();
|
|
65
|
-
|
|
67
|
+
snapshotHistoryIds = /* @__PURE__ */ new Set();
|
|
68
|
+
persistedHistoryIds = /* @__PURE__ */ new Set();
|
|
69
|
+
historyWrites = /* @__PURE__ */ new Map();
|
|
66
70
|
_isLoading = false;
|
|
67
71
|
_loadPromise;
|
|
68
72
|
pendingResumeMessageId = null;
|
|
@@ -94,6 +98,9 @@ var AgUiThreadRuntimeCore = class {
|
|
|
94
98
|
}
|
|
95
99
|
detachRuntime() {
|
|
96
100
|
this.runtime = void 0;
|
|
101
|
+
this.cancel().catch((error) => {
|
|
102
|
+
this.logger.error("[agui] failed to cancel run during teardown", error);
|
|
103
|
+
});
|
|
97
104
|
}
|
|
98
105
|
getMessages() {
|
|
99
106
|
return this.repository.getMessages();
|
|
@@ -298,17 +305,65 @@ var AgUiThreadRuntimeCore = class {
|
|
|
298
305
|
const openIds = pending.interrupts.map((i) => i.id);
|
|
299
306
|
const missing = openIds.filter((id) => !responsesById.has(id));
|
|
300
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) {
|
|
301
336
|
const now = Date.now();
|
|
302
|
-
for (const interrupt of
|
|
337
|
+
for (const interrupt of interrupts) {
|
|
303
338
|
if (!interrupt.expiresAt) continue;
|
|
304
339
|
const expiry = new Date(interrupt.expiresAt).getTime();
|
|
305
|
-
if (Number.isNaN(expiry)) throw new Error(`[agui]
|
|
306
|
-
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}`);
|
|
307
342
|
}
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
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);
|
|
312
367
|
}
|
|
313
368
|
async steerAway(message, responses) {
|
|
314
369
|
const pending = this.getPendingInterrupts();
|
|
@@ -331,7 +386,7 @@ var AgUiThreadRuntimeCore = class {
|
|
|
331
386
|
const resume = this.resolveSteerAwayResume(pending.interrupts, responses);
|
|
332
387
|
if (this.isRunningFlag) throw new Error("[agui] steerAway: a run is already in progress");
|
|
333
388
|
const normalized = this.toAppendMessage(message);
|
|
334
|
-
this.clearPendingInterrupts(pending.messageId);
|
|
389
|
+
this.clearPendingInterrupts(pending.messageId, resume);
|
|
335
390
|
const threadMessageId = this.appendEntry(normalized);
|
|
336
391
|
await this.startRun(threadMessageId, normalized.runConfig, resume);
|
|
337
392
|
}
|
|
@@ -380,7 +435,7 @@ var AgUiThreadRuntimeCore = class {
|
|
|
380
435
|
startRun: message.startRun
|
|
381
436
|
};
|
|
382
437
|
}
|
|
383
|
-
clearPendingInterrupts(messageId) {
|
|
438
|
+
clearPendingInterrupts(messageId, resume) {
|
|
384
439
|
if (this.updateMessage(messageId, (message) => {
|
|
385
440
|
if (message.role !== "assistant") return message;
|
|
386
441
|
const assistant = message;
|
|
@@ -391,6 +446,7 @@ var AgUiThreadRuntimeCore = class {
|
|
|
391
446
|
else delete newCustom["agui"];
|
|
392
447
|
return {
|
|
393
448
|
...assistant,
|
|
449
|
+
content: withSettledToolApprovals(assistant.content, resume),
|
|
394
450
|
status: {
|
|
395
451
|
type: "complete",
|
|
396
452
|
reason: "unknown"
|
|
@@ -543,8 +599,8 @@ var AgUiThreadRuntimeCore = class {
|
|
|
543
599
|
}
|
|
544
600
|
this.resetRepositoryHead(lastAppliedId);
|
|
545
601
|
}
|
|
546
|
-
this.
|
|
547
|
-
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);
|
|
548
604
|
this.notifyUpdate();
|
|
549
605
|
}
|
|
550
606
|
applyExternalMessageRepository(loaded) {
|
|
@@ -593,8 +649,9 @@ var AgUiThreadRuntimeCore = class {
|
|
|
593
649
|
this.resetRepositoryHead(previousId);
|
|
594
650
|
} else this.resetRepositoryHead(headId);
|
|
595
651
|
this.assistantHistoryParents.clear();
|
|
596
|
-
this.
|
|
597
|
-
|
|
652
|
+
this.snapshotHistoryIds.clear();
|
|
653
|
+
this.persistedHistoryIds.clear();
|
|
654
|
+
for (const { message } of loaded.messages) this.persistedHistoryIds.add(message.id);
|
|
598
655
|
this.notifyUpdate();
|
|
599
656
|
}
|
|
600
657
|
loadExternalState(state) {
|
|
@@ -615,6 +672,7 @@ var AgUiThreadRuntimeCore = class {
|
|
|
615
672
|
const parent = parentId === null ? void 0 : this.tryGetMessage(parentId);
|
|
616
673
|
const shouldEagerlyInsertAssistant = parentId !== null && parent !== void 0 && parentId !== this.repository.headId;
|
|
617
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));
|
|
618
676
|
this.pendingError = null;
|
|
619
677
|
const assistantParentId = parent ? parentId : this.repository.headId;
|
|
620
678
|
let assistantMessageId;
|
|
@@ -623,7 +681,8 @@ var AgUiThreadRuntimeCore = class {
|
|
|
623
681
|
const cached = assistantMessageId;
|
|
624
682
|
if (cached !== void 0 && this.tryGetMessage(cached)) return cached;
|
|
625
683
|
if (cached !== void 0 && (assistantCollided || !allowRecreate)) return cached;
|
|
626
|
-
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;
|
|
627
686
|
const created = this.insertAssistantPlaceholder(parentId, cached);
|
|
628
687
|
assistantMessageId = created;
|
|
629
688
|
this.markPendingAssistantHistory(created, parentId);
|
|
@@ -631,8 +690,8 @@ var AgUiThreadRuntimeCore = class {
|
|
|
631
690
|
};
|
|
632
691
|
if (shouldEagerlyInsertAssistant) ensureAssistant();
|
|
633
692
|
const applyUpdate = (update) => {
|
|
634
|
-
const
|
|
635
|
-
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);
|
|
636
695
|
if (resolved !== assistantMessageId) assistantMessageId = resolved;
|
|
637
696
|
};
|
|
638
697
|
const aggregator = new RunAggregator({
|
|
@@ -642,8 +701,15 @@ var AgUiThreadRuntimeCore = class {
|
|
|
642
701
|
onServerMessageId: (serverId) => {
|
|
643
702
|
const placeholder = ensureAssistant(true);
|
|
644
703
|
if (placeholder === serverId) return;
|
|
645
|
-
|
|
646
|
-
|
|
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;
|
|
647
713
|
}
|
|
648
714
|
});
|
|
649
715
|
const dispatch = (event) => this.handleEvent(aggregator, event, assistantMessageId);
|
|
@@ -714,6 +780,7 @@ var AgUiThreadRuntimeCore = class {
|
|
|
714
780
|
}
|
|
715
781
|
if (this.pendingError) {
|
|
716
782
|
const err = this.pendingError;
|
|
783
|
+
this.reportedErrors.add(err);
|
|
717
784
|
this.pendingError = null;
|
|
718
785
|
this.pendingResumeMessageId = null;
|
|
719
786
|
this.pendingA2uiResume = false;
|
|
@@ -885,9 +952,24 @@ var AgUiThreadRuntimeCore = class {
|
|
|
885
952
|
this.assistantHistoryParents.delete(oldId);
|
|
886
953
|
if (!collidesWithExisting && !this.assistantHistoryParents.has(newId)) this.assistantHistoryParents.set(newId, pendingParent);
|
|
887
954
|
}
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
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
|
+
}
|
|
891
973
|
}
|
|
892
974
|
this.notifyUpdate();
|
|
893
975
|
return !collidesWithExisting;
|
|
@@ -1084,14 +1166,16 @@ var AgUiThreadRuntimeCore = class {
|
|
|
1084
1166
|
} catch (error) {
|
|
1085
1167
|
this.logger.error?.("[agui] failed to import message from snapshot", error);
|
|
1086
1168
|
}
|
|
1087
|
-
const snapshotHeadId = converted.at(-1)?.id ?? null;
|
|
1088
1169
|
const snapshotContainsActiveAssistant = converted.some((message) => message.id === activeAssistant?.id);
|
|
1089
1170
|
const preservesActiveAssistant = activeAssistant !== void 0 && !snapshotContainsActiveAssistant && (activeAssistant.metadata.isOptimistic !== true || converted.at(-1)?.role !== "assistant");
|
|
1090
1171
|
if (preservesActiveAssistant) converted.push(activeAssistant);
|
|
1091
1172
|
this.applyExternalMessages(converted);
|
|
1092
|
-
if (
|
|
1093
|
-
this.
|
|
1094
|
-
|
|
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
|
+
}
|
|
1095
1179
|
}
|
|
1096
1180
|
} catch (error) {
|
|
1097
1181
|
this.logger.error?.("[agui] failed to import messages snapshot", error);
|
|
@@ -1108,32 +1192,82 @@ var AgUiThreadRuntimeCore = class {
|
|
|
1108
1192
|
return status?.type === "requires-action" && status.reason === "interrupt";
|
|
1109
1193
|
}
|
|
1110
1194
|
recordHistoryEntry(parentId, message) {
|
|
1111
|
-
this.appendHistoryItem(parentId, message)
|
|
1195
|
+
this.appendHistoryItem(parentId, message)?.catch((error) => {
|
|
1196
|
+
this.logger.error?.("[agui] failed to append history entry", error);
|
|
1197
|
+
});
|
|
1112
1198
|
}
|
|
1113
1199
|
markPendingAssistantHistory(messageId, parentId) {
|
|
1114
1200
|
if (!this.history) return;
|
|
1115
1201
|
this.assistantHistoryParents.set(messageId, parentId);
|
|
1116
1202
|
}
|
|
1117
1203
|
persistAssistantHistory(messageId) {
|
|
1118
|
-
|
|
1204
|
+
const history = this.history;
|
|
1205
|
+
if (!history) return;
|
|
1119
1206
|
const parentId = this.assistantHistoryParents.get(messageId);
|
|
1120
1207
|
if (parentId === void 0) return;
|
|
1121
1208
|
const message = this.tryGetMessage(messageId)?.message;
|
|
1122
1209
|
if (!message || message.role !== "assistant") return;
|
|
1123
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;
|
|
1124
1234
|
this.assistantHistoryParents.delete(messageId);
|
|
1125
|
-
|
|
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
|
+
});
|
|
1126
1240
|
}
|
|
1127
1241
|
appendHistoryItem(parentId, message) {
|
|
1128
|
-
if (!this.history || this.
|
|
1129
|
-
this.
|
|
1130
|
-
|
|
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({
|
|
1131
1247
|
parentId,
|
|
1132
1248
|
message
|
|
1133
|
-
})
|
|
1134
|
-
|
|
1135
|
-
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);
|
|
1136
1269
|
});
|
|
1270
|
+
return next;
|
|
1137
1271
|
}
|
|
1138
1272
|
};
|
|
1139
1273
|
//#endregion
|