@assistant-ui/react-ag-ui 0.0.43 → 0.0.45
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agUiExtras.d.ts +0 -1
- package/dist/agUiExtras.d.ts.map +1 -1
- package/dist/hooks.d.ts +15 -9
- package/dist/hooks.d.ts.map +1 -1
- package/dist/hooks.js +17 -8
- package/dist/hooks.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/runtime/AgUiThreadRuntimeCore.d.ts +26 -4
- package/dist/runtime/AgUiThreadRuntimeCore.d.ts.map +1 -1
- package/dist/runtime/AgUiThreadRuntimeCore.js +296 -89
- package/dist/runtime/AgUiThreadRuntimeCore.js.map +1 -1
- package/dist/runtime/adapter/conversions.d.ts +0 -1
- package/dist/runtime/adapter/conversions.d.ts.map +1 -1
- package/dist/runtime/adapter/conversions.js +24 -5
- package/dist/runtime/adapter/conversions.js.map +1 -1
- package/dist/runtime/adapter/run-aggregator.d.ts +0 -1
- package/dist/runtime/adapter/run-aggregator.d.ts.map +1 -1
- package/dist/runtime/adapter/run-aggregator.js +36 -7
- package/dist/runtime/adapter/run-aggregator.js.map +1 -1
- package/dist/runtime/adapter/subscriber.d.ts +0 -1
- package/dist/runtime/adapter/subscriber.d.ts.map +1 -1
- package/dist/runtime/event-parser.d.ts +0 -1
- package/dist/runtime/event-parser.d.ts.map +1 -1
- package/dist/runtime/event-parser.js +18 -9
- package/dist/runtime/event-parser.js.map +1 -1
- package/dist/runtime/logger.d.ts.map +1 -1
- package/dist/runtime/types.d.ts +13 -1
- package/dist/runtime/types.d.ts.map +1 -1
- package/dist/useAgUiRuntime.d.ts +0 -1
- package/dist/useAgUiRuntime.d.ts.map +1 -1
- package/dist/useAgUiRuntime.js +8 -2
- package/dist/useAgUiRuntime.js.map +1 -1
- package/package.json +6 -6
- package/src/hooks.ts +29 -7
- package/src/index.ts +2 -0
- package/src/runtime/AgUiThreadRuntimeCore.ts +390 -108
- package/src/runtime/adapter/conversions.ts +71 -15
- package/src/runtime/adapter/run-aggregator.ts +69 -5
- package/src/runtime/types.ts +17 -0
- package/src/useAgUiRuntime.ts +8 -1
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import type { InputContent } from "@ag-ui/client";
|
|
4
|
-
import type {
|
|
4
|
+
import type {
|
|
5
|
+
ThreadMessageLike as CoreThreadMessageLike,
|
|
6
|
+
ToolModelContentPart,
|
|
7
|
+
} from "@assistant-ui/core";
|
|
5
8
|
import { getAutoStatus } from "@assistant-ui/core/internal";
|
|
6
9
|
import { type Tool, toToolsJSONSchema } from "assistant-stream";
|
|
7
10
|
import type { ReadonlyJSONObject } from "assistant-stream/utils";
|
|
11
|
+
import {
|
|
12
|
+
AG_UI_METADATA_NAMESPACE,
|
|
13
|
+
type AgUiCustomMetadata,
|
|
14
|
+
} from "./run-aggregator";
|
|
15
|
+
import type { AgUiInterrupt } from "../types";
|
|
8
16
|
|
|
9
17
|
export type { InputContent };
|
|
10
18
|
|
|
@@ -54,6 +62,7 @@ type ToolCallPart = {
|
|
|
54
62
|
args?: ReadonlyJSONObject;
|
|
55
63
|
result?: unknown;
|
|
56
64
|
isError?: boolean;
|
|
65
|
+
modelContent?: readonly ToolModelContentPart[];
|
|
57
66
|
unstable_toolMessageId?: string;
|
|
58
67
|
};
|
|
59
68
|
|
|
@@ -440,6 +449,28 @@ function extractAssistantToolCalls(
|
|
|
440
449
|
return parts;
|
|
441
450
|
}
|
|
442
451
|
|
|
452
|
+
function validateInterrupts(value: unknown): AgUiInterrupt[] | undefined {
|
|
453
|
+
if (!Array.isArray(value)) return undefined;
|
|
454
|
+
const valid = value.filter(
|
|
455
|
+
(entry): entry is AgUiInterrupt =>
|
|
456
|
+
isObject(entry) &&
|
|
457
|
+
typeof entry.id === "string" &&
|
|
458
|
+
typeof entry.reason === "string",
|
|
459
|
+
);
|
|
460
|
+
return valid.length > 0 ? valid : undefined;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
function readPersistedInterrupts(
|
|
464
|
+
metadata: unknown,
|
|
465
|
+
): AgUiInterrupt[] | undefined {
|
|
466
|
+
if (!isObject(metadata)) return undefined;
|
|
467
|
+
const custom = metadata.custom;
|
|
468
|
+
if (!isObject(custom)) return undefined;
|
|
469
|
+
const namespaced = custom[AG_UI_METADATA_NAMESPACE];
|
|
470
|
+
if (!isObject(namespaced)) return undefined;
|
|
471
|
+
return validateInterrupts(namespaced.interrupts);
|
|
472
|
+
}
|
|
473
|
+
|
|
443
474
|
function toAssistantSnapshotMessage(
|
|
444
475
|
rawMessage: Record<string, unknown>,
|
|
445
476
|
): CoreThreadMessageLike {
|
|
@@ -450,11 +481,23 @@ function toAssistantSnapshotMessage(
|
|
|
450
481
|
...toolCallParts,
|
|
451
482
|
];
|
|
452
483
|
const messageName = getString(rawMessage, "name");
|
|
484
|
+
const interrupts = readPersistedInterrupts(rawMessage.metadata);
|
|
453
485
|
return {
|
|
454
486
|
id: getString(rawMessage, "id") ?? generateId(),
|
|
455
487
|
role: "assistant",
|
|
456
488
|
content: assistantContent.length > 0 ? assistantContent : "",
|
|
457
489
|
...(messageName !== undefined ? { name: messageName } : {}),
|
|
490
|
+
...(interrupts
|
|
491
|
+
? {
|
|
492
|
+
metadata: {
|
|
493
|
+
custom: {
|
|
494
|
+
[AG_UI_METADATA_NAMESPACE]: {
|
|
495
|
+
interrupts,
|
|
496
|
+
} satisfies AgUiCustomMetadata,
|
|
497
|
+
},
|
|
498
|
+
},
|
|
499
|
+
}
|
|
500
|
+
: {}),
|
|
458
501
|
};
|
|
459
502
|
}
|
|
460
503
|
|
|
@@ -608,19 +651,29 @@ export function fromAgUiMessages(
|
|
|
608
651
|
|
|
609
652
|
for (let i = 0; i < converted.length; i++) {
|
|
610
653
|
const message = converted[i]!;
|
|
611
|
-
if (message.role !== "assistant"
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
part
|
|
619
|
-
|
|
620
|
-
|
|
654
|
+
if (message.role !== "assistant") continue;
|
|
655
|
+
|
|
656
|
+
const hasInterrupt =
|
|
657
|
+
readPersistedInterrupts(message.metadata) !== undefined;
|
|
658
|
+
const hasPendingToolCall =
|
|
659
|
+
Array.isArray(message.content) &&
|
|
660
|
+
message.content.some(
|
|
661
|
+
(part) =>
|
|
662
|
+
isObject(part) &&
|
|
663
|
+
part.type === "tool-call" &&
|
|
664
|
+
part.result === undefined,
|
|
665
|
+
);
|
|
666
|
+
|
|
667
|
+
if (hasInterrupt || hasPendingToolCall) {
|
|
621
668
|
converted[i] = {
|
|
622
669
|
...message,
|
|
623
|
-
status: getAutoStatus(
|
|
670
|
+
status: getAutoStatus(
|
|
671
|
+
false,
|
|
672
|
+
false,
|
|
673
|
+
hasInterrupt,
|
|
674
|
+
hasPendingToolCall,
|
|
675
|
+
undefined,
|
|
676
|
+
),
|
|
624
677
|
};
|
|
625
678
|
}
|
|
626
679
|
}
|
|
@@ -666,10 +719,13 @@ function convertAssistantMessage(
|
|
|
666
719
|
for (const { id: toolCallId, part } of toolCalls) {
|
|
667
720
|
if (part.result === undefined) continue;
|
|
668
721
|
|
|
722
|
+
const modelText = extractText(part.modelContent);
|
|
669
723
|
const resultContent =
|
|
670
|
-
|
|
671
|
-
?
|
|
672
|
-
:
|
|
724
|
+
modelText.length > 0
|
|
725
|
+
? modelText
|
|
726
|
+
: typeof part.result === "string"
|
|
727
|
+
? part.result
|
|
728
|
+
: JSON.stringify(part.result);
|
|
673
729
|
|
|
674
730
|
const toolMessage: AgUiMessage = {
|
|
675
731
|
id: part.unstable_toolMessageId ?? `${toolCallId}:tool`,
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
type MessageTiming,
|
|
7
7
|
type ThreadAssistantMessagePart,
|
|
8
8
|
type ToolCallMessagePart,
|
|
9
|
+
type ToolModelContentPart,
|
|
9
10
|
} from "@assistant-ui/core";
|
|
10
11
|
import type { AgUiEvent, AgUiInterrupt } from "../types";
|
|
11
12
|
import type { Logger } from "../logger";
|
|
@@ -28,10 +29,16 @@ type ToolCallState = {
|
|
|
28
29
|
parentMessageId?: string;
|
|
29
30
|
toolMessageId?: string;
|
|
30
31
|
mcpAppResourceUri?: string;
|
|
32
|
+
mcpAppServerId?: string;
|
|
33
|
+
modelContent?: ToolModelContentPart[];
|
|
34
|
+
snapshotResultApplied: boolean;
|
|
31
35
|
};
|
|
32
36
|
|
|
33
37
|
const MCP_APPS_ACTIVITY_TYPE = "mcp-apps";
|
|
34
38
|
|
|
39
|
+
const isPlainObject = (value: unknown): value is Record<string, unknown> =>
|
|
40
|
+
!!value && typeof value === "object" && !Array.isArray(value);
|
|
41
|
+
|
|
35
42
|
export type RunAggregatorOptions = {
|
|
36
43
|
showThinking: boolean;
|
|
37
44
|
logger: Logger;
|
|
@@ -236,8 +243,13 @@ export class RunAggregator {
|
|
|
236
243
|
}
|
|
237
244
|
case "ACTIVITY_SNAPSHOT": {
|
|
238
245
|
if (event.activityType !== MCP_APPS_ACTIVITY_TYPE) break;
|
|
239
|
-
const
|
|
240
|
-
const entry =
|
|
246
|
+
const toolCallId = event.content.toolCallId;
|
|
247
|
+
const entry =
|
|
248
|
+
typeof toolCallId === "string"
|
|
249
|
+
? this.toolCalls.get(toolCallId)
|
|
250
|
+
: this.lastResolvedToolCallId
|
|
251
|
+
? this.toolCalls.get(this.lastResolvedToolCallId)
|
|
252
|
+
: undefined;
|
|
241
253
|
const resourceUri = event.content.resourceUri;
|
|
242
254
|
if (
|
|
243
255
|
entry &&
|
|
@@ -245,6 +257,35 @@ export class RunAggregator {
|
|
|
245
257
|
isMcpAppUri(resourceUri)
|
|
246
258
|
) {
|
|
247
259
|
entry.mcpAppResourceUri = resourceUri;
|
|
260
|
+
const id = event.content.serverId;
|
|
261
|
+
const hash = event.content.serverHash;
|
|
262
|
+
if (typeof id === "string" && id.length > 0) {
|
|
263
|
+
entry.mcpAppServerId = id;
|
|
264
|
+
} else if (typeof hash === "string" && hash.length > 0) {
|
|
265
|
+
entry.mcpAppServerId = hash;
|
|
266
|
+
}
|
|
267
|
+
const result = event.content.result;
|
|
268
|
+
if (isPlainObject(result)) {
|
|
269
|
+
if (
|
|
270
|
+
entry.result !== undefined &&
|
|
271
|
+
entry.modelContent === undefined
|
|
272
|
+
) {
|
|
273
|
+
entry.modelContent = [
|
|
274
|
+
{
|
|
275
|
+
type: "text",
|
|
276
|
+
text:
|
|
277
|
+
typeof entry.result === "string"
|
|
278
|
+
? entry.result
|
|
279
|
+
: JSON.stringify(entry.result),
|
|
280
|
+
},
|
|
281
|
+
];
|
|
282
|
+
}
|
|
283
|
+
entry.result = result;
|
|
284
|
+
entry.snapshotResultApplied = true;
|
|
285
|
+
if (typeof result.isError === "boolean") {
|
|
286
|
+
entry.isError = result.isError;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
248
289
|
this.emit();
|
|
249
290
|
}
|
|
250
291
|
break;
|
|
@@ -343,6 +384,7 @@ export class RunAggregator {
|
|
|
343
384
|
parsedArgs: undefined,
|
|
344
385
|
result: undefined,
|
|
345
386
|
isError: undefined,
|
|
387
|
+
snapshotResultApplied: false,
|
|
346
388
|
};
|
|
347
389
|
if (parentMessageId) {
|
|
348
390
|
state.parentMessageId = parentMessageId;
|
|
@@ -411,6 +453,7 @@ export class RunAggregator {
|
|
|
411
453
|
parsedArgs: undefined,
|
|
412
454
|
result: undefined,
|
|
413
455
|
isError: undefined,
|
|
456
|
+
snapshotResultApplied: false,
|
|
414
457
|
};
|
|
415
458
|
this.toolCalls.set(id, entry);
|
|
416
459
|
}
|
|
@@ -421,8 +464,17 @@ export class RunAggregator {
|
|
|
421
464
|
) {
|
|
422
465
|
this.partOrder.push({ kind: "tool-call", toolCallId: id });
|
|
423
466
|
}
|
|
424
|
-
entry.
|
|
425
|
-
|
|
467
|
+
if (entry.snapshotResultApplied) {
|
|
468
|
+
if (entry.modelContent === undefined) {
|
|
469
|
+
entry.modelContent = [{ type: "text", text: content }];
|
|
470
|
+
}
|
|
471
|
+
if (entry.isError === undefined) {
|
|
472
|
+
entry.isError = isError;
|
|
473
|
+
}
|
|
474
|
+
} else {
|
|
475
|
+
entry.result = tryParseJSON(content);
|
|
476
|
+
entry.isError = isError;
|
|
477
|
+
}
|
|
426
478
|
if (toolMessageId) {
|
|
427
479
|
entry.toolMessageId = toolMessageId;
|
|
428
480
|
}
|
|
@@ -460,9 +512,21 @@ export class RunAggregator {
|
|
|
460
512
|
args: (entry.parsedArgs ?? {}) as any,
|
|
461
513
|
argsText: entry.argsText,
|
|
462
514
|
...(entry.result !== undefined ? { result: entry.result } : {}),
|
|
515
|
+
...(entry.modelContent !== undefined
|
|
516
|
+
? { modelContent: entry.modelContent }
|
|
517
|
+
: {}),
|
|
463
518
|
...(entry.isError !== undefined ? { isError: entry.isError } : {}),
|
|
464
519
|
...(entry.mcpAppResourceUri
|
|
465
|
-
? {
|
|
520
|
+
? {
|
|
521
|
+
mcp: {
|
|
522
|
+
app: {
|
|
523
|
+
resourceUri: entry.mcpAppResourceUri,
|
|
524
|
+
...(entry.mcpAppServerId
|
|
525
|
+
? { serverId: entry.mcpAppServerId }
|
|
526
|
+
: {}),
|
|
527
|
+
},
|
|
528
|
+
},
|
|
529
|
+
}
|
|
466
530
|
: {}),
|
|
467
531
|
...(entry.parentMessageId ? { parentId: entry.parentMessageId } : {}),
|
|
468
532
|
...(entry.toolMessageId
|
package/src/runtime/types.ts
CHANGED
|
@@ -59,6 +59,17 @@ export type UseAgUiRuntimeOptions = ExternalStoreSharedOptions & {
|
|
|
59
59
|
agent: AbstractAgent;
|
|
60
60
|
logger?: Partial<Logger>;
|
|
61
61
|
showThinking?: boolean;
|
|
62
|
+
/**
|
|
63
|
+
* When the user sends, edits, or reloads a message while client-side tool
|
|
64
|
+
* calls are still pending, automatically cancel the unresolved tool calls
|
|
65
|
+
* with an error result so the agent's tool-call accounting stays
|
|
66
|
+
* consistent. Pending AG-UI interrupts are exempt: they still reject the
|
|
67
|
+
* run and must be answered with `useAgUiSubmitInterruptResponses` or
|
|
68
|
+
* discarded with `useAgUiSteerAway`. When disabled, `useAgUiSteerAway`
|
|
69
|
+
* remains the explicit way to cancel pending tool calls.
|
|
70
|
+
* Defaults to `true`.
|
|
71
|
+
*/
|
|
72
|
+
autoCancelPendingToolCalls?: boolean | undefined;
|
|
62
73
|
onError?: (e: Error) => void;
|
|
63
74
|
onCancel?: () => void;
|
|
64
75
|
adapters?: UseAgUiRuntimeAdapters;
|
|
@@ -95,6 +106,12 @@ export type AgUiRuntimeExtras = {
|
|
|
95
106
|
message: CreateAppendMessage,
|
|
96
107
|
responses?: readonly AgUiResumeEntry[],
|
|
97
108
|
) => Promise<void>;
|
|
109
|
+
state: ReadonlyJSONValue | undefined;
|
|
110
|
+
setState: (
|
|
111
|
+
next:
|
|
112
|
+
| ReadonlyJSONValue
|
|
113
|
+
| ((prev: ReadonlyJSONValue | undefined) => ReadonlyJSONValue),
|
|
114
|
+
) => void;
|
|
98
115
|
};
|
|
99
116
|
|
|
100
117
|
export type AgUiRunFinishedOutcome =
|
package/src/useAgUiRuntime.ts
CHANGED
|
@@ -58,6 +58,7 @@ export function useAgUiRuntime(
|
|
|
58
58
|
agent: options.agent,
|
|
59
59
|
logger,
|
|
60
60
|
showThinking: options.showThinking ?? true,
|
|
61
|
+
autoCancelPendingToolCalls: options.autoCancelPendingToolCalls,
|
|
61
62
|
...(options.onError && { onError: options.onError }),
|
|
62
63
|
...(options.onCancel && { onCancel: options.onCancel }),
|
|
63
64
|
...(historyAdapter && { history: historyAdapter }),
|
|
@@ -70,6 +71,7 @@ export function useAgUiRuntime(
|
|
|
70
71
|
agent: options.agent,
|
|
71
72
|
logger,
|
|
72
73
|
showThinking: options.showThinking ?? true,
|
|
74
|
+
autoCancelPendingToolCalls: options.autoCancelPendingToolCalls,
|
|
73
75
|
...(options.onError && { onError: options.onError }),
|
|
74
76
|
...(options.onCancel && { onCancel: options.onCancel }),
|
|
75
77
|
...(historyAdapter && { history: historyAdapter }),
|
|
@@ -95,6 +97,7 @@ export function useAgUiRuntime(
|
|
|
95
97
|
? async () => {
|
|
96
98
|
await onSwitchToNewThread();
|
|
97
99
|
core.applyExternalMessages([]);
|
|
100
|
+
core.resetState();
|
|
98
101
|
}
|
|
99
102
|
: undefined,
|
|
100
103
|
onSwitchToThread: onSwitchToThread
|
|
@@ -106,6 +109,8 @@ export function useAgUiRuntime(
|
|
|
106
109
|
core.applyExternalMessages(result.messages);
|
|
107
110
|
if (result.state !== undefined) {
|
|
108
111
|
core.loadExternalState(result.state);
|
|
112
|
+
} else {
|
|
113
|
+
core.resetState();
|
|
109
114
|
}
|
|
110
115
|
if (result.unstable_resume) {
|
|
111
116
|
void core.resumeInFlightRun(result.messages);
|
|
@@ -136,7 +141,7 @@ export function useAgUiRuntime(
|
|
|
136
141
|
return {
|
|
137
142
|
...shared,
|
|
138
143
|
isLoading: core.isLoading,
|
|
139
|
-
|
|
144
|
+
messageRepository: core.getMessageRepository(),
|
|
140
145
|
state: core.getState(),
|
|
141
146
|
isRunning: core.isRunning() || hasExecutingTools,
|
|
142
147
|
extras: agUiExtras.provide({
|
|
@@ -145,6 +150,8 @@ export function useAgUiRuntime(
|
|
|
145
150
|
submitInterruptResponses: (responses) =>
|
|
146
151
|
core.submitInterruptResponses(responses),
|
|
147
152
|
steerAway: (message, responses) => core.steerAway(message, responses),
|
|
153
|
+
state: core.getState(),
|
|
154
|
+
setState: (next) => core.setState(next),
|
|
148
155
|
}),
|
|
149
156
|
unstable_enableToolInvocations: true,
|
|
150
157
|
setToolStatuses,
|