@assistant-ui/react-ag-ui 0.0.33 → 0.0.35
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/runtime/AgUiThreadRuntimeCore.d.ts +8 -2
- package/dist/runtime/AgUiThreadRuntimeCore.d.ts.map +1 -1
- package/dist/runtime/AgUiThreadRuntimeCore.js +161 -52
- package/dist/runtime/AgUiThreadRuntimeCore.js.map +1 -1
- package/dist/runtime/types.d.ts +3 -2
- package/dist/runtime/types.d.ts.map +1 -1
- package/dist/useAgUiRuntime.d.ts.map +1 -1
- package/dist/useAgUiRuntime.js +6 -2
- package/dist/useAgUiRuntime.js.map +1 -1
- package/package.json +6 -5
- package/src/runtime/AgUiThreadRuntimeCore.ts +253 -87
- package/src/runtime/types.ts +4 -1
- package/src/useAgUiRuntime.ts +5 -1
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import { Logger } from "./logger.js";
|
|
2
2
|
import { AgUiInterrupt, AgUiResumeEntry } from "./types.js";
|
|
3
|
-
import { AddToolResultOptions, AppendMessage, AssistantRuntime, ThreadHistoryAdapter, ThreadMessage } from "@assistant-ui/core";
|
|
3
|
+
import { AddToolResultOptions, AppendMessage, AssistantRuntime, ChatModelRunOptions, ChatModelRunResult, ThreadHistoryAdapter, ThreadMessage } from "@assistant-ui/core";
|
|
4
4
|
import { HttpAgent } from "@ag-ui/client";
|
|
5
5
|
import { ReadonlyJSONValue } from "assistant-stream/utils";
|
|
6
6
|
|
|
7
7
|
//#region src/runtime/AgUiThreadRuntimeCore.d.ts
|
|
8
8
|
type RunConfig = NonNullable<AppendMessage["runConfig"]>;
|
|
9
|
+
type ResumeStream = (options: ChatModelRunOptions) => AsyncGenerator<ChatModelRunResult, void, unknown>;
|
|
9
10
|
type ResumeRunConfig = {
|
|
10
11
|
parentId: string | null;
|
|
11
12
|
sourceId: string | null;
|
|
12
13
|
runConfig: RunConfig;
|
|
13
|
-
stream?:
|
|
14
|
+
stream?: ResumeStream;
|
|
14
15
|
};
|
|
15
16
|
type CoreOptions = {
|
|
16
17
|
agent: HttpAgent;
|
|
@@ -40,6 +41,7 @@ declare class AgUiThreadRuntimeCore {
|
|
|
40
41
|
private readonly recordedHistoryIds;
|
|
41
42
|
private _isLoading;
|
|
42
43
|
private _loadPromise;
|
|
44
|
+
private pendingResumeMessageId;
|
|
43
45
|
constructor(options: CoreOptions);
|
|
44
46
|
updateOptions(options: Omit<CoreOptions, "notifyUpdate">): void;
|
|
45
47
|
attachRuntime(runtime: AssistantRuntime): void;
|
|
@@ -65,9 +67,12 @@ declare class AgUiThreadRuntimeCore {
|
|
|
65
67
|
private clearPendingInterrupts;
|
|
66
68
|
findMessageIdForToolCall(toolCallId: string): string | undefined;
|
|
67
69
|
addToolResult(options: AddToolResultOptions): void;
|
|
70
|
+
private maybeResumeAfterToolResults;
|
|
71
|
+
private startResumeRun;
|
|
68
72
|
applyExternalMessages(messages: readonly ThreadMessage[]): void;
|
|
69
73
|
loadExternalState(state: ReadonlyJSONValue): void;
|
|
70
74
|
private startRun;
|
|
75
|
+
private consumeResumeStream;
|
|
71
76
|
private buildRunInput;
|
|
72
77
|
private installResumeShim;
|
|
73
78
|
private setRunning;
|
|
@@ -75,6 +80,7 @@ declare class AgUiThreadRuntimeCore {
|
|
|
75
80
|
private insertAssistantPlaceholder;
|
|
76
81
|
private reassignAssistantId;
|
|
77
82
|
private updateAssistantMessage;
|
|
83
|
+
private preserveToolResults;
|
|
78
84
|
private mergeAssistantMetadata;
|
|
79
85
|
private handleEvent;
|
|
80
86
|
private importMessagesSnapshot;
|
|
@@ -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":";;;;;;;KAsCK,SAAA,GAAY,WAAW,CAAC,aAAA;AAAA,KACxB,YAAA,IACH,OAAA,EAAS,mBAAA,KACN,cAAA,CAAe,kBAAA;AAAA,KACf,eAAA;EACH,QAAA;EACA,QAAA;EACA,SAAA,EAAW,SAAA;EACX,MAAA,GAAS,YAAY;AAAA;AAAA,KAGlB,WAAA;EACH,KAAA,EAAO,SAAA;EACP,MAAA,EAAQ,MAAA;EACR,YAAA;EACA,OAAA,IAAW,KAAA,EAAO,KAAA;EAClB,QAAA;EACA,OAAA,GAAU,oBAAA;EACV,YAAA;AAAA;AAAA,cAKW,qBAAA;EAAA,QACH,KAAA;EAAA,QACA,MAAA;EAAA,QACA,YAAA;EAAA,QACA,OAAA;EAAA,QACA,QAAA;EAAA,iBACS,YAAA;EAAA,QAET,OAAA;EAAA,QACA,QAAA;EAAA,QACA,aAAA;EAAA,QACA,eAAA;EAAA,QACA,aAAA;EAAA,QACA,YAAA;EAAA,QACA,OAAA;EAAA,QACA,aAAA;EAAA,iBACS,uBAAA;EAAA,iBACA,kBAAA;EAAA,QACT,UAAA;EAAA,QACA,YAAA;EAAA,QACA,sBAAA;cAEI,OAAA,EAAS,WAAA;EAWrB,aAAA,CAAc,OAAA,EAAS,IAAA,CAAK,WAAA;EAU5B,aAAA,CAAc,OAAA,EAAS,gBAAA;EAIvB,aAAA;EAIA,WAAA,aAAwB,aAAA;EAIxB,QAAA,IAAY,iBAAA;EAIZ,SAAA;EAAA,IAII,SAAA;EAIJ,eAAA,IAAmB,OAAA;EAwCb,MAAA,CAAO,OAAA,EAAS,aAAA,GAAgB,OAAA;EAmBhC,IAAA,CAAK,OAAA,EAAS,aAAA,GAAgB,OAAA;EAI9B,MAAA,CACJ,QAAA,iBACA,MAAA;IAAU,SAAA,GAAY,SAAA;EAAA,IACrB,OAAA;EAOG,MAAA,IAAU,OAAA;EAKV,MAAA,CAAO,MAAA,EAAQ,eAAA,GAAkB,OAAA;EAAA,QAU/B,yBAAA;EAOR,oBAAA;IACE,SAAA;IACA,UAAA,WAAqB,aAAA;EAAA;EAqBjB,wBAAA,CACJ,SAAA,WAAoB,eAAA,KACnB,OAAA;EAAA,QAyEK,sBAAA;EAgCR,wBAAA,CAAyB,UAAA;EAiBzB,aAAA,CAAc,OAAA,EAAS,oBAAA;EAAA,QAiCf,2BAAA;EAAA,QAqCA,cAAA;EAMR,qBAAA,CAAsB,QAAA,WAAmB,aAAA;EAUzC,iBAAA,CAAkB,KAAA,EAAO,iBAAA;EAAA,QAKX,QAAA;EAAA,QAqIA,mBAAA;EAAA,QAyDN,aAAA;EAAA,QA2BA,iBAAA;EAAA,QAqBA,UAAA;EAAA,QAKA,SAAA;EAAA,QAOA,0BAAA;EAAA,QAqBA,mBAAA;EAAA,QAiCA,sBAAA;EAAA,QAoDA,mBAAA;EAAA,QAiCA,sBAAA;EAAA,QA4BA,WAAA;EAAA,QAiCA,sBAAA;EAAA,QA0BA,eAAA;EAAA,QAQA,SAAA;EAAA,QAYA,gBAAA;EAAA,QAIA,mBAAA;EAAA,QAKA,kBAAA;EAAA,QAIA,2BAAA;EAAA,QAQA,uBAAA;EAAA,QAWA,iBAAA;AAAA"}
|
|
@@ -2,8 +2,12 @@
|
|
|
2
2
|
import { AG_UI_METADATA_NAMESPACE, RunAggregator } from "./adapter/run-aggregator.js";
|
|
3
3
|
import { fromAgUiMessages, toAgUiMessages, toAgUiTools } from "./adapter/conversions.js";
|
|
4
4
|
import { createAgUiSubscriber } from "./adapter/subscriber.js";
|
|
5
|
-
import { fromThreadMessageLike, generateId
|
|
5
|
+
import { fromThreadMessageLike, generateId } from "@assistant-ui/core/internal";
|
|
6
|
+
import jsonpatch from "fast-json-patch";
|
|
6
7
|
//#region src/runtime/AgUiThreadRuntimeCore.ts
|
|
8
|
+
const optimisticPrefix = "__optimistic__";
|
|
9
|
+
const generateOptimisticId = () => `${optimisticPrefix}${generateId()}`;
|
|
10
|
+
const isOptimisticId = (id) => id.startsWith(optimisticPrefix);
|
|
7
11
|
const symbolResumeShim = Symbol("agui-resume-shim");
|
|
8
12
|
const FALLBACK_USER_STATUS = {
|
|
9
13
|
type: "complete",
|
|
@@ -28,6 +32,7 @@ var AgUiThreadRuntimeCore = class {
|
|
|
28
32
|
recordedHistoryIds = /* @__PURE__ */ new Set();
|
|
29
33
|
_isLoading = false;
|
|
30
34
|
_loadPromise;
|
|
35
|
+
pendingResumeMessageId = null;
|
|
31
36
|
constructor(options) {
|
|
32
37
|
this.agent = options.agent;
|
|
33
38
|
this.logger = options.logger;
|
|
@@ -75,7 +80,8 @@ var AgUiThreadRuntimeCore = class {
|
|
|
75
80
|
this.applyExternalMessages(messages);
|
|
76
81
|
if (repo.unstable_resume) {
|
|
77
82
|
const parentId = repo.headId ?? messages.at(-1)?.id ?? null;
|
|
78
|
-
|
|
83
|
+
const resumeStream = this.history?.resume?.bind(this.history);
|
|
84
|
+
await this.startRun(parentId, this.lastRunConfig, void 0, resumeStream);
|
|
79
85
|
}
|
|
80
86
|
}).catch((error) => {
|
|
81
87
|
this.logger.error?.("[agui] failed to load history", error);
|
|
@@ -114,8 +120,7 @@ var AgUiThreadRuntimeCore = class {
|
|
|
114
120
|
}
|
|
115
121
|
async resume(config) {
|
|
116
122
|
this.assertNoPendingInterrupts();
|
|
117
|
-
|
|
118
|
-
await this.startRun(config.parentId, config.runConfig ?? this.lastRunConfig);
|
|
123
|
+
await this.startRun(config.parentId, config.runConfig ?? this.lastRunConfig, void 0, config.stream);
|
|
119
124
|
}
|
|
120
125
|
assertNoPendingInterrupts() {
|
|
121
126
|
if (!this.getPendingInterrupts()) return;
|
|
@@ -199,7 +204,6 @@ var AgUiThreadRuntimeCore = class {
|
|
|
199
204
|
}
|
|
200
205
|
addToolResult(options) {
|
|
201
206
|
let updated = false;
|
|
202
|
-
let shouldResume = false;
|
|
203
207
|
this.messages = this.messages.map((message) => {
|
|
204
208
|
if (message.id !== options.messageId || message.role !== "assistant") return message;
|
|
205
209
|
const assistant = message;
|
|
@@ -216,31 +220,40 @@ var AgUiThreadRuntimeCore = class {
|
|
|
216
220
|
});
|
|
217
221
|
if (!matchedToolCall) return message;
|
|
218
222
|
updated = true;
|
|
219
|
-
if (assistant.status?.type === "requires-action" && assistant.status.reason === "tool-calls" && content.every((part) => part.type !== "tool-call" || "result" in part && part.result !== void 0)) {
|
|
220
|
-
shouldResume = true;
|
|
221
|
-
return {
|
|
222
|
-
...assistant,
|
|
223
|
-
content,
|
|
224
|
-
status: {
|
|
225
|
-
type: "complete",
|
|
226
|
-
reason: "unknown"
|
|
227
|
-
}
|
|
228
|
-
};
|
|
229
|
-
}
|
|
230
223
|
return {
|
|
231
224
|
...assistant,
|
|
232
225
|
content
|
|
233
226
|
};
|
|
234
227
|
});
|
|
235
|
-
if (updated)
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
228
|
+
if (!updated) return;
|
|
229
|
+
this.notifyUpdate();
|
|
230
|
+
this.maybeResumeAfterToolResults(options.messageId);
|
|
231
|
+
}
|
|
232
|
+
maybeResumeAfterToolResults(messageId) {
|
|
233
|
+
const message = this.messages.find((m) => m.id === messageId);
|
|
234
|
+
if (!message || message.role !== "assistant") return;
|
|
235
|
+
const assistant = message;
|
|
236
|
+
if (assistant.status?.type !== "requires-action" || assistant.status.reason !== "tool-calls") return;
|
|
237
|
+
if (!assistant.content.every((part) => part.type !== "tool-call" || "result" in part && part.result !== void 0)) return;
|
|
238
|
+
this.messages = this.messages.map((m) => m.id === messageId && m.role === "assistant" ? {
|
|
239
|
+
...m,
|
|
240
|
+
status: {
|
|
241
|
+
type: "complete",
|
|
242
|
+
reason: "unknown"
|
|
242
243
|
}
|
|
244
|
+
} : m);
|
|
245
|
+
this.notifyUpdate();
|
|
246
|
+
this.persistAssistantHistory(messageId);
|
|
247
|
+
if (this.isRunningFlag) {
|
|
248
|
+
this.pendingResumeMessageId = messageId;
|
|
249
|
+
return;
|
|
243
250
|
}
|
|
251
|
+
this.startResumeRun(messageId);
|
|
252
|
+
}
|
|
253
|
+
startResumeRun(messageId) {
|
|
254
|
+
this.startRun(messageId, this.lastRunConfig).catch((error) => {
|
|
255
|
+
this.onError?.(error instanceof Error ? error : new Error(String(error)));
|
|
256
|
+
});
|
|
244
257
|
}
|
|
245
258
|
applyExternalMessages(messages) {
|
|
246
259
|
this.assistantHistoryParents.clear();
|
|
@@ -253,14 +266,12 @@ var AgUiThreadRuntimeCore = class {
|
|
|
253
266
|
this.stateSnapshot = state;
|
|
254
267
|
this.notifyUpdate();
|
|
255
268
|
}
|
|
256
|
-
async startRun(parentId, runConfig, resume) {
|
|
269
|
+
async startRun(parentId, runConfig, resume, resumeStream) {
|
|
257
270
|
const normalizedRunConfig = runConfig ?? {};
|
|
258
271
|
this.lastRunConfig = normalizedRunConfig;
|
|
259
272
|
this.resetHead(parentId);
|
|
260
273
|
const historicalMessages = [...this.messages];
|
|
261
|
-
const runId = generateId();
|
|
262
274
|
this.pendingError = null;
|
|
263
|
-
const input = this.buildRunInput(runId, normalizedRunConfig, historicalMessages, resume);
|
|
264
275
|
const assistantParentId = parentId ?? this.messages.at(-1)?.id ?? null;
|
|
265
276
|
let assistantMessageId;
|
|
266
277
|
const ensureAssistant = () => {
|
|
@@ -270,13 +281,14 @@ var AgUiThreadRuntimeCore = class {
|
|
|
270
281
|
this.markPendingAssistantHistory(created, assistantParentId ?? null);
|
|
271
282
|
return created;
|
|
272
283
|
};
|
|
284
|
+
const applyUpdate = (update) => {
|
|
285
|
+
const resolved = this.updateAssistantMessage(ensureAssistant(), update);
|
|
286
|
+
if (resolved !== assistantMessageId) assistantMessageId = resolved;
|
|
287
|
+
};
|
|
273
288
|
const aggregator = new RunAggregator({
|
|
274
289
|
showThinking: this.showThinking,
|
|
275
290
|
logger: this.logger,
|
|
276
|
-
emit:
|
|
277
|
-
const resolved = this.updateAssistantMessage(ensureAssistant(), update);
|
|
278
|
-
if (resolved !== assistantMessageId) assistantMessageId = resolved;
|
|
279
|
-
},
|
|
291
|
+
emit: applyUpdate,
|
|
280
292
|
onServerMessageId: (serverId) => {
|
|
281
293
|
const placeholder = ensureAssistant();
|
|
282
294
|
if (placeholder === serverId) return;
|
|
@@ -288,32 +300,52 @@ var AgUiThreadRuntimeCore = class {
|
|
|
288
300
|
const abortController = new AbortController();
|
|
289
301
|
const abortSignal = abortController.signal;
|
|
290
302
|
this.abortController = abortController;
|
|
303
|
+
let cancelRun = () => dispatch({ type: "RUN_CANCELLED" });
|
|
291
304
|
abortSignal.addEventListener("abort", () => {
|
|
292
|
-
|
|
305
|
+
cancelRun();
|
|
293
306
|
this.finishRun(abortController);
|
|
294
307
|
this.onCancel?.();
|
|
295
308
|
}, { once: true });
|
|
296
|
-
const subscriber = createAgUiSubscriber({
|
|
297
|
-
dispatch,
|
|
298
|
-
runId,
|
|
299
|
-
logger: this.logger,
|
|
300
|
-
onRunFailed: (error) => {
|
|
301
|
-
this.pendingError = error;
|
|
302
|
-
this.onError?.(error);
|
|
303
|
-
}
|
|
304
|
-
});
|
|
305
|
-
aggregator.handle({
|
|
306
|
-
type: "RUN_STARTED",
|
|
307
|
-
runId
|
|
308
|
-
});
|
|
309
309
|
this.setRunning(true);
|
|
310
310
|
try {
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
311
|
+
if (resumeStream) {
|
|
312
|
+
cancelRun = () => applyUpdate({ status: {
|
|
313
|
+
type: "incomplete",
|
|
314
|
+
reason: "cancelled"
|
|
315
|
+
} });
|
|
316
|
+
await this.consumeResumeStream(resumeStream, {
|
|
317
|
+
runConfig: normalizedRunConfig,
|
|
318
|
+
threadId: this.agent.threadId || "main",
|
|
319
|
+
parentId: assistantParentId,
|
|
320
|
+
historicalMessages,
|
|
321
|
+
abortSignal,
|
|
322
|
+
ensureAssistant,
|
|
323
|
+
applyUpdate,
|
|
324
|
+
getAssistantMessageId: () => assistantMessageId
|
|
325
|
+
});
|
|
326
|
+
} else {
|
|
327
|
+
const runId = generateId();
|
|
328
|
+
aggregator.handle({
|
|
329
|
+
type: "RUN_STARTED",
|
|
330
|
+
runId
|
|
331
|
+
});
|
|
332
|
+
const input = this.buildRunInput(runId, normalizedRunConfig, historicalMessages, resume);
|
|
333
|
+
const subscriber = createAgUiSubscriber({
|
|
334
|
+
dispatch,
|
|
335
|
+
runId,
|
|
336
|
+
logger: this.logger,
|
|
337
|
+
onRunFailed: (error) => {
|
|
338
|
+
this.pendingError = error;
|
|
339
|
+
this.onError?.(error);
|
|
340
|
+
}
|
|
341
|
+
});
|
|
342
|
+
try {
|
|
343
|
+
this.agent.messages = input.messages;
|
|
344
|
+
this.agent.threadId = input.threadId;
|
|
345
|
+
this.agent.state = input.state ?? null;
|
|
346
|
+
} catch {}
|
|
347
|
+
await this.agent.runAgent(input, subscriber, { signal: abortSignal });
|
|
348
|
+
}
|
|
317
349
|
} catch (error) {
|
|
318
350
|
if (!abortSignal.aborted) {
|
|
319
351
|
const err = error instanceof Error ? error : new Error(String(error));
|
|
@@ -330,8 +362,55 @@ var AgUiThreadRuntimeCore = class {
|
|
|
330
362
|
if (this.pendingError) {
|
|
331
363
|
const err = this.pendingError;
|
|
332
364
|
this.pendingError = null;
|
|
365
|
+
this.pendingResumeMessageId = null;
|
|
333
366
|
throw err;
|
|
334
367
|
}
|
|
368
|
+
if (this.pendingResumeMessageId !== null) {
|
|
369
|
+
const resumeMessageId = this.pendingResumeMessageId;
|
|
370
|
+
this.pendingResumeMessageId = null;
|
|
371
|
+
if (!abortSignal.aborted) this.startResumeRun(resumeMessageId);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
async consumeResumeStream(stream, ctx) {
|
|
375
|
+
const assistantId = ctx.ensureAssistant();
|
|
376
|
+
const currentId = () => ctx.getAssistantMessageId() ?? assistantId;
|
|
377
|
+
const options = {
|
|
378
|
+
messages: ctx.historicalMessages,
|
|
379
|
+
runConfig: ctx.runConfig,
|
|
380
|
+
abortSignal: ctx.abortSignal,
|
|
381
|
+
context: this.runtime?.thread.getModelContext() ?? {},
|
|
382
|
+
unstable_assistantMessageId: assistantId,
|
|
383
|
+
unstable_threadId: ctx.threadId,
|
|
384
|
+
unstable_parentId: ctx.parentId,
|
|
385
|
+
unstable_getMessage: () => {
|
|
386
|
+
const message = this.messages.find((m) => m.id === currentId());
|
|
387
|
+
if (!message) throw new Error("[agui] resume stream requested the assistant message before it existed");
|
|
388
|
+
return message;
|
|
389
|
+
}
|
|
390
|
+
};
|
|
391
|
+
try {
|
|
392
|
+
for await (const result of stream(options)) {
|
|
393
|
+
if (ctx.abortSignal.aborted) return;
|
|
394
|
+
ctx.applyUpdate(result);
|
|
395
|
+
}
|
|
396
|
+
} catch (error) {
|
|
397
|
+
if (ctx.abortSignal.aborted) return;
|
|
398
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
399
|
+
ctx.applyUpdate({ status: {
|
|
400
|
+
type: "incomplete",
|
|
401
|
+
reason: "error",
|
|
402
|
+
error: err.message
|
|
403
|
+
} });
|
|
404
|
+
this.onError?.(err);
|
|
405
|
+
this.pendingError = this.pendingError ?? err;
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
408
|
+
if (ctx.abortSignal.aborted) return;
|
|
409
|
+
const current = this.messages.find((m) => m.id === currentId());
|
|
410
|
+
if (!current || current.status?.type === "running") ctx.applyUpdate({ status: {
|
|
411
|
+
type: "complete",
|
|
412
|
+
reason: "unknown"
|
|
413
|
+
} });
|
|
335
414
|
}
|
|
336
415
|
buildRunInput(runId, runConfig, historyMessages, resume) {
|
|
337
416
|
const threadId = this.agent.threadId || "main";
|
|
@@ -430,9 +509,10 @@ var AgUiThreadRuntimeCore = class {
|
|
|
430
509
|
const assistant = message;
|
|
431
510
|
const metadata = update.metadata ? this.mergeAssistantMetadata(assistant.metadata, update.metadata) : assistant.metadata;
|
|
432
511
|
latestStatus = update.status ?? assistant.status;
|
|
512
|
+
const content = update.content !== void 0 ? this.preserveToolResults(assistant.content, update.content) : assistant.content;
|
|
433
513
|
return {
|
|
434
514
|
...assistant,
|
|
435
|
-
content
|
|
515
|
+
content,
|
|
436
516
|
status: latestStatus,
|
|
437
517
|
metadata
|
|
438
518
|
};
|
|
@@ -445,8 +525,29 @@ var AgUiThreadRuntimeCore = class {
|
|
|
445
525
|
resolvedMessageId = stableId;
|
|
446
526
|
} else this.notifyUpdate();
|
|
447
527
|
if (this.isPersistableStatus(latestStatus)) this.persistAssistantHistory(resolvedMessageId);
|
|
528
|
+
this.maybeResumeAfterToolResults(resolvedMessageId);
|
|
448
529
|
return resolvedMessageId;
|
|
449
530
|
}
|
|
531
|
+
preserveToolResults(previous, next) {
|
|
532
|
+
const resolved = /* @__PURE__ */ new Map();
|
|
533
|
+
for (const part of previous) if (part.type === "tool-call" && "result" in part && part.result !== void 0) resolved.set(part.toolCallId, part);
|
|
534
|
+
if (resolved.size === 0) return next;
|
|
535
|
+
let changed = false;
|
|
536
|
+
const merged = next.map((part) => {
|
|
537
|
+
if (part.type !== "tool-call") return part;
|
|
538
|
+
if ("result" in part && part.result !== void 0) return part;
|
|
539
|
+
const prior = resolved.get(part.toolCallId);
|
|
540
|
+
if (!prior) return part;
|
|
541
|
+
changed = true;
|
|
542
|
+
return {
|
|
543
|
+
...part,
|
|
544
|
+
result: prior.result,
|
|
545
|
+
...prior.artifact !== void 0 ? { artifact: prior.artifact } : {},
|
|
546
|
+
...prior.isError !== void 0 ? { isError: prior.isError } : {}
|
|
547
|
+
};
|
|
548
|
+
});
|
|
549
|
+
return changed ? merged : next;
|
|
550
|
+
}
|
|
450
551
|
mergeAssistantMetadata(current, incoming) {
|
|
451
552
|
const annotations = incoming.unstable_annotations ? [...current.unstable_annotations, ...incoming.unstable_annotations] : current.unstable_annotations;
|
|
452
553
|
const data = incoming.unstable_data ? [...current.unstable_data, ...incoming.unstable_data] : current.unstable_data;
|
|
@@ -470,7 +571,15 @@ var AgUiThreadRuntimeCore = class {
|
|
|
470
571
|
this.notifyUpdate();
|
|
471
572
|
return;
|
|
472
573
|
case "STATE_DELTA":
|
|
473
|
-
|
|
574
|
+
if (event.delta.length === 0) return;
|
|
575
|
+
try {
|
|
576
|
+
const state = this.stateSnapshot ?? {};
|
|
577
|
+
const result = jsonpatch.applyPatch(state, event.delta, true, false);
|
|
578
|
+
this.stateSnapshot = result.newDocument;
|
|
579
|
+
this.notifyUpdate();
|
|
580
|
+
} catch (error) {
|
|
581
|
+
this.logger.error?.("[agui] failed to apply state delta", error);
|
|
582
|
+
}
|
|
474
583
|
return;
|
|
475
584
|
case "MESSAGES_SNAPSHOT":
|
|
476
585
|
this.importMessagesSnapshot(event.messages);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AgUiThreadRuntimeCore.js","names":[],"sources":["../../src/runtime/AgUiThreadRuntimeCore.ts"],"sourcesContent":["\"use client\";\n\nimport {\n generateId,\n generateOptimisticId,\n isOptimisticId,\n fromThreadMessageLike,\n} from \"@assistant-ui/core/internal\";\nimport type {\n AddToolResultOptions,\n AppendMessage,\n AssistantRuntime,\n ChatModelRunResult,\n MessageStatus,\n ThreadAssistantMessage,\n ThreadHistoryAdapter,\n ThreadMessage,\n} from \"@assistant-ui/core\";\nimport type { HttpAgent } from \"@ag-ui/client\";\nimport type { Logger } from \"./logger\";\nimport type { AgUiEvent, AgUiInterrupt, AgUiResumeEntry } from \"./types\";\nimport type { ReadonlyJSONValue } from \"assistant-stream/utils\";\nimport {\n AG_UI_METADATA_NAMESPACE,\n type AgUiCustomMetadata,\n RunAggregator,\n} from \"./adapter/run-aggregator\";\nimport {\n fromAgUiMessages,\n toAgUiMessages,\n toAgUiTools,\n} from \"./adapter/conversions\";\nimport { createAgUiSubscriber } from \"./adapter/subscriber\";\n\nconst symbolResumeShim = Symbol(\"agui-resume-shim\");\n\ntype RunConfig = NonNullable<AppendMessage[\"runConfig\"]>;\ntype ResumeRunConfig = {\n parentId: string | null;\n sourceId: string | null;\n runConfig: RunConfig;\n stream?: unknown;\n};\n\ntype CoreOptions = {\n agent: HttpAgent;\n logger: Logger;\n showThinking: boolean;\n onError?: (error: Error) => void;\n onCancel?: () => void;\n history?: ThreadHistoryAdapter;\n notifyUpdate: () => void;\n};\n\nconst FALLBACK_USER_STATUS = { type: \"complete\", reason: \"unknown\" } as const;\n\nexport class AgUiThreadRuntimeCore {\n private agent: HttpAgent;\n private logger: Logger;\n private showThinking: boolean;\n private onError: ((error: Error) => void) | undefined;\n private onCancel: (() => void) | undefined;\n private readonly notifyUpdate: () => void;\n\n private runtime: AssistantRuntime | undefined;\n private messages: ThreadMessage[] = [];\n private isRunningFlag = false;\n private abortController: AbortController | null = null;\n private stateSnapshot: ReadonlyJSONValue | undefined;\n private pendingError: Error | null = null;\n private history: ThreadHistoryAdapter | undefined;\n private lastRunConfig: RunConfig | undefined;\n private readonly assistantHistoryParents = new Map<string, string | null>();\n private readonly recordedHistoryIds = new Set<string>();\n private _isLoading = false;\n private _loadPromise: Promise<void> | undefined;\n\n constructor(options: CoreOptions) {\n this.agent = options.agent;\n this.logger = options.logger;\n this.showThinking = options.showThinking;\n this.onError = options.onError;\n this.onCancel = options.onCancel;\n this.history = options.history;\n this.notifyUpdate = options.notifyUpdate;\n this.installResumeShim();\n }\n\n updateOptions(options: Omit<CoreOptions, \"notifyUpdate\">) {\n this.agent = options.agent;\n this.logger = options.logger;\n this.showThinking = options.showThinking;\n this.onError = options.onError;\n this.onCancel = options.onCancel;\n this.history = options.history;\n this.installResumeShim();\n }\n\n attachRuntime(runtime: AssistantRuntime) {\n this.runtime = runtime;\n }\n\n detachRuntime() {\n this.runtime = undefined;\n }\n\n getMessages(): readonly ThreadMessage[] {\n return this.messages;\n }\n\n getState(): ReadonlyJSONValue | undefined {\n return this.stateSnapshot;\n }\n\n isRunning(): boolean {\n return this.isRunningFlag;\n }\n\n get isLoading(): boolean {\n return this._isLoading;\n }\n\n __internal_load(): Promise<void> {\n if (this._loadPromise) return this._loadPromise;\n\n const promise = this.history?.load() ?? Promise.resolve(null);\n\n this._isLoading = true;\n\n this._loadPromise = promise\n .then(async (repo) => {\n if (!repo) return;\n\n const messages = repo.messages.map((item) => item.message);\n this.applyExternalMessages(messages);\n\n if (repo.unstable_resume) {\n const parentId = repo.headId ?? messages.at(-1)?.id ?? null;\n await this.startRun(parentId, this.lastRunConfig);\n }\n })\n .catch((error) => {\n this.logger.error?.(\"[agui] failed to load history\", error);\n this.onError?.(\n error instanceof Error ? error : new Error(String(error)),\n );\n })\n .finally(() => {\n this._isLoading = false;\n this.notifyUpdate();\n });\n\n this.notifyUpdate();\n return this._loadPromise;\n }\n\n async append(message: AppendMessage): Promise<void> {\n const startRun = message.startRun ?? message.role === \"user\";\n if (startRun) this.assertNoPendingInterrupts();\n if (message.sourceId) {\n this.messages = this.messages.filter(\n (entry) => entry.id !== message.sourceId,\n );\n }\n this.resetHead(message.parentId);\n\n const threadMessage = this.toThreadMessage(message);\n this.messages = [...this.messages, threadMessage];\n this.notifyUpdate();\n this.recordHistoryEntry(message.parentId ?? null, threadMessage);\n\n if (!startRun) return;\n await this.startRun(threadMessage.id, message.runConfig);\n }\n\n async edit(message: AppendMessage): Promise<void> {\n await this.append(message);\n }\n\n async reload(\n parentId: string | null,\n config: { runConfig?: RunConfig } = {},\n ): Promise<void> {\n this.assertNoPendingInterrupts();\n this.resetHead(parentId);\n this.notifyUpdate();\n await this.startRun(parentId, config.runConfig);\n }\n\n async cancel(): Promise<void> {\n if (!this.abortController) return;\n this.abortController.abort();\n }\n\n async resume(config: ResumeRunConfig): Promise<void> {\n this.assertNoPendingInterrupts();\n if (config.stream) {\n this.logger.debug?.(\n \"[agui] resume stream is not supported, falling back to regular run\",\n );\n }\n await this.startRun(\n config.parentId,\n config.runConfig ?? this.lastRunConfig,\n );\n }\n\n private assertNoPendingInterrupts(): void {\n if (!this.getPendingInterrupts()) return;\n throw new Error(\n \"[agui] cannot start a new run while interrupts are pending; resolve them with submitInterruptResponses()\",\n );\n }\n\n getPendingInterrupts(): {\n messageId: string;\n interrupts: readonly AgUiInterrupt[];\n } | null {\n const assistant = this.messages.findLast((m) => m.role === \"assistant\") as\n | ThreadAssistantMessage\n | undefined;\n if (\n !assistant ||\n assistant.status?.type !== \"requires-action\" ||\n assistant.status.reason !== \"interrupt\"\n ) {\n return null;\n }\n const stored = (\n assistant.metadata.custom[AG_UI_METADATA_NAMESPACE] as\n | AgUiCustomMetadata\n | undefined\n )?.interrupts;\n if (!stored?.length) return null;\n return { messageId: assistant.id, interrupts: stored };\n }\n\n async submitInterruptResponses(\n responses: readonly AgUiResumeEntry[],\n ): Promise<void> {\n const pending = this.getPendingInterrupts();\n if (!pending) {\n throw new Error(\n \"[agui] submitInterruptResponses: no pending interrupts on this thread\",\n );\n }\n\n const responsesById = new Map<string, AgUiResumeEntry>();\n for (const entry of responses) {\n if (!entry || typeof entry.interruptId !== \"string\") {\n throw new Error(\n \"[agui] submitInterruptResponses: every entry must have an interruptId\",\n );\n }\n if (entry.status !== \"resolved\" && entry.status !== \"cancelled\") {\n throw new Error(\n `[agui] submitInterruptResponses: invalid status \"${entry.status}\" for interrupt ${entry.interruptId}`,\n );\n }\n if (responsesById.has(entry.interruptId)) {\n throw new Error(\n `[agui] submitInterruptResponses: duplicate response for interrupt ${entry.interruptId}`,\n );\n }\n responsesById.set(entry.interruptId, entry);\n }\n\n const openIds = pending.interrupts.map((i) => i.id);\n const missing = openIds.filter((id) => !responsesById.has(id));\n if (missing.length > 0) {\n throw new Error(\n `[agui] submitInterruptResponses: missing responses for open interrupts: ${missing.join(\", \")}`,\n );\n }\n const known = new Set(openIds);\n const unknownIds = [...responsesById.keys()].filter((id) => !known.has(id));\n if (unknownIds.length > 0) {\n throw new Error(\n `[agui] submitInterruptResponses: unknown interrupt ids: ${unknownIds.join(\", \")}`,\n );\n }\n\n const now = Date.now();\n for (const interrupt of pending.interrupts) {\n if (!interrupt.expiresAt) continue;\n const expiry = new Date(interrupt.expiresAt).getTime();\n if (Number.isNaN(expiry)) {\n throw new Error(\n `[agui] submitInterruptResponses: interrupt ${interrupt.id} has malformed expiresAt \"${interrupt.expiresAt}\"`,\n );\n }\n if (expiry <= now) {\n throw new Error(\n `[agui] submitInterruptResponses: interrupt ${interrupt.id} expired at ${interrupt.expiresAt}`,\n );\n }\n }\n\n const resume: AgUiResumeEntry[] = openIds.map(\n (id) => responsesById.get(id)!,\n );\n\n if (this.isRunningFlag) {\n throw new Error(\n \"[agui] submitInterruptResponses: a run is already in progress\",\n );\n }\n\n this.clearPendingInterrupts(pending.messageId);\n await this.startRun(pending.messageId, this.lastRunConfig, resume);\n }\n\n private clearPendingInterrupts(messageId: string): void {\n let touched = false;\n this.messages = this.messages.map((message) => {\n if (message.id !== messageId || message.role !== \"assistant\")\n return message;\n const assistant = message as ThreadAssistantMessage;\n if (\n assistant.status?.type !== \"requires-action\" ||\n assistant.status.reason !== \"interrupt\"\n ) {\n return assistant;\n }\n touched = true;\n const aguiMeta = assistant.metadata.custom[AG_UI_METADATA_NAMESPACE] as\n | AgUiCustomMetadata\n | undefined;\n const { interrupts: _drop, ...restAgui } = aguiMeta ?? {};\n const newCustom = { ...assistant.metadata.custom };\n if (Object.keys(restAgui).length > 0) {\n newCustom[AG_UI_METADATA_NAMESPACE] = restAgui;\n } else {\n delete newCustom[AG_UI_METADATA_NAMESPACE];\n }\n return {\n ...assistant,\n status: { type: \"complete\" as const, reason: \"unknown\" as const },\n metadata: { ...assistant.metadata, custom: newCustom },\n };\n });\n if (touched) this.notifyUpdate();\n }\n\n findMessageIdForToolCall(toolCallId: string): string | undefined {\n let fallbackMessageId: string | undefined;\n for (let index = this.messages.length - 1; index >= 0; index--) {\n const message = this.messages[index];\n if (!message || message.role !== \"assistant\") continue;\n for (const part of message.content) {\n if (part.type !== \"tool-call\" || part.toolCallId !== toolCallId)\n continue;\n if (!(\"result\" in part) || part.result === undefined) {\n return message.id;\n }\n fallbackMessageId ??= message.id;\n }\n }\n return fallbackMessageId;\n }\n\n addToolResult(options: AddToolResultOptions): void {\n let updated = false;\n let shouldResume = false;\n this.messages = this.messages.map((message) => {\n if (message.id !== options.messageId || message.role !== \"assistant\")\n return message;\n const assistant = message as ThreadAssistantMessage;\n let matchedToolCall = false;\n const content = assistant.content.map((part) => {\n if (part.type !== \"tool-call\" || part.toolCallId !== options.toolCallId)\n return part;\n matchedToolCall = true;\n return {\n ...part,\n result: options.result,\n artifact: options.artifact,\n isError: options.isError,\n };\n });\n if (!matchedToolCall) return message;\n updated = true;\n\n if (\n assistant.status?.type === \"requires-action\" &&\n assistant.status.reason === \"tool-calls\" &&\n content.every(\n (part) =>\n part.type !== \"tool-call\" ||\n (\"result\" in part && part.result !== undefined),\n )\n ) {\n shouldResume = true;\n return {\n ...assistant,\n content,\n status: { type: \"complete\" as const, reason: \"unknown\" as const },\n };\n }\n\n return {\n ...assistant,\n content,\n };\n });\n\n if (updated) {\n this.notifyUpdate();\n\n if (shouldResume) {\n this.persistAssistantHistory(options.messageId);\n\n if (!this.isRunningFlag) {\n void this.startRun(options.messageId, this.lastRunConfig).catch(\n (error) => {\n this.onError?.(\n error instanceof Error ? error : new Error(String(error)),\n );\n },\n );\n }\n }\n }\n }\n\n applyExternalMessages(messages: readonly ThreadMessage[]): void {\n this.assistantHistoryParents.clear();\n this.messages = [...messages];\n this.recordedHistoryIds.clear();\n for (const message of this.messages) {\n this.recordedHistoryIds.add(message.id);\n }\n this.notifyUpdate();\n }\n\n loadExternalState(state: ReadonlyJSONValue): void {\n this.stateSnapshot = state;\n this.notifyUpdate();\n }\n\n private async startRun(\n parentId: string | null,\n runConfig?: RunConfig,\n resume?: AgUiResumeEntry[],\n ): Promise<void> {\n const normalizedRunConfig = runConfig ?? {};\n this.lastRunConfig = normalizedRunConfig;\n this.resetHead(parentId);\n const historicalMessages = [...this.messages];\n\n const runId = generateId();\n this.pendingError = null;\n const input = this.buildRunInput(\n runId,\n normalizedRunConfig,\n historicalMessages,\n resume,\n );\n const assistantParentId = parentId ?? this.messages.at(-1)?.id ?? null;\n let assistantMessageId: string | undefined;\n const ensureAssistant = () => {\n if (assistantMessageId) return assistantMessageId;\n const created = this.insertAssistantPlaceholder();\n assistantMessageId = created;\n this.markPendingAssistantHistory(created, assistantParentId ?? null);\n return created;\n };\n\n const aggregator = new RunAggregator({\n showThinking: this.showThinking,\n logger: this.logger,\n emit: (update) => {\n const resolved = this.updateAssistantMessage(ensureAssistant(), update);\n if (resolved !== assistantMessageId) {\n assistantMessageId = resolved;\n }\n },\n onServerMessageId: (serverId) => {\n const placeholder = ensureAssistant();\n if (placeholder === serverId) return;\n this.reassignAssistantId(placeholder, serverId);\n assistantMessageId = serverId;\n },\n });\n const dispatch = (event: AgUiEvent) => this.handleEvent(aggregator, event);\n\n const abortController = new AbortController();\n const abortSignal = abortController.signal;\n this.abortController = abortController;\n\n abortSignal.addEventListener(\n \"abort\",\n () => {\n dispatch({ type: \"RUN_CANCELLED\" });\n this.finishRun(abortController);\n this.onCancel?.();\n },\n { once: true },\n );\n\n const subscriber = createAgUiSubscriber({\n dispatch,\n runId,\n logger: this.logger,\n onRunFailed: (error) => {\n this.pendingError = error;\n this.onError?.(error);\n },\n });\n\n aggregator.handle({ type: \"RUN_STARTED\", runId });\n this.setRunning(true);\n\n try {\n try {\n (this.agent as any).messages = input.messages;\n (this.agent as any).threadId = input.threadId;\n (this.agent as any).state = input.state ?? null;\n } catch {\n // ignore\n }\n await (this.agent as any).runAgent(input, subscriber, {\n signal: abortSignal,\n });\n } catch (error) {\n if (!abortSignal.aborted) {\n const err = error instanceof Error ? error : new Error(String(error));\n dispatch({ type: \"RUN_ERROR\", message: err.message });\n this.onError?.(err);\n this.pendingError = this.pendingError ?? err;\n }\n } finally {\n this.finishRun(abortController);\n }\n\n if (this.pendingError) {\n const err = this.pendingError;\n this.pendingError = null;\n throw err;\n }\n }\n\n private buildRunInput(\n runId: string,\n runConfig: RunConfig | undefined,\n historyMessages: readonly ThreadMessage[] | undefined,\n resume?: AgUiResumeEntry[],\n ) {\n const threadId = this.agent.threadId || \"main\";\n const messages = toAgUiMessages(historyMessages ?? this.messages);\n const context = this.runtime?.thread.getModelContext();\n return {\n threadId,\n runId,\n state: this.stateSnapshot ?? null,\n messages,\n tools: toAgUiTools(context?.tools),\n context: context?.system\n ? [{ description: \"system\", value: context.system }]\n : [],\n forwardedProps: {\n ...(context?.callSettings ?? {}),\n ...(context?.config ?? {}),\n ...(runConfig?.custom ? { runConfig: runConfig.custom } : {}),\n },\n ...(resume !== undefined ? { resume } : {}),\n };\n }\n\n private installResumeShim(): void {\n const agent = this.agent as any;\n if (agent[symbolResumeShim]) return;\n agent[symbolResumeShim] = true;\n const onInstance = Object.hasOwn(agent, \"prepareRunAgentInput\");\n const original = onInstance\n ? agent.prepareRunAgentInput\n : Object.getPrototypeOf(agent)?.prepareRunAgentInput;\n if (typeof original !== \"function\") return;\n agent.prepareRunAgentInput = function (\n this: unknown,\n params: { resume?: unknown } | undefined,\n ) {\n const input = original.call(this, params);\n if (params?.resume !== undefined && input && typeof input === \"object\") {\n return { ...(input as object), resume: params.resume };\n }\n return input;\n };\n }\n\n private setRunning(running: boolean) {\n this.isRunningFlag = running;\n this.notifyUpdate();\n }\n\n private finishRun(controller: AbortController | null) {\n if (this.abortController === controller) {\n this.abortController = null;\n }\n this.setRunning(false);\n }\n\n private insertAssistantPlaceholder(): string {\n const id = generateOptimisticId();\n const assistant: ThreadAssistantMessage = {\n id,\n role: \"assistant\",\n createdAt: new Date(),\n status: { type: \"running\" },\n content: [],\n metadata: {\n unstable_state: this.stateSnapshot ?? null,\n unstable_annotations: [],\n unstable_data: [],\n steps: [],\n custom: {},\n },\n };\n this.messages = [...this.messages, assistant];\n this.notifyUpdate();\n return id;\n }\n\n private reassignAssistantId(oldId: string, newId: string): void {\n if (oldId === newId) return;\n\n const collidesWithExisting = this.messages.some((m) => m.id === newId);\n\n if (collidesWithExisting) {\n this.logger.debug?.(\n \"[agui] reassignAssistantId: server id already present in messages, dropping placeholder\",\n { oldId, newId },\n );\n this.messages = this.messages.filter((m) => m.id !== oldId);\n } else {\n this.messages = this.messages.map((m) =>\n m.id === oldId ? { ...m, id: newId } : m,\n );\n }\n\n const pendingParent = this.assistantHistoryParents.get(oldId);\n if (pendingParent !== undefined) {\n this.assistantHistoryParents.delete(oldId);\n if (!this.assistantHistoryParents.has(newId)) {\n this.assistantHistoryParents.set(newId, pendingParent);\n }\n }\n\n if (this.recordedHistoryIds.has(oldId)) {\n this.recordedHistoryIds.delete(oldId);\n this.recordedHistoryIds.add(newId);\n }\n\n this.notifyUpdate();\n }\n\n private updateAssistantMessage(\n messageId: string,\n update: ChatModelRunResult,\n ): string {\n let touched = false;\n let latestStatus: MessageStatus | undefined;\n this.messages = this.messages.map((message) => {\n if (message.id !== messageId || message.role !== \"assistant\")\n return message;\n touched = true;\n const assistant = message as ThreadAssistantMessage;\n const metadata = update.metadata\n ? this.mergeAssistantMetadata(assistant.metadata, update.metadata)\n : assistant.metadata;\n latestStatus = update.status ?? assistant.status;\n return {\n ...assistant,\n content: (update.content ??\n assistant.content) as ThreadAssistantMessage[\"content\"],\n status: latestStatus,\n metadata,\n };\n });\n if (!touched) return messageId;\n\n let resolvedMessageId = messageId;\n const isSettled =\n latestStatus !== undefined && latestStatus.type !== \"running\";\n if (isSettled && isOptimisticId(messageId)) {\n const stableId = generateId();\n this.reassignAssistantId(messageId, stableId);\n resolvedMessageId = stableId;\n } else {\n this.notifyUpdate();\n }\n if (this.isPersistableStatus(latestStatus)) {\n this.persistAssistantHistory(resolvedMessageId);\n }\n return resolvedMessageId;\n }\n\n private mergeAssistantMetadata(\n current: ThreadAssistantMessage[\"metadata\"],\n incoming: NonNullable<ChatModelRunResult[\"metadata\"]>,\n ): ThreadAssistantMessage[\"metadata\"] {\n const annotations = incoming.unstable_annotations\n ? [...current.unstable_annotations, ...incoming.unstable_annotations]\n : current.unstable_annotations;\n const data = incoming.unstable_data\n ? [...current.unstable_data, ...incoming.unstable_data]\n : current.unstable_data;\n const steps = incoming.steps\n ? [...current.steps, ...incoming.steps]\n : current.steps;\n return {\n unstable_state:\n incoming.unstable_state !== undefined\n ? incoming.unstable_state\n : current.unstable_state,\n unstable_annotations: annotations,\n unstable_data: data,\n steps,\n ...(incoming.timing ? { timing: incoming.timing } : {}),\n custom: incoming.custom\n ? { ...current.custom, ...incoming.custom }\n : current.custom,\n };\n }\n\n private handleEvent(aggregator: RunAggregator, event: AgUiEvent) {\n switch (event.type) {\n case \"STATE_SNAPSHOT\": {\n this.stateSnapshot = event.snapshot as ReadonlyJSONValue;\n this.notifyUpdate();\n return;\n }\n case \"STATE_DELTA\": {\n this.logger.debug?.(\"[agui] state delta event ignored\", event.delta);\n return;\n }\n case \"MESSAGES_SNAPSHOT\": {\n this.importMessagesSnapshot(event.messages);\n return;\n }\n default:\n aggregator.handle(event);\n }\n }\n\n private importMessagesSnapshot(rawMessages: readonly unknown[]) {\n try {\n const normalized = fromAgUiMessages(rawMessages);\n const converted: ThreadMessage[] = [];\n for (const message of normalized) {\n try {\n converted.push(\n fromThreadMessageLike(\n message as any,\n generateId(),\n FALLBACK_USER_STATUS,\n ),\n );\n } catch (error) {\n this.logger.error?.(\n \"[agui] failed to import message from snapshot\",\n error,\n );\n }\n }\n this.applyExternalMessages(converted);\n } catch (error) {\n this.logger.error?.(\"[agui] failed to import messages snapshot\", error);\n }\n }\n\n private toThreadMessage(message: AppendMessage): ThreadMessage {\n return fromThreadMessageLike(\n message as any,\n generateId(),\n FALLBACK_USER_STATUS,\n );\n }\n\n private resetHead(parentId: string | null | undefined) {\n if (!parentId) {\n if (this.messages.length) {\n this.messages = [];\n }\n return;\n }\n const idx = this.messages.findIndex((message) => message.id === parentId);\n if (idx === -1) return;\n this.messages = this.messages.slice(0, idx + 1);\n }\n\n private isTerminalStatus(status?: MessageStatus): boolean {\n return status?.type === \"complete\" || status?.type === \"incomplete\";\n }\n\n private isPersistableStatus(status?: MessageStatus): boolean {\n if (this.isTerminalStatus(status)) return true;\n return status?.type === \"requires-action\" && status.reason === \"interrupt\";\n }\n\n private recordHistoryEntry(parentId: string | null, message: ThreadMessage) {\n this.appendHistoryItem(parentId, message);\n }\n\n private markPendingAssistantHistory(\n messageId: string,\n parentId: string | null,\n ) {\n if (!this.history) return;\n this.assistantHistoryParents.set(messageId, parentId);\n }\n\n private persistAssistantHistory(messageId: string) {\n if (!this.history) return;\n const parentId = this.assistantHistoryParents.get(messageId);\n if (parentId === undefined) return;\n const message = this.messages.find((m) => m.id === messageId);\n if (!message || message.role !== \"assistant\") return;\n if (!this.isPersistableStatus(message.status)) return;\n this.assistantHistoryParents.delete(messageId);\n this.appendHistoryItem(parentId, message);\n }\n\n private appendHistoryItem(parentId: string | null, message: ThreadMessage) {\n if (!this.history || this.recordedHistoryIds.has(message.id)) return;\n this.recordedHistoryIds.add(message.id);\n void this.history.append({ parentId, message }).catch((error) => {\n this.recordedHistoryIds.delete(message.id);\n this.logger.error?.(\"[agui] failed to append history entry\", error);\n });\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AgUiThreadRuntimeCore.js","names":[],"sources":["../../src/runtime/AgUiThreadRuntimeCore.ts"],"sourcesContent":["\"use client\";\n\nimport { generateId, fromThreadMessageLike } from \"@assistant-ui/core/internal\";\nimport type {\n AddToolResultOptions,\n AppendMessage,\n AssistantRuntime,\n ChatModelRunOptions,\n ChatModelRunResult,\n MessageStatus,\n ThreadAssistantMessage,\n ThreadHistoryAdapter,\n ThreadMessage,\n ToolCallMessagePart,\n} from \"@assistant-ui/core\";\nimport type { HttpAgent } from \"@ag-ui/client\";\nimport jsonpatch, { type Operation } from \"fast-json-patch\";\nimport type { Logger } from \"./logger\";\nimport type { AgUiEvent, AgUiInterrupt, AgUiResumeEntry } from \"./types\";\nimport type { ReadonlyJSONValue } from \"assistant-stream/utils\";\nimport {\n AG_UI_METADATA_NAMESPACE,\n type AgUiCustomMetadata,\n RunAggregator,\n} from \"./adapter/run-aggregator\";\nimport {\n fromAgUiMessages,\n toAgUiMessages,\n toAgUiTools,\n} from \"./adapter/conversions\";\nimport { createAgUiSubscriber } from \"./adapter/subscriber\";\n\nconst optimisticPrefix = \"__optimistic__\";\nconst generateOptimisticId = () => `${optimisticPrefix}${generateId()}`;\nconst isOptimisticId = (id: string) => id.startsWith(optimisticPrefix);\n\nconst symbolResumeShim = Symbol(\"agui-resume-shim\");\n\ntype RunConfig = NonNullable<AppendMessage[\"runConfig\"]>;\ntype ResumeStream = (\n options: ChatModelRunOptions,\n) => AsyncGenerator<ChatModelRunResult, void, unknown>;\ntype ResumeRunConfig = {\n parentId: string | null;\n sourceId: string | null;\n runConfig: RunConfig;\n stream?: ResumeStream;\n};\n\ntype CoreOptions = {\n agent: HttpAgent;\n logger: Logger;\n showThinking: boolean;\n onError?: (error: Error) => void;\n onCancel?: () => void;\n history?: ThreadHistoryAdapter;\n notifyUpdate: () => void;\n};\n\nconst FALLBACK_USER_STATUS = { type: \"complete\", reason: \"unknown\" } as const;\n\nexport class AgUiThreadRuntimeCore {\n private agent: HttpAgent;\n private logger: Logger;\n private showThinking: boolean;\n private onError: ((error: Error) => void) | undefined;\n private onCancel: (() => void) | undefined;\n private readonly notifyUpdate: () => void;\n\n private runtime: AssistantRuntime | undefined;\n private messages: ThreadMessage[] = [];\n private isRunningFlag = false;\n private abortController: AbortController | null = null;\n private stateSnapshot: ReadonlyJSONValue | undefined;\n private pendingError: Error | null = null;\n private history: ThreadHistoryAdapter | undefined;\n private lastRunConfig: RunConfig | undefined;\n private readonly assistantHistoryParents = new Map<string, string | null>();\n private readonly recordedHistoryIds = new Set<string>();\n private _isLoading = false;\n private _loadPromise: Promise<void> | undefined;\n private pendingResumeMessageId: string | null = null;\n\n constructor(options: CoreOptions) {\n this.agent = options.agent;\n this.logger = options.logger;\n this.showThinking = options.showThinking;\n this.onError = options.onError;\n this.onCancel = options.onCancel;\n this.history = options.history;\n this.notifyUpdate = options.notifyUpdate;\n this.installResumeShim();\n }\n\n updateOptions(options: Omit<CoreOptions, \"notifyUpdate\">) {\n this.agent = options.agent;\n this.logger = options.logger;\n this.showThinking = options.showThinking;\n this.onError = options.onError;\n this.onCancel = options.onCancel;\n this.history = options.history;\n this.installResumeShim();\n }\n\n attachRuntime(runtime: AssistantRuntime) {\n this.runtime = runtime;\n }\n\n detachRuntime() {\n this.runtime = undefined;\n }\n\n getMessages(): readonly ThreadMessage[] {\n return this.messages;\n }\n\n getState(): ReadonlyJSONValue | undefined {\n return this.stateSnapshot;\n }\n\n isRunning(): boolean {\n return this.isRunningFlag;\n }\n\n get isLoading(): boolean {\n return this._isLoading;\n }\n\n __internal_load(): Promise<void> {\n if (this._loadPromise) return this._loadPromise;\n\n const promise = this.history?.load() ?? Promise.resolve(null);\n\n this._isLoading = true;\n\n this._loadPromise = promise\n .then(async (repo) => {\n if (!repo) return;\n\n const messages = repo.messages.map((item) => item.message);\n this.applyExternalMessages(messages);\n\n if (repo.unstable_resume) {\n const parentId = repo.headId ?? messages.at(-1)?.id ?? null;\n const resumeStream = this.history?.resume?.bind(this.history);\n await this.startRun(\n parentId,\n this.lastRunConfig,\n undefined,\n resumeStream,\n );\n }\n })\n .catch((error) => {\n this.logger.error?.(\"[agui] failed to load history\", error);\n this.onError?.(\n error instanceof Error ? error : new Error(String(error)),\n );\n })\n .finally(() => {\n this._isLoading = false;\n this.notifyUpdate();\n });\n\n this.notifyUpdate();\n return this._loadPromise;\n }\n\n async append(message: AppendMessage): Promise<void> {\n const startRun = message.startRun ?? message.role === \"user\";\n if (startRun) this.assertNoPendingInterrupts();\n if (message.sourceId) {\n this.messages = this.messages.filter(\n (entry) => entry.id !== message.sourceId,\n );\n }\n this.resetHead(message.parentId);\n\n const threadMessage = this.toThreadMessage(message);\n this.messages = [...this.messages, threadMessage];\n this.notifyUpdate();\n this.recordHistoryEntry(message.parentId ?? null, threadMessage);\n\n if (!startRun) return;\n await this.startRun(threadMessage.id, message.runConfig);\n }\n\n async edit(message: AppendMessage): Promise<void> {\n await this.append(message);\n }\n\n async reload(\n parentId: string | null,\n config: { runConfig?: RunConfig } = {},\n ): Promise<void> {\n this.assertNoPendingInterrupts();\n this.resetHead(parentId);\n this.notifyUpdate();\n await this.startRun(parentId, config.runConfig);\n }\n\n async cancel(): Promise<void> {\n if (!this.abortController) return;\n this.abortController.abort();\n }\n\n async resume(config: ResumeRunConfig): Promise<void> {\n this.assertNoPendingInterrupts();\n await this.startRun(\n config.parentId,\n config.runConfig ?? this.lastRunConfig,\n undefined,\n config.stream,\n );\n }\n\n private assertNoPendingInterrupts(): void {\n if (!this.getPendingInterrupts()) return;\n throw new Error(\n \"[agui] cannot start a new run while interrupts are pending; resolve them with submitInterruptResponses()\",\n );\n }\n\n getPendingInterrupts(): {\n messageId: string;\n interrupts: readonly AgUiInterrupt[];\n } | null {\n const assistant = this.messages.findLast((m) => m.role === \"assistant\") as\n | ThreadAssistantMessage\n | undefined;\n if (\n !assistant ||\n assistant.status?.type !== \"requires-action\" ||\n assistant.status.reason !== \"interrupt\"\n ) {\n return null;\n }\n const stored = (\n assistant.metadata.custom[AG_UI_METADATA_NAMESPACE] as\n | AgUiCustomMetadata\n | undefined\n )?.interrupts;\n if (!stored?.length) return null;\n return { messageId: assistant.id, interrupts: stored };\n }\n\n async submitInterruptResponses(\n responses: readonly AgUiResumeEntry[],\n ): Promise<void> {\n const pending = this.getPendingInterrupts();\n if (!pending) {\n throw new Error(\n \"[agui] submitInterruptResponses: no pending interrupts on this thread\",\n );\n }\n\n const responsesById = new Map<string, AgUiResumeEntry>();\n for (const entry of responses) {\n if (!entry || typeof entry.interruptId !== \"string\") {\n throw new Error(\n \"[agui] submitInterruptResponses: every entry must have an interruptId\",\n );\n }\n if (entry.status !== \"resolved\" && entry.status !== \"cancelled\") {\n throw new Error(\n `[agui] submitInterruptResponses: invalid status \"${entry.status}\" for interrupt ${entry.interruptId}`,\n );\n }\n if (responsesById.has(entry.interruptId)) {\n throw new Error(\n `[agui] submitInterruptResponses: duplicate response for interrupt ${entry.interruptId}`,\n );\n }\n responsesById.set(entry.interruptId, entry);\n }\n\n const openIds = pending.interrupts.map((i) => i.id);\n const missing = openIds.filter((id) => !responsesById.has(id));\n if (missing.length > 0) {\n throw new Error(\n `[agui] submitInterruptResponses: missing responses for open interrupts: ${missing.join(\", \")}`,\n );\n }\n const known = new Set(openIds);\n const unknownIds = [...responsesById.keys()].filter((id) => !known.has(id));\n if (unknownIds.length > 0) {\n throw new Error(\n `[agui] submitInterruptResponses: unknown interrupt ids: ${unknownIds.join(\", \")}`,\n );\n }\n\n const now = Date.now();\n for (const interrupt of pending.interrupts) {\n if (!interrupt.expiresAt) continue;\n const expiry = new Date(interrupt.expiresAt).getTime();\n if (Number.isNaN(expiry)) {\n throw new Error(\n `[agui] submitInterruptResponses: interrupt ${interrupt.id} has malformed expiresAt \"${interrupt.expiresAt}\"`,\n );\n }\n if (expiry <= now) {\n throw new Error(\n `[agui] submitInterruptResponses: interrupt ${interrupt.id} expired at ${interrupt.expiresAt}`,\n );\n }\n }\n\n const resume: AgUiResumeEntry[] = openIds.map(\n (id) => responsesById.get(id)!,\n );\n\n if (this.isRunningFlag) {\n throw new Error(\n \"[agui] submitInterruptResponses: a run is already in progress\",\n );\n }\n\n this.clearPendingInterrupts(pending.messageId);\n await this.startRun(pending.messageId, this.lastRunConfig, resume);\n }\n\n private clearPendingInterrupts(messageId: string): void {\n let touched = false;\n this.messages = this.messages.map((message) => {\n if (message.id !== messageId || message.role !== \"assistant\")\n return message;\n const assistant = message as ThreadAssistantMessage;\n if (\n assistant.status?.type !== \"requires-action\" ||\n assistant.status.reason !== \"interrupt\"\n ) {\n return assistant;\n }\n touched = true;\n const aguiMeta = assistant.metadata.custom[AG_UI_METADATA_NAMESPACE] as\n | AgUiCustomMetadata\n | undefined;\n const { interrupts: _drop, ...restAgui } = aguiMeta ?? {};\n const newCustom = { ...assistant.metadata.custom };\n if (Object.keys(restAgui).length > 0) {\n newCustom[AG_UI_METADATA_NAMESPACE] = restAgui;\n } else {\n delete newCustom[AG_UI_METADATA_NAMESPACE];\n }\n return {\n ...assistant,\n status: { type: \"complete\" as const, reason: \"unknown\" as const },\n metadata: { ...assistant.metadata, custom: newCustom },\n };\n });\n if (touched) this.notifyUpdate();\n }\n\n findMessageIdForToolCall(toolCallId: string): string | undefined {\n let fallbackMessageId: string | undefined;\n for (let index = this.messages.length - 1; index >= 0; index--) {\n const message = this.messages[index];\n if (!message || message.role !== \"assistant\") continue;\n for (const part of message.content) {\n if (part.type !== \"tool-call\" || part.toolCallId !== toolCallId)\n continue;\n if (!(\"result\" in part) || part.result === undefined) {\n return message.id;\n }\n fallbackMessageId ??= message.id;\n }\n }\n return fallbackMessageId;\n }\n\n addToolResult(options: AddToolResultOptions): void {\n let updated = false;\n this.messages = this.messages.map((message) => {\n if (message.id !== options.messageId || message.role !== \"assistant\")\n return message;\n const assistant = message as ThreadAssistantMessage;\n let matchedToolCall = false;\n const content = assistant.content.map((part) => {\n if (part.type !== \"tool-call\" || part.toolCallId !== options.toolCallId)\n return part;\n matchedToolCall = true;\n return {\n ...part,\n result: options.result,\n artifact: options.artifact,\n isError: options.isError,\n };\n });\n if (!matchedToolCall) return message;\n updated = true;\n return { ...assistant, content };\n });\n\n if (!updated) return;\n this.notifyUpdate();\n this.maybeResumeAfterToolResults(options.messageId);\n }\n\n // Re-evaluate whether every tool call on the assistant message now has a\n // result and, if so, drive the follow-up run. Centralizing this lets the\n // continuation fire whether the frontend result lands before RUN_FINISHED\n // (status flips to requires-action only later, while a run is still draining)\n // or after it. Idempotent: once the message is marked complete it no-ops.\n private maybeResumeAfterToolResults(messageId: string): void {\n const message = this.messages.find((m) => m.id === messageId);\n if (!message || message.role !== \"assistant\") return;\n const assistant = message as ThreadAssistantMessage;\n if (\n assistant.status?.type !== \"requires-action\" ||\n assistant.status.reason !== \"tool-calls\"\n ) {\n return;\n }\n const allResolved = assistant.content.every(\n (part) =>\n part.type !== \"tool-call\" ||\n (\"result\" in part && part.result !== undefined),\n );\n if (!allResolved) return;\n\n this.messages = this.messages.map((m) =>\n m.id === messageId && m.role === \"assistant\"\n ? {\n ...(m as ThreadAssistantMessage),\n status: { type: \"complete\" as const, reason: \"unknown\" as const },\n }\n : m,\n );\n this.notifyUpdate();\n this.persistAssistantHistory(messageId);\n\n if (this.isRunningFlag) {\n // A run is still draining (RUN_FINISHED arrived but the stream has not\n // closed). Defer until startRun's tail so we never start two runs.\n this.pendingResumeMessageId = messageId;\n return;\n }\n this.startResumeRun(messageId);\n }\n\n private startResumeRun(messageId: string): void {\n void this.startRun(messageId, this.lastRunConfig).catch((error) => {\n this.onError?.(error instanceof Error ? error : new Error(String(error)));\n });\n }\n\n applyExternalMessages(messages: readonly ThreadMessage[]): void {\n this.assistantHistoryParents.clear();\n this.messages = [...messages];\n this.recordedHistoryIds.clear();\n for (const message of this.messages) {\n this.recordedHistoryIds.add(message.id);\n }\n this.notifyUpdate();\n }\n\n loadExternalState(state: ReadonlyJSONValue): void {\n this.stateSnapshot = state;\n this.notifyUpdate();\n }\n\n private async startRun(\n parentId: string | null,\n runConfig?: RunConfig,\n resume?: AgUiResumeEntry[],\n resumeStream?: ResumeStream,\n ): Promise<void> {\n const normalizedRunConfig = runConfig ?? {};\n this.lastRunConfig = normalizedRunConfig;\n this.resetHead(parentId);\n const historicalMessages = [...this.messages];\n\n this.pendingError = null;\n const assistantParentId = parentId ?? this.messages.at(-1)?.id ?? null;\n let assistantMessageId: string | undefined;\n const ensureAssistant = () => {\n if (assistantMessageId) return assistantMessageId;\n const created = this.insertAssistantPlaceholder();\n assistantMessageId = created;\n this.markPendingAssistantHistory(created, assistantParentId ?? null);\n return created;\n };\n\n const applyUpdate = (update: ChatModelRunResult) => {\n const resolved = this.updateAssistantMessage(ensureAssistant(), update);\n if (resolved !== assistantMessageId) {\n assistantMessageId = resolved;\n }\n };\n\n const aggregator = new RunAggregator({\n showThinking: this.showThinking,\n logger: this.logger,\n emit: applyUpdate,\n onServerMessageId: (serverId) => {\n const placeholder = ensureAssistant();\n if (placeholder === serverId) return;\n this.reassignAssistantId(placeholder, serverId);\n assistantMessageId = serverId;\n },\n });\n const dispatch = (event: AgUiEvent) => this.handleEvent(aggregator, event);\n\n const abortController = new AbortController();\n const abortSignal = abortController.signal;\n this.abortController = abortController;\n\n let cancelRun = () => dispatch({ type: \"RUN_CANCELLED\" });\n abortSignal.addEventListener(\n \"abort\",\n () => {\n cancelRun();\n this.finishRun(abortController);\n this.onCancel?.();\n },\n { once: true },\n );\n\n this.setRunning(true);\n\n try {\n if (resumeStream) {\n // Cancel flips only the status; an aggregator RUN_CANCELLED would emit an empty snapshot and wipe the replayed content.\n cancelRun = () =>\n applyUpdate({ status: { type: \"incomplete\", reason: \"cancelled\" } });\n await this.consumeResumeStream(resumeStream, {\n runConfig: normalizedRunConfig,\n threadId: this.agent.threadId || \"main\",\n parentId: assistantParentId,\n historicalMessages,\n abortSignal,\n ensureAssistant,\n applyUpdate,\n getAssistantMessageId: () => assistantMessageId,\n });\n } else {\n const runId = generateId();\n aggregator.handle({ type: \"RUN_STARTED\", runId });\n const input = this.buildRunInput(\n runId,\n normalizedRunConfig,\n historicalMessages,\n resume,\n );\n const subscriber = createAgUiSubscriber({\n dispatch,\n runId,\n logger: this.logger,\n onRunFailed: (error) => {\n this.pendingError = error;\n this.onError?.(error);\n },\n });\n try {\n (this.agent as any).messages = input.messages;\n (this.agent as any).threadId = input.threadId;\n (this.agent as any).state = input.state ?? null;\n } catch {\n // ignore\n }\n await (this.agent as any).runAgent(input, subscriber, {\n signal: abortSignal,\n });\n }\n } catch (error) {\n if (!abortSignal.aborted) {\n const err = error instanceof Error ? error : new Error(String(error));\n dispatch({ type: \"RUN_ERROR\", message: err.message });\n this.onError?.(err);\n this.pendingError = this.pendingError ?? err;\n }\n } finally {\n this.finishRun(abortController);\n }\n\n if (this.pendingError) {\n const err = this.pendingError;\n this.pendingError = null;\n this.pendingResumeMessageId = null;\n throw err;\n }\n\n // A tool result that landed before the run settled deferred its\n // continuation here so a second run never overlaps the first.\n if (this.pendingResumeMessageId !== null) {\n const resumeMessageId = this.pendingResumeMessageId;\n this.pendingResumeMessageId = null;\n if (!abortSignal.aborted) {\n this.startResumeRun(resumeMessageId);\n }\n }\n }\n\n // Replays a persisted run's snapshots into the existing assistant message, bypassing agent.runAgent so it is not re-invoked.\n private async consumeResumeStream(\n stream: ResumeStream,\n ctx: {\n runConfig: RunConfig;\n threadId: string;\n parentId: string | null;\n historicalMessages: readonly ThreadMessage[];\n abortSignal: AbortSignal;\n ensureAssistant: () => string;\n applyUpdate: (update: ChatModelRunResult) => void;\n getAssistantMessageId: () => string | undefined;\n },\n ): Promise<void> {\n const assistantId = ctx.ensureAssistant();\n const currentId = () => ctx.getAssistantMessageId() ?? assistantId;\n const options: ChatModelRunOptions = {\n messages: ctx.historicalMessages,\n runConfig: ctx.runConfig,\n abortSignal: ctx.abortSignal,\n context: this.runtime?.thread.getModelContext() ?? {},\n unstable_assistantMessageId: assistantId,\n unstable_threadId: ctx.threadId,\n unstable_parentId: ctx.parentId,\n unstable_getMessage: () => {\n const message = this.messages.find((m) => m.id === currentId());\n if (!message) {\n throw new Error(\n \"[agui] resume stream requested the assistant message before it existed\",\n );\n }\n return message;\n },\n };\n\n try {\n for await (const result of stream(options)) {\n if (ctx.abortSignal.aborted) return;\n ctx.applyUpdate(result);\n }\n } catch (error) {\n if (ctx.abortSignal.aborted) return;\n const err = error instanceof Error ? error : new Error(String(error));\n ctx.applyUpdate({\n status: { type: \"incomplete\", reason: \"error\", error: err.message },\n });\n this.onError?.(err);\n this.pendingError = this.pendingError ?? err;\n return;\n }\n\n if (ctx.abortSignal.aborted) return;\n const current = this.messages.find((m) => m.id === currentId());\n if (!current || current.status?.type === \"running\") {\n ctx.applyUpdate({ status: { type: \"complete\", reason: \"unknown\" } });\n }\n }\n\n private buildRunInput(\n runId: string,\n runConfig: RunConfig | undefined,\n historyMessages: readonly ThreadMessage[] | undefined,\n resume?: AgUiResumeEntry[],\n ) {\n const threadId = this.agent.threadId || \"main\";\n const messages = toAgUiMessages(historyMessages ?? this.messages);\n const context = this.runtime?.thread.getModelContext();\n return {\n threadId,\n runId,\n state: this.stateSnapshot ?? null,\n messages,\n tools: toAgUiTools(context?.tools),\n context: context?.system\n ? [{ description: \"system\", value: context.system }]\n : [],\n forwardedProps: {\n ...(context?.callSettings ?? {}),\n ...(context?.config ?? {}),\n ...(runConfig?.custom ? { runConfig: runConfig.custom } : {}),\n },\n ...(resume !== undefined ? { resume } : {}),\n };\n }\n\n private installResumeShim(): void {\n const agent = this.agent as any;\n if (agent[symbolResumeShim]) return;\n agent[symbolResumeShim] = true;\n const onInstance = Object.hasOwn(agent, \"prepareRunAgentInput\");\n const original = onInstance\n ? agent.prepareRunAgentInput\n : Object.getPrototypeOf(agent)?.prepareRunAgentInput;\n if (typeof original !== \"function\") return;\n agent.prepareRunAgentInput = function (\n this: unknown,\n params: { resume?: unknown } | undefined,\n ) {\n const input = original.call(this, params);\n if (params?.resume !== undefined && input && typeof input === \"object\") {\n return { ...(input as object), resume: params.resume };\n }\n return input;\n };\n }\n\n private setRunning(running: boolean) {\n this.isRunningFlag = running;\n this.notifyUpdate();\n }\n\n private finishRun(controller: AbortController | null) {\n if (this.abortController === controller) {\n this.abortController = null;\n }\n this.setRunning(false);\n }\n\n private insertAssistantPlaceholder(): string {\n const id = generateOptimisticId();\n const assistant: ThreadAssistantMessage = {\n id,\n role: \"assistant\",\n createdAt: new Date(),\n status: { type: \"running\" },\n content: [],\n metadata: {\n unstable_state: this.stateSnapshot ?? null,\n unstable_annotations: [],\n unstable_data: [],\n steps: [],\n custom: {},\n },\n };\n this.messages = [...this.messages, assistant];\n this.notifyUpdate();\n return id;\n }\n\n private reassignAssistantId(oldId: string, newId: string): void {\n if (oldId === newId) return;\n\n const collidesWithExisting = this.messages.some((m) => m.id === newId);\n\n if (collidesWithExisting) {\n this.logger.debug?.(\n \"[agui] reassignAssistantId: server id already present in messages, dropping placeholder\",\n { oldId, newId },\n );\n this.messages = this.messages.filter((m) => m.id !== oldId);\n } else {\n this.messages = this.messages.map((m) =>\n m.id === oldId ? { ...m, id: newId } : m,\n );\n }\n\n const pendingParent = this.assistantHistoryParents.get(oldId);\n if (pendingParent !== undefined) {\n this.assistantHistoryParents.delete(oldId);\n if (!this.assistantHistoryParents.has(newId)) {\n this.assistantHistoryParents.set(newId, pendingParent);\n }\n }\n\n if (this.recordedHistoryIds.has(oldId)) {\n this.recordedHistoryIds.delete(oldId);\n this.recordedHistoryIds.add(newId);\n }\n\n this.notifyUpdate();\n }\n\n private updateAssistantMessage(\n messageId: string,\n update: ChatModelRunResult,\n ): string {\n let touched = false;\n let latestStatus: MessageStatus | undefined;\n this.messages = this.messages.map((message) => {\n if (message.id !== messageId || message.role !== \"assistant\")\n return message;\n touched = true;\n const assistant = message as ThreadAssistantMessage;\n const metadata = update.metadata\n ? this.mergeAssistantMetadata(assistant.metadata, update.metadata)\n : assistant.metadata;\n latestStatus = update.status ?? assistant.status;\n const content =\n update.content !== undefined\n ? this.preserveToolResults(\n assistant.content,\n update.content as ThreadAssistantMessage[\"content\"],\n )\n : assistant.content;\n return {\n ...assistant,\n content,\n status: latestStatus,\n metadata,\n };\n });\n if (!touched) return messageId;\n\n let resolvedMessageId = messageId;\n const isSettled =\n latestStatus !== undefined && latestStatus.type !== \"running\";\n if (isSettled && isOptimisticId(messageId)) {\n const stableId = generateId();\n this.reassignAssistantId(messageId, stableId);\n resolvedMessageId = stableId;\n } else {\n this.notifyUpdate();\n }\n if (this.isPersistableStatus(latestStatus)) {\n this.persistAssistantHistory(resolvedMessageId);\n }\n this.maybeResumeAfterToolResults(resolvedMessageId);\n return resolvedMessageId;\n }\n\n // The RunAggregator rebuilds the assistant content from stream events only,\n // so a fresh snapshot omits results injected via addToolResult (frontend tool\n // execution). Carry those results forward so the aggregator never clobbers\n // them. Results are only ever added in this flow, so preserving is safe.\n private preserveToolResults(\n previous: ThreadAssistantMessage[\"content\"],\n next: ThreadAssistantMessage[\"content\"],\n ): ThreadAssistantMessage[\"content\"] {\n const resolved = new Map<string, ToolCallMessagePart>();\n for (const part of previous) {\n if (\n part.type === \"tool-call\" &&\n \"result\" in part &&\n part.result !== undefined\n ) {\n resolved.set(part.toolCallId, part);\n }\n }\n if (resolved.size === 0) return next;\n\n let changed = false;\n const merged = next.map((part) => {\n if (part.type !== \"tool-call\") return part;\n if (\"result\" in part && part.result !== undefined) return part;\n const prior = resolved.get(part.toolCallId);\n if (!prior) return part;\n changed = true;\n return {\n ...part,\n result: prior.result,\n ...(prior.artifact !== undefined ? { artifact: prior.artifact } : {}),\n ...(prior.isError !== undefined ? { isError: prior.isError } : {}),\n };\n });\n return changed ? (merged as ThreadAssistantMessage[\"content\"]) : next;\n }\n\n private mergeAssistantMetadata(\n current: ThreadAssistantMessage[\"metadata\"],\n incoming: NonNullable<ChatModelRunResult[\"metadata\"]>,\n ): ThreadAssistantMessage[\"metadata\"] {\n const annotations = incoming.unstable_annotations\n ? [...current.unstable_annotations, ...incoming.unstable_annotations]\n : current.unstable_annotations;\n const data = incoming.unstable_data\n ? [...current.unstable_data, ...incoming.unstable_data]\n : current.unstable_data;\n const steps = incoming.steps\n ? [...current.steps, ...incoming.steps]\n : current.steps;\n return {\n unstable_state:\n incoming.unstable_state !== undefined\n ? incoming.unstable_state\n : current.unstable_state,\n unstable_annotations: annotations,\n unstable_data: data,\n steps,\n ...(incoming.timing ? { timing: incoming.timing } : {}),\n custom: incoming.custom\n ? { ...current.custom, ...incoming.custom }\n : current.custom,\n };\n }\n\n private handleEvent(aggregator: RunAggregator, event: AgUiEvent) {\n switch (event.type) {\n case \"STATE_SNAPSHOT\": {\n this.stateSnapshot = event.snapshot as ReadonlyJSONValue;\n this.notifyUpdate();\n return;\n }\n case \"STATE_DELTA\": {\n if (event.delta.length === 0) return;\n try {\n const state = this.stateSnapshot ?? {};\n const result = jsonpatch.applyPatch(\n state,\n event.delta as Operation[],\n /* validateOperation */ true,\n /* mutateDocument */ false,\n );\n this.stateSnapshot = result.newDocument as ReadonlyJSONValue;\n this.notifyUpdate();\n } catch (error) {\n this.logger.error?.(\"[agui] failed to apply state delta\", error);\n }\n return;\n }\n case \"MESSAGES_SNAPSHOT\": {\n this.importMessagesSnapshot(event.messages);\n return;\n }\n default:\n aggregator.handle(event);\n }\n }\n\n private importMessagesSnapshot(rawMessages: readonly unknown[]) {\n try {\n const normalized = fromAgUiMessages(rawMessages);\n const converted: ThreadMessage[] = [];\n for (const message of normalized) {\n try {\n converted.push(\n fromThreadMessageLike(\n message as any,\n generateId(),\n FALLBACK_USER_STATUS,\n ),\n );\n } catch (error) {\n this.logger.error?.(\n \"[agui] failed to import message from snapshot\",\n error,\n );\n }\n }\n this.applyExternalMessages(converted);\n } catch (error) {\n this.logger.error?.(\"[agui] failed to import messages snapshot\", error);\n }\n }\n\n private toThreadMessage(message: AppendMessage): ThreadMessage {\n return fromThreadMessageLike(\n message as any,\n generateId(),\n FALLBACK_USER_STATUS,\n );\n }\n\n private resetHead(parentId: string | null | undefined) {\n if (!parentId) {\n if (this.messages.length) {\n this.messages = [];\n }\n return;\n }\n const idx = this.messages.findIndex((message) => message.id === parentId);\n if (idx === -1) return;\n this.messages = this.messages.slice(0, idx + 1);\n }\n\n private isTerminalStatus(status?: MessageStatus): boolean {\n return status?.type === \"complete\" || status?.type === \"incomplete\";\n }\n\n private isPersistableStatus(status?: MessageStatus): boolean {\n if (this.isTerminalStatus(status)) return true;\n return status?.type === \"requires-action\" && status.reason === \"interrupt\";\n }\n\n private recordHistoryEntry(parentId: string | null, message: ThreadMessage) {\n this.appendHistoryItem(parentId, message);\n }\n\n private markPendingAssistantHistory(\n messageId: string,\n parentId: string | null,\n ) {\n if (!this.history) return;\n this.assistantHistoryParents.set(messageId, parentId);\n }\n\n private persistAssistantHistory(messageId: string) {\n if (!this.history) return;\n const parentId = this.assistantHistoryParents.get(messageId);\n if (parentId === undefined) return;\n const message = this.messages.find((m) => m.id === messageId);\n if (!message || message.role !== \"assistant\") return;\n if (!this.isPersistableStatus(message.status)) return;\n this.assistantHistoryParents.delete(messageId);\n this.appendHistoryItem(parentId, message);\n }\n\n private appendHistoryItem(parentId: string | null, message: ThreadMessage) {\n if (!this.history || this.recordedHistoryIds.has(message.id)) return;\n this.recordedHistoryIds.add(message.id);\n void this.history.append({ parentId, message }).catch((error) => {\n this.recordedHistoryIds.delete(message.id);\n this.logger.error?.(\"[agui] failed to append history entry\", error);\n });\n }\n}\n"],"mappings":";;;;;;;AAgCA,MAAM,mBAAmB;AACzB,MAAM,6BAA6B,GAAG,mBAAmB,WAAW;AACpE,MAAM,kBAAkB,OAAe,GAAG,WAAW,gBAAgB;AAErE,MAAM,mBAAmB,OAAO,kBAAkB;AAuBlD,MAAM,uBAAuB;CAAE,MAAM;CAAY,QAAQ;AAAU;AAEnE,IAAa,wBAAb,MAAmC;CACjC;CACA;CACA;CACA;CACA;CACA;CAEA;CACA,WAAoC,CAAC;CACrC,gBAAwB;CACxB,kBAAkD;CAClD;CACA,eAAqC;CACrC;CACA;CACA,0CAA2C,IAAI,IAA2B;CAC1E,qCAAsC,IAAI,IAAY;CACtD,aAAqB;CACrB;CACA,yBAAgD;CAEhD,YAAY,SAAsB;EAChC,KAAK,QAAQ,QAAQ;EACrB,KAAK,SAAS,QAAQ;EACtB,KAAK,eAAe,QAAQ;EAC5B,KAAK,UAAU,QAAQ;EACvB,KAAK,WAAW,QAAQ;EACxB,KAAK,UAAU,QAAQ;EACvB,KAAK,eAAe,QAAQ;EAC5B,KAAK,kBAAkB;CACzB;CAEA,cAAc,SAA4C;EACxD,KAAK,QAAQ,QAAQ;EACrB,KAAK,SAAS,QAAQ;EACtB,KAAK,eAAe,QAAQ;EAC5B,KAAK,UAAU,QAAQ;EACvB,KAAK,WAAW,QAAQ;EACxB,KAAK,UAAU,QAAQ;EACvB,KAAK,kBAAkB;CACzB;CAEA,cAAc,SAA2B;EACvC,KAAK,UAAU;CACjB;CAEA,gBAAgB;EACd,KAAK,UAAU,KAAA;CACjB;CAEA,cAAwC;EACtC,OAAO,KAAK;CACd;CAEA,WAA0C;EACxC,OAAO,KAAK;CACd;CAEA,YAAqB;EACnB,OAAO,KAAK;CACd;CAEA,IAAI,YAAqB;EACvB,OAAO,KAAK;CACd;CAEA,kBAAiC;EAC/B,IAAI,KAAK,cAAc,OAAO,KAAK;EAEnC,MAAM,UAAU,KAAK,SAAS,KAAK,KAAK,QAAQ,QAAQ,IAAI;EAE5D,KAAK,aAAa;EAElB,KAAK,eAAe,QACjB,KAAK,OAAO,SAAS;GACpB,IAAI,CAAC,MAAM;GAEX,MAAM,WAAW,KAAK,SAAS,KAAK,SAAS,KAAK,OAAO;GACzD,KAAK,sBAAsB,QAAQ;GAEnC,IAAI,KAAK,iBAAiB;IACxB,MAAM,WAAW,KAAK,UAAU,SAAS,GAAG,EAAE,GAAG,MAAM;IACvD,MAAM,eAAe,KAAK,SAAS,QAAQ,KAAK,KAAK,OAAO;IAC5D,MAAM,KAAK,SACT,UACA,KAAK,eACL,KAAA,GACA,YACF;GACF;EACF,CAAC,EACA,OAAO,UAAU;GAChB,KAAK,OAAO,QAAQ,iCAAiC,KAAK;GAC1D,KAAK,UACH,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC,CAC1D;EACF,CAAC,EACA,cAAc;GACb,KAAK,aAAa;GAClB,KAAK,aAAa;EACpB,CAAC;EAEH,KAAK,aAAa;EAClB,OAAO,KAAK;CACd;CAEA,MAAM,OAAO,SAAuC;EAClD,MAAM,WAAW,QAAQ,YAAY,QAAQ,SAAS;EACtD,IAAI,UAAU,KAAK,0BAA0B;EAC7C,IAAI,QAAQ,UACV,KAAK,WAAW,KAAK,SAAS,QAC3B,UAAU,MAAM,OAAO,QAAQ,QAClC;EAEF,KAAK,UAAU,QAAQ,QAAQ;EAE/B,MAAM,gBAAgB,KAAK,gBAAgB,OAAO;EAClD,KAAK,WAAW,CAAC,GAAG,KAAK,UAAU,aAAa;EAChD,KAAK,aAAa;EAClB,KAAK,mBAAmB,QAAQ,YAAY,MAAM,aAAa;EAE/D,IAAI,CAAC,UAAU;EACf,MAAM,KAAK,SAAS,cAAc,IAAI,QAAQ,SAAS;CACzD;CAEA,MAAM,KAAK,SAAuC;EAChD,MAAM,KAAK,OAAO,OAAO;CAC3B;CAEA,MAAM,OACJ,UACA,SAAoC,CAAC,GACtB;EACf,KAAK,0BAA0B;EAC/B,KAAK,UAAU,QAAQ;EACvB,KAAK,aAAa;EAClB,MAAM,KAAK,SAAS,UAAU,OAAO,SAAS;CAChD;CAEA,MAAM,SAAwB;EAC5B,IAAI,CAAC,KAAK,iBAAiB;EAC3B,KAAK,gBAAgB,MAAM;CAC7B;CAEA,MAAM,OAAO,QAAwC;EACnD,KAAK,0BAA0B;EAC/B,MAAM,KAAK,SACT,OAAO,UACP,OAAO,aAAa,KAAK,eACzB,KAAA,GACA,OAAO,MACT;CACF;CAEA,4BAA0C;EACxC,IAAI,CAAC,KAAK,qBAAqB,GAAG;EAClC,MAAM,IAAI,MACR,0GACF;CACF;CAEA,uBAGS;EACP,MAAM,YAAY,KAAK,SAAS,UAAU,MAAM,EAAE,SAAS,WAAW;EAGtE,IACE,CAAC,aACD,UAAU,QAAQ,SAAS,qBAC3B,UAAU,OAAO,WAAW,aAE5B,OAAO;EAET,MAAM,SACJ,UAAU,SAAS,OAAO,2BAGzB;EACH,IAAI,CAAC,QAAQ,QAAQ,OAAO;EAC5B,OAAO;GAAE,WAAW,UAAU;GAAI,YAAY;EAAO;CACvD;CAEA,MAAM,yBACJ,WACe;EACf,MAAM,UAAU,KAAK,qBAAqB;EAC1C,IAAI,CAAC,SACH,MAAM,IAAI,MACR,uEACF;EAGF,MAAM,gCAAgB,IAAI,IAA6B;EACvD,KAAK,MAAM,SAAS,WAAW;GAC7B,IAAI,CAAC,SAAS,OAAO,MAAM,gBAAgB,UACzC,MAAM,IAAI,MACR,uEACF;GAEF,IAAI,MAAM,WAAW,cAAc,MAAM,WAAW,aAClD,MAAM,IAAI,MACR,oDAAoD,MAAM,OAAO,kBAAkB,MAAM,aAC3F;GAEF,IAAI,cAAc,IAAI,MAAM,WAAW,GACrC,MAAM,IAAI,MACR,qEAAqE,MAAM,aAC7E;GAEF,cAAc,IAAI,MAAM,aAAa,KAAK;EAC5C;EAEA,MAAM,UAAU,QAAQ,WAAW,KAAK,MAAM,EAAE,EAAE;EAClD,MAAM,UAAU,QAAQ,QAAQ,OAAO,CAAC,cAAc,IAAI,EAAE,CAAC;EAC7D,IAAI,QAAQ,SAAS,GACnB,MAAM,IAAI,MACR,2EAA2E,QAAQ,KAAK,IAAI,GAC9F;EAEF,MAAM,QAAQ,IAAI,IAAI,OAAO;EAC7B,MAAM,aAAa,CAAC,GAAG,cAAc,KAAK,CAAC,EAAE,QAAQ,OAAO,CAAC,MAAM,IAAI,EAAE,CAAC;EAC1E,IAAI,WAAW,SAAS,GACtB,MAAM,IAAI,MACR,2DAA2D,WAAW,KAAK,IAAI,GACjF;EAGF,MAAM,MAAM,KAAK,IAAI;EACrB,KAAK,MAAM,aAAa,QAAQ,YAAY;GAC1C,IAAI,CAAC,UAAU,WAAW;GAC1B,MAAM,SAAS,IAAI,KAAK,UAAU,SAAS,EAAE,QAAQ;GACrD,IAAI,OAAO,MAAM,MAAM,GACrB,MAAM,IAAI,MACR,8CAA8C,UAAU,GAAG,4BAA4B,UAAU,UAAU,EAC7G;GAEF,IAAI,UAAU,KACZ,MAAM,IAAI,MACR,8CAA8C,UAAU,GAAG,cAAc,UAAU,WACrF;EAEJ;EAEA,MAAM,SAA4B,QAAQ,KACvC,OAAO,cAAc,IAAI,EAAE,CAC9B;EAEA,IAAI,KAAK,eACP,MAAM,IAAI,MACR,+DACF;EAGF,KAAK,uBAAuB,QAAQ,SAAS;EAC7C,MAAM,KAAK,SAAS,QAAQ,WAAW,KAAK,eAAe,MAAM;CACnE;CAEA,uBAA+B,WAAyB;EACtD,IAAI,UAAU;EACd,KAAK,WAAW,KAAK,SAAS,KAAK,YAAY;GAC7C,IAAI,QAAQ,OAAO,aAAa,QAAQ,SAAS,aAC/C,OAAO;GACT,MAAM,YAAY;GAClB,IACE,UAAU,QAAQ,SAAS,qBAC3B,UAAU,OAAO,WAAW,aAE5B,OAAO;GAET,UAAU;GAIV,MAAM,EAAE,YAAY,OAAO,GAAG,aAHb,UAAU,SAAS,OAAA,WAGmB,CAAC;GACxD,MAAM,YAAY,EAAE,GAAG,UAAU,SAAS,OAAO;GACjD,IAAI,OAAO,KAAK,QAAQ,EAAE,SAAS,GACjC,UAAU,4BAA4B;QAEtC,OAAO,UAAU;GAEnB,OAAO;IACL,GAAG;IACH,QAAQ;KAAE,MAAM;KAAqB,QAAQ;IAAmB;IAChE,UAAU;KAAE,GAAG,UAAU;KAAU,QAAQ;IAAU;GACvD;EACF,CAAC;EACD,IAAI,SAAS,KAAK,aAAa;CACjC;CAEA,yBAAyB,YAAwC;EAC/D,IAAI;EACJ,KAAK,IAAI,QAAQ,KAAK,SAAS,SAAS,GAAG,SAAS,GAAG,SAAS;GAC9D,MAAM,UAAU,KAAK,SAAS;GAC9B,IAAI,CAAC,WAAW,QAAQ,SAAS,aAAa;GAC9C,KAAK,MAAM,QAAQ,QAAQ,SAAS;IAClC,IAAI,KAAK,SAAS,eAAe,KAAK,eAAe,YACnD;IACF,IAAI,EAAE,YAAY,SAAS,KAAK,WAAW,KAAA,GACzC,OAAO,QAAQ;IAEjB,sBAAsB,QAAQ;GAChC;EACF;EACA,OAAO;CACT;CAEA,cAAc,SAAqC;EACjD,IAAI,UAAU;EACd,KAAK,WAAW,KAAK,SAAS,KAAK,YAAY;GAC7C,IAAI,QAAQ,OAAO,QAAQ,aAAa,QAAQ,SAAS,aACvD,OAAO;GACT,MAAM,YAAY;GAClB,IAAI,kBAAkB;GACtB,MAAM,UAAU,UAAU,QAAQ,KAAK,SAAS;IAC9C,IAAI,KAAK,SAAS,eAAe,KAAK,eAAe,QAAQ,YAC3D,OAAO;IACT,kBAAkB;IAClB,OAAO;KACL,GAAG;KACH,QAAQ,QAAQ;KAChB,UAAU,QAAQ;KAClB,SAAS,QAAQ;IACnB;GACF,CAAC;GACD,IAAI,CAAC,iBAAiB,OAAO;GAC7B,UAAU;GACV,OAAO;IAAE,GAAG;IAAW;GAAQ;EACjC,CAAC;EAED,IAAI,CAAC,SAAS;EACd,KAAK,aAAa;EAClB,KAAK,4BAA4B,QAAQ,SAAS;CACpD;CAOA,4BAAoC,WAAyB;EAC3D,MAAM,UAAU,KAAK,SAAS,MAAM,MAAM,EAAE,OAAO,SAAS;EAC5D,IAAI,CAAC,WAAW,QAAQ,SAAS,aAAa;EAC9C,MAAM,YAAY;EAClB,IACE,UAAU,QAAQ,SAAS,qBAC3B,UAAU,OAAO,WAAW,cAE5B;EAOF,IAAI,CALgB,UAAU,QAAQ,OACnC,SACC,KAAK,SAAS,eACb,YAAY,QAAQ,KAAK,WAAW,KAAA,CAE1B,GAAG;EAElB,KAAK,WAAW,KAAK,SAAS,KAAK,MACjC,EAAE,OAAO,aAAa,EAAE,SAAS,cAC7B;GACE,GAAI;GACJ,QAAQ;IAAE,MAAM;IAAqB,QAAQ;GAAmB;EAClE,IACA,CACN;EACA,KAAK,aAAa;EAClB,KAAK,wBAAwB,SAAS;EAEtC,IAAI,KAAK,eAAe;GAGtB,KAAK,yBAAyB;GAC9B;EACF;EACA,KAAK,eAAe,SAAS;CAC/B;CAEA,eAAuB,WAAyB;EAC9C,KAAU,SAAS,WAAW,KAAK,aAAa,EAAE,OAAO,UAAU;GACjE,KAAK,UAAU,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC,CAAC;EAC1E,CAAC;CACH;CAEA,sBAAsB,UAA0C;EAC9D,KAAK,wBAAwB,MAAM;EACnC,KAAK,WAAW,CAAC,GAAG,QAAQ;EAC5B,KAAK,mBAAmB,MAAM;EAC9B,KAAK,MAAM,WAAW,KAAK,UACzB,KAAK,mBAAmB,IAAI,QAAQ,EAAE;EAExC,KAAK,aAAa;CACpB;CAEA,kBAAkB,OAAgC;EAChD,KAAK,gBAAgB;EACrB,KAAK,aAAa;CACpB;CAEA,MAAc,SACZ,UACA,WACA,QACA,cACe;EACf,MAAM,sBAAsB,aAAa,CAAC;EAC1C,KAAK,gBAAgB;EACrB,KAAK,UAAU,QAAQ;EACvB,MAAM,qBAAqB,CAAC,GAAG,KAAK,QAAQ;EAE5C,KAAK,eAAe;EACpB,MAAM,oBAAoB,YAAY,KAAK,SAAS,GAAG,EAAE,GAAG,MAAM;EAClE,IAAI;EACJ,MAAM,wBAAwB;GAC5B,IAAI,oBAAoB,OAAO;GAC/B,MAAM,UAAU,KAAK,2BAA2B;GAChD,qBAAqB;GACrB,KAAK,4BAA4B,SAAS,qBAAqB,IAAI;GACnE,OAAO;EACT;EAEA,MAAM,eAAe,WAA+B;GAClD,MAAM,WAAW,KAAK,uBAAuB,gBAAgB,GAAG,MAAM;GACtE,IAAI,aAAa,oBACf,qBAAqB;EAEzB;EAEA,MAAM,aAAa,IAAI,cAAc;GACnC,cAAc,KAAK;GACnB,QAAQ,KAAK;GACb,MAAM;GACN,oBAAoB,aAAa;IAC/B,MAAM,cAAc,gBAAgB;IACpC,IAAI,gBAAgB,UAAU;IAC9B,KAAK,oBAAoB,aAAa,QAAQ;IAC9C,qBAAqB;GACvB;EACF,CAAC;EACD,MAAM,YAAY,UAAqB,KAAK,YAAY,YAAY,KAAK;EAEzE,MAAM,kBAAkB,IAAI,gBAAgB;EAC5C,MAAM,cAAc,gBAAgB;EACpC,KAAK,kBAAkB;EAEvB,IAAI,kBAAkB,SAAS,EAAE,MAAM,gBAAgB,CAAC;EACxD,YAAY,iBACV,eACM;GACJ,UAAU;GACV,KAAK,UAAU,eAAe;GAC9B,KAAK,WAAW;EAClB,GACA,EAAE,MAAM,KAAK,CACf;EAEA,KAAK,WAAW,IAAI;EAEpB,IAAI;GACF,IAAI,cAAc;IAEhB,kBACE,YAAY,EAAE,QAAQ;KAAE,MAAM;KAAc,QAAQ;IAAY,EAAE,CAAC;IACrE,MAAM,KAAK,oBAAoB,cAAc;KAC3C,WAAW;KACX,UAAU,KAAK,MAAM,YAAY;KACjC,UAAU;KACV;KACA;KACA;KACA;KACA,6BAA6B;IAC/B,CAAC;GACH,OAAO;IACL,MAAM,QAAQ,WAAW;IACzB,WAAW,OAAO;KAAE,MAAM;KAAe;IAAM,CAAC;IAChD,MAAM,QAAQ,KAAK,cACjB,OACA,qBACA,oBACA,MACF;IACA,MAAM,aAAa,qBAAqB;KACtC;KACA;KACA,QAAQ,KAAK;KACb,cAAc,UAAU;MACtB,KAAK,eAAe;MACpB,KAAK,UAAU,KAAK;KACtB;IACF,CAAC;IACD,IAAI;KACF,KAAM,MAAc,WAAW,MAAM;KACrC,KAAM,MAAc,WAAW,MAAM;KACrC,KAAM,MAAc,QAAQ,MAAM,SAAS;IAC7C,QAAQ,CAER;IACA,MAAO,KAAK,MAAc,SAAS,OAAO,YAAY,EACpD,QAAQ,YACV,CAAC;GACH;EACF,SAAS,OAAO;GACd,IAAI,CAAC,YAAY,SAAS;IACxB,MAAM,MAAM,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC;IACpE,SAAS;KAAE,MAAM;KAAa,SAAS,IAAI;IAAQ,CAAC;IACpD,KAAK,UAAU,GAAG;IAClB,KAAK,eAAe,KAAK,gBAAgB;GAC3C;EACF,UAAU;GACR,KAAK,UAAU,eAAe;EAChC;EAEA,IAAI,KAAK,cAAc;GACrB,MAAM,MAAM,KAAK;GACjB,KAAK,eAAe;GACpB,KAAK,yBAAyB;GAC9B,MAAM;EACR;EAIA,IAAI,KAAK,2BAA2B,MAAM;GACxC,MAAM,kBAAkB,KAAK;GAC7B,KAAK,yBAAyB;GAC9B,IAAI,CAAC,YAAY,SACf,KAAK,eAAe,eAAe;EAEvC;CACF;CAGA,MAAc,oBACZ,QACA,KAUe;EACf,MAAM,cAAc,IAAI,gBAAgB;EACxC,MAAM,kBAAkB,IAAI,sBAAsB,KAAK;EACvD,MAAM,UAA+B;GACnC,UAAU,IAAI;GACd,WAAW,IAAI;GACf,aAAa,IAAI;GACjB,SAAS,KAAK,SAAS,OAAO,gBAAgB,KAAK,CAAC;GACpD,6BAA6B;GAC7B,mBAAmB,IAAI;GACvB,mBAAmB,IAAI;GACvB,2BAA2B;IACzB,MAAM,UAAU,KAAK,SAAS,MAAM,MAAM,EAAE,OAAO,UAAU,CAAC;IAC9D,IAAI,CAAC,SACH,MAAM,IAAI,MACR,wEACF;IAEF,OAAO;GACT;EACF;EAEA,IAAI;GACF,WAAW,MAAM,UAAU,OAAO,OAAO,GAAG;IAC1C,IAAI,IAAI,YAAY,SAAS;IAC7B,IAAI,YAAY,MAAM;GACxB;EACF,SAAS,OAAO;GACd,IAAI,IAAI,YAAY,SAAS;GAC7B,MAAM,MAAM,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC;GACpE,IAAI,YAAY,EACd,QAAQ;IAAE,MAAM;IAAc,QAAQ;IAAS,OAAO,IAAI;GAAQ,EACpE,CAAC;GACD,KAAK,UAAU,GAAG;GAClB,KAAK,eAAe,KAAK,gBAAgB;GACzC;EACF;EAEA,IAAI,IAAI,YAAY,SAAS;EAC7B,MAAM,UAAU,KAAK,SAAS,MAAM,MAAM,EAAE,OAAO,UAAU,CAAC;EAC9D,IAAI,CAAC,WAAW,QAAQ,QAAQ,SAAS,WACvC,IAAI,YAAY,EAAE,QAAQ;GAAE,MAAM;GAAY,QAAQ;EAAU,EAAE,CAAC;CAEvE;CAEA,cACE,OACA,WACA,iBACA,QACA;EACA,MAAM,WAAW,KAAK,MAAM,YAAY;EACxC,MAAM,WAAW,eAAe,mBAAmB,KAAK,QAAQ;EAChE,MAAM,UAAU,KAAK,SAAS,OAAO,gBAAgB;EACrD,OAAO;GACL;GACA;GACA,OAAO,KAAK,iBAAiB;GAC7B;GACA,OAAO,YAAY,SAAS,KAAK;GACjC,SAAS,SAAS,SACd,CAAC;IAAE,aAAa;IAAU,OAAO,QAAQ;GAAO,CAAC,IACjD,CAAC;GACL,gBAAgB;IACd,GAAI,SAAS,gBAAgB,CAAC;IAC9B,GAAI,SAAS,UAAU,CAAC;IACxB,GAAI,WAAW,SAAS,EAAE,WAAW,UAAU,OAAO,IAAI,CAAC;GAC7D;GACA,GAAI,WAAW,KAAA,IAAY,EAAE,OAAO,IAAI,CAAC;EAC3C;CACF;CAEA,oBAAkC;EAChC,MAAM,QAAQ,KAAK;EACnB,IAAI,MAAM,mBAAmB;EAC7B,MAAM,oBAAoB;EAE1B,MAAM,WADa,OAAO,OAAO,OAAO,sBACd,IACtB,MAAM,uBACN,OAAO,eAAe,KAAK,GAAG;EAClC,IAAI,OAAO,aAAa,YAAY;EACpC,MAAM,uBAAuB,SAE3B,QACA;GACA,MAAM,QAAQ,SAAS,KAAK,MAAM,MAAM;GACxC,IAAI,QAAQ,WAAW,KAAA,KAAa,SAAS,OAAO,UAAU,UAC5D,OAAO;IAAE,GAAI;IAAkB,QAAQ,OAAO;GAAO;GAEvD,OAAO;EACT;CACF;CAEA,WAAmB,SAAkB;EACnC,KAAK,gBAAgB;EACrB,KAAK,aAAa;CACpB;CAEA,UAAkB,YAAoC;EACpD,IAAI,KAAK,oBAAoB,YAC3B,KAAK,kBAAkB;EAEzB,KAAK,WAAW,KAAK;CACvB;CAEA,6BAA6C;EAC3C,MAAM,KAAK,qBAAqB;EAChC,MAAM,YAAoC;GACxC;GACA,MAAM;GACN,2BAAW,IAAI,KAAK;GACpB,QAAQ,EAAE,MAAM,UAAU;GAC1B,SAAS,CAAC;GACV,UAAU;IACR,gBAAgB,KAAK,iBAAiB;IACtC,sBAAsB,CAAC;IACvB,eAAe,CAAC;IAChB,OAAO,CAAC;IACR,QAAQ,CAAC;GACX;EACF;EACA,KAAK,WAAW,CAAC,GAAG,KAAK,UAAU,SAAS;EAC5C,KAAK,aAAa;EAClB,OAAO;CACT;CAEA,oBAA4B,OAAe,OAAqB;EAC9D,IAAI,UAAU,OAAO;EAIrB,IAF6B,KAAK,SAAS,MAAM,MAAM,EAAE,OAAO,KAEzC,GAAG;GACxB,KAAK,OAAO,QACV,2FACA;IAAE;IAAO;GAAM,CACjB;GACA,KAAK,WAAW,KAAK,SAAS,QAAQ,MAAM,EAAE,OAAO,KAAK;EAC5D,OACE,KAAK,WAAW,KAAK,SAAS,KAAK,MACjC,EAAE,OAAO,QAAQ;GAAE,GAAG;GAAG,IAAI;EAAM,IAAI,CACzC;EAGF,MAAM,gBAAgB,KAAK,wBAAwB,IAAI,KAAK;EAC5D,IAAI,kBAAkB,KAAA,GAAW;GAC/B,KAAK,wBAAwB,OAAO,KAAK;GACzC,IAAI,CAAC,KAAK,wBAAwB,IAAI,KAAK,GACzC,KAAK,wBAAwB,IAAI,OAAO,aAAa;EAEzD;EAEA,IAAI,KAAK,mBAAmB,IAAI,KAAK,GAAG;GACtC,KAAK,mBAAmB,OAAO,KAAK;GACpC,KAAK,mBAAmB,IAAI,KAAK;EACnC;EAEA,KAAK,aAAa;CACpB;CAEA,uBACE,WACA,QACQ;EACR,IAAI,UAAU;EACd,IAAI;EACJ,KAAK,WAAW,KAAK,SAAS,KAAK,YAAY;GAC7C,IAAI,QAAQ,OAAO,aAAa,QAAQ,SAAS,aAC/C,OAAO;GACT,UAAU;GACV,MAAM,YAAY;GAClB,MAAM,WAAW,OAAO,WACpB,KAAK,uBAAuB,UAAU,UAAU,OAAO,QAAQ,IAC/D,UAAU;GACd,eAAe,OAAO,UAAU,UAAU;GAC1C,MAAM,UACJ,OAAO,YAAY,KAAA,IACf,KAAK,oBACH,UAAU,SACV,OAAO,OACT,IACA,UAAU;GAChB,OAAO;IACL,GAAG;IACH;IACA,QAAQ;IACR;GACF;EACF,CAAC;EACD,IAAI,CAAC,SAAS,OAAO;EAErB,IAAI,oBAAoB;EAGxB,IADE,iBAAiB,KAAA,KAAa,aAAa,SAAS,aACrC,eAAe,SAAS,GAAG;GAC1C,MAAM,WAAW,WAAW;GAC5B,KAAK,oBAAoB,WAAW,QAAQ;GAC5C,oBAAoB;EACtB,OACE,KAAK,aAAa;EAEpB,IAAI,KAAK,oBAAoB,YAAY,GACvC,KAAK,wBAAwB,iBAAiB;EAEhD,KAAK,4BAA4B,iBAAiB;EAClD,OAAO;CACT;CAMA,oBACE,UACA,MACmC;EACnC,MAAM,2BAAW,IAAI,IAAiC;EACtD,KAAK,MAAM,QAAQ,UACjB,IACE,KAAK,SAAS,eACd,YAAY,QACZ,KAAK,WAAW,KAAA,GAEhB,SAAS,IAAI,KAAK,YAAY,IAAI;EAGtC,IAAI,SAAS,SAAS,GAAG,OAAO;EAEhC,IAAI,UAAU;EACd,MAAM,SAAS,KAAK,KAAK,SAAS;GAChC,IAAI,KAAK,SAAS,aAAa,OAAO;GACtC,IAAI,YAAY,QAAQ,KAAK,WAAW,KAAA,GAAW,OAAO;GAC1D,MAAM,QAAQ,SAAS,IAAI,KAAK,UAAU;GAC1C,IAAI,CAAC,OAAO,OAAO;GACnB,UAAU;GACV,OAAO;IACL,GAAG;IACH,QAAQ,MAAM;IACd,GAAI,MAAM,aAAa,KAAA,IAAY,EAAE,UAAU,MAAM,SAAS,IAAI,CAAC;IACnE,GAAI,MAAM,YAAY,KAAA,IAAY,EAAE,SAAS,MAAM,QAAQ,IAAI,CAAC;GAClE;EACF,CAAC;EACD,OAAO,UAAW,SAA+C;CACnE;CAEA,uBACE,SACA,UACoC;EACpC,MAAM,cAAc,SAAS,uBACzB,CAAC,GAAG,QAAQ,sBAAsB,GAAG,SAAS,oBAAoB,IAClE,QAAQ;EACZ,MAAM,OAAO,SAAS,gBAClB,CAAC,GAAG,QAAQ,eAAe,GAAG,SAAS,aAAa,IACpD,QAAQ;EACZ,MAAM,QAAQ,SAAS,QACnB,CAAC,GAAG,QAAQ,OAAO,GAAG,SAAS,KAAK,IACpC,QAAQ;EACZ,OAAO;GACL,gBACE,SAAS,mBAAmB,KAAA,IACxB,SAAS,iBACT,QAAQ;GACd,sBAAsB;GACtB,eAAe;GACf;GACA,GAAI,SAAS,SAAS,EAAE,QAAQ,SAAS,OAAO,IAAI,CAAC;GACrD,QAAQ,SAAS,SACb;IAAE,GAAG,QAAQ;IAAQ,GAAG,SAAS;GAAO,IACxC,QAAQ;EACd;CACF;CAEA,YAAoB,YAA2B,OAAkB;EAC/D,QAAQ,MAAM,MAAd;GACE,KAAK;IACH,KAAK,gBAAgB,MAAM;IAC3B,KAAK,aAAa;IAClB;GAEF,KAAK;IACH,IAAI,MAAM,MAAM,WAAW,GAAG;IAC9B,IAAI;KACF,MAAM,QAAQ,KAAK,iBAAiB,CAAC;KACrC,MAAM,SAAS,UAAU,WACvB,OACA,MAAM,OACkB,MACH,KACvB;KACA,KAAK,gBAAgB,OAAO;KAC5B,KAAK,aAAa;IACpB,SAAS,OAAO;KACd,KAAK,OAAO,QAAQ,sCAAsC,KAAK;IACjE;IACA;GAEF,KAAK;IACH,KAAK,uBAAuB,MAAM,QAAQ;IAC1C;GAEF,SACE,WAAW,OAAO,KAAK;EAC3B;CACF;CAEA,uBAA+B,aAAiC;EAC9D,IAAI;GACF,MAAM,aAAa,iBAAiB,WAAW;GAC/C,MAAM,YAA6B,CAAC;GACpC,KAAK,MAAM,WAAW,YACpB,IAAI;IACF,UAAU,KACR,sBACE,SACA,WAAW,GACX,oBACF,CACF;GACF,SAAS,OAAO;IACd,KAAK,OAAO,QACV,iDACA,KACF;GACF;GAEF,KAAK,sBAAsB,SAAS;EACtC,SAAS,OAAO;GACd,KAAK,OAAO,QAAQ,6CAA6C,KAAK;EACxE;CACF;CAEA,gBAAwB,SAAuC;EAC7D,OAAO,sBACL,SACA,WAAW,GACX,oBACF;CACF;CAEA,UAAkB,UAAqC;EACrD,IAAI,CAAC,UAAU;GACb,IAAI,KAAK,SAAS,QAChB,KAAK,WAAW,CAAC;GAEnB;EACF;EACA,MAAM,MAAM,KAAK,SAAS,WAAW,YAAY,QAAQ,OAAO,QAAQ;EACxE,IAAI,QAAQ,IAAI;EAChB,KAAK,WAAW,KAAK,SAAS,MAAM,GAAG,MAAM,CAAC;CAChD;CAEA,iBAAyB,QAAiC;EACxD,OAAO,QAAQ,SAAS,cAAc,QAAQ,SAAS;CACzD;CAEA,oBAA4B,QAAiC;EAC3D,IAAI,KAAK,iBAAiB,MAAM,GAAG,OAAO;EAC1C,OAAO,QAAQ,SAAS,qBAAqB,OAAO,WAAW;CACjE;CAEA,mBAA2B,UAAyB,SAAwB;EAC1E,KAAK,kBAAkB,UAAU,OAAO;CAC1C;CAEA,4BACE,WACA,UACA;EACA,IAAI,CAAC,KAAK,SAAS;EACnB,KAAK,wBAAwB,IAAI,WAAW,QAAQ;CACtD;CAEA,wBAAgC,WAAmB;EACjD,IAAI,CAAC,KAAK,SAAS;EACnB,MAAM,WAAW,KAAK,wBAAwB,IAAI,SAAS;EAC3D,IAAI,aAAa,KAAA,GAAW;EAC5B,MAAM,UAAU,KAAK,SAAS,MAAM,MAAM,EAAE,OAAO,SAAS;EAC5D,IAAI,CAAC,WAAW,QAAQ,SAAS,aAAa;EAC9C,IAAI,CAAC,KAAK,oBAAoB,QAAQ,MAAM,GAAG;EAC/C,KAAK,wBAAwB,OAAO,SAAS;EAC7C,KAAK,kBAAkB,UAAU,OAAO;CAC1C;CAEA,kBAA0B,UAAyB,SAAwB;EACzE,IAAI,CAAC,KAAK,WAAW,KAAK,mBAAmB,IAAI,QAAQ,EAAE,GAAG;EAC9D,KAAK,mBAAmB,IAAI,QAAQ,EAAE;EACtC,KAAU,QAAQ,OAAO;GAAE;GAAU;EAAQ,CAAC,EAAE,OAAO,UAAU;GAC/D,KAAK,mBAAmB,OAAO,QAAQ,EAAE;GACzC,KAAK,OAAO,QAAQ,yCAAyC,KAAK;EACpE,CAAC;CACH;AACF"}
|
package/dist/runtime/types.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Logger } from "./logger.js";
|
|
2
|
-
import { AttachmentAdapter, DictationAdapter, FeedbackAdapter, SpeechSynthesisAdapter, ThreadHistoryAdapter, ThreadMessage } from "@assistant-ui/core";
|
|
2
|
+
import { AttachmentAdapter, DictationAdapter, ExternalStoreSharedOptions, FeedbackAdapter, RealtimeVoiceAdapter, SpeechSynthesisAdapter, ThreadHistoryAdapter, ThreadMessage } from "@assistant-ui/core";
|
|
3
3
|
import { HttpAgent } from "@ag-ui/client";
|
|
4
4
|
import { ReadonlyJSONValue } from "assistant-stream/utils";
|
|
5
5
|
|
|
@@ -22,6 +22,7 @@ type UseAgUiRuntimeAdapters = {
|
|
|
22
22
|
attachments?: AttachmentAdapter;
|
|
23
23
|
speech?: SpeechSynthesisAdapter;
|
|
24
24
|
dictation?: DictationAdapter;
|
|
25
|
+
voice?: RealtimeVoiceAdapter;
|
|
25
26
|
feedback?: FeedbackAdapter;
|
|
26
27
|
history?: ThreadHistoryAdapter;
|
|
27
28
|
/**
|
|
@@ -29,7 +30,7 @@ type UseAgUiRuntimeAdapters = {
|
|
|
29
30
|
*/
|
|
30
31
|
threadList?: UseAgUiThreadListAdapter;
|
|
31
32
|
};
|
|
32
|
-
type UseAgUiRuntimeOptions = {
|
|
33
|
+
type UseAgUiRuntimeOptions = ExternalStoreSharedOptions & {
|
|
33
34
|
agent: HttpAgent;
|
|
34
35
|
logger?: Partial<Logger>;
|
|
35
36
|
showThinking?: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","names":[],"sources":["../../src/runtime/types.ts"],"mappings":";;;;;;;;
|
|
1
|
+
{"version":3,"file":"types.d.ts","names":[],"sources":["../../src/runtime/types.ts"],"mappings":";;;;;;;;AAiBA;KAAY,wBAAA;EACV,QAAA;EACA,mBAAA,UAA6B,OAAA;EAC7B,gBAAA,KACM,QAAA,aACE,OAAA;IACE,QAAA,WAAmB,aAAA;IACnB,KAAA,GAAQ,iBAAA;EAAA;IAER,QAAA,WAAmB,aAAA;IAAiB,KAAA,GAAQ,iBAAA;EAAA;AAAA;AAAA,KAI5C,sBAAA;EACV,WAAA,GAAc,iBAAA;EACd,MAAA,GAAS,sBAAA;EACT,SAAA,GAAY,gBAAA;EACZ,KAAA,GAAQ,oBAAA;EACR,QAAA,GAAW,eAAA;EACX,OAAA,GAAU,oBAAA;EAZA;;;EAgBV,UAAA,GAAa,wBAAA;AAAA;AAAA,KAGH,qBAAA,GAAwB,0BAAA;EAClC,KAAA,EAAO,SAAA;EACP,MAAA,GAAS,OAAA,CAAQ,MAAA;EACjB,YAAA;EACA,OAAA,IAAW,CAAA,EAAG,KAAA;EACd,QAAA;EACA,QAAA,GAAW,sBAAA;AAAA;AAAA,KAGD,mBAAA;AAAA,KAMA,aAAA;EACV,EAAA;EACA,MAAA,EAAQ,mBAAA;EACR,OAAA;EACA,UAAA;EACA,cAAA,GAAiB,MAAA;EACjB,SAAA;EACA,QAAA,GAAW,MAAA;AAAA;AAAA,KAGD,eAAA;EACV,WAAA;EACA,MAAA;EACA,OAAA;AAAA;AAAA,KAGU,sBAAA;EACN,IAAA;AAAA;EACA,IAAA;EAAmB,UAAA,EAAY,aAAa;AAAA;AAAA,KAEtC,SAAA;EACN,IAAA;EAAqB,KAAA;AAAA;EAErB,IAAA;EACA,KAAA;EACA,OAAA,GAAU,sBAAsB;AAAA;EAEhC,IAAA;EAAuB,KAAA;AAAA;EACvB,IAAA;EAAmB,OAAA;EAAkB,IAAA;AAAA;EACrC,IAAA;EAA4B,SAAA;AAAA;EAC5B,IAAA;EAA8B,SAAA;EAAoB,KAAA;AAAA;EAClD,IAAA;EAA0B,SAAA;AAAA;EAC1B,IAAA;EAA4B,KAAA;AAAA;EAC5B,IAAA;EAAwB,KAAA;AAAA;EACxB,IAAA;AAAA;EACA,IAAA;EAAuC,KAAA;AAAA;EACvC,IAAA;AAAA;EACA,IAAA;AAAA;EACA,IAAA;EAAyB,SAAA;AAAA;EACzB,IAAA;EAAiC,SAAA;AAAA;EACjC,IAAA;EAAmC,SAAA;EAAoB,KAAA;AAAA;EACvD,IAAA;EAA+B,SAAA;AAAA;EAC/B,IAAA;EAAuB,SAAA;AAAA;EAEvB,IAAA;EACA,UAAA;EACA,YAAA;EACA,eAAA;AAAA;EAEA,IAAA;EAAwB,UAAA;EAAoB,KAAA;AAAA;EAC5C,IAAA;EAAuB,UAAA;AAAA;EAEvB,IAAA;EACA,UAAA;EACA,YAAA;EACA,eAAA;EACA,KAAA;AAAA;EAGA,IAAA;EACA,SAAA;EACA,UAAA;EACA,OAAA;EACA,IAAA;AAAA;EAEA,IAAA;EAAa,KAAA;EAAY,MAAA;AAAA;EACzB,IAAA;EAAgB,IAAA;EAAc,KAAA;AAAA;EAC9B,IAAA;EAAwB,QAAA;AAAA;EACxB,IAAA;EAAqB,KAAA;AAAA;EACrB,IAAA;EAA2B,QAAA;AAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useAgUiRuntime.d.ts","names":[],"sources":["../src/useAgUiRuntime.ts"],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"useAgUiRuntime.d.ts","names":[],"sources":["../src/useAgUiRuntime.ts"],"mappings":";;;;KAwBY,oBAAA,GAAuB,gBAAA;EACjC,6BAAA,iBAA8C,aAAA;EAC9C,iCAAA,GACE,SAAA,WAAoB,eAAA,OACjB,OAAA;AAAA;AAAA,iBAGS,cAAA,CACd,OAAA,EAAS,qBAAA,GACR,oBAAoB"}
|
package/dist/useAgUiRuntime.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { makeLogger } from "./runtime/logger.js";
|
|
3
3
|
import { AgUiThreadRuntimeCore } from "./runtime/AgUiThreadRuntimeCore.js";
|
|
4
4
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
5
|
-
import { useExternalStoreRuntime, useRuntimeAdapters } from "@assistant-ui/core/react";
|
|
5
|
+
import { useExternalStoreRuntime, useExternalStoreSharedOptions, useRuntimeAdapters } from "@assistant-ui/core/react";
|
|
6
6
|
//#region src/useAgUiRuntime.ts
|
|
7
7
|
function useAgUiRuntime(options) {
|
|
8
8
|
const logger = useMemo(() => makeLogger(options.logger), [options.logger]);
|
|
@@ -53,6 +53,7 @@ function useAgUiRuntime(options) {
|
|
|
53
53
|
attachments: adapters?.attachments ?? runtimeAdapters?.attachments,
|
|
54
54
|
speech: adapters?.speech,
|
|
55
55
|
dictation: adapters?.dictation,
|
|
56
|
+
voice: adapters?.voice,
|
|
56
57
|
feedback: adapters?.feedback,
|
|
57
58
|
threadList
|
|
58
59
|
}), [
|
|
@@ -60,8 +61,10 @@ function useAgUiRuntime(options) {
|
|
|
60
61
|
runtimeAdapters,
|
|
61
62
|
threadList
|
|
62
63
|
]);
|
|
64
|
+
const shared = useExternalStoreSharedOptions(options);
|
|
63
65
|
const baseRuntime = useExternalStoreRuntime(useMemo(() => {
|
|
64
66
|
return {
|
|
67
|
+
...shared,
|
|
65
68
|
isLoading: core.isLoading,
|
|
66
69
|
messages: core.getMessages(),
|
|
67
70
|
state: core.getState(),
|
|
@@ -85,7 +88,8 @@ function useAgUiRuntime(options) {
|
|
|
85
88
|
adapterAdapters,
|
|
86
89
|
core,
|
|
87
90
|
_version,
|
|
88
|
-
hasExecutingTools
|
|
91
|
+
hasExecutingTools,
|
|
92
|
+
shared
|
|
89
93
|
]));
|
|
90
94
|
const runtime = useMemo(() => {
|
|
91
95
|
const wrapper = Object.create(baseRuntime);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useAgUiRuntime.js","names":[],"sources":["../src/useAgUiRuntime.ts"],"sourcesContent":["\"use client\";\n\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport {\n useExternalStoreRuntime,\n useRuntimeAdapters,\n} from \"@assistant-ui/core/react\";\nimport type { ToolExecutionStatus } from \"@assistant-ui/core\";\nimport type {\n AssistantRuntime,\n AppendMessage,\n ExternalStoreAdapter,\n ThreadMessage,\n} from \"@assistant-ui/core\";\nimport type { ReadonlyJSONValue } from \"assistant-stream/utils\";\nimport { makeLogger } from \"./runtime/logger\";\nimport type {\n AgUiInterrupt,\n AgUiResumeEntry,\n UseAgUiRuntimeOptions,\n} from \"./runtime/types\";\nimport { AgUiThreadRuntimeCore } from \"./runtime/AgUiThreadRuntimeCore\";\n\nexport type AgUiAssistantRuntime = AssistantRuntime & {\n unstable_getPendingInterrupts: () => readonly AgUiInterrupt[];\n unstable_submitInterruptResponses: (\n responses: readonly AgUiResumeEntry[],\n ) => Promise<void>;\n};\n\nexport function useAgUiRuntime(\n options: UseAgUiRuntimeOptions,\n): AgUiAssistantRuntime {\n const logger = useMemo(() => makeLogger(options.logger), [options.logger]);\n const [_version, setVersion] = useState(0);\n const notifyUpdate = useCallback(() => setVersion((v) => v + 1), []);\n const coreRef = useRef<AgUiThreadRuntimeCore | null>(null);\n const runtimeAdapters = useRuntimeAdapters();\n\n const historyAdapter = options.adapters?.history ?? runtimeAdapters?.history;\n const threadListAdapter = options.adapters?.threadList;\n\n if (!coreRef.current) {\n coreRef.current = new AgUiThreadRuntimeCore({\n agent: options.agent,\n logger,\n showThinking: options.showThinking ?? true,\n ...(options.onError && { onError: options.onError }),\n ...(options.onCancel && { onCancel: options.onCancel }),\n ...(historyAdapter && { history: historyAdapter }),\n notifyUpdate,\n });\n }\n\n const core = coreRef.current;\n core.updateOptions({\n agent: options.agent,\n logger,\n showThinking: options.showThinking ?? true,\n ...(options.onError && { onError: options.onError }),\n ...(options.onCancel && { onCancel: options.onCancel }),\n ...(historyAdapter && { history: historyAdapter }),\n });\n\n const [toolStatuses, setToolStatuses] = useState<\n Record<string, ToolExecutionStatus>\n >({});\n\n const hasExecutingTools = Object.values(toolStatuses).some(\n (s) => s?.type === \"executing\",\n );\n\n const threadList = useMemo(() => {\n if (!threadListAdapter) return undefined;\n\n const { onSwitchToNewThread, onSwitchToThread } = threadListAdapter;\n\n return {\n threadId: threadListAdapter.threadId,\n onSwitchToNewThread: onSwitchToNewThread\n ? async () => {\n await onSwitchToNewThread();\n core.applyExternalMessages([]);\n }\n : undefined,\n onSwitchToThread: onSwitchToThread\n ? async (threadId: string) => {\n const result = await onSwitchToThread(threadId);\n core.applyExternalMessages(result.messages);\n if (result.state) {\n core.loadExternalState(result.state);\n }\n }\n : undefined,\n };\n }, [threadListAdapter, core]);\n\n const adapters = options.adapters;\n const adapterAdapters = useMemo(\n () => ({\n attachments: adapters?.attachments ?? runtimeAdapters?.attachments,\n speech: adapters?.speech,\n dictation: adapters?.dictation,\n feedback: adapters?.feedback,\n threadList,\n }),\n [adapters, runtimeAdapters, threadList],\n );\n\n const store = useMemo(\n () => {\n void _version; // rerender on version change\n\n return {\n isLoading: core.isLoading,\n messages: core.getMessages(),\n state: core.getState(),\n isRunning: core.isRunning() || hasExecutingTools,\n unstable_enableToolInvocations: true,\n setToolStatuses,\n onNew: (message: AppendMessage) => core.append(message),\n onEdit: (message: AppendMessage) => core.edit(message),\n onReload: (parentId: string | null, config: { runConfig?: any }) =>\n core.reload(parentId, config),\n onCancel: async () => {\n core.cancel();\n },\n onAddToolResult: (options) => core.addToolResult(options),\n onResume: (config) => core.resume(config),\n setMessages: (messages: readonly ThreadMessage[]) =>\n core.applyExternalMessages(messages),\n onImport: (messages: readonly ThreadMessage[]) =>\n core.applyExternalMessages(messages),\n onLoadExternalState: (state: ReadonlyJSONValue) =>\n core.loadExternalState(state),\n adapters: adapterAdapters,\n } satisfies ExternalStoreAdapter<ThreadMessage>;\n },\n // _version is intentionally included to trigger re-computation when core state changes via notifyUpdate\n // toolInvocations intentionally excluded: abort/resume use refs internally and work with stale captures\n [adapterAdapters, core, _version, hasExecutingTools],\n );\n\n const baseRuntime = useExternalStoreRuntime(store);\n\n const runtime = useMemo<AgUiAssistantRuntime>(() => {\n const wrapper = Object.create(baseRuntime) as AgUiAssistantRuntime;\n wrapper.unstable_getPendingInterrupts = () =>\n core.getPendingInterrupts()?.interrupts ?? [];\n wrapper.unstable_submitInterruptResponses = (responses) =>\n core.submitInterruptResponses(responses);\n return wrapper;\n }, [baseRuntime, core]);\n\n useEffect(() => {\n core.attachRuntime(runtime);\n return () => {\n core.detachRuntime();\n };\n }, [core, runtime]);\n\n useEffect(() => {\n core.__internal_load();\n }, [core]);\n\n return runtime;\n}\n"],"mappings":";;;;;;
|
|
1
|
+
{"version":3,"file":"useAgUiRuntime.js","names":[],"sources":["../src/useAgUiRuntime.ts"],"sourcesContent":["\"use client\";\n\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport {\n useExternalStoreRuntime,\n useExternalStoreSharedOptions,\n useRuntimeAdapters,\n} from \"@assistant-ui/core/react\";\nimport type { ToolExecutionStatus } from \"@assistant-ui/core\";\nimport type {\n AssistantRuntime,\n AppendMessage,\n ExternalStoreAdapter,\n ThreadMessage,\n} from \"@assistant-ui/core\";\nimport type { ReadonlyJSONValue } from \"assistant-stream/utils\";\nimport { makeLogger } from \"./runtime/logger\";\nimport type {\n AgUiInterrupt,\n AgUiResumeEntry,\n UseAgUiRuntimeOptions,\n} from \"./runtime/types\";\nimport { AgUiThreadRuntimeCore } from \"./runtime/AgUiThreadRuntimeCore\";\n\nexport type AgUiAssistantRuntime = AssistantRuntime & {\n unstable_getPendingInterrupts: () => readonly AgUiInterrupt[];\n unstable_submitInterruptResponses: (\n responses: readonly AgUiResumeEntry[],\n ) => Promise<void>;\n};\n\nexport function useAgUiRuntime(\n options: UseAgUiRuntimeOptions,\n): AgUiAssistantRuntime {\n const logger = useMemo(() => makeLogger(options.logger), [options.logger]);\n const [_version, setVersion] = useState(0);\n const notifyUpdate = useCallback(() => setVersion((v) => v + 1), []);\n const coreRef = useRef<AgUiThreadRuntimeCore | null>(null);\n const runtimeAdapters = useRuntimeAdapters();\n\n const historyAdapter = options.adapters?.history ?? runtimeAdapters?.history;\n const threadListAdapter = options.adapters?.threadList;\n\n if (!coreRef.current) {\n coreRef.current = new AgUiThreadRuntimeCore({\n agent: options.agent,\n logger,\n showThinking: options.showThinking ?? true,\n ...(options.onError && { onError: options.onError }),\n ...(options.onCancel && { onCancel: options.onCancel }),\n ...(historyAdapter && { history: historyAdapter }),\n notifyUpdate,\n });\n }\n\n const core = coreRef.current;\n core.updateOptions({\n agent: options.agent,\n logger,\n showThinking: options.showThinking ?? true,\n ...(options.onError && { onError: options.onError }),\n ...(options.onCancel && { onCancel: options.onCancel }),\n ...(historyAdapter && { history: historyAdapter }),\n });\n\n const [toolStatuses, setToolStatuses] = useState<\n Record<string, ToolExecutionStatus>\n >({});\n\n const hasExecutingTools = Object.values(toolStatuses).some(\n (s) => s?.type === \"executing\",\n );\n\n const threadList = useMemo(() => {\n if (!threadListAdapter) return undefined;\n\n const { onSwitchToNewThread, onSwitchToThread } = threadListAdapter;\n\n return {\n threadId: threadListAdapter.threadId,\n onSwitchToNewThread: onSwitchToNewThread\n ? async () => {\n await onSwitchToNewThread();\n core.applyExternalMessages([]);\n }\n : undefined,\n onSwitchToThread: onSwitchToThread\n ? async (threadId: string) => {\n const result = await onSwitchToThread(threadId);\n core.applyExternalMessages(result.messages);\n if (result.state) {\n core.loadExternalState(result.state);\n }\n }\n : undefined,\n };\n }, [threadListAdapter, core]);\n\n const adapters = options.adapters;\n const adapterAdapters = useMemo(\n () => ({\n attachments: adapters?.attachments ?? runtimeAdapters?.attachments,\n speech: adapters?.speech,\n dictation: adapters?.dictation,\n voice: adapters?.voice,\n feedback: adapters?.feedback,\n threadList,\n }),\n [adapters, runtimeAdapters, threadList],\n );\n\n const shared = useExternalStoreSharedOptions(options);\n const store = useMemo(\n () => {\n void _version; // rerender on version change\n\n return {\n ...shared,\n isLoading: core.isLoading,\n messages: core.getMessages(),\n state: core.getState(),\n isRunning: core.isRunning() || hasExecutingTools,\n unstable_enableToolInvocations: true,\n setToolStatuses,\n onNew: (message: AppendMessage) => core.append(message),\n onEdit: (message: AppendMessage) => core.edit(message),\n onReload: (parentId: string | null, config: { runConfig?: any }) =>\n core.reload(parentId, config),\n onCancel: async () => {\n core.cancel();\n },\n onAddToolResult: (options) => core.addToolResult(options),\n onResume: (config) => core.resume(config),\n setMessages: (messages: readonly ThreadMessage[]) =>\n core.applyExternalMessages(messages),\n onImport: (messages: readonly ThreadMessage[]) =>\n core.applyExternalMessages(messages),\n onLoadExternalState: (state: ReadonlyJSONValue) =>\n core.loadExternalState(state),\n adapters: adapterAdapters,\n } satisfies ExternalStoreAdapter<ThreadMessage>;\n },\n // _version is intentionally included to trigger re-computation when core state changes via notifyUpdate\n // toolInvocations intentionally excluded: abort/resume use refs internally and work with stale captures\n [adapterAdapters, core, _version, hasExecutingTools, shared],\n );\n\n const baseRuntime = useExternalStoreRuntime(store);\n\n const runtime = useMemo<AgUiAssistantRuntime>(() => {\n const wrapper = Object.create(baseRuntime) as AgUiAssistantRuntime;\n wrapper.unstable_getPendingInterrupts = () =>\n core.getPendingInterrupts()?.interrupts ?? [];\n wrapper.unstable_submitInterruptResponses = (responses) =>\n core.submitInterruptResponses(responses);\n return wrapper;\n }, [baseRuntime, core]);\n\n useEffect(() => {\n core.attachRuntime(runtime);\n return () => {\n core.detachRuntime();\n };\n }, [core, runtime]);\n\n useEffect(() => {\n core.__internal_load();\n }, [core]);\n\n return runtime;\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@assistant-ui/react-ag-ui",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.35",
|
|
4
4
|
"description": "AG-UI protocol adapter for assistant-ui",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ag-ui",
|
|
@@ -29,9 +29,10 @@
|
|
|
29
29
|
],
|
|
30
30
|
"sideEffects": false,
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@ag-ui/client": "^0.0.
|
|
33
|
-
"@assistant-ui/core": "^0.2.
|
|
34
|
-
"assistant-stream": "^0.3.
|
|
32
|
+
"@ag-ui/client": "^0.0.54",
|
|
33
|
+
"@assistant-ui/core": "^0.2.10",
|
|
34
|
+
"assistant-stream": "^0.3.20",
|
|
35
|
+
"fast-json-patch": "^3.1.1"
|
|
35
36
|
},
|
|
36
37
|
"peerDependencies": {
|
|
37
38
|
"@types/react": "*",
|
|
@@ -47,7 +48,7 @@
|
|
|
47
48
|
"react": "^19.2.6",
|
|
48
49
|
"vitest": "^4.1.7",
|
|
49
50
|
"zod": "^4.4.3",
|
|
50
|
-
"@assistant-ui/x-buildutils": "0.0.
|
|
51
|
+
"@assistant-ui/x-buildutils": "0.0.11"
|
|
51
52
|
},
|
|
52
53
|
"publishConfig": {
|
|
53
54
|
"access": "public",
|
|
@@ -1,22 +1,20 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
generateId,
|
|
5
|
-
generateOptimisticId,
|
|
6
|
-
isOptimisticId,
|
|
7
|
-
fromThreadMessageLike,
|
|
8
|
-
} from "@assistant-ui/core/internal";
|
|
3
|
+
import { generateId, fromThreadMessageLike } from "@assistant-ui/core/internal";
|
|
9
4
|
import type {
|
|
10
5
|
AddToolResultOptions,
|
|
11
6
|
AppendMessage,
|
|
12
7
|
AssistantRuntime,
|
|
8
|
+
ChatModelRunOptions,
|
|
13
9
|
ChatModelRunResult,
|
|
14
10
|
MessageStatus,
|
|
15
11
|
ThreadAssistantMessage,
|
|
16
12
|
ThreadHistoryAdapter,
|
|
17
13
|
ThreadMessage,
|
|
14
|
+
ToolCallMessagePart,
|
|
18
15
|
} from "@assistant-ui/core";
|
|
19
16
|
import type { HttpAgent } from "@ag-ui/client";
|
|
17
|
+
import jsonpatch, { type Operation } from "fast-json-patch";
|
|
20
18
|
import type { Logger } from "./logger";
|
|
21
19
|
import type { AgUiEvent, AgUiInterrupt, AgUiResumeEntry } from "./types";
|
|
22
20
|
import type { ReadonlyJSONValue } from "assistant-stream/utils";
|
|
@@ -32,14 +30,21 @@ import {
|
|
|
32
30
|
} from "./adapter/conversions";
|
|
33
31
|
import { createAgUiSubscriber } from "./adapter/subscriber";
|
|
34
32
|
|
|
33
|
+
const optimisticPrefix = "__optimistic__";
|
|
34
|
+
const generateOptimisticId = () => `${optimisticPrefix}${generateId()}`;
|
|
35
|
+
const isOptimisticId = (id: string) => id.startsWith(optimisticPrefix);
|
|
36
|
+
|
|
35
37
|
const symbolResumeShim = Symbol("agui-resume-shim");
|
|
36
38
|
|
|
37
39
|
type RunConfig = NonNullable<AppendMessage["runConfig"]>;
|
|
40
|
+
type ResumeStream = (
|
|
41
|
+
options: ChatModelRunOptions,
|
|
42
|
+
) => AsyncGenerator<ChatModelRunResult, void, unknown>;
|
|
38
43
|
type ResumeRunConfig = {
|
|
39
44
|
parentId: string | null;
|
|
40
45
|
sourceId: string | null;
|
|
41
46
|
runConfig: RunConfig;
|
|
42
|
-
stream?:
|
|
47
|
+
stream?: ResumeStream;
|
|
43
48
|
};
|
|
44
49
|
|
|
45
50
|
type CoreOptions = {
|
|
@@ -74,6 +79,7 @@ export class AgUiThreadRuntimeCore {
|
|
|
74
79
|
private readonly recordedHistoryIds = new Set<string>();
|
|
75
80
|
private _isLoading = false;
|
|
76
81
|
private _loadPromise: Promise<void> | undefined;
|
|
82
|
+
private pendingResumeMessageId: string | null = null;
|
|
77
83
|
|
|
78
84
|
constructor(options: CoreOptions) {
|
|
79
85
|
this.agent = options.agent;
|
|
@@ -136,7 +142,13 @@ export class AgUiThreadRuntimeCore {
|
|
|
136
142
|
|
|
137
143
|
if (repo.unstable_resume) {
|
|
138
144
|
const parentId = repo.headId ?? messages.at(-1)?.id ?? null;
|
|
139
|
-
|
|
145
|
+
const resumeStream = this.history?.resume?.bind(this.history);
|
|
146
|
+
await this.startRun(
|
|
147
|
+
parentId,
|
|
148
|
+
this.lastRunConfig,
|
|
149
|
+
undefined,
|
|
150
|
+
resumeStream,
|
|
151
|
+
);
|
|
140
152
|
}
|
|
141
153
|
})
|
|
142
154
|
.catch((error) => {
|
|
@@ -194,14 +206,11 @@ export class AgUiThreadRuntimeCore {
|
|
|
194
206
|
|
|
195
207
|
async resume(config: ResumeRunConfig): Promise<void> {
|
|
196
208
|
this.assertNoPendingInterrupts();
|
|
197
|
-
if (config.stream) {
|
|
198
|
-
this.logger.debug?.(
|
|
199
|
-
"[agui] resume stream is not supported, falling back to regular run",
|
|
200
|
-
);
|
|
201
|
-
}
|
|
202
209
|
await this.startRun(
|
|
203
210
|
config.parentId,
|
|
204
211
|
config.runConfig ?? this.lastRunConfig,
|
|
212
|
+
undefined,
|
|
213
|
+
config.stream,
|
|
205
214
|
);
|
|
206
215
|
}
|
|
207
216
|
|
|
@@ -361,7 +370,6 @@ export class AgUiThreadRuntimeCore {
|
|
|
361
370
|
|
|
362
371
|
addToolResult(options: AddToolResultOptions): void {
|
|
363
372
|
let updated = false;
|
|
364
|
-
let shouldResume = false;
|
|
365
373
|
this.messages = this.messages.map((message) => {
|
|
366
374
|
if (message.id !== options.messageId || message.role !== "assistant")
|
|
367
375
|
return message;
|
|
@@ -380,47 +388,60 @@ export class AgUiThreadRuntimeCore {
|
|
|
380
388
|
});
|
|
381
389
|
if (!matchedToolCall) return message;
|
|
382
390
|
updated = true;
|
|
383
|
-
|
|
384
|
-
if (
|
|
385
|
-
assistant.status?.type === "requires-action" &&
|
|
386
|
-
assistant.status.reason === "tool-calls" &&
|
|
387
|
-
content.every(
|
|
388
|
-
(part) =>
|
|
389
|
-
part.type !== "tool-call" ||
|
|
390
|
-
("result" in part && part.result !== undefined),
|
|
391
|
-
)
|
|
392
|
-
) {
|
|
393
|
-
shouldResume = true;
|
|
394
|
-
return {
|
|
395
|
-
...assistant,
|
|
396
|
-
content,
|
|
397
|
-
status: { type: "complete" as const, reason: "unknown" as const },
|
|
398
|
-
};
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
return {
|
|
402
|
-
...assistant,
|
|
403
|
-
content,
|
|
404
|
-
};
|
|
391
|
+
return { ...assistant, content };
|
|
405
392
|
});
|
|
406
393
|
|
|
407
|
-
if (updated)
|
|
408
|
-
|
|
394
|
+
if (!updated) return;
|
|
395
|
+
this.notifyUpdate();
|
|
396
|
+
this.maybeResumeAfterToolResults(options.messageId);
|
|
397
|
+
}
|
|
409
398
|
|
|
410
|
-
|
|
411
|
-
|
|
399
|
+
// Re-evaluate whether every tool call on the assistant message now has a
|
|
400
|
+
// result and, if so, drive the follow-up run. Centralizing this lets the
|
|
401
|
+
// continuation fire whether the frontend result lands before RUN_FINISHED
|
|
402
|
+
// (status flips to requires-action only later, while a run is still draining)
|
|
403
|
+
// or after it. Idempotent: once the message is marked complete it no-ops.
|
|
404
|
+
private maybeResumeAfterToolResults(messageId: string): void {
|
|
405
|
+
const message = this.messages.find((m) => m.id === messageId);
|
|
406
|
+
if (!message || message.role !== "assistant") return;
|
|
407
|
+
const assistant = message as ThreadAssistantMessage;
|
|
408
|
+
if (
|
|
409
|
+
assistant.status?.type !== "requires-action" ||
|
|
410
|
+
assistant.status.reason !== "tool-calls"
|
|
411
|
+
) {
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
const allResolved = assistant.content.every(
|
|
415
|
+
(part) =>
|
|
416
|
+
part.type !== "tool-call" ||
|
|
417
|
+
("result" in part && part.result !== undefined),
|
|
418
|
+
);
|
|
419
|
+
if (!allResolved) return;
|
|
420
|
+
|
|
421
|
+
this.messages = this.messages.map((m) =>
|
|
422
|
+
m.id === messageId && m.role === "assistant"
|
|
423
|
+
? {
|
|
424
|
+
...(m as ThreadAssistantMessage),
|
|
425
|
+
status: { type: "complete" as const, reason: "unknown" as const },
|
|
426
|
+
}
|
|
427
|
+
: m,
|
|
428
|
+
);
|
|
429
|
+
this.notifyUpdate();
|
|
430
|
+
this.persistAssistantHistory(messageId);
|
|
412
431
|
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
);
|
|
419
|
-
},
|
|
420
|
-
);
|
|
421
|
-
}
|
|
422
|
-
}
|
|
432
|
+
if (this.isRunningFlag) {
|
|
433
|
+
// A run is still draining (RUN_FINISHED arrived but the stream has not
|
|
434
|
+
// closed). Defer until startRun's tail so we never start two runs.
|
|
435
|
+
this.pendingResumeMessageId = messageId;
|
|
436
|
+
return;
|
|
423
437
|
}
|
|
438
|
+
this.startResumeRun(messageId);
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
private startResumeRun(messageId: string): void {
|
|
442
|
+
void this.startRun(messageId, this.lastRunConfig).catch((error) => {
|
|
443
|
+
this.onError?.(error instanceof Error ? error : new Error(String(error)));
|
|
444
|
+
});
|
|
424
445
|
}
|
|
425
446
|
|
|
426
447
|
applyExternalMessages(messages: readonly ThreadMessage[]): void {
|
|
@@ -442,20 +463,14 @@ export class AgUiThreadRuntimeCore {
|
|
|
442
463
|
parentId: string | null,
|
|
443
464
|
runConfig?: RunConfig,
|
|
444
465
|
resume?: AgUiResumeEntry[],
|
|
466
|
+
resumeStream?: ResumeStream,
|
|
445
467
|
): Promise<void> {
|
|
446
468
|
const normalizedRunConfig = runConfig ?? {};
|
|
447
469
|
this.lastRunConfig = normalizedRunConfig;
|
|
448
470
|
this.resetHead(parentId);
|
|
449
471
|
const historicalMessages = [...this.messages];
|
|
450
472
|
|
|
451
|
-
const runId = generateId();
|
|
452
473
|
this.pendingError = null;
|
|
453
|
-
const input = this.buildRunInput(
|
|
454
|
-
runId,
|
|
455
|
-
normalizedRunConfig,
|
|
456
|
-
historicalMessages,
|
|
457
|
-
resume,
|
|
458
|
-
);
|
|
459
474
|
const assistantParentId = parentId ?? this.messages.at(-1)?.id ?? null;
|
|
460
475
|
let assistantMessageId: string | undefined;
|
|
461
476
|
const ensureAssistant = () => {
|
|
@@ -466,15 +481,17 @@ export class AgUiThreadRuntimeCore {
|
|
|
466
481
|
return created;
|
|
467
482
|
};
|
|
468
483
|
|
|
484
|
+
const applyUpdate = (update: ChatModelRunResult) => {
|
|
485
|
+
const resolved = this.updateAssistantMessage(ensureAssistant(), update);
|
|
486
|
+
if (resolved !== assistantMessageId) {
|
|
487
|
+
assistantMessageId = resolved;
|
|
488
|
+
}
|
|
489
|
+
};
|
|
490
|
+
|
|
469
491
|
const aggregator = new RunAggregator({
|
|
470
492
|
showThinking: this.showThinking,
|
|
471
493
|
logger: this.logger,
|
|
472
|
-
emit:
|
|
473
|
-
const resolved = this.updateAssistantMessage(ensureAssistant(), update);
|
|
474
|
-
if (resolved !== assistantMessageId) {
|
|
475
|
-
assistantMessageId = resolved;
|
|
476
|
-
}
|
|
477
|
-
},
|
|
494
|
+
emit: applyUpdate,
|
|
478
495
|
onServerMessageId: (serverId) => {
|
|
479
496
|
const placeholder = ensureAssistant();
|
|
480
497
|
if (placeholder === serverId) return;
|
|
@@ -488,40 +505,63 @@ export class AgUiThreadRuntimeCore {
|
|
|
488
505
|
const abortSignal = abortController.signal;
|
|
489
506
|
this.abortController = abortController;
|
|
490
507
|
|
|
508
|
+
let cancelRun = () => dispatch({ type: "RUN_CANCELLED" });
|
|
491
509
|
abortSignal.addEventListener(
|
|
492
510
|
"abort",
|
|
493
511
|
() => {
|
|
494
|
-
|
|
512
|
+
cancelRun();
|
|
495
513
|
this.finishRun(abortController);
|
|
496
514
|
this.onCancel?.();
|
|
497
515
|
},
|
|
498
516
|
{ once: true },
|
|
499
517
|
);
|
|
500
518
|
|
|
501
|
-
const subscriber = createAgUiSubscriber({
|
|
502
|
-
dispatch,
|
|
503
|
-
runId,
|
|
504
|
-
logger: this.logger,
|
|
505
|
-
onRunFailed: (error) => {
|
|
506
|
-
this.pendingError = error;
|
|
507
|
-
this.onError?.(error);
|
|
508
|
-
},
|
|
509
|
-
});
|
|
510
|
-
|
|
511
|
-
aggregator.handle({ type: "RUN_STARTED", runId });
|
|
512
519
|
this.setRunning(true);
|
|
513
520
|
|
|
514
521
|
try {
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
522
|
+
if (resumeStream) {
|
|
523
|
+
// Cancel flips only the status; an aggregator RUN_CANCELLED would emit an empty snapshot and wipe the replayed content.
|
|
524
|
+
cancelRun = () =>
|
|
525
|
+
applyUpdate({ status: { type: "incomplete", reason: "cancelled" } });
|
|
526
|
+
await this.consumeResumeStream(resumeStream, {
|
|
527
|
+
runConfig: normalizedRunConfig,
|
|
528
|
+
threadId: this.agent.threadId || "main",
|
|
529
|
+
parentId: assistantParentId,
|
|
530
|
+
historicalMessages,
|
|
531
|
+
abortSignal,
|
|
532
|
+
ensureAssistant,
|
|
533
|
+
applyUpdate,
|
|
534
|
+
getAssistantMessageId: () => assistantMessageId,
|
|
535
|
+
});
|
|
536
|
+
} else {
|
|
537
|
+
const runId = generateId();
|
|
538
|
+
aggregator.handle({ type: "RUN_STARTED", runId });
|
|
539
|
+
const input = this.buildRunInput(
|
|
540
|
+
runId,
|
|
541
|
+
normalizedRunConfig,
|
|
542
|
+
historicalMessages,
|
|
543
|
+
resume,
|
|
544
|
+
);
|
|
545
|
+
const subscriber = createAgUiSubscriber({
|
|
546
|
+
dispatch,
|
|
547
|
+
runId,
|
|
548
|
+
logger: this.logger,
|
|
549
|
+
onRunFailed: (error) => {
|
|
550
|
+
this.pendingError = error;
|
|
551
|
+
this.onError?.(error);
|
|
552
|
+
},
|
|
553
|
+
});
|
|
554
|
+
try {
|
|
555
|
+
(this.agent as any).messages = input.messages;
|
|
556
|
+
(this.agent as any).threadId = input.threadId;
|
|
557
|
+
(this.agent as any).state = input.state ?? null;
|
|
558
|
+
} catch {
|
|
559
|
+
// ignore
|
|
560
|
+
}
|
|
561
|
+
await (this.agent as any).runAgent(input, subscriber, {
|
|
562
|
+
signal: abortSignal,
|
|
563
|
+
});
|
|
521
564
|
}
|
|
522
|
-
await (this.agent as any).runAgent(input, subscriber, {
|
|
523
|
-
signal: abortSignal,
|
|
524
|
-
});
|
|
525
565
|
} catch (error) {
|
|
526
566
|
if (!abortSignal.aborted) {
|
|
527
567
|
const err = error instanceof Error ? error : new Error(String(error));
|
|
@@ -536,8 +576,77 @@ export class AgUiThreadRuntimeCore {
|
|
|
536
576
|
if (this.pendingError) {
|
|
537
577
|
const err = this.pendingError;
|
|
538
578
|
this.pendingError = null;
|
|
579
|
+
this.pendingResumeMessageId = null;
|
|
539
580
|
throw err;
|
|
540
581
|
}
|
|
582
|
+
|
|
583
|
+
// A tool result that landed before the run settled deferred its
|
|
584
|
+
// continuation here so a second run never overlaps the first.
|
|
585
|
+
if (this.pendingResumeMessageId !== null) {
|
|
586
|
+
const resumeMessageId = this.pendingResumeMessageId;
|
|
587
|
+
this.pendingResumeMessageId = null;
|
|
588
|
+
if (!abortSignal.aborted) {
|
|
589
|
+
this.startResumeRun(resumeMessageId);
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
// Replays a persisted run's snapshots into the existing assistant message, bypassing agent.runAgent so it is not re-invoked.
|
|
595
|
+
private async consumeResumeStream(
|
|
596
|
+
stream: ResumeStream,
|
|
597
|
+
ctx: {
|
|
598
|
+
runConfig: RunConfig;
|
|
599
|
+
threadId: string;
|
|
600
|
+
parentId: string | null;
|
|
601
|
+
historicalMessages: readonly ThreadMessage[];
|
|
602
|
+
abortSignal: AbortSignal;
|
|
603
|
+
ensureAssistant: () => string;
|
|
604
|
+
applyUpdate: (update: ChatModelRunResult) => void;
|
|
605
|
+
getAssistantMessageId: () => string | undefined;
|
|
606
|
+
},
|
|
607
|
+
): Promise<void> {
|
|
608
|
+
const assistantId = ctx.ensureAssistant();
|
|
609
|
+
const currentId = () => ctx.getAssistantMessageId() ?? assistantId;
|
|
610
|
+
const options: ChatModelRunOptions = {
|
|
611
|
+
messages: ctx.historicalMessages,
|
|
612
|
+
runConfig: ctx.runConfig,
|
|
613
|
+
abortSignal: ctx.abortSignal,
|
|
614
|
+
context: this.runtime?.thread.getModelContext() ?? {},
|
|
615
|
+
unstable_assistantMessageId: assistantId,
|
|
616
|
+
unstable_threadId: ctx.threadId,
|
|
617
|
+
unstable_parentId: ctx.parentId,
|
|
618
|
+
unstable_getMessage: () => {
|
|
619
|
+
const message = this.messages.find((m) => m.id === currentId());
|
|
620
|
+
if (!message) {
|
|
621
|
+
throw new Error(
|
|
622
|
+
"[agui] resume stream requested the assistant message before it existed",
|
|
623
|
+
);
|
|
624
|
+
}
|
|
625
|
+
return message;
|
|
626
|
+
},
|
|
627
|
+
};
|
|
628
|
+
|
|
629
|
+
try {
|
|
630
|
+
for await (const result of stream(options)) {
|
|
631
|
+
if (ctx.abortSignal.aborted) return;
|
|
632
|
+
ctx.applyUpdate(result);
|
|
633
|
+
}
|
|
634
|
+
} catch (error) {
|
|
635
|
+
if (ctx.abortSignal.aborted) return;
|
|
636
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
637
|
+
ctx.applyUpdate({
|
|
638
|
+
status: { type: "incomplete", reason: "error", error: err.message },
|
|
639
|
+
});
|
|
640
|
+
this.onError?.(err);
|
|
641
|
+
this.pendingError = this.pendingError ?? err;
|
|
642
|
+
return;
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
if (ctx.abortSignal.aborted) return;
|
|
646
|
+
const current = this.messages.find((m) => m.id === currentId());
|
|
647
|
+
if (!current || current.status?.type === "running") {
|
|
648
|
+
ctx.applyUpdate({ status: { type: "complete", reason: "unknown" } });
|
|
649
|
+
}
|
|
541
650
|
}
|
|
542
651
|
|
|
543
652
|
private buildRunInput(
|
|
@@ -669,10 +778,16 @@ export class AgUiThreadRuntimeCore {
|
|
|
669
778
|
? this.mergeAssistantMetadata(assistant.metadata, update.metadata)
|
|
670
779
|
: assistant.metadata;
|
|
671
780
|
latestStatus = update.status ?? assistant.status;
|
|
781
|
+
const content =
|
|
782
|
+
update.content !== undefined
|
|
783
|
+
? this.preserveToolResults(
|
|
784
|
+
assistant.content,
|
|
785
|
+
update.content as ThreadAssistantMessage["content"],
|
|
786
|
+
)
|
|
787
|
+
: assistant.content;
|
|
672
788
|
return {
|
|
673
789
|
...assistant,
|
|
674
|
-
content
|
|
675
|
-
assistant.content) as ThreadAssistantMessage["content"],
|
|
790
|
+
content,
|
|
676
791
|
status: latestStatus,
|
|
677
792
|
metadata,
|
|
678
793
|
};
|
|
@@ -692,9 +807,47 @@ export class AgUiThreadRuntimeCore {
|
|
|
692
807
|
if (this.isPersistableStatus(latestStatus)) {
|
|
693
808
|
this.persistAssistantHistory(resolvedMessageId);
|
|
694
809
|
}
|
|
810
|
+
this.maybeResumeAfterToolResults(resolvedMessageId);
|
|
695
811
|
return resolvedMessageId;
|
|
696
812
|
}
|
|
697
813
|
|
|
814
|
+
// The RunAggregator rebuilds the assistant content from stream events only,
|
|
815
|
+
// so a fresh snapshot omits results injected via addToolResult (frontend tool
|
|
816
|
+
// execution). Carry those results forward so the aggregator never clobbers
|
|
817
|
+
// them. Results are only ever added in this flow, so preserving is safe.
|
|
818
|
+
private preserveToolResults(
|
|
819
|
+
previous: ThreadAssistantMessage["content"],
|
|
820
|
+
next: ThreadAssistantMessage["content"],
|
|
821
|
+
): ThreadAssistantMessage["content"] {
|
|
822
|
+
const resolved = new Map<string, ToolCallMessagePart>();
|
|
823
|
+
for (const part of previous) {
|
|
824
|
+
if (
|
|
825
|
+
part.type === "tool-call" &&
|
|
826
|
+
"result" in part &&
|
|
827
|
+
part.result !== undefined
|
|
828
|
+
) {
|
|
829
|
+
resolved.set(part.toolCallId, part);
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
if (resolved.size === 0) return next;
|
|
833
|
+
|
|
834
|
+
let changed = false;
|
|
835
|
+
const merged = next.map((part) => {
|
|
836
|
+
if (part.type !== "tool-call") return part;
|
|
837
|
+
if ("result" in part && part.result !== undefined) return part;
|
|
838
|
+
const prior = resolved.get(part.toolCallId);
|
|
839
|
+
if (!prior) return part;
|
|
840
|
+
changed = true;
|
|
841
|
+
return {
|
|
842
|
+
...part,
|
|
843
|
+
result: prior.result,
|
|
844
|
+
...(prior.artifact !== undefined ? { artifact: prior.artifact } : {}),
|
|
845
|
+
...(prior.isError !== undefined ? { isError: prior.isError } : {}),
|
|
846
|
+
};
|
|
847
|
+
});
|
|
848
|
+
return changed ? (merged as ThreadAssistantMessage["content"]) : next;
|
|
849
|
+
}
|
|
850
|
+
|
|
698
851
|
private mergeAssistantMetadata(
|
|
699
852
|
current: ThreadAssistantMessage["metadata"],
|
|
700
853
|
incoming: NonNullable<ChatModelRunResult["metadata"]>,
|
|
@@ -731,7 +884,20 @@ export class AgUiThreadRuntimeCore {
|
|
|
731
884
|
return;
|
|
732
885
|
}
|
|
733
886
|
case "STATE_DELTA": {
|
|
734
|
-
|
|
887
|
+
if (event.delta.length === 0) return;
|
|
888
|
+
try {
|
|
889
|
+
const state = this.stateSnapshot ?? {};
|
|
890
|
+
const result = jsonpatch.applyPatch(
|
|
891
|
+
state,
|
|
892
|
+
event.delta as Operation[],
|
|
893
|
+
/* validateOperation */ true,
|
|
894
|
+
/* mutateDocument */ false,
|
|
895
|
+
);
|
|
896
|
+
this.stateSnapshot = result.newDocument as ReadonlyJSONValue;
|
|
897
|
+
this.notifyUpdate();
|
|
898
|
+
} catch (error) {
|
|
899
|
+
this.logger.error?.("[agui] failed to apply state delta", error);
|
|
900
|
+
}
|
|
735
901
|
return;
|
|
736
902
|
}
|
|
737
903
|
case "MESSAGES_SNAPSHOT": {
|
package/src/runtime/types.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
AttachmentAdapter,
|
|
3
3
|
DictationAdapter,
|
|
4
|
+
ExternalStoreSharedOptions,
|
|
4
5
|
FeedbackAdapter,
|
|
6
|
+
RealtimeVoiceAdapter,
|
|
5
7
|
SpeechSynthesisAdapter,
|
|
6
8
|
ThreadHistoryAdapter,
|
|
7
9
|
ThreadMessage,
|
|
@@ -30,6 +32,7 @@ export type UseAgUiRuntimeAdapters = {
|
|
|
30
32
|
attachments?: AttachmentAdapter;
|
|
31
33
|
speech?: SpeechSynthesisAdapter;
|
|
32
34
|
dictation?: DictationAdapter;
|
|
35
|
+
voice?: RealtimeVoiceAdapter;
|
|
33
36
|
feedback?: FeedbackAdapter;
|
|
34
37
|
history?: ThreadHistoryAdapter;
|
|
35
38
|
/**
|
|
@@ -38,7 +41,7 @@ export type UseAgUiRuntimeAdapters = {
|
|
|
38
41
|
threadList?: UseAgUiThreadListAdapter;
|
|
39
42
|
};
|
|
40
43
|
|
|
41
|
-
export type UseAgUiRuntimeOptions = {
|
|
44
|
+
export type UseAgUiRuntimeOptions = ExternalStoreSharedOptions & {
|
|
42
45
|
agent: HttpAgent;
|
|
43
46
|
logger?: Partial<Logger>;
|
|
44
47
|
showThinking?: boolean;
|
package/src/useAgUiRuntime.ts
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
4
4
|
import {
|
|
5
5
|
useExternalStoreRuntime,
|
|
6
|
+
useExternalStoreSharedOptions,
|
|
6
7
|
useRuntimeAdapters,
|
|
7
8
|
} from "@assistant-ui/core/react";
|
|
8
9
|
import type { ToolExecutionStatus } from "@assistant-ui/core";
|
|
@@ -101,17 +102,20 @@ export function useAgUiRuntime(
|
|
|
101
102
|
attachments: adapters?.attachments ?? runtimeAdapters?.attachments,
|
|
102
103
|
speech: adapters?.speech,
|
|
103
104
|
dictation: adapters?.dictation,
|
|
105
|
+
voice: adapters?.voice,
|
|
104
106
|
feedback: adapters?.feedback,
|
|
105
107
|
threadList,
|
|
106
108
|
}),
|
|
107
109
|
[adapters, runtimeAdapters, threadList],
|
|
108
110
|
);
|
|
109
111
|
|
|
112
|
+
const shared = useExternalStoreSharedOptions(options);
|
|
110
113
|
const store = useMemo(
|
|
111
114
|
() => {
|
|
112
115
|
void _version; // rerender on version change
|
|
113
116
|
|
|
114
117
|
return {
|
|
118
|
+
...shared,
|
|
115
119
|
isLoading: core.isLoading,
|
|
116
120
|
messages: core.getMessages(),
|
|
117
121
|
state: core.getState(),
|
|
@@ -138,7 +142,7 @@ export function useAgUiRuntime(
|
|
|
138
142
|
},
|
|
139
143
|
// _version is intentionally included to trigger re-computation when core state changes via notifyUpdate
|
|
140
144
|
// toolInvocations intentionally excluded: abort/resume use refs internally and work with stale captures
|
|
141
|
-
[adapterAdapters, core, _version, hasExecutingTools],
|
|
145
|
+
[adapterAdapters, core, _version, hasExecutingTools, shared],
|
|
142
146
|
);
|
|
143
147
|
|
|
144
148
|
const baseRuntime = useExternalStoreRuntime(store);
|