@assistant-ui/react-ag-ui 0.0.64 → 0.0.66

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 (35) hide show
  1. package/dist/runtime/AgUiThreadRuntimeCore.d.ts +19 -3
  2. package/dist/runtime/AgUiThreadRuntimeCore.d.ts.map +1 -1
  3. package/dist/runtime/AgUiThreadRuntimeCore.js +193 -39
  4. package/dist/runtime/AgUiThreadRuntimeCore.js.map +1 -1
  5. package/dist/runtime/adapter/conversions.d.ts.map +1 -1
  6. package/dist/runtime/adapter/conversions.js +19 -29
  7. package/dist/runtime/adapter/conversions.js.map +1 -1
  8. package/dist/runtime/adapter/run-aggregator.d.ts +4 -16
  9. package/dist/runtime/adapter/run-aggregator.d.ts.map +1 -1
  10. package/dist/runtime/adapter/run-aggregator.js +100 -23
  11. package/dist/runtime/adapter/run-aggregator.js.map +1 -1
  12. package/dist/runtime/event-parser.d.ts.map +1 -1
  13. package/dist/runtime/event-parser.js +16 -8
  14. package/dist/runtime/event-parser.js.map +1 -1
  15. package/dist/runtime/types.d.ts +30 -5
  16. package/dist/runtime/types.d.ts.map +1 -1
  17. package/dist/runtime/types.js +8 -0
  18. package/dist/runtime/types.js.map +1 -0
  19. package/dist/useAgUiRuntime.d.ts.map +1 -1
  20. package/dist/useAgUiRuntime.js +80 -33
  21. package/dist/useAgUiRuntime.js.map +1 -1
  22. package/package.json +7 -6
  23. package/src/runtime/AgUiThreadRuntimeCore.test.ts +380 -6
  24. package/src/runtime/AgUiThreadRuntimeCore.ts +314 -70
  25. package/src/runtime/adapter/conversions.content.test.ts +41 -0
  26. package/src/runtime/adapter/conversions.nested-tool-calls.test.ts +107 -0
  27. package/src/runtime/adapter/conversions.ts +37 -64
  28. package/src/runtime/adapter/run-aggregator.reasoning.test.ts +143 -0
  29. package/src/runtime/adapter/run-aggregator.ts +163 -40
  30. package/src/runtime/event-parser.ts +18 -4
  31. package/src/runtime/types.ts +43 -11
  32. package/src/useAgUiRuntime.external-store.test.tsx +51 -0
  33. package/src/useAgUiRuntime.queue.test.tsx +63 -2
  34. package/src/useAgUiRuntime.thread-switch.test.tsx +587 -3
  35. package/src/useAgUiRuntime.ts +106 -44
@@ -1,11 +1,11 @@
1
1
  "use client";
2
2
  import { readMcpAppResourceUri } from "../mcp-tool-result.js";
3
+ import { AG_UI_METADATA_NAMESPACE } from "../types.js";
3
4
  import { projectAgUiToolApprovals } from "./tool-approval.js";
4
5
  import { isMcpAppUri } from "@assistant-ui/core";
5
6
  import jsonpatch from "fast-json-patch";
6
- import { applyA2uiOperations, convertSurfaceToUISpec, surfaceToOperations } from "@assistant-ui/react-generative-ui/a2ui";
7
+ import { applyA2uiOperations, surfaceToPresentToolCall } from "@assistant-ui/generative-ui/a2ui";
7
8
  //#region src/runtime/adapter/run-aggregator.ts
8
- const AG_UI_METADATA_NAMESPACE = "agui";
9
9
  const ROOT_SCOPE = "";
10
10
  const MAX_SUBAGENT_DEPTH = 16;
