@agent-native/toolkit 0.205.0 → 0.206.0-nightly-20261009011246

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 (143) hide show
  1. package/dist/app/agentkit/react/components.d.ts.map +1 -1
  2. package/dist/app/agentkit/react/components.js +17 -9
  3. package/dist/app/agentkit/react/components.js.map +1 -1
  4. package/dist/app/auth/AuthPage.d.ts.map +1 -1
  5. package/dist/app/auth/AuthPage.js +11 -0
  6. package/dist/app/auth/AuthPage.js.map +1 -1
  7. package/dist/app/chat/AgentKitAssistantChat.d.ts +2 -0
  8. package/dist/app/chat/AgentKitAssistantChat.d.ts.map +1 -1
  9. package/dist/app/chat/AgentKitAssistantChat.js +24 -14
  10. package/dist/app/chat/AgentKitAssistantChat.js.map +1 -1
  11. package/dist/app/chat/AgentPanel.d.ts.map +1 -1
  12. package/dist/app/chat/AgentPanel.js +3 -2
  13. package/dist/app/chat/AgentPanel.js.map +1 -1
  14. package/dist/app/chat/MultiTabAssistantChat.d.ts +4 -1
  15. package/dist/app/chat/MultiTabAssistantChat.d.ts.map +1 -1
  16. package/dist/app/chat/MultiTabAssistantChat.js +61 -3
  17. package/dist/app/chat/MultiTabAssistantChat.js.map +1 -1
  18. package/dist/app/chat/agentkit-chat/approval-card.d.ts.map +1 -1
  19. package/dist/app/chat/agentkit-chat/approval-card.js +2 -1
  20. package/dist/app/chat/agentkit-chat/approval-card.js.map +1 -1
  21. package/dist/app/chat/chat/run-recovery.d.ts.map +1 -1
  22. package/dist/app/chat/chat/run-recovery.js +74 -8
  23. package/dist/app/chat/chat/run-recovery.js.map +1 -1
  24. package/dist/app/chat/chat/tool-call-display.d.ts.map +1 -1
  25. package/dist/app/chat/chat/tool-call-display.js +6 -4
  26. package/dist/app/chat/chat/tool-call-display.js.map +1 -1
  27. package/dist/app/chat/chat-first/ChatFirstAgentActivityPanel.d.ts.map +1 -1
  28. package/dist/app/chat/chat-first/ChatFirstAgentActivityPanel.js +2 -1
  29. package/dist/app/chat/chat-first/ChatFirstAgentActivityPanel.js.map +1 -1
  30. package/dist/app/chat/conversation/AgentConversation.d.ts.map +1 -1
  31. package/dist/app/chat/conversation/AgentConversation.js +10 -7
  32. package/dist/app/chat/conversation/AgentConversation.js.map +1 -1
  33. package/dist/app/chat/fallback-chat-title.d.ts.map +1 -1
  34. package/dist/app/chat/fallback-chat-title.js +2 -4
  35. package/dist/app/chat/fallback-chat-title.js.map +1 -1
  36. package/dist/app/chat/mcp-apps/McpAppRenderer.d.ts.map +1 -1
  37. package/dist/app/chat/mcp-apps/McpAppRenderer.js +3 -2
  38. package/dist/app/chat/mcp-apps/McpAppRenderer.js.map +1 -1
  39. package/dist/app/chat/session-replay-privacy.d.ts +7 -0
  40. package/dist/app/chat/session-replay-privacy.d.ts.map +1 -0
  41. package/dist/app/chat/session-replay-privacy.js +6 -0
  42. package/dist/app/chat/session-replay-privacy.js.map +1 -0
  43. package/dist/app/extensions/ExtensionViewer.d.ts +4 -1
  44. package/dist/app/extensions/ExtensionViewer.d.ts.map +1 -1
  45. package/dist/app/extensions/ExtensionViewer.js +3 -3
  46. package/dist/app/extensions/ExtensionViewer.js.map +1 -1
  47. package/dist/app/extensions/ExtensionViewerPage.d.ts +4 -1
  48. package/dist/app/extensions/ExtensionViewerPage.d.ts.map +1 -1
  49. package/dist/app/extensions/ExtensionViewerPage.js +2 -2
  50. package/dist/app/extensions/ExtensionViewerPage.js.map +1 -1
  51. package/dist/app/onboarding/FirstRunOnboarding.d.ts.map +1 -1
  52. package/dist/app/onboarding/FirstRunOnboarding.js +53 -12
  53. package/dist/app/onboarding/FirstRunOnboarding.js.map +1 -1
  54. package/dist/app/onboarding/OnboardingPanel.d.ts.map +1 -1
  55. package/dist/app/onboarding/OnboardingPanel.js +14 -4
  56. package/dist/app/onboarding/OnboardingPanel.js.map +1 -1
  57. package/dist/app/resources/ResourceEditor.d.ts +4 -1
  58. package/dist/app/resources/ResourceEditor.d.ts.map +1 -1
  59. package/dist/app/resources/ResourceEditor.js +18 -7
  60. package/dist/app/resources/ResourceEditor.js.map +1 -1
  61. package/dist/app/resources/ResourcesPanel.d.ts +4 -1
  62. package/dist/app/resources/ResourcesPanel.d.ts.map +1 -1
  63. package/dist/app/resources/ResourcesPanel.js +18 -18
  64. package/dist/app/resources/ResourcesPanel.js.map +1 -1
  65. package/dist/app/resources/custom-agent-model-options.d.ts +16 -0
  66. package/dist/app/resources/custom-agent-model-options.d.ts.map +1 -0
  67. package/dist/app/resources/custom-agent-model-options.js +53 -0
  68. package/dist/app/resources/custom-agent-model-options.js.map +1 -0
  69. package/dist/app/settings/SettingsPanel.d.ts.map +1 -1
  70. package/dist/app/settings/SettingsPanel.js +34 -3
  71. package/dist/app/settings/SettingsPanel.js.map +1 -1
  72. package/dist/app/settings/SettingsTabsPage.d.ts.map +1 -1
  73. package/dist/app/settings/SettingsTabsPage.js +2 -0
  74. package/dist/app/settings/SettingsTabsPage.js.map +1 -1
  75. package/dist/app/settings/api-keys/ApiKeyDialogs.d.ts.map +1 -1
  76. package/dist/app/settings/api-keys/ApiKeyDialogs.js +49 -9
  77. package/dist/app/settings/api-keys/ApiKeyDialogs.js.map +1 -1
  78. package/dist/app/settings/api-keys/ApiKeysSettingsPage.js +2 -1
  79. package/dist/app/settings/api-keys/ApiKeysSettingsPage.js.map +1 -1
  80. package/dist/app/settings/model/ModelSettingsPage.d.ts.map +1 -1
  81. package/dist/app/settings/model/ModelSettingsPage.js +43 -9
  82. package/dist/app/settings/model/ModelSettingsPage.js.map +1 -1
  83. package/dist/app/settings/model/ProviderDialog.d.ts +4 -0
  84. package/dist/app/settings/model/ProviderDialog.d.ts.map +1 -1
  85. package/dist/app/settings/model/ProviderDialog.js +107 -11
  86. package/dist/app/settings/model/ProviderDialog.js.map +1 -1
  87. package/dist/app/settings/useBuilderStatus.d.ts.map +1 -1
  88. package/dist/app/settings/useBuilderStatus.js +13 -10
  89. package/dist/app/settings/useBuilderStatus.js.map +1 -1
  90. package/dist/composer/TiptapComposer.d.ts.map +1 -1
  91. package/dist/composer/TiptapComposer.js +51 -13
  92. package/dist/composer/TiptapComposer.js.map +1 -1
  93. package/package.json +4 -4
  94. package/src/app/agentkit/react/components.interaction.spec.tsx +229 -1
  95. package/src/app/agentkit/react/components.spec.tsx +42 -1
  96. package/src/app/agentkit/react/components.tsx +46 -12
  97. package/src/app/auth/AuthPage.tsx +13 -0
  98. package/src/app/chat/AgentKitAssistantChat.spec.tsx +116 -5
  99. package/src/app/chat/AgentKitAssistantChat.tsx +41 -20
  100. package/src/app/chat/AgentPanel.tsx +5 -1
  101. package/src/app/chat/MultiTabAssistantChat.spec.tsx +152 -7
  102. package/src/app/chat/MultiTabAssistantChat.tsx +71 -4
  103. package/src/app/chat/agentkit-chat/approval-card.spec.tsx +17 -0
  104. package/src/app/chat/agentkit-chat/approval-card.tsx +2 -0
  105. package/src/app/chat/chat/run-recovery.spec.tsx +354 -9
  106. package/src/app/chat/chat/run-recovery.tsx +178 -10
  107. package/src/app/chat/chat/tool-call-display.spec.tsx +77 -0
  108. package/src/app/chat/chat/tool-call-display.tsx +23 -6
  109. package/src/app/chat/chat-first/ChatFirstAgentActivityPanel.tsx +2 -1
  110. package/src/app/chat/conversation/AgentConversation.spec.tsx +190 -1
  111. package/src/app/chat/conversation/AgentConversation.tsx +32 -8
  112. package/src/app/chat/fallback-chat-title.spec.ts +44 -0
  113. package/src/app/chat/fallback-chat-title.ts +3 -4
  114. package/src/app/chat/mcp-apps/McpAppRenderer.spec.ts +24 -0
  115. package/src/app/chat/mcp-apps/McpAppRenderer.tsx +10 -2
  116. package/src/app/chat/session-replay-privacy.spec.ts +14 -0
  117. package/src/app/chat/session-replay-privacy.ts +5 -0
  118. package/src/app/extensions/ExtensionViewer.tsx +15 -3
  119. package/src/app/extensions/ExtensionViewerPage.tsx +7 -3
  120. package/src/app/onboarding/FirstRunOnboarding.spec.tsx +252 -2
  121. package/src/app/onboarding/FirstRunOnboarding.tsx +56 -9
  122. package/src/app/onboarding/OnboardingPanel.save-scope.spec.tsx +1 -0
  123. package/src/app/onboarding/OnboardingPanel.tsx +15 -3
  124. package/src/app/resources/ResourceEditor.spec.tsx +240 -5
  125. package/src/app/resources/ResourceEditor.tsx +57 -10
  126. package/src/app/resources/ResourcesPanel.spec.ts +86 -4
  127. package/src/app/resources/ResourcesPanel.tsx +49 -16
  128. package/src/app/resources/custom-agent-model-options.ts +92 -0
  129. package/src/app/settings/SettingsPanel.model-defaults.spec.tsx +26 -13
  130. package/src/app/settings/SettingsPanel.tsx +35 -3
  131. package/src/app/settings/SettingsTabsPage.spec.tsx +97 -0
  132. package/src/app/settings/SettingsTabsPage.tsx +2 -0
  133. package/src/app/settings/api-keys/ApiKeyDialogs.tsx +84 -22
  134. package/src/app/settings/api-keys/ApiKeysSettingsPage.spec.tsx +229 -1
  135. package/src/app/settings/api-keys/ApiKeysSettingsPage.tsx +3 -2
  136. package/src/app/settings/model/ModelSettingsPage.spec.tsx +99 -9
  137. package/src/app/settings/model/ModelSettingsPage.tsx +79 -11
  138. package/src/app/settings/model/ProviderDialog.spec.tsx +192 -1
  139. package/src/app/settings/model/ProviderDialog.tsx +225 -29
  140. package/src/app/settings/useBuilderStatus.spec.tsx +49 -0
  141. package/src/app/settings/useBuilderStatus.ts +16 -10
  142. package/src/composer/TiptapComposer.spec.ts +97 -2
  143. package/src/composer/TiptapComposer.tsx +67 -13
