@agent-native/toolkit 0.221.0-same-host-mcp-20261027-20261027081850 → 0.221.0

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 (259) hide show
  1. package/agent-native.eject.json +16 -1
  2. package/dist/app/agentkit/react/components.d.ts +5 -3
  3. package/dist/app/agentkit/react/components.d.ts.map +1 -1
  4. package/dist/app/agentkit/react/components.js +103 -42
  5. package/dist/app/agentkit/react/components.js.map +1 -1
  6. package/dist/app/agentkit/react/styles.css +93 -65
  7. package/dist/app/agentkit/react/voice-transcripts.d.ts +22 -1
  8. package/dist/app/agentkit/react/voice-transcripts.d.ts.map +1 -1
  9. package/dist/app/agentkit/react/voice-transcripts.js +209 -8
  10. package/dist/app/agentkit/react/voice-transcripts.js.map +1 -1
  11. package/dist/app/chat/AgentKitAssistantChat.d.ts.map +1 -1
  12. package/dist/app/chat/AgentKitAssistantChat.js +55 -40
  13. package/dist/app/chat/AgentKitAssistantChat.js.map +1 -1
  14. package/dist/app/chat/agentkit-chat/guided-questions.d.ts +1 -0
  15. package/dist/app/chat/agentkit-chat/guided-questions.d.ts.map +1 -1
  16. package/dist/app/chat/agentkit-chat/guided-questions.js +18 -6
  17. package/dist/app/chat/agentkit-chat/guided-questions.js.map +1 -1
  18. package/dist/app/chat/agentkit-chat/index.d.ts +1 -1
  19. package/dist/app/chat/agentkit-chat/index.d.ts.map +1 -1
  20. package/dist/app/chat/agentkit-chat/index.js +1 -1
  21. package/dist/app/chat/agentkit-chat/index.js.map +1 -1
  22. package/dist/app/chat/chat/run-recovery.d.ts.map +1 -1
  23. package/dist/app/chat/chat/run-recovery.js +5 -1
  24. package/dist/app/chat/chat/run-recovery.js.map +1 -1
  25. package/dist/app/chat/composer/runtime-adapters.d.ts.map +1 -1
  26. package/dist/app/chat/composer/runtime-adapters.js +2 -0
  27. package/dist/app/chat/composer/runtime-adapters.js.map +1 -1
  28. package/dist/app/chat/reply-connection-suggestion.d.ts +29 -0
  29. package/dist/app/chat/reply-connection-suggestion.d.ts.map +1 -0
  30. package/dist/app/chat/reply-connection-suggestion.js +42 -0
  31. package/dist/app/chat/reply-connection-suggestion.js.map +1 -0
  32. package/dist/app/i18n/catalogs/ar-SA.d.ts.map +1 -1
  33. package/dist/app/i18n/catalogs/ar-SA.js +7 -0
  34. package/dist/app/i18n/catalogs/ar-SA.js.map +1 -1
  35. package/dist/app/i18n/catalogs/de-DE.d.ts.map +1 -1
  36. package/dist/app/i18n/catalogs/de-DE.js +7 -0
  37. package/dist/app/i18n/catalogs/de-DE.js.map +1 -1
  38. package/dist/app/i18n/catalogs/en-US.d.ts +7 -0
  39. package/dist/app/i18n/catalogs/en-US.d.ts.map +1 -1
  40. package/dist/app/i18n/catalogs/en-US.js +7 -0
  41. package/dist/app/i18n/catalogs/en-US.js.map +1 -1
  42. package/dist/app/i18n/catalogs/es-ES.d.ts.map +1 -1
  43. package/dist/app/i18n/catalogs/es-ES.js +7 -0
  44. package/dist/app/i18n/catalogs/es-ES.js.map +1 -1
  45. package/dist/app/i18n/catalogs/fr-FR.d.ts.map +1 -1
  46. package/dist/app/i18n/catalogs/fr-FR.js +7 -0
  47. package/dist/app/i18n/catalogs/fr-FR.js.map +1 -1
  48. package/dist/app/i18n/catalogs/hi-IN.d.ts.map +1 -1
  49. package/dist/app/i18n/catalogs/hi-IN.js +7 -0
  50. package/dist/app/i18n/catalogs/hi-IN.js.map +1 -1
  51. package/dist/app/i18n/catalogs/ja-JP.d.ts.map +1 -1
  52. package/dist/app/i18n/catalogs/ja-JP.js +7 -0
  53. package/dist/app/i18n/catalogs/ja-JP.js.map +1 -1
  54. package/dist/app/i18n/catalogs/ko-KR.d.ts.map +1 -1
  55. package/dist/app/i18n/catalogs/ko-KR.js +7 -0
  56. package/dist/app/i18n/catalogs/ko-KR.js.map +1 -1
  57. package/dist/app/i18n/catalogs/pt-BR.d.ts.map +1 -1
  58. package/dist/app/i18n/catalogs/pt-BR.js +7 -0
  59. package/dist/app/i18n/catalogs/pt-BR.js.map +1 -1
  60. package/dist/app/i18n/catalogs/zh-CN.d.ts.map +1 -1
  61. package/dist/app/i18n/catalogs/zh-CN.js +7 -0
  62. package/dist/app/i18n/catalogs/zh-CN.js.map +1 -1
  63. package/dist/app/i18n/catalogs/zh-TW.d.ts.map +1 -1
  64. package/dist/app/i18n/catalogs/zh-TW.js +7 -0
  65. package/dist/app/i18n/catalogs/zh-TW.js.map +1 -1
  66. package/dist/app/mcp-approval/McpApprovalReview.d.ts +25 -0
  67. package/dist/app/mcp-approval/McpApprovalReview.d.ts.map +1 -0
  68. package/dist/app/mcp-approval/McpApprovalReview.js +342 -0
  69. package/dist/app/mcp-approval/McpApprovalReview.js.map +1 -0
  70. package/dist/app/onboarding/FirstRunOnboarding.d.ts.map +1 -1
  71. package/dist/app/onboarding/FirstRunOnboarding.js +299 -71
  72. package/dist/app/onboarding/FirstRunOnboarding.js.map +1 -1
  73. package/dist/app/onboarding/GettingStartedCard.js +1 -1
  74. package/dist/app/onboarding/GettingStartedCard.js.map +1 -1
  75. package/dist/app/onboarding/first-run-builder-credits-flag.d.ts +2 -0
  76. package/dist/app/onboarding/first-run-builder-credits-flag.d.ts.map +1 -0
  77. package/dist/app/onboarding/first-run-builder-credits-flag.js +8 -0
  78. package/dist/app/onboarding/first-run-builder-credits-flag.js.map +1 -0
  79. package/dist/app/providers/AppProviders.d.ts.map +1 -1
  80. package/dist/app/providers/AppProviders.js +14 -1
  81. package/dist/app/providers/AppProviders.js.map +1 -1
  82. package/dist/app/resources/McpConnectionSuggestion.d.ts +2 -4
  83. package/dist/app/resources/McpConnectionSuggestion.d.ts.map +1 -1
  84. package/dist/app/resources/McpConnectionSuggestion.js +59 -36
  85. package/dist/app/resources/McpConnectionSuggestion.js.map +1 -1
  86. package/dist/app/settings/account/PreferencesSettings.d.ts.map +1 -1
  87. package/dist/app/settings/account/PreferencesSettings.js +14 -1
  88. package/dist/app/settings/account/PreferencesSettings.js.map +1 -1
  89. package/dist/app/settings/account/ThemeFamilySettings.js +2 -2
  90. package/dist/app/settings/account/ThemeFamilySettings.js.map +1 -1
  91. package/dist/app/settings/account/search-entries.d.ts +1 -0
  92. package/dist/app/settings/account/search-entries.d.ts.map +1 -1
  93. package/dist/app/settings/account/search-entries.js +5 -0
  94. package/dist/app/settings/account/search-entries.js.map +1 -1
  95. package/dist/app/settings/shell/SettingsShell.d.ts.map +1 -1
  96. package/dist/app/settings/shell/SettingsShell.js +26 -2
  97. package/dist/app/settings/shell/SettingsShell.js.map +1 -1
  98. package/dist/app/shared/AppShellSkeleton.d.ts.map +1 -1
  99. package/dist/app/shared/AppShellSkeleton.js +4 -10
  100. package/dist/app/shared/AppShellSkeleton.js.map +1 -1
  101. package/dist/app/shared/EnvironmentBadge.d.ts +2 -1
  102. package/dist/app/shared/EnvironmentBadge.d.ts.map +1 -1
  103. package/dist/app/shared/EnvironmentBadge.js +2 -3
  104. package/dist/app/shared/EnvironmentBadge.js.map +1 -1
  105. package/dist/app/shared/is-builder-io-employee.d.ts +8 -0
  106. package/dist/app/shared/is-builder-io-employee.d.ts.map +1 -0
  107. package/dist/app/shared/is-builder-io-employee.js +10 -0
  108. package/dist/app/shared/is-builder-io-employee.js.map +1 -0
  109. package/dist/app/styles/agent-conversation.css +30 -17
  110. package/dist/app/styles/theme-families.css +22 -5
  111. package/dist/app/styles/tokens/agent-kit.css +3 -3
  112. package/dist/composer/PromptComposer.d.ts +15 -3
  113. package/dist/composer/PromptComposer.d.ts.map +1 -1
  114. package/dist/composer/PromptComposer.js +74 -16
  115. package/dist/composer/PromptComposer.js.map +1 -1
  116. package/dist/composer/RealtimeVoiceMode.d.ts +7 -0
  117. package/dist/composer/RealtimeVoiceMode.d.ts.map +1 -1
  118. package/dist/composer/RealtimeVoiceMode.js +9 -2
  119. package/dist/composer/RealtimeVoiceMode.js.map +1 -1
  120. package/dist/composer/TiptapComposer.d.ts +7 -0
  121. package/dist/composer/TiptapComposer.d.ts.map +1 -1
  122. package/dist/composer/TiptapComposer.js +12 -4
  123. package/dist/composer/TiptapComposer.js.map +1 -1
  124. package/dist/composer/VoiceButton.d.ts +2 -0
  125. package/dist/composer/VoiceButton.d.ts.map +1 -1
  126. package/dist/composer/VoiceButton.js +5 -4
  127. package/dist/composer/VoiceButton.js.map +1 -1
  128. package/dist/composer/index.d.ts +2 -2
  129. package/dist/composer/index.d.ts.map +1 -1
  130. package/dist/composer/index.js +1 -1
  131. package/dist/composer/index.js.map +1 -1
  132. package/dist/composer/prompt-attachments.d.ts +20 -0
  133. package/dist/composer/prompt-attachments.d.ts.map +1 -1
  134. package/dist/composer/prompt-attachments.js +188 -11
  135. package/dist/composer/prompt-attachments.js.map +1 -1
  136. package/dist/composer/realtime-voice-transcript.d.ts +27 -0
  137. package/dist/composer/realtime-voice-transcript.d.ts.map +1 -1
  138. package/dist/composer/realtime-voice-transcript.js +34 -1
  139. package/dist/composer/realtime-voice-transcript.js.map +1 -1
  140. package/dist/composer/realtime-voice-turn-metrics.d.ts +43 -0
  141. package/dist/composer/realtime-voice-turn-metrics.d.ts.map +1 -0
  142. package/dist/composer/realtime-voice-turn-metrics.js +132 -0
  143. package/dist/composer/realtime-voice-turn-metrics.js.map +1 -0
  144. package/dist/composer/runtime-adapters.d.ts +4 -1
  145. package/dist/composer/runtime-adapters.d.ts.map +1 -1
  146. package/dist/composer/runtime-adapters.js +2 -1
  147. package/dist/composer/runtime-adapters.js.map +1 -1
  148. package/dist/composer/useRealtimeVoiceMode.d.ts +61 -9
  149. package/dist/composer/useRealtimeVoiceMode.d.ts.map +1 -1
  150. package/dist/composer/useRealtimeVoiceMode.js +536 -99
  151. package/dist/composer/useRealtimeVoiceMode.js.map +1 -1
  152. package/dist/composer/voice-chat-icon.d.ts +7 -0
  153. package/dist/composer/voice-chat-icon.d.ts.map +1 -0
  154. package/dist/composer/voice-chat-icon.js +14 -0
  155. package/dist/composer/voice-chat-icon.js.map +1 -0
  156. package/dist/editor/DragHandle.d.ts +12 -1
  157. package/dist/editor/DragHandle.d.ts.map +1 -1
  158. package/dist/editor/DragHandle.js +165 -41
  159. package/dist/editor/DragHandle.js.map +1 -1
  160. package/dist/editor/RegistryBlockNode.d.ts +20 -0
  161. package/dist/editor/RegistryBlockNode.d.ts.map +1 -1
  162. package/dist/editor/RegistryBlockNode.js +58 -13
  163. package/dist/editor/RegistryBlockNode.js.map +1 -1
  164. package/dist/editor/index.d.ts +2 -2
  165. package/dist/editor/index.d.ts.map +1 -1
  166. package/dist/editor/index.js +1 -1
  167. package/dist/editor/index.js.map +1 -1
  168. package/dist/styles.css +2 -0
  169. package/dist/ui/dropdown-menu.d.ts.map +1 -1
  170. package/dist/ui/dropdown-menu.js +5 -1
  171. package/dist/ui/dropdown-menu.js.map +1 -1
  172. package/package.json +14 -4
  173. package/src/app/agentkit/react/components.regenerate-readiness.spec.tsx +3 -1
  174. package/src/app/agentkit/react/components.spec.tsx +24 -4
  175. package/src/app/agentkit/react/components.tsx +174 -44
  176. package/src/app/agentkit/react/composer-context.spec.tsx +3 -0
  177. package/src/app/agentkit/react/disclosures.spec.tsx +124 -0
  178. package/src/app/agentkit/react/lifecycle.spec.tsx +8 -2
  179. package/src/app/agentkit/react/message-actions.spec.tsx +161 -5
  180. package/src/app/agentkit/react/saved-reply-reload.spec.tsx +206 -0
  181. package/src/app/agentkit/react/styles.css +93 -65
  182. package/src/app/agentkit/react/voice-transcripts.spec.tsx +239 -4
  183. package/src/app/agentkit/react/voice-transcripts.ts +245 -8
  184. package/src/app/chat/AgentKitAssistantChat.spec.tsx +59 -11
  185. package/src/app/chat/AgentKitAssistantChat.tsx +74 -62
  186. package/src/app/chat/agentkit-chat/guided-answers-context.spec.ts +9 -0
  187. package/src/app/chat/agentkit-chat/guided-questions.tsx +17 -7
  188. package/src/app/chat/agentkit-chat/index.ts +1 -0
  189. package/src/app/chat/chat/run-recovery.spec.tsx +45 -0
  190. package/src/app/chat/chat/run-recovery.tsx +6 -1
  191. package/src/app/chat/composer/runtime-adapters.tsx +2 -0
  192. package/src/app/chat/reply-connection-suggestion.spec.ts +121 -0
  193. package/src/app/chat/reply-connection-suggestion.ts +76 -0
  194. package/src/app/i18n/catalogs/ar-SA.ts +10 -0
  195. package/src/app/i18n/catalogs/de-DE.ts +10 -0
  196. package/src/app/i18n/catalogs/en-US.ts +10 -0
  197. package/src/app/i18n/catalogs/es-ES.ts +10 -0
  198. package/src/app/i18n/catalogs/fr-FR.ts +10 -0
  199. package/src/app/i18n/catalogs/hi-IN.ts +10 -0
  200. package/src/app/i18n/catalogs/ja-JP.ts +10 -0
  201. package/src/app/i18n/catalogs/ko-KR.ts +10 -0
  202. package/src/app/i18n/catalogs/pt-BR.ts +10 -0
  203. package/src/app/i18n/catalogs/zh-CN.ts +10 -0
  204. package/src/app/i18n/catalogs/zh-TW.ts +10 -0
  205. package/src/app/mcp-approval/McpApprovalReview.spec.tsx +687 -0
  206. package/src/app/mcp-approval/McpApprovalReview.tsx +913 -0
  207. package/src/app/onboarding/FirstRunOnboarding.spec.tsx +736 -26
  208. package/src/app/onboarding/FirstRunOnboarding.tsx +473 -145
  209. package/src/app/onboarding/GettingStartedCard.tsx +1 -1
  210. package/src/app/onboarding/first-run-builder-credits-flag.spec.ts +13 -0
  211. package/src/app/onboarding/first-run-builder-credits-flag.ts +9 -0
  212. package/src/app/providers/AppProviders.spec.tsx +109 -0
  213. package/src/app/providers/AppProviders.tsx +53 -1
  214. package/src/app/resources/McpConnectionSuggestion.spec.ts +77 -12
  215. package/src/app/resources/McpConnectionSuggestion.tsx +69 -51
  216. package/src/app/settings/VoiceChatSettings.spec.tsx +6 -1
  217. package/src/app/settings/account/PreferencesSettings.spec.tsx +73 -0
  218. package/src/app/settings/account/PreferencesSettings.tsx +24 -7
  219. package/src/app/settings/account/ThemeFamilySettings.tsx +2 -2
  220. package/src/app/settings/account/search-entries.spec.ts +14 -0
  221. package/src/app/settings/account/search-entries.ts +9 -0
  222. package/src/app/settings/shell/SettingsShell.tsx +29 -2
  223. package/src/app/shared/AppShellSkeleton.spec.tsx +2 -2
  224. package/src/app/shared/AppShellSkeleton.tsx +3 -16
  225. package/src/app/shared/EnvironmentBadge.tsx +3 -4
  226. package/src/app/shared/is-builder-io-employee.ts +17 -0
  227. package/src/app/styles/agent-conversation.css +30 -17
  228. package/src/app/styles/theme-families.css +22 -5
  229. package/src/app/styles/theme-families.spec.ts +73 -0
  230. package/src/app/styles/tokens/agent-kit.css +3 -3
  231. package/src/composer/AgentSuggestionBar.overflow.spec.tsx +1 -1
  232. package/src/composer/AttachmentChip.spec.tsx +87 -0
  233. package/src/composer/PromptComposer.context.spec.tsx +8 -4
  234. package/src/composer/PromptComposer.setup-card.spec.tsx +170 -0
  235. package/src/composer/PromptComposer.tsx +125 -14
  236. package/src/composer/RealtimeVoiceMode.spec.tsx +48 -0
  237. package/src/composer/RealtimeVoiceMode.tsx +45 -2
  238. package/src/composer/TiptapComposer.tsx +25 -0
  239. package/src/composer/TiptapComposer.voice-toolbar.spec.tsx +4 -1
  240. package/src/composer/VoiceButton.tsx +8 -3
  241. package/src/composer/index.ts +4 -0
  242. package/src/composer/prompt-attachments.spec.ts +160 -8
  243. package/src/composer/prompt-attachments.ts +221 -12
  244. package/src/composer/realtime-voice-transcript.spec.ts +36 -0
  245. package/src/composer/realtime-voice-transcript.ts +56 -1
  246. package/src/composer/realtime-voice-turn-metrics.spec.ts +175 -0
  247. package/src/composer/realtime-voice-turn-metrics.ts +192 -0
  248. package/src/composer/runtime-adapters.tsx +5 -1
  249. package/src/composer/useRealtimeVoiceMode.session.spec.tsx +487 -2
  250. package/src/composer/useRealtimeVoiceMode.spec.ts +267 -65
  251. package/src/composer/useRealtimeVoiceMode.tsx +716 -139
  252. package/src/composer/voice-chat-icon.tsx +19 -0
  253. package/src/editor/DragHandle.spec.ts +217 -0
  254. package/src/editor/DragHandle.ts +214 -55
  255. package/src/editor/RegistryBlockNode.spec.ts +172 -2
  256. package/src/editor/RegistryBlockNode.tsx +91 -13
  257. package/src/editor/index.ts +4 -0
  258. package/src/styles.css +2 -0
  259. package/src/ui/dropdown-menu.tsx +4 -0
