@agent-native/toolkit 0.198.3 → 0.198.5

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 (157) hide show
  1. package/dist/app/agentkit/react/components.d.ts +8 -3
  2. package/dist/app/agentkit/react/components.d.ts.map +1 -1
  3. package/dist/app/agentkit/react/components.js +24 -10
  4. package/dist/app/agentkit/react/components.js.map +1 -1
  5. package/dist/app/chat/AgentKitAssistantChat.d.ts.map +1 -1
  6. package/dist/app/chat/AgentKitAssistantChat.js +65 -33
  7. package/dist/app/chat/AgentKitAssistantChat.js.map +1 -1
  8. package/dist/app/chat/AgentSidebar.d.ts.map +1 -1
  9. package/dist/app/chat/AgentSidebar.js +2 -0
  10. package/dist/app/chat/AgentSidebar.js.map +1 -1
  11. package/dist/app/chat/FileStorageSetupPopover.js +2 -2
  12. package/dist/app/chat/FileStorageSetupPopover.js.map +1 -1
  13. package/dist/app/chat/MultiTabAssistantChat.js +1 -1
  14. package/dist/app/chat/agentkit-chat/connections.d.ts +1 -4
  15. package/dist/app/chat/agentkit-chat/connections.d.ts.map +1 -1
  16. package/dist/app/chat/agentkit-chat/connections.js +1 -26
  17. package/dist/app/chat/agentkit-chat/connections.js.map +1 -1
  18. package/dist/app/chat/agentkit-chat/history.d.ts +4 -1
  19. package/dist/app/chat/agentkit-chat/history.d.ts.map +1 -1
  20. package/dist/app/chat/agentkit-chat/history.js +17 -5
  21. package/dist/app/chat/agentkit-chat/history.js.map +1 -1
  22. package/dist/app/chat/chat/history-types.d.ts +1 -1
  23. package/dist/app/chat/chat/history-types.d.ts.map +1 -1
  24. package/dist/app/chat/chat-first/copy.d.ts +1 -2
  25. package/dist/app/chat/chat-first/copy.d.ts.map +1 -1
  26. package/dist/app/chat/chat-first/copy.js +1 -121
  27. package/dist/app/chat/chat-first/copy.js.map +1 -1
  28. package/dist/app/chat/chat-first-copy.d.ts +3 -0
  29. package/dist/app/chat/chat-first-copy.d.ts.map +1 -0
  30. package/dist/app/chat/chat-first-copy.js +122 -0
  31. package/dist/app/chat/chat-first-copy.js.map +1 -0
  32. package/dist/app/feature-flags/FeatureFlagsPanel.d.ts.map +1 -1
  33. package/dist/app/feature-flags/FeatureFlagsPanel.js +1 -0
  34. package/dist/app/feature-flags/FeatureFlagsPanel.js.map +1 -1
  35. package/dist/app/i18n/catalogs/ar-SA.d.ts.map +1 -1
  36. package/dist/app/i18n/catalogs/ar-SA.js +2 -1
  37. package/dist/app/i18n/catalogs/ar-SA.js.map +1 -1
  38. package/dist/app/i18n/catalogs/de-DE.d.ts.map +1 -1
  39. package/dist/app/i18n/catalogs/de-DE.js +2 -1
  40. package/dist/app/i18n/catalogs/de-DE.js.map +1 -1
  41. package/dist/app/i18n/catalogs/en-US.d.ts +2 -1
  42. package/dist/app/i18n/catalogs/en-US.d.ts.map +1 -1
  43. package/dist/app/i18n/catalogs/en-US.js +2 -1
  44. package/dist/app/i18n/catalogs/en-US.js.map +1 -1
  45. package/dist/app/i18n/catalogs/es-ES.d.ts.map +1 -1
  46. package/dist/app/i18n/catalogs/es-ES.js +2 -1
  47. package/dist/app/i18n/catalogs/es-ES.js.map +1 -1
  48. package/dist/app/i18n/catalogs/fr-FR.d.ts.map +1 -1
  49. package/dist/app/i18n/catalogs/fr-FR.js +2 -1
  50. package/dist/app/i18n/catalogs/fr-FR.js.map +1 -1
  51. package/dist/app/i18n/catalogs/hi-IN.d.ts.map +1 -1
  52. package/dist/app/i18n/catalogs/hi-IN.js +2 -1
  53. package/dist/app/i18n/catalogs/hi-IN.js.map +1 -1
  54. package/dist/app/i18n/catalogs/ja-JP.d.ts.map +1 -1
  55. package/dist/app/i18n/catalogs/ja-JP.js +2 -1
  56. package/dist/app/i18n/catalogs/ja-JP.js.map +1 -1
  57. package/dist/app/i18n/catalogs/ko-KR.d.ts.map +1 -1
  58. package/dist/app/i18n/catalogs/ko-KR.js +2 -1
  59. package/dist/app/i18n/catalogs/ko-KR.js.map +1 -1
  60. package/dist/app/i18n/catalogs/pt-BR.d.ts.map +1 -1
  61. package/dist/app/i18n/catalogs/pt-BR.js +2 -1
  62. package/dist/app/i18n/catalogs/pt-BR.js.map +1 -1
  63. package/dist/app/i18n/catalogs/zh-CN.d.ts.map +1 -1
  64. package/dist/app/i18n/catalogs/zh-CN.js +2 -1
  65. package/dist/app/i18n/catalogs/zh-CN.js.map +1 -1
  66. package/dist/app/i18n/catalogs/zh-TW.d.ts.map +1 -1
  67. package/dist/app/i18n/catalogs/zh-TW.js +2 -1
  68. package/dist/app/i18n/catalogs/zh-TW.js.map +1 -1
  69. package/dist/app/labs/LabsSettings.d.ts.map +1 -1
  70. package/dist/app/labs/LabsSettings.js +7 -3
  71. package/dist/app/labs/LabsSettings.js.map +1 -1
  72. package/dist/app/org/OrgGeneralSection.d.ts.map +1 -1
  73. package/dist/app/org/OrgGeneralSection.js +21 -18
  74. package/dist/app/org/OrgGeneralSection.js.map +1 -1
  75. package/dist/app/org/OrgSwitcher.d.ts.map +1 -1
  76. package/dist/app/org/OrgSwitcher.js +3 -2
  77. package/dist/app/org/OrgSwitcher.js.map +1 -1
  78. package/dist/app/org/workspace-app-links.d.ts +1 -0
  79. package/dist/app/org/workspace-app-links.d.ts.map +1 -1
  80. package/dist/app/org/workspace-app-links.js +3 -0
  81. package/dist/app/org/workspace-app-links.js.map +1 -1
  82. package/dist/app/resources/McpAgentKitConnectionRequest.d.ts +7 -2
  83. package/dist/app/resources/McpAgentKitConnectionRequest.d.ts.map +1 -1
  84. package/dist/app/resources/McpAgentKitConnectionRequest.js +132 -13
  85. package/dist/app/resources/McpAgentKitConnectionRequest.js.map +1 -1
  86. package/dist/composer/PromptComposer.d.ts +3 -0
  87. package/dist/composer/PromptComposer.d.ts.map +1 -1
  88. package/dist/composer/PromptComposer.js +2 -2
  89. package/dist/composer/PromptComposer.js.map +1 -1
  90. package/dist/composer/TiptapComposer.d.ts +4 -1
  91. package/dist/composer/TiptapComposer.d.ts.map +1 -1
  92. package/dist/composer/TiptapComposer.js +8 -4
  93. package/dist/composer/TiptapComposer.js.map +1 -1
  94. package/dist/icons/ResourceIconPicker.d.ts +6 -1
  95. package/dist/icons/ResourceIconPicker.d.ts.map +1 -1
  96. package/dist/icons/ResourceIconPicker.js +45 -6
  97. package/dist/icons/ResourceIconPicker.js.map +1 -1
  98. package/package.json +6 -4
  99. package/src/app/agentkit/react/components.interaction.spec.tsx +93 -0
  100. package/src/app/agentkit/react/components.spec.tsx +4 -5
  101. package/src/app/agentkit/react/components.tsx +51 -8
  102. package/src/app/agentkit/react/composer-context.spec.tsx +7 -0
  103. package/src/app/agentkit/react/lifecycle.spec.tsx +42 -0
  104. package/src/app/chat/AgentKitAssistantChat.spec.tsx +151 -11
  105. package/src/app/chat/AgentKitAssistantChat.tsx +110 -28
  106. package/src/app/chat/AgentPanel.header.spec.ts +59 -22
  107. package/src/app/chat/AgentSidebar.tsx +2 -0
  108. package/src/app/chat/FileStorageSetupPopover.tsx +2 -2
  109. package/src/app/chat/MultiTabAssistantChat.messageCount.spec.tsx +3 -1
  110. package/src/app/chat/MultiTabAssistantChat.spec.tsx +8 -4
  111. package/src/app/chat/MultiTabAssistantChat.tsx +1 -1
  112. package/src/app/chat/agentkit-chat/action-widget.spec.tsx +5 -17
  113. package/src/app/chat/agentkit-chat/connections.spec.tsx +137 -0
  114. package/src/app/chat/agentkit-chat/connections.ts +2 -37
  115. package/src/app/chat/agentkit-chat/history.tsx +23 -8
  116. package/src/app/chat/chat/history-types.ts +1 -1
  117. package/src/app/chat/chat/tool-call-display.spec.tsx +2 -2
  118. package/src/app/chat/chat/widgets/record-change-widget.spec.tsx +20 -29
  119. package/src/app/chat/chat-first/copy.ts +1 -133
  120. package/src/app/chat/chat-first-copy.ts +133 -0
  121. package/src/app/chat/composer/use-agentkit-capabilities.hardening.spec.tsx +3 -2
  122. package/src/app/feature-flags/FeatureFlagsPanel.tsx +1 -0
  123. package/src/app/i18n/catalogs/ar-SA.ts +2 -1
  124. package/src/app/i18n/catalogs/de-DE.ts +3 -1
  125. package/src/app/i18n/catalogs/en-US.ts +2 -1
  126. package/src/app/i18n/catalogs/es-ES.ts +2 -1
  127. package/src/app/i18n/catalogs/fr-FR.ts +3 -1
  128. package/src/app/i18n/catalogs/hi-IN.ts +2 -1
  129. package/src/app/i18n/catalogs/ja-JP.ts +2 -1
  130. package/src/app/i18n/catalogs/ko-KR.ts +2 -1
  131. package/src/app/i18n/catalogs/pt-BR.ts +3 -1
  132. package/src/app/i18n/catalogs/zh-CN.ts +2 -1
  133. package/src/app/i18n/catalogs/zh-TW.ts +2 -1
  134. package/src/app/integrations/IntegrationDetailPage.spec.tsx +1 -1
  135. package/src/app/labs/LabsSettings.tsx +46 -10
  136. package/src/app/observability/ThumbsFeedback.spec.tsx +15 -0
  137. package/src/app/onboarding/FirstRunOnboarding.spec.tsx +4 -0
  138. package/src/app/org/OrgGeneralSection.tsx +32 -22
  139. package/src/app/org/OrgSwitcher.tsx +3 -2
  140. package/src/app/org/workspace-app-links.spec.ts +4 -0
  141. package/src/app/org/workspace-app-links.ts +8 -1
  142. package/src/app/resources/McpAgentKitConnectionRequest.spec.tsx +371 -2
  143. package/src/app/resources/McpAgentKitConnectionRequest.tsx +171 -11
  144. package/src/app/settings/SettingsTabsPage.spec.tsx +4 -2
  145. package/src/app/settings/shell/SettingsShell.spec.tsx +14 -8
  146. package/src/app/settings/shell/pages/app-group-pages.spec.tsx +35 -5
  147. package/src/app/settings/shell/pages/resource-pages.spec.tsx +3 -0
  148. package/src/app/settings/usage/UsagePage.spec.tsx +3 -3
  149. package/src/app/shared/lazy-chunk-error-boundary.spec.tsx +2 -0
  150. package/src/app-shell/sidebar-overflow.spec.tsx +4 -1
  151. package/src/composer/PromptComposer.tsx +7 -0
  152. package/src/composer/TiptapComposer.spec.ts +26 -4
  153. package/src/composer/TiptapComposer.tsx +12 -3
  154. package/src/design-tweaks/visual-style-controls.spec.tsx +38 -0
  155. package/src/editor/useCollabReconcile.concurrent.spec.ts +2 -1
  156. package/src/icons/ResourceIconPicker.spec.tsx +270 -0
  157. package/src/icons/ResourceIconPicker.tsx +72 -4
