@agent-native/toolkit 0.225.1 → 0.226.0-nightly-20261106182619

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 (174) hide show
  1. package/agent-native.eject.json +1 -0
  2. package/dist/app/agentkit/react/components.d.ts.map +1 -1
  3. package/dist/app/agentkit/react/components.js +124 -9
  4. package/dist/app/agentkit/react/components.js.map +1 -1
  5. package/dist/app/agentkit/react/context.d.ts +1 -1
  6. package/dist/app/agentkit/react/core-file-storage.d.ts +21 -0
  7. package/dist/app/agentkit/react/core-file-storage.d.ts.map +1 -0
  8. package/dist/app/agentkit/react/core-file-storage.js +3 -0
  9. package/dist/app/agentkit/react/core-file-storage.js.map +1 -0
  10. package/dist/app/auth/AuthPage.d.ts.map +1 -1
  11. package/dist/app/auth/AuthPage.js +89 -10
  12. package/dist/app/auth/AuthPage.js.map +1 -1
  13. package/dist/app/chat/AgentKitAssistantChat.d.ts.map +1 -1
  14. package/dist/app/chat/AgentKitAssistantChat.js +39 -58
  15. package/dist/app/chat/AgentKitAssistantChat.js.map +1 -1
  16. package/dist/app/chat/MultiTabAssistantChat.d.ts.map +1 -1
  17. package/dist/app/chat/MultiTabAssistantChat.js +3 -1
  18. package/dist/app/chat/MultiTabAssistantChat.js.map +1 -1
  19. package/dist/app/chat/agentkit-chat/root.d.ts.map +1 -1
  20. package/dist/app/chat/agentkit-chat/root.js +66 -2
  21. package/dist/app/chat/agentkit-chat/root.js.map +1 -1
  22. package/dist/app/chat/chat/surface-types.d.ts +1 -0
  23. package/dist/app/chat/chat/surface-types.d.ts.map +1 -1
  24. package/dist/app/chat/chat/surface-types.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/composer/use-agentkit-capabilities.d.ts +3 -1
  29. package/dist/app/chat/composer/use-agentkit-capabilities.d.ts.map +1 -1
  30. package/dist/app/chat/composer/use-agentkit-capabilities.js +11 -11
  31. package/dist/app/chat/composer/use-agentkit-capabilities.js.map +1 -1
  32. package/dist/app/chat/composer/use-workspace-composer-menu.d.ts +3 -0
  33. package/dist/app/chat/composer/use-workspace-composer-menu.d.ts.map +1 -0
  34. package/dist/app/chat/composer/use-workspace-composer-menu.js +80 -0
  35. package/dist/app/chat/composer/use-workspace-composer-menu.js.map +1 -0
  36. package/dist/app/chat/index.d.ts +1 -0
  37. package/dist/app/chat/index.d.ts.map +1 -1
  38. package/dist/app/chat/index.js +1 -0
  39. package/dist/app/chat/index.js.map +1 -1
  40. package/dist/app/chat/prepare-prompt-composer-agent-handoff.d.ts +5 -0
  41. package/dist/app/chat/prepare-prompt-composer-agent-handoff.d.ts.map +1 -0
  42. package/dist/app/chat/prepare-prompt-composer-agent-handoff.js +108 -0
  43. package/dist/app/chat/prepare-prompt-composer-agent-handoff.js.map +1 -0
  44. package/dist/app/i18n/catalogs/ar-SA.d.ts.map +1 -1
  45. package/dist/app/i18n/catalogs/ar-SA.js +6 -1
  46. package/dist/app/i18n/catalogs/ar-SA.js.map +1 -1
  47. package/dist/app/i18n/catalogs/de-DE.d.ts.map +1 -1
  48. package/dist/app/i18n/catalogs/de-DE.js +6 -1
  49. package/dist/app/i18n/catalogs/de-DE.js.map +1 -1
  50. package/dist/app/i18n/catalogs/en-US.d.ts +6 -1
  51. package/dist/app/i18n/catalogs/en-US.d.ts.map +1 -1
  52. package/dist/app/i18n/catalogs/en-US.js +6 -1
  53. package/dist/app/i18n/catalogs/en-US.js.map +1 -1
  54. package/dist/app/i18n/catalogs/es-ES.d.ts.map +1 -1
  55. package/dist/app/i18n/catalogs/es-ES.js +6 -1
  56. package/dist/app/i18n/catalogs/es-ES.js.map +1 -1
  57. package/dist/app/i18n/catalogs/fr-FR.d.ts.map +1 -1
  58. package/dist/app/i18n/catalogs/fr-FR.js +6 -1
  59. package/dist/app/i18n/catalogs/fr-FR.js.map +1 -1
  60. package/dist/app/i18n/catalogs/hi-IN.d.ts.map +1 -1
  61. package/dist/app/i18n/catalogs/hi-IN.js +6 -1
  62. package/dist/app/i18n/catalogs/hi-IN.js.map +1 -1
  63. package/dist/app/i18n/catalogs/ja-JP.d.ts.map +1 -1
  64. package/dist/app/i18n/catalogs/ja-JP.js +6 -1
  65. package/dist/app/i18n/catalogs/ja-JP.js.map +1 -1
  66. package/dist/app/i18n/catalogs/ko-KR.d.ts.map +1 -1
  67. package/dist/app/i18n/catalogs/ko-KR.js +6 -1
  68. package/dist/app/i18n/catalogs/ko-KR.js.map +1 -1
  69. package/dist/app/i18n/catalogs/pt-BR.d.ts.map +1 -1
  70. package/dist/app/i18n/catalogs/pt-BR.js +6 -1
  71. package/dist/app/i18n/catalogs/pt-BR.js.map +1 -1
  72. package/dist/app/i18n/catalogs/zh-CN.d.ts.map +1 -1
  73. package/dist/app/i18n/catalogs/zh-CN.js +6 -1
  74. package/dist/app/i18n/catalogs/zh-CN.js.map +1 -1
  75. package/dist/app/i18n/catalogs/zh-TW.d.ts.map +1 -1
  76. package/dist/app/i18n/catalogs/zh-TW.js +6 -1
  77. package/dist/app/i18n/catalogs/zh-TW.js.map +1 -1
  78. package/dist/app/resources/McpIntegrationDialog.d.ts +3 -1
  79. package/dist/app/resources/McpIntegrationDialog.d.ts.map +1 -1
  80. package/dist/app/resources/McpIntegrationDialog.js +2 -2
  81. package/dist/app/resources/McpIntegrationDialog.js.map +1 -1
  82. package/dist/app/settings/useBuilderStatus.d.ts.map +1 -1
  83. package/dist/app/settings/useBuilderStatus.js +119 -40
  84. package/dist/app/settings/useBuilderStatus.js.map +1 -1
  85. package/dist/composer/ComposerContextMenu.d.ts +36 -2
  86. package/dist/composer/ComposerContextMenu.d.ts.map +1 -1
  87. package/dist/composer/ComposerContextMenu.js +108 -40
  88. package/dist/composer/ComposerContextMenu.js.map +1 -1
  89. package/dist/composer/ComposerContextPickerDialog.d.ts.map +1 -1
  90. package/dist/composer/ComposerContextPickerDialog.js +63 -26
  91. package/dist/composer/ComposerContextPickerDialog.js.map +1 -1
  92. package/dist/composer/ComposerPlusMenu.d.ts +5 -3
  93. package/dist/composer/ComposerPlusMenu.d.ts.map +1 -1
  94. package/dist/composer/ComposerPlusMenu.js +166 -121
  95. package/dist/composer/ComposerPlusMenu.js.map +1 -1
  96. package/dist/composer/MentionItemMedia.d.ts.map +1 -1
  97. package/dist/composer/MentionItemMedia.js +3 -1
  98. package/dist/composer/MentionItemMedia.js.map +1 -1
  99. package/dist/composer/PromptComposer.d.ts +1 -0
  100. package/dist/composer/PromptComposer.d.ts.map +1 -1
  101. package/dist/composer/PromptComposer.js +1 -0
  102. package/dist/composer/PromptComposer.js.map +1 -1
  103. package/dist/composer/TiptapComposer.d.ts.map +1 -1
  104. package/dist/composer/TiptapComposer.js +117 -50
  105. package/dist/composer/TiptapComposer.js.map +1 -1
  106. package/dist/composer/index.d.ts +1 -0
  107. package/dist/composer/index.d.ts.map +1 -1
  108. package/dist/composer/runtime-adapters.d.ts +15 -0
  109. package/dist/composer/runtime-adapters.d.ts.map +1 -1
  110. package/dist/composer/runtime-adapters.js.map +1 -1
  111. package/dist/composer/use-composer-panel-placement.d.ts.map +1 -1
  112. package/dist/composer/use-composer-panel-placement.js +2 -4
  113. package/dist/composer/use-composer-panel-placement.js.map +1 -1
  114. package/dist/composer/use-mention-search.d.ts +7 -0
  115. package/dist/composer/use-mention-search.d.ts.map +1 -1
  116. package/dist/composer/use-mention-search.js +86 -19
  117. package/dist/composer/use-mention-search.js.map +1 -1
  118. package/dist/composer/useComposerContextPicker.d.ts +5 -1
  119. package/dist/composer/useComposerContextPicker.d.ts.map +1 -1
  120. package/dist/composer/useComposerContextPicker.js +18 -4
  121. package/dist/composer/useComposerContextPicker.js.map +1 -1
  122. package/package.json +4 -4
  123. package/src/app/agentkit/react/components.tsx +158 -11
  124. package/src/app/agentkit/react/composer-context.spec.tsx +180 -0
  125. package/src/app/agentkit/react/core-file-storage.tsx +14 -0
  126. package/src/app/auth/AuthPage.interaction.spec.tsx +143 -0
  127. package/src/app/auth/AuthPage.tsx +159 -13
  128. package/src/app/chat/AgentKitAssistantChat.spec.tsx +65 -3
  129. package/src/app/chat/AgentKitAssistantChat.tsx +57 -65
  130. package/src/app/chat/MultiTabAssistantChat.spec.tsx +21 -0
  131. package/src/app/chat/MultiTabAssistantChat.tsx +4 -0
  132. package/src/app/chat/agentkit-chat/root.tsx +91 -8
  133. package/src/app/chat/chat/surface-types.ts +1 -0
  134. package/src/app/chat/composer/runtime-adapters.tsx +2 -0
  135. package/src/app/chat/composer/use-agentkit-capabilities.hardening.spec.tsx +26 -1
  136. package/src/app/chat/composer/use-agentkit-capabilities.ts +11 -11
  137. package/src/app/chat/composer/use-agentkit-integration-menu.spec.tsx +2 -4
  138. package/src/app/chat/composer/use-workspace-composer-menu.spec.tsx +174 -0
  139. package/src/app/chat/composer/use-workspace-composer-menu.tsx +122 -0
  140. package/src/app/chat/index.ts +1 -0
  141. package/src/app/chat/prepare-prompt-composer-agent-handoff.spec.ts +162 -0
  142. package/src/app/chat/prepare-prompt-composer-agent-handoff.ts +134 -0
  143. package/src/app/i18n/catalogs/ar-SA.ts +6 -1
  144. package/src/app/i18n/catalogs/de-DE.ts +6 -1
  145. package/src/app/i18n/catalogs/en-US.ts +6 -1
  146. package/src/app/i18n/catalogs/es-ES.ts +6 -1
  147. package/src/app/i18n/catalogs/fr-FR.ts +6 -1
  148. package/src/app/i18n/catalogs/hi-IN.ts +6 -1
  149. package/src/app/i18n/catalogs/ja-JP.ts +6 -1
  150. package/src/app/i18n/catalogs/ko-KR.ts +6 -1
  151. package/src/app/i18n/catalogs/pt-BR.ts +6 -1
  152. package/src/app/i18n/catalogs/zh-CN.ts +6 -1
  153. package/src/app/i18n/catalogs/zh-TW.ts +6 -1
  154. package/src/app/resources/McpIntegrationDialog.tsx +5 -0
  155. package/src/app/settings/useBuilderStatus.spec.tsx +229 -13
  156. package/src/app/settings/useBuilderStatus.ts +145 -47
  157. package/src/composer/ComposerContextMenu.spec.tsx +95 -32
  158. package/src/composer/ComposerContextMenu.tsx +284 -73
  159. package/src/composer/ComposerContextPicker.spec.tsx +13 -14
  160. package/src/composer/ComposerContextPickerDialog.spec.tsx +40 -3
  161. package/src/composer/ComposerContextPickerDialog.tsx +158 -24
  162. package/src/composer/ComposerPlusMenu.parity.spec.tsx +442 -62
  163. package/src/composer/ComposerPlusMenu.tsx +322 -237
  164. package/src/composer/MentionItemMedia.tsx +3 -0
  165. package/src/composer/PromptComposer.context.spec.tsx +253 -82
  166. package/src/composer/PromptComposer.tsx +2 -0
  167. package/src/composer/TiptapComposer.spec.ts +6 -8
  168. package/src/composer/TiptapComposer.tsx +148 -55
  169. package/src/composer/index.ts +5 -0
  170. package/src/composer/runtime-adapters.tsx +18 -0
  171. package/src/composer/use-composer-panel-placement.ts +2 -4
  172. package/src/composer/use-mention-search.spec.tsx +99 -1
  173. package/src/composer/use-mention-search.ts +101 -24
  174. package/src/composer/useComposerContextPicker.ts +25 -5