@@ -5,21 +5,24 @@ import { listFirstRunOnboardingExtensions, } from "@agent-native/core/client/onb
5
5
  import { saveFirstRunOnboardingRole } from "@agent-native/core/client/onboarding/first-run-status";
6
6
  import { createOnboardingCorrelationId, setCustomKeyOnboardingAttempt, setOnboardingExperiment, trackOnboardingEvent, useOnboarding, } from "@agent-native/core/client/onboarding/use-onboarding";
7
7
  import { ONBOARDING_PREVIEW_QUERY_PARAM, ONBOARDING_PREVIEW_STEP_QUERY_PARAM, useOnboardingPreviewMode, useOnboardingPreviewStep, } from "@agent-native/core/client/onboarding/use-preview-mode";
8
+ import { useActionMutation } from "@agent-native/core/client/use-action";
8
9
  import { SETTINGS_REDESIGN_FLAG } from "@agent-native/core/feature-flags/registry";
9
10
  import { buildSettingsRoute, SETTINGS_PAGE_IDS, } from "@agent-native/core/navigation";
11
+ import { FIRST_RUN_BUILDER_CREDITS_V2_EXPERIMENT_ID, FIRST_RUN_BUILDER_CREDITS_V2_FLAG_KEY, } from "@agent-native/core/shared";
10
12
  import { Badge } from "@agent-native/toolkit/ui/badge";