11
11
  const createJSONContainerScanner = () => ({
@@ -62,8 +62,6 @@ const scanJSONContainerDelta = (scanner, delta) => {
62
62
  }
63
63
  return completed && scanner.state === "complete";
64
64
  };
65
- const MCP_APPS_ACTIVITY_TYPE = "mcp-apps";
66
- const A2UI_SURFACE_ACTIVITY_TYPE = "a2ui-surface";
67
65
  const isPlainObject = (value) => !!value && typeof value === "object" && !Array.isArray(value);
68
66
  /**
69
67
  * Collects AG-UI events into assistant-ui run snapshots that can be yielded from a ChatModelAdapter.
@@ -108,6 +106,7 @@ var RunAggregator = class {
108
106
  activityParts = /* @__PURE__ */ new Map();
109
107
  lastResolvedToolCallIdByScope = /* @__PURE__ */ new Map();
110
108
  partOrder = [];
109
+ lastReasoningBeforeToolCall;
111
110
  textPartCounter = 0;
112
111
  serverMessageIdReported = false;
113
112
  reportedServerMessageId;
@@ -228,7 +227,7 @@ var RunAggregator = class {
228
227
  case "THINKING_TEXT_MESSAGE_START":
229
228
  case "REASONING_START":
230
229
  case "REASONING_MESSAGE_START":
231
- this.handleReasoningStart(this.scopeOf("subagentRunId" in event ? event : {}), "messageId" in event ? event.messageId : void 0, event.type === "REASONING_MESSAGE_START");
230
+ this.handleReasoningStart(this.scopeOf("subagentRunId" in event ? event : {}), "messageId" in event ? event.messageId : void 0, event.type === "REASONING_MESSAGE_START", event.timestamp);
232
231
  break;
233
232
  case "REASONING_ENCRYPTED_VALUE":
234
233
  if (event.subtype === "message") {
@@ -259,11 +258,11 @@ var RunAggregator = class {
259
258
  break;
260
259
  case "THINKING_TEXT_MESSAGE_END":
261
260
  case "THINKING_END":
262
- this.handleReasoningEnd(ROOT_SCOPE);
261
+ this.handleReasoningEnd(ROOT_SCOPE, event.timestamp);
263
262
  break;
264
263
  case "REASONING_MESSAGE_END":
265
264
  case "REASONING_END":
266
- this.handleReasoningEnd(this.scopeOf(event));
265
+ this.handleReasoningEnd(this.scopeOf(event), event.timestamp);
267
266
  break;
268
267
  case "TOOL_CALL_START": {
269
268
  const scope = this.scopeOf(event);
@@ -441,19 +440,19 @@ var RunAggregator = class {
441
440
  for (const bucket of this.a2uiBuckets.values()) for (const [surfaceId, surface] of bucket) surfaces.set(surfaceId, surface);
442
441
  const activeToolCallIds = /* @__PURE__ */ new Set();
443
442
  for (const [surfaceId, surface] of surfaces) {
444
- const toolCallId = `a2ui:${surfaceId}`;
445
- const { spec, warnings } = convertSurfaceToUISpec(surface);
443
+ const { toolCall, warnings } = surfaceToPresentToolCall(surfaceId, surface);
446
444
  for (const warning of warnings) this.logger.debug("[agui] a2ui surface conversion warning", warning);
447
- if (!spec) continue;
445
+ if (!toolCall) continue;
446
+ const { toolCallId } = toolCall;
448
447
  activeToolCallIds.add(toolCallId);
449
448
  const entry = {
450
- toolCallId,
451
- toolCallName: "present",
452
- argsText: JSON.stringify(spec),
453
- parsedArgs: spec,
454
- result: {},
449
+ toolCallId: toolCall.toolCallId,
450
+ toolCallName: toolCall.toolName,
451
+ argsText: toolCall.argsText,
452
+ parsedArgs: toolCall.args,
453
+ result: toolCall.result,
455
454
  isError: void 0,
456
- artifact: { a2ui: surfaceToOperations(surface) },
455
+ artifact: toolCall.artifact,
457
456
  snapshotResultApplied: false
458
457
  };
459
458
  if (!this.toolCalls.has(toolCallId)) this.partOrder.push({
@@ -508,6 +507,7 @@ var RunAggregator = class {
508
507
  this.activityParts.clear();
509
508
  this.lastResolvedToolCallIdByScope.clear();
510
509
  this.partOrder.length = 0;
510
+ this.lastReasoningBeforeToolCall = void 0;
511
511
  this.textPartCounter = 0;
512
512
  this.activeTextMessageIdByScope.clear();
513
513
  this.reportedServerMessageId = void 0;
@@ -523,8 +523,70 @@ var RunAggregator = class {
523
523
  }
524
524
  beginDistinctTextMessage(messageId) {
525
525
  if (!messageId || this.lastTextMessageId === void 0 || this.lastTextMessageId === messageId || !this.onTextMessageStart) return;
526
+ const carried = this.takeTrailingReasoning();
527
+ if (carried.parts.length > 0 || carried.hiddenSignatures.length > 0) this.emit();
528
+ const reasoningPartCounter = this.reasoningPartCounter;
526
529
  this.resetMessageParts();
527
530
  this.onTextMessageStart(messageId);
531
+ this.reasoningPartCounter = reasoningPartCounter;
532
+ this.restoreReasoning(carried);
533
+ }
534
+ takeTrailingReasoning() {
535
+ const parts = [];
536
+ let last = this.partOrder.at(-1);
537
+ while (last?.kind === "reasoning" && last.subagentRunId === void 0 && last !== this.lastReasoningBeforeToolCall) {
538
+ this.partOrder.pop();
539
+ parts.unshift({
540
+ key: last.key,
541
+ entry: this.reasoningParts.get(last.key) ?? { buffer: "" },
542
+ signature: this.reasoningSignatures.get(last.key),
543
+ signatureId: this.reasoningSignatureIds.get(last.key),
544
+ reasoningId: this.reasoningMessageIds.get(last.key),
545
+ anonymous: this.anonymousReasoningKeys.has(last.key)
546
+ });
547
+ last = this.partOrder.at(-1);
548
+ }
549
+ const cut = this.partOrder.length;
550
+ const hiddenSignatures = Array.from(this.hiddenSignatures).filter(([id]) => this.hiddenSignatureAnchors.get(id) >= cut);
551
+ for (const [id] of hiddenSignatures) {
552
+ this.hiddenSignatures.delete(id);
553
+ this.hiddenSignatureAnchors.delete(id);
554
+ }
555
+ const hiddenBlockIds = Array.from(this.hiddenBlockAnchors).filter(([, anchor]) => anchor >= cut).map(([id]) => id);
556
+ const hiddenAnonymous = this.hiddenAnonymousAnchor !== void 0 && this.hiddenAnonymousAnchor >= cut;
557
+ const activeCarried = this.hiddenActiveReasoning === "identified" && hiddenBlockIds.length > 0 || this.hiddenActiveReasoning === "anonymous" && hiddenAnonymous;
558
+ return {
559
+ parts,
560
+ activeKey: this.activeReasoningKeyByScope.get(ROOT_SCOPE),
561
+ hiddenSignatures,
562
+ hiddenBlockIds,
563
+ hiddenAnonymous,
564
+ hiddenActiveReasoning: activeCarried ? this.hiddenActiveReasoning : "none"
565
+ };
566
+ }
567
+ restoreReasoning(carried) {
568
+ for (const part of carried.parts) {
569
+ this.partOrder.push({
570
+ kind: "reasoning",
571
+ key: part.key
572
+ });
573
+ this.reasoningParts.set(part.key, part.entry);
574
+ if (part.signature !== void 0) this.reasoningSignatures.set(part.key, part.signature);
575
+ if (part.signatureId !== void 0) this.reasoningSignatureIds.set(part.key, part.signatureId);
576
+ if (part.reasoningId !== void 0) this.reasoningMessageIds.set(part.key, part.reasoningId);
577
+ if (part.anonymous) this.anonymousReasoningKeys.add(part.key);
578
+ }
579
+ if (carried.activeKey !== void 0 && this.reasoningParts.has(carried.activeKey)) this.activeReasoningKeyByScope.set(ROOT_SCOPE, carried.activeKey);
580
+ for (const [id, encryptedValue] of carried.hiddenSignatures) {
581
+ this.hiddenSignatures.set(id, encryptedValue);
582
+ this.hiddenSignatureAnchors.set(id, 0);
583
+ }
584
+ for (const id of carried.hiddenBlockIds) {
585
+ this.hiddenReasoningIds.add(id);
586
+ this.hiddenBlockAnchors.set(id, 0);
587
+ }
588
+ if (carried.hiddenAnonymous) this.hiddenAnonymousAnchor = 0;
589
+ this.hiddenActiveReasoning = carried.hiddenActiveReasoning;
528
590
  }
529
591
  generateTextKey() {
530
592
  this.textPartCounter += 1;
@@ -571,7 +633,10 @@ var RunAggregator = class {
571
633
  startToolCall(scope, id, name, parentMessageId) {
572
634
  if (!id) return;
573
635
  this.activeTextMessageIdByScope.delete(scope);
574
- if (!this.partOrder.some((part) => part.kind === "tool-call" && part.toolCallId === id)) this.insertToolPart(scope, id, parentMessageId);
636
+ if (!this.partOrder.some((part) => part.kind === "tool-call" && part.toolCallId === id)) {
637
+ if (scope === ROOT_SCOPE) this.lastReasoningBeforeToolCall = this.partOrder.findLast((part) => part.kind === "reasoning" && part.subagentRunId === void 0);
638
+ this.insertToolPart(scope, id, parentMessageId);
639
+ }
575
640
  const state = {
576
641
  toolCallId: id,
577
642
  toolCallName: name ?? "tool",
@@ -773,7 +838,8 @@ var RunAggregator = class {
773
838
  const rawScope = "subagentRunId" in part ? part.subagentRunId ?? ROOT_SCOPE : ROOT_SCOPE;
774
839
  if (part.kind === "reasoning") {
775
840
  if (this.showThinking) {
776
- const buffer = this.reasoningParts.get(part.key) ?? "";
841
+ const entry = this.reasoningParts.get(part.key);
842
+ const buffer = entry?.buffer ?? "";
777
843
  const isActive = this.activeReasoningKeyByScope.get(rawScope) === part.key;
778
844
  if (buffer.length > 0 || isActive) {
779
845
  const encryptedValue = this.reasoningSignatures.get(part.key);
@@ -785,6 +851,7 @@ var RunAggregator = class {
785
851
  snapshot.push({
786
852
  type: "reasoning",
787
853
  text: buffer,
854
+ ...entry?.timing ? { timing: entry.timing } : {},
788
855
  ...Object.keys(meta).length > 0 ? { providerMetadata: { [AG_UI_METADATA_NAMESPACE]: meta } } : {}
789
856
  });
790
857
  materialized();
@@ -905,7 +972,7 @@ var RunAggregator = class {
905
972
  toolCallCount: this.toolCalls.size
906
973
  };
907
974
  }
908
- handleReasoningStart(scope, messageId, isMessageId = false) {
975
+ handleReasoningStart(scope, messageId, isMessageId = false, timestamp) {
909
976
  if (!this.showThinking) {
910
977
  if (scope !== ROOT_SCOPE) return;
911
978
  const anchor = this.partOrder.length;
@@ -924,7 +991,7 @@ var RunAggregator = class {
924
991
  if (messageId === void 0) this.anonymousReasoningKeys.add(key);
925
992
  else if (isMessageId) this.reasoningMessageIds.set(key, messageId);
926
993
  if (!this.reasoningParts.has(key)) {
927
- this.reasoningParts.set(key, "");
994
+ this.reasoningParts.set(key, { buffer: "" });
928
995
  this.partOrder.push(scope === ROOT_SCOPE ? {
929
996
  kind: "reasoning",
930
997
  key
@@ -934,6 +1001,9 @@ var RunAggregator = class {
934
1001
  subagentRunId: scope
935
1002
  });
936
1003
  }
1004
+ const entry = this.reasoningParts.get(key);
1005
+ if (entry?.timing) entry.timing = { startedAt: entry.timing.startedAt };
1006
+ else if (entry && typeof timestamp === "number" && Number.isFinite(timestamp)) entry.timing = { startedAt: timestamp };
937
1007
  this.activeReasoningKeyByScope.set(scope, key);
938
1008
  this.emit();
939
1009
  }
@@ -947,14 +1017,21 @@ var RunAggregator = class {
947
1017
  if (!this.activeReasoningKeyByScope.has(scope)) this.handleReasoningStart(scope, messageId, isMessageId);
948
1018
  const key = this.activeReasoningKeyByScope.get(scope);
949
1019
  if (!key) return;
950
- this.reasoningParts.set(key, (this.reasoningParts.get(key) ?? "") + delta);
1020
+ const entry = this.reasoningParts.get(key);
1021
+ if (entry) entry.buffer += delta;
951
1022
  this.emit();
952
1023
  }
953
- handleReasoningEnd(scope) {
1024
+ handleReasoningEnd(scope, timestamp) {
954
1025
  if (!this.showThinking) {
955
1026
  if (scope === ROOT_SCOPE) this.hiddenActiveReasoning = "none";
956
1027
  return;
957
1028
  }
1029
+ const key = this.activeReasoningKeyByScope.get(scope);
1030
+ const entry = key ? this.reasoningParts.get(key) : void 0;
1031
+ if (entry?.timing && typeof timestamp === "number" && Number.isFinite(timestamp)) entry.timing = {
1032
+ ...entry.timing,
1033
+ completedAt: timestamp
1034
+ };
958
1035
  this.activeReasoningKeyByScope.delete(scope);
959
1036
  this.emit();
960
1037
  }
@@ -968,6 +1045,6 @@ function tryParseJSON(value) {
968
1045
  }
969
1046
  }
970
1047
  //#endregion
971
- export { A2UI_SURFACE_ACTIVITY_TYPE, AG_UI_METADATA_NAMESPACE, MCP_APPS_ACTIVITY_TYPE, RunAggregator, isPlainObject, tryParseJSON };
1048
+ export { RunAggregator, isPlainObject, tryParseJSON };
972
1049
 
973
1050
  //# sourceMappingURL=run-aggregator.js.map