@@ -2,16 +2,16 @@
2
2
 
3
3
  import { readFileSync } from "node:fs";
4
4
 
5
+ import {
6
+ DropdownMenu,
7
+ DropdownMenuContent,
8
+ DropdownMenuTrigger,
9
+ } from "@agent-native/toolkit/ui/dropdown-menu";
5
10
  import React, { act } from "react";
6
11
  import { createRoot, type Root } from "react-dom/client";
7
12
  import { MemoryRouter, useLocation } from "react-router";
8
13
  import { describe, expect, it, vi } from "vitest";
9
14
 
10
- import {
11
- DropdownMenu,
12
- DropdownMenuContent,
13
- DropdownMenuTrigger,
14
- } from "../../ui/dropdown-menu.js";
15
15
  import {
16
16
  AgentChatSurface,
17
17
  AgentPanelFullViewMenuItem,
@@ -51,6 +51,13 @@ import {
51
51
  preloadAgentChatSurface,
52
52
  } from "./AgentSidebar.js";
53
53
 
54
+ function readSource(
55
+ path: string,
56
+ _options?: "utf8" | { encoding: "utf8" },
57
+ ): string {
58
+ return readFileSync(path, "utf8").replace(/\r\n/g, "\n");
59
+ }
60
+
54
61
  describe("AgentPanel compatibility exports", () => {
55
62
  it("preserves the legacy sidebar entry point", () => {
56
63
  expect(LegacyAgentSidebar).toBe(AgentSidebar);
@@ -60,7 +67,7 @@ describe("AgentPanel compatibility exports", () => {
60
67
  });
61
68
 
62
69
  it("uses a stable-ref link in the full-view menu item", () => {
63
- const source = readFileSync("src/app/chat/AgentPanel.tsx", "utf8").replace(
70
+ const source = readSource("src/app/chat/AgentPanel.tsx", "utf8").replace(
64
71
  /\s+/g,
65
72
  " ",
66
73
  );
@@ -150,7 +157,7 @@ describe("AgentPanel fullscreen menu", () => {
150
157
 
151
158
  describe("AgentPanel suggestion placement", () => {
152
159
  it("forwards explicit placement and defaults to context chips", () => {
153
- const source = readFileSync("src/app/chat/AgentPanel.tsx", {
160
+ const source = readSource("src/app/chat/AgentPanel.tsx", {
154
161
  encoding: "utf8",
155
162
  });
156
163
 
@@ -323,7 +330,7 @@ describe("AgentPanel header tab visibility", () => {
323
330
  });
324
331
 
325
332
  it("exposes page header composition without moving it into app chrome", () => {
326
- const source = readFileSync("src/app/chat/AgentPanel.tsx", "utf8");
333
+ const source = readSource("src/app/chat/AgentPanel.tsx", "utf8");
327
334
 
328
335
  expect(source).toContain('data-agent-page-chat-header=""');
329
336
  expect(source).toContain("pageHeaderLeadingSlot");
@@ -699,7 +706,7 @@ describe("AgentPanel Integrations link", () => {
699
706
  });
700
707
 
701
708
  it("sits right after Open full view and shares its separator", () => {
702
- const source = readFileSync("src/app/chat/AgentPanel.tsx", {
709
+ const source = readSource("src/app/chat/AgentPanel.tsx", {
703
710
  encoding: "utf8",
704
711
  });
705
712
  const overflowMenu = source.slice(
@@ -820,6 +827,36 @@ describe("AgentSidebar composer focus", () => {
820
827
  }
821
828
  });
822
829
 
830
+ it("stops retrying when the document is torn down", () => {
831
+ vi.useFakeTimers();
832
+ const previousRequestAnimationFrame = window.requestAnimationFrame;
833
+ const frames: Array<FrameRequestCallback> = [];
834
+ const panel = document.createElement("div");
835
+ panel.className = "agent-sidebar-panel";
836
+ panel.dataset.agentSidebarState = "open";
837
+ document.body.appendChild(panel);
838
+
839
+ window.requestAnimationFrame = ((callback: FrameRequestCallback) => {
840
+ frames.push(callback);
841
+ return frames.length;
842
+ }) as typeof window.requestAnimationFrame;
843
+
844
+ try {
845
+ focusAgentChat();
846
+ frames[0]!(0);
847
+ vi.stubGlobal("document", undefined);
848
+
849
+ expect(() => vi.advanceTimersByTime(50)).not.toThrow();
850
+ expect(vi.getTimerCount()).toBe(0);
851
+ } finally {
852
+ vi.unstubAllGlobals();
853
+ window.requestAnimationFrame = previousRequestAnimationFrame;
854
+ panel.remove();
855
+ vi.clearAllTimers();
856
+ vi.useRealTimers();
857
+ }
858
+ });
859
+
823
860
  it("focuses a frame-owned composer", () => {
824
861
  const previousRequestAnimationFrame = window.requestAnimationFrame;
825
862
  const frames: Array<FrameRequestCallback> = [];
@@ -914,10 +951,10 @@ describe("AgentPanel header overflow actions", () => {
914
951
  });
915
952
 
916
953
  it("keeps width and full-view actions out of the icon row", () => {
917
- const source = readFileSync("src/app/chat/AgentPanel.tsx", {
954
+ const source = readSource("src/app/chat/AgentPanel.tsx", {
918
955
  encoding: "utf8",
919
956
  });
920
- const sidebarSource = readFileSync("src/app/chat/AgentSidebar.tsx", {
957
+ const sidebarSource = readSource("src/app/chat/AgentSidebar.tsx", {
921
958
  encoding: "utf8",
922
959
  });
923
960
  const headerActions = source.slice(
@@ -957,7 +994,7 @@ describe("AgentPanel header overflow actions", () => {
957
994
  });
958
995
 
959
996
  it("keeps the overflow menu scrollable within the viewport", () => {
960
- const source = readFileSync("src/app/chat/AgentPanel.tsx", {
997
+ const source = readSource("src/app/chat/AgentPanel.tsx", {
961
998
  encoding: "utf8",
962
999
  });
963
1000
  const overflowMenu = source.slice(
@@ -972,7 +1009,7 @@ describe("AgentPanel header overflow actions", () => {
972
1009
  });
973
1010
 
974
1011
  it("offers sharing from the sidebar overflow for an active chat", () => {
975
- const source = readFileSync("src/app/chat/AgentPanel.tsx", {
1012
+ const source = readSource("src/app/chat/AgentPanel.tsx", {
976
1013
  encoding: "utf8",
977
1014
  });
978
1015
  const overflowMenu = source.slice(
@@ -992,7 +1029,7 @@ describe("AgentPanel header overflow actions", () => {
992
1029
  });
993
1030
 
994
1031
  it("keeps chat headers persistent while switching app surfaces", () => {
995
- const source = readFileSync("src/app/chat/AgentPanel.tsx", {
1032
+ const source = readSource("src/app/chat/AgentPanel.tsx", {
996
1033
  encoding: "utf8",
997
1034
  });
998
1035
 
@@ -1002,10 +1039,10 @@ describe("AgentPanel header overflow actions", () => {
1002
1039
  });
1003
1040
 
1004
1041
  it("supports a persistent two-state sidebar toggle", () => {
1005
- const source = readFileSync("src/app/chat/AgentSidebar.tsx", {
1042
+ const source = readSource("src/app/chat/AgentSidebar.tsx", {
1006
1043
  encoding: "utf8",
1007
1044
  });
1008
- const panelSource = readFileSync("src/app/chat/AgentPanel.tsx", {
1045
+ const panelSource = readSource("src/app/chat/AgentPanel.tsx", {
1009
1046
  encoding: "utf8",
1010
1047
  });
1011
1048
 
@@ -1021,7 +1058,7 @@ describe("AgentPanel header overflow actions", () => {
1021
1058
  });
1022
1059
 
1023
1060
  it("keeps host CLI tabs mounted while chat is active", () => {
1024
- const source = readFileSync("src/app/chat/AgentPanel.tsx", {
1061
+ const source = readSource("src/app/chat/AgentPanel.tsx", {
1025
1062
  encoding: "utf8",
1026
1063
  });
1027
1064
 
@@ -1035,7 +1072,7 @@ describe("AgentPanel header overflow actions", () => {
1035
1072
  });
1036
1073
 
1037
1074
  it("only shows tabs for the active desktop surface", () => {
1038
- const source = readFileSync("src/app/chat/AgentPanel.tsx", {
1075
+ const source = readSource("src/app/chat/AgentPanel.tsx", {
1039
1076
  encoding: "utf8",
1040
1077
  });
1041
1078
 
@@ -1046,7 +1083,7 @@ describe("AgentPanel header overflow actions", () => {
1046
1083
 
1047
1084
  describe("AgentSidebar wide drawer layout", () => {
1048
1085
  it("can disable the panel without unmounting the app surface", () => {
1049
- const source = readFileSync("src/app/chat/AgentSidebar.tsx", {
1086
+ const source = readSource("src/app/chat/AgentSidebar.tsx", {
1050
1087
  encoding: "utf8",
1051
1088
  });
1052
1089
 
@@ -1058,7 +1095,7 @@ describe("AgentSidebar wide drawer layout", () => {
1058
1095
  });
1059
1096
 
1060
1097
  it("does not reserve the drawer placeholder after the panel closes", () => {
1061
- const source = readFileSync("src/app/chat/AgentSidebar.tsx", {
1098
+ const source = readSource("src/app/chat/AgentSidebar.tsx", {
1062
1099
  encoding: "utf8",
1063
1100
  });
1064
1101
  const placeholderStart = source.indexOf("const drawerPlaceholder");
@@ -1083,7 +1120,7 @@ describe("AgentChatSurface chrome defaults", () => {
1083
1120
  });
1084
1121
 
1085
1122
  it("keeps settings out of every chat surface", () => {
1086
- const source = readFileSync("src/app/chat/AgentPanel.tsx", {
1123
+ const source = readSource("src/app/chat/AgentPanel.tsx", {
1087
1124
  encoding: "utf8",
1088
1125
  });
1089
1126
 
@@ -1117,7 +1154,7 @@ describe("AgentChatSurface chrome defaults", () => {
1117
1154
 
1118
1155
  describe("AgentPanel stale lazy chunk recovery", () => {
1119
1156
  it("uses the guarded reload path before the panel reset fallback", () => {
1120
- const source = readFileSync("src/app/chat/AgentPanel.tsx", {
1157
+ const source = readSource("src/app/chat/AgentPanel.tsx", {
1121
1158
  encoding: "utf8",
1122
1159
  });
1123
1160
  const componentDidCatch = source.slice(
@@ -1331,6 +1331,8 @@ export function focusAgentChat() {
1331
1331
 
1332
1332
  function focusAgentChatComposer() {
1333
1333
  const focusComposer = (attempt = 0) => {
1334
+ if (typeof window === "undefined" || typeof document === "undefined")
1335
+ return;
1334
1336
  const panel = document.querySelector(
1335
1337
  ".agent-sidebar-panel[data-agent-sidebar-state='open'], " +
1336
1338
  ".agent-frame-sidebar[data-agent-frame-sidebar-state='open']",
@@ -8,8 +8,8 @@ import {
8
8
  import { IconCloudUpload } from "@tabler/icons-react";
9
9
  import { useRef, type RefObject } from "react";
10
10
 
11
- import { BuilderConnectPopover } from "../settings/index.js";
12
- import { BuilderConnectCard } from "../setup-connections/index.js";
11
+ import { DeferredBuilderConnectPopover as BuilderConnectPopover } from "../settings/deferred-builder-connect-popover.js";
12
+ import { BuilderConnectCard } from "../setup-connections/BuilderConnectCard.js";
13
13
 
14
14
  type FileStorageSetupPopoverCommonProps = {
15
15
  open: boolean;
@@ -1,6 +1,8 @@
1
1
  import { describe, expect, it, vi } from "vitest";
2
2
 
3
- import MultiTabSource from "./MultiTabAssistantChat.tsx?raw";
3
+ import rawMultiTabSource from "./MultiTabAssistantChat.tsx?raw";
4
+
5
+ const MultiTabSource = rawMultiTabSource.replace(/\r\n/g, "\n");
4
6
 
5
7
  describe("MultiTabAssistantChat message count", () => {
6
8
  it("forwards the host callback instead of only counting tabs", () => {
@@ -210,9 +210,11 @@ const modelCatalogMocks = vi.hoisted(() => ({
210
210
  load: null as null | (() => Promise<unknown>),
211
211
  }));
212
212
 
213
- vi.mock("@agent-native/core/client/hooks", async (importOriginal) => {
213
+ vi.mock("@agent-native/core/client/use-action", async (importOriginal) => {
214
214
  const actual =
215
- await importOriginal<typeof import("@agent-native/core/client/hooks")>();
215
+ await importOriginal<
216
+ typeof import("@agent-native/core/client/use-action")
217
+ >();
216
218
  return { ...actual, ...actionMocks };
217
219
  });
218
220
 
@@ -433,6 +435,8 @@ describe("MultiTabAssistantChat postMessage bridge", () => {
433
435
  beforeEach(async () => {
434
436
  vi.stubGlobal("IS_REACT_ACT_ENVIRONMENT", true);
435
437
  resetThreadMocks();
438
+ actionMocks.callAction.mockReset();
439
+ actionMocks.callAction.mockResolvedValue(null as never);
436
440
  ensureLocalStorage();
437
441
  vi.stubGlobal(
438
442
  "fetch",
@@ -3345,7 +3349,7 @@ describe("MultiTabAssistantChat page overlay", () => {
3345
3349
  ).not.toBeNull();
3346
3350
  });
3347
3351
 
3348
- it("reserves the page top bar even when its actions are temporarily empty", async () => {
3352
+ it("reserves one page-header height when its actions are temporarily empty", async () => {
3349
3353
  await act(async () => {
3350
3354
  root.render(
3351
3355
  <MultiTabAssistantChat
@@ -3359,7 +3363,7 @@ describe("MultiTabAssistantChat page overlay", () => {
3359
3363
  "[data-agent-page-chat-topbar]",
3360
3364
  );
3361
3365
  expect(topbar).not.toBeNull();
3362
- expect(topbar?.className).toContain("pt-14");
3366
+ expect(topbar?.className).toContain("pt-12");
3363
3367
  });
3364
3368
  });
3365
3369
 
@@ -3090,7 +3090,7 @@ export function MultiTabAssistantChat({
3090
3090
  <div
3091
3091
  className={cn(
3092
3092
  "relative flex-1 flex flex-col min-h-0",
3093
- renderOverlay && "pt-14",
3093
+ renderOverlay && "pt-12",
3094
3094
  )}
3095
3095
  data-agent-page-chat-topbar={renderOverlay ? "" : undefined}
3096
3096
  data-agent-page-chat-scrolled={
@@ -11,10 +11,7 @@ import {
11
11
  ACTION_CHAT_UI_DATA_WIDGET_RENDERER,
12
12
  ACTION_CHAT_UI_RECORD_CHANGE_RENDERER,
13
13
  } from "@agent-native/core/action-ui";
14
- import {
15
- AgentNativeI18nProvider as CoreAgentNativeI18nProvider,
16
- type AgentNativeI18nProviderProps,
17
- } from "@agent-native/core/client/i18n";
14
+ import { AgentNativeI18nProvider } from "@agent-native/core/client/i18n";
18
15
  import {
19
16
  createDataInsightsWidgetResult,
20
17
  createDataTableWidgetResult,
@@ -32,16 +29,7 @@ import {
32
29
  } from "../chat/tool-render-registry.js";
33
30
  import { AgentKitActionWidget } from "./action-widget.js";
34
31
 
35
- const toolkitI18nCatalog = createToolkitI18nCatalog({ messages: {} });
36
-
37
- function AgentNativeI18nProvider(props: AgentNativeI18nProviderProps) {
38
- return (
39
- <CoreAgentNativeI18nProvider
40
- {...props}
41
- catalog={props.catalog ?? toolkitI18nCatalog}
42
- />
43
- );
44
- }
32
+ const catalog = createToolkitI18nCatalog({ messages: {} });
45
33
 
46
34
  describe("AgentKitActionWidget", () => {
47
35
  it("resolves the renderer with stored action args and structured result", async () => {
@@ -229,7 +217,7 @@ describe("AgentKitActionWidget", () => {
229
217
  try {
230
218
  await act(async () => {
231
219
  root.render(
232
- <AgentNativeI18nProvider persistPreference={false}>
220
+ <AgentNativeI18nProvider catalog={catalog} persistPreference={false}>
233
221
  <AgentKitProvider controller={client} threadId="thread-1">
234
222
  <>
235
223
  <AgentKitActionWidget value={formsWidget} threadId="thread-1" />
@@ -316,7 +304,7 @@ describe("AgentKitActionWidget", () => {
316
304
  try {
317
305
  await act(async () => {
318
306
  root.render(
319
- <AgentNativeI18nProvider persistPreference={false}>
307
+ <AgentNativeI18nProvider catalog={catalog} persistPreference={false}>
320
308
  <AgentKitProvider controller={client} threadId="thread-1">
321
309
  <>
322
310
  {widgets.map((widget) => (
@@ -399,7 +387,7 @@ describe("AgentKitActionWidget", () => {
399
387
  try {
400
388
  await act(async () => {
401
389
  root.render(
402
- <AgentNativeI18nProvider persistPreference={false}>
390
+ <AgentNativeI18nProvider catalog={catalog} persistPreference={false}>
403
391
  <AgentKitProvider controller={client} threadId="thread-1">
404
392
  {widgets.map((widget) => (
405
393
  <AgentKitActionWidget
@@ -1,6 +1,8 @@
1
1
  // @vitest-environment happy-dom
2
2
 
3
3
  import {
4
+ clearMcpConnectionResume,
5
+ getPendingMcpConnectionResume,
4
6
  notifyMcpConnectionComplete,
5
7
  saveMcpConnectionResume,
6
8
  } from "@agent-native/core/client/resources/mcp-connection-resume";
@@ -16,6 +18,7 @@ afterEach(() => {
16
18
  if (root) act(() => root?.unmount());
17
19
  root = undefined;
18
20
  window.sessionStorage.clear();
21
+ window.localStorage.clear();
19
22
  window.history.replaceState({}, "", "/chat/thread-1");
20
23
  });
21
24
 
@@ -78,4 +81,138 @@ describe("McpAgentKitConnectionResume", () => {
78
81
  );
79
82
  expect(onMessageResume).not.toHaveBeenCalled();
80
83
  });
84
+
85
+ it("shows a failure and keeps the saved request when resuming fails", async () => {
86
+ const target = { threadId: "thread-1", runId: "run-1", requestId: "req-1" };
87
+ saveMcpConnectionResume("Restore the request.", target);
88
+ const onResume = vi.fn().mockRejectedValue(new Error("offline"));
89
+ const container = document.createElement("div");
90
+ root = createRoot(container);
91
+
92
+ await act(async () => {
93
+ root?.render(<McpAgentKitConnectionResume onResume={onResume} />);
94
+ await Promise.resolve();
95
+ });
96
+
97
+ expect(container.querySelector('[role="alert"]')).not.toBeNull();
98
+ expect(getPendingMcpConnectionResume()).toMatchObject({
99
+ message: "Restore the request.",
100
+ agentKit: target,
101
+ });
102
+ });
103
+
104
+ it("resumes a completed popup after remount and continues when the run is gone", async () => {
105
+ window.history.replaceState({}, "", "/chat/thread-1");
106
+ const target = {
107
+ threadId: "thread-1",
108
+ runId: "run-1",
109
+ requestId: "req-1",
110
+ };
111
+ const completionId = "8d1bd7cf-419e-4ff0-8545-19dedb819154";
112
+ saveMcpConnectionResume("Restore the request.", target, completionId);
113
+ const onResume = vi
114
+ .fn()
115
+ .mockRejectedValue(new Error(`Unknown AgentKit run: ${target.runId}`));
116
+ const onMessageResume = vi.fn();
117
+ const container = document.createElement("div");
118
+ root = createRoot(container);
119
+
120
+ await act(async () => {
121
+ root?.render(
122
+ <McpAgentKitConnectionResume
123
+ onResume={onResume}
124
+ onMessageResume={onMessageResume}
125
+ />,
126
+ );
127
+ });
128
+ expect(onResume).not.toHaveBeenCalled();
129
+ expect(getPendingMcpConnectionResume()).toBeNull();
130
+
131
+ act(() => {
132
+ root?.unmount();
133
+ });
134
+ root = undefined;
135
+ window.localStorage.setItem(
136
+ `agent-native:mcp-connection-completion:${completionId}`,
137
+ "1",
138
+ );
139
+
140
+ const remountedContainer = document.createElement("div");
141
+ root = createRoot(remountedContainer);
142
+ await act(async () => {
143
+ root?.render(
144
+ <McpAgentKitConnectionResume
145
+ onResume={onResume}
146
+ onMessageResume={onMessageResume}
147
+ />,
148
+ );
149
+ await new Promise((resolve) => setTimeout(resolve, 0));
150
+ });
151
+
152
+ expect(onResume).toHaveBeenCalledOnce();
153
+ expect(onResume).toHaveBeenCalledWith(
154
+ target,
155
+ expect.objectContaining({ message: "Restore the request." }),
156
+ );
157
+ expect(onMessageResume).toHaveBeenCalledOnce();
158
+ expect(onMessageResume).toHaveBeenCalledWith(
159
+ expect.objectContaining({ message: "Restore the request." }),
160
+ );
161
+ expect(getPendingMcpConnectionResume()).toBeNull();
162
+ clearMcpConnectionResume();
163
+ });
164
+
165
+ it("keeps the saved request when the fallback chat submission fails", async () => {
166
+ const target = { threadId: "thread-1", runId: "run-1", requestId: "req-1" };
167
+ saveMcpConnectionResume("Restore the request.", target);
168
+ const onResume = vi
169
+ .fn()
170
+ .mockRejectedValue(new Error(`Unknown AgentKit run: ${target.runId}`));
171
+ const onMessageResume = vi.fn().mockRejectedValue(new Error("offline"));
172
+ const container = document.createElement("div");
173
+ root = createRoot(container);
174
+
175
+ await act(async () => {
176
+ root?.render(
177
+ <McpAgentKitConnectionResume
178
+ onResume={onResume}
179
+ onMessageResume={onMessageResume}
180
+ />,
181
+ );
182
+ await new Promise((resolve) => setTimeout(resolve, 0));
183
+ });
184
+
185
+ expect(onMessageResume).toHaveBeenCalledOnce();
186
+ expect(container.querySelector('[role="alert"]')).not.toBeNull();
187
+ expect(getPendingMcpConnectionResume()).toMatchObject({
188
+ message: "Restore the request.",
189
+ agentKit: target,
190
+ });
191
+ });
192
+
193
+ it("clears a stale failure alert after a later successful retry", async () => {
194
+ const target = { threadId: "thread-1", runId: "run-1", requestId: "req-1" };
195
+ saveMcpConnectionResume("Restore the request.", target);
196
+ const onResume = vi
197
+ .fn()
198
+ .mockRejectedValueOnce(new Error("offline"))
199
+ .mockResolvedValue(undefined);
200
+ const container = document.createElement("div");
201
+ root = createRoot(container);
202
+
203
+ await act(async () => {
204
+ root?.render(<McpAgentKitConnectionResume onResume={onResume} />);
205
+ await new Promise((resolve) => setTimeout(resolve, 0));
206
+ });
207
+ expect(container.querySelector('[role="alert"]')).not.toBeNull();
208
+
209
+ await act(async () => {
210
+ notifyMcpConnectionComplete();
211
+ await new Promise((resolve) => setTimeout(resolve, 0));
212
+ });
213
+
214
+ expect(onResume).toHaveBeenCalledTimes(2);
215
+ expect(container.querySelector('[role="alert"]')).toBeNull();
216
+ expect(getPendingMcpConnectionResume()).toBeNull();
217
+ });
81
218
  });
@@ -1,39 +1,4 @@
1
- import {
2
- addMcpConnectionCompleteListener,
3
- consumeMcpConnectionResume,
4
- } from "@agent-native/core/client/resources/mcp-connection-resume";
5
- import { useEffect, useRef } from "react";
6
-
7
- import {
1
+ export {
8
2
  McpAgentKitConnectionRequestCard,
9
- type McpAgentKitConnectionResumeProps,
3
+ McpAgentKitConnectionResume,
10
4
  } from "../../resources/index.js";
11
-
12
- export { McpAgentKitConnectionRequestCard };
13
-
14
- /** Consumes OAuth resumes once and routes them to the matching chat submit API. */
15
- export function McpAgentKitConnectionResume({
16
- onResume,
17
- onMessageResume,
18
- }: McpAgentKitConnectionResumeProps) {
19
- const onResumeRef = useRef(onResume);
20
- const onMessageResumeRef = useRef(onMessageResume);
21
- onResumeRef.current = onResume;
22
- onMessageResumeRef.current = onMessageResume;
23
-
24
- // Chat callbacks change with render state; one listener must own each stored resume.
25
- useEffect(() => {
26
- const resumePending = () => {
27
- const request = consumeMcpConnectionResume();
28
- if (!request) return;
29
- const operation = request.agentKit
30
- ? onResumeRef.current(request.agentKit, request)
31
- : onMessageResumeRef.current?.(request);
32
- void Promise.resolve(operation).catch(() => {});
33
- };
34
-
35
- resumePending();
36
- return addMcpConnectionCompleteListener(resumePending);
37
- }, []);
38
- return null;
39
- }
@@ -79,7 +79,7 @@ export interface AgentKitHistoryConfig<
79
79
  args?:
80
80
  | Record<string, unknown>
81
81
  | ((threadId: ThreadId) => Record<string, unknown>);
82
- getVersions: (result: TListResult) => readonly TVersion[];
82
+ getVersions: (result: TListResult) => readonly TVersion[] | null;
83
83
  };
84
84
  restore: {
85
85
  action: string;
@@ -108,6 +108,8 @@ export interface AgentKitHistoryConfig<
108
108
 
109
109
  export interface AgentKitHistoryContextValue {
110
110
  beginningVersion: AgentKitHistoryVersion | null;
111
+ historyLoadFailed: boolean;
112
+ isRetryingHistory: boolean;
111
113
  isBusy: boolean;
112
114
  isRestoring: boolean;
113
115
  isSubmissionInFlight: boolean;
@@ -117,6 +119,7 @@ export interface AgentKitHistoryContextValue {
117
119
  beginSubmission: () => Promise<(() => void) | null>;
118
120
  /** Wait until an in-flight restore has finished before mutating the host. */
119
121
  waitForRestore: () => Promise<void>;
122
+ retryHistory: () => void;
120
123
  findVersion: (
121
124
  message: AgentKitHistoryMessage,
122
125
  ) => AgentKitHistoryVersion | null;
@@ -711,15 +714,18 @@ export function AgentKitHistoryProvider<
711
714
  const currentThreadRef = useRef(threadId);
712
715
  const initializedRef = useRef(false);
713
716
 
714
- const versions = useMemo(() => {
715
- if (listQuery.data == null) return [];
717
+ const loadedVersions = useMemo(() => {
718
+ if (listQuery.data == null) return null;
716
719
  const result = history.list.getVersions(listQuery.data);
717
- return Array.isArray(result)
718
- ? result.filter((version): version is TVersion =>
719
- isAssistantChatHistoryVersion(version),
720
- )
721
- : [];
720
+ if (!Array.isArray(result)) return null;
721
+ return result.filter((version): version is TVersion =>
722
+ isAssistantChatHistoryVersion(version),
723
+ );
722
724
  }, [history.list, listQuery.data]);
725
+ const versions = loadedVersions ?? [];
726
+ const retryHistory = useCallback(() => {
727
+ void listQuery.refetch();
728
+ }, [listQuery.refetch]);
723
729
  const historyMessages = useMemo(
724
730
  () => getAgentKitHistoryMessages(threadId, thread, history.scope),
725
731
  [history.scope, thread, threadId],
@@ -918,6 +924,9 @@ export function AgentKitHistoryProvider<
918
924
  threadId,
919
925
  history.isEditable,
920
926
  ),
927
+ historyLoadFailed:
928
+ listQuery.isError || (listQuery.isSuccess && loadedVersions == null),
929
+ isRetryingHistory: listQuery.isFetching,
921
930
  isBusy:
922
931
  thread.activeRunIds.length > 0 ||
923
932
  isRestoring ||
@@ -930,6 +939,7 @@ export function AgentKitHistoryProvider<
930
939
  findVersion,
931
940
  toHistoryMessage,
932
941
  restoreVersion,
942
+ retryHistory,
933
943
  }),
934
944
  [
935
945
  findVersion,
@@ -937,8 +947,13 @@ export function AgentKitHistoryProvider<
937
947
  beginSubmission,
938
948
  waitForRestore,
939
949
  history.isEditable,
950
+ listQuery.isError,
951
+ listQuery.isFetching,
952
+ listQuery.isSuccess,
953
+ loadedVersions,
940
954
  isRestoring,
941
955
  restoreVersion,
956
+ retryHistory,
942
957
  submissionsInFlight,
943
958
  submissionCountsByThread,
944
959
  thread.activeRunIds.length,
@@ -38,7 +38,7 @@ export interface AssistantChatHistoryConfig<
38
38
  args?:
39
39
  | Record<string, unknown>
40
40
  | ((threadId?: string) => Record<string, unknown>);
41
- getVersions: (result: TListResult) => readonly TVersion[];
41
+ getVersions: (result: TListResult) => readonly TVersion[] | null;
42
42
  };
43
43
  restore: {
44
44
  action: string;
@@ -990,7 +990,7 @@ describe("ToolCallDisplay native renderers", () => {
990
990
  await act(async () => {
991
991
  root.render(
992
992
  <AgentNativeI18nProvider
993
- catalog={{
993
+ catalog={createToolkitI18nCatalog({
994
994
  sourceLocale: "en-US",
995
995
  messages: {
996
996
  agentChat: {
@@ -1001,7 +1001,7 @@ describe("ToolCallDisplay native renderers", () => {
1001
1001
  },
1002
1002
  },
1003
1003
  },
1004
- }}
1004
+ })}
1005
1005
  initialLocale="en-US"
1006
1006
  initialPreference="en-US"
1007
1007
  persistPreference={false}