11
13
  import { Button } from "@agent-native/toolkit/ui/button";
12
14
  import { Popover, PopoverContent, PopoverTrigger, } from "@agent-native/toolkit/ui/popover";
13
15
  import { Skeleton } from "@agent-native/toolkit/ui/skeleton";
14
16
  import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from "@agent-native/toolkit/ui/tooltip";
15
17
  import { cn } from "@agent-native/toolkit/utils";
16
- import { IconArrowRight, IconChevronDown, IconChevronUp, IconHelpCircle, IconKey, IconLoader2, } from "@tabler/icons-react";
18
+ import { IconAlertCircle, IconArrowRight, IconChevronDown, IconChevronUp, IconHelpCircle, IconKey, IconLoader2, } from "@tabler/icons-react";
17
19
  import { useCallback, useEffect, useMemo, useRef, useState, } from "react";
18
20
  import { useNavigate } from "react-router";
19
21
  import { useBuilderConnectFlow } from "../settings/useBuilderStatus.js";
20
22
  import { BuilderIncludedServices, CapabilityInfoButton, getBuilderIncludedCapabilities, } from "./BuilderIncludedServices.js";
21
23
  import { assignFirstRunSetupVariant, FIRST_RUN_SETUP_EXPERIMENT_ID, getPreviewFirstRunSetupVariant, } from "./first-run-experiment.js";
22
24
  import { ONBOARDING_PRIMARY_BUTTON_CLASS, OnboardingStepLayout, } from "./OnboardingStepLayout.js";
25
+ const FIRST_RUN_CREDITS_V2_APP_IDS = new Set(["clips", "design", "slides"]);
23
26
  function trackFirstRunSetupOutcome(attempt, outcome, errorType) {
24
27
  if (!attempt || attempt.outcomeTracked)
25
28
  return;
@@ -33,7 +36,7 @@ function trackFirstRunSetupOutcome(attempt, outcome, errorType) {
33
36
  ...(errorType ? { error_type: errorType } : {}),
34
37
  });
35
38
  }
36
- function firstRunStepProperties(screen, beforeSetupExtensions, afterSetupExtensions, extensionPlacement, extensionIndex, extensionStepIndex, setupSkippable) {
39
+ function firstRunStepProperties(screen, beforeSetupExtensions, afterSetupExtensions, extensionPlacement, extensionIndex, extensionStepIndex, setupSkippable, setupRecoverySkippable = false) {
37
40
  // `optional` must match whether the screen offers a skip: Analytics funnels
38
41
  // read it to keep a skip from counting as drop-off.
39
42
  const beforeStepCount = beforeSetupExtensions.reduce((count, extension) => count + (extension.stepCount ?? 1), 0);
@@ -65,7 +68,9 @@ function firstRunStepProperties(screen, beforeSetupExtensions, afterSetupExtensi
65
68
  flow: "first_run",
66
69
  step_id: screen,
67
70
  step_index: stepIndex,
68
- optional: screen === "role" || (screen === "choice" && setupSkippable),
71
+ optional: screen === "role" ||
72
+ (screen === "choice" && setupSkippable) ||
73
+ (screen === "connecting" && setupRecoverySkippable),
69
74
  };
70
75
  }
71
76
  const SETUP_EXPERIMENT_DECISION_TIMEOUT_MS = 4000;
@@ -104,12 +109,49 @@ export function FirstRunOnboarding({ initialFirstRun = false, } = {}) {
104
109
  const canTrackOnboardingScreen = (!loading && profile !== null) || canRenderRoleWithoutProfile;
105
110
  const setupSkippable = profile?.appId === "clips";
106
111
  const [setupExperimentDecision, setSetupExperimentDecision] = useState(null);
107
- const setupVariant = useMemo(() => (previewMode && getPreviewFirstRunSetupVariant(window.location.search)) ||
108
- assignFirstRunSetupVariant(), [previewMode]);
112
+ const [creditsV2Assignment, setCreditsV2Assignment] = useState(null);
113
+ const [creditsV2ExposureTracked, setCreditsV2ExposureTracked] = useState(false);
114
+ const creditsV2Flag = useFeatureFlagState(FIRST_RUN_BUILDER_CREDITS_V2_FLAG_KEY);
115
+ const creditsV2FlagAppliesToApp = FIRST_RUN_CREDITS_V2_APP_IDS.has(profile?.appId ?? "");
116
+ const creditsV2Exposure = useActionMutation("record-first-run-builder-credits-v2-exposure", {
117
+ uiOnly: true,
118
+ skipActionQueryInvalidation: true,
119
+ });
120
+ const previewCreditsV2 = previewMode &&
121
+ typeof window !== "undefined" &&
122
+ new URLSearchParams(window.location.search).get("experiment") === "v2";
123
+ const creditsV2Enabled = creditsV2FlagAppliesToApp &&
124
+ creditsV2Flag.status === "ready" &&
125
+ creditsV2Flag.enabled;
126
+ const creditsV2FlowEnabled = creditsV2Enabled || previewCreditsV2;
127
+ const creditsV2FlagPending = !previewMode &&
128
+ creditsV2FlagAppliesToApp &&
129
+ creditsV2Flag.status === "loading";
130
+ const setupVariant = useMemo(() => {
131
+ const previewVariant = previewMode && getPreviewFirstRunSetupVariant(window.location.search);
132
+ if (previewCreditsV2)
133
+ return previewVariant;
134
+ if (previewVariant)
135
+ return previewVariant;
136
+ if (creditsV2FlowEnabled) {
137
+ return creditsV2Assignment?.experiment_variant ?? null;
138
+ }
139
+ return assignFirstRunSetupVariant();
140
+ }, [
141
+ creditsV2FlowEnabled,
142
+ creditsV2Assignment,
143
+ previewCreditsV2,
144
+ previewMode,
145
+ profile?.appId,
146
+ ]);
147
+ const setupExperimentId = creditsV2FlowEnabled
148
+ ? FIRST_RUN_BUILDER_CREDITS_V2_EXPERIMENT_ID
149
+ : FIRST_RUN_SETUP_EXPERIMENT_ID;
109
150
  // The credits arm offers Skip in every app, so analytics must mark the choice step optional there too.
110
151
  const choiceSkippable = setupSkippable ||
111
152
  (setupVariant === "credits" &&
112
- (previewMode || setupExperimentDecision === true));
153
+ (previewMode || setupExperimentDecision === true) &&
154
+ (!creditsV2Enabled || creditsV2ExposureTracked));
113
155
  const [extensionPlacement, setExtensionPlacement] = useState("after-setup");
114
156
  const [extensionIndex, setExtensionIndex] = useState(0);
115
157
  const [extensionStepIndex, setExtensionStepIndex] = useState(0);
@@ -155,11 +197,14 @@ export function FirstRunOnboarding({ initialFirstRun = false, } = {}) {
155
197
  previewMode,
156
198
  choiceSkippable,
157
199
  ]);