@@ -2,12 +2,43 @@
2
2
 
3
3
  import React, { act } from "react";
4
4
  import { createRoot, type Root } from "react-dom/client";
5
- import { createMemoryRouter, RouterProvider } from "react-router";
5
+ import { createMemoryRouter, RouterProvider, useLocation } from "react-router";
6
6
  import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
7
7
 
8
8
  const clipboardMock = vi.hoisted(() => ({
9
9
  writeClipboardText: vi.fn(),
10
10
  }));
11
+ const setupTelemetryMock = vi.hoisted(() => vi.fn());
12
+ const builderConnectMock = vi.hoisted(() => ({
13
+ onConnect: undefined as ((provisionAccount: boolean) => void) | undefined,
14
+ }));
15
+
16
+ function SetupTrackingFlowProbe() {
17
+ const { state } = useLocation();
18
+ const trackingFlow = (state as { providerSetupTrackingFlow?: string } | null)
19
+ ?.providerSetupTrackingFlow;
20
+ return <div data-testid="setup-tracking-flow">{trackingFlow}</div>;
21
+ }
22
+ const builderFlowMock = vi.hoisted(() => ({
23
+ onConnected: undefined as
24
+ | ((state: { orgName: string | null }) => void | Promise<void>)
25
+ | undefined,
26
+ state: {
27
+ configured: false,
28
+ connecting: false,
29
+ error: null as string | null,
30
+ errorKind: null as "status-read" | "connection" | "launch" | null,
31
+ terminalError: null as string | null,
32
+ statusUnavailable: false,
33
+ statusReadSettledCount: 0,
34
+ hasFetchedStatus: true,
35
+ },
36
+ start: vi.fn(),
37
+ }));
38
+
39
+ vi.mock("@agent-native/core/client/onboarding/use-onboarding", () => ({
40
+ trackOnboardingEvent: setupTelemetryMock,
41
+ }));
11
42
 
