@assistant-ui/react-ag-ui 0.1.1 → 0.1.2

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.
Files changed (41) hide show
  1. package/README.md +2 -0
  2. package/dist/runtime/AgUiThreadController.d.ts +11 -0
  3. package/dist/runtime/AgUiThreadController.d.ts.map +1 -1
  4. package/dist/runtime/AgUiThreadController.js +212 -22
  5. package/dist/runtime/AgUiThreadController.js.map +1 -1
  6. package/dist/runtime/adapter/conversions.d.ts +9 -1
  7. package/dist/runtime/adapter/conversions.d.ts.map +1 -1
  8. package/dist/runtime/adapter/conversions.js +184 -9
  9. package/dist/runtime/adapter/conversions.js.map +1 -1
  10. package/dist/runtime/adapter/run-aggregator.d.ts +1 -0
  11. package/dist/runtime/adapter/run-aggregator.d.ts.map +1 -1
  12. package/dist/runtime/adapter/run-aggregator.js +34 -12
  13. package/dist/runtime/adapter/run-aggregator.js.map +1 -1
  14. package/dist/runtime/adapter/subscriber.d.ts +0 -1
  15. package/dist/runtime/adapter/subscriber.d.ts.map +1 -1
  16. package/dist/runtime/adapter/subscriber.js +1 -8
  17. package/dist/runtime/adapter/subscriber.js.map +1 -1
  18. package/dist/runtime/event-parser.d.ts.map +1 -1
  19. package/dist/runtime/event-parser.js +1 -0
  20. package/dist/runtime/event-parser.js.map +1 -1
  21. package/dist/runtime/types.d.ts +2 -0
  22. package/dist/runtime/types.d.ts.map +1 -1
  23. package/dist/runtime/types.js +2 -1
  24. package/dist/runtime/types.js.map +1 -1
  25. package/package.json +5 -5
  26. package/src/runtime/AgUiThreadController.disconnect.test.ts +914 -0
  27. package/src/runtime/AgUiThreadController.interrupt-history.test.ts +392 -0
  28. package/src/runtime/AgUiThreadController.replay.test.ts +8 -1
  29. package/src/runtime/AgUiThreadController.snapshot-head-move.test.ts +167 -0
  30. package/src/runtime/AgUiThreadController.subagent-snapshot.test.ts +854 -0
  31. package/src/runtime/AgUiThreadController.test.ts +40 -0
  32. package/src/runtime/AgUiThreadController.ts +459 -27
  33. package/src/runtime/adapter/conversions.content.test.ts +129 -0
  34. package/src/runtime/adapter/conversions.subagent-messages.test.ts +550 -0
  35. package/src/runtime/adapter/conversions.ts +331 -20
  36. package/src/runtime/adapter/run-aggregator.metadata.test.ts +151 -0
  37. package/src/runtime/adapter/run-aggregator.ts +52 -9
  38. package/src/runtime/adapter/subscriber.test.ts +1 -1
  39. package/src/runtime/adapter/subscriber.ts +1 -6
  40. package/src/runtime/event-parser.ts +3 -0
  41. package/src/runtime/types.ts +5 -0
@@ -1,5 +1,5 @@
1
1
  import type { ContentPart, RunAgentParameters } from "@ag-ui/client";
2
- import type { ThreadMessageLike as CoreThreadMessageLike } from "@assistant-ui/core";
2
+ import type { ThreadMessageLike as CoreThreadMessageLike, PartProviderMetadata } from "@assistant-ui/core";
3
3
  import { type Tool } from "assistant-stream";