158
- const trackFirstRunStepSkipped = useCallback((stepScreen, reason = "user_action", stepExtensionIndex = extensionIndex, stepIndex = extensionStepIndex) => {
200
+ const trackFirstRunStepSkipped = useCallback((stepScreen, reason = "user_action", stepExtensionIndex = extensionIndex, stepIndex = extensionStepIndex, optionalOverride) => {
159
201
  if (previewMode)
160
202
  return;
161
203
  trackOnboardingEvent("onboarding_step_skipped", {
162
204
  ...firstRunStepProperties(stepScreen, beforeSetupExtensions, afterSetupExtensions, extensionPlacement, stepExtensionIndex, stepIndex, choiceSkippable),
205
+ ...(optionalOverride !== undefined
206
+ ? { optional: optionalOverride }
207
+ : {}),
163
208
  reason,
164
209
  });
165
210
  }, [
@@ -175,6 +220,12 @@ export function FirstRunOnboarding({ initialFirstRun = false, } = {}) {
175
220
  const completionInFlightRef = useRef(false);
176
221
  const completionRedirectRef = useRef(null);
177
222
  const setupSkipStartedRef = useRef(false);
223
+ const creditsV2ExposureStartedRef = useRef(false);
224
+ const creditsV2ExposureFailedRef = useRef(false);
225
+ const creditsV2ExposureSettledRef = useRef(false);
226
+ const creditsV2ExposureTrackedRef = useRef(false);
227
+ const creditsV2ExposureConfirmationStartedRef = useRef(false);
228
+ const setupExperimentTimedOutRef = useRef(false);
178
229
  const onboardingTerminalRef = useRef(false);
179
230
  const abandonmentTrackedRef = useRef(false);
180
231
  const onboardingStartedRef = useRef(false);
@@ -238,13 +289,133 @@ export function FirstRunOnboarding({ initialFirstRun = false, } = {}) {
238
289
  trackOnboardingEvent("onboarding_started", { flow: "first_run" });
239
290
  }
240
291
  }, [canTrackOnboardingScreen, firstRun, previewMode]);
241
- // Both arms render a skeleton until the experiment decision is known, and the
242
- // credits arm's skip depends on it, so the choice view waits for the decision
243
- // and is sent once with its resolved optional flag. Without an assigned arm
244
- // no decision is ever made, and the choice UI renders as is.
292
+ // The v2 choice event waits until the rendered arm has been attributed.
245
293
  const choiceStepKnown = screen !== "choice" ||
246
- setupVariant === null ||
247
- setupExperimentDecision !== null;
294
+ (!creditsV2FlagPending &&
295
+ (!creditsV2FlowEnabled || setupExperimentDecision !== null) &&
296
+ (setupVariant === null || setupExperimentDecision !== null) &&
297
+ (!creditsV2Enabled ||
298
+ setupExperimentDecision !== true ||
299
+ creditsV2ExposureTracked));
300
+ const beginBeforeSetup = useCallback(() => {
301
+ if (beforeSetupExtensions.length === 0) {
302
+ setScreen("choice");
303
+ return;
304
+ }
305
+ setExtensionPlacement("before-setup");
306
+ setExtensionIndex(0);
307
+ setExtensionStepIndex(0);
308
+ setScreen("extension");
309
+ }, [beforeSetupExtensions.length]);
310
+ const handleFinish = useCallback((completedScreen, track = true) => {
311
+ if (afterSetupExtensions.length === 0) {
312
+ void finishOnboarding(completedScreen);
313
+ return;
314
+ }
315
+ if (completedScreen && track)
316
+ trackFirstRunStepCompleted(completedScreen);
317
+ setExtensionPlacement("after-setup");
318
+ setExtensionIndex(0);
319
+ setExtensionStepIndex(0);
320
+ setScreen("extension");
321
+ }, [afterSetupExtensions.length, finishOnboarding, trackFirstRunStepCompleted]);
322
+ const skipFailedBuilderSetup = useCallback(() => {
323
+ if (setupSkipStartedRef.current || completionInFlightRef.current)
324
+ return;
325
+ setupSkipStartedRef.current = true;
326
+ trackFirstRunStepSkipped("connecting", "builder_provision_failed", extensionIndex, extensionStepIndex, true);
327
+ if (setupSkippable)
328
+ completionRedirectRef.current = "/record";
329
+ handleFinish(null);
330
+ }, [
331
+ extensionIndex,
332
+ extensionStepIndex,
333
+ handleFinish,
334
+ setupSkippable,
335
+ trackFirstRunStepSkipped,
336
+ ]);
337
+ const handleBuilderConnected = useCallback(() => {
338
+ trackFirstRunSetupOutcome(builderSetupAttemptRef.current, "connected");
339
+ builderSetupAttemptRef.current = null;
340
+ setupAttemptRef.current = null;
341
+ trackFirstRunStepCompleted("choice");
342
+ trackFirstRunStepCompleted("connecting");
343
+ handleFinish(null);
344
+ }, [handleFinish, trackFirstRunStepCompleted]);
345
+ const connectFlow = useBuilderConnectFlow({
346
+ enabled: firstRun && !previewMode,
347
+ provisionAccount: true,
348
+ trackingSource: "first_run_onboarding",
349
+ trackingFlow: "connect_llm",
350
+ onConnected: handleBuilderConnected,
351
+ });
352
+ const accountExists = connectFlow.accountExists && !connectFlow.connecting;
353
+ const provisioning = builderConnectionMode === "provision" && !accountExists;
354
+ const provisioningFailed = provisioning &&
355
+ !connectFlow.connecting &&
356
+ Boolean(connectFlow.terminalError);
357
+ const connectionFailed = provisioningFailed;
358
+ useEffect(() => {
359
+ if (previewMode ||
360
+ !firstRun ||
361
+ !canTrackOnboardingScreen ||
362
+ screen !== "choice" ||
363
+ !creditsV2Enabled ||
364
+ setupExperimentDecision !== true ||
365
+ creditsV2ExposureTrackedRef.current ||
366
+ !creditsV2Assignment) {
367
+ return;
368
+ }
369
+ creditsV2ExposureTrackedRef.current = true;
370
+ setOnboardingExperiment({
371
+ id: creditsV2Assignment.experiment_id,
372
+ variant: creditsV2Assignment.experiment_variant,
373
+ });
374
+ setCreditsV2ExposureTracked(true);
375
+ trackOnboardingEvent("onboarding_experiment_exposed", {
376
+ flow: "first_run",
377
+ step_id: "choice",
378
+ experiment_id: creditsV2Assignment.experiment_id,
379
+ experiment_variant: creditsV2Assignment.experiment_variant,
380
+ });
381
+ if (creditsV2ExposureConfirmationStartedRef.current)
382
+ return;
383
+ creditsV2ExposureConfirmationStartedRef.current = true;
384
+ const persistExposure = async () => {
385
+ let lastError;
386
+ // A write can commit even when its response is lost, so retry the idempotent assignment to reconcile it.
387
+ for (const delayMs of [0, 0, 1_000, 5_000, 30_000]) {
388
+ if (delayMs > 0) {
389
+ await new Promise((resolve) => window.setTimeout(resolve, delayMs));
390
+ }
391
+ try {
392
+ const { assignment } = await creditsV2Exposure.mutateAsync({
393
+ confirmedVariant: creditsV2Assignment.experiment_variant,
394
+ });
395
+ if (assignment?.experiment_id !== creditsV2Assignment.experiment_id ||
396
+ assignment.experiment_variant !==
397
+ creditsV2Assignment.experiment_variant) {
398
+ throw new Error("The server did not persist the displayed first-run Builder credits v2 assignment.");
399
+ }
400
+ return;
401
+ }
402
+ catch (error) {
403
+ lastError = error;
404
+ }
405
+ }
406
+ console.error("Failed to persist first-run Builder credits v2 exposure:", lastError);
407
+ };
408
+ void persistExposure();
409
+ }, [
410
+ canTrackOnboardingScreen,
411
+ creditsV2Assignment,
412
+ creditsV2Enabled,
413
+ creditsV2Exposure.mutateAsync,
414
+ firstRun,
415
+ previewMode,
416
+ setupExperimentDecision,
417
+ screen,
418
+ ]);
248
419
  useEffect(() => {
249
420
  if (previewMode ||
250
421
  !firstRun ||
@@ -253,8 +424,13 @@ export function FirstRunOnboarding({ initialFirstRun = false, } = {}) {
253
424
  stepViewRef.current = null;
254
425
  return;
255
426
  }
256
- const step = firstRunStepProperties(screen, beforeSetupExtensions, afterSetupExtensions, extensionPlacement, extensionIndex, extensionStepIndex, choiceSkippable);
257
- const key = [step.step_id, step.extension_id, step.step_index].join(":");
427
+ const step = firstRunStepProperties(screen, beforeSetupExtensions, afterSetupExtensions, extensionPlacement, extensionIndex, extensionStepIndex, choiceSkippable, provisioningFailed);
428
+ const key = [
429
+ step.step_id,
430
+ step.extension_id,
431
+ step.step_index,
432
+ step.optional,
433
+ ].join(":");
258
434
  if (stepViewRef.current?.key !== key) {
259
435
  stepViewRef.current = { key, id: createOnboardingCorrelationId() };
260
436
  }
@@ -275,6 +451,7 @@ export function FirstRunOnboarding({ initialFirstRun = false, } = {}) {
275
451
  previewMode,
276
452
  canTrackOnboardingScreen,
277
453
  choiceStepKnown,
454
+ provisioningFailed,
278
455
  screen,
279
456
  ]);
280
457
  useEffect(() => {
@@ -289,7 +466,7 @@ export function FirstRunOnboarding({ initialFirstRun = false, } = {}) {
289
466
  return;
290
467
  abandonmentTrackedRef.current = true;
291
468
  trackOnboardingEvent("onboarding_abandoned", {
292
- ...firstRunStepProperties(screen, beforeSetupExtensions, afterSetupExtensions, extensionPlacement, extensionIndex, extensionStepIndex, choiceSkippable),
469
+ ...firstRunStepProperties(screen, beforeSetupExtensions, afterSetupExtensions, extensionPlacement, extensionIndex, extensionStepIndex, choiceSkippable, provisioningFailed),
293
470
  reason: "page_exit",
294
471
  });
295
472
  };
@@ -306,44 +483,8 @@ export function FirstRunOnboarding({ initialFirstRun = false, } = {}) {
306
483
  canTrackOnboardingScreen,
307
484
  screen,
308
485
  choiceSkippable,
486
+ provisioningFailed,
309
487
  ]);
310
- const beginBeforeSetup = useCallback(() => {
311
- if (beforeSetupExtensions.length === 0) {
312
- setScreen("choice");
313
- return;
314
- }
315
- setExtensionPlacement("before-setup");
316
- setExtensionIndex(0);
317
- setExtensionStepIndex(0);
318
- setScreen("extension");
319
- }, [beforeSetupExtensions.length]);
320
- const handleFinish = useCallback((completedScreen, track = true) => {
321
- if (afterSetupExtensions.length === 0) {
322
- void finishOnboarding(completedScreen);
323
- return;
324
- }
325
- if (completedScreen && track)
326
- trackFirstRunStepCompleted(completedScreen);
327
- setExtensionPlacement("after-setup");
328
- setExtensionIndex(0);
329
- setExtensionStepIndex(0);
330
- setScreen("extension");
331
- }, [afterSetupExtensions.length, finishOnboarding, trackFirstRunStepCompleted]);
332
- const handleBuilderConnected = useCallback(() => {
333
- trackFirstRunSetupOutcome(builderSetupAttemptRef.current, "connected");
334
- builderSetupAttemptRef.current = null;
335
- setupAttemptRef.current = null;
336
- trackFirstRunStepCompleted("choice");
337
- trackFirstRunStepCompleted("connecting");
338
- handleFinish(null);
339
- }, [handleFinish, trackFirstRunStepCompleted]);
340
- const connectFlow = useBuilderConnectFlow({
341
- enabled: firstRun && !previewMode,
342
- provisionAccount: true,
343
- trackingSource: "first_run_onboarding",
344
- trackingFlow: "connect_llm",
345
- onConnected: handleBuilderConnected,
346
- });
347
488
  const builderStatusReadCount = connectFlow.statusReadSettledCount ?? 0;
348
489
  useEffect(() => {
349
490
  const startedAt = retryingBuilderStatusAtCountRef.current;
@@ -368,12 +509,13 @@ export function FirstRunOnboarding({ initialFirstRun = false, } = {}) {
368
509
  trackFirstRunSetupOutcome(attempt, "failed", "account_exists");
369
510
  return;
370
511
  }
371
- if (connectFlow.terminalError) {
512
+ if (connectFlow.terminalError || connectFlow.statusUnavailable) {
372
513
  trackFirstRunSetupOutcome(attempt, "failed", "connection_error");
373
514
  }
374
515
  }, [
375
516
  connectFlow.accountExists,
376
517
  connectFlow.connecting,
518
+ connectFlow.statusUnavailable,
377
519
  connectFlow.terminalError,
378
520
  ]);
379
521
  // Preview never fetches status, so show the activation path it would offer.
@@ -386,11 +528,14 @@ export function FirstRunOnboarding({ initialFirstRun = false, } = {}) {
386
528
  // page in both arms and stays out of the results.
387
529
  const setupExperimentEligible = setupStatusSettled &&
388
530
  canActivateBuilderFreeCredits &&
389
- !connectFlow.configured;
531
+ !connectFlow.configured &&
532
+ !connectFlow.statusUnavailable;
390
533
  useEffect(() => {
391
534
  if (previewMode ||
392
535
  !firstRun ||
393
536
  screen !== "choice" ||
537
+ creditsV2FlagPending ||
538
+ creditsV2FlowEnabled ||
394
539
  !setupVariant ||
395
540
  !setupStatusSettled ||
396
541
  setupExperimentDecision !== null) {
@@ -398,12 +543,14 @@ export function FirstRunOnboarding({ initialFirstRun = false, } = {}) {
398
543
  }
399
544
  if (setupExperimentEligible) {
400
545
  setOnboardingExperiment({
401
- id: FIRST_RUN_SETUP_EXPERIMENT_ID,
546
+ id: setupExperimentId,
402
547
  variant: setupVariant,
403
548
  });
404
549
  trackOnboardingEvent("onboarding_experiment_exposed", {
405
550
  flow: "first_run",
406
551
  step_id: "choice",
552
+ experiment_id: setupExperimentId,
553
+ experiment_variant: setupVariant,
407
554
  });
408
555
  }
409
556
  setSetupExperimentDecision(setupExperimentEligible);
@@ -411,11 +558,73 @@ export function FirstRunOnboarding({ initialFirstRun = false, } = {}) {
411
558
  firstRun,
412
559
  previewMode,
413
560
  screen,
561
+ creditsV2FlagPending,
562
+ creditsV2FlowEnabled,
414
563
  setupExperimentEligible,
415
564
  setupExperimentDecision,
416
565
  setupStatusSettled,
566
+ setupExperimentId,
417
567
  setupVariant,
418
568
  ]);
569
+ useEffect(() => {
570
+ if (previewMode ||
571
+ !firstRun ||
572
+ screen !== "choice" ||
573
+ !creditsV2Enabled ||
574
+ !setupStatusSettled ||
575
+ setupExperimentTimedOutRef.current ||
576
+ setupExperimentDecision === true) {
577
+ return;
578
+ }
579
+ if (!setupExperimentEligible) {
580
+ creditsV2ExposureFailedRef.current = false;
581
+ setSetupExperimentDecision(false);
582
+ return;
583
+ }
584
+ if (creditsV2ExposureFailedRef.current)
585
+ return;
586
+ if (creditsV2ExposureStartedRef.current)
587
+ return;
588
+ creditsV2ExposureStartedRef.current = true;
589
+ creditsV2ExposureSettledRef.current = false;
590
+ setSetupExperimentDecision(null);
591
+ void creditsV2Exposure
592
+ .mutateAsync({})
593
+ .then(({ assignment }) => {
594
+ creditsV2ExposureSettledRef.current = true;
595
+ if (setupExperimentTimedOutRef.current)
596
+ return;
597
+ if (assignment?.experiment_id !==
598
+ FIRST_RUN_BUILDER_CREDITS_V2_EXPERIMENT_ID ||
599
+ (assignment.experiment_variant !== "control" &&
600
+ assignment.experiment_variant !== "credits")) {
601
+ setSetupExperimentDecision(false);
602
+ return;
603
+ }
604
+ setCreditsV2Assignment({
605
+ experiment_id: FIRST_RUN_BUILDER_CREDITS_V2_EXPERIMENT_ID,
606
+ experiment_variant: assignment.experiment_variant,
607
+ });
608
+ setSetupExperimentDecision(true);
609
+ })
610
+ .catch(() => {
611
+ creditsV2ExposureSettledRef.current = true;
612
+ if (setupExperimentTimedOutRef.current)
613
+ return;
614
+ creditsV2ExposureStartedRef.current = false;
615
+ creditsV2ExposureFailedRef.current = true;
616
+ setSetupExperimentDecision(false);
617
+ });
618
+ }, [
619
+ creditsV2Enabled,
620
+ creditsV2Exposure.mutateAsync,
621
+ firstRun,
622
+ previewMode,
623
+ screen,
624
+ setupExperimentDecision,
625
+ setupExperimentEligible,
626
+ setupStatusSettled,
627
+ ]);
419
628
  // A stalled Builder status read must not leave the setup screen blank. Past
420
629
  // the limit the person gets the existing page and stays out of the
421
630
  // experiment, identically in both arms, so exposure counts stay honest.
@@ -423,13 +632,28 @@ export function FirstRunOnboarding({ initialFirstRun = false, } = {}) {
423
632
  if (previewMode ||
424
633
  !firstRun ||
425
634
  screen !== "choice" ||
426
- !setupVariant ||
635
+ creditsV2FlagPending ||
636
+ (!setupVariant && !creditsV2FlowEnabled) ||
427
637
  setupExperimentDecision !== null) {
428
638
  return;
429
639
  }
430
- const timer = window.setTimeout(() => setSetupExperimentDecision((decision) => decision ?? false), SETUP_EXPERIMENT_DECISION_TIMEOUT_MS);
640
+ const timer = window.setTimeout(() => {
641
+ if (creditsV2ExposureSettledRef.current)
642
+ return;
643
+ setupExperimentTimedOutRef.current = true;
644
+ setSetupExperimentDecision((decision) => decision ?? false);
645
+ }, SETUP_EXPERIMENT_DECISION_TIMEOUT_MS);
431
646
  return () => window.clearTimeout(timer);
432
- }, [firstRun, previewMode, screen, setupExperimentDecision, setupVariant]);
647
+ }, [
648
+ creditsV2FlowEnabled,
649
+ creditsV2FlagPending,
650
+ firstRun,
651
+ previewMode,
652
+ screen,
653
+ setupExperimentDecision,
654
+ setupStatusSettled,
655
+ setupVariant,
656
+ ]);
433
657
  const retryOnboardingCompletion = useCallback(() => {
434
658
  const attempt = completionAttemptRef.current;
435
659
  void finishOnboarding(attempt?.screen ?? null, attempt?.extensionIndex ?? extensionIndex, attempt?.redirect ?? null);
@@ -588,9 +812,10 @@ export function FirstRunOnboarding({ initialFirstRun = false, } = {}) {
588
812
  // shows, plus the app's own headline capabilities it covers.
589
813
  const builderCapabilities = getBuilderIncludedCapabilities(profile.capabilities);
590
814
  const inCreditsArm = setupVariant === "credits";
591
- // Both arms must wait: the control setup actions can be used as soon as
592
- // they render, so exposure has to be recorded before either arm is usable.
593
- if (!previewMode && setupVariant && setupExperimentDecision === null) {
815
+ if (!previewMode &&
816
+ (creditsV2FlagPending ||
817
+ (creditsV2FlowEnabled && setupExperimentDecision === null) ||
818
+ (setupVariant && setupExperimentDecision === null))) {
594
819
  return _jsx(OnboardingSkeleton, {});
595
820
  }
596
821
  const creditsEligible = inCreditsArm && (previewMode || setupExperimentDecision === true);
@@ -619,10 +844,10 @@ export function FirstRunOnboarding({ initialFirstRun = false, } = {}) {
619
844
  };
620
845
  return (_jsx(OnboardingShell, { profile: profile, screen: "choice", progressWidth: progressWidth, ...completionErrorProps, children: _jsxs("div", { "data-testid": "first-run-credits-offer", className: "mx-auto flex w-full flex-col items-center gap-5 pt-[max(0rem,6vh)] text-center", children: [_jsx("h1", { className: cn("text-balance text-[28px] font-bold leading-tight tracking-[-0.02em] text-foreground", profile.appId === "clips" ? "max-w-lg" : "max-w-sm"), children: profile.appId === "clips"
621
846
  ? t("agentChat.onboarding.creditsTitleClips")
622
- : t("agentChat.onboarding.creditsTitle") }), _jsxs("p", { className: "max-w-sm text-sm leading-6 text-muted-foreground", children: [t("agentChat.onboarding.creditsSubtitle"), " ", _jsx("button", { type: "button", "data-testid": "first-run-credits-learn-more", "aria-expanded": moreInfoOpen, "aria-controls": moreInfoOpen ? "first-run-credits-details" : undefined, className: "rounded-sm underline underline-offset-2 transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", onClick: toggleMoreInfo, children: t("agentChat.onboarding.creditsLearnMore") })] }), _jsxs("div", { className: "flex w-full max-w-sm flex-col items-center gap-3", children: [_jsx("button", { type: "button", "data-testid": "first-run-credits-activate", className: cn(ONBOARDING_PRIMARY_BUTTON_CLASS, "min-h-11 w-full text-sm"), onClick: () => handleBuilder(true), disabled: connectFlow.connecting, children: t("agentChat.onboarding.creditsActivate") }), _jsx("button", { type: "button", "data-testid": "first-run-credits-connect-existing", className: cn(mutedButtonClass, "min-h-11 w-full text-sm"), onClick: () => handleBuilder(false), disabled: connectFlow.connecting, children: t("agentChat.onboarding.builderExistingAccount") }), _jsxs("p", { className: "text-xs leading-5 text-muted-foreground", children: [t("agentChat.onboarding.creditsFinePrint"), " ", _jsx("a", { href: "https://www.builder.io/legal/terms", target: "_blank", rel: "noreferrer", className: "underline underline-offset-2 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", children: t("agentChat.onboarding.builderTerms") }), " ", t("agentChat.onboarding.builderConsentAnd"), " ", _jsx("a", { href: "https://www.builder.io/legal/privacy", target: "_blank", rel: "noreferrer", className: "underline underline-offset-2 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", children: t("agentChat.onboarding.builderPrivacy") }), "."] })] }), builderStatusError, _jsx("div", { className: "flex flex-wrap items-center justify-center gap-x-3 gap-y-2", children: _jsxs("button", { type: "button", "data-testid": "first-run-credits-more-info", "aria-expanded": moreInfoOpen, "aria-controls": moreInfoOpen ? "first-run-credits-details" : undefined, className: "inline-flex min-h-8 items-center gap-1 rounded-full border border-border px-3 text-xs font-medium text-foreground transition-colors hover:bg-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", onClick: toggleMoreInfo, children: [t("agentChat.onboarding.creditsMoreInfo"), _jsx(MoreInfoChevron, { size: 14, "aria-hidden": true })] }) }), moreInfoOpen && (_jsx("div", { id: "first-run-credits-details", "data-testid": "first-run-credits-details", className: "mt-3 flex w-full max-w-3xl flex-col gap-6 text-left", children: setupCards })), _jsx("div", { className: "fixed bottom-6 right-6 z-10", children: _jsxs(Popover, { open: skipConfirmOpen, onOpenChange: setSkipConfirmOpen, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsx("button", { type: "button", "data-testid": "first-run-credits-skip", className: "inline-flex min-h-6 items-center px-2 rounded-sm text-xs font-medium text-muted-foreground transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", children: t("agentChat.onboarding.creditsSkip") }) }), _jsxs(PopoverContent, { side: "top", align: "end", portalled: false, "aria-labelledby": "first-run-credits-skip-title", "aria-describedby": "first-run-credits-skip-body", className: "w-96 max-w-[calc(100vw-2rem)] space-y-3 p-4 text-left", onOpenAutoFocus: (event) => {
847
+ : t("agentChat.onboarding.creditsTitle") }), _jsxs("p", { className: "max-w-sm text-sm leading-6 text-muted-foreground", children: [t("agentChat.onboarding.creditsSubtitle"), " ", _jsx("button", { type: "button", "data-testid": "first-run-credits-learn-more", "aria-expanded": moreInfoOpen, "aria-controls": moreInfoOpen ? "first-run-credits-details" : undefined, className: "rounded-sm underline underline-offset-2 transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", onClick: toggleMoreInfo, children: t("agentChat.onboarding.creditsLearnMore") })] }), _jsxs("div", { className: "flex w-full max-w-sm flex-col items-center gap-3", children: [_jsx("button", { type: "button", "data-testid": "first-run-credits-activate", className: cn(ONBOARDING_PRIMARY_BUTTON_CLASS, "min-h-11 w-full text-sm"), onClick: () => handleBuilder(true), disabled: connectFlow.connecting, children: t("agentChat.onboarding.creditsActivate") }), _jsx("button", { type: "button", "data-testid": "first-run-credits-connect-existing", className: cn(mutedButtonClass, "min-h-11 w-full text-sm"), onClick: () => handleBuilder(false), disabled: connectFlow.connecting, children: t("agentChat.onboarding.builderExistingAccount") }), creditsV2FlowEnabled && (_jsx("button", { type: "button", "data-testid": "first-run-credits-own-api-keys", className: "min-h-7 text-xs font-medium text-muted-foreground underline underline-offset-4 transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", onClick: () => void handleOpenSettings(), disabled: connectFlow.connecting, children: t("agentChat.onboarding.creditsUseOwnApiKeys") })), _jsxs("p", { className: "text-xs leading-5 text-muted-foreground", children: [t("agentChat.onboarding.creditsFinePrint"), " ", _jsx("a", { href: "https://www.builder.io/legal/terms", target: "_blank", rel: "noreferrer", className: "underline underline-offset-2 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", children: t("agentChat.onboarding.builderTerms") }), " ", t("agentChat.onboarding.builderConsentAnd"), " ", _jsx("a", { href: "https://www.builder.io/legal/privacy", target: "_blank", rel: "noreferrer", className: "underline underline-offset-2 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", children: t("agentChat.onboarding.builderPrivacy") }), "."] })] }), builderStatusError, _jsx("div", { className: "flex flex-wrap items-center justify-center gap-x-3 gap-y-2", children: _jsxs("button", { type: "button", "data-testid": "first-run-credits-more-info", "aria-expanded": moreInfoOpen, "aria-controls": moreInfoOpen ? "first-run-credits-details" : undefined, className: "inline-flex min-h-8 items-center gap-1 rounded-full border border-border px-3 text-xs font-medium text-foreground transition-colors hover:bg-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", onClick: toggleMoreInfo, children: [t("agentChat.onboarding.creditsMoreInfo"), _jsx(MoreInfoChevron, { size: 14, "aria-hidden": true })] }) }), moreInfoOpen && (_jsx("div", { id: "first-run-credits-details", "data-testid": "first-run-credits-details", className: "mt-3 flex w-full max-w-3xl flex-col gap-6 text-left", children: setupCards })), _jsx("div", { className: "fixed bottom-6 right-6 z-10", children: creditsV2FlowEnabled ? (_jsx("button", { type: "button", "data-testid": "first-run-credits-skip", className: "inline-flex min-h-6 items-center px-2 rounded-sm text-xs font-medium text-muted-foreground transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", onClick: skipSetup, children: t("agentChat.onboarding.creditsSkip") })) : (_jsxs(Popover, { open: skipConfirmOpen, onOpenChange: setSkipConfirmOpen, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsx("button", { type: "button", "data-testid": "first-run-credits-skip", className: "inline-flex min-h-6 items-center px-2 rounded-sm text-xs font-medium text-muted-foreground transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", children: t("agentChat.onboarding.creditsSkip") }) }), _jsxs(PopoverContent, { side: "top", align: "end", portalled: false, "aria-labelledby": "first-run-credits-skip-title", "aria-describedby": "first-run-credits-skip-body", className: "w-96 max-w-[calc(100vw-2rem)] space-y-3 p-4 text-left", onOpenAutoFocus: (event) => {
623
848
  event.preventDefault();
624
849
  skipGoBackRef.current?.focus();
625
- }, children: [_jsxs("div", { className: "space-y-1", children: [_jsx("p", { id: "first-run-credits-skip-title", className: "text-sm font-semibold text-foreground", children: t("agentChat.onboarding.creditsSkipConfirmTitle") }), _jsx("p", { id: "first-run-credits-skip-body", className: "text-xs leading-5 text-muted-foreground", children: t("agentChat.onboarding.creditsSkipConfirmBody") })] }), _jsxs("div", { className: "flex flex-wrap items-center justify-between gap-2", children: [_jsx(Button, { type: "button", variant: "ghost", size: "sm", className: "px-0 text-muted-foreground hover:bg-transparent hover:text-foreground", onClick: () => void handleOpenSettings(), children: t("agentChat.onboarding.creditsUseCustomKeys") }), _jsxs("div", { className: "flex flex-wrap justify-end gap-2", children: [_jsx(Button, { type: "button", variant: "secondary", size: "sm", "data-testid": "first-run-credits-skip-confirm", onClick: skipSetup, children: t("agentChat.onboarding.creditsSkipAnyway") }), _jsx(Button, { type: "button", size: "sm", ref: skipGoBackRef, onClick: () => setSkipConfirmOpen(false), children: t("agentChat.onboarding.creditsSkipGoBack") })] })] })] })] }) })] }) }));
850
+ }, children: [_jsxs("div", { className: "space-y-1", children: [_jsx("p", { id: "first-run-credits-skip-title", className: "text-sm font-semibold text-foreground", children: t("agentChat.onboarding.creditsSkipConfirmTitle") }), _jsx("p", { id: "first-run-credits-skip-body", className: "text-xs leading-5 text-muted-foreground", children: t("agentChat.onboarding.creditsSkipConfirmBody") })] }), _jsxs("div", { className: "flex flex-wrap items-center justify-between gap-2", children: [_jsx(Button, { type: "button", variant: "ghost", size: "sm", className: "px-0 text-muted-foreground hover:bg-transparent hover:text-foreground", onClick: () => void handleOpenSettings(), children: t("agentChat.onboarding.creditsUseCustomKeys") }), _jsxs("div", { className: "flex flex-wrap justify-end gap-2", children: [_jsx(Button, { type: "button", variant: "secondary", size: "sm", "data-testid": "first-run-credits-skip-confirm", onClick: skipSetup, children: t("agentChat.onboarding.creditsSkipAnyway") }), _jsx(Button, { type: "button", size: "sm", ref: skipGoBackRef, onClick: () => setSkipConfirmOpen(false), children: t("agentChat.onboarding.creditsSkipGoBack") })] })] })] })] })) })] }) }));
626
851
  }