12
43
  const referralInfoQueryMock = vi.hoisted(() => ({ data: null as unknown }));
13
44
 
@@ -155,19 +186,26 @@ vi.mock("@agent-native/core/client/i18n", () => ({
155
186
  vi.mock("../../settings/BuilderConnectPopover.js", () => {
156
187
  deferredUiModuleLoads.builderConnectPopover = true;
157
188
  return {
158
- BuilderConnectPopover: ({ children }: { children: React.ReactNode }) =>
189
+ BuilderConnectPopover: ({
159
190
  children,
191
+ onConnect,
192
+ }: {
193
+ children: React.ReactNode;
194
+ onConnect?: (provisionAccount: boolean) => void;
195
+ }) => {
196
+ builderConnectMock.onConnect = onConnect;
197
+ return children;
198
+ },
160
199
  };
161
200
  });
162
201
 
163
202
  vi.mock("../../settings/useBuilderStatus.js", () => ({
164
- useBuilderConnectFlow: () => ({
165
- configured: false,
166
- connecting: false,
167
- error: null,
168
- hasFetchedStatus: true,
169
- start: vi.fn(),
170
- }),
203
+ useBuilderConnectFlow: (options?: {
204
+ onConnected?: (state: { orgName: string | null }) => void | Promise<void>;
205
+ }) => {
206
+ builderFlowMock.onConnected = options?.onConnected;
207
+ return { ...builderFlowMock.state, start: builderFlowMock.start };
208
+ },
171
209
  }));
172
210
 
173
211
  import { AgentNativeI18nProvider } from "@agent-native/core/client/i18n";
@@ -189,6 +227,20 @@ describe("run recovery surfaces", () => {
189
227
  document.body.appendChild(container);
190
228
  root = createRoot(container);
191
229
  clipboardMock.writeClipboardText.mockReset();
230
+ setupTelemetryMock.mockReset();
231
+ builderConnectMock.onConnect = undefined;
232
+ builderFlowMock.onConnected = undefined;
233
+ builderFlowMock.start.mockReset();
234
+ Object.assign(builderFlowMock.state, {
235
+ configured: false,
236
+ connecting: false,
237
+ error: null,
238
+ errorKind: null,
239
+ terminalError: null,
240
+ statusUnavailable: false,
241
+ statusReadSettledCount: 0,
242
+ hasFetchedStatus: true,
243
+ });
192
244
  referralInfoQueryMock.data = null;
193
245
  });
194
246
 
@@ -201,6 +253,50 @@ describe("run recovery surfaces", () => {
201
253
  window.history.replaceState(null, "", "/");
202
254
  });
203
255
 
256
+ it.each(["message", "details"] as const)(
257
+ "masks run failure %s without masking recovery controls",
258
+ async (field) => {
259
+ const info = {
260
+ message: "Example Person's example notes are locked.",
261
+ details: "Example Document cannot be opened for Example Person.",
262
+ runId: "run-example",
263
+ errorCode: "runtime_error",
264
+ };
265
+ await act(async () => {
266
+ root.render(
267
+ <RunErrorRecoveryCard
268
+ info={info}
269
+ onContinue={vi.fn()}
270
+ onRetry={vi.fn()}
271
+ onDismiss={vi.fn()}
272
+ />,
273
+ );
274
+ });
275
+ if (field === "details") {
276
+ await act(async () => {
277
+ Array.from(container.querySelectorAll("button"))
278
+ .find((button) => button.textContent === "Details")!
279
+ .click();
280
+ });
281
+ }
282
+ const message = Array.from(
283
+ container.querySelectorAll(field === "message" ? "p" : "pre"),
284
+ ).find((element) => element.textContent === info[field]);
285
+ expect(message?.hasAttribute("data-an-mask")).toBe(true);
286
+ expect(container.firstElementChild?.hasAttribute("data-an-mask")).toBe(
287
+ false,
288
+ );
289
+ expect(
290
+ container
291
+ .querySelector(".font-medium.text-foreground")
292
+ ?.closest("[data-an-mask]"),
293
+ ).toBeNull();
294
+ for (const button of container.querySelectorAll("button")) {
295
+ expect(button.closest("[data-an-mask]")).toBeNull();
296
+ }
297
+ },
298
+ );
299
+
204
300
  it("offers the Builder subscription link for credit limits only", async () => {
205
301
  await act(async () => {
206
302
  root.render(
@@ -630,6 +726,215 @@ describe("run recovery surfaces", () => {
630
726
  expect(container.textContent).not.toContain("Choose a provider");
631
727
  });
632
728
 
729
+ it("tracks setup card exposure once and the Builder and custom key choices", async () => {
730
+ const renderCard = () => (
731
+ <AgentNativeI18nProvider
732
+ initialLocale="en-US"
733
+ initialPreference="en-US"
734
+ persistPreference={false}
735
+ >
736
+ <BuilderSetupCard />
737
+ </AgentNativeI18nProvider>
738
+ );
739
+
740
+ await act(async () => {
741
+ root.render(renderCard());
742
+ });
743
+ await act(async () => {
744
+ root.render(renderCard());
745
+ });
746
+
747
+ expect(setupTelemetryMock).toHaveBeenCalledTimes(1);
748
+ expect(setupTelemetryMock).toHaveBeenCalledWith(
749
+ "integration_setup_exposed",
750
+ expect.objectContaining({
751
+ flow: "chat_setup",
752
+ app_name: expect.any(String),
753
+ step_id: "connect_ai",
754
+ method_id: "setup_card",
755
+ action: "view",
756
+ outcome: "exposed",
757
+ }),
758
+ );
759
+
760
+ await act(async () => {
761
+ container
762
+ .querySelector<HTMLButtonElement>(
763
+ ".agent-builder-setup-card__builder-button",
764
+ )
765
+ ?.click();
766
+ container
767
+ .querySelector<HTMLAnchorElement>('a[href="/settings/keys"]')
768
+ ?.click();
769
+ });
770
+
771
+ expect(setupTelemetryMock).toHaveBeenCalledWith(
772
+ "integration_method_clicked",
773
+ expect.objectContaining({
774
+ method_id: "builder",
775
+ action: "click",
776
+ outcome: "started",
777
+ }),
778
+ );
779
+ expect(setupTelemetryMock).toHaveBeenCalledWith(
780
+ "integration_method_clicked",
781
+ expect.objectContaining({
782
+ method_id: "custom_keys",
783
+ action: "click",
784
+ outcome: "started",
785
+ }),
786
+ );
787
+ });
788
+
789
+ it("tracks Builder success only after a selected setup attempt is confirmed", async () => {
790
+ const card = (
791
+ <AgentNativeI18nProvider
792
+ initialLocale="en-US"
793
+ initialPreference="en-US"
794
+ persistPreference={false}
795
+ >
796
+ <BuilderSetupCard />
797
+ </AgentNativeI18nProvider>
798
+ );
799
+ await act(async () => {
800
+ root.render(card);
801
+ });
802
+ expect(
803
+ setupTelemetryMock.mock.calls.some(
804
+ ([name]) => name === "integration_method_outcome",
805
+ ),
806
+ ).toBe(false);
807
+
808
+ await act(async () => {
809
+ container
810
+ .querySelector<HTMLButtonElement>(
811
+ ".agent-builder-setup-card__builder-button",
812
+ )
813
+ ?.click();
814
+ });
815
+ await vi.waitFor(() =>
816
+ expect(builderConnectMock.onConnect).toBeTypeOf("function"),
817
+ );
818
+ await act(async () => builderConnectMock.onConnect?.(true));
819
+ expect(builderFlowMock.start).toHaveBeenCalledWith({
820
+ provisionAccount: true,
821
+ });
822
+
823
+ await act(async () => {
824
+ await builderFlowMock.onConnected?.({ orgName: null });
825
+ });
826
+ expect(setupTelemetryMock).toHaveBeenCalledWith(
827
+ "integration_method_outcome",
828
+ expect.objectContaining({
829
+ method_id: "builder",
830
+ action: "connect",
831
+ outcome: "connected",
832
+ }),
833
+ );
834
+ });
835
+
836
+ it("records bounded Builder failure outcomes after a setup attempt", async () => {
837
+ const renderCard = () => (
838
+ <AgentNativeI18nProvider
839
+ initialLocale="en-US"
840
+ initialPreference="en-US"
841
+ persistPreference={false}
842
+ >
843
+ <BuilderSetupCard />
844
+ </AgentNativeI18nProvider>
845
+ );
846
+ await act(async () => {
847
+ root.render(renderCard());
848
+ });
849
+ await act(async () => {
850
+ container
851
+ .querySelector<HTMLButtonElement>(
852
+ ".agent-builder-setup-card__builder-button",
853
+ )
854
+ ?.click();
855
+ });
856
+ await vi.waitFor(() =>
857
+ expect(builderConnectMock.onConnect).toBeTypeOf("function"),
858
+ );
859
+ await act(async () => builderConnectMock.onConnect?.(true));
860
+
861
+ builderFlowMock.state.statusUnavailable = true;
862
+ builderFlowMock.state.errorKind = "status-read";
863
+ builderFlowMock.state.error = "Builder status unavailable: private text";
864
+ await act(async () => {
865
+ root.render(renderCard());
866
+ });
867
+ expect(setupTelemetryMock).toHaveBeenCalledWith(
868
+ "integration_method_outcome",
869
+ expect.objectContaining({
870
+ method_id: "builder",
871
+ action: "connect",
872
+ outcome: "status_read_failed",
873
+ }),
874
+ );
875
+
876
+ setupTelemetryMock.mockClear();
877
+ builderFlowMock.state.statusUnavailable = false;
878
+ builderFlowMock.state.errorKind = null;
879
+ builderFlowMock.state.error = null;
880
+ builderFlowMock.state.terminalError = null;
881
+ await act(async () => {
882
+ root.render(renderCard());
883
+ });
884
+ await act(async () => {
885
+ container
886
+ .querySelector<HTMLButtonElement>(
887
+ ".agent-builder-setup-card__builder-button",
888
+ )
889
+ ?.click();
890
+ });
891
+ await vi.waitFor(() =>
892
+ expect(builderConnectMock.onConnect).toBeTypeOf("function"),
893
+ );
894
+ await act(async () => builderConnectMock.onConnect?.(true));
895
+ builderFlowMock.state.terminalError = "Private connection error text";
896
+ builderFlowMock.state.errorKind = "connection";
897
+ builderFlowMock.state.error = "Private connection error text";
898
+ await act(async () => {
899
+ root.render(renderCard());
900
+ });
901
+ expect(setupTelemetryMock).toHaveBeenCalledWith(
902
+ "integration_method_outcome",
903
+ expect.objectContaining({
904
+ method_id: "builder",
905
+ action: "connect",
906
+ outcome: "connection_failed",
907
+ }),
908
+ );
909
+ expect(JSON.stringify(setupTelemetryMock.mock.calls)).not.toContain(
910
+ "Private connection error text",
911
+ );
912
+ });
913
+
914
+ it("does not report a configured or preexisting Builder error as an outcome", async () => {
915
+ builderFlowMock.state.configured = true;
916
+ builderFlowMock.state.terminalError = "Preexisting connection error";
917
+ builderFlowMock.state.errorKind = "connection";
918
+ const card = (
919
+ <AgentNativeI18nProvider
920
+ initialLocale="en-US"
921
+ initialPreference="en-US"
922
+ persistPreference={false}
923
+ >
924
+ <BuilderSetupCard />
925
+ </AgentNativeI18nProvider>
926
+ );
927
+ await act(async () => {
928
+ root.render(card);
929
+ });
930
+
931
+ expect(
932
+ setupTelemetryMock.mock.calls.some(
933
+ ([name]) => name === "integration_method_outcome",
934
+ ),
935
+ ).toBe(false);
936
+ });
937
+
633
938
  it("keeps the Custom keys link within a mounted workspace app", async () => {
634
939
  const router = createMemoryRouter(
635
940
  [
@@ -660,6 +965,46 @@ describe("run recovery surfaces", () => {
660
965
  expect(container.querySelector('input[type="password"]')).toBeNull();
661
966
  });
662
967
 
968
+ it("passes chat setup attribution through the Custom keys link", async () => {
969
+ featureFlagMock.state = { status: "ready", enabled: false };
970
+ const router = createMemoryRouter(
971
+ [
972
+ {
973
+ path: "/ask",
974
+ element: (
975
+ <AgentNativeI18nProvider
976
+ initialLocale="en-US"
977
+ initialPreference="en-US"
978
+ persistPreference={false}
979
+ >
980
+ <BuilderSetupContent />
981
+ </AgentNativeI18nProvider>
982
+ ),
983
+ },
984
+ { path: "/settings/keys", element: <SetupTrackingFlowProbe /> },
985
+ ],
986
+ { initialEntries: ["/ask"] },
987
+ );
988
+
989
+ await act(async () => {
990
+ root.render(<RouterProvider router={router} />);
991
+ });
992
+ const customKeysLink = container.querySelector<HTMLAnchorElement>(
993
+ 'a[href="/settings/keys"]',
994
+ );
995
+ expect(customKeysLink).not.toBeNull();
996
+
997
+ await act(async () => {
998
+ customKeysLink?.click();
999
+ await Promise.resolve();
1000
+ });
1001
+
1002
+ expect(
1003
+ container.querySelector('[data-testid="setup-tracking-flow"]')
1004
+ ?.textContent,
1005
+ ).toBe("chat_setup");
1006
+ });
1007
+
663
1008
  it("links custom keys to the Model page with the settings redesign on", async () => {
664
1009
  featureFlagMock.state = { status: "ready", enabled: true };
665
1010
  try {