@@ -3420,6 +3420,7 @@ describe("useBuilderConnectFlow", () => {
3420
3420
  setUserAgent("Mozilla/5.0 Chrome/140.0");
3421
3421
  const popup = createPopupStub();
3422
3422
  openSpy.mockReturnValue(popup);
3423
+ const fetchMock = vi.mocked(fetch);
3423
3424
 
3424
3425
  await act(async () => {
3425
3426
  root.render(<BuilderConnectProbe />);
@@ -3435,32 +3436,191 @@ describe("useBuilderConnectFlow", () => {
3435
3436
  });
3436
3437
 
3437
3438
  expect(container.textContent).toContain("not-configured connecting");
3438
- const fetchCountBeforeReturn = vi.mocked(fetch).mock.calls.length;
3439
+ const fetchCountBeforeReturn = fetchMock.mock.calls.length;
3439
3440
 
3440
3441
  await act(async () => {
3441
3442
  window.dispatchEvent(new Event("focus"));
3442
3443
  await vi.advanceTimersByTimeAsync(26_000);
3443
3444
  });
3444
3445
 
3445
- expect(vi.mocked(fetch).mock.calls.length).toBeGreaterThan(
3446
- fetchCountBeforeReturn,
3447
- );
3446
+ expect(fetchMock.mock.calls.length).toBeGreaterThan(fetchCountBeforeReturn);
3448
3447
  expect(container.textContent).toContain("not-configured connecting");
3449
3448
  expect(container.textContent).not.toContain("Didn't finish connecting");
3450
3449
 
3450
+ (popup as unknown as { closed: boolean }).closed = true;
3451
3451
  await act(async () => {
3452
+ await vi.advanceTimersByTimeAsync(3_000);
3452
3453
  container
3453
3454
  .querySelector<HTMLButtonElement>("[data-testid='cancel-connect']")
3454
3455
  ?.click();
3456
+ });
3457
+ expect(container.textContent).toContain("not-configured idle");
3458
+ await act(async () => {
3455
3459
  await vi.advanceTimersByTimeAsync(24_000);
3456
3460
  });
3457
3461
 
3458
3462
  expect(container.textContent).toContain("not-configured idle");
3459
- expect(container.textContent).toContain(
3463
+ expect(container.textContent).not.toContain(
3460
3464
  "Builder.io setup didn't finish. Try again, or use your own keys.",
3461
3465
  );
3462
3466
  });
3463
3467
 
3468
+ it("resolves a pending initial status after a blocked popup", async () => {
3469
+ vi.useFakeTimers();
3470
+ let resolveStatus: ((status: BuilderStatus) => void) | undefined;
3471
+ const transport: BuilderConnectTransport = {
3472
+ readStatus: () =>
3473
+ new Promise((resolve) => {
3474
+ resolveStatus = resolve;
3475
+ }),
3476
+ activateAccount: vi.fn(),
3477
+ };
3478
+ await act(async () => {
3479
+ root.render(<BuilderConnectProbe transport={transport} />);
3480
+ });
3481
+ await act(async () => {
3482
+ await vi.advanceTimersByTimeAsync(300);
3483
+ });
3484
+ expect(resolveStatus).toBeDefined();
3485
+ await act(async () => {
3486
+ container.querySelector("button")?.click();
3487
+ });
3488
+ expect(container.textContent).toContain("Allow popups");
3489
+ await act(async () => {
3490
+ resolveStatus?.({ ...connectedBuilderStatus, configured: false });
3491
+ await Promise.resolve();
3492
+ });
3493
+ expect(container.textContent).toContain("not-configured idle resolved");
3494
+ expect(container.textContent).toContain("Allow popups");
3495
+ });
3496
+
3497
+ it("reports a timed-out normal poll as unavailable", async () => {
3498
+ vi.useFakeTimers();
3499
+ const popup = createPopupStub();
3500
+ openSpy.mockReturnValue(popup);
3501
+ let stall = false;
3502
+ const disconnected = { ...connectedBuilderStatus, configured: false };
3503
+ const transport: BuilderConnectTransport = {
3504
+ readStatus: ({ signal }) =>
3505
+ stall
3506
+ ? new Promise((_resolve, reject) =>
3507
+ signal?.addEventListener("abort", () =>
3508
+ reject(new Error("timeout")),
3509
+ ),
3510
+ )
3511
+ : Promise.resolve(disconnected),
3512
+ activateAccount: vi.fn(),
3513
+ };
3514
+ await act(async () => {
3515
+ root.render(<BuilderConnectProbe transport={transport} />);
3516
+ });
3517
+ await act(async () => {
3518
+ await vi.advanceTimersByTimeAsync(300);
3519
+ });
3520
+ await act(async () => {
3521
+ container.querySelector("button")?.click();
3522
+ await Promise.resolve();
3523
+ });
3524
+ stall = true;
3525
+ await act(async () => {
3526
+ await vi.advanceTimersByTimeAsync(12_000);
3527
+ });
3528
+ expect(
3529
+ container.querySelector("[data-testid='status-unavailable']")
3530
+ ?.textContent,
3531
+ ).toBe("unavailable");
3532
+ });
3533
+
3534
+ it("restarts reconciliation when an earlier poll is stuck", async () => {
3535
+ vi.useFakeTimers();
3536
+ vi.setSystemTime(new Date("2026-05-14T12:00:00.000Z"));
3537
+ openSpy.mockReturnValue(createPopupStub());
3538
+ let stall = false;
3539
+ let connected = false;
3540
+ let stalledSignal: AbortSignal | undefined;
3541
+ let release: ((status: BuilderStatus) => void) | undefined;
3542
+ let disconnected: BuilderStatus = {
3543
+ ...connectedBuilderStatus,
3544
+ configured: false,
3545
+ };
3546
+ const readStatus: BuilderConnectTransport["readStatus"] = async ({
3547
+ signal,
3548
+ }) => {
3549
+ if (stall) {
3550
+ stall = false;
3551
+ stalledSignal = signal;
3552
+ return new Promise<BuilderStatus>((resolve) => {
3553
+ release = resolve;
3554
+ });
3555
+ }
3556
+ return connected ? connectedBuilderStatus : disconnected;
3557
+ };
3558
+ const transport: BuilderConnectTransport = {
3559
+ readStatus,
3560
+ activateAccount: vi.fn(),
3561
+ };
3562
+ await act(async () => {
3563
+ root.render(<BuilderConnectProbe transport={transport} />);
3564
+ });
3565
+ await act(async () => {
3566
+ await vi.advanceTimersByTimeAsync(300);
3567
+ });
3568
+ const startedAt = Date.now();
3569
+ await act(async () => {
3570
+ container.querySelector("button")?.click();
3571
+ await Promise.resolve();
3572
+ await Promise.resolve();
3573
+ });
3574
+ stall = true;
3575
+ await act(async () => {
3576
+ await vi.advanceTimersByTimeAsync(2_000);
3577
+ });
3578
+ expect(release).toBeDefined();
3579
+ await act(async () => {
3580
+ container
3581
+ .querySelector<HTMLButtonElement>("[data-testid='cancel-connect']")
3582
+ ?.click();
3583
+ });
3584
+ expect(stalledSignal?.aborted).toBe(true);
3585
+ await act(async () => {
3586
+ vi.setSystemTime(startedAt);
3587
+ container.querySelector("button")?.click();
3588
+ await Promise.resolve();
3589
+ });
3590
+ await act(async () => {
3591
+ release?.(connectedBuilderStatus);
3592
+ await Promise.resolve();
3593
+ await Promise.resolve();
3594
+ });
3595
+ expect(container.textContent).toContain("not-configured connecting");
3596
+ await act(async () => {
3597
+ container
3598
+ .querySelector<HTMLButtonElement>("[data-testid='cancel-connect']")
3599
+ ?.click();
3600
+ });
3601
+ disconnected = {
3602
+ ...disconnected,
3603
+ connectError: {
3604
+ code: "account_exists",
3605
+ message: "Already exists",
3606
+ at: Date.now(),
3607
+ },
3608
+ };
3609
+ await act(async () => {
3610
+ window.dispatchEvent(new Event("focus"));
3611
+ await Promise.resolve();
3612
+ await vi.advanceTimersByTimeAsync(50_000);
3613
+ });
3614
+ expect(container.textContent).toContain(
3615
+ "not-configured idle resolved no-account-exists",
3616
+ );
3617
+ connected = true;
3618
+ await act(async () => {
3619
+ await vi.advanceTimersByTimeAsync(2_000);
3620
+ });
3621
+ expect(container.textContent).toContain("configured idle resolved");
3622
+ });
3623
+
3464
3624
  it("keeps a real success during the explicit cancel grace window", async () => {
3465
3625
  vi.useFakeTimers();
3466
3626
  vi.setSystemTime(new Date("2026-05-14T12:00:00.000Z"));
@@ -3469,7 +3629,7 @@ describe("useBuilderConnectFlow", () => {
3469
3629
  openSpy.mockReturnValue(popup);
3470
3630
 
3471
3631
  const startedAt = Date.now();
3472
- const configuredAfterMs = 24_200;
3632
+ const configuredAfterMs = 34_200;
3473
3633
  vi.mocked(fetch).mockImplementation(async () => {
3474
3634
  const isConfigured = Date.now() - startedAt >= configuredAfterMs;
3475
3635
  return jsonResponse(
@@ -3510,7 +3670,7 @@ describe("useBuilderConnectFlow", () => {
3510
3670
  expect(popup.close).toHaveBeenCalledOnce();
3511
3671
 
3512
3672
  await act(async () => {
3513
- await vi.advanceTimersByTimeAsync(20_000);
3673
+ await vi.advanceTimersByTimeAsync(30_000);
3514
3674
  });
3515
3675
 
3516
3676
  await act(async () => {
@@ -3528,7 +3688,7 @@ describe("useBuilderConnectFlow", () => {
3528
3688
  expect(container.textContent).not.toContain("Didn't finish connecting");
3529
3689
  });
3530
3690
 
3531
- it("allows cancellation after success confirmation retries exhaust", async () => {
3691
+ it("bounds cancellation after success confirmation retries exhaust", async () => {
3532
3692
  vi.useFakeTimers();
3533
3693
  vi.setSystemTime(new Date("2026-05-14T12:00:00.000Z"));
3534
3694
  setUserAgent("Mozilla/5.0 Chrome/140.0");
@@ -3571,16 +3731,22 @@ describe("useBuilderConnectFlow", () => {
3571
3731
  await vi.advanceTimersByTimeAsync(5000);
3572
3732
  });
3573
3733
  expect(container.textContent).toContain("not-configured connecting");
3574
-
3734
+ const fetchMock = vi.mocked(fetch);
3575
3735
  await act(async () => {
3576
3736
  container
3577
3737
  .querySelector<HTMLButtonElement>("[data-testid='cancel-connect']")
3578
3738
  ?.click();
3579
- await vi.advanceTimersByTimeAsync(22_000);
3580
3739
  });
3581
-
3740
+ await act(async () => {
3741
+ await vi.advanceTimersByTimeAsync(130_000);
3742
+ });
3743
+ const readsAtDeadline = fetchMock.mock.calls.length;
3744
+ await act(async () => {
3745
+ await vi.advanceTimersByTimeAsync(10_000);
3746
+ });
3747
+ expect(fetchMock.mock.calls.length).toBe(readsAtDeadline);
3582
3748
  expect(container.textContent).toContain("not-configured idle");
3583
- expect(container.textContent).toContain(
3749
+ expect(container.textContent).not.toContain(
3584
3750
  "Builder.io setup didn't finish. Try again, or use your own keys.",
3585
3751
  );
3586
3752
  });
@@ -3666,7 +3832,7 @@ describe("useBuilderConnectFlow", () => {
3666
3832
  await vi.advanceTimersByTimeAsync(22_000);
3667
3833
  });
3668
3834
  expect(container.textContent).toContain("not-configured idle");
3669
- expect(container.textContent).toContain(
3835
+ expect(container.textContent).not.toContain(
3670
3836
  "Builder.io setup didn't finish. Try again, or use your own keys.",
3671
3837
  );
3672
3838
  releaseStalledStatus?.();
@@ -3815,6 +3981,56 @@ describe("useBuilderConnectFlow", () => {
3815
3981
  ).toBe("not-configured idle resolved no-account-exists");
3816
3982
  });
3817
3983
 
3984
+ it("ignores a failed embedded host launch after cancel and retry", async () => {
3985
+ vi.useFakeTimers();
3986
+ setEmbeddedWindow(true);
3987
+ let finishFirst: ((opened: boolean) => void) | undefined;
3988
+ vi.mocked(openMcpAppHostLink)
3989
+ .mockImplementationOnce(
3990
+ () =>
3991
+ new Promise<boolean>((resolve) => {
3992
+ finishFirst = resolve;
3993
+ }),
3994
+ )
3995
+ .mockResolvedValueOnce(true);
3996
+ const transport: BuilderConnectTransport = {
3997
+ readStatus: async () => ({
3998
+ ...connectedBuilderStatus,
3999
+ configured: false,
4000
+ }),
4001
+ activateAccount: vi.fn(),
4002
+ };
4003
+ await act(async () => {
4004
+ root.render(<BuilderConnectProbe transport={transport} />);
4005
+ });
4006
+ await act(async () => {
4007
+ await vi.advanceTimersByTimeAsync(300);
4008
+ });
4009
+ await act(async () => {
4010
+ container.querySelector("button")?.click();
4011
+ await Promise.resolve();
4012
+ });
4013
+ expect(finishFirst).toBeDefined();
4014
+ await act(async () => {
4015
+ container
4016
+ .querySelector<HTMLButtonElement>("[data-testid='cancel-connect']")
4017
+ ?.click();
4018
+ });
4019
+ await act(async () => {
4020
+ container.querySelector("button")?.click();
4021
+ await Promise.resolve();
4022
+ });
4023
+ expect(container.textContent).toContain("not-configured connecting");
4024
+ await act(async () => {
4025
+ finishFirst?.(false);
4026
+ await Promise.resolve();
4027
+ });
4028
+ expect(container.textContent).toContain("not-configured connecting");
4029
+ expect(
4030
+ container.querySelector("[data-testid='terminal-error']")?.textContent,
4031
+ ).toBe("none");
4032
+ });
4033
+
3818
4034
  it("does not open the MCP host after cancelling a pending embedded startup", async () => {
3819
4035
  setUserAgent("Mozilla/5.0 Chrome/140.0");
3820
4036
  setEmbeddedWindow(true);