627
852
  return (_jsx(OnboardingShell, { profile: profile, screen: "choice", progressWidth: progressWidth, ...completionErrorProps, children: _jsxs("div", { className: "mx-auto flex w-full max-w-3xl flex-col gap-9", children: [_jsxs("div", { className: "flex flex-col gap-6", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx("h1", { className: "text-[28px] font-bold leading-tight tracking-[-0.02em] text-foreground", children: "Choose your setup" }), _jsx("p", { className: "text-sm text-muted-foreground", children: "We recommend starting with Builder.io for the fastest setup." })] }), setupCards, setupSkipButton] }), _jsxs("p", { className: "text-center text-xs leading-5 text-muted-foreground", children: [t("agentChat.onboarding.builderConsentPrefix"), " ", _jsx("a", { href: "https://www.builder.io/legal/terms", target: "_blank", rel: "noreferrer", className: "underline underline-offset-2 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", children: t("agentChat.onboarding.builderTerms") }), " ", t("agentChat.onboarding.builderConsentAnd"), " ", _jsx("a", { href: "https://www.builder.io/legal/privacy", target: "_blank", rel: "noreferrer", className: "underline underline-offset-2 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", children: t("agentChat.onboarding.builderPrivacy") }), "."] })] }) }));
628
853
  }