4
4
  type AttachmentLike = {
5
5
  name?: string | undefined;
@@ -57,6 +57,7 @@ export type AgUiMessage = {
57
57
  error?: string;
58
58
  metadata?: Record<string, unknown>;
59
59
  };
60
+ export declare function withAgUiPartMessageMetadata(providerMetadata: PartProviderMetadata | undefined, messageMetadata: Record<string, unknown> | undefined): PartProviderMetadata;
60
61
  export type FromAgUiMessagesOptions = {
61
62
  /**
62
63
  * Whether to convert `reasoning` messages into visible reasoning parts.
@@ -65,6 +66,13 @@ export type FromAgUiMessagesOptions = {
65
66
  showThinking?: boolean;
66
67
  };
67
68
  export declare function fromAgUiMessages(messages: readonly unknown[], options?: FromAgUiMessagesOptions): CoreThreadMessageLike[];
69
+ /**
70
+ * Converts AG-UI messages, nesting each subagent run's messages under the tool
71
+ * call `SUBAGENT_STARTED.parentToolCallId` announced for it. A message's
72
+ * `subagentRunId` names its run; a run with no announced spawning call in
73
+ * `messages` stays in the parent thread.
74
+ */
75
+ export declare function fromAgUiSnapshotMessages(messages: readonly unknown[], options?: FromAgUiMessagesOptions, subagentParentToolCallIds?: ReadonlyMap<string, string | undefined>): CoreThreadMessageLike[];
68
76
  export declare function toAgUiMessages(messages: readonly ThreadMessageLike[]): AgUiMessage[];
69
77
  type AgUiTool = NonNullable<RunAgentParameters["tools"]>[number];
70
78
  export declare function toAgUiTools(tools: Record<string, Tool> | undefined): AgUiTool[];
@@ -1 +1 @@
1
- {"version":3,"file":"conversions.d.ts","sourceRoot":"","sources":["../../../src/runtime/adapter/conversions.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"conversions.d.ts","sourceRoot":"","sources":["../../../src/runtime/adapter/conversions.ts"],"names":[],"mappings":"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"}
@@ -2,7 +2,7 @@
2
2
  import { parseMcpToolCallResult, readMcpAppResourceUri, toolContentText } from "../mcp-tool-result.js";
3
3
  import { AG_UI_METADATA_NAMESPACE } from "../types.js";
4
4
  import { projectAgUiToolApprovals } from "./tool-approval.js";
5
- import { generateId } from "@assistant-ui/core";
5
+ import { fromThreadMessageLike, generateId } from "@assistant-ui/core";
6
6
  import { getAutoStatus, parseDataUrl, resolveFilePartSource, resolveImageMediaType, walkToolCallTree } from "@assistant-ui/core/internal";
7
7
  import { applyA2uiOperations, surfaceToPresentToolCall } from "@assistant-ui/generative-ui/a2ui";
8
8
  import { toToolsJSONSchema } from "assistant-stream";
@@ -63,11 +63,15 @@ function withAgUiPartMessageMetadata(providerMetadata, messageMetadata) {
63
63
  if (!messageMetadata) return providerMetadata ?? {};
64
64
  const existing = providerMetadata ?? {};
65
65
  const namespaced = isObject(existing["agui"]) ? existing[AG_UI_METADATA_NAMESPACE] : {};
66
+ const previousMessageMetadata = isObject(namespaced.messageMetadata) ? namespaced.messageMetadata : {};
66
67
  return {
67
68
  ...existing,
68
69
  [AG_UI_METADATA_NAMESPACE]: {
69
70
  ...namespaced,
70
- messageMetadata
71
+ messageMetadata: {
72
+ ...previousMessageMetadata,
73
+ ...messageMetadata
74
+ }
71
75
  }
72
76
  };
73
77
  }
@@ -141,6 +145,11 @@ function mediaTypeForMime(mimeType) {
141
145
  return "document";
142
146
  }
143
147
  function buildPartSource(value, declaredMimeType, sourceType) {
148
+ if (sourceType === "id") return {
149
+ type: "file",
150
+ value,
151
+ ...declaredMimeType !== void 0 && { mimeType: declaredMimeType }
152
+ };
144
153
  const source = resolveFilePartSource({
145
154
  data: value,
146
155
  mimeType: declaredMimeType ?? "application/octet-stream",
@@ -245,6 +254,11 @@ function partSourceToString(value) {
245
254
  isUrl: true,
246
255
  ...mimeType !== void 0 && { mimeType }
247
256
  };
257
+ if (type === "file") return {
258
+ value: sourceValue,
259
+ isId: true,
260
+ ...mimeType !== void 0 && { mimeType }
261
+ };
248
262
  if (type === "data") {
249
263
  const resolvedMimeType = mimeType ?? "application/octet-stream";
250
264
  return {
@@ -298,11 +312,12 @@ function toUserContentLayout(content) {
298
312
  }
299
313
  const part = normalizeInputPart(rawPart);
300
314
  if (!part || !mediaInputTypes.has(getString(part, "type") ?? "")) continue;
301
- if (isObject(part.source) && part.source.type === "file" && typeof part.source.value === "string") fileSourceParts.push({
302
- index: mediaIndex++,
315
+ if (!partSourceToString(part.source)) continue;
316
+ if (isObject(part.source) && part.source.type === "file") fileSourceParts.push({
317
+ index: mediaIndex,
303
318
  part
304
319
  });
305
- else if (partSourceToString(part.source)) mediaIndex++;
320
+ mediaIndex++;
306
321
  }
307
322
  return {
308
323
  fileSourceParts,
@@ -322,7 +337,7 @@ function toSnapshotAttachments(content) {
322
337
  if (!source) continue;
323
338
  const { filename, providerMetadata } = readPartMetadata(part.metadata);
324
339
  const id = attachments.length.toString();
325
- if (type === "image") {
340
+ if (type === "image" && !source.isId) {
326
341
  attachments.push({
327
342
  id,
328
343
  type: "image",
@@ -350,6 +365,7 @@ function toSnapshotAttachments(content) {
350
365
  data: source.value,
351
366
  mimeType,
352
367
  ...source.isUrl && { sourceType: "url" },
368
+ ...source.isId && { sourceType: "id" },
353
369
  ...filename !== void 0 && { filename },
354
370
  ...providerMetadata && { providerMetadata }
355
371
  }]
@@ -398,10 +414,16 @@ function buildUserContent(message) {
398
414
  const restoresText = textSlots.length > 0 && textSlots.map(({ part }) => part.text).join("\n") === mergedText;
399
415
  const fileSourceSlots = readContentSlots(namespaced.fileSourceParts).flatMap(({ index, entry: { part } }) => isObject(part) && mediaInputTypes.has(getString(part, "type") ?? "") && isObject(part.source) && part.source.type === "file" && typeof part.source.value === "string" ? [{
400
416
  index,
401
- part
417
+ part,
418
+ value: part.source.value
402
419
  }] : []);
403
420
  const ordered = restoresText ? converted.filter((part) => part.type !== "text") : converted;
404
- for (const { index, part } of fileSourceSlots.sort((a, b) => a.index - b.index)) {
421
+ for (const { index, part, value } of fileSourceSlots.sort((a, b) => a.index - b.index)) {
422
+ const attached = ordered.findIndex((contentPart) => contentPart.type !== "text" && contentPart.source.type === "file" && contentPart.source.value === value && !fileSourceSlots.some((slot) => slot.part === contentPart));
423
+ if (attached !== -1) {
424
+ ordered[attached] = part;
425
+ continue;
426
+ }
405
427
  const mediaPositions = ordered.flatMap((contentPart, position) => contentPart.type !== "text" ? [position] : []);
406
428
  ordered.splice(mediaPositions[index] ?? ordered.length, 0, part);
407
429
  }
@@ -643,7 +665,160 @@ function foldTurnRecords(previous, message) {
643
665
  ...Object.keys(metadata).length > 0 ? { metadata } : {}
644
666
  };
645
667
  }
668
+ const readSubagentRunId = (record) => {
669
+ const runId = getString(record, "subagentRunId");
670
+ return runId?.trim() ? runId : void 0;
671
+ };
672
+ const readToolCallIds = (record) => extractAssistantToolCalls(record).flatMap((part) => part.toolCallId ? [part.toolCallId] : []);
646
673
  function fromAgUiMessages(messages, options) {
674
+ return fromAgUiSnapshotMessages(messages, options);
675
+ }
676
+ /**
677
+ * Converts AG-UI messages, nesting each subagent run's messages under the tool
678
+ * call `SUBAGENT_STARTED.parentToolCallId` announced for it. A message's
679
+ * `subagentRunId` names its run; a run with no announced spawning call in
680
+ * `messages` stays in the parent thread.
681
+ */
682
+ function fromAgUiSnapshotMessages(messages, options, subagentParentToolCallIds) {
683
+ const records = messages.filter(isObject);
684
+ const callOwners = /* @__PURE__ */ new Map();
685
+ for (const record of records) {
686
+ if (getString(record, "role") !== "assistant") continue;
687
+ for (const toolCallId of readToolCallIds(record)) callOwners.set(toolCallId, readSubagentRunId(record));
688
+ }
689
+ const spawningCalls = /* @__PURE__ */ new Map();
690
+ for (const record of records) {
691
+ const runId = readSubagentRunId(record);
692
+ if (runId === void 0 || spawningCalls.has(runId)) continue;
693
+ const toolCallId = subagentParentToolCallIds?.get(runId);
694
+ if (toolCallId !== void 0 && callOwners.has(toolCallId)) spawningCalls.set(runId, toolCallId);
695
+ }
696
+ if (spawningCalls.size === 0) return convertAgUiMessages(messages, options);
697
+ const reachesRoot = (runId) => {
698
+ let current = runId;
699
+ for (let depth = 0; current !== void 0; depth++) {
700
+ const toolCallId = spawningCalls.get(current);
701
+ if (toolCallId === void 0 || depth > 16) return false;
702
+ current = callOwners.get(toolCallId);
703
+ }
704
+ return true;
705
+ };
706
+ const nestedRuns = new Set([...spawningCalls.keys()].filter(reachesRoot));
707
+ const groups = /* @__PURE__ */ new Map();
708
+ for (const record of records) {
709
+ const ownRunId = readSubagentRunId(record);
710
+ const answered = getString(record, "role") === "tool" ? getToolCallId(record) : void 0;
711
+ const runId = answered !== void 0 && callOwners.has(answered) ? callOwners.get(answered) : ownRunId;
712
+ const role = getString(record, "role");
713
+ const group = role !== "user" && role !== "system" && role !== "developer" && runId !== void 0 && nestedRuns.has(runId) ? runId : void 0;
714
+ const members = groups.get(group) ?? [];
715
+ members.push(record);
716
+ groups.set(group, members);
717
+ }
718
+ const runsBySpawningCall = /* @__PURE__ */ new Map();
719
+ for (const runId of nestedRuns) {
720
+ const toolCallId = spawningCalls.get(runId);
721
+ runsBySpawningCall.set(toolCallId, [...runsBySpawningCall.get(toolCallId) ?? [], runId]);
722
+ }
723
+ const convertGroup = (group) => convertAgUiMessages(groups.get(group) ?? [], options).map((message) => {
724
+ if (message.role !== "assistant" || !Array.isArray(message.content)) return message;
725
+ const nested = [];
726
+ const content = message.content.map((part) => {
727
+ if (part.type !== "tool-call") return part;
728
+ const runIds = runsBySpawningCall.get(part.toolCallId);
729
+ if (!runIds) return part;
730
+ const subagents = runIds.map(toSubagentMessage);
731
+ nested.push(...subagents);
732
+ return {
733
+ ...part,
734
+ messages: [...part.messages ?? [], ...subagents]
735
+ };
736
+ });
737
+ return liftSubagentState({
738
+ ...message,
739
+ content
740
+ }, nested);
741
+ });
742
+ const toSubagentMessage = (runId) => {
743
+ const messages = convertGroup(runId).filter((message) => message.role === "assistant");
744
+ const folded = messages.map((message) => typeof message.content === "string" ? {
745
+ ...message,
746
+ content: message.content ? [{
747
+ type: "text",
748
+ text: message.content
749
+ }] : []
750
+ } : message).reduce((merged, message) => merged ? foldTurnRecords(merged, message) : message, void 0);
751
+ const content = Array.isArray(folded?.content) ? folded.content : [];
752
+ const pendingToolCallIds = new Set(messages.filter((message) => message.status?.type === "requires-action" && message.status.reason === "tool-calls").flatMap((message) => admittedUnresolvedToolCallIds(message, true)));
753
+ const metadata = pendingToolCallIds.size ? withPendingToolCallIds(folded?.metadata, pendingToolCallIds) : folded?.metadata;
754
+ return fromThreadMessageLike({
755
+ id: runId,
756
+ role: "assistant",
757
+ content,
758
+ ...metadata ? { metadata } : {}
759
+ }, runId, subagentStatus(folded?.metadata, messages));
760
+ };
761
+ return convertGroup(void 0);
762
+ }
763
+ const hasUnresolvedToolCall = (content) => content.some((part) => isObject(part) && part.type === "tool-call" && part.result === void 0);
764
+ function admittedUnresolvedToolCallIds(message, includeNested) {
765
+ if (message.role !== "assistant" || !Array.isArray(message.content)) return [];
766
+ const listed = readPersistedPendingToolCallIds(message.metadata);
767
+ if (listed) return listed;
768
+ return message.content.flatMap((part) => {
769
+ if (part.type !== "tool-call") return [];
770
+ return (includeNested ? [part, ...Array.from(walkToolCallTree(part.messages ?? []), ({ part }) => part)] : [part]).flatMap((call) => call.result === void 0 && call.toolCallId ? [call.toolCallId] : []);
771
+ });
772
+ }
773
+ function withPendingToolCallIds(metadata, ids) {
774
+ return {
775
+ ...isObject(metadata) ? metadata : {},
776
+ custom: {
777
+ ...readCustomMetadata(metadata),
778
+ [AG_UI_METADATA_NAMESPACE]: {
779
+ ...readNamespacedMetadata(metadata),
780
+ pendingToolCallIds: [...ids]
781
+ }
782
+ }
783
+ };
784
+ }
785
+ function subagentStatus(metadata, messages) {
786
+ const hasInterrupt = readPersistedInterrupts(metadata) !== void 0;
787
+ const hasPendingToolCall = messages.some((message) => message.status?.type === "requires-action" && message.status.reason === "tool-calls");
788
+ return hasInterrupt || hasPendingToolCall ? getAutoStatus(false, false, hasInterrupt, hasPendingToolCall, void 0) : {
789
+ type: "complete",
790
+ reason: "unknown"
791
+ };
792
+ }
793
+ /**
794
+ * A nested run's open interrupts and unanswered calls surface on the message
795
+ * that spawned it, as the live run does.
796
+ */
797
+ function liftSubagentState(message, nested) {
798
+ const waiting = nested.filter((subagent) => subagent.status?.type === "requires-action");
799
+ if (waiting.length === 0) return message;
800
+ const interrupts = [...readPersistedInterrupts(message.metadata) ?? [], ...waiting.flatMap((subagent) => readPersistedInterrupts(subagent.metadata) ?? [])];
801
+ const custom = readCustomMetadata(message.metadata);
802
+ const metadata = {
803
+ ...isObject(message.metadata) ? message.metadata : {},
804
+ custom: {
805
+ ...custom,
806
+ [AG_UI_METADATA_NAMESPACE]: {
807
+ ...readNamespacedMetadata(message.metadata),
808
+ ...interrupts.length > 0 ? { interrupts } : {}
809
+ }
810
+ }
811
+ };
812
+ const hasInterrupt = interrupts.length > 0;
813
+ const hasPendingToolCall = waiting.some((subagent) => subagent.status?.type === "requires-action" && subagent.status.reason === "tool-calls") || Array.isArray(message.content) && hasUnresolvedToolCall(message.content);
814
+ const pendingToolCallIds = /* @__PURE__ */ new Set([...admittedUnresolvedToolCallIds(message, false), ...waiting.flatMap((subagent) => subagent.status?.type === "requires-action" && subagent.status.reason === "tool-calls" ? admittedUnresolvedToolCallIds(subagent, true) : [])]);
815
+ return {
816
+ ...message,
817
+ metadata: pendingToolCallIds.size ? withPendingToolCallIds(metadata, pendingToolCallIds) : metadata,
818
+ status: getAutoStatus(false, false, hasInterrupt, hasPendingToolCall, void 0)
819
+ };
820
+ }
821
+ function convertAgUiMessages(messages, options) {
647
822
  const showThinking = options?.showThinking ?? true;
648
823
  const converted = [];
649
824
  const a2uiBuckets = /* @__PURE__ */ new Map();
@@ -1045,6 +1220,6 @@ function toAgUiTools(tools) {
1045
1220
  parameters: tool.parameters
1046
1221
  }));
1047
1222
  }
1048
- export { fromAgUiMessages, toAgUiMessages, toAgUiTools };
1223
+ export { fromAgUiMessages, fromAgUiSnapshotMessages, toAgUiMessages, toAgUiTools, withAgUiPartMessageMetadata };
1049
1224
 
1050
1225
  //# sourceMappingURL=conversions.js.map