@@ -645,22 +870,25 @@ export function FirstRunOnboarding({ initialFirstRun = false, } = {}) {
645
870
  });
646
871
  }, className: "size-4 shrink-0 accent-primary" }), _jsx("span", { className: "font-medium text-foreground", children: t(labelKey) })] }, value)))] }), selectedRole === "other" && (_jsxs("div", { className: "mt-4 flex flex-col gap-2", children: [_jsx("label", { htmlFor: "first-run-role-other", className: "text-sm font-medium text-foreground", children: t("agentChat.onboarding.roleOtherInputLabel") }), _jsx("input", { id: "first-run-role-other", "data-testid": "first-run-role-other-input", type: "text", value: customRole, maxLength: 120, disabled: savingRole, onChange: (event) => setCustomRole(event.target.value), className: "min-h-10 rounded-lg border border-border bg-card px-3 text-sm text-foreground outline-none transition-colors placeholder:text-muted-foreground focus:border-primary/40 focus:ring-2 focus:ring-ring" })] })), roleSaveError && (_jsx("p", { className: "mt-4 text-xs leading-5 text-destructive", role: "alert", children: roleSaveError }))] }) }) }));
647
872
  }
648
- const accountExists = connectFlow.accountExists && !connectFlow.connecting;
649
- const provisioning = builderConnectionMode === "provision" && !accountExists;
650
873
  const returnToSetupChoice = () => setScreen("choice");
651
- return (_jsx(OnboardingShell, { profile: profile, screen: "connecting", progressWidth: progressWidth, ...completionErrorProps, children: _jsxs("div", { className: "mx-auto flex w-full max-w-md flex-col items-center text-center", role: "status", "aria-live": "polite", "aria-busy": connectFlow.connecting, children: [_jsx("div", { className: "flex size-10 items-center justify-center rounded-full bg-primary/10 text-primary", children: accountExists ? (_jsx(IconKey, { size: 19 })) : (_jsx(IconLoader2, { className: "animate-spin", size: 19 })) }), _jsx("h1", { className: "mt-5 text-xl font-semibold tracking-[-0.04em]", children: accountExists
874
+ return (_jsx(OnboardingShell, { profile: profile, screen: "connecting", progressWidth: progressWidth, ...completionErrorProps, children: _jsxs("div", { className: "mx-auto flex w-full max-w-md flex-col items-center text-center", role: "status", "aria-live": "polite", "aria-busy": connectFlow.connecting, children: [_jsxs("div", { className: cn("flex size-10 items-center justify-center rounded-full", connectionFailed
875
+ ? "bg-destructive/10 text-destructive"
876
+ : "bg-primary/10 text-primary"), children: [accountExists ? _jsx(IconKey, { size: 19 }) : null, connectionFailed ? _jsx(IconAlertCircle, { size: 19 }) : null, !accountExists && !connectionFailed ? (_jsx(IconLoader2, { className: "animate-spin", size: 19 })) : null] }), _jsx("h1", { className: "mt-5 text-xl font-semibold tracking-[-0.04em]", children: accountExists
652
877
  ? t("agentChat.onboarding.builderAccountExistsTitle")
653
- : provisioning
654
- ? t("agentChat.onboarding.creditsActivating")
655
- : t("agentChat.onboarding.builderConnecting") }), _jsx("p", { className: "mt-2 text-sm text-muted-foreground", children: accountExists ? (t("agentChat.onboarding.builderAccountExistsDescription")) : provisioning ? (_jsxs(_Fragment, { children: [t("agentChat.onboarding.builderProvisioningDescription"), _jsx(TooltipProvider, { children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { type: "button", "aria-label": t("agentChat.onboarding.creditsAboutLabel"), className: "ml-0 inline-flex size-6 items-center justify-center rounded-full align-middle text-muted-foreground transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", children: _jsx(IconHelpCircle, { size: 12, "aria-hidden": true }) }) }), _jsx(TooltipContent, { side: "top", className: "max-w-xs text-xs", children: t("agentChat.onboarding.creditsActivatingTooltip") })] }) })] })) : (t("agentChat.onboarding.builderConnectionDescription")) }), accountExists ? (_jsxs("button", { type: "button", className: cn(ONBOARDING_PRIMARY_BUTTON_CLASS, "mt-7 w-full"), onClick: () => handleBuilder(false), disabled: connectFlow.connecting, children: [t("agentChat.auth.logIn"), _jsx(IconArrowRight, { size: 15 })] })) : (_jsxs(_Fragment, { children: [_jsxs("div", { className: "mt-7 w-full rounded-xl bg-muted/35 p-4 text-left", children: [_jsxs("div", { className: "flex items-center justify-between gap-3", children: [_jsx(Skeleton, { className: "h-3 w-28" }), _jsx(Skeleton, { className: "h-5 w-16 rounded-full" })] }), _jsx(Skeleton, { className: "mt-4 h-8 w-full" }), _jsxs("div", { className: "mt-3 grid grid-cols-3 gap-2", children: [_jsx(Skeleton, { className: "h-7 w-full" }), _jsx(Skeleton, { className: "h-7 w-full" }), _jsx(Skeleton, { className: "h-7 w-full" })] })] }), connectFlow.connecting &&
878
+ : provisioningFailed
879
+ ? t("agentChat.settingsShell.builder.setupFailed")
880
+ : provisioning
881
+ ? t("agentChat.onboarding.creditsActivating")
882
+ : t("agentChat.onboarding.builderConnecting") }), _jsx("p", { className: "mt-2 text-sm text-muted-foreground", role: connectionFailed ? "alert" : undefined, children: accountExists ? (t("agentChat.onboarding.builderAccountExistsDescription")) : provisioningFailed ? (t("agentChat.onboarding.builderProvisionFailedDetail")) : provisioning ? (_jsxs(_Fragment, { children: [t("agentChat.onboarding.builderProvisioningDescription"), _jsx(TooltipProvider, { children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { type: "button", "aria-label": t("agentChat.onboarding.creditsAboutLabel"), className: "ml-0 inline-flex size-6 items-center justify-center rounded-full align-middle text-muted-foreground transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", children: _jsx(IconHelpCircle, { size: 12, "aria-hidden": true }) }) }), _jsx(TooltipContent, { side: "top", className: "max-w-xs text-xs", children: t("agentChat.onboarding.creditsActivatingTooltip") })] }) })] })) : (t("agentChat.onboarding.builderConnectionDescription")) }), accountExists ? (_jsxs("button", { type: "button", className: cn(ONBOARDING_PRIMARY_BUTTON_CLASS, "mt-7 w-full"), onClick: () => handleBuilder(false), disabled: connectFlow.connecting, children: [t("agentChat.auth.logIn"), _jsx(IconArrowRight, { size: 15 })] })) : (_jsxs(_Fragment, { children: [connectFlow.connecting ? (_jsxs("div", { className: "mt-7 w-full rounded-xl bg-muted/35 p-4 text-left", children: [_jsxs("div", { className: "flex items-center justify-between gap-3", children: [_jsx(Skeleton, { className: "h-3 w-28" }), _jsx(Skeleton, { className: "h-5 w-16 rounded-full" })] }), _jsx(Skeleton, { className: "mt-4 h-8 w-full" }), _jsxs("div", { className: "mt-3 grid grid-cols-3 gap-2", children: [_jsx(Skeleton, { className: "h-7 w-full" }), _jsx(Skeleton, { className: "h-7 w-full" }), _jsx(Skeleton, { className: "h-7 w-full" })] })] })) : null, connectFlow.connecting &&
656
883
  (builderCancelRequested ? (_jsxs("div", { className: "mt-4 flex flex-col items-center gap-2", children: [_jsx("p", { className: "text-sm text-muted-foreground", "data-testid": "first-run-cancel-pending", children: t("agentChat.onboarding.builderCancelPending") }), _jsx("button", { type: "button", "data-testid": "first-run-back-to-choice", className: secondaryButtonClass, onClick: returnToSetupChoice, children: t("agentChat.onboarding.back") })] })) : (_jsx("button", { type: "button", "data-testid": "first-run-cancel-builder", className: "fixed bottom-6 left-6 inline-flex min-h-6 items-center px-2 rounded-sm text-xs font-medium text-muted-foreground transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", onClick: () => {
657
884
  setBuilderCancelRequested(true);
658
885
  connectFlow.cancel();
659
886
  }, children: t("common.cancel") }))), connectFlow.statusUnavailable &&
660
887
  connectFlow.connecting &&
661
888
  !connectFlow.terminalError && (_jsx("p", { className: "mt-4 text-xs text-destructive", role: "alert", children: t("agentChat.settingsShell.integrations.builderStatusFailed") })), connectFlow.terminalError && connectFlow.connecting && (_jsx("p", { className: "mt-4 text-xs text-destructive", role: "alert", children: connectFlow.terminalError })), !connectFlow.connecting &&
889
+ !provisioningFailed &&
662
890
  (connectFlow.statusUnavailable || connectFlow.terminalError) && (_jsxs("div", { className: "mt-4 flex flex-col items-center gap-2", children: [_jsx("p", { className: "text-xs text-destructive", role: "alert", children: connectFlow.terminalError ??
663
- t("agentChat.settingsShell.integrations.builderStatusFailed") }), _jsx("button", { type: "button", className: secondaryButtonClass, onClick: () => handleBuilder(builderConnectionMode === "provision"), children: "Try again" }), _jsx("button", { type: "button", "data-testid": "first-run-back-to-choice", className: secondaryButtonClass, onClick: returnToSetupChoice, children: t("agentChat.onboarding.back") })] })), !connectFlow.connecting &&
891
+ t("agentChat.settingsShell.integrations.builderStatusFailed") }), _jsx("button", { type: "button", className: secondaryButtonClass, onClick: () => handleBuilder(builderConnectionMode === "provision"), children: "Try again" }), _jsx("button", { type: "button", "data-testid": "first-run-back-to-choice", className: secondaryButtonClass, onClick: returnToSetupChoice, children: t("agentChat.onboarding.back") })] })), provisioningFailed ? (_jsxs("div", { className: "mt-4 flex w-full flex-col items-center gap-2", children: [_jsx("button", { type: "button", "data-testid": "first-run-retry-builder", className: cn(ONBOARDING_PRIMARY_BUTTON_CLASS, "w-full"), onClick: () => handleBuilder(true), children: t("agentChat.settingsShell.builder.retry") }), _jsx("button", { type: "button", "data-testid": "first-run-open-key-settings-after-failure", className: cn(secondaryButtonClass, "w-full"), onClick: () => void handleOpenSettings(), children: t("agentChat.onboarding.openAiKeySettings") }), _jsx("button", { type: "button", "data-testid": "first-run-skip-builder-after-failure", className: "inline-flex min-h-9 items-center justify-center rounded-lg px-2.5 text-xs font-medium text-muted-foreground transition-colors hover:bg-muted hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", onClick: skipFailedBuilderSetup, children: t("agentChat.onboarding.skipForNow") })] })) : null, !connectFlow.connecting &&
664
892
  !connectFlow.statusUnavailable &&
665
893
  !connectFlow.terminalError && (_jsx("button", { type: "button", "data-testid": "first-run-back-to-choice", className: "fixed bottom-6 left-6 inline-flex min-h-6 items-center px-2 rounded-sm text-xs font-medium text-muted-foreground transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", onClick: returnToSetupChoice, children: t("agentChat.onboarding.back") }))] })), accountExists && !connectFlow.connecting && (_jsx("button", { type: "button", "data-testid": "first-run-back-to-choice", className: cn(secondaryButtonClass, "mt-3"), onClick: returnToSetupChoice, children: t("agentChat.onboarding.back") }))] }) }));
666